From 68d7af919d6dbc2efc19a6e9f3d7fb51de6ff140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20G=C3=B3is?= Date: Mon, 8 Jan 2024 15:12:47 +0000 Subject: [PATCH] chore: incoming webhook hooks (#5788) https://linear.app/unleash/issue/2-1813/create-new-incoming-webhook-hooks-on-the-frontend Adds incoming webhook hooks to help us with CRUD operations on the frontend. --- .../useIncomingWebhooksApi.ts | 106 ++++++++++++++++++ .../useIncomingWebhooks.ts | 36 ++++++ frontend/src/interfaces/incomingWebhook.ts | 7 ++ 3 files changed, 149 insertions(+) create mode 100644 frontend/src/hooks/api/actions/useIncomingWebhooksApi/useIncomingWebhooksApi.ts create mode 100644 frontend/src/hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks.ts create mode 100644 frontend/src/interfaces/incomingWebhook.ts diff --git a/frontend/src/hooks/api/actions/useIncomingWebhooksApi/useIncomingWebhooksApi.ts b/frontend/src/hooks/api/actions/useIncomingWebhooksApi/useIncomingWebhooksApi.ts new file mode 100644 index 0000000000..ce1f9a5b39 --- /dev/null +++ b/frontend/src/hooks/api/actions/useIncomingWebhooksApi/useIncomingWebhooksApi.ts @@ -0,0 +1,106 @@ +import { IIncomingWebhook } from 'interfaces/incomingWebhook'; +import useAPI from '../useApi/useApi'; + +const ENDPOINT = 'api/admin/incoming-webhooks'; + +export type AddOrUpdateIncomingWebhook = Omit< + IIncomingWebhook, + 'id' | 'createdAt' | 'createdByUserId' +>; + +export const useIncomingWebhooksApi = () => { + const { loading, makeRequest, createRequest, errors } = useAPI({ + propagateErrors: true, + }); + + const addIncomingWebhook = async ( + incomingWebhook: AddOrUpdateIncomingWebhook, + ) => { + const requestId = 'addIncomingWebhook'; + const req = createRequest( + ENDPOINT, + { + method: 'POST', + body: JSON.stringify(incomingWebhook), + }, + requestId, + ); + + const response = await makeRequest(req.caller, req.id); + return response.json(); + }; + + const updateIncomingWebhook = async ( + incomingWebhookId: number, + incomingWebhook: AddOrUpdateIncomingWebhook, + ) => { + const requestId = 'updateIncomingWebhook'; + const req = createRequest( + `${ENDPOINT}/${incomingWebhookId}`, + { + method: 'PUT', + body: JSON.stringify(incomingWebhook), + }, + requestId, + ); + + await makeRequest(req.caller, req.id); + }; + + const enableIncomingWebhook = async (incomingWebhookId: number) => { + const requestId = 'enableIncomingWebhook'; + const req = createRequest( + `${ENDPOINT}/${incomingWebhookId}/on`, + { + method: 'POST', + }, + requestId, + ); + + await makeRequest(req.caller, req.id); + }; + + const disableIncomingWebhook = async (incomingWebhookId: number) => { + const requestId = 'disableIncomingWebhook'; + const req = createRequest( + `${ENDPOINT}/${incomingWebhookId}/off`, + { + method: 'POST', + }, + requestId, + ); + + await makeRequest(req.caller, req.id); + }; + + const toggleIncomingWebhook = async ( + incomingWebhookId: number, + enabled: boolean, + ) => { + if (enabled) { + await enableIncomingWebhook(incomingWebhookId); + } else { + await disableIncomingWebhook(incomingWebhookId); + } + }; + + const removeIncomingWebhook = async (incomingWebhookId: number) => { + const requestId = 'removeIncomingWebhook'; + const req = createRequest( + `${ENDPOINT}/${incomingWebhookId}`, + { method: 'DELETE' }, + requestId, + ); + + await makeRequest(req.caller, req.id); + }; + + return { + addIncomingWebhook, + updateIncomingWebhook, + removeIncomingWebhook, + toggleIncomingWebhook, + errors, + loading, + }; +}; diff --git a/frontend/src/hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks.ts b/frontend/src/hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks.ts new file mode 100644 index 0000000000..9ca49f1e83 --- /dev/null +++ b/frontend/src/hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks.ts @@ -0,0 +1,36 @@ +import { useMemo } from 'react'; +import { formatApiPath } from 'utils/formatPath'; +import handleErrorResponses from '../httpErrorResponseHandler'; +import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR'; +import useUiConfig from '../useUiConfig/useUiConfig'; +import { IIncomingWebhook } from 'interfaces/incomingWebhook'; + +const ENDPOINT = 'api/admin/incoming-webhooks'; + +export const useIncomingWebhooks = () => { + const { isEnterprise } = useUiConfig(); + + const { data, error, mutate } = useConditionalSWR( + isEnterprise(), + { incomingWebhooks: [] }, + formatApiPath(ENDPOINT), + fetcher, + ); + + return useMemo( + () => ({ + incomingWebhooks: (data?.incomingWebhooks ?? + []) as IIncomingWebhook[], + loading: !error && !data, + refetch: () => mutate(), + error, + }), + [data, error, mutate], + ); +}; + +const fetcher = (path: string) => { + return fetch(path) + .then(handleErrorResponses('Incoming webhooks')) + .then((res) => res.json()); +}; diff --git a/frontend/src/interfaces/incomingWebhook.ts b/frontend/src/interfaces/incomingWebhook.ts new file mode 100644 index 0000000000..2fabb45686 --- /dev/null +++ b/frontend/src/interfaces/incomingWebhook.ts @@ -0,0 +1,7 @@ +export interface IIncomingWebhook { + id: number; + enabled: boolean; + name: string; + createdAt: string; + createdByUserId: number; +}