mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2026-01-06 12:36:56 +00:00
Added entity selection changes to webhook (#2777)
* Implemented Listing, Add and Delete Webhook functionalities * Implementing Edit Webhook and minor changes * Minor updates * Minor changes for generate button * Addressing comments * Addressing comment * Added entity selection changes to webhook * Addressing change requests * Minor changes * Removed unnecessary file * Update openmetadata-ui/src/main/resources/ui/src/components/common/webhook-data-card/WebhookDataCard.tsx Co-authored-by: Sachin Chaurasiya <sachinchaurasiyachotey87@gmail.com> * Addressing change requests * Schema to Typescript interfaces for webhook Co-authored-by: Sachin Chaurasiya <sachinchaurasiyachotey87@gmail.com>
This commit is contained in:
parent
891ff465a0
commit
78ee32f585
@ -12,13 +12,18 @@
|
||||
*/
|
||||
|
||||
import { LoadingState } from 'Models';
|
||||
import { FormSubmitType } from '../../enums/form.enum';
|
||||
import { CreateWebhook } from '../../generated/api/events/createWebhook';
|
||||
import { Webhook } from '../../generated/entity/events/webhook';
|
||||
|
||||
export interface AddWebhookProps {
|
||||
data?: Webhook;
|
||||
header: string;
|
||||
mode: FormSubmitType;
|
||||
saveState?: LoadingState;
|
||||
deleteState?: LoadingState;
|
||||
allowAccess?: boolean;
|
||||
onCancel: () => void;
|
||||
onDelete?: (id: string) => void;
|
||||
onSave: (data: CreateWebhook) => void;
|
||||
}
|
||||
|
||||
@ -17,7 +17,9 @@ import { cloneDeep, isEmpty, isNil, startCase } from 'lodash';
|
||||
import { EditorContentRef } from 'Models';
|
||||
import React, { FunctionComponent, useRef, useState } from 'react';
|
||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
import { WILD_CARD_CHAR } from '../../constants/char.constants';
|
||||
import { EntityType } from '../../enums/entity.enum';
|
||||
import { FormSubmitType } from '../../enums/form.enum';
|
||||
import { PageLayoutType } from '../../enums/layout.enum';
|
||||
import {
|
||||
CreateWebhook,
|
||||
@ -38,24 +40,87 @@ import MarkdownWithPreview from '../common/editor/MarkdownWithPreview';
|
||||
import PageLayout from '../containers/PageLayout';
|
||||
import DropDown from '../dropdown/DropDown';
|
||||
import Loader from '../Loader/Loader';
|
||||
import ConfirmationModal from '../Modals/ConfirmationModal/ConfirmationModal';
|
||||
import { AddWebhookProps } from './AddWebhook.interface';
|
||||
|
||||
const Field = ({ children }: { children: React.ReactNode }) => {
|
||||
return <div className="tw-mt-4">{children}</div>;
|
||||
};
|
||||
|
||||
const getEntitiesList = () => {
|
||||
const retVal: Array<{ name: string; value: string }> = [
|
||||
EntityType.TABLE,
|
||||
EntityType.TOPIC,
|
||||
EntityType.DASHBOARD,
|
||||
EntityType.PIPELINE,
|
||||
].map((item) => {
|
||||
return {
|
||||
name: startCase(item),
|
||||
value: item,
|
||||
};
|
||||
});
|
||||
retVal.unshift({ name: 'All entities', value: WILD_CARD_CHAR });
|
||||
|
||||
return retVal;
|
||||
};
|
||||
|
||||
const getHiddenEntitiesList = (entities: Array<string> = []) => {
|
||||
if (entities.includes(WILD_CARD_CHAR)) {
|
||||
return entities.filter((item) => item !== WILD_CARD_CHAR);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const getSelectedEvents = (prev: EventFilter, value: string) => {
|
||||
let entities = prev.entities || [];
|
||||
if (entities.includes(value)) {
|
||||
if (value === WILD_CARD_CHAR) {
|
||||
entities = [];
|
||||
} else {
|
||||
if (entities.includes(WILD_CARD_CHAR)) {
|
||||
const allIndex = entities.indexOf(WILD_CARD_CHAR);
|
||||
entities.splice(allIndex, 1);
|
||||
}
|
||||
const index = entities.indexOf(value);
|
||||
entities.splice(index, 1);
|
||||
}
|
||||
} else {
|
||||
if (value === WILD_CARD_CHAR) {
|
||||
entities = getEntitiesList().map((item) => item.value);
|
||||
} else {
|
||||
entities.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
return { ...prev, entities };
|
||||
};
|
||||
|
||||
const getEventFilterByType = (
|
||||
filters: Array<EventFilter>,
|
||||
type: EventType
|
||||
): EventFilter => {
|
||||
return filters.find((item) => item.eventType === type) || ({} as EventFilter);
|
||||
let eventFilter =
|
||||
filters.find((item) => item.eventType === type) || ({} as EventFilter);
|
||||
if (eventFilter.entities?.includes(WILD_CARD_CHAR)) {
|
||||
eventFilter = getSelectedEvents(
|
||||
{ ...eventFilter, entities: [] },
|
||||
WILD_CARD_CHAR
|
||||
);
|
||||
}
|
||||
|
||||
return eventFilter;
|
||||
};
|
||||
|
||||
const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
data,
|
||||
header,
|
||||
mode = FormSubmitType.ADD,
|
||||
saveState = 'initial',
|
||||
deleteState = 'initial',
|
||||
allowAccess = true,
|
||||
onCancel,
|
||||
onDelete,
|
||||
onSave,
|
||||
}: AddWebhookProps) => {
|
||||
const markdownRef = useRef<EditorContentRef>();
|
||||
@ -93,13 +158,25 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
endpointUrl: false,
|
||||
eventFilters: false,
|
||||
invalidEndpointUrl: false,
|
||||
invalidEventFilters: false,
|
||||
});
|
||||
const [copiedSecret, setCopiedSecret] = useState<boolean>(false);
|
||||
const [generatingSecret, setGeneratingSecret] = useState<boolean>(false);
|
||||
const [isDelete, setIsDelete] = useState<boolean>(false);
|
||||
|
||||
const handleDelete = () => {
|
||||
if (data) {
|
||||
onDelete && onDelete(data.id);
|
||||
}
|
||||
setIsDelete(false);
|
||||
};
|
||||
|
||||
const handleValidation = (
|
||||
event: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>
|
||||
) => {
|
||||
if (!allowAccess) {
|
||||
return;
|
||||
}
|
||||
const value = event.target.value;
|
||||
const eleName = event.target.name;
|
||||
let { name, endpointUrl, invalidEndpointUrl } = cloneDeep(showErrorMsg);
|
||||
@ -135,6 +212,9 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
};
|
||||
|
||||
const generateSecret = () => {
|
||||
if (!allowAccess) {
|
||||
return;
|
||||
}
|
||||
const apiKey = cryptoRandomString({ length: 50, type: 'alphanumeric' });
|
||||
setGeneratingSecret(true);
|
||||
setTimeout(() => {
|
||||
@ -149,44 +229,10 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
setSecretKey('');
|
||||
};
|
||||
|
||||
const getEntitiesList = () => {
|
||||
const retVal: Array<{ name: string; value: string }> = [
|
||||
EntityType.TABLE,
|
||||
EntityType.TOPIC,
|
||||
EntityType.DASHBOARD,
|
||||
EntityType.PIPELINE,
|
||||
].map((item) => {
|
||||
return {
|
||||
name: startCase(item),
|
||||
value: item,
|
||||
};
|
||||
});
|
||||
retVal.unshift({ name: 'All entities', value: '*' });
|
||||
|
||||
return retVal;
|
||||
};
|
||||
|
||||
const getSelectedEvents = (prev: EventFilter, value: string) => {
|
||||
let entities = prev.entities || [];
|
||||
if (entities.includes(value)) {
|
||||
const index = entities.indexOf(value);
|
||||
entities.splice(index, 1);
|
||||
} else {
|
||||
if (value === '*') {
|
||||
entities = [value];
|
||||
} else {
|
||||
if (value !== '*' && entities.includes('*')) {
|
||||
const allIndex = entities.indexOf('*');
|
||||
entities.splice(allIndex, 1);
|
||||
}
|
||||
entities.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
return { ...prev, entities };
|
||||
};
|
||||
|
||||
const toggleEventFilters = (type: EventType, value: boolean) => {
|
||||
if (!allowAccess) {
|
||||
return;
|
||||
}
|
||||
let setter;
|
||||
switch (type) {
|
||||
case EventType.EntityCreated: {
|
||||
@ -214,7 +260,34 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
: ({} as EventFilter)
|
||||
);
|
||||
setShowErrorMsg((prev) => {
|
||||
return { ...prev, eventFilters: false };
|
||||
return { ...prev, eventFilters: false, invalidEventFilters: false };
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleEntitySelection = (type: EventType, value: string) => {
|
||||
let setter;
|
||||
switch (type) {
|
||||
case EventType.EntityCreated: {
|
||||
setter = setCreateEvents;
|
||||
|
||||
break;
|
||||
}
|
||||
case EventType.EntityUpdated: {
|
||||
setter = setUpdateEvents;
|
||||
|
||||
break;
|
||||
}
|
||||
case EventType.EntityDeleted: {
|
||||
setter = setDeleteEvents;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (setter) {
|
||||
setter((prev) => getSelectedEvents(prev, value));
|
||||
setShowErrorMsg((prev) => {
|
||||
return { ...prev, eventFilters: false, invalidEventFilters: false };
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -222,18 +295,42 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
const getEventFiltersData = () => {
|
||||
const eventFilters: Array<EventFilter> = [];
|
||||
if (!isEmpty(createEvents)) {
|
||||
eventFilters.push(createEvents);
|
||||
const event = createEvents.entities?.includes(WILD_CARD_CHAR)
|
||||
? { ...createEvents, entities: [WILD_CARD_CHAR] }
|
||||
: createEvents;
|
||||
eventFilters.push(event);
|
||||
}
|
||||
if (!isEmpty(updateEvents)) {
|
||||
eventFilters.push(updateEvents);
|
||||
const event = updateEvents.entities?.includes(WILD_CARD_CHAR)
|
||||
? { ...updateEvents, entities: [WILD_CARD_CHAR] }
|
||||
: updateEvents;
|
||||
eventFilters.push(event);
|
||||
}
|
||||
if (!isEmpty(deleteEvents)) {
|
||||
eventFilters.push(deleteEvents);
|
||||
const event = deleteEvents.entities?.includes(WILD_CARD_CHAR)
|
||||
? { ...deleteEvents, entities: [WILD_CARD_CHAR] }
|
||||
: deleteEvents;
|
||||
eventFilters.push(event);
|
||||
}
|
||||
|
||||
return eventFilters;
|
||||
};
|
||||
|
||||
const validateEventFilters = () => {
|
||||
let isValid = false;
|
||||
if (!isEmpty(createEvents)) {
|
||||
isValid = Boolean(createEvents.entities?.length);
|
||||
}
|
||||
if (!isEmpty(updateEvents)) {
|
||||
isValid = Boolean(updateEvents.entities?.length);
|
||||
}
|
||||
if (!isEmpty(deleteEvents) && deleteEvents.entities?.length) {
|
||||
isValid = Boolean(deleteEvents.entities?.length);
|
||||
}
|
||||
|
||||
return isValid;
|
||||
};
|
||||
|
||||
const validateForm = () => {
|
||||
const errMsg = {
|
||||
name: !name.trim(),
|
||||
@ -244,6 +341,7 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
...deleteEvents,
|
||||
}),
|
||||
invalidEndpointUrl: !isValidUrl(endpointUrl.trim()),
|
||||
invalidEventFilters: !validateEventFilters(),
|
||||
};
|
||||
setShowErrorMsg(errMsg);
|
||||
|
||||
@ -266,6 +364,73 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const getDeleteButton = () => {
|
||||
return allowAccess ? (
|
||||
<>
|
||||
{deleteState === 'waiting' ? (
|
||||
<Button
|
||||
disabled
|
||||
className="tw-w-16 tw-h-10 disabled:tw-opacity-100"
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="text">
|
||||
<Loader size="small" type="default" />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
className={classNames({
|
||||
'tw-opacity-40': !allowAccess,
|
||||
})}
|
||||
data-testid="delete-webhook"
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="text"
|
||||
onClick={() => setIsDelete(true)}>
|
||||
Delete
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
) : null;
|
||||
};
|
||||
|
||||
const getSaveButton = () => {
|
||||
return allowAccess ? (
|
||||
<>
|
||||
{saveState === 'waiting' ? (
|
||||
<Button
|
||||
disabled
|
||||
className="tw-w-16 tw-h-10 disabled:tw-opacity-100"
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="contained">
|
||||
<Loader size="small" type="white" />
|
||||
</Button>
|
||||
) : saveState === 'success' ? (
|
||||
<Button
|
||||
disabled
|
||||
className="tw-w-16 tw-h-10 disabled:tw-opacity-100"
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="contained">
|
||||
<i aria-hidden="true" className="fa fa-check" />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
className={classNames('tw-w-16 tw-h-10', {
|
||||
'tw-opacity-40': !allowAccess,
|
||||
})}
|
||||
data-testid="save-webhook"
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="contained"
|
||||
onClick={handleSave}>
|
||||
Save
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
) : null;
|
||||
};
|
||||
|
||||
const fetchRightPanel = () => {
|
||||
return (
|
||||
<>
|
||||
@ -333,6 +498,7 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
</label>
|
||||
<MarkdownWithPreview
|
||||
data-testid="description"
|
||||
readonly={!allowAccess}
|
||||
ref={markdownRef}
|
||||
value={description}
|
||||
/>
|
||||
@ -344,6 +510,7 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
<input
|
||||
className="tw-form-inputs tw-px-3 tw-py-1"
|
||||
data-testid="endpoint-url"
|
||||
disabled={!allowAccess}
|
||||
id="endpoint-url"
|
||||
name="endpoint-url"
|
||||
placeholder="http(s)://www.example.com"
|
||||
@ -364,7 +531,7 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
className={classNames('toggle-switch', { open: active })}
|
||||
data-testid="active"
|
||||
onClick={() => {
|
||||
setActive((prev) => !prev);
|
||||
allowAccess && setActive((prev) => !prev);
|
||||
}}>
|
||||
<div className="switch" />
|
||||
</div>
|
||||
@ -385,6 +552,7 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
checked={!isEmpty(createEvents)}
|
||||
className="tw-mr-1 custom-checkbox"
|
||||
data-testid="checkbox"
|
||||
disabled={!allowAccess}
|
||||
type="checkbox"
|
||||
onChange={(e) => {
|
||||
toggleEventFilters(EventType.EntityCreated, e.target.checked);
|
||||
@ -399,15 +567,14 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
</div>
|
||||
<DropDown
|
||||
className="tw-bg-white"
|
||||
disabled={isEmpty(createEvents)}
|
||||
disabled={!allowAccess || isEmpty(createEvents)}
|
||||
dropDownList={getEntitiesList()}
|
||||
hiddenItems={getHiddenEntitiesList(createEvents.entities)}
|
||||
label="select entities"
|
||||
selectedItems={createEvents.entities}
|
||||
type="checkbox"
|
||||
onSelect={(_e, value) =>
|
||||
setCreateEvents((prev) =>
|
||||
getSelectedEvents(prev, value as string)
|
||||
)
|
||||
handleEntitySelection(EventType.EntityCreated, value as string)
|
||||
}
|
||||
/>
|
||||
</Field>
|
||||
@ -420,6 +587,7 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
checked={!isEmpty(updateEvents)}
|
||||
className="tw-mr-1 custom-checkbox"
|
||||
data-testid="checkbox"
|
||||
disabled={!allowAccess}
|
||||
type="checkbox"
|
||||
onChange={(e) => {
|
||||
toggleEventFilters(EventType.EntityUpdated, e.target.checked);
|
||||
@ -434,15 +602,14 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
</div>
|
||||
<DropDown
|
||||
className="tw-bg-white"
|
||||
disabled={isEmpty(updateEvents)}
|
||||
disabled={!allowAccess || isEmpty(updateEvents)}
|
||||
dropDownList={getEntitiesList()}
|
||||
hiddenItems={getHiddenEntitiesList(updateEvents.entities)}
|
||||
label="select entities"
|
||||
selectedItems={updateEvents.entities}
|
||||
type="checkbox"
|
||||
onSelect={(_e, value) =>
|
||||
setUpdateEvents((prev) =>
|
||||
getSelectedEvents(prev, value as string)
|
||||
)
|
||||
handleEntitySelection(EventType.EntityUpdated, value as string)
|
||||
}
|
||||
/>
|
||||
</Field>
|
||||
@ -455,6 +622,7 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
checked={!isEmpty(deleteEvents)}
|
||||
className="tw-mr-1 custom-checkbox"
|
||||
data-testid="checkbox"
|
||||
disabled={!allowAccess}
|
||||
type="checkbox"
|
||||
onChange={(e) => {
|
||||
toggleEventFilters(EventType.EntityDeleted, e.target.checked);
|
||||
@ -469,19 +637,21 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
</div>
|
||||
<DropDown
|
||||
className="tw-bg-white"
|
||||
disabled={isEmpty(deleteEvents)}
|
||||
disabled={!allowAccess || isEmpty(deleteEvents)}
|
||||
dropDownList={getEntitiesList()}
|
||||
hiddenItems={getHiddenEntitiesList(deleteEvents.entities)}
|
||||
label="select entities"
|
||||
selectedItems={deleteEvents.entities}
|
||||
type="checkbox"
|
||||
onSelect={(_e, value) =>
|
||||
setDeleteEvents((prev) =>
|
||||
getSelectedEvents(prev, value as string)
|
||||
)
|
||||
handleEntitySelection(EventType.EntityDeleted, value as string)
|
||||
}
|
||||
/>
|
||||
{showErrorMsg.eventFilters &&
|
||||
errorMsg('Webhook event filters are required.')}
|
||||
{showErrorMsg.eventFilters
|
||||
? errorMsg('Webhook event filters are required.')
|
||||
: showErrorMsg.invalidEventFilters
|
||||
? errorMsg('Webhook event filters are invalid.')
|
||||
: null}
|
||||
</Field>
|
||||
<Field>
|
||||
<div className="tw-flex tw-justify-end tw-pt-1">
|
||||
@ -512,6 +682,7 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
<input
|
||||
className="tw-form-inputs tw-px-3 tw-py-1"
|
||||
data-testid="batch-size"
|
||||
disabled={!allowAccess}
|
||||
id="batch-size"
|
||||
name="batch-size"
|
||||
placeholder="10"
|
||||
@ -529,6 +700,7 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
<input
|
||||
className="tw-form-inputs tw-px-3 tw-py-1"
|
||||
data-testid="connection-timeout"
|
||||
disabled={!allowAccess}
|
||||
id="connection-timeout"
|
||||
name="connection-timeout"
|
||||
placeholder="10"
|
||||
@ -540,143 +712,146 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
</div>
|
||||
</Field>
|
||||
<Field>
|
||||
{!data ? (
|
||||
<>
|
||||
<label
|
||||
className="tw-block tw-form-label tw-my-0"
|
||||
htmlFor="secret-key">
|
||||
Secret Key:
|
||||
</label>
|
||||
{allowAccess ? (
|
||||
!data ? (
|
||||
<>
|
||||
<label
|
||||
className="tw-block tw-form-label tw-my-0"
|
||||
htmlFor="secret-key">
|
||||
Secret Key:
|
||||
</label>
|
||||
<div className="tw-flex tw-items-center">
|
||||
<input
|
||||
readOnly
|
||||
className="tw-form-inputs tw-px-3 tw-py-1"
|
||||
data-testid="connection-timeout"
|
||||
id="connection-timeout"
|
||||
name="connection-timeout"
|
||||
placeholder="secret key"
|
||||
type="text"
|
||||
value={secretKey}
|
||||
/>
|
||||
<Button
|
||||
className="tw-w-8 tw-h-8 tw--ml-8 tw-rounded-md"
|
||||
data-testid="generate-secret"
|
||||
size="custom"
|
||||
theme="default"
|
||||
variant="text"
|
||||
onClick={generateSecret}>
|
||||
{generatingSecret ? (
|
||||
<Loader size="small" type="default" />
|
||||
) : (
|
||||
<i className="fas fa-sync-alt" />
|
||||
)}
|
||||
</Button>
|
||||
{secretKey ? (
|
||||
<>
|
||||
<CopyToClipboard
|
||||
text={secretKey}
|
||||
onCopy={() => setCopiedSecret(true)}>
|
||||
<Button
|
||||
className="tw-h-8 tw-ml-4"
|
||||
data-testid="copy-secret"
|
||||
size="custom"
|
||||
theme="default"
|
||||
variant="text">
|
||||
<SVGIcons
|
||||
alt="Copy"
|
||||
icon={Icons.COPY}
|
||||
width="16px"
|
||||
/>
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
<Button
|
||||
className="tw-h-8 tw-ml-4"
|
||||
data-testid="clear-secret"
|
||||
size="custom"
|
||||
theme="default"
|
||||
variant="text"
|
||||
onClick={resetSecret}>
|
||||
<SVGIcons
|
||||
alt="Delete"
|
||||
icon={Icons.DELETE}
|
||||
width="16px"
|
||||
/>
|
||||
</Button>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
</>
|
||||
) : data.secretKey ? (
|
||||
<div className="tw-flex tw-items-center">
|
||||
<input
|
||||
readOnly
|
||||
className="tw-form-inputs tw-px-3 tw-py-1"
|
||||
data-testid="connection-timeout"
|
||||
id="connection-timeout"
|
||||
name="connection-timeout"
|
||||
data-testid="secret-key"
|
||||
id="secret-key"
|
||||
name="secret-key"
|
||||
placeholder="secret key"
|
||||
type="text"
|
||||
value={secretKey}
|
||||
/>
|
||||
<Button
|
||||
className="tw-w-8 tw-h-8 tw--ml-8 tw-rounded-md"
|
||||
data-testid="generate-secret"
|
||||
size="custom"
|
||||
theme="default"
|
||||
variant="text"
|
||||
onClick={generateSecret}>
|
||||
{generatingSecret ? (
|
||||
<Loader size="small" type="default" />
|
||||
) : (
|
||||
<i className="fas fa-sync-alt" />
|
||||
)}
|
||||
</Button>
|
||||
{secretKey ? (
|
||||
<>
|
||||
<CopyToClipboard
|
||||
text={secretKey}
|
||||
onCopy={() => setCopiedSecret(true)}>
|
||||
<Button
|
||||
className="tw-h-8 tw-ml-4"
|
||||
data-testid="copy-secret"
|
||||
size="custom"
|
||||
theme="default"
|
||||
variant="text">
|
||||
<SVGIcons
|
||||
alt="Copy"
|
||||
icon={Icons.COPY}
|
||||
width="16px"
|
||||
/>
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
<Button
|
||||
className="tw-h-8 tw-ml-4"
|
||||
data-testid="clear-secret"
|
||||
size="custom"
|
||||
theme="default"
|
||||
variant="text"
|
||||
onClick={resetSecret}>
|
||||
<SVGIcons
|
||||
alt="Delete"
|
||||
icon={Icons.DELETE}
|
||||
width="16px"
|
||||
/>
|
||||
</Button>
|
||||
</>
|
||||
) : null}
|
||||
<CopyToClipboard
|
||||
text={secretKey}
|
||||
onCopy={() => setCopiedSecret(true)}>
|
||||
<Button
|
||||
className="tw-h-8 tw-ml-4"
|
||||
data-testid="copy-secret"
|
||||
size="custom"
|
||||
theme="default"
|
||||
variant="text">
|
||||
<SVGIcons alt="Copy" icon={Icons.COPY} width="16px" />
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
</>
|
||||
) : data.secretKey ? (
|
||||
<div className="tw-flex tw-items-center">
|
||||
<input
|
||||
readOnly
|
||||
className="tw-form-inputs tw-px-3 tw-py-1"
|
||||
data-testid="secret-key"
|
||||
id="secret-key"
|
||||
name="secret-key"
|
||||
placeholder="secret key"
|
||||
type="text"
|
||||
value={secretKey}
|
||||
/>
|
||||
<CopyToClipboard
|
||||
text={secretKey}
|
||||
onCopy={() => setCopiedSecret(true)}>
|
||||
<Button
|
||||
className="tw-h-8 tw-ml-4"
|
||||
data-testid="copy-secret"
|
||||
size="custom"
|
||||
theme="default"
|
||||
variant="text">
|
||||
<SVGIcons alt="Copy" icon={Icons.COPY} width="16px" />
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
) : null
|
||||
) : null}
|
||||
{copiedSecret && validMsg('Copied to clipboard.')}
|
||||
{copiedSecret && validMsg('Copied to the clipboard.')}
|
||||
</Field>
|
||||
</>
|
||||
) : null}
|
||||
<Field>
|
||||
<div className="tw-flex tw-justify-end">
|
||||
<Button
|
||||
data-testid="cancel-webhook"
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="text"
|
||||
onClick={onCancel}>
|
||||
Discard
|
||||
</Button>
|
||||
{saveState === 'waiting' ? (
|
||||
{data && mode === 'edit' ? (
|
||||
<div className="tw-flex tw-justify-between">
|
||||
<Button
|
||||
disabled
|
||||
className="tw-w-16 tw-h-10 disabled:tw-opacity-100"
|
||||
data-testid="cancel-webhook"
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="contained">
|
||||
<Loader size="small" type="white" />
|
||||
variant="outlined"
|
||||
onClick={onCancel}>
|
||||
<i className="fas fa-arrow-left tw-text-sm tw-align-middle tw-pr-1.5" />{' '}
|
||||
<span>Back</span>
|
||||
</Button>
|
||||
) : saveState === 'success' ? (
|
||||
<div className="tw-flex tw-justify-end">
|
||||
{getDeleteButton()}
|
||||
{getSaveButton()}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="tw-flex tw-justify-end">
|
||||
<Button
|
||||
disabled
|
||||
className="tw-w-16 tw-h-10 disabled:tw-opacity-100"
|
||||
data-testid="cancel-webhook"
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="contained">
|
||||
<i aria-hidden="true" className="fa fa-check" />
|
||||
variant="text"
|
||||
onClick={onCancel}>
|
||||
Discard
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
className="tw-w-16 tw-h-10"
|
||||
data-testid="save-webhook"
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="contained"
|
||||
onClick={handleSave}>
|
||||
Save
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{getSaveButton()}
|
||||
</div>
|
||||
)}
|
||||
</Field>
|
||||
{data && isDelete && (
|
||||
<ConfirmationModal
|
||||
bodyText={`You want to delete webhook ${data.name} permanently? This action cannot be reverted.`}
|
||||
cancelText="Discard"
|
||||
confirmButtonCss="tw-bg-error hover:tw-bg-error focus:tw-bg-error"
|
||||
confirmText="Delete"
|
||||
header="Are you sure?"
|
||||
onCancel={() => setIsDelete(false)}
|
||||
onConfirm={handleDelete}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</PageLayout>
|
||||
);
|
||||
|
||||
@ -18,7 +18,6 @@ export interface WebhooksProps {
|
||||
data: Array<Webhook>;
|
||||
paging: Paging;
|
||||
onAddWebhook: () => void;
|
||||
onDeleteWebhook: (id: string) => void;
|
||||
onEditWebhook: (name: string) => void;
|
||||
onClickWebhook: (name: string) => void;
|
||||
onPageChange: (type: string) => void;
|
||||
}
|
||||
|
||||
@ -13,27 +13,27 @@
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { isNil, startCase } from 'lodash';
|
||||
import React, { FunctionComponent, useState } from 'react';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import { TITLE_FOR_NON_ADMIN_ACTION } from '../../constants/constants';
|
||||
import { Status, Webhook } from '../../generated/entity/events/webhook';
|
||||
import { Status } from '../../generated/entity/events/webhook';
|
||||
import { useAuth } from '../../hooks/authHooks';
|
||||
import { getDocButton } from '../../utils/CommonUtils';
|
||||
import { Button } from '../buttons/Button/Button';
|
||||
import ErrorPlaceHolder from '../common/error-with-placeholder/ErrorPlaceHolder';
|
||||
import NextPrevious from '../common/next-previous/NextPrevious';
|
||||
import NonAdminAction from '../common/non-admin-action/NonAdminAction';
|
||||
import WebhookDataCard from '../common/webhook-data-card/WebhookDataCard';
|
||||
import PageLayout from '../containers/PageLayout';
|
||||
import ConfirmationModal from '../Modals/ConfirmationModal/ConfirmationModal';
|
||||
import { WebhooksProps } from './Webhooks.interface';
|
||||
|
||||
const statuses = [
|
||||
{
|
||||
name: startCase(Status.NotStarted),
|
||||
value: Status.NotStarted,
|
||||
name: startCase(Status.Disabled),
|
||||
value: Status.Disabled,
|
||||
},
|
||||
{
|
||||
name: startCase(Status.Started),
|
||||
value: Status.Started,
|
||||
name: startCase(Status.Active),
|
||||
value: Status.Active,
|
||||
},
|
||||
{
|
||||
name: startCase(Status.Failed),
|
||||
@ -53,19 +53,10 @@ const Webhooks: FunctionComponent<WebhooksProps> = ({
|
||||
data = [],
|
||||
paging,
|
||||
onAddWebhook,
|
||||
onDeleteWebhook,
|
||||
onEditWebhook,
|
||||
onClickWebhook,
|
||||
onPageChange,
|
||||
}: WebhooksProps) => {
|
||||
const { isAuthDisabled, isAdminUser } = useAuth();
|
||||
const [deleteData, setDeleteData] = useState<Webhook>();
|
||||
|
||||
const handleDelete = () => {
|
||||
if (deleteData) {
|
||||
onDeleteWebhook(deleteData.id);
|
||||
}
|
||||
setDeleteData(undefined);
|
||||
};
|
||||
|
||||
const fetchLeftPanel = () => {
|
||||
return (
|
||||
@ -117,7 +108,7 @@ const Webhooks: FunctionComponent<WebhooksProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
return data.length ? (
|
||||
<PageLayout leftPanel={fetchLeftPanel()} rightPanel={fetchRightPanel()}>
|
||||
<div className="">
|
||||
<div className="tw-flex tw-justify-end tw-items-center">
|
||||
@ -142,31 +133,36 @@ const Webhooks: FunctionComponent<WebhooksProps> = ({
|
||||
endpoint={webhook.endpoint}
|
||||
name={webhook.name}
|
||||
status={webhook.status}
|
||||
onDelete={() => {
|
||||
setDeleteData(webhook);
|
||||
}}
|
||||
onEdit={() => {
|
||||
onEditWebhook(webhook.name);
|
||||
}}
|
||||
onClick={onClickWebhook}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
{Boolean(!isNil(paging.after) || !isNil(paging.before)) && (
|
||||
<NextPrevious paging={paging} pagingHandler={onPageChange} />
|
||||
)}
|
||||
{deleteData && (
|
||||
<ConfirmationModal
|
||||
bodyText={`You want to delete webhook ${deleteData.name} permanently? This action cannot be reverted.`}
|
||||
cancelText="Discard"
|
||||
confirmButtonCss="tw-bg-error hover:tw-bg-error focus:tw-bg-error"
|
||||
confirmText="Delete"
|
||||
header="Are you sure?"
|
||||
onCancel={() => setDeleteData(undefined)}
|
||||
onConfirm={handleDelete}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</PageLayout>
|
||||
) : (
|
||||
<PageLayout>
|
||||
<ErrorPlaceHolder>
|
||||
<p className="tw-text-center">No webhooks found</p>
|
||||
<p className="tw-text-center">
|
||||
<NonAdminAction position="bottom" title={TITLE_FOR_NON_ADMIN_ACTION}>
|
||||
<Button
|
||||
className={classNames('tw-h-8 tw-rounded tw-my-3', {
|
||||
'tw-opacity-40': !isAdminUser && !isAuthDisabled,
|
||||
})}
|
||||
data-testid="add-webhook-button"
|
||||
size="small"
|
||||
theme="primary"
|
||||
variant="contained"
|
||||
onClick={onAddWebhook}>
|
||||
Add New Webhook
|
||||
</Button>
|
||||
</NonAdminAction>
|
||||
</p>
|
||||
</ErrorPlaceHolder>
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -29,10 +29,11 @@ type EditorContentRef = {
|
||||
|
||||
type Props = {
|
||||
value: string;
|
||||
readonly?: boolean;
|
||||
};
|
||||
|
||||
const MarkdownWithPreview = forwardRef<editorRef, Props>(
|
||||
({ value }: Props, ref) => {
|
||||
({ value, readonly }: Props, ref) => {
|
||||
const [activeTab, setActiveTab] = useState<number>(1);
|
||||
const [preview, setPreview] = useState<string>('');
|
||||
const [initValue, setInitValue] = useState<string>(value ?? '');
|
||||
@ -106,6 +107,7 @@ const MarkdownWithPreview = forwardRef<editorRef, Props>(
|
||||
<RichTextEditor
|
||||
format={isValidJSONString(initValue) ? 'json' : 'markdown'}
|
||||
initvalue={initValue}
|
||||
readonly={readonly}
|
||||
ref={editorRef}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -12,11 +12,8 @@
|
||||
*/
|
||||
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import { TITLE_FOR_NON_ADMIN_ACTION } from '../../../constants/constants';
|
||||
import { Status } from '../../../generated/entity/events/webhook';
|
||||
import { stringToHTML } from '../../../utils/StringsUtils';
|
||||
import SVGIcons, { Icons } from '../../../utils/SvgUtils';
|
||||
import NonAdminAction from '../non-admin-action/NonAdminAction';
|
||||
import WebhookDataCardBody from './WebhookDataCardBody';
|
||||
|
||||
type Props = {
|
||||
@ -24,18 +21,20 @@ type Props = {
|
||||
description?: string;
|
||||
endpoint: string;
|
||||
status?: Status;
|
||||
onDelete: () => void;
|
||||
onEdit: () => void;
|
||||
onClick?: (name: string) => void;
|
||||
};
|
||||
|
||||
const WebhookDataCard: FunctionComponent<Props> = ({
|
||||
name,
|
||||
description,
|
||||
endpoint,
|
||||
status = Status.NotStarted,
|
||||
onDelete,
|
||||
onEdit,
|
||||
status = Status.Disabled,
|
||||
onClick,
|
||||
}: Props) => {
|
||||
const handleLinkClick = () => {
|
||||
onClick?.(name);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="tw-bg-white tw-p-3 tw-border tw-border-main tw-rounded-md"
|
||||
@ -43,36 +42,13 @@ const WebhookDataCard: FunctionComponent<Props> = ({
|
||||
<div>
|
||||
<div className="tw-flex tw-items-center">
|
||||
<h6 className="tw-flex tw-items-center tw-m-0 tw-heading">
|
||||
<span className="tw-text-grey-body tw-font-medium">
|
||||
<button
|
||||
className="tw-text-grey-body tw-font-medium"
|
||||
data-testid="webhook-link"
|
||||
onClick={handleLinkClick}>
|
||||
{stringToHTML(name)}
|
||||
</span>
|
||||
</button>
|
||||
</h6>
|
||||
<div className="tw-flex tw-flex-auto tw-justify-end">
|
||||
<NonAdminAction position="top" title={TITLE_FOR_NON_ADMIN_ACTION}>
|
||||
<button
|
||||
className="focus:tw-outline-none tw-ml-2"
|
||||
data-testid={`edit-webhook-${name}`}
|
||||
onClick={onEdit}>
|
||||
<SVGIcons
|
||||
alt="edit"
|
||||
icon={Icons.EDIT}
|
||||
title="Edit"
|
||||
width="12px"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
className="focus:tw-outline-none tw-ml-2"
|
||||
data-testid={`delete-webhook-${name}`}
|
||||
onClick={onDelete}>
|
||||
<SVGIcons
|
||||
alt="delete"
|
||||
icon={Icons.DELETE}
|
||||
title="Delete"
|
||||
width="12px"
|
||||
/>
|
||||
</button>
|
||||
</NonAdminAction>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="tw-pt-3">
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { startCase } from 'lodash';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import RichTextEditorPreviewer from '../rich-text-editor/RichTextEditorPreviewer';
|
||||
@ -28,8 +29,16 @@ const WebhookDataCardBody: FunctionComponent<Props> = ({
|
||||
}: Props) => {
|
||||
return (
|
||||
<div data-testid="card-body">
|
||||
<div className="tw-mb-3">
|
||||
<span>{startCase(status)}</span>
|
||||
<div className="tw-mb-3 tw-flex">
|
||||
<span className="tw-flex tw-items-center">
|
||||
<div
|
||||
className={classNames(
|
||||
'tw-w-3 tw-h-3 tw-rounded-full',
|
||||
`tw-bg-${status}`
|
||||
)}
|
||||
/>
|
||||
<span className="tw-ml-1">{startCase(status)}</span>
|
||||
</span>
|
||||
<span className="tw-mx-1.5 tw-inline-block tw-text-gray-400">|</span>
|
||||
<span>{endpoint}</span>
|
||||
</div>
|
||||
|
||||
@ -20,6 +20,7 @@ const CheckBoxDropDownList = ({
|
||||
setIsOpen,
|
||||
onSelect,
|
||||
selectedItems,
|
||||
disabledItems,
|
||||
}: DropDownListProp) => {
|
||||
const { isAuthDisabled, isAdminUser } = useAuth();
|
||||
|
||||
@ -37,11 +38,11 @@ const CheckBoxDropDownList = ({
|
||||
tw-right-0 tw-w-full tw-mt-1 tw-shadow-lg tw-border tw-border-main
|
||||
tw-bg-white tw-rounded focus:tw-outline-none"
|
||||
role="menu">
|
||||
<div className="py-1" role="none">
|
||||
<div className="tw-py-1" role="none">
|
||||
{dropDownList.map((item: DropDownListItem, index: number) =>
|
||||
!item.isAdminOnly || isAuthDisabled || isAdminUser ? (
|
||||
<div
|
||||
className="tw-cursor-pointer"
|
||||
className="tw-cursor-pointer tw-py-1"
|
||||
key={index}
|
||||
onClick={(e) => onSelect && onSelect(e, item.value as string)}>
|
||||
<input
|
||||
@ -49,6 +50,9 @@ const CheckBoxDropDownList = ({
|
||||
selectedItems?.includes(item.value as string)
|
||||
)}
|
||||
className="tw-ml-3 tw-mr-2 tw-align-middle custom-checkbox"
|
||||
disabled={Boolean(
|
||||
disabledItems?.includes(item.value as string)
|
||||
)}
|
||||
type="checkbox"
|
||||
onChange={() => {
|
||||
return;
|
||||
|
||||
@ -28,6 +28,8 @@ const DropDown: React.FC<DropDownProp> = ({
|
||||
dropDownList,
|
||||
onSelect,
|
||||
selectedItems,
|
||||
disabledItems,
|
||||
hiddenItems = [],
|
||||
isDropDownIconVisible = true,
|
||||
isLableVisible = true,
|
||||
}: DropDownProp) => {
|
||||
@ -50,6 +52,7 @@ const DropDown: React.FC<DropDownProp> = ({
|
||||
case DropDownType.CHECKBOX:
|
||||
return (
|
||||
<CheckBoxDropDownList
|
||||
disabledItems={disabledItems}
|
||||
dropDownList={dropDownList}
|
||||
selectedItems={selectedItems}
|
||||
setIsOpen={setIsOpen}
|
||||
@ -92,7 +95,11 @@ const DropDown: React.FC<DropDownProp> = ({
|
||||
) : (
|
||||
<span className="tw-flex tw-flex-wrap tw--my-0.5">
|
||||
{dropDownList.map((item: DropDownListItem) => {
|
||||
if (selectedItems?.includes(item.value as string)) {
|
||||
if (
|
||||
selectedItems
|
||||
?.filter((item) => !hiddenItems.includes(item))
|
||||
.includes(item.value as string)
|
||||
) {
|
||||
return (
|
||||
<p
|
||||
className={classNames(
|
||||
|
||||
@ -43,6 +43,8 @@ export type DropDownListProp = {
|
||||
listGroups?: Array<string>;
|
||||
searchString?: string;
|
||||
selectedItems?: Array<string>;
|
||||
disabledItems?: Array<string>;
|
||||
hiddenItems?: Array<string>;
|
||||
showSearchBar?: boolean;
|
||||
value?: string;
|
||||
onSelect?: (
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2021 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const WILD_CARD_CHAR = '*';
|
||||
17
openmetadata-ui/src/main/resources/ui/src/enums/form.enum.ts
Normal file
17
openmetadata-ui/src/main/resources/ui/src/enums/form.enum.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2021 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export enum FormSubmitType {
|
||||
ADD = 'add',
|
||||
EDIT = 'edit',
|
||||
}
|
||||
@ -32,6 +32,10 @@ export interface Webhook {
|
||||
* Description of the application.
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* Display Name that identifies this webhook.
|
||||
*/
|
||||
displayName?: string;
|
||||
/**
|
||||
* When set to `true`, the webhook event notification is enabled. Set it to `false` to
|
||||
* disable the subscription. (Default `true`).
|
||||
@ -67,8 +71,8 @@ export interface Webhook {
|
||||
*/
|
||||
secretKey?: string;
|
||||
/**
|
||||
* Status is `notStarted`, when webhook was created with `enabled` set to false and it never
|
||||
* started publishing events. Status is `started` when webhook is normally functioning and
|
||||
* Status is `disabled`, when webhook was created with `enabled` set to false and it never
|
||||
* started publishing events. Status is `active` when webhook is normally functioning and
|
||||
* 200 OK response was received for callback notification. Status is `failed` on bad
|
||||
* callback URL, connection failures, `1xx`, and `3xx` response was received for callback
|
||||
* notification. Status is `awaitingRetry` when previous attempt at callback timed out or
|
||||
@ -187,17 +191,17 @@ export interface FailureDetails {
|
||||
}
|
||||
|
||||
/**
|
||||
* Status is `notStarted`, when webhook was created with `enabled` set to false and it never
|
||||
* started publishing events. Status is `started` when webhook is normally functioning and
|
||||
* Status is `disabled`, when webhook was created with `enabled` set to false and it never
|
||||
* started publishing events. Status is `active` when webhook is normally functioning and
|
||||
* 200 OK response was received for callback notification. Status is `failed` on bad
|
||||
* callback URL, connection failures, `1xx`, and `3xx` response was received for callback
|
||||
* notification. Status is `awaitingRetry` when previous attempt at callback timed out or
|
||||
* received `4xx`, `5xx` response. Status is `retryLimitReached` after all retries fail.
|
||||
*/
|
||||
export enum Status {
|
||||
Active = 'active',
|
||||
AwaitingRetry = 'awaitingRetry',
|
||||
Disabled = 'disabled',
|
||||
Failed = 'failed',
|
||||
NotStarted = 'notStarted',
|
||||
RetryLimitReached = 'retryLimitReached',
|
||||
Started = 'started',
|
||||
}
|
||||
|
||||
@ -19,10 +19,13 @@ import { addWebhook } from '../../axiosAPIs/webhookAPI';
|
||||
import AddWebhook from '../../components/AddWebhook/AddWebhook';
|
||||
import PageContainerV1 from '../../components/containers/PageContainerV1';
|
||||
import { ROUTES } from '../../constants/constants';
|
||||
import { FormSubmitType } from '../../enums/form.enum';
|
||||
import { CreateWebhook } from '../../generated/api/events/createWebhook';
|
||||
import { useAuth } from '../../hooks/authHooks';
|
||||
import useToastContext from '../../hooks/useToastContext';
|
||||
|
||||
const AddWebhookPage: FunctionComponent = () => {
|
||||
const { isAuthDisabled, isAdminUser } = useAuth();
|
||||
const history = useHistory();
|
||||
const showToast = useToastContext();
|
||||
const [status, setStatus] = useState<LoadingState>('initial');
|
||||
@ -57,7 +60,9 @@ const AddWebhookPage: FunctionComponent = () => {
|
||||
return (
|
||||
<PageContainerV1>
|
||||
<AddWebhook
|
||||
allowAccess={isAdminUser || isAuthDisabled}
|
||||
header="Add Webhook"
|
||||
mode={FormSubmitType.ADD}
|
||||
saveState={status}
|
||||
onCancel={handleCancel}
|
||||
onSave={handleSave}
|
||||
|
||||
@ -15,22 +15,30 @@ import { AxiosError } from 'axios';
|
||||
import { LoadingState } from 'Models';
|
||||
import React, { FunctionComponent, useEffect, useState } from 'react';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { getWebhookByName, updateWebhook } from '../../axiosAPIs/webhookAPI';
|
||||
import {
|
||||
deleteWebhook,
|
||||
getWebhookByName,
|
||||
updateWebhook,
|
||||
} from '../../axiosAPIs/webhookAPI';
|
||||
import AddWebhook from '../../components/AddWebhook/AddWebhook';
|
||||
import PageContainerV1 from '../../components/containers/PageContainerV1';
|
||||
import Loader from '../../components/Loader/Loader';
|
||||
import { ROUTES } from '../../constants/constants';
|
||||
import { FormSubmitType } from '../../enums/form.enum';
|
||||
import { CreateWebhook } from '../../generated/api/events/createWebhook';
|
||||
import { Webhook } from '../../generated/entity/events/webhook';
|
||||
import { useAuth } from '../../hooks/authHooks';
|
||||
import useToastContext from '../../hooks/useToastContext';
|
||||
|
||||
const EditWebhookPage: FunctionComponent = () => {
|
||||
const { webhookName } = useParams<{ [key: string]: string }>();
|
||||
const { isAuthDisabled, isAdminUser } = useAuth();
|
||||
const history = useHistory();
|
||||
const showToast = useToastContext();
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [webhookData, setWebhookData] = useState<Webhook>();
|
||||
const [status, setStatus] = useState<LoadingState>('initial');
|
||||
const [deleteStatus, setDeleteStatus] = useState<LoadingState>('initial');
|
||||
|
||||
const fetchWebhook = () => {
|
||||
setIsLoading(true);
|
||||
@ -75,6 +83,22 @@ const EditWebhookPage: FunctionComponent = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (id: string) => {
|
||||
setDeleteStatus('waiting');
|
||||
deleteWebhook(id)
|
||||
.then(() => {
|
||||
setDeleteStatus('initial');
|
||||
goToWebhooks();
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
showToast({
|
||||
variant: 'error',
|
||||
body: err.message || 'Something went wrong!',
|
||||
});
|
||||
setDeleteStatus('initial');
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchWebhook();
|
||||
}, []);
|
||||
@ -83,10 +107,14 @@ const EditWebhookPage: FunctionComponent = () => {
|
||||
<PageContainerV1>
|
||||
{!isLoading ? (
|
||||
<AddWebhook
|
||||
allowAccess={isAdminUser || isAuthDisabled}
|
||||
data={webhookData}
|
||||
deleteState={deleteStatus}
|
||||
header="Edit Webhook"
|
||||
mode={FormSubmitType.EDIT}
|
||||
saveState={status}
|
||||
onCancel={handleCancel}
|
||||
onDelete={handleDelete}
|
||||
onSave={handleSave}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@ -15,7 +15,7 @@ import { AxiosError } from 'axios';
|
||||
import { Paging } from 'Models';
|
||||
import React, { FunctionComponent, useEffect, useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { deleteWebhook, getWebhooks } from '../../axiosAPIs/webhookAPI';
|
||||
import { getWebhooks } from '../../axiosAPIs/webhookAPI';
|
||||
import PageContainerV1 from '../../components/containers/PageContainerV1';
|
||||
import Loader from '../../components/Loader/Loader';
|
||||
import Webhooks from '../../components/Webhooks/Webhooks';
|
||||
@ -68,24 +68,10 @@ const WebhooksPage: FunctionComponent = () => {
|
||||
history.push(ROUTES.ADD_WEBHOOK);
|
||||
};
|
||||
|
||||
const handleEditWebhook = (name: string) => {
|
||||
const handleClickWebhook = (name: string) => {
|
||||
history.push(getEditWebhookPath(name));
|
||||
};
|
||||
|
||||
const handleDeleteWebhook = (id: string) => {
|
||||
setIsLoading(true);
|
||||
deleteWebhook(id)
|
||||
.then(() => {
|
||||
fetchData();
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
showToast({
|
||||
variant: 'error',
|
||||
body: err.message || 'Something went wrong!',
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
@ -97,8 +83,7 @@ const WebhooksPage: FunctionComponent = () => {
|
||||
data={data}
|
||||
paging={paging}
|
||||
onAddWebhook={handleAddWebhook}
|
||||
onDeleteWebhook={handleDeleteWebhook}
|
||||
onEditWebhook={handleEditWebhook}
|
||||
onClickWebhook={handleClickWebhook}
|
||||
onPageChange={handlePageChange}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@ -91,10 +91,10 @@ const AuthenticatedAppRouter: FunctionComponent = () => {
|
||||
/>
|
||||
<Route exact component={EntityVersionPage} path={ROUTES.ENTITY_VERSION} />
|
||||
<Route exact component={WebhooksPage} path={ROUTES.WEBHOOKS} />
|
||||
<Route exact component={AddWebhookPage} path={ROUTES.ADD_WEBHOOK} />
|
||||
<Route exact component={EditWebhookPage} path={ROUTES.EDIT_WEBHOOK} />
|
||||
{isAuthDisabled || isAdminUser ? (
|
||||
<>
|
||||
<Route exact component={AddWebhookPage} path={ROUTES.ADD_WEBHOOK} />
|
||||
<Route exact component={RolesPage} path={ROUTES.ROLES} />
|
||||
<Route exact component={UserListPage} path={ROUTES.USER_LIST} />
|
||||
</>
|
||||
|
||||
@ -182,6 +182,9 @@
|
||||
@apply tw-w-full tw-text-grey-body tw-rounded tw-border tw-border-main
|
||||
focus:tw-outline-none focus:tw-border-focus hover:tw-border-hover;
|
||||
}
|
||||
input:disabled {
|
||||
@apply tw-cursor-not-allowed;
|
||||
}
|
||||
|
||||
/* Dropdown CSS start */
|
||||
.dropdown-list {
|
||||
|
||||
@ -121,12 +121,12 @@ module.exports = {
|
||||
notStarted: ideal,
|
||||
started: success,
|
||||
failed: error,
|
||||
awaitingRetry: info,
|
||||
awaitingRetry: success,
|
||||
retryLimitReached: warning,
|
||||
'notStarted-lite': idealBG,
|
||||
'started-lite': successBG,
|
||||
'failed-lite': errorBG,
|
||||
'awaitingRetry-lite': infoBG,
|
||||
'awaitingRetry-lite': successBG,
|
||||
'retryLimitReached-lite': warningBG,
|
||||
// Webhook statuses end
|
||||
separator: mainSeparator,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user