From 8e655daa717a47bfd3a44273ba8245ca77def999 Mon Sep 17 00:00:00 2001 From: anik-ghosh-au7 Date: Thu, 14 Jul 2022 23:41:44 +0530 Subject: [PATCH 01/17] update: webhooks --- dashboard/package-lock.json | 12 +- dashboard/src/components/AddWebhookModal.tsx | 401 +++++++++++++++++++ dashboard/src/components/Menu.tsx | 2 + dashboard/src/graphql/mutation/index.ts | 8 + dashboard/src/pages/Webhooks.tsx | 18 + dashboard/src/routes/index.tsx | 42 +- dashboard/src/utils/index.ts | 5 + 7 files changed, 460 insertions(+), 28 deletions(-) create mode 100644 dashboard/src/components/AddWebhookModal.tsx create mode 100644 dashboard/src/pages/Webhooks.tsx diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json index c04cac0..41d31f9 100644 --- a/dashboard/package-lock.json +++ b/dashboard/package-lock.json @@ -2529,8 +2529,7 @@ "@chakra-ui/css-reset": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@chakra-ui/css-reset/-/css-reset-1.1.1.tgz", - "integrity": "sha512-+KNNHL4OWqeKia5SL858K3Qbd8WxMij9mWIilBzLD4j2KFrl/+aWFw8syMKth3NmgIibrjsljo+PU3fy2o50dg==", - "requires": {} + "integrity": "sha512-+KNNHL4OWqeKia5SL858K3Qbd8WxMij9mWIilBzLD4j2KFrl/+aWFw8syMKth3NmgIibrjsljo+PU3fy2o50dg==" }, "@chakra-ui/descendant": { "version": "2.1.1", @@ -3134,8 +3133,7 @@ "@graphql-typed-document-node/core": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz", - "integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==", - "requires": {} + "integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==" }, "@popperjs/core": { "version": "2.11.0", @@ -3845,8 +3843,7 @@ "react-icons": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.3.1.tgz", - "integrity": "sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ==", - "requires": {} + "integrity": "sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ==" }, "react-is": { "version": "16.13.1", @@ -4032,8 +4029,7 @@ "use-callback-ref": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.2.5.tgz", - "integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==", - "requires": {} + "integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==" }, "use-sidecar": { "version": "1.0.5", diff --git a/dashboard/src/components/AddWebhookModal.tsx b/dashboard/src/components/AddWebhookModal.tsx new file mode 100644 index 0000000..4ad99c6 --- /dev/null +++ b/dashboard/src/components/AddWebhookModal.tsx @@ -0,0 +1,401 @@ +import React, { useState } from 'react'; +import { + Button, + Center, + Flex, + Input, + InputGroup, + InputRightElement, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, + Switch, + Text, + useDisclosure, + useToast, +} from '@chakra-ui/react'; +import { FaMinusCircle, FaPlus } from 'react-icons/fa'; +import { useClient } from 'urql'; +import { ArrayInputOperations } from '../constants'; +import { validateEventName, validateURI } from '../utils'; + +enum INPUT_FIELDS { + EVENT_NAME = 'event_name', + ENDPOINT = 'endpoint', + ENABLED = 'enabled', + HEADERS = 'headers', +} + +enum HEADER_FIELDS { + KEY = 'key', + VALUE = 'value', +} + +interface headersDataType { + [HEADER_FIELDS.KEY]: string; + [HEADER_FIELDS.VALUE]: string; +} + +interface headersValidatorDataType { + [HEADER_FIELDS.KEY]: boolean; + [HEADER_FIELDS.VALUE]: boolean; +} + +const initHeadersData: headersDataType = { + [HEADER_FIELDS.KEY]: '', + [HEADER_FIELDS.VALUE]: '', +}; + +const initHeadersValidatorData: headersValidatorDataType = { + [HEADER_FIELDS.KEY]: true, + [HEADER_FIELDS.VALUE]: true, +}; + +interface webhookDataType { + [INPUT_FIELDS.EVENT_NAME]: string; + [INPUT_FIELDS.ENDPOINT]: string; + [INPUT_FIELDS.ENABLED]: boolean; + [INPUT_FIELDS.HEADERS]: headersDataType[]; +} + +interface validatorDataType { + [INPUT_FIELDS.EVENT_NAME]: boolean; + [INPUT_FIELDS.ENDPOINT]: boolean; + [INPUT_FIELDS.HEADERS]: headersValidatorDataType[]; +} + +const initWebhookData: webhookDataType = { + [INPUT_FIELDS.EVENT_NAME]: '', + [INPUT_FIELDS.ENDPOINT]: '', + [INPUT_FIELDS.ENABLED]: false, + [INPUT_FIELDS.HEADERS]: [{ ...initHeadersData }], +}; + +const initWebhookValidatorData: validatorDataType = { + [INPUT_FIELDS.EVENT_NAME]: true, + [INPUT_FIELDS.ENDPOINT]: true, + [INPUT_FIELDS.HEADERS]: [{ ...initHeadersValidatorData }], +}; + +const AddWebhookModal = () => { + const client = useClient(); + const toast = useToast(); + const { isOpen, onOpen, onClose } = useDisclosure(); + const [loading, setLoading] = useState(false); + const [webhook, setWebhook] = useState({ + ...initWebhookData, + }); + const [validator, setValidator] = useState({ + ...initWebhookValidatorData, + }); + const inputChangehandler = ( + inputType: string, + value: any, + headerInputType: string = HEADER_FIELDS.KEY, + headerIndex: number = 0 + ) => { + switch (inputType) { + case INPUT_FIELDS.EVENT_NAME: + setWebhook({ ...webhook, [inputType]: value }); + setValidator({ + ...validator, + [INPUT_FIELDS.EVENT_NAME]: validateEventName(value), + }); + break; + case INPUT_FIELDS.ENDPOINT: + setWebhook({ ...webhook, [inputType]: value }); + setValidator({ + ...validator, + [INPUT_FIELDS.ENDPOINT]: validateURI(value), + }); + break; + case INPUT_FIELDS.ENABLED: + setWebhook({ ...webhook, [inputType]: value }); + break; + case INPUT_FIELDS.HEADERS: + const updatedHeaders: any = [...webhook[INPUT_FIELDS.HEADERS]]; + const updatedHeadersValidatorData: any = [ + ...validator[INPUT_FIELDS.HEADERS], + ]; + const otherHeaderInputType = + headerInputType === HEADER_FIELDS.KEY + ? HEADER_FIELDS.VALUE + : HEADER_FIELDS.KEY; + updatedHeaders[headerIndex][headerInputType] = value; + updatedHeadersValidatorData[headerIndex][headerInputType] = + value.length > 0 + ? updatedHeaders[headerIndex][otherHeaderInputType].length > 0 + : updatedHeaders[headerIndex][otherHeaderInputType].length === 0; + updatedHeadersValidatorData[headerIndex][otherHeaderInputType] = + value.length > 0 + ? updatedHeaders[headerIndex][otherHeaderInputType].length > 0 + : updatedHeaders[headerIndex][otherHeaderInputType].length === 0; + setWebhook({ ...webhook, [inputType]: updatedHeaders }); + setValidator({ + ...validator, + [inputType]: updatedHeadersValidatorData, + }); + break; + default: + break; + } + }; + const updateHeadersHandler = (operation: string, index: number = 0) => { + switch (operation) { + case ArrayInputOperations.APPEND: + setWebhook({ + ...webhook, + [INPUT_FIELDS.HEADERS]: [ + ...(webhook?.[INPUT_FIELDS.HEADERS] || []), + { ...initHeadersData }, + ], + }); + setValidator({ + ...validator, + [INPUT_FIELDS.HEADERS]: [ + ...(validator?.[INPUT_FIELDS.HEADERS] || []), + { ...initHeadersValidatorData }, + ], + }); + break; + case ArrayInputOperations.REMOVE: + if (webhook?.[INPUT_FIELDS.HEADERS]?.length) { + const updatedHeaders = [...webhook[INPUT_FIELDS.HEADERS]]; + updatedHeaders.splice(index, 1); + setWebhook({ + ...webhook, + [INPUT_FIELDS.HEADERS]: updatedHeaders, + }); + } + if (validator?.[INPUT_FIELDS.HEADERS]?.length) { + const updatedHeadersData = [...validator[INPUT_FIELDS.HEADERS]]; + updatedHeadersData.splice(index, 1); + setValidator({ + ...validator, + [INPUT_FIELDS.HEADERS]: updatedHeadersData, + }); + } + break; + default: + break; + } + }; + return ( + <> + + + + + Add New Webhook + + + + + Event Name + + + + inputChangehandler( + INPUT_FIELDS.EVENT_NAME, + e.currentTarget.value + ) + } + /> + + + + + Endpoint + + + + inputChangehandler( + INPUT_FIELDS.ENDPOINT, + e.currentTarget.value + ) + } + /> + + + + + Enabled + + + Off + + + inputChangehandler( + INPUT_FIELDS.ENABLED, + !webhook[INPUT_FIELDS.ENABLED] + ) + } + /> + + On + + + + + Headers + + + + + + {webhook[INPUT_FIELDS.HEADERS]?.map((headerData, index) => ( + + + + inputChangehandler( + INPUT_FIELDS.HEADERS, + e.target.value, + HEADER_FIELDS.KEY, + index + ) + } + width="30%" + marginRight="2%" + /> +
+ : +
+ + inputChangehandler( + INPUT_FIELDS.HEADERS, + e.target.value, + HEADER_FIELDS.VALUE, + index + ) + } + width="65%" + /> + + + +
+
+ ))} +
+
+
+ + + +
+
+ + ); +}; + +export default AddWebhookModal; diff --git a/dashboard/src/components/Menu.tsx b/dashboard/src/components/Menu.tsx index 8593bb0..460e6c3 100644 --- a/dashboard/src/components/Menu.tsx +++ b/dashboard/src/components/Menu.tsx @@ -30,6 +30,7 @@ import { FiMenu, FiUsers, FiChevronDown, + FiAnchor, } from 'react-icons/fi'; import { BiCustomize } from 'react-icons/bi'; import { AiOutlineKey } from 'react-icons/ai'; @@ -111,6 +112,7 @@ const LinkItems: Array = [ ], }, { name: 'Users', icon: FiUsers, route: '/users' }, + { name: 'Webhooks', icon: FiAnchor, route: '/webhooks' }, ]; interface SidebarProps extends BoxProps { diff --git a/dashboard/src/graphql/mutation/index.ts b/dashboard/src/graphql/mutation/index.ts index 46c5fcb..36cea16 100644 --- a/dashboard/src/graphql/mutation/index.ts +++ b/dashboard/src/graphql/mutation/index.ts @@ -79,3 +79,11 @@ export const GenerateKeys = ` } } `; + +export const AddWebhook = ` + mutation addWebhook($params: AddWebhookRequest!) { + _add_webhook(params: $params) { + message + } + } +`; diff --git a/dashboard/src/pages/Webhooks.tsx b/dashboard/src/pages/Webhooks.tsx new file mode 100644 index 0000000..c0717ac --- /dev/null +++ b/dashboard/src/pages/Webhooks.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { Box, Flex, Text } from '@chakra-ui/react'; +import AddWebhookModal from '../components/AddWebhookModal'; + +const Webhooks = () => { + return ( + + + + Webhooks + + + + + ); +}; + +export default Webhooks; diff --git a/dashboard/src/routes/index.tsx b/dashboard/src/routes/index.tsx index f611e92..6ae73df 100644 --- a/dashboard/src/routes/index.tsx +++ b/dashboard/src/routes/index.tsx @@ -8,32 +8,34 @@ const Auth = lazy(() => import('../pages/Auth')); const Environment = lazy(() => import('../pages/Environment')); const Home = lazy(() => import('../pages/Home')); const Users = lazy(() => import('../pages/Users')); +const Webhooks = lazy(() => import('../pages/Webhooks')); export const AppRoutes = () => { const { isLoggedIn } = useAuthContext(); if (isLoggedIn) { return ( -
- }> - - - - - } - > - }> - } /> - } /> - - } /> - } /> - - - -
+
+ }> + + + + + } + > + }> + } /> + } /> + + } /> + } /> + } /> + + + +
); } return ( diff --git a/dashboard/src/utils/index.ts b/dashboard/src/utils/index.ts index eccc7a8..99e134a 100644 --- a/dashboard/src/utils/index.ts +++ b/dashboard/src/utils/index.ts @@ -86,3 +86,8 @@ export const validateURI = (uri: string) => { ? true : false; }; + +export const validateEventName = (name: string) => { + if (!name || name === '') return true; + return name.toLowerCase().match(/^.{4,}[.].{5,}$/) ? true : false; +}; From d837b1590a3e96c83a68dbc25c8674d4a3a978e8 Mon Sep 17 00:00:00 2001 From: anik-ghosh-au7 Date: Fri, 15 Jul 2022 12:20:51 +0530 Subject: [PATCH 02/17] update: webhooks --- dashboard/src/components/AddWebhookModal.tsx | 68 ++++++++++++++++---- 1 file changed, 57 insertions(+), 11 deletions(-) diff --git a/dashboard/src/components/AddWebhookModal.tsx b/dashboard/src/components/AddWebhookModal.tsx index 4ad99c6..11206e9 100644 --- a/dashboard/src/components/AddWebhookModal.tsx +++ b/dashboard/src/components/AddWebhookModal.tsx @@ -21,7 +21,12 @@ import { import { FaMinusCircle, FaPlus } from 'react-icons/fa'; import { useClient } from 'urql'; import { ArrayInputOperations } from '../constants'; -import { validateEventName, validateURI } from '../utils'; +import { + capitalizeFirstLetter, + validateEventName, + validateURI, +} from '../utils'; +import { AddWebhook } from '../graphql/mutation'; enum INPUT_FIELDS { EVENT_NAME = 'event_name', @@ -144,7 +149,7 @@ const AddWebhookModal = () => { break; } }; - const updateHeadersHandler = (operation: string, index: number = 0) => { + const updateHeaders = (operation: string, index: number = 0) => { switch (operation) { case ArrayInputOperations.APPEND: setWebhook({ @@ -184,6 +189,52 @@ const AddWebhookModal = () => { break; } }; + const validateData = () => { + return ( + !loading && + webhook[INPUT_FIELDS.EVENT_NAME].length > 0 && + webhook[INPUT_FIELDS.ENDPOINT].length > 0 && + validator[INPUT_FIELDS.EVENT_NAME] && + validator[INPUT_FIELDS.ENDPOINT] && + !validator[INPUT_FIELDS.HEADERS].some( + (headerData: headersValidatorDataType) => + !headerData.key || !headerData.value + ) + ); + }; + const saveData = async () => { + if (!validateData()) return; + let params: any = { + [INPUT_FIELDS.EVENT_NAME]: webhook[INPUT_FIELDS.EVENT_NAME], + [INPUT_FIELDS.ENDPOINT]: webhook[INPUT_FIELDS.ENDPOINT], + [INPUT_FIELDS.ENABLED]: webhook[INPUT_FIELDS.ENABLED], + }; + if (webhook[INPUT_FIELDS.HEADERS].length > 0) { + const headers = webhook[INPUT_FIELDS.HEADERS].reduce((acc, data) => { + return { ...acc, [data.key]: data.value }; + }, {}); + params[INPUT_FIELDS.HEADERS] = headers; + } + const res = await client.mutation(AddWebhook, { params }).toPromise(); + if (res.error) { + toast({ + title: capitalizeFirstLetter(res.error.message), + isClosable: true, + status: 'error', + position: 'bottom-right', + }); + return; + } else if (res.data?._add_webhook) { + toast({ + title: capitalizeFirstLetter(res.data?._add_webhook.message), + isClosable: true, + status: 'success', + position: 'bottom-right', + }); + setWebhook({ ...initWebhookData }); + onClose(); + } + }; return ( <> @@ -365,10 +414,7 @@ const AddWebhookModal = () => { variant="ghost" padding="0" onClick={() => - updateHeadersHandler( - ArrayInputOperations.REMOVE, - index - ) + updateHeaders(ArrayInputOperations.REMOVE, index) } > @@ -384,8 +430,8 @@ const AddWebhookModal = () => { - - - - ))} + onChange={(e) => + inputChangehandler( + WebhookInputDataFields.HEADERS, + e.target.value, + WebhookInputHeaderFields.KEY, + index + ) + } + width="30%" + marginRight="2%" + /> +
+ : +
+ + inputChangehandler( + WebhookInputDataFields.HEADERS, + e.target.value, + WebhookInputHeaderFields.VALUE, + index + ) + } + width="65%" + /> + + + + + + ) + )} diff --git a/dashboard/src/constants.ts b/dashboard/src/constants.ts index f74f1fc..f68aa8b 100644 --- a/dashboard/src/constants.ts +++ b/dashboard/src/constants.ts @@ -153,3 +153,18 @@ export const envSubViews = { ADMIN_SECRET: 'admin-secret', DB_CRED: 'db-cred', }; + +export enum WebhookInputDataFields { + ID = 'id', + EVENT_NAME = 'event_name', + ENDPOINT = 'endpoint', + ENABLED = 'enabled', + HEADERS = 'headers', +} + +export enum WebhookInputHeaderFields { + KEY = 'key', + VALUE = 'value', +} + +export const pageLimits: number[] = [5, 10, 15]; diff --git a/dashboard/src/graphql/queries/index.ts b/dashboard/src/graphql/queries/index.ts index 3f646a0..001be18 100644 --- a/dashboard/src/graphql/queries/index.ts +++ b/dashboard/src/graphql/queries/index.ts @@ -101,3 +101,23 @@ export const EmailVerificationQuery = ` } } `; + +export const WebhooksDataQuery = ` + query getWebhooksData { + _webhooks{ + webhooks{ + id + event_name + endpoint + enabled + headers + } + pagination{ + limit + page + offset + total + } + } + } +`; diff --git a/dashboard/src/pages/Webhooks.tsx b/dashboard/src/pages/Webhooks.tsx index c0717ac..fec0e74 100644 --- a/dashboard/src/pages/Webhooks.tsx +++ b/dashboard/src/pages/Webhooks.tsx @@ -1,8 +1,109 @@ -import React from 'react'; -import { Box, Flex, Text } from '@chakra-ui/react'; +import React, { useEffect, useState } from 'react'; +import { useClient } from 'urql'; +import { + Box, + Button, + Center, + Flex, + IconButton, + Menu, + MenuButton, + MenuItem, + MenuList, + NumberDecrementStepper, + NumberIncrementStepper, + NumberInput, + NumberInputField, + NumberInputStepper, + Select, + Spinner, + Table, + TableCaption, + Tag, + Tbody, + Td, + Text, + Th, + Thead, + Tooltip, + Tr, +} from '@chakra-ui/react'; +import { + FaAngleDoubleLeft, + FaAngleDoubleRight, + FaAngleDown, + FaAngleLeft, + FaAngleRight, + FaExclamationCircle, +} from 'react-icons/fa'; import AddWebhookModal from '../components/AddWebhookModal'; +import { pageLimits, WebhookInputDataFields } from '../constants'; +import { WebhooksDataQuery } from '../graphql/queries'; + +interface paginationPropTypes { + limit: number; + page: number; + offset: number; + total: number; + maxPages: number; +} + +interface webhookDataTypes { + [WebhookInputDataFields.ID]: string; + [WebhookInputDataFields.EVENT_NAME]: string; + [WebhookInputDataFields.ENDPOINT]: string; + [WebhookInputDataFields.ENABLED]: boolean; + [WebhookInputDataFields.HEADERS]?: Record; +} const Webhooks = () => { + const client = useClient(); + const [loading, setLoading] = useState(false); + const [webhookData, setWebhookData] = useState([]); + const [paginationProps, setPaginationProps] = useState({ + limit: 5, + page: 1, + offset: 0, + total: 0, + maxPages: 1, + }); + const getMaxPages = (pagination: paginationPropTypes) => { + const { limit, total } = pagination; + if (total > 1) { + return total % limit === 0 + ? total / limit + : parseInt(`${total / limit}`) + 1; + } else return 1; + }; + const fetchWebookData = async () => { + setLoading(true); + const res = await client.query(WebhooksDataQuery).toPromise(); + if (res.data?._webhooks) { + const { pagination, webhooks } = res.data?._webhooks; + const maxPages = getMaxPages(pagination); + if (webhooks?.length) { + setWebhookData(webhooks); + setPaginationProps({ ...paginationProps, ...pagination, maxPages }); + } else { + if (paginationProps.page !== 1) { + setPaginationProps({ + ...paginationProps, + ...pagination, + maxPages, + page: 1, + }); + } + } + } + setLoading(false); + }; + useEffect(() => { + fetchWebookData(); + }, []); + const paginationHandler = (value: Record) => { + setPaginationProps({ ...paginationProps, ...value }); + }; + console.log('webhookData ==>> ', webhookData); return ( @@ -11,6 +112,218 @@ const Webhooks = () => { + {!loading ? ( + webhookData.length ? ( + + + + + + + + + + + + {webhookData.map((webhook: webhookDataTypes, index: number) => ( + + + + + + + + ))} + + {(paginationProps.maxPages > 1 || paginationProps.total >= 5) && ( + + + + + + paginationHandler({ + page: 1, + }) + } + isDisabled={paginationProps.page <= 1} + mr={4} + icon={} + /> + + + + paginationHandler({ + page: paginationProps.page - 1, + }) + } + isDisabled={paginationProps.page <= 1} + icon={} + /> + + + + + Page{' '} + + {paginationProps.page} + {' '} + of{' '} + + {paginationProps.maxPages} + + + + Go to page:{' '} + + paginationHandler({ + page: parseInt(value), + }) + } + value={paginationProps.page} + > + + + + + + + + + + + + + paginationHandler({ + page: paginationProps.page + 1, + }) + } + isDisabled={ + paginationProps.page >= paginationProps.maxPages + } + icon={} + /> + + + + paginationHandler({ + page: paginationProps.maxPages, + }) + } + isDisabled={ + paginationProps.page >= paginationProps.maxPages + } + ml={4} + icon={} + /> + + + + + )} +
Event NameEndpointEnabledHeadersActions
+ {webhook[WebhookInputDataFields.EVENT_NAME]} + {webhook[WebhookInputDataFields.ENDPOINT]} + + {webhook[WebhookInputDataFields.ENABLED].toString()} + + + { + Object.keys(webhook[WebhookInputDataFields.HEADERS] || {}) + ?.length + } + + + + + + Menu + + + + + + {}}>Edit + {}}>Delete + {}}>View Logs + + +
+ ) : ( + +
+ +
+ + No Data + +
+ ) + ) : ( +
+ +
+ )}
); }; From 913c5c94fbfa35dade91cbd66ef5047d4e6e79b9 Mon Sep 17 00:00:00 2001 From: anik-ghosh-au7 Date: Sat, 16 Jul 2022 09:42:10 +0530 Subject: [PATCH 07/17] update: webhooks --- dashboard/src/pages/Webhooks.tsx | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/dashboard/src/pages/Webhooks.tsx b/dashboard/src/pages/Webhooks.tsx index fec0e74..ee3b760 100644 --- a/dashboard/src/pages/Webhooks.tsx +++ b/dashboard/src/pages/Webhooks.tsx @@ -148,10 +148,26 @@ const Webhooks = () => { - { - Object.keys(webhook[WebhookInputDataFields.HEADERS] || {}) - ?.length - } + +
+ + {Object.keys( + webhook[WebhookInputDataFields.HEADERS] || {} + )?.length.toString()} + +
+
From 301bde4da2f4e7e8700438fd88a0d2fa8ca01c69 Mon Sep 17 00:00:00 2001 From: anik-ghosh-au7 Date: Sat, 16 Jul 2022 09:53:29 +0530 Subject: [PATCH 08/17] update: webhooks --- dashboard/src/graphql/queries/index.ts | 4 ++-- dashboard/src/pages/Webhooks.tsx | 20 ++++++++++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/dashboard/src/graphql/queries/index.ts b/dashboard/src/graphql/queries/index.ts index 001be18..d22dcca 100644 --- a/dashboard/src/graphql/queries/index.ts +++ b/dashboard/src/graphql/queries/index.ts @@ -103,8 +103,8 @@ export const EmailVerificationQuery = ` `; export const WebhooksDataQuery = ` - query getWebhooksData { - _webhooks{ + query getWebhooksData($params: PaginatedInput!) { + _webhooks(params: $params){ webhooks{ id event_name diff --git a/dashboard/src/pages/Webhooks.tsx b/dashboard/src/pages/Webhooks.tsx index ee3b760..eaaffa2 100644 --- a/dashboard/src/pages/Webhooks.tsx +++ b/dashboard/src/pages/Webhooks.tsx @@ -77,7 +77,16 @@ const Webhooks = () => { }; const fetchWebookData = async () => { setLoading(true); - const res = await client.query(WebhooksDataQuery).toPromise(); + const res = await client + .query(WebhooksDataQuery, { + params: { + pagination: { + limit: paginationProps.limit, + page: paginationProps.page, + }, + }, + }) + .toPromise(); if (res.data?._webhooks) { const { pagination, webhooks } = res.data?._webhooks; const maxPages = getMaxPages(pagination); @@ -97,12 +106,15 @@ const Webhooks = () => { } setLoading(false); }; - useEffect(() => { - fetchWebookData(); - }, []); const paginationHandler = (value: Record) => { setPaginationProps({ ...paginationProps, ...value }); }; + useEffect(() => { + fetchWebookData(); + }, []); + React.useEffect(() => { + fetchWebookData(); + }, [paginationProps.page, paginationProps.limit]); console.log('webhookData ==>> ', webhookData); return ( From d3260f4f32be3b493b2f4804cb6f622c527e2d19 Mon Sep 17 00:00:00 2001 From: anik-ghosh-au7 Date: Sat, 16 Jul 2022 15:24:50 +0530 Subject: [PATCH 09/17] update: webhooks --- ...ebhookModal.tsx => UpdateWebhookModal.tsx} | 122 +++++++++++++++--- dashboard/src/constants.ts | 5 + dashboard/src/graphql/mutation/index.ts | 8 ++ dashboard/src/pages/Webhooks.tsx | 25 +++- 4 files changed, 135 insertions(+), 25 deletions(-) rename dashboard/src/components/{AddWebhookModal.tsx => UpdateWebhookModal.tsx} (80%) diff --git a/dashboard/src/components/AddWebhookModal.tsx b/dashboard/src/components/UpdateWebhookModal.tsx similarity index 80% rename from dashboard/src/components/AddWebhookModal.tsx rename to dashboard/src/components/UpdateWebhookModal.tsx index 4310681..80df822 100644 --- a/dashboard/src/components/AddWebhookModal.tsx +++ b/dashboard/src/components/UpdateWebhookModal.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { Button, Center, @@ -6,6 +6,7 @@ import { Input, InputGroup, InputRightElement, + MenuItem, Modal, ModalBody, ModalCloseButton, @@ -24,13 +25,14 @@ import { ArrayInputOperations, WebhookInputDataFields, WebhookInputHeaderFields, + UpdateWebhookModalViews, } from '../constants'; import { capitalizeFirstLetter, validateEventName, validateURI, } from '../utils'; -import { AddWebhook } from '../graphql/mutation'; +import { AddWebhook, EditWebhook } from '../graphql/mutation'; interface headersDataType { [WebhookInputHeaderFields.KEY]: string; @@ -42,6 +44,20 @@ interface headersValidatorDataType { [WebhookInputHeaderFields.VALUE]: boolean; } +interface selecetdWebhookDataTypes { + [WebhookInputDataFields.ID]: string; + [WebhookInputDataFields.EVENT_NAME]: string; + [WebhookInputDataFields.ENDPOINT]: string; + [WebhookInputDataFields.ENABLED]: boolean; + [WebhookInputDataFields.HEADERS]?: Record; +} + +interface UpdateWebhookModalInputPropTypes { + view: UpdateWebhookModalViews; + selectedWebhook?: selecetdWebhookDataTypes; + fetchWebookData: Function; +} + const initHeadersData: headersDataType = { [WebhookInputHeaderFields.KEY]: '', [WebhookInputHeaderFields.VALUE]: '', @@ -78,7 +94,11 @@ const initWebhookValidatorData: validatorDataType = { [WebhookInputDataFields.HEADERS]: [{ ...initHeadersValidatorData }], }; -const AddWebhookModal = () => { +const UpdateWebhookModal = ({ + view, + selectedWebhook, + fetchWebookData, +}: UpdateWebhookModalInputPropTypes) => { const client = useClient(); const toast = useToast(); const { isOpen, onOpen, onClose } = useDisclosure(); @@ -201,7 +221,13 @@ const AddWebhookModal = () => { const saveData = async () => { if (!validateData()) return; setLoading(true); - let { [WebhookInputDataFields.HEADERS]: _, ...params }: any = webhook; + let params: any = { + [WebhookInputDataFields.EVENT_NAME]: + webhook[WebhookInputDataFields.EVENT_NAME], + [WebhookInputDataFields.ENDPOINT]: + webhook[WebhookInputDataFields.ENDPOINT], + [WebhookInputDataFields.ENABLED]: webhook[WebhookInputDataFields.ENABLED], + }; if (webhook[WebhookInputDataFields.HEADERS].length) { const headers = webhook[WebhookInputDataFields.HEADERS].reduce( (acc, data) => { @@ -213,7 +239,22 @@ const AddWebhookModal = () => { params[WebhookInputDataFields.HEADERS] = headers; } } - const res = await client.mutation(AddWebhook, { params }).toPromise(); + let res: any = {}; + if ( + view === UpdateWebhookModalViews.Edit && + selectedWebhook?.[WebhookInputDataFields.ID] + ) { + res = await client + .mutation(EditWebhook, { + params: { + ...params, + id: selectedWebhook[WebhookInputDataFields.ID], + }, + }) + .toPromise(); + } else { + res = await client.mutation(AddWebhook, { params }).toPromise(); + } if (res.error) { toast({ title: capitalizeFirstLetter(res.error.message), @@ -223,9 +264,11 @@ const AddWebhookModal = () => { }); setLoading(false); return; - } else if (res.data?._add_webhook) { + } else if (res.data?._add_webhook || res.data?._update_webhook) { toast({ - title: capitalizeFirstLetter(res.data?._add_webhook.message), + title: capitalizeFirstLetter( + res.data?._add_webhook?.message || res.data?._update_webhook?.message + ), isClosable: true, status: 'success', position: 'bottom-right', @@ -236,25 +279,66 @@ const AddWebhookModal = () => { }); setValidator({ ...initWebhookValidatorData }); onClose(); + fetchWebookData(); } setLoading(false); }; + useEffect(() => { + if ( + view === UpdateWebhookModalViews.Edit && + selectedWebhook && + Object.keys(selectedWebhook || {}).length + ) { + const { headers, ...rest } = selectedWebhook; + const headerItems = Object.entries(headers || {}); + if (headerItems.length) { + let formattedHeadersData = headerItems.map((headerData) => { + return { + [WebhookInputHeaderFields.KEY]: headerData[0], + [WebhookInputHeaderFields.VALUE]: headerData[1], + }; + }); + setWebhook({ + ...rest, + [WebhookInputDataFields.HEADERS]: formattedHeadersData, + }); + setValidator({ + ...validator, + [WebhookInputDataFields.HEADERS]: new Array( + formattedHeadersData.length + ) + .fill({}) + .map(() => ({ ...initHeadersValidatorData })), + }); + } else { + setWebhook({ ...rest, [WebhookInputDataFields.HEADERS]: [] }); + } + } + }, [view]); return ( <> - + {view === UpdateWebhookModalViews.ADD ? ( + + ) : ( + Edit + )} - Add New Webhook + + {view === UpdateWebhookModalViews.ADD + ? 'Add New Webhook' + : 'Edit Webhook'} + { ); }; -export default AddWebhookModal; +export default UpdateWebhookModal; diff --git a/dashboard/src/constants.ts b/dashboard/src/constants.ts index f68aa8b..fb00802 100644 --- a/dashboard/src/constants.ts +++ b/dashboard/src/constants.ts @@ -167,4 +167,9 @@ export enum WebhookInputHeaderFields { VALUE = 'value', } +export enum UpdateWebhookModalViews { + ADD = 'add', + Edit = 'edit', +} + export const pageLimits: number[] = [5, 10, 15]; diff --git a/dashboard/src/graphql/mutation/index.ts b/dashboard/src/graphql/mutation/index.ts index 36cea16..ebb7d48 100644 --- a/dashboard/src/graphql/mutation/index.ts +++ b/dashboard/src/graphql/mutation/index.ts @@ -87,3 +87,11 @@ export const AddWebhook = ` } } `; + +export const EditWebhook = ` + mutation editWebhook($params: UpdateWebhookRequest!) { + _update_webhook(params: $params) { + message + } + } +`; diff --git a/dashboard/src/pages/Webhooks.tsx b/dashboard/src/pages/Webhooks.tsx index eaaffa2..5c81cb2 100644 --- a/dashboard/src/pages/Webhooks.tsx +++ b/dashboard/src/pages/Webhooks.tsx @@ -36,8 +36,12 @@ import { FaAngleRight, FaExclamationCircle, } from 'react-icons/fa'; -import AddWebhookModal from '../components/AddWebhookModal'; -import { pageLimits, WebhookInputDataFields } from '../constants'; +import UpdateWebhookModal from '../components/UpdateWebhookModal'; +import { + pageLimits, + WebhookInputDataFields, + UpdateWebhookModalViews, +} from '../constants'; import { WebhooksDataQuery } from '../graphql/queries'; interface paginationPropTypes { @@ -115,14 +119,16 @@ const Webhooks = () => { React.useEffect(() => { fetchWebookData(); }, [paginationProps.page, paginationProps.limit]); - console.log('webhookData ==>> ', webhookData); return ( Webhooks - + {!loading ? ( webhookData.length ? ( @@ -161,10 +167,13 @@ const Webhooks = () => {
{ - {}}>Edit + {}}>Delete {}}>View Logs From a69dd959927d4420ed7dc765a89f73f977b331f2 Mon Sep 17 00:00:00 2001 From: anik-ghosh-au7 Date: Sat, 16 Jul 2022 15:59:21 +0530 Subject: [PATCH 10/17] update: webhooks --- .../src/components/DeleteWebhookModal.tsx | 106 ++++++++++++++++++ dashboard/src/graphql/mutation/index.ts | 8 ++ dashboard/src/pages/Webhooks.tsx | 7 +- 3 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 dashboard/src/components/DeleteWebhookModal.tsx diff --git a/dashboard/src/components/DeleteWebhookModal.tsx b/dashboard/src/components/DeleteWebhookModal.tsx new file mode 100644 index 0000000..49e1443 --- /dev/null +++ b/dashboard/src/components/DeleteWebhookModal.tsx @@ -0,0 +1,106 @@ +import React from 'react'; +import { + Button, + Center, + Flex, + MenuItem, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, + useDisclosure, + Text, + useToast, +} from '@chakra-ui/react'; +import { useClient } from 'urql'; +import { FaRegTrashAlt } from 'react-icons/fa'; +import { DeleteWebhook } from '../graphql/mutation'; +import { capitalizeFirstLetter } from '../utils'; + +interface deleteWebhookModalInputPropTypes { + webhookId: string; + eventName: string; + fetchWebookData: Function; +} + +const DeleteWebhookModal = ({ + webhookId, + eventName, + fetchWebookData, +}: deleteWebhookModalInputPropTypes) => { + const client = useClient(); + const toast = useToast(); + const { isOpen, onOpen, onClose } = useDisclosure(); + + const deleteHandler = async () => { + const res = await client + .mutation(DeleteWebhook, { params: { id: webhookId } }) + .toPromise(); + if (res.error) { + toast({ + title: capitalizeFirstLetter(res.error.message), + isClosable: true, + status: 'error', + position: 'bottom-right', + }); + + return; + } else if (res.data?._delete_webhook) { + toast({ + title: capitalizeFirstLetter(res.data?._delete_webhook.message), + isClosable: true, + status: 'success', + position: 'bottom-right', + }); + } + onClose(); + fetchWebookData(); + }; + return ( + <> + Delete + + + + Delete Webhook + + + Are you sure? + + + Webhook for event {eventName} will be deleted + permanently! + + + + + + + + + + + ); +}; + +export default DeleteWebhookModal; diff --git a/dashboard/src/graphql/mutation/index.ts b/dashboard/src/graphql/mutation/index.ts index ebb7d48..ee7ade3 100644 --- a/dashboard/src/graphql/mutation/index.ts +++ b/dashboard/src/graphql/mutation/index.ts @@ -95,3 +95,11 @@ export const EditWebhook = ` } } `; + +export const DeleteWebhook = ` + mutation deleteWebhook($params: WebhookRequest!) { + _delete_webhook(params: $params) { + message + } + } +`; diff --git a/dashboard/src/pages/Webhooks.tsx b/dashboard/src/pages/Webhooks.tsx index 5c81cb2..ed1f9fa 100644 --- a/dashboard/src/pages/Webhooks.tsx +++ b/dashboard/src/pages/Webhooks.tsx @@ -43,6 +43,7 @@ import { UpdateWebhookModalViews, } from '../constants'; import { WebhooksDataQuery } from '../graphql/queries'; +import DeleteWebhookModal from '../components/DeleteWebhookModal'; interface paginationPropTypes { limit: number; @@ -209,7 +210,11 @@ const Webhooks = () => { selectedWebhook={webhook} fetchWebookData={fetchWebookData} /> - {}}>Delete + {}}>View Logs
From 8b1511a07b7d384e5c54a31fee860b0f096294ea Mon Sep 17 00:00:00 2001 From: anik-ghosh-au7 Date: Sat, 16 Jul 2022 23:10:05 +0530 Subject: [PATCH 11/17] update: webhooks --- .../src/components/ViewWebhookLogsModal.tsx | 426 ++++++++++++++++++ dashboard/src/graphql/queries/index.ts | 20 + dashboard/src/pages/Webhooks.tsx | 28 +- 3 files changed, 458 insertions(+), 16 deletions(-) create mode 100644 dashboard/src/components/ViewWebhookLogsModal.tsx diff --git a/dashboard/src/components/ViewWebhookLogsModal.tsx b/dashboard/src/components/ViewWebhookLogsModal.tsx new file mode 100644 index 0000000..e26089f --- /dev/null +++ b/dashboard/src/components/ViewWebhookLogsModal.tsx @@ -0,0 +1,426 @@ +import React, { useEffect, useState } from 'react'; +import dayjs from 'dayjs'; +import { + Button, + Center, + Flex, + MenuItem, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, + useDisclosure, + Text, + Spinner, + Table, + Th, + Thead, + Tr, + Tbody, + IconButton, + NumberDecrementStepper, + NumberIncrementStepper, + NumberInput, + NumberInputField, + NumberInputStepper, + Select, + TableCaption, + Tooltip, + Td, + Tag, +} from '@chakra-ui/react'; +import { useClient } from 'urql'; +import { + FaAngleDoubleLeft, + FaAngleDoubleRight, + FaAngleLeft, + FaAngleRight, + FaExclamationCircle, + FaRegClone, +} from 'react-icons/fa'; +import { copyTextToClipboard } from '../utils'; +import { WebhookLogsQuery } from '../graphql/queries'; +import { pageLimits } from '../constants'; + +interface paginationPropTypes { + limit: number; + page: number; + offset: number; + total: number; + maxPages: number; +} + +interface deleteWebhookModalInputPropTypes { + webhookId: string; + eventName: string; +} + +interface webhookLogsDataTypes { + id: string; + http_status: number; + request: string; + response: string; + created_at: number; +} + +const ViewWebhookLogsModal = ({ + webhookId, + eventName, +}: deleteWebhookModalInputPropTypes) => { + const client = useClient(); + const { isOpen, onOpen, onClose } = useDisclosure(); + const [loading, setLoading] = useState(false); + const [webhookLogs, setWebhookLogs] = useState([]); + const [paginationProps, setPaginationProps] = useState({ + limit: 5, + page: 1, + offset: 0, + total: 0, + maxPages: 1, + }); + const getMaxPages = (pagination: paginationPropTypes) => { + const { limit, total } = pagination; + if (total > 1) { + return total % limit === 0 + ? total / limit + : parseInt(`${total / limit}`) + 1; + } else return 1; + }; + const fetchWebhookLogsData = async () => { + setLoading(true); + const res = await client + .query(WebhookLogsQuery, { + params: { + webhook_id: webhookId, + pagination: { + limit: paginationProps.limit, + page: paginationProps.page, + }, + }, + }) + .toPromise(); + if (res.data?._webhook_logs) { + const { pagination, webhook_logs } = res.data?._webhook_logs; + const maxPages = getMaxPages(pagination); + if (webhook_logs?.length) { + setWebhookLogs(webhook_logs); + setPaginationProps({ ...paginationProps, ...pagination, maxPages }); + } else { + if (paginationProps.page !== 1) { + setPaginationProps({ + ...paginationProps, + ...pagination, + maxPages, + page: 1, + }); + } + } + } + setLoading(false); + }; + const paginationHandler = (value: Record) => { + setPaginationProps({ ...paginationProps, ...value }); + }; + useEffect(() => { + isOpen && fetchWebhookLogsData(); + }, [isOpen, paginationProps.page, paginationProps.limit]); + return ( + <> + View Logs + + + + Webhook Logs - {eventName} + + + + {!loading ? ( + webhookLogs.length ? ( + + + + + + + + + + + + {webhookLogs.map((logData: webhookLogsDataTypes) => ( + + + + + + + + ))} + + {(paginationProps.maxPages > 1 || + paginationProps.total >= 5) && ( + + + + + + paginationHandler({ + page: 1, + }) + } + isDisabled={paginationProps.page <= 1} + mr={4} + icon={} + /> + + + + paginationHandler({ + page: paginationProps.page - 1, + }) + } + isDisabled={paginationProps.page <= 1} + icon={} + /> + + + + + Page{' '} + + {paginationProps.page} + {' '} + of{' '} + + {paginationProps.maxPages} + + + + Go to page:{' '} + + paginationHandler({ + page: parseInt(value), + }) + } + value={paginationProps.page} + > + + + + + + + + + + + + + paginationHandler({ + page: paginationProps.page + 1, + }) + } + isDisabled={ + paginationProps.page >= + paginationProps.maxPages + } + icon={} + /> + + + + paginationHandler({ + page: paginationProps.maxPages, + }) + } + isDisabled={ + paginationProps.page >= + paginationProps.maxPages + } + ml={4} + icon={} + /> + + + + + )} +
IDCreated AtHttp StatusRequestResponse
+ {`${logData.id.substring( + 0, + 5 + )}***${logData.id.substring( + logData.id.length - 5, + logData.id.length + )}`} + + {dayjs(logData.created_at * 1000).format( + 'MMM DD, YYYY' + )} + + = 400 ? 'red' : 'green' + } + > + {logData.http_status} + + + + + + {logData.request ? 'Payload' : 'No Data'} + + + {logData.request && ( + + )} + + + + + + {logData.response ? 'Preview' : 'No Data'} + + + {logData.response && ( + + )} + +
+ ) : ( + +
+ +
+ + No Data + +
+ ) + ) : ( +
+ +
+ )} +
+
+ + + +
+
+ + ); +}; + +export default ViewWebhookLogsModal; diff --git a/dashboard/src/graphql/queries/index.ts b/dashboard/src/graphql/queries/index.ts index d22dcca..ac9ba15 100644 --- a/dashboard/src/graphql/queries/index.ts +++ b/dashboard/src/graphql/queries/index.ts @@ -121,3 +121,23 @@ export const WebhooksDataQuery = ` } } `; + +export const WebhookLogsQuery = ` + query getWebhookLogs($params: ListWebhookLogRequest!) { + _webhook_logs(params: $params) { + webhook_logs { + id + http_status + request + response + created_at + } + pagination { + limit + page + offset + total + } + } + } +`; diff --git a/dashboard/src/pages/Webhooks.tsx b/dashboard/src/pages/Webhooks.tsx index ed1f9fa..c233819 100644 --- a/dashboard/src/pages/Webhooks.tsx +++ b/dashboard/src/pages/Webhooks.tsx @@ -44,6 +44,7 @@ import { } from '../constants'; import { WebhooksDataQuery } from '../graphql/queries'; import DeleteWebhookModal from '../components/DeleteWebhookModal'; +import ViewWebhookLogsModal from '../components/ViewWebhookLogsModal'; interface paginationPropTypes { limit: number; @@ -117,7 +118,7 @@ const Webhooks = () => { useEffect(() => { fetchWebookData(); }, []); - React.useEffect(() => { + useEffect(() => { fetchWebookData(); }, [paginationProps.page, paginationProps.limit]); return ( @@ -144,7 +145,7 @@ const Webhooks = () => { - {webhookData.map((webhook: webhookDataTypes, index: number) => ( + {webhookData.map((webhook: webhookDataTypes) => ( { { ' ' )} > -
- - {Object.keys( - webhook[WebhookInputDataFields.HEADERS] || {} - )?.length.toString()} - -
+ + {Object.keys( + webhook[WebhookInputDataFields.HEADERS] || {} + )?.length.toString()} +
@@ -215,7 +208,10 @@ const Webhooks = () => { eventName={webhook[WebhookInputDataFields.EVENT_NAME]} fetchWebookData={fetchWebookData} /> - {}}>View Logs + From 390846c85f13818b629667f5d571a3366158c3df Mon Sep 17 00:00:00 2001 From: anik-ghosh-au7 Date: Sun, 17 Jul 2022 13:38:18 +0530 Subject: [PATCH 12/17] update: webhooks --- dashboard/src/components/UpdateWebhookModal.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dashboard/src/components/UpdateWebhookModal.tsx b/dashboard/src/components/UpdateWebhookModal.tsx index 80df822..52fc67c 100644 --- a/dashboard/src/components/UpdateWebhookModal.tsx +++ b/dashboard/src/components/UpdateWebhookModal.tsx @@ -227,6 +227,7 @@ const UpdateWebhookModal = ({ [WebhookInputDataFields.ENDPOINT]: webhook[WebhookInputDataFields.ENDPOINT], [WebhookInputDataFields.ENABLED]: webhook[WebhookInputDataFields.ENABLED], + [WebhookInputDataFields.HEADERS]: {}, }; if (webhook[WebhookInputDataFields.HEADERS].length) { const headers = webhook[WebhookInputDataFields.HEADERS].reduce( @@ -311,7 +312,10 @@ const UpdateWebhookModal = ({ .map(() => ({ ...initHeadersValidatorData })), }); } else { - setWebhook({ ...rest, [WebhookInputDataFields.HEADERS]: [] }); + setWebhook({ + ...rest, + [WebhookInputDataFields.HEADERS]: [{ ...initHeadersData }], + }); } } }, [view]); From 1c61fcc17ac348f419a1c497bb082acaf6e0787d Mon Sep 17 00:00:00 2001 From: anik-ghosh-au7 Date: Sun, 17 Jul 2022 13:52:31 +0530 Subject: [PATCH 13/17] update: webhooks --- dashboard/src/components/UpdateWebhookModal.tsx | 5 +++-- dashboard/src/pages/Webhooks.tsx | 3 --- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/dashboard/src/components/UpdateWebhookModal.tsx b/dashboard/src/components/UpdateWebhookModal.tsx index 52fc67c..15480a9 100644 --- a/dashboard/src/components/UpdateWebhookModal.tsx +++ b/dashboard/src/components/UpdateWebhookModal.tsx @@ -279,13 +279,14 @@ const UpdateWebhookModal = ({ [WebhookInputDataFields.HEADERS]: [{ ...initHeadersData }], }); setValidator({ ...initWebhookValidatorData }); - onClose(); fetchWebookData(); + return; } setLoading(false); }; useEffect(() => { if ( + isOpen && view === UpdateWebhookModalViews.Edit && selectedWebhook && Object.keys(selectedWebhook || {}).length @@ -318,7 +319,7 @@ const UpdateWebhookModal = ({ }); } } - }, [view]); + }, [isOpen]); return ( <> {view === UpdateWebhookModalViews.ADD ? ( diff --git a/dashboard/src/pages/Webhooks.tsx b/dashboard/src/pages/Webhooks.tsx index c233819..78d863e 100644 --- a/dashboard/src/pages/Webhooks.tsx +++ b/dashboard/src/pages/Webhooks.tsx @@ -115,9 +115,6 @@ const Webhooks = () => { const paginationHandler = (value: Record) => { setPaginationProps({ ...paginationProps, ...value }); }; - useEffect(() => { - fetchWebookData(); - }, []); useEffect(() => { fetchWebookData(); }, [paginationProps.page, paginationProps.limit]); From 94066d440863793a329023e8fd4c3bd91609e599 Mon Sep 17 00:00:00 2001 From: anik-ghosh-au7 Date: Sun, 17 Jul 2022 14:42:46 +0530 Subject: [PATCH 14/17] update: webhooks --- .../src/components/UpdateWebhookModal.tsx | 51 +++++++++---------- dashboard/src/constants.ts | 9 ++++ 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/dashboard/src/components/UpdateWebhookModal.tsx b/dashboard/src/components/UpdateWebhookModal.tsx index 15480a9..48a8c50 100644 --- a/dashboard/src/components/UpdateWebhookModal.tsx +++ b/dashboard/src/components/UpdateWebhookModal.tsx @@ -14,6 +14,7 @@ import { ModalFooter, ModalHeader, ModalOverlay, + Select, Switch, Text, useDisclosure, @@ -22,6 +23,7 @@ import { import { FaMinusCircle, FaPlus } from 'react-icons/fa'; import { useClient } from 'urql'; import { + webhookEventNames, ArrayInputOperations, WebhookInputDataFields, WebhookInputHeaderFields, @@ -33,6 +35,7 @@ import { validateURI, } from '../utils'; import { AddWebhook, EditWebhook } from '../graphql/mutation'; +import { rest } from 'lodash'; interface headersDataType { [WebhookInputHeaderFields.KEY]: string; @@ -76,20 +79,18 @@ interface webhookDataType { } interface validatorDataType { - [WebhookInputDataFields.EVENT_NAME]: boolean; [WebhookInputDataFields.ENDPOINT]: boolean; [WebhookInputDataFields.HEADERS]: headersValidatorDataType[]; } const initWebhookData: webhookDataType = { - [WebhookInputDataFields.EVENT_NAME]: '', + [WebhookInputDataFields.EVENT_NAME]: webhookEventNames.USER_LOGIN, [WebhookInputDataFields.ENDPOINT]: '', [WebhookInputDataFields.ENABLED]: false, [WebhookInputDataFields.HEADERS]: [{ ...initHeadersData }], }; const initWebhookValidatorData: validatorDataType = { - [WebhookInputDataFields.EVENT_NAME]: true, [WebhookInputDataFields.ENDPOINT]: true, [WebhookInputDataFields.HEADERS]: [{ ...initHeadersValidatorData }], }; @@ -118,10 +119,6 @@ const UpdateWebhookModal = ({ switch (inputType) { case WebhookInputDataFields.EVENT_NAME: setWebhook({ ...webhook, [inputType]: value }); - setValidator({ - ...validator, - [WebhookInputDataFields.EVENT_NAME]: validateEventName(value), - }); break; case WebhookInputDataFields.ENDPOINT: setWebhook({ ...webhook, [inputType]: value }); @@ -210,7 +207,6 @@ const UpdateWebhookModal = ({ !loading && webhook[WebhookInputDataFields.EVENT_NAME].length > 0 && webhook[WebhookInputDataFields.ENDPOINT].length > 0 && - validator[WebhookInputDataFields.EVENT_NAME] && validator[WebhookInputDataFields.ENDPOINT] && !validator[WebhookInputDataFields.HEADERS].some( (headerData: headersValidatorDataType) => @@ -256,6 +252,7 @@ const UpdateWebhookModal = ({ } else { res = await client.mutation(AddWebhook, { params }).toPromise(); } + setLoading(false); if (res.error) { toast({ title: capitalizeFirstLetter(res.error.message), @@ -263,8 +260,6 @@ const UpdateWebhookModal = ({ status: 'error', position: 'bottom-right', }); - setLoading(false); - return; } else if (res.data?._add_webhook || res.data?._update_webhook) { toast({ title: capitalizeFirstLetter( @@ -280,9 +275,8 @@ const UpdateWebhookModal = ({ }); setValidator({ ...initWebhookValidatorData }); fetchWebookData(); - return; } - setLoading(false); + view === UpdateWebhookModalViews.ADD && onClose(); }; useEffect(() => { if ( @@ -361,21 +355,24 @@ const UpdateWebhookModal = ({ > Event Name - - - inputChangehandler( - WebhookInputDataFields.EVENT_NAME, - e.currentTarget.value - ) - } - /> - + Date: Sun, 17 Jul 2022 14:48:20 +0530 Subject: [PATCH 15/17] update: webhooks --- dashboard/src/components/UpdateWebhookModal.tsx | 6 +----- dashboard/src/utils/index.ts | 5 ----- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/dashboard/src/components/UpdateWebhookModal.tsx b/dashboard/src/components/UpdateWebhookModal.tsx index 48a8c50..bba0042 100644 --- a/dashboard/src/components/UpdateWebhookModal.tsx +++ b/dashboard/src/components/UpdateWebhookModal.tsx @@ -29,11 +29,7 @@ import { WebhookInputHeaderFields, UpdateWebhookModalViews, } from '../constants'; -import { - capitalizeFirstLetter, - validateEventName, - validateURI, -} from '../utils'; +import { capitalizeFirstLetter, validateURI } from '../utils'; import { AddWebhook, EditWebhook } from '../graphql/mutation'; import { rest } from 'lodash'; diff --git a/dashboard/src/utils/index.ts b/dashboard/src/utils/index.ts index 99e134a..eccc7a8 100644 --- a/dashboard/src/utils/index.ts +++ b/dashboard/src/utils/index.ts @@ -86,8 +86,3 @@ export const validateURI = (uri: string) => { ? true : false; }; - -export const validateEventName = (name: string) => { - if (!name || name === '') return true; - return name.toLowerCase().match(/^.{4,}[.].{5,}$/) ? true : false; -}; From 3b925bb0729a6ec4ef94f58494da058b25bebfbc Mon Sep 17 00:00:00 2001 From: anik-ghosh-au7 Date: Sun, 17 Jul 2022 16:03:07 +0530 Subject: [PATCH 16/17] update: webhooks --- .../src/components/UpdateWebhookModal.tsx | 32 +++++++++++++++++++ dashboard/src/constants.ts | 6 ++++ 2 files changed, 38 insertions(+) diff --git a/dashboard/src/components/UpdateWebhookModal.tsx b/dashboard/src/components/UpdateWebhookModal.tsx index bba0042..c2d0dc0 100644 --- a/dashboard/src/components/UpdateWebhookModal.tsx +++ b/dashboard/src/components/UpdateWebhookModal.tsx @@ -28,10 +28,12 @@ import { WebhookInputDataFields, WebhookInputHeaderFields, UpdateWebhookModalViews, + webhookVerifiedStatus, } from '../constants'; import { capitalizeFirstLetter, validateURI } from '../utils'; import { AddWebhook, EditWebhook } from '../graphql/mutation'; import { rest } from 'lodash'; +import { BiCheckCircle, BiError, BiErrorCircle } from 'react-icons/bi'; interface headersDataType { [WebhookInputHeaderFields.KEY]: string; @@ -100,12 +102,16 @@ const UpdateWebhookModal = ({ const toast = useToast(); const { isOpen, onOpen, onClose } = useDisclosure(); const [loading, setLoading] = useState(false); + const [verifyingEndpoint, setVerifyingEndpoint] = useState(false); const [webhook, setWebhook] = useState({ ...initWebhookData, }); const [validator, setValidator] = useState({ ...initWebhookValidatorData, }); + const [verifiedStatus, setVerifiedStatus] = useState( + webhookVerifiedStatus.PENDING + ); const inputChangehandler = ( inputType: string, value: any, @@ -201,6 +207,7 @@ const UpdateWebhookModal = ({ const validateData = () => { return ( !loading && + !verifyingEndpoint && webhook[WebhookInputDataFields.EVENT_NAME].length > 0 && webhook[WebhookInputDataFields.ENDPOINT].length > 0 && validator[WebhookInputDataFields.ENDPOINT] && @@ -515,6 +522,31 @@ const UpdateWebhookModal = ({ +