From 68729333e0ab5487435bce58faa630ae2d80e51b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20G=C3=B3is?= Date: Mon, 4 Mar 2024 12:08:05 +0000 Subject: [PATCH] chore: rename incoming webhooks to signals (#6415) https://linear.app/unleash/issue/2-1994/ui-feature-rename-adapt-the-signals-ui https://linear.app/unleash/issue/2-1996/rename-feature-in-the-code-base Implements the feature rename to Signals by adapting the code base and UI. --- .../common/PremiumFeature/PremiumFeature.tsx | 6 +- .../incomingWebhooks/IncomingWebhooks.tsx | 41 ----- .../IncomingWebhooksDeleteDialog.tsx | 32 ---- .../AvailableIntegrations.tsx | 14 +- .../ConfiguredIntegrations.tsx | 28 +++ .../IntegrationCard/IntegrationCard.tsx | 28 ++- .../IntegrationList/IntegrationList.tsx | 160 ++--------------- .../__snapshots__/routes.test.tsx.snap | 2 +- frontend/src/component/menu/routes.ts | 11 +- .../ProjectActionsEventsDetailsSource.tsx | 22 --- .../ProjectActionsEventsDetails.tsx | 6 +- .../ProjectActionsEventsDetailsAction.tsx | 0 .../ProjectActionsEventsDetailsSource.tsx | 20 +++ ...ionsEventsDetailsSourceSignalEndpoint.tsx} | 35 ++-- .../ProjectActionsEventsModal.tsx | 8 +- .../ProjectActionsForm/ProjectActionsForm.tsx | 27 ++- .../useProjectActionsForm.ts | 8 +- .../ProjectActionsModal.tsx | 4 +- .../ProjectActionsTable.tsx | 8 +- .../ProjectActionsTriggerCell.tsx | 12 +- .../SignalEndpointsForm.tsx} | 50 +++--- .../SignalEndpointsFormURL.tsx} | 28 +-- .../SignalEndpointsTokens.tsx} | 71 ++++---- .../SignalEndpointsTokensCreateDialog.tsx} | 14 +- .../SignalEndpointsTokensDialog.tsx} | 10 +- .../useSignalEndpointsForm.ts} | 30 ++-- .../SignalEndpointsModal.tsx} | 74 ++++---- .../SignalEndpointsSignalsModal.tsx} | 50 +++--- .../SignalEndpointsActionsCell.tsx} | 22 +-- .../SignalEndpointsDeleteDialog.tsx | 32 ++++ .../SignalEndpointsTable.tsx} | 170 +++++++++--------- .../SignalEndpointsTokensCell.tsx} | 20 +-- frontend/src/component/signals/Signals.tsx | 21 +++ .../useIncomingWebhookTokensApi.ts | 77 -------- .../useIncomingWebhooksApi.ts | 106 ----------- .../useSignalEndpointTokensApi.ts | 77 ++++++++ .../useSignalEndpointsApi.ts | 104 +++++++++++ .../useSignalEndpointSignals.ts} | 35 ++-- .../useSignalEndpointTokens.ts} | 27 +-- .../useSignalEndpoints.ts} | 18 +- frontend/src/interfaces/action.ts | 28 +-- frontend/src/interfaces/incomingWebhook.ts | 28 --- frontend/src/interfaces/signal.ts | 33 ++++ frontend/src/interfaces/uiConfig.ts | 2 +- .../__snapshots__/create-config.test.ts.snap | 4 +- src/lib/create-config.ts | 6 +- src/lib/metrics.ts | 5 +- src/lib/types/events.ts | 30 ++-- src/lib/types/experimental.ts | 6 +- src/lib/types/option.ts | 2 +- 50 files changed, 777 insertions(+), 875 deletions(-) delete mode 100644 frontend/src/component/incomingWebhooks/IncomingWebhooks.tsx delete mode 100644 frontend/src/component/incomingWebhooks/IncomingWebhooksTable/IncomingWebhooksDeleteDialog.tsx delete mode 100644 frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/ProjectActionsEventsDetails.tsx/ProjectActionsEventsDetailsSource/ProjectActionsEventsDetailsSource.tsx rename frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/{ProjectActionsEventsDetails.tsx => ProjectActionsEventsDetails}/ProjectActionsEventsDetails.tsx (91%) rename frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/{ProjectActionsEventsDetails.tsx => ProjectActionsEventsDetails}/ProjectActionsEventsDetailsAction.tsx (100%) create mode 100644 frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/ProjectActionsEventsDetails/ProjectActionsEventsDetailsSource/ProjectActionsEventsDetailsSource.tsx rename frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/{ProjectActionsEventsDetails.tsx/ProjectActionsEventsDetailsSource/ProjectActionsEventsDetailsSourceIncomingWebhook.tsx => ProjectActionsEventsDetails/ProjectActionsEventsDetailsSource/ProjectActionsEventsDetailsSourceSignalEndpoint.tsx} (58%) rename frontend/src/component/{incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksForm.tsx => signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsForm.tsx} (86%) rename frontend/src/component/{incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksFormURL.tsx => signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsFormURL.tsx} (66%) rename frontend/src/component/{incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksTokens/IncomingWebhooksTokens.tsx => signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsTokens/SignalEndpointsTokens.tsx} (82%) rename frontend/src/component/{incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksTokens/IncomingWebhooksTokensCreateDialog.tsx => signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsTokens/SignalEndpointsTokensCreateDialog.tsx} (84%) rename frontend/src/component/{incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksTokens/IncomingWebhooksTokensDialog.tsx => signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsTokens/SignalEndpointsTokensDialog.tsx} (76%) rename frontend/src/component/{incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/useIncomingWebhooksForm.ts => signals/SignalEndpointsModal/SignalEndpointsForm/useSignalEndpointsForm.ts} (76%) rename frontend/src/component/{incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksModal.tsx => signals/SignalEndpointsModal/SignalEndpointsModal.tsx} (70%) rename frontend/src/component/{incomingWebhooks/IncomingWebhooksEvents/IncomingWebhooksEventsModal.tsx => signals/SignalEndpointsSignals/SignalEndpointsSignalsModal.tsx} (77%) rename frontend/src/component/{incomingWebhooks/IncomingWebhooksTable/IncomingWebhooksActionsCell.tsx => signals/SignalEndpointsTable/SignalEndpointsActionsCell.tsx} (91%) create mode 100644 frontend/src/component/signals/SignalEndpointsTable/SignalEndpointsDeleteDialog.tsx rename frontend/src/component/{incomingWebhooks/IncomingWebhooksTable/IncomingWebhooksTable.tsx => signals/SignalEndpointsTable/SignalEndpointsTable.tsx} (56%) rename frontend/src/component/{incomingWebhooks/IncomingWebhooksTable/IncomingWebhooksTokensCell.tsx => signals/SignalEndpointsTable/SignalEndpointsTokensCell.tsx} (73%) create mode 100644 frontend/src/component/signals/Signals.tsx delete mode 100644 frontend/src/hooks/api/actions/useIncomingWebhookTokensApi/useIncomingWebhookTokensApi.ts delete mode 100644 frontend/src/hooks/api/actions/useIncomingWebhooksApi/useIncomingWebhooksApi.ts create mode 100644 frontend/src/hooks/api/actions/useSignalEndpointTokensApi/useSignalEndpointTokensApi.ts create mode 100644 frontend/src/hooks/api/actions/useSignalEndpointsApi/useSignalEndpointsApi.ts rename frontend/src/hooks/api/getters/{useIncomingWebhookEvents/useIncomingWebhookEvents.ts => useSignalEndpointSignals/useSignalEndpointSignals.ts} (57%) rename frontend/src/hooks/api/getters/{useIncomingWebhookTokens/useIncomingWebhookTokens.ts => useSignalEndpointTokens/useSignalEndpointTokens.ts} (50%) rename frontend/src/hooks/api/getters/{useIncomingWebhooks/useIncomingWebhooks.ts => useSignalEndpoints/useSignalEndpoints.ts} (64%) delete mode 100644 frontend/src/interfaces/incomingWebhook.ts create mode 100644 frontend/src/interfaces/signal.ts diff --git a/frontend/src/component/common/PremiumFeature/PremiumFeature.tsx b/frontend/src/component/common/PremiumFeature/PremiumFeature.tsx index 3fc12b30ddb0..395bb1ea8ac0 100644 --- a/frontend/src/component/common/PremiumFeature/PremiumFeature.tsx +++ b/frontend/src/component/common/PremiumFeature/PremiumFeature.tsx @@ -108,10 +108,10 @@ const PremiumFeatures = { url: 'https://docs.getunleash.io/reference/banners', label: 'Banners', }, - 'incoming-webhooks': { + signals: { plan: FeaturePlan.ENTERPRISE, - url: 'https://docs.getunleash.io/reference/incoming-webhooks', - label: 'Incoming Webhooks', + url: 'https://docs.getunleash.io/reference/signals', + label: 'Signals', }, actions: { plan: FeaturePlan.ENTERPRISE, diff --git a/frontend/src/component/incomingWebhooks/IncomingWebhooks.tsx b/frontend/src/component/incomingWebhooks/IncomingWebhooks.tsx deleted file mode 100644 index 03c903e32e12..000000000000 --- a/frontend/src/component/incomingWebhooks/IncomingWebhooks.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { ADMIN } from 'component/providers/AccessProvider/permissions'; -import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard'; -import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; -import { PremiumFeature } from 'component/common/PremiumFeature/PremiumFeature'; -import { IncomingWebhooksTable } from './IncomingWebhooksTable/IncomingWebhooksTable'; -import { IIncomingWebhook } from 'interfaces/incomingWebhook'; - -interface IIncomingWebhooksProps { - modalOpen: boolean; - setModalOpen: React.Dispatch>; - selectedIncomingWebhook?: IIncomingWebhook; - setSelectedIncomingWebhook: React.Dispatch< - React.SetStateAction - >; -} - -export const IncomingWebhooks = ({ - modalOpen, - setModalOpen, - selectedIncomingWebhook, - setSelectedIncomingWebhook, -}: IIncomingWebhooksProps) => { - const { isEnterprise } = useUiConfig(); - - if (!isEnterprise()) { - return ; - } - - return ( -
- - - -
- ); -}; diff --git a/frontend/src/component/incomingWebhooks/IncomingWebhooksTable/IncomingWebhooksDeleteDialog.tsx b/frontend/src/component/incomingWebhooks/IncomingWebhooksTable/IncomingWebhooksDeleteDialog.tsx deleted file mode 100644 index b8568551b32c..000000000000 --- a/frontend/src/component/incomingWebhooks/IncomingWebhooksTable/IncomingWebhooksDeleteDialog.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { Dialogue } from 'component/common/Dialogue/Dialogue'; -import { IIncomingWebhook } from 'interfaces/incomingWebhook'; - -interface IIncomingWebhooksDeleteDialogProps { - incomingWebhook?: IIncomingWebhook; - open: boolean; - setOpen: React.Dispatch>; - onConfirm: (incomingWebhook: IIncomingWebhook) => void; -} - -export const IncomingWebhooksDeleteDialog = ({ - incomingWebhook, - open, - setOpen, - onConfirm, -}: IIncomingWebhooksDeleteDialogProps) => ( - onConfirm(incomingWebhook!)} - onClose={() => { - setOpen(false); - }} - > -

- You are about to delete incoming webhook:{' '} - {incomingWebhook?.name} -

-
-); diff --git a/frontend/src/component/integrations/IntegrationList/AvailableIntegrations/AvailableIntegrations.tsx b/frontend/src/component/integrations/IntegrationList/AvailableIntegrations/AvailableIntegrations.tsx index 445f27e68584..d7f44be1517a 100644 --- a/frontend/src/component/integrations/IntegrationList/AvailableIntegrations/AvailableIntegrations.tsx +++ b/frontend/src/component/integrations/IntegrationList/AvailableIntegrations/AvailableIntegrations.tsx @@ -8,10 +8,10 @@ import { RequestIntegrationCard } from '../RequestIntegrationCard/RequestIntegra import { OFFICIAL_SDKS } from './SDKs'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { useUiFlag } from 'hooks/useUiFlag'; +import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; interface IAvailableIntegrationsProps { providers: AddonTypeSchema[]; - onNewIncomingWebhook: () => void; } const StyledContainer = styled('div')(({ theme }) => ({ @@ -53,9 +53,9 @@ const StyledGrayContainer = styled('div')(({ theme }) => ({ export const AvailableIntegrations: VFC = ({ providers, - onNewIncomingWebhook, }) => { - const incomingWebhooksEnabled = useUiFlag('incomingWebhooks'); + const { isEnterprise } = useUiConfig(); + const signalsEnabled = useUiFlag('signals'); const customProviders = [JIRA_INFO]; const serverSdks = OFFICIAL_SDKS.filter((sdk) => sdk.type === 'server'); @@ -98,13 +98,13 @@ export const AvailableIntegrations: VFC = ({ ), )} } /> diff --git a/frontend/src/component/integrations/IntegrationList/ConfiguredIntegrations/ConfiguredIntegrations.tsx b/frontend/src/component/integrations/IntegrationList/ConfiguredIntegrations/ConfiguredIntegrations.tsx index 7dc81d56a3fe..23bdf0063387 100644 --- a/frontend/src/component/integrations/IntegrationList/ConfiguredIntegrations/ConfiguredIntegrations.tsx +++ b/frontend/src/component/integrations/IntegrationList/ConfiguredIntegrations/ConfiguredIntegrations.tsx @@ -4,6 +4,10 @@ import { StyledCardsGrid } from '../IntegrationList.styles'; import { IntegrationCard } from '../IntegrationCard/IntegrationCard'; import { VFC } from 'react'; import { Typography, styled } from '@mui/material'; +import { useSignalEndpoints } from 'hooks/api/getters/useSignalEndpoints/useSignalEndpoints'; +import { useUiFlag } from 'hooks/useUiFlag'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; const StyledConfiguredSection = styled('section')(({ theme }) => ({ display: 'flex', @@ -23,6 +27,10 @@ export const ConfiguredIntegrations: VFC = ({ addons, providers, }) => { + const { signalEndpoints } = useSignalEndpoints(); + const signalsEnabled = useUiFlag('signals'); + const { isEnterprise } = useUiConfig(); + const ref = useLoading(loading || false); return ( @@ -65,6 +73,26 @@ export const ConfiguredIntegrations: VFC = ({ /> ); })} + 0 + } + show={ + + } + /> ); diff --git a/frontend/src/component/integrations/IntegrationList/IntegrationCard/IntegrationCard.tsx b/frontend/src/component/integrations/IntegrationList/IntegrationCard/IntegrationCard.tsx index 4c843f92d06f..fe564e0c97a1 100644 --- a/frontend/src/component/integrations/IntegrationList/IntegrationCard/IntegrationCard.tsx +++ b/frontend/src/component/integrations/IntegrationList/IntegrationCard/IntegrationCard.tsx @@ -10,7 +10,10 @@ import type { AddonSchema } from 'openapi'; import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; +type CardVariant = 'default' | 'stacked'; + interface IIntegrationCardBaseProps { + variant?: CardVariant; id?: string | number; icon?: string; title: string; @@ -37,7 +40,9 @@ type IIntegrationCardProps = | IIntegrationCardWithLinkProps | IIntegrationCardWithOnClickProps; -const StyledCard = styled('div')(({ theme }) => ({ +const StyledCard = styled('div', { + shouldForwardProp: (prop) => prop !== 'variant', +})<{ variant?: CardVariant }>(({ theme, variant = 'default' }) => ({ display: 'flex', flexDirection: 'column', padding: theme.spacing(3), @@ -48,6 +53,24 @@ const StyledCard = styled('div')(({ theme }) => ({ ':hover': { backgroundColor: theme.palette.action.hover, }, + ...(variant === 'stacked' && { + position: 'relative', + zIndex: 0, + '&:after': { + content: '""', + width: 'auto', + height: theme.spacing(0.75), + position: 'absolute', + zIndex: -1, + bottom: theme.spacing(-0.75), + left: theme.spacing(1), + right: theme.spacing(1), + borderBottomLeftRadius: `${theme.shape.borderRadiusMedium}px`, + borderBottomRightRadius: `${theme.shape.borderRadiusMedium}px`, + border: `1px solid ${theme.palette.divider}`, + boxShadow: theme.boxShadows.card, + }, + }), })); const StyledLink = styled(Link)({ @@ -89,6 +112,7 @@ const StyledOpenInNewIcon = styled(OpenInNewIcon)(({ theme }) => ({ })); export const IntegrationCard: VFC = ({ + variant = 'default', icon, title, description, @@ -113,7 +137,7 @@ export const IntegrationCard: VFC = ({ }; const content = ( - + {title} diff --git a/frontend/src/component/integrations/IntegrationList/IntegrationList.tsx b/frontend/src/component/integrations/IntegrationList/IntegrationList.tsx index 3cb053142714..40af0efcaaac 100644 --- a/frontend/src/component/integrations/IntegrationList/IntegrationList.tsx +++ b/frontend/src/component/integrations/IntegrationList/IntegrationList.tsx @@ -1,162 +1,32 @@ -import { VFC, useState } from 'react'; +import { VFC } from 'react'; import useAddons from 'hooks/api/getters/useAddons/useAddons'; import { AvailableIntegrations } from './AvailableIntegrations/AvailableIntegrations'; import { ConfiguredIntegrations } from './ConfiguredIntegrations/ConfiguredIntegrations'; -import { Tab, Tabs, styled, useTheme } from '@mui/material'; -import { Add } from '@mui/icons-material'; -import { Route, Routes, useLocation, useNavigate } from 'react-router-dom'; -import { useUiFlag } from 'hooks/useUiFlag'; -import { useIncomingWebhooks } from 'hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks'; import { PageContent } from 'component/common/PageContent/PageContent'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { PageHeader } from 'component/common/PageHeader/PageHeader'; -import { TabLink } from 'component/common/TabNav/TabLink'; -import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton'; -import { ADMIN } from 'component/providers/AccessProvider/permissions'; -import { IIncomingWebhook } from 'interfaces/incomingWebhook'; -import { IncomingWebhooks } from 'component/incomingWebhooks/IncomingWebhooks'; -import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; - -const StyledHeader = styled('div')(() => ({ - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', -})); - -const StyledTabsContainer = styled('div')({ - flex: 1, -}); - -const StyledActions = styled('div')({ - display: 'flex', - alignItems: 'center', -}); +import { useSignalEndpoints } from 'hooks/api/getters/useSignalEndpoints/useSignalEndpoints'; export const IntegrationList: VFC = () => { - const { isEnterprise } = useUiConfig(); - const { pathname } = useLocation(); - const navigate = useNavigate(); - const theme = useTheme(); - const incomingWebhooksEnabled = useUiFlag('incomingWebhooks'); + const { signalEndpoints } = useSignalEndpoints(); const { providers, addons, loading } = useAddons(); - const { incomingWebhooks } = useIncomingWebhooks(); - - const [selectedIncomingWebhook, setSelectedIncomingWebhook] = - useState(); - const [incomingWebhookModalOpen, setIncomingWebhookModalOpen] = - useState(false); - - const onNewIncomingWebhook = () => { - navigate('/integrations/incoming-webhooks'); - setSelectedIncomingWebhook(undefined); - setIncomingWebhookModalOpen(true); - }; - - const tabs = [ - { - label: 'Integrations', - path: '/integrations', - }, - { - label: `Incoming webhooks (${incomingWebhooks.length})`, - path: '/integrations/incoming-webhooks', - }, - ]; return ( - - - {tabs.map(({ label, path }) => ( - - {label} - - } - sx={{ - padding: 0, - }} - /> - ))} - - - - - New incoming webhook - - } - /> - - - } - elseShow={} - /> - } + header={} isLoading={loading} - withTabs={incomingWebhooksEnabled} > - - - } - /> - - 0} - show={ - - } - /> - - - } - /> - + 0 || signalEndpoints.length > 0} + show={ + + } + /> + ); }; diff --git a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap index 206cceae8357..e9f1d7e2bf77 100644 --- a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap +++ b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap @@ -343,7 +343,7 @@ exports[`returns all baseRoutes 1`] = ` "advanced": true, "mobile": true, }, - "path": "/integrations/*", + "path": "/integrations", "title": "Integrations", "type": "protected", }, diff --git a/frontend/src/component/menu/routes.ts b/frontend/src/component/menu/routes.ts index 8e2aac805ad1..d26ac722ebf7 100644 --- a/frontend/src/component/menu/routes.ts +++ b/frontend/src/component/menu/routes.ts @@ -47,6 +47,7 @@ import { AddonRedirect } from 'component/integrations/AddonRedirect/AddonRedirec import { ExecutiveDashboard } from 'component/executiveDashboard/ExecutiveDashboard'; import { FeedbackList } from '../feedbackNew/FeedbackList'; import { Application } from 'component/application/Application'; +import { Signals } from 'component/signals/Signals'; export const routes: IRoute[] = [ // Splash @@ -351,13 +352,21 @@ export const routes: IRoute[] = [ menu: {}, }, { - path: '/integrations/*', + path: '/integrations', title: 'Integrations', component: IntegrationList, hidden: false, type: 'protected', menu: { mobile: true, advanced: true }, }, + { + path: '/integrations/signals', + title: 'Signals', + component: Signals, + hidden: true, + type: 'protected', + menu: {}, + }, // Segments { diff --git a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/ProjectActionsEventsDetails.tsx/ProjectActionsEventsDetailsSource/ProjectActionsEventsDetailsSource.tsx b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/ProjectActionsEventsDetails.tsx/ProjectActionsEventsDetailsSource/ProjectActionsEventsDetailsSource.tsx deleted file mode 100644 index fbe2a04c7d5f..000000000000 --- a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/ProjectActionsEventsDetails.tsx/ProjectActionsEventsDetailsSource/ProjectActionsEventsDetailsSource.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { IObservableEvent } from 'interfaces/action'; -import { ProjectActionsEventsDetailsSourceIncomingWebhook } from './ProjectActionsEventsDetailsSourceIncomingWebhook'; - -interface IProjectActionsEventsDetailsSourceProps { - observableEvent: IObservableEvent; -} - -export const ProjectActionsEventsDetailsSource = ({ - observableEvent, -}: IProjectActionsEventsDetailsSourceProps) => { - const { source } = observableEvent; - - if (source === 'incoming-webhook') { - return ( - - ); - } - - return null; -}; diff --git a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/ProjectActionsEventsDetails.tsx/ProjectActionsEventsDetails.tsx b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/ProjectActionsEventsDetails/ProjectActionsEventsDetails.tsx similarity index 91% rename from frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/ProjectActionsEventsDetails.tsx/ProjectActionsEventsDetails.tsx rename to frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/ProjectActionsEventsDetails/ProjectActionsEventsDetails.tsx index c1ef8af68182..0e858148cec9 100644 --- a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/ProjectActionsEventsDetails.tsx/ProjectActionsEventsDetails.tsx +++ b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/ProjectActionsEventsDetails/ProjectActionsEventsDetails.tsx @@ -13,7 +13,7 @@ const StyledDetails = styled('div')(({ theme }) => ({ export const ProjectActionsEventsDetails = ({ state, actionSet: { actions }, - observableEvent, + signal, }: IActionSetEvent) => { const stateText = state === 'failed' @@ -27,9 +27,7 @@ export const ProjectActionsEventsDetails = ({ {stateText} - + {actions.map((action, i) => ( { + const { source } = signal; + + if (source === 'signal-endpoint') { + return ( + + ); + } + + return null; +}; diff --git a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/ProjectActionsEventsDetails.tsx/ProjectActionsEventsDetailsSource/ProjectActionsEventsDetailsSourceIncomingWebhook.tsx b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/ProjectActionsEventsDetails/ProjectActionsEventsDetailsSource/ProjectActionsEventsDetailsSourceSignalEndpoint.tsx similarity index 58% rename from frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/ProjectActionsEventsDetails.tsx/ProjectActionsEventsDetailsSource/ProjectActionsEventsDetailsSourceIncomingWebhook.tsx rename to frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/ProjectActionsEventsDetails/ProjectActionsEventsDetailsSource/ProjectActionsEventsDetailsSourceSignalEndpoint.tsx index 50e37d3589a7..4ecc08610e37 100644 --- a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/ProjectActionsEventsDetails.tsx/ProjectActionsEventsDetailsSource/ProjectActionsEventsDetailsSourceIncomingWebhook.tsx +++ b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsEventsModal/ProjectActionsEventsDetails/ProjectActionsEventsDetailsSource/ProjectActionsEventsDetailsSourceSignalEndpoint.tsx @@ -6,8 +6,8 @@ import { IconButton, styled, } from '@mui/material'; -import { useIncomingWebhooks } from 'hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks'; -import { IObservableEvent } from 'interfaces/action'; +import { useSignalEndpoints } from 'hooks/api/getters/useSignalEndpoints/useSignalEndpoints'; +import { ISignal } from 'interfaces/signal'; import { Suspense, lazy, useMemo } from 'react'; import { Link } from 'react-router-dom'; @@ -28,23 +28,22 @@ const StyledLink = styled(Link)(({ theme }) => ({ marginLeft: theme.spacing(1), })); -interface IProjectActionsEventsDetailsSourceIncomingWebhookProps { - observableEvent: IObservableEvent; +interface IProjectActionsEventsDetailsSourceSignalEndpointProps { + signal: ISignal; } -export const ProjectActionsEventsDetailsSourceIncomingWebhook = ({ - observableEvent, -}: IProjectActionsEventsDetailsSourceIncomingWebhookProps) => { - const { incomingWebhooks } = useIncomingWebhooks(); +export const ProjectActionsEventsDetailsSourceSignalEndpoint = ({ + signal, +}: IProjectActionsEventsDetailsSourceSignalEndpointProps) => { + const { signalEndpoints } = useSignalEndpoints(); - const incomingWebhookName = useMemo(() => { - const incomingWebhook = incomingWebhooks.find( - (incomingWebhook) => - incomingWebhook.id === observableEvent.sourceId, + const signalEndpointName = useMemo(() => { + const signalEndpoint = signalEndpoints.find( + ({ id }) => id === signal.sourceId, ); - return incomingWebhook?.name; - }, [incomingWebhooks, observableEvent.sourceId]); + return signalEndpoint?.name; + }, [signalEndpoints, signal.sourceId]); return ( @@ -55,15 +54,15 @@ export const ProjectActionsEventsDetailsSourceIncomingWebhook = ({ } > - Incoming webhook: - - {incomingWebhookName} + Signal endpoint: + + {signalEndpointName} ({ display: 'flex', @@ -86,9 +86,9 @@ export const ProjectActionsEventsModal = ({ diff --git a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/ProjectActionsForm.tsx b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/ProjectActionsForm.tsx index d4519d20f2a7..92b93ec694cf 100644 --- a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/ProjectActionsForm.tsx +++ b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/ProjectActionsForm.tsx @@ -9,7 +9,7 @@ import { ProjectActionsFormErrors, } from './useProjectActionsForm'; import { useServiceAccounts } from 'hooks/api/getters/useServiceAccounts/useServiceAccounts'; -import { useIncomingWebhooks } from 'hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks'; +import { useSignalEndpoints } from 'hooks/api/getters/useSignalEndpoints/useSignalEndpoints'; import { v4 as uuidv4 } from 'uuid'; import { useMemo } from 'react'; import GeneralSelect, {} from 'component/common/GeneralSelect/GeneralSelect'; @@ -100,8 +100,8 @@ export const ProjectActionsForm = ({ const projectId = useRequiredPathParam('projectId'); const { serviceAccounts, loading: serviceAccountsLoading } = useServiceAccounts(); - const { incomingWebhooks, loading: incomingWebhooksLoading } = - useIncomingWebhooks(); + const { signalEndpoints, loading: signalEndpointsLoading } = + useSignalEndpoints(); const handleOnBlur = (callback: Function) => { setTimeout(() => callback(), 300); @@ -151,16 +151,16 @@ export const ProjectActionsForm = ({ ); }; - const incomingWebhookOptions = useMemo(() => { - if (incomingWebhooksLoading) { + const signalEndpointOptions = useMemo(() => { + if (signalEndpointsLoading) { return []; } - return incomingWebhooks.map((webhook) => ({ - label: webhook.name, - key: `${webhook.id}`, + return signalEndpoints.map(({ id, name }) => ({ + label: name, + key: `${id}`, })); - }, [incomingWebhooksLoading, incomingWebhooks]); + }, [signalEndpointsLoading, signalEndpoints]); const serviceAccountOptions = useMemo(() => { if (serviceAccountsLoading) { @@ -218,15 +218,14 @@ export const ProjectActionsForm = ({ - Create incoming webhook + + Create signal endpoint } > { setSourceId(parseInt(v)); diff --git a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/useProjectActionsForm.ts b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/useProjectActionsForm.ts index 92b59602a596..93bcc186c49b 100644 --- a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/useProjectActionsForm.ts +++ b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/useProjectActionsForm.ts @@ -6,7 +6,7 @@ import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; enum ErrorField { NAME = 'name', - TRIGGER = 'trigger', + SOURCE = 'source', FILTERS = 'filters', ACTOR = 'actor', ACTIONS = 'actions', @@ -27,7 +27,7 @@ export type ActionsActionState = Omit< const DEFAULT_PROJECT_ACTIONS_FORM_ERRORS = { [ErrorField.NAME]: undefined, - [ErrorField.TRIGGER]: undefined, + [ErrorField.SOURCE]: undefined, [ErrorField.FILTERS]: undefined, [ErrorField.ACTOR]: undefined, [ErrorField.ACTIONS]: undefined, @@ -127,11 +127,11 @@ export const useProjectActionsForm = (action?: IActionSet) => { const validateSourceId = (sourceId: number) => { if (isIdEmpty(sourceId)) { - setError(ErrorField.TRIGGER, 'Incoming webhook is required.'); + setError(ErrorField.SOURCE, 'Source is required.'); return false; } - clearError(ErrorField.TRIGGER); + clearError(ErrorField.SOURCE); return true; }; diff --git a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsModal.tsx b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsModal.tsx index 2e3e38c2e4c1..2c653cf86e5c 100644 --- a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsModal.tsx +++ b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsModal.tsx @@ -95,7 +95,7 @@ export const ProjectActionsModal = ({ enabled, name, match: { - source: 'incoming-webhook', + source: 'signal-endpoint', sourceId, payload: filters .filter((f) => f.parameter.length > 0) @@ -173,7 +173,7 @@ export const ProjectActionsModal = ({ ( ), }, @@ -202,7 +202,7 @@ export const ProjectActionsTable = ({ disableSortBy: true, }, ], - [incomingWebhooks, serviceAccounts], + [signalEndpoints, serviceAccounts], ); const [initialState] = useState({ diff --git a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsTriggerCell.tsx b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsTriggerCell.tsx index 73bd342ac1d4..9c0fdd134a46 100644 --- a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsTriggerCell.tsx +++ b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsTriggerCell.tsx @@ -1,7 +1,7 @@ import { Avatar, Box, Link, styled } from '@mui/material'; import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; import { IActionSet } from 'interfaces/action'; -import { IIncomingWebhook } from 'interfaces/incomingWebhook'; +import { ISignalEndpoint } from 'interfaces/signal'; import webhooksIcon from 'assets/icons/webhooks.svg'; import { Link as RouterLink } from 'react-router-dom'; import { ComponentType } from 'react'; @@ -32,15 +32,15 @@ const StyledLink = styled(Link)<{ interface IProjectActionsTriggerCellProps { action: IActionSet; - incomingWebhooks: IIncomingWebhook[]; + signalEndpoints: ISignalEndpoint[]; } export const ProjectActionsTriggerCell = ({ action, - incomingWebhooks, + signalEndpoints, }: IProjectActionsTriggerCellProps) => { const { sourceId } = action.match; - const trigger = incomingWebhooks.find(({ id }) => id === sourceId); + const trigger = signalEndpoints.find(({ id }) => id === sourceId); if (!trigger) { return No trigger; @@ -51,12 +51,12 @@ export const ProjectActionsTriggerCell = ({ {trigger.name} diff --git a/frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksForm.tsx b/frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsForm.tsx similarity index 86% rename from frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksForm.tsx rename to frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsForm.tsx index 7ab5618dd993..35eb655ebd03 100644 --- a/frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksForm.tsx +++ b/frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsForm.tsx @@ -10,13 +10,13 @@ import { import Input from 'component/common/Input/Input'; import { FormSwitch } from 'component/common/FormSwitch/FormSwitch'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { IIncomingWebhook } from 'interfaces/incomingWebhook'; +import { ISignalEndpoint } from 'interfaces/signal'; import { - IncomingWebhooksFormErrors, + SignalEndpointsFormErrors, TokenGeneration, -} from './useIncomingWebhooksForm'; -import { IncomingWebhooksFormURL } from './IncomingWebhooksFormURL'; -import { IncomingWebhooksTokens } from './IncomingWebhooksTokens/IncomingWebhooksTokens'; +} from './useSignalEndpointsForm'; +import { SignalEndpointsFormURL } from './SignalEndpointsFormURL'; +import { SignalEndpointsTokens } from './SignalEndpointsTokens/SignalEndpointsTokens'; const StyledRaisedSection = styled('div')(({ theme }) => ({ background: theme.palette.background.elevation1, @@ -59,8 +59,8 @@ const StyledInlineContainer = styled('div')(({ theme }) => ({ }, })); -interface IIncomingWebhooksFormProps { - incomingWebhook?: IIncomingWebhook; +interface ISignalEndpointsFormProps { + signalEndpoint?: ISignalEndpoint; enabled: boolean; setEnabled: React.Dispatch>; name: string; @@ -71,7 +71,7 @@ interface IIncomingWebhooksFormProps { setTokenGeneration: React.Dispatch>; tokenName: string; setTokenName: React.Dispatch>; - errors: IncomingWebhooksFormErrors; + errors: SignalEndpointsFormErrors; validateName: (name: string) => boolean; validateTokenName: ( tokenGeneration: TokenGeneration, @@ -80,8 +80,8 @@ interface IIncomingWebhooksFormProps { validated: boolean; } -export const IncomingWebhooksForm = ({ - incomingWebhook, +export const SignalEndpointsForm = ({ + signalEndpoint, enabled, setEnabled, name, @@ -96,7 +96,7 @@ export const IncomingWebhooksForm = ({ validateName, validateTokenName, validated, -}: IIncomingWebhooksFormProps) => { +}: ISignalEndpointsFormProps) => { const handleOnBlur = (callback: Function) => { setTimeout(() => callback(), 300); }; @@ -107,15 +107,15 @@ export const IncomingWebhooksForm = ({
- Incoming webhook status + Signal endpoint status - What is your new incoming webhook name? + What is your new signal endpoint name? - What is your new incoming webhook description? + What is your new signal endpoint description? setDescription(e.target.value)} autoComplete='off' /> - + Token - In order to connect your newly created incoming - webhook, you will also need a token.{' '} + In order to connect your newly created signal + endpoint, you will also need a token.{' '} - A new incoming webhook token will be generated - for the incoming webhook, so you can get started + A new signal endpoint token will be generated + for the signal endpoint, so you can get started right away. - Incoming webhook tokens + Signal endpoint tokens - } diff --git a/frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksFormURL.tsx b/frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsFormURL.tsx similarity index 66% rename from frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksFormURL.tsx rename to frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsFormURL.tsx index 4deaed9bbe8e..fb79ccdb9c1c 100644 --- a/frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksFormURL.tsx +++ b/frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsFormURL.tsx @@ -4,7 +4,7 @@ import copy from 'copy-to-clipboard'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useToast from 'hooks/useToast'; -const StyledIncomingWebhookUrlSection = styled('div')(({ theme }) => ({ +const StyledSignalEndpointUrlSection = styled('div')(({ theme }) => ({ display: 'flex', flexDirection: 'column', padding: theme.spacing(1.5), @@ -15,11 +15,11 @@ const StyledIncomingWebhookUrlSection = styled('div')(({ theme }) => ({ marginTop: theme.spacing(3), })); -const StyledIncomingWebhookUrlSectionDescription = styled('p')(({ theme }) => ({ +const StyledSignalEndpointUrlSectionDescription = styled('p')(({ theme }) => ({ fontSize: theme.fontSizes.smallBody, })); -const StyledIncomingWebhookUrl = styled('div')(({ theme }) => ({ +const StyledSignalEndpointUrl = styled('div')(({ theme }) => ({ fontSize: theme.fontSizes.smallBody, backgroundColor: theme.palette.background.elevation2, padding: theme.spacing(0.5, 1, 0.5, 2), @@ -31,17 +31,17 @@ const StyledIncomingWebhookUrl = styled('div')(({ theme }) => ({ wordBreak: 'break-all', })); -interface IIncomingWebhooksFormURLProps { +interface ISignalEndpointsFormURLProps { name: string; } -export const IncomingWebhooksFormURL = ({ +export const SignalEndpointsFormURL = ({ name, -}: IIncomingWebhooksFormURLProps) => { +}: ISignalEndpointsFormURLProps) => { const { uiConfig } = useUiConfig(); const { setToastData } = useToast(); - const url = `${uiConfig.unleashUrl}/api/incoming-webhook/${name}`; + const url = `${uiConfig.unleashUrl}/api/signal-endpoint/${name}`; const onCopyToClipboard = () => { copy(url); @@ -52,18 +52,18 @@ export const IncomingWebhooksFormURL = ({ }; return ( - - - Incoming webhook URL: - - + + + Signal endpoint URL: + + {url} - - + + ); }; diff --git a/frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksTokens/IncomingWebhooksTokens.tsx b/frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsTokens/SignalEndpointsTokens.tsx similarity index 82% rename from frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksTokens/IncomingWebhooksTokens.tsx rename to frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsTokens/SignalEndpointsTokens.tsx index 794fc2cccfa4..7d125b92f184 100644 --- a/frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksTokens/IncomingWebhooksTokens.tsx +++ b/frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsTokens/SignalEndpointsTokens.tsx @@ -15,27 +15,24 @@ import { DateCell } from 'component/common/Table/cells/DateCell/DateCell'; import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell'; import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; import { PAT_LIMIT } from '@server/util/constants'; -import { useIncomingWebhookTokens } from 'hooks/api/getters/useIncomingWebhookTokens/useIncomingWebhookTokens'; +import { useSignalEndpointTokens } from 'hooks/api/getters/useSignalEndpointTokens/useSignalEndpointTokens'; import { useSearch } from 'hooks/useSearch'; import { useMemo, useState } from 'react'; import { useTable, SortingRule, useSortBy, useFlexLayout } from 'react-table'; import { sortTypes } from 'utils/sortTypes'; -import { IncomingWebhooksTokensCreateDialog } from './IncomingWebhooksTokensCreateDialog'; -import { IncomingWebhooksTokensDialog } from './IncomingWebhooksTokensDialog'; +import { SignalEndpointsTokensCreateDialog } from './SignalEndpointsTokensCreateDialog'; +import { SignalEndpointsTokensDialog } from './SignalEndpointsTokensDialog'; import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { Dialogue } from 'component/common/Dialogue/Dialogue'; import { - IncomingWebhookTokenPayload, - useIncomingWebhookTokensApi, -} from 'hooks/api/actions/useIncomingWebhookTokensApi/useIncomingWebhookTokensApi'; + SignalEndpointTokenPayload, + useSignalEndpointTokensApi, +} from 'hooks/api/actions/useSignalEndpointTokensApi/useSignalEndpointTokensApi'; import useToast from 'hooks/useToast'; import { formatUnknownError } from 'utils/formatUnknownError'; -import { - IIncomingWebhook, - IIncomingWebhookToken, -} from 'interfaces/incomingWebhook'; -import { useIncomingWebhooks } from 'hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks'; +import { ISignalEndpoint, ISignalEndpointToken } from 'interfaces/signal'; +import { useSignalEndpoints } from 'hooks/api/getters/useSignalEndpoints/useSignalEndpoints'; const StyledHeader = styled('div')(({ theme }) => ({ display: 'flex', @@ -74,21 +71,21 @@ export type PageQueryType = Partial< const defaultSort: SortingRule = { id: 'createdAt', desc: true }; -interface IIncomingWebhooksTokensProps { - incomingWebhook: IIncomingWebhook; +interface ISignalEndpointsTokensProps { + signalEndpoint: ISignalEndpoint; } -export const IncomingWebhooksTokens = ({ - incomingWebhook, -}: IIncomingWebhooksTokensProps) => { +export const SignalEndpointsTokens = ({ + signalEndpoint, +}: ISignalEndpointsTokensProps) => { const theme = useTheme(); const { setToastData, setToastApiError } = useToast(); const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); - const { incomingWebhookTokens, refetch: refetchTokens } = - useIncomingWebhookTokens(incomingWebhook.id); - const { refetch } = useIncomingWebhooks(); - const { addIncomingWebhookToken, removeIncomingWebhookToken } = - useIncomingWebhookTokensApi(); + const { signalEndpointTokens, refetch: refetchTokens } = + useSignalEndpointTokens(signalEndpoint.id); + const { refetch } = useSignalEndpoints(); + const { addSignalEndpointToken, removeSignalEndpointToken } = + useSignalEndpointTokensApi(); const [initialState] = useState(() => ({ sortBy: [defaultSort], @@ -99,12 +96,12 @@ export const IncomingWebhooksTokens = ({ const [tokenOpen, setTokenOpen] = useState(false); const [deleteOpen, setDeleteOpen] = useState(false); const [newToken, setNewToken] = useState(''); - const [selectedToken, setSelectedToken] = useState(); + const [selectedToken, setSelectedToken] = useState(); - const onCreateClick = async (newToken: IncomingWebhookTokenPayload) => { + const onCreateClick = async (newToken: SignalEndpointTokenPayload) => { try { - const { token } = await addIncomingWebhookToken( - incomingWebhook.id, + const { token } = await addSignalEndpointToken( + signalEndpoint.id, newToken, ); refetch(); @@ -124,8 +121,8 @@ export const IncomingWebhooksTokens = ({ const onDeleteClick = async () => { if (selectedToken) { try { - await removeIncomingWebhookToken( - incomingWebhook.id, + await removeSignalEndpointToken( + signalEndpoint.id, selectedToken.id, ); refetch(); @@ -186,7 +183,7 @@ export const IncomingWebhooksTokens = ({ const { data, getSearchText, getSearchContext } = useSearch( columns, searchValue, - incomingWebhookTokens, + signalEndpointTokens, ); const { headerGroups, rows, prepareRow, setHiddenColumns } = useTable( @@ -226,7 +223,7 @@ export const IncomingWebhooksTokens = ({ } /> } /> - - Any applications or scripts using this token " {selectedToken?.name}" will no longer be - able to make requests to this incoming webhook. You cannot + able to make requests to this signal endpoint. You cannot undo this action. diff --git a/frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksTokens/IncomingWebhooksTokensCreateDialog.tsx b/frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsTokens/SignalEndpointsTokensCreateDialog.tsx similarity index 84% rename from frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksTokens/IncomingWebhooksTokensCreateDialog.tsx rename to frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsTokens/SignalEndpointsTokensCreateDialog.tsx index 6e94f60515e7..72edd500546a 100644 --- a/frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksTokens/IncomingWebhooksTokensCreateDialog.tsx +++ b/frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsTokens/SignalEndpointsTokensCreateDialog.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react'; import { Dialogue } from 'component/common/Dialogue/Dialogue'; -import { IncomingWebhookTokenPayload } from 'hooks/api/actions/useIncomingWebhookTokensApi/useIncomingWebhookTokensApi'; -import { IIncomingWebhookToken } from 'interfaces/incomingWebhook'; +import { SignalEndpointTokenPayload } from 'hooks/api/actions/useSignalEndpointTokensApi/useSignalEndpointTokensApi'; +import { ISignalEndpointToken } from 'interfaces/signal'; import { styled } from '@mui/material'; import Input from 'component/common/Input/Input'; @@ -19,19 +19,19 @@ const StyledInput = styled(Input)(({ theme }) => ({ maxWidth: theme.spacing(50), })); -interface IIncomingWebhooksTokensCreateDialogProps { +interface ISignalEndpointsTokensCreateDialogProps { open: boolean; setOpen: React.Dispatch>; - tokens: IIncomingWebhookToken[]; - onCreateClick: (newToken: IncomingWebhookTokenPayload) => void; + tokens: ISignalEndpointToken[]; + onCreateClick: (newToken: SignalEndpointTokenPayload) => void; } -export const IncomingWebhooksTokensCreateDialog = ({ +export const SignalEndpointsTokensCreateDialog = ({ open, setOpen, tokens, onCreateClick, -}: IIncomingWebhooksTokensCreateDialogProps) => { +}: ISignalEndpointsTokensCreateDialogProps) => { const [name, setName] = useState(''); const [nameError, setNameError] = useState(''); diff --git a/frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksTokens/IncomingWebhooksTokensDialog.tsx b/frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsTokens/SignalEndpointsTokensDialog.tsx similarity index 76% rename from frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksTokens/IncomingWebhooksTokensDialog.tsx rename to frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsTokens/SignalEndpointsTokensDialog.tsx index 9e3b76ae9fa8..a9f8ccbe7aad 100644 --- a/frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksTokens/IncomingWebhooksTokensDialog.tsx +++ b/frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsTokens/SignalEndpointsTokensDialog.tsx @@ -6,17 +6,17 @@ const StyledAlert = styled(Alert)(({ theme }) => ({ marginBottom: theme.spacing(3), })); -interface IIncomingWebhooksTokensDialogProps { +interface ISignalEndpointsTokensDialogProps { open: boolean; setOpen: React.Dispatch>; token?: string; } -export const IncomingWebhooksTokensDialog = ({ +export const SignalEndpointsTokensDialog = ({ open, setOpen, token, -}: IIncomingWebhooksTokensDialogProps) => ( +}: ISignalEndpointsTokensDialogProps) => ( - Make sure to copy your incoming webhook token now. You won't be able + Make sure to copy your signal endpoint token now. You won't be able to see it again! Your token: diff --git a/frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/useIncomingWebhooksForm.ts b/frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsForm/useSignalEndpointsForm.ts similarity index 76% rename from frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/useIncomingWebhooksForm.ts rename to frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsForm/useSignalEndpointsForm.ts index 5642ad87235f..1d6d6686e1ab 100644 --- a/frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksForm/useIncomingWebhooksForm.ts +++ b/frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsForm/useSignalEndpointsForm.ts @@ -1,6 +1,6 @@ import { URL_SAFE_BASIC } from '@server/util/constants'; -import { useIncomingWebhooks } from 'hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks'; -import { IIncomingWebhook } from 'interfaces/incomingWebhook'; +import { useSignalEndpoints } from 'hooks/api/getters/useSignalEndpoints/useSignalEndpoints'; +import { ISignalEndpoint } from 'interfaces/signal'; import { useEffect, useState } from 'react'; enum ErrorField { @@ -8,20 +8,20 @@ enum ErrorField { TOKEN_NAME = 'tokenName', } -const DEFAULT_INCOMING_WEBHOOKS_FORM_ERRORS = { +const DEFAULT_SIGNAL_ENDPOINTS_FORM_ERRORS = { [ErrorField.NAME]: undefined, [ErrorField.TOKEN_NAME]: undefined, }; -export type IncomingWebhooksFormErrors = Record; +export type SignalEndpointsFormErrors = Record; export enum TokenGeneration { LATER = 'later', NOW = 'now', } -export const useIncomingWebhooksForm = (incomingWebhook?: IIncomingWebhook) => { - const { incomingWebhooks } = useIncomingWebhooks(); +export const useSignalEndpointsForm = (signalEndpoint?: ISignalEndpoint) => { + const { signalEndpoints } = useSignalEndpoints(); const [enabled, setEnabled] = useState(false); const [name, setName] = useState(''); @@ -32,21 +32,21 @@ export const useIncomingWebhooksForm = (incomingWebhook?: IIncomingWebhook) => { const [tokenName, setTokenName] = useState(''); const reloadForm = () => { - setEnabled(incomingWebhook?.enabled ?? true); - setName(incomingWebhook?.name || ''); - setDescription(incomingWebhook?.description || ''); + setEnabled(signalEndpoint?.enabled ?? true); + setName(signalEndpoint?.name || ''); + setDescription(signalEndpoint?.description || ''); setTokenGeneration(TokenGeneration.LATER); setTokenName(''); setValidated(false); - setErrors(DEFAULT_INCOMING_WEBHOOKS_FORM_ERRORS); + setErrors(DEFAULT_SIGNAL_ENDPOINTS_FORM_ERRORS); }; useEffect(() => { reloadForm(); - }, [incomingWebhook]); + }, [signalEndpoint]); - const [errors, setErrors] = useState( - DEFAULT_INCOMING_WEBHOOKS_FORM_ERRORS, + const [errors, setErrors] = useState( + DEFAULT_SIGNAL_ENDPOINTS_FORM_ERRORS, ); const [validated, setValidated] = useState(false); @@ -61,8 +61,8 @@ export const useIncomingWebhooksForm = (incomingWebhook?: IIncomingWebhook) => { const isEmpty = (value: string) => !value.length; const isNameNotUnique = (value: string) => - incomingWebhooks?.some( - ({ id, name }) => id !== incomingWebhook?.id && name === value, + signalEndpoints?.some( + ({ id, name }) => id !== signalEndpoint?.id && name === value, ); const isNameInvalid = (value: string) => !URL_SAFE_BASIC.test(value); diff --git a/frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksModal.tsx b/frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsModal.tsx similarity index 70% rename from frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksModal.tsx rename to frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsModal.tsx index a9dd8ee0e237..4632df970346 100644 --- a/frontend/src/component/incomingWebhooks/IncomingWebhooksModal/IncomingWebhooksModal.tsx +++ b/frontend/src/component/signals/SignalEndpointsModal/SignalEndpointsModal.tsx @@ -5,18 +5,18 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import FormTemplate from 'component/common/FormTemplate/FormTemplate'; import useToast from 'hooks/useToast'; import { formatUnknownError } from 'utils/formatUnknownError'; -import { IIncomingWebhook } from 'interfaces/incomingWebhook'; -import { useIncomingWebhooks } from 'hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks'; +import { ISignalEndpoint } from 'interfaces/signal'; +import { useSignalEndpoints } from 'hooks/api/getters/useSignalEndpoints/useSignalEndpoints'; import { - IncomingWebhookPayload, - useIncomingWebhooksApi, -} from 'hooks/api/actions/useIncomingWebhooksApi/useIncomingWebhooksApi'; -import { useIncomingWebhookTokensApi } from 'hooks/api/actions/useIncomingWebhookTokensApi/useIncomingWebhookTokensApi'; -import { IncomingWebhooksForm } from './IncomingWebhooksForm/IncomingWebhooksForm'; + SignalEndpointPayload, + useSignalEndpointsApi, +} from 'hooks/api/actions/useSignalEndpointsApi/useSignalEndpointsApi'; +import { useSignalEndpointTokensApi } from 'hooks/api/actions/useSignalEndpointTokensApi/useSignalEndpointTokensApi'; +import { SignalEndpointsForm } from './SignalEndpointsForm/SignalEndpointsForm'; import { TokenGeneration, - useIncomingWebhooksForm, -} from './IncomingWebhooksForm/useIncomingWebhooksForm'; + useSignalEndpointsForm, +} from './SignalEndpointsForm/useSignalEndpointsForm'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; const StyledHeader = styled('div')(({ theme }) => ({ @@ -48,25 +48,25 @@ const StyledCancelButton = styled(Button)(({ theme }) => ({ marginLeft: theme.spacing(3), })); -interface IIncomingWebhooksModalProps { - incomingWebhook?: IIncomingWebhook; +interface ISignalEndpointsModalProps { + signalEndpoint?: ISignalEndpoint; open: boolean; setOpen: React.Dispatch>; newToken: (token: string) => void; - onOpenEvents: () => void; + onOpenSignals: () => void; } -export const IncomingWebhooksModal = ({ - incomingWebhook, +export const SignalEndpointsModal = ({ + signalEndpoint, open, setOpen, newToken, - onOpenEvents, -}: IIncomingWebhooksModalProps) => { - const { refetch } = useIncomingWebhooks(); - const { addIncomingWebhook, updateIncomingWebhook, loading } = - useIncomingWebhooksApi(); - const { addIncomingWebhookToken } = useIncomingWebhookTokensApi(); + onOpenSignals, +}: ISignalEndpointsModalProps) => { + const { refetch } = useSignalEndpoints(); + const { addSignalEndpoint, updateSignalEndpoint, loading } = + useSignalEndpointsApi(); + const { addSignalEndpointToken } = useSignalEndpointTokensApi(); const { setToastData, setToastApiError } = useToast(); const { uiConfig } = useUiConfig(); @@ -87,16 +87,16 @@ export const IncomingWebhooksModal = ({ validate, validated, reloadForm, - } = useIncomingWebhooksForm(incomingWebhook); + } = useSignalEndpointsForm(signalEndpoint); useEffect(() => { reloadForm(); }, [open]); - const editing = incomingWebhook !== undefined; - const title = `${editing ? 'Edit' : 'New'} incoming webhook`; + const editing = signalEndpoint !== undefined; + const title = `${editing ? 'Edit' : 'New'} signal endpoint`; - const payload: IncomingWebhookPayload = { + const payload: SignalEndpointPayload = { enabled, name, description, @@ -104,8 +104,8 @@ export const IncomingWebhooksModal = ({ const formatApiCode = () => `curl --location --request ${ editing ? 'PUT' : 'POST' - } '${uiConfig.unleashUrl}/api/admin/incoming-webhooks${ - editing ? `/${incomingWebhook.id}` : '' + } '${uiConfig.unleashUrl}/api/admin/signal-endpoints${ + editing ? `/${signalEndpoint.id}` : '' }' \\ --header 'Authorization: INSERT_API_KEY' \\ --header 'Content-Type: application/json' \\ @@ -118,18 +118,18 @@ export const IncomingWebhooksModal = ({ try { if (editing) { - await updateIncomingWebhook(incomingWebhook.id, payload); + await updateSignalEndpoint(signalEndpoint.id, payload); } else { - const { id } = await addIncomingWebhook(payload); + const { id } = await addSignalEndpoint(payload); if (tokenGeneration === TokenGeneration.NOW) { - const { token } = await addIncomingWebhookToken(id, { + const { token } = await addSignalEndpointToken(id, { name: tokenName, }); newToken(token); } } setToastData({ - title: `Incoming webhook ${ + title: `Signal endpoint ${ editing ? 'updated' : 'added' } successfully`, type: 'success', @@ -152,21 +152,21 @@ export const IncomingWebhooksModal = ({ {title} View events} + show={View signals} /> - - {editing ? 'Save' : 'Add'} incoming webhook + {editing ? 'Save' : 'Add'} signal endpoint { diff --git a/frontend/src/component/incomingWebhooks/IncomingWebhooksEvents/IncomingWebhooksEventsModal.tsx b/frontend/src/component/signals/SignalEndpointsSignals/SignalEndpointsSignalsModal.tsx similarity index 77% rename from frontend/src/component/incomingWebhooks/IncomingWebhooksEvents/IncomingWebhooksEventsModal.tsx rename to frontend/src/component/signals/SignalEndpointsSignals/SignalEndpointsSignalsModal.tsx index b7c99c157d17..2ae065a6172f 100644 --- a/frontend/src/component/incomingWebhooks/IncomingWebhooksEvents/IncomingWebhooksEventsModal.tsx +++ b/frontend/src/component/signals/SignalEndpointsSignals/SignalEndpointsSignalsModal.tsx @@ -1,7 +1,7 @@ import { Button, Link, styled } from '@mui/material'; import { SidebarModal } from 'component/common/SidebarModal/SidebarModal'; -import { IIncomingWebhook } from 'interfaces/incomingWebhook'; -import { useIncomingWebhookEvents } from 'hooks/api/getters/useIncomingWebhookEvents/useIncomingWebhookEvents'; +import { ISignalEndpoint } from 'interfaces/signal'; +import { useSignalEndpointSignals } from 'hooks/api/getters/useSignalEndpointSignals/useSignalEndpointSignals'; import { Suspense, lazy } from 'react'; import FormTemplate from 'component/common/FormTemplate/FormTemplate'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; @@ -55,31 +55,31 @@ const StyledButtonContainer = styled('div')(({ theme }) => ({ paddingTop: theme.spacing(4), })); -interface IIncomingWebhooksEventsModalProps { - incomingWebhook?: IIncomingWebhook; +interface ISignalEndpointsSignalsModalProps { + signalEndpoint?: ISignalEndpoint; open: boolean; setOpen: React.Dispatch>; onOpenConfiguration: () => void; } -export const IncomingWebhooksEventsModal = ({ - incomingWebhook, +export const SignalEndpointsSignalsModal = ({ + signalEndpoint, open, setOpen, onOpenConfiguration, -}: IIncomingWebhooksEventsModalProps) => { +}: ISignalEndpointsSignalsModalProps) => { const { uiConfig } = useUiConfig(); const { locationSettings } = useLocationSettings(); - const { incomingWebhookEvents, hasMore, loadMore, loading } = - useIncomingWebhookEvents(incomingWebhook?.id, 20, { + const { signalEndpointSignals, hasMore, loadMore, loading } = + useSignalEndpointSignals(signalEndpoint?.id, 20, { refreshInterval: 5000, }); - if (!incomingWebhook) { + if (!signalEndpoint) { return null; } - const title = `Events: ${incomingWebhook.name}`; + const title = `Signals: ${signalEndpoint.name}`; return ( @@ -106,32 +106,32 @@ export const IncomingWebhooksEventsModal = ({

- {uiConfig.unleashUrl}/api/incoming-webhook/ - {incomingWebhook.name} + {uiConfig.unleashUrl}/api/signal-endpoint/ + {signalEndpoint.name}

- {incomingWebhook.description} + {signalEndpoint.description}
+ cell: ({ createdAt }) => formatDateYMDHMS( - event.createdAt, + createdAt, locationSettings?.locale, ), }, { header: 'Token', maxWidth: 350, - cell: (event) => event.tokenName, + cell: ({ tokenName }) => tokenName, }, ]} sidePanelHeader='Payload' @@ -157,11 +157,11 @@ export const IncomingWebhooksEventsModal = ({ } /> - No events have been received for this incoming - webhook. + No signals have been received on this signal + endpoint.

} /> diff --git a/frontend/src/component/incomingWebhooks/IncomingWebhooksTable/IncomingWebhooksActionsCell.tsx b/frontend/src/component/signals/SignalEndpointsTable/SignalEndpointsActionsCell.tsx similarity index 91% rename from frontend/src/component/incomingWebhooks/IncomingWebhooksTable/IncomingWebhooksActionsCell.tsx rename to frontend/src/component/signals/SignalEndpointsTable/SignalEndpointsActionsCell.tsx index 53f4af211256..49054f59520d 100644 --- a/frontend/src/component/incomingWebhooks/IncomingWebhooksTable/IncomingWebhooksActionsCell.tsx +++ b/frontend/src/component/signals/SignalEndpointsTable/SignalEndpointsActionsCell.tsx @@ -23,21 +23,21 @@ const StyledBoxCell = styled(Box)({ justifyContent: 'center', }); -interface IIncomingWebhooksActionsCellProps { - incomingWebhookId: number; +interface ISignalEndpointsActionsCellProps { + signalEndpointId: number; onCopyToClipboard: (event: React.SyntheticEvent) => void; - onOpenEvents: (event: React.SyntheticEvent) => void; + onOpenSignals: (event: React.SyntheticEvent) => void; onEdit: (event: React.SyntheticEvent) => void; onDelete: (event: React.SyntheticEvent) => void; } -export const IncomingWebhooksActionsCell = ({ - incomingWebhookId, +export const SignalEndpointsActionsCell = ({ + signalEndpointId, onCopyToClipboard, - onOpenEvents, + onOpenSignals, onEdit, onDelete, -}: IIncomingWebhooksActionsCellProps) => { +}: ISignalEndpointsActionsCellProps) => { const [anchorEl, setAnchorEl] = useState(null); const open = Boolean(anchorEl); @@ -50,12 +50,12 @@ export const IncomingWebhooksActionsCell = ({ setAnchorEl(null); }; - const id = `incoming-webhook-${incomingWebhookId}-actions`; + const id = `signal-endpoint-${signalEndpointId}-actions`; const menuId = `${id}-menu`; return ( - + ( @@ -108,7 +108,7 @@ export const IncomingWebhooksActionsCell = ({ - View events + View signals diff --git a/frontend/src/component/signals/SignalEndpointsTable/SignalEndpointsDeleteDialog.tsx b/frontend/src/component/signals/SignalEndpointsTable/SignalEndpointsDeleteDialog.tsx new file mode 100644 index 000000000000..7639e16b5360 --- /dev/null +++ b/frontend/src/component/signals/SignalEndpointsTable/SignalEndpointsDeleteDialog.tsx @@ -0,0 +1,32 @@ +import { Dialogue } from 'component/common/Dialogue/Dialogue'; +import { ISignalEndpoint } from 'interfaces/signal'; + +interface ISignalEndpointsDeleteDialogProps { + signalEndpoint?: ISignalEndpoint; + open: boolean; + setOpen: React.Dispatch>; + onConfirm: (signalEndpoint: ISignalEndpoint) => void; +} + +export const SignalEndpointsDeleteDialog = ({ + signalEndpoint, + open, + setOpen, + onConfirm, +}: ISignalEndpointsDeleteDialogProps) => ( + onConfirm(signalEndpoint!)} + onClose={() => { + setOpen(false); + }} + > +

+ You are about to delete signal endpoint:{' '} + {signalEndpoint?.name} +

+
+); diff --git a/frontend/src/component/incomingWebhooks/IncomingWebhooksTable/IncomingWebhooksTable.tsx b/frontend/src/component/signals/SignalEndpointsTable/SignalEndpointsTable.tsx similarity index 56% rename from frontend/src/component/incomingWebhooks/IncomingWebhooksTable/IncomingWebhooksTable.tsx rename to frontend/src/component/signals/SignalEndpointsTable/SignalEndpointsTable.tsx index 38072b9fb9af..3246b7d26c01 100644 --- a/frontend/src/component/incomingWebhooks/IncomingWebhooksTable/IncomingWebhooksTable.tsx +++ b/frontend/src/component/signals/SignalEndpointsTable/SignalEndpointsTable.tsx @@ -3,65 +3,55 @@ import { TablePlaceholder, VirtualizedTable } from 'component/common/Table'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import useToast from 'hooks/useToast'; import { formatUnknownError } from 'utils/formatUnknownError'; -import { useMediaQuery } from '@mui/material'; +import { Button, useMediaQuery } from '@mui/material'; import { useFlexLayout, useSortBy, useTable } from 'react-table'; import { sortTypes } from 'utils/sortTypes'; import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; import { DateCell } from 'component/common/Table/cells/DateCell/DateCell'; import theme from 'themes/theme'; import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns'; -import { useIncomingWebhooks } from 'hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks'; -import { useIncomingWebhooksApi } from 'hooks/api/actions/useIncomingWebhooksApi/useIncomingWebhooksApi'; -import { IIncomingWebhook } from 'interfaces/incomingWebhook'; -import { IncomingWebhooksActionsCell } from './IncomingWebhooksActionsCell'; -import { IncomingWebhooksDeleteDialog } from './IncomingWebhooksDeleteDialog'; +import { useSignalEndpoints } from 'hooks/api/getters/useSignalEndpoints/useSignalEndpoints'; +import { useSignalEndpointsApi } from 'hooks/api/actions/useSignalEndpointsApi/useSignalEndpointsApi'; +import { ISignalEndpoint } from 'interfaces/signal'; +import { SignalEndpointsActionsCell } from './SignalEndpointsActionsCell'; +import { SignalEndpointsDeleteDialog } from './SignalEndpointsDeleteDialog'; import { ToggleCell } from 'component/common/Table/cells/ToggleCell/ToggleCell'; import copy from 'copy-to-clipboard'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; -import { IncomingWebhookTokensCell } from './IncomingWebhooksTokensCell'; -import { IncomingWebhooksModal } from '../IncomingWebhooksModal/IncomingWebhooksModal'; -import { IncomingWebhooksTokensDialog } from '../IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksTokens/IncomingWebhooksTokensDialog'; +import { SignalEndpointsTokensCell } from './SignalEndpointsTokensCell'; +import { SignalEndpointsModal } from '../SignalEndpointsModal/SignalEndpointsModal'; +import { SignalEndpointsTokensDialog } from '../SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsTokens/SignalEndpointsTokensDialog'; import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell'; -import { IncomingWebhooksEventsModal } from '../IncomingWebhooksEvents/IncomingWebhooksEventsModal'; +import { SignalEndpointsSignalsModal } from '../SignalEndpointsSignals/SignalEndpointsSignalsModal'; +import { PageContent } from 'component/common/PageContent/PageContent'; +import { PageHeader } from 'component/common/PageHeader/PageHeader'; -interface IIncomingWebhooksTableProps { - modalOpen: boolean; - setModalOpen: React.Dispatch>; - selectedIncomingWebhook?: IIncomingWebhook; - setSelectedIncomingWebhook: React.Dispatch< - React.SetStateAction - >; -} - -export const IncomingWebhooksTable = ({ - modalOpen, - setModalOpen, - selectedIncomingWebhook, - setSelectedIncomingWebhook, -}: IIncomingWebhooksTableProps) => { +export const SignalEndpointsTable = () => { const { setToastData, setToastApiError } = useToast(); const { uiConfig } = useUiConfig(); - const { incomingWebhooks, refetch } = useIncomingWebhooks(); - const { toggleIncomingWebhook, removeIncomingWebhook } = - useIncomingWebhooksApi(); + const { signalEndpoints, refetch } = useSignalEndpoints(); + const { toggleSignalEndpoint, removeSignalEndpoint } = + useSignalEndpointsApi(); + + const [selectedSignalEndpoint, setSelectedSignalEndpoint] = + useState(); + const [modalOpen, setModalOpen] = useState(false); const [tokenDialog, setTokenDialog] = useState(false); const [newToken, setNewToken] = useState(''); const [deleteOpen, setDeleteOpen] = useState(false); - const [eventsModalOpen, setEventsModalOpen] = useState(false); + const [signalsModalOpen, setSignalsModalOpen] = useState(false); - const onToggleIncomingWebhook = async ( - incomingWebhook: IIncomingWebhook, + const onToggleSignalEndpoint = async ( + { id, name }: ISignalEndpoint, enabled: boolean, ) => { try { - await toggleIncomingWebhook(incomingWebhook.id, enabled); + await toggleSignalEndpoint(id, enabled); setToastData({ - title: `"${incomingWebhook.name}" has been ${ - enabled ? 'enabled' : 'disabled' - }`, + title: `"${name}" has been ${enabled ? 'enabled' : 'disabled'}`, type: 'success', }); refetch(); @@ -70,11 +60,11 @@ export const IncomingWebhooksTable = ({ } }; - const onDeleteConfirm = async (incomingWebhook: IIncomingWebhook) => { + const onDeleteConfirm = async ({ id, name }: ISignalEndpoint) => { try { - await removeIncomingWebhook(incomingWebhook.id); + await removeSignalEndpoint(id); setToastData({ - title: `"${incomingWebhook.name}" has been deleted`, + title: `"${name}" has been deleted`, type: 'success', }); refetch(); @@ -92,42 +82,42 @@ export const IncomingWebhooksTable = ({ Header: 'Name', accessor: 'name', Cell: ({ - row: { original: incomingWebhook }, - }: { row: { original: IIncomingWebhook } }) => ( + row: { original: signalEndpoint }, + }: { row: { original: ISignalEndpoint } }) => ( { - setSelectedIncomingWebhook(incomingWebhook); + setSelectedSignalEndpoint(signalEndpoint); setModalOpen(true); }} - subtitle={incomingWebhook.description} + subtitle={signalEndpoint.description} /> ), width: 240, }, { Header: 'URL', - accessor: (row: IIncomingWebhook) => - `${uiConfig.unleashUrl}/api/incoming-webhook/${row.name}`, + accessor: (row: ISignalEndpoint) => + `${uiConfig.unleashUrl}/api/signal-endpoint/${row.name}`, minWidth: 200, }, { id: 'tokens', Header: 'Tokens', - accessor: (row: IIncomingWebhook) => + accessor: (row: ISignalEndpoint) => row.tokens?.map(({ name }) => name).join('\n') || '', Cell: ({ - row: { original: incomingWebhook }, + row: { original: signalEndpoint }, value, }: { - row: { original: IIncomingWebhook }; + row: { original: ISignalEndpoint }; value: string; }) => ( - { - setSelectedIncomingWebhook(incomingWebhook); + setSelectedSignalEndpoint(signalEndpoint); setModalOpen(true); }} /> @@ -146,12 +136,12 @@ export const IncomingWebhooksTable = ({ Header: 'Enabled', accessor: 'enabled', Cell: ({ - row: { original: incomingWebhook }, - }: { row: { original: IIncomingWebhook } }) => ( + row: { original: signalEndpoint }, + }: { row: { original: ISignalEndpoint } }) => ( - onToggleIncomingWebhook(incomingWebhook, enabled) + onToggleSignalEndpoint(signalEndpoint, enabled) } /> ), @@ -164,29 +154,29 @@ export const IncomingWebhooksTable = ({ id: 'Actions', align: 'center', Cell: ({ - row: { original: incomingWebhook }, - }: { row: { original: IIncomingWebhook } }) => ( - ( + { copy( - `${uiConfig.unleashUrl}/api/incoming-webhook/${incomingWebhook.name}`, + `${uiConfig.unleashUrl}/api/signal-endpoint/${signalEndpoint.name}`, ); setToastData({ type: 'success', title: 'Copied to clipboard', }); }} - onOpenEvents={() => { - setSelectedIncomingWebhook(incomingWebhook); - setEventsModalOpen(true); + onOpenSignals={() => { + setSelectedSignalEndpoint(signalEndpoint); + setSignalsModalOpen(true); }} onEdit={() => { - setSelectedIncomingWebhook(incomingWebhook); + setSelectedSignalEndpoint(signalEndpoint); setModalOpen(true); }} onDelete={() => { - setSelectedIncomingWebhook(incomingWebhook); + setSelectedSignalEndpoint(signalEndpoint); setDeleteOpen(true); }} /> @@ -205,7 +195,7 @@ export const IncomingWebhooksTable = ({ const { headerGroups, rows, prepareRow, setHiddenColumns } = useTable( { columns: columns as any, - data: incomingWebhooks, + data: signalEndpoints, initialState, sortTypes, autoResetHiddenColumns: false, @@ -232,7 +222,25 @@ export const IncomingWebhooksTable = ({ ); return ( - <> + { + setSelectedSignalEndpoint(undefined); + setModalOpen(true); + }} + > + New signal endpoint + + } + /> + } + > - No incoming webhooks available. Get started by adding + No signal endpoints available. Get started by adding one. } /> - { setNewToken(token); setTokenDialog(true); }} - onOpenEvents={() => { + onOpenSignals={() => { setModalOpen(false); - setEventsModalOpen(true); + setSignalsModalOpen(true); }} /> - { - setEventsModalOpen(false); + setSignalsModalOpen(false); setModalOpen(true); }} /> - - - + ); }; diff --git a/frontend/src/component/incomingWebhooks/IncomingWebhooksTable/IncomingWebhooksTokensCell.tsx b/frontend/src/component/signals/SignalEndpointsTable/SignalEndpointsTokensCell.tsx similarity index 73% rename from frontend/src/component/incomingWebhooks/IncomingWebhooksTable/IncomingWebhooksTokensCell.tsx rename to frontend/src/component/signals/SignalEndpointsTable/SignalEndpointsTokensCell.tsx index 7a0f444829f2..60a33d71979d 100644 --- a/frontend/src/component/incomingWebhooks/IncomingWebhooksTable/IncomingWebhooksTokensCell.tsx +++ b/frontend/src/component/signals/SignalEndpointsTable/SignalEndpointsTokensCell.tsx @@ -2,7 +2,7 @@ import { styled, Typography } from '@mui/material'; import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; import { Highlighter } from 'component/common/Highlighter/Highlighter'; import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; -import { IIncomingWebhook } from 'interfaces/incomingWebhook'; +import { ISignalEndpoint } from 'interfaces/signal'; import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell'; import { TooltipLink } from 'component/common/TooltipLink/TooltipLink'; @@ -10,20 +10,20 @@ const StyledItem = styled(Typography)(({ theme }) => ({ fontSize: theme.fontSizes.smallerBody, })); -interface IIncomingWebhookTokensCellProps { - incomingWebhook: IIncomingWebhook; +interface ISignalEndpointsTokensCellProps { + signalEndpoint: ISignalEndpoint; value: string; onCreateToken?: () => void; } -export const IncomingWebhookTokensCell = ({ - incomingWebhook, +export const SignalEndpointsTokensCell = ({ + signalEndpoint: { tokens }, value, onCreateToken, -}: IIncomingWebhookTokensCellProps) => { +}: ISignalEndpointsTokensCellProps) => { const { searchQuery } = useSearchHighlightContext(); - if (!incomingWebhook.tokens || incomingWebhook.tokens.length === 0) { + if (!tokens || tokens.length === 0) { if (!onCreateToken) return 0 tokens; else return ; } @@ -33,7 +33,7 @@ export const IncomingWebhookTokensCell = ({ - {incomingWebhook.tokens?.map(({ id, name }) => ( + {tokens?.map(({ id, name }) => ( {name} @@ -47,9 +47,7 @@ export const IncomingWebhookTokensCell = ({ value.toLowerCase().includes(searchQuery.toLowerCase()) } > - {incomingWebhook.tokens?.length === 1 - ? '1 token' - : `${incomingWebhook.tokens?.length} tokens`} + {tokens?.length === 1 ? '1 token' : `${tokens?.length} tokens`} ); diff --git a/frontend/src/component/signals/Signals.tsx b/frontend/src/component/signals/Signals.tsx new file mode 100644 index 000000000000..573893831178 --- /dev/null +++ b/frontend/src/component/signals/Signals.tsx @@ -0,0 +1,21 @@ +import { ADMIN } from 'component/providers/AccessProvider/permissions'; +import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard'; +import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; +import { PremiumFeature } from 'component/common/PremiumFeature/PremiumFeature'; +import { SignalEndpointsTable } from './SignalEndpointsTable/SignalEndpointsTable'; + +export const Signals = () => { + const { isEnterprise } = useUiConfig(); + + if (!isEnterprise()) { + return ; + } + + return ( +
+ + + +
+ ); +}; diff --git a/frontend/src/hooks/api/actions/useIncomingWebhookTokensApi/useIncomingWebhookTokensApi.ts b/frontend/src/hooks/api/actions/useIncomingWebhookTokensApi/useIncomingWebhookTokensApi.ts deleted file mode 100644 index ccf384574af1..000000000000 --- a/frontend/src/hooks/api/actions/useIncomingWebhookTokensApi/useIncomingWebhookTokensApi.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { IIncomingWebhookToken } from 'interfaces/incomingWebhook'; -import useAPI from '../useApi/useApi'; - -const ENDPOINT = 'api/admin/incoming-webhooks'; - -export type IncomingWebhookTokenPayload = Omit< - IIncomingWebhookToken, - 'id' | 'incomingWebhookId' | 'createdAt' | 'createdByUserId' ->; - -type IncomingWebhookTokenWithTokenSecret = IIncomingWebhookToken & { - token: string; -}; - -export const useIncomingWebhookTokensApi = () => { - const { loading, makeRequest, createRequest, errors } = useAPI({ - propagateErrors: true, - }); - - const addIncomingWebhookToken = async ( - incomingWebhookId: number, - incomingWebhookToken: IncomingWebhookTokenPayload, - ): Promise => { - const requestId = 'addIncomingWebhookToken'; - const req = createRequest( - `${ENDPOINT}/${incomingWebhookId}/tokens`, - { - method: 'POST', - body: JSON.stringify(incomingWebhookToken), - }, - requestId, - ); - - const response = await makeRequest(req.caller, req.id); - return response.json(); - }; - - const updateIncomingWebhookToken = async ( - incomingWebhookId: number, - incomingWebhookTokenId: number, - incomingWebhookToken: IncomingWebhookTokenPayload, - ) => { - const requestId = 'updateIncomingWebhookToken'; - const req = createRequest( - `${ENDPOINT}/${incomingWebhookId}/tokens/${incomingWebhookTokenId}`, - { - method: 'PUT', - body: JSON.stringify(incomingWebhookToken), - }, - requestId, - ); - - await makeRequest(req.caller, req.id); - }; - - const removeIncomingWebhookToken = async ( - incomingWebhookId: number, - incomingWebhookTokenId: number, - ) => { - const requestId = 'removeIncomingWebhookToken'; - const req = createRequest( - `${ENDPOINT}/${incomingWebhookId}/tokens/${incomingWebhookTokenId}`, - { method: 'DELETE' }, - requestId, - ); - - await makeRequest(req.caller, req.id); - }; - - return { - addIncomingWebhookToken, - updateIncomingWebhookToken, - removeIncomingWebhookToken, - errors, - loading, - }; -}; diff --git a/frontend/src/hooks/api/actions/useIncomingWebhooksApi/useIncomingWebhooksApi.ts b/frontend/src/hooks/api/actions/useIncomingWebhooksApi/useIncomingWebhooksApi.ts deleted file mode 100644 index de5773f7a344..000000000000 --- a/frontend/src/hooks/api/actions/useIncomingWebhooksApi/useIncomingWebhooksApi.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { IIncomingWebhook } from 'interfaces/incomingWebhook'; -import useAPI from '../useApi/useApi'; - -const ENDPOINT = 'api/admin/incoming-webhooks'; - -export type IncomingWebhookPayload = Omit< - IIncomingWebhook, - 'id' | 'createdAt' | 'createdByUserId' | 'tokens' ->; - -export const useIncomingWebhooksApi = () => { - const { loading, makeRequest, createRequest, errors } = useAPI({ - propagateErrors: true, - }); - - const addIncomingWebhook = async ( - incomingWebhook: IncomingWebhookPayload, - ) => { - 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: IncomingWebhookPayload, - ) => { - 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/actions/useSignalEndpointTokensApi/useSignalEndpointTokensApi.ts b/frontend/src/hooks/api/actions/useSignalEndpointTokensApi/useSignalEndpointTokensApi.ts new file mode 100644 index 000000000000..92c12dae3ac3 --- /dev/null +++ b/frontend/src/hooks/api/actions/useSignalEndpointTokensApi/useSignalEndpointTokensApi.ts @@ -0,0 +1,77 @@ +import { ISignalEndpointToken } from 'interfaces/signal'; +import useAPI from '../useApi/useApi'; + +const ENDPOINT = 'api/admin/signal-endpoints'; + +export type SignalEndpointTokenPayload = Omit< + ISignalEndpointToken, + 'id' | 'signalEndpointId' | 'createdAt' | 'createdByUserId' +>; + +type SignalEndpointTokenWithTokenSecret = ISignalEndpointToken & { + token: string; +}; + +export const useSignalEndpointTokensApi = () => { + const { loading, makeRequest, createRequest, errors } = useAPI({ + propagateErrors: true, + }); + + const addSignalEndpointToken = async ( + signalEndpointId: number, + signalEndpointToken: SignalEndpointTokenPayload, + ): Promise => { + const requestId = 'addSignalEndpointToken'; + const req = createRequest( + `${ENDPOINT}/${signalEndpointId}/tokens`, + { + method: 'POST', + body: JSON.stringify(signalEndpointToken), + }, + requestId, + ); + + const response = await makeRequest(req.caller, req.id); + return response.json(); + }; + + const updateSignalEndpointToken = async ( + signalEndpointId: number, + signalEndpointTokenId: number, + signalEndpointToken: SignalEndpointTokenPayload, + ) => { + const requestId = 'updateSignalEndpointToken'; + const req = createRequest( + `${ENDPOINT}/${signalEndpointId}/tokens/${signalEndpointTokenId}`, + { + method: 'PUT', + body: JSON.stringify(signalEndpointToken), + }, + requestId, + ); + + await makeRequest(req.caller, req.id); + }; + + const removeSignalEndpointToken = async ( + signalEndpointId: number, + signalEndpointTokenId: number, + ) => { + const requestId = 'removeSignalEndpointToken'; + const req = createRequest( + `${ENDPOINT}/${signalEndpointId}/tokens/${signalEndpointTokenId}`, + { method: 'DELETE' }, + requestId, + ); + + await makeRequest(req.caller, req.id); + }; + + return { + addSignalEndpointToken, + updateSignalEndpointToken, + removeSignalEndpointToken, + errors, + loading, + }; +}; diff --git a/frontend/src/hooks/api/actions/useSignalEndpointsApi/useSignalEndpointsApi.ts b/frontend/src/hooks/api/actions/useSignalEndpointsApi/useSignalEndpointsApi.ts new file mode 100644 index 000000000000..549a47e3b6a8 --- /dev/null +++ b/frontend/src/hooks/api/actions/useSignalEndpointsApi/useSignalEndpointsApi.ts @@ -0,0 +1,104 @@ +import { ISignalEndpoint } from 'interfaces/signal'; +import useAPI from '../useApi/useApi'; + +const ENDPOINT = 'api/admin/signal-endpoints'; + +export type SignalEndpointPayload = Omit< + ISignalEndpoint, + 'id' | 'createdAt' | 'createdByUserId' | 'tokens' +>; + +export const useSignalEndpointsApi = () => { + const { loading, makeRequest, createRequest, errors } = useAPI({ + propagateErrors: true, + }); + + const addSignalEndpoint = async (signalEndpoint: SignalEndpointPayload) => { + const requestId = 'addSignalEndpoint'; + const req = createRequest( + ENDPOINT, + { + method: 'POST', + body: JSON.stringify(signalEndpoint), + }, + requestId, + ); + + const response = await makeRequest(req.caller, req.id); + return response.json(); + }; + + const updateSignalEndpoint = async ( + signalEndpointId: number, + signalEndpoint: SignalEndpointPayload, + ) => { + const requestId = 'updateSignalEndpoint'; + const req = createRequest( + `${ENDPOINT}/${signalEndpointId}`, + { + method: 'PUT', + body: JSON.stringify(signalEndpoint), + }, + requestId, + ); + + await makeRequest(req.caller, req.id); + }; + + const enableSignalEndpoint = async (signalEndpointId: number) => { + const requestId = 'enableSignalEndpoint'; + const req = createRequest( + `${ENDPOINT}/${signalEndpointId}/on`, + { + method: 'POST', + }, + requestId, + ); + + await makeRequest(req.caller, req.id); + }; + + const disableSignalEndpoint = async (signalEndpointId: number) => { + const requestId = 'disableSignalEndpoint'; + const req = createRequest( + `${ENDPOINT}/${signalEndpointId}/off`, + { + method: 'POST', + }, + requestId, + ); + + await makeRequest(req.caller, req.id); + }; + + const toggleSignalEndpoint = async ( + signalEndpointId: number, + enabled: boolean, + ) => { + if (enabled) { + await enableSignalEndpoint(signalEndpointId); + } else { + await disableSignalEndpoint(signalEndpointId); + } + }; + + const removeSignalEndpoint = async (signalEndpointId: number) => { + const requestId = 'removeSignalEndpoint'; + const req = createRequest( + `${ENDPOINT}/${signalEndpointId}`, + { method: 'DELETE' }, + requestId, + ); + + await makeRequest(req.caller, req.id); + }; + + return { + addSignalEndpoint, + updateSignalEndpoint, + removeSignalEndpoint, + toggleSignalEndpoint, + errors, + loading, + }; +}; diff --git a/frontend/src/hooks/api/getters/useIncomingWebhookEvents/useIncomingWebhookEvents.ts b/frontend/src/hooks/api/getters/useSignalEndpointSignals/useSignalEndpointSignals.ts similarity index 57% rename from frontend/src/hooks/api/getters/useIncomingWebhookEvents/useIncomingWebhookEvents.ts rename to frontend/src/hooks/api/getters/useSignalEndpointSignals/useSignalEndpointSignals.ts index 1ecb92f55204..63016cc0ab8c 100644 --- a/frontend/src/hooks/api/getters/useIncomingWebhookEvents/useIncomingWebhookEvents.ts +++ b/frontend/src/hooks/api/getters/useSignalEndpointSignals/useSignalEndpointSignals.ts @@ -5,63 +5,62 @@ import useSWRInfinite, { import { formatApiPath } from 'utils/formatPath'; import handleErrorResponses from '../httpErrorResponseHandler'; import useUiConfig from '../useUiConfig/useUiConfig'; -import { IIncomingWebhookEvent } from 'interfaces/incomingWebhook'; +import { ISignalEndpointSignal } from 'interfaces/signal'; import { useUiFlag } from 'hooks/useUiFlag'; -const ENDPOINT = 'api/admin/incoming-webhooks'; +const ENDPOINT = 'api/admin/signal-endpoints'; -type IncomingWebhookEventsResponse = { - incomingWebhookEvents: IIncomingWebhookEvent[]; +type SignalsResponse = { + signalEndpointSignals: ISignalEndpointSignal[]; }; const fetcher = async (url: string) => { const response = await fetch(url); - await handleErrorResponses('Incoming webhook events')(response); + await handleErrorResponses('Signals')(response); return response.json(); }; -export const useIncomingWebhookEvents = ( - incomingWebhookId?: number, +export const useSignalEndpointSignals = ( + signalEndpointId?: number, limit = 50, options: SWRInfiniteConfiguration = {}, ) => { const { isEnterprise } = useUiConfig(); - const incomingWebhooksEnabled = useUiFlag('incomingWebhooks'); + const signalsEnabled = useUiFlag('signals'); const getKey: SWRInfiniteKeyLoader = ( pageIndex: number, - previousPageData: IncomingWebhookEventsResponse, + previousPageData: SignalsResponse, ) => { // Does not meet conditions - if (!incomingWebhookId || !isEnterprise || !incomingWebhooksEnabled) - return null; + if (!signalEndpointId || !isEnterprise || !signalsEnabled) return null; // Reached the end - if (previousPageData && !previousPageData.incomingWebhookEvents.length) + if (previousPageData && !previousPageData.signalEndpointSignals.length) return null; return formatApiPath( - `${ENDPOINT}/${incomingWebhookId}/events?limit=${limit}&offset=${ + `${ENDPOINT}/${signalEndpointId}/signals?limit=${limit}&offset=${ pageIndex * limit }`, ); }; const { data, error, size, setSize, mutate } = - useSWRInfinite(getKey, fetcher, { + useSWRInfinite(getKey, fetcher, { ...options, revalidateAll: true, }); - const incomingWebhookEvents = data - ? data.flatMap(({ incomingWebhookEvents }) => incomingWebhookEvents) + const signalEndpointSignals = data + ? data.flatMap(({ signalEndpointSignals }) => signalEndpointSignals) : []; const isLoadingInitialData = !data && !error; const isLoadingMore = size > 0 && !data?.[size - 1]; const loading = isLoadingInitialData || isLoadingMore; - const hasMore = data?.[size - 1]?.incomingWebhookEvents.length === limit; + const hasMore = data?.[size - 1]?.signalEndpointSignals.length === limit; const loadMore = () => { if (loading || !hasMore) return; @@ -69,7 +68,7 @@ export const useIncomingWebhookEvents = ( }; return { - incomingWebhookEvents, + signalEndpointSignals, hasMore, loadMore, loading, diff --git a/frontend/src/hooks/api/getters/useIncomingWebhookTokens/useIncomingWebhookTokens.ts b/frontend/src/hooks/api/getters/useSignalEndpointTokens/useSignalEndpointTokens.ts similarity index 50% rename from frontend/src/hooks/api/getters/useIncomingWebhookTokens/useIncomingWebhookTokens.ts rename to frontend/src/hooks/api/getters/useSignalEndpointTokens/useSignalEndpointTokens.ts index cf1042cd1e28..48a99fd8dd21 100644 --- a/frontend/src/hooks/api/getters/useIncomingWebhookTokens/useIncomingWebhookTokens.ts +++ b/frontend/src/hooks/api/getters/useSignalEndpointTokens/useSignalEndpointTokens.ts @@ -3,26 +3,31 @@ import { formatApiPath } from 'utils/formatPath'; import handleErrorResponses from '../httpErrorResponseHandler'; import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR'; import useUiConfig from '../useUiConfig/useUiConfig'; -import { IIncomingWebhookToken } from 'interfaces/incomingWebhook'; +import { ISignalEndpointToken } from 'interfaces/signal'; import { useUiFlag } from 'hooks/useUiFlag'; -const ENDPOINT = 'api/admin/incoming-webhooks'; +const ENDPOINT = 'api/admin/signal-endpoints'; -export const useIncomingWebhookTokens = (incomingWebhookId: number) => { +const DEFAULT_DATA = { + signalEndpointTokens: [], +}; + +export const useSignalEndpointTokens = (signalEndpointId: number) => { const { isEnterprise } = useUiConfig(); - const incomingWebhooksEnabled = useUiFlag('incomingWebhooks'); + const signalsEnabled = useUiFlag('signals'); - const { data, error, mutate } = useConditionalSWR( - isEnterprise() && incomingWebhooksEnabled, - { incomingWebhookTokens: [] }, - formatApiPath(`${ENDPOINT}/${incomingWebhookId}/tokens`), + const { data, error, mutate } = useConditionalSWR<{ + signalEndpointTokens: ISignalEndpointToken[]; + }>( + isEnterprise() && signalsEnabled, + DEFAULT_DATA, + formatApiPath(`${ENDPOINT}/${signalEndpointId}/tokens`), fetcher, ); return useMemo( () => ({ - incomingWebhookTokens: (data?.incomingWebhookTokens ?? - []) as IIncomingWebhookToken[], + signalEndpointTokens: data?.signalEndpointTokens ?? [], loading: !error && !data, refetch: () => mutate(), error, @@ -33,6 +38,6 @@ export const useIncomingWebhookTokens = (incomingWebhookId: number) => { const fetcher = (path: string) => { return fetch(path) - .then(handleErrorResponses('Incoming webhook tokens')) + .then(handleErrorResponses('Signal endpoint tokens')) .then((res) => res.json()); }; diff --git a/frontend/src/hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks.ts b/frontend/src/hooks/api/getters/useSignalEndpoints/useSignalEndpoints.ts similarity index 64% rename from frontend/src/hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks.ts rename to frontend/src/hooks/api/getters/useSignalEndpoints/useSignalEndpoints.ts index 0336fc92066c..e55cb435e870 100644 --- a/frontend/src/hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks.ts +++ b/frontend/src/hooks/api/getters/useSignalEndpoints/useSignalEndpoints.ts @@ -3,23 +3,23 @@ import { formatApiPath } from 'utils/formatPath'; import handleErrorResponses from '../httpErrorResponseHandler'; import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR'; import useUiConfig from '../useUiConfig/useUiConfig'; -import { IIncomingWebhook } from 'interfaces/incomingWebhook'; +import { ISignalEndpoint } from 'interfaces/signal'; import { useUiFlag } from 'hooks/useUiFlag'; -const ENDPOINT = 'api/admin/incoming-webhooks'; +const ENDPOINT = 'api/admin/signal-endpoints'; const DEFAULT_DATA = { - incomingWebhooks: [], + signalEndpoints: [], }; -export const useIncomingWebhooks = () => { +export const useSignalEndpoints = () => { const { isEnterprise } = useUiConfig(); - const incomingWebhooksEnabled = useUiFlag('incomingWebhooks'); + const signalsEnabled = useUiFlag('signals'); const { data, error, mutate } = useConditionalSWR<{ - incomingWebhooks: IIncomingWebhook[]; + signalEndpoints: ISignalEndpoint[]; }>( - isEnterprise() && incomingWebhooksEnabled, + isEnterprise() && signalsEnabled, DEFAULT_DATA, formatApiPath(ENDPOINT), fetcher, @@ -27,7 +27,7 @@ export const useIncomingWebhooks = () => { return useMemo( () => ({ - incomingWebhooks: data?.incomingWebhooks ?? [], + signalEndpoints: data?.signalEndpoints ?? [], loading: !error && !data, refetch: () => mutate(), error, @@ -38,6 +38,6 @@ export const useIncomingWebhooks = () => { const fetcher = (path: string) => { return fetch(path) - .then(handleErrorResponses('Incoming webhooks')) + .then(handleErrorResponses('Signal endpoints')) .then((res) => res.json()); }; diff --git a/frontend/src/interfaces/action.ts b/frontend/src/interfaces/action.ts index e0b3acdc0464..d6614be63993 100644 --- a/frontend/src/interfaces/action.ts +++ b/frontend/src/interfaces/action.ts @@ -1,5 +1,10 @@ +import { ISignal, SignalSource } from './signal'; import { IConstraint } from './strategy'; +type ActionSetState = 'started' | 'success' | 'failed'; + +type ActionState = ActionSetState | 'not started'; + export interface IActionSet { id: number; enabled: boolean; @@ -12,15 +17,13 @@ export interface IActionSet { createdByUserId: number; } -type MatchSource = 'incoming-webhook'; - export type ParameterMatch = Pick< IConstraint, 'inverted' | 'operator' | 'caseInsensitive' | 'value' | 'values' >; export interface IMatch { - source: MatchSource; + source: SignalSource; sourceId: number; payload: Record; } @@ -34,21 +37,6 @@ export interface IAction { createdByUserId: number; } -export type ObservableEventSource = 'incoming-webhook'; - -export interface IObservableEvent { - id: number; - source: ObservableEventSource; - sourceId: number; - createdAt: string; - createdByIncomingWebhookTokenId: number; - payload: Record; -} - -type ActionSetState = 'started' | 'success' | 'failed'; - -type ActionState = ActionSetState | 'not started'; - export interface IActionEvent extends IAction { state: ActionState; details?: string; @@ -61,9 +49,9 @@ interface IActionSetEventActionSet extends IActionSet { export interface IActionSetEvent { id: number; actionSetId: number; - observableEventId: number; + signalId: number; createdAt: string; state: ActionSetState; - observableEvent: IObservableEvent; + signal: ISignal; actionSet: IActionSetEventActionSet; } diff --git a/frontend/src/interfaces/incomingWebhook.ts b/frontend/src/interfaces/incomingWebhook.ts deleted file mode 100644 index 4a8623731e54..000000000000 --- a/frontend/src/interfaces/incomingWebhook.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ObservableEventSource } from './action'; - -export interface IIncomingWebhook { - id: number; - enabled: boolean; - name: string; - createdAt: string; - createdByUserId: number; - description: string; - tokens: IIncomingWebhookToken[]; -} - -export interface IIncomingWebhookToken { - id: number; - name: string; - incomingWebhookId: number; - createdAt: string; - createdByUserId: number; -} - -export interface IIncomingWebhookEvent { - id: number; - payload: Record; - createdAt: string; - source: ObservableEventSource; - sourceId: number; - tokenName: string; -} diff --git a/frontend/src/interfaces/signal.ts b/frontend/src/interfaces/signal.ts new file mode 100644 index 000000000000..0a8ca150379f --- /dev/null +++ b/frontend/src/interfaces/signal.ts @@ -0,0 +1,33 @@ +export interface ISignalEndpoint { + id: number; + enabled: boolean; + name: string; + createdAt: string; + createdByUserId: number; + description: string; + tokens: ISignalEndpointToken[]; +} + +export interface ISignalEndpointToken { + id: number; + name: string; + signalEndpointId: number; + createdAt: string; + createdByUserId: number; +} + +export type SignalSource = 'signal-endpoint'; + +export interface ISignal { + id: number; + source: SignalSource; + sourceId: number; + createdAt: string; + createdBySourceTokenId: number; + payload: Record; +} + +export interface ISignalEndpointSignal + extends Omit { + tokenName: string; +} diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index cd51858ed029..34c8f6bdd0ae 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -63,7 +63,7 @@ export type UiFlags = { doraMetrics?: boolean; dependentFeatures?: boolean; newStrategyConfiguration?: boolean; - incomingWebhooks?: boolean; + signals?: boolean; automatedActions?: boolean; celebrateUnleash?: boolean; increaseUnleashWidth?: boolean; diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap index ff3e1d4c103e..9cbc9da5b965 100644 --- a/src/lib/__snapshots__/create-config.test.ts.snap +++ b/src/lib/__snapshots__/create-config.test.ts.snap @@ -117,7 +117,6 @@ exports[`should create default config 1`] = ` "filterInvalidClientMetrics": false, "googleAuthEnabled": false, "inMemoryScheduledChangeRequests": false, - "incomingWebhooks": false, "increaseUnleashWidth": false, "maintenanceMode": false, "messageBanner": { @@ -137,6 +136,7 @@ exports[`should create default config 1`] = ` "scimApi": false, "sdkReporting": false, "showInactiveUsers": false, + "signals": false, "strictSchemaValidation": false, "stripClientHeadersOn304": false, "useMemoizedActiveTokens": false, @@ -176,7 +176,7 @@ exports[`should create default config 1`] = ` "prometheusApi": undefined, "publicFolder": undefined, "rateLimiting": { - "callIncomingWebhookMaxPerSecond": 1, + "callSignalEndpointMaxPerSecond": 1, "createUserMaxPerMinute": 20, "passwordResetMaxPerMinute": 1, "simpleLoginMaxPerMinute": 10, diff --git a/src/lib/create-config.ts b/src/lib/create-config.ts index 597aafd33c73..d713467151c4 100644 --- a/src/lib/create-config.ts +++ b/src/lib/create-config.ts @@ -150,8 +150,8 @@ function loadRateLimitingConfig(options: IUnleashOptions): IRateLimiting { process.env.PASSWORD_RESET_LIMIT_PER_MINUTE, 1, ); - const callIncomingWebhookMaxPerSecond = parseEnvVarNumber( - process.env.INCOMING_WEBHOOK_RATE_LIMIT_PER_SECOND, + const callSignalEndpointMaxPerSecond = parseEnvVarNumber( + process.env.SIGNAL_ENDPOINT_RATE_LIMIT_PER_SECOND, 1, ); @@ -159,7 +159,7 @@ function loadRateLimitingConfig(options: IUnleashOptions): IRateLimiting { createUserMaxPerMinute, simpleLoginMaxPerMinute, passwordResetMaxPerMinute, - callIncomingWebhookMaxPerSecond, + callSignalEndpointMaxPerSecond, }; return mergeAll([defaultRateLimitOptions, options.rateLimiting || {}]); } diff --git a/src/lib/metrics.ts b/src/lib/metrics.ts index e68d68f95b16..fcf154c1f608 100644 --- a/src/lib/metrics.ts +++ b/src/lib/metrics.ts @@ -372,12 +372,11 @@ export default class MetricsMonitor { .set(config.rateLimiting.passwordResetMaxPerMinute); rateLimits .labels({ - endpoint: '/api/incoming-webhook/:name', + endpoint: '/api/signal-endpoint/:name', method: 'POST', }) .set( - config.rateLimiting.callIncomingWebhookMaxPerSecond * - 60, + config.rateLimiting.callSignalEndpointMaxPerSecond * 60, ); } catch (e) {} } diff --git a/src/lib/types/events.ts b/src/lib/types/events.ts index 5e9e6d95d03f..165e95636370 100644 --- a/src/lib/types/events.ts +++ b/src/lib/types/events.ts @@ -176,16 +176,16 @@ export const BANNER_CREATED = 'banner-created' as const; export const BANNER_UPDATED = 'banner-updated' as const; export const BANNER_DELETED = 'banner-deleted' as const; -export const INCOMING_WEBHOOK_CREATED = 'incoming-webhook-created' as const; -export const INCOMING_WEBHOOK_UPDATED = 'incoming-webhook-updated' as const; -export const INCOMING_WEBHOOK_DELETED = 'incoming-webhook-deleted' as const; +export const SIGNAL_ENDPOINT_CREATED = 'signal-endpoint-created' as const; +export const SIGNAL_ENDPOINT_UPDATED = 'signal-endpoint-updated' as const; +export const SIGNAL_ENDPOINT_DELETED = 'signal-endpoint-deleted' as const; -export const INCOMING_WEBHOOK_TOKEN_CREATED = - 'incoming-webhook-token-created' as const; -export const INCOMING_WEBHOOK_TOKEN_UPDATED = - 'incoming-webhook-token-updated' as const; -export const INCOMING_WEBHOOK_TOKEN_DELETED = - 'incoming-webhook-token-deleted' as const; +export const SIGNAL_ENDPOINT_TOKEN_CREATED = + 'signal-endpoint-token-created' as const; +export const SIGNAL_ENDPOINT_TOKEN_UPDATED = + 'signal-endpoint-token-updated' as const; +export const SIGNAL_ENDPOINT_TOKEN_DELETED = + 'signal-endpoint-token-deleted' as const; export const ACTIONS_CREATED = 'actions-created' as const; export const ACTIONS_UPDATED = 'actions-updated' as const; @@ -325,12 +325,12 @@ export const IEventTypes = [ PROJECT_ENVIRONMENT_REMOVED, DEFAULT_STRATEGY_UPDATED, SEGMENT_IMPORT, - INCOMING_WEBHOOK_CREATED, - INCOMING_WEBHOOK_UPDATED, - INCOMING_WEBHOOK_DELETED, - INCOMING_WEBHOOK_TOKEN_CREATED, - INCOMING_WEBHOOK_TOKEN_UPDATED, - INCOMING_WEBHOOK_TOKEN_DELETED, + SIGNAL_ENDPOINT_CREATED, + SIGNAL_ENDPOINT_UPDATED, + SIGNAL_ENDPOINT_DELETED, + SIGNAL_ENDPOINT_TOKEN_CREATED, + SIGNAL_ENDPOINT_TOKEN_UPDATED, + SIGNAL_ENDPOINT_TOKEN_DELETED, ACTIONS_CREATED, ACTIONS_UPDATED, ACTIONS_DELETED, diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index cbaee15667dd..3c0355dc44a0 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -28,7 +28,7 @@ export type IFlagKey = | 'disableMetrics' | 'stripClientHeadersOn304' | 'stripHeadersOnAPI' - | 'incomingWebhooks' + | 'signals' | 'automatedActions' | 'celebrateUnleash' | 'increaseUnleashWidth' @@ -135,8 +135,8 @@ const flags: IFlags = { .UNLEASH_EXPERIMENTAL_DETECT_SEGMENT_USAGE_IN_CHANGE_REQUESTS, false, ), - incomingWebhooks: parseEnvVarBoolean( - process.env.UNLEASH_EXPERIMENTAL_INCOMING_WEBHOOKS, + signals: parseEnvVarBoolean( + process.env.UNLEASH_EXPERIMENTAL_SIGNALS, false, ), automatedActions: parseEnvVarBoolean( diff --git a/src/lib/types/option.ts b/src/lib/types/option.ts index e9132b5cf665..8242d7d4979a 100644 --- a/src/lib/types/option.ts +++ b/src/lib/types/option.ts @@ -207,7 +207,7 @@ export interface IRateLimiting { createUserMaxPerMinute: number; simpleLoginMaxPerMinute: number; passwordResetMaxPerMinute: number; - callIncomingWebhookMaxPerSecond: number; + callSignalEndpointMaxPerSecond: number; } export interface IUnleashConfig {