mirror of
https://github.com/Unleash/unleash.git
synced 2025-08-04 13:48:56 +02:00
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.
This commit is contained in:
parent
4fc0a806f1
commit
68729333e0
@ -108,10 +108,10 @@ const PremiumFeatures = {
|
|||||||
url: 'https://docs.getunleash.io/reference/banners',
|
url: 'https://docs.getunleash.io/reference/banners',
|
||||||
label: 'Banners',
|
label: 'Banners',
|
||||||
},
|
},
|
||||||
'incoming-webhooks': {
|
signals: {
|
||||||
plan: FeaturePlan.ENTERPRISE,
|
plan: FeaturePlan.ENTERPRISE,
|
||||||
url: 'https://docs.getunleash.io/reference/incoming-webhooks',
|
url: 'https://docs.getunleash.io/reference/signals',
|
||||||
label: 'Incoming Webhooks',
|
label: 'Signals',
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
plan: FeaturePlan.ENTERPRISE,
|
plan: FeaturePlan.ENTERPRISE,
|
||||||
|
@ -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<React.SetStateAction<boolean>>;
|
|
||||||
selectedIncomingWebhook?: IIncomingWebhook;
|
|
||||||
setSelectedIncomingWebhook: React.Dispatch<
|
|
||||||
React.SetStateAction<IIncomingWebhook | undefined>
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const IncomingWebhooks = ({
|
|
||||||
modalOpen,
|
|
||||||
setModalOpen,
|
|
||||||
selectedIncomingWebhook,
|
|
||||||
setSelectedIncomingWebhook,
|
|
||||||
}: IIncomingWebhooksProps) => {
|
|
||||||
const { isEnterprise } = useUiConfig();
|
|
||||||
|
|
||||||
if (!isEnterprise()) {
|
|
||||||
return <PremiumFeature feature='incoming-webhooks' />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<PermissionGuard permissions={ADMIN}>
|
|
||||||
<IncomingWebhooksTable
|
|
||||||
modalOpen={modalOpen}
|
|
||||||
setModalOpen={setModalOpen}
|
|
||||||
selectedIncomingWebhook={selectedIncomingWebhook}
|
|
||||||
setSelectedIncomingWebhook={setSelectedIncomingWebhook}
|
|
||||||
/>
|
|
||||||
</PermissionGuard>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -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<React.SetStateAction<boolean>>;
|
|
||||||
onConfirm: (incomingWebhook: IIncomingWebhook) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const IncomingWebhooksDeleteDialog = ({
|
|
||||||
incomingWebhook,
|
|
||||||
open,
|
|
||||||
setOpen,
|
|
||||||
onConfirm,
|
|
||||||
}: IIncomingWebhooksDeleteDialogProps) => (
|
|
||||||
<Dialogue
|
|
||||||
title='Delete incoming webhook?'
|
|
||||||
open={open}
|
|
||||||
primaryButtonText='Delete incoming webhook'
|
|
||||||
secondaryButtonText='Cancel'
|
|
||||||
onClick={() => onConfirm(incomingWebhook!)}
|
|
||||||
onClose={() => {
|
|
||||||
setOpen(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
You are about to delete incoming webhook:{' '}
|
|
||||||
<strong>{incomingWebhook?.name}</strong>
|
|
||||||
</p>
|
|
||||||
</Dialogue>
|
|
||||||
);
|
|
@ -8,10 +8,10 @@ import { RequestIntegrationCard } from '../RequestIntegrationCard/RequestIntegra
|
|||||||
import { OFFICIAL_SDKS } from './SDKs';
|
import { OFFICIAL_SDKS } from './SDKs';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
|
|
||||||
interface IAvailableIntegrationsProps {
|
interface IAvailableIntegrationsProps {
|
||||||
providers: AddonTypeSchema[];
|
providers: AddonTypeSchema[];
|
||||||
onNewIncomingWebhook: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const StyledContainer = styled('div')(({ theme }) => ({
|
const StyledContainer = styled('div')(({ theme }) => ({
|
||||||
@ -53,9 +53,9 @@ const StyledGrayContainer = styled('div')(({ theme }) => ({
|
|||||||
|
|
||||||
export const AvailableIntegrations: VFC<IAvailableIntegrationsProps> = ({
|
export const AvailableIntegrations: VFC<IAvailableIntegrationsProps> = ({
|
||||||
providers,
|
providers,
|
||||||
onNewIncomingWebhook,
|
|
||||||
}) => {
|
}) => {
|
||||||
const incomingWebhooksEnabled = useUiFlag('incomingWebhooks');
|
const { isEnterprise } = useUiConfig();
|
||||||
|
const signalsEnabled = useUiFlag('signals');
|
||||||
|
|
||||||
const customProviders = [JIRA_INFO];
|
const customProviders = [JIRA_INFO];
|
||||||
const serverSdks = OFFICIAL_SDKS.filter((sdk) => sdk.type === 'server');
|
const serverSdks = OFFICIAL_SDKS.filter((sdk) => sdk.type === 'server');
|
||||||
@ -98,13 +98,13 @@ export const AvailableIntegrations: VFC<IAvailableIntegrationsProps> = ({
|
|||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={incomingWebhooksEnabled}
|
condition={isEnterprise() && signalsEnabled}
|
||||||
show={
|
show={
|
||||||
<IntegrationCard
|
<IntegrationCard
|
||||||
icon='webhook'
|
icon='webhook'
|
||||||
title='Incoming Webhooks'
|
title='Signals'
|
||||||
description='Incoming Webhooks allow third-party services to send observable events to Unleash.'
|
description='Signal endpoints allow third-party services to send signals to Unleash.'
|
||||||
onClick={onNewIncomingWebhook}
|
link='/integrations/signals'
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -4,6 +4,10 @@ import { StyledCardsGrid } from '../IntegrationList.styles';
|
|||||||
import { IntegrationCard } from '../IntegrationCard/IntegrationCard';
|
import { IntegrationCard } from '../IntegrationCard/IntegrationCard';
|
||||||
import { VFC } from 'react';
|
import { VFC } from 'react';
|
||||||
import { Typography, styled } from '@mui/material';
|
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 }) => ({
|
const StyledConfiguredSection = styled('section')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -23,6 +27,10 @@ export const ConfiguredIntegrations: VFC<ConfiguredIntegrationsProps> = ({
|
|||||||
addons,
|
addons,
|
||||||
providers,
|
providers,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { signalEndpoints } = useSignalEndpoints();
|
||||||
|
const signalsEnabled = useUiFlag('signals');
|
||||||
|
const { isEnterprise } = useUiConfig();
|
||||||
|
|
||||||
const ref = useLoading(loading || false);
|
const ref = useLoading(loading || false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -65,6 +73,26 @@ export const ConfiguredIntegrations: VFC<ConfiguredIntegrationsProps> = ({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={
|
||||||
|
isEnterprise() && signalsEnabled
|
||||||
|
// && signalEndpoints.length > 0
|
||||||
|
}
|
||||||
|
show={
|
||||||
|
<IntegrationCard
|
||||||
|
variant='stacked'
|
||||||
|
icon='webhook'
|
||||||
|
title='Signals'
|
||||||
|
description={`${
|
||||||
|
signalEndpoints.length
|
||||||
|
} signal endpoint${
|
||||||
|
signalEndpoints.length === 1 ? '' : 's'
|
||||||
|
} configured`}
|
||||||
|
link='/integrations/signals'
|
||||||
|
configureActionText='View signal endpoints'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</StyledCardsGrid>
|
</StyledCardsGrid>
|
||||||
</StyledConfiguredSection>
|
</StyledConfiguredSection>
|
||||||
);
|
);
|
||||||
|
@ -10,7 +10,10 @@ import type { AddonSchema } from 'openapi';
|
|||||||
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
||||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||||
|
|
||||||
|
type CardVariant = 'default' | 'stacked';
|
||||||
|
|
||||||
interface IIntegrationCardBaseProps {
|
interface IIntegrationCardBaseProps {
|
||||||
|
variant?: CardVariant;
|
||||||
id?: string | number;
|
id?: string | number;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
title: string;
|
title: string;
|
||||||
@ -37,7 +40,9 @@ type IIntegrationCardProps =
|
|||||||
| IIntegrationCardWithLinkProps
|
| IIntegrationCardWithLinkProps
|
||||||
| IIntegrationCardWithOnClickProps;
|
| IIntegrationCardWithOnClickProps;
|
||||||
|
|
||||||
const StyledCard = styled('div')(({ theme }) => ({
|
const StyledCard = styled('div', {
|
||||||
|
shouldForwardProp: (prop) => prop !== 'variant',
|
||||||
|
})<{ variant?: CardVariant }>(({ theme, variant = 'default' }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
padding: theme.spacing(3),
|
padding: theme.spacing(3),
|
||||||
@ -48,6 +53,24 @@ const StyledCard = styled('div')(({ theme }) => ({
|
|||||||
':hover': {
|
':hover': {
|
||||||
backgroundColor: theme.palette.action.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)({
|
const StyledLink = styled(Link)({
|
||||||
@ -89,6 +112,7 @@ const StyledOpenInNewIcon = styled(OpenInNewIcon)(({ theme }) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
export const IntegrationCard: VFC<IIntegrationCardProps> = ({
|
export const IntegrationCard: VFC<IIntegrationCardProps> = ({
|
||||||
|
variant = 'default',
|
||||||
icon,
|
icon,
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
@ -113,7 +137,7 @@ export const IntegrationCard: VFC<IIntegrationCardProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const content = (
|
const content = (
|
||||||
<StyledCard>
|
<StyledCard variant={variant}>
|
||||||
<StyledHeader>
|
<StyledHeader>
|
||||||
<StyledTitle variant='h3' data-loading>
|
<StyledTitle variant='h3' data-loading>
|
||||||
<IntegrationIcon name={icon as string} /> {title}
|
<IntegrationIcon name={icon as string} /> {title}
|
||||||
|
@ -1,162 +1,32 @@
|
|||||||
import { VFC, useState } from 'react';
|
import { VFC } from 'react';
|
||||||
import useAddons from 'hooks/api/getters/useAddons/useAddons';
|
import useAddons from 'hooks/api/getters/useAddons/useAddons';
|
||||||
import { AvailableIntegrations } from './AvailableIntegrations/AvailableIntegrations';
|
import { AvailableIntegrations } from './AvailableIntegrations/AvailableIntegrations';
|
||||||
import { ConfiguredIntegrations } from './ConfiguredIntegrations/ConfiguredIntegrations';
|
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 { PageContent } from 'component/common/PageContent/PageContent';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||||
import { TabLink } from 'component/common/TabNav/TabLink';
|
import { useSignalEndpoints } from 'hooks/api/getters/useSignalEndpoints/useSignalEndpoints';
|
||||||
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',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const IntegrationList: VFC = () => {
|
export const IntegrationList: VFC = () => {
|
||||||
const { isEnterprise } = useUiConfig();
|
const { signalEndpoints } = useSignalEndpoints();
|
||||||
const { pathname } = useLocation();
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const theme = useTheme();
|
|
||||||
const incomingWebhooksEnabled = useUiFlag('incomingWebhooks');
|
|
||||||
const { providers, addons, loading } = useAddons();
|
const { providers, addons, loading } = useAddons();
|
||||||
const { incomingWebhooks } = useIncomingWebhooks();
|
|
||||||
|
|
||||||
const [selectedIncomingWebhook, setSelectedIncomingWebhook] =
|
|
||||||
useState<IIncomingWebhook>();
|
|
||||||
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 (
|
return (
|
||||||
<PageContent
|
<PageContent
|
||||||
header={
|
header={<PageHeader title='Integrations' />}
|
||||||
<ConditionallyRender
|
|
||||||
condition={incomingWebhooksEnabled}
|
|
||||||
show={
|
|
||||||
<StyledHeader>
|
|
||||||
<StyledTabsContainer>
|
|
||||||
<Tabs
|
|
||||||
value={pathname}
|
|
||||||
indicatorColor='primary'
|
|
||||||
textColor='primary'
|
|
||||||
variant='scrollable'
|
|
||||||
allowScrollButtonsMobile
|
|
||||||
>
|
|
||||||
{tabs.map(({ label, path }) => (
|
|
||||||
<Tab
|
|
||||||
key={label}
|
|
||||||
value={path}
|
|
||||||
label={
|
|
||||||
<TabLink to={path}>
|
|
||||||
{label}
|
|
||||||
</TabLink>
|
|
||||||
}
|
|
||||||
sx={{
|
|
||||||
padding: 0,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Tabs>
|
|
||||||
</StyledTabsContainer>
|
|
||||||
<StyledActions>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={
|
|
||||||
pathname.includes(
|
|
||||||
'incoming-webhooks',
|
|
||||||
) && isEnterprise()
|
|
||||||
}
|
|
||||||
show={
|
|
||||||
<ResponsiveButton
|
|
||||||
onClick={onNewIncomingWebhook}
|
|
||||||
maxWidth={`${theme.breakpoints.values.sm}px`}
|
|
||||||
Icon={Add}
|
|
||||||
permission={ADMIN}
|
|
||||||
>
|
|
||||||
New incoming webhook
|
|
||||||
</ResponsiveButton>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</StyledActions>
|
|
||||||
</StyledHeader>
|
|
||||||
}
|
|
||||||
elseShow={<PageHeader title='Integrations' />}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
withTabs={incomingWebhooksEnabled}
|
|
||||||
>
|
>
|
||||||
<Routes>
|
<ConditionallyRender
|
||||||
<Route
|
condition={addons.length > 0 || signalEndpoints.length > 0}
|
||||||
path='incoming-webhooks'
|
show={
|
||||||
element={
|
<ConfiguredIntegrations
|
||||||
<IncomingWebhooks
|
addons={addons}
|
||||||
modalOpen={incomingWebhookModalOpen}
|
providers={providers}
|
||||||
setModalOpen={setIncomingWebhookModalOpen}
|
loading={loading}
|
||||||
selectedIncomingWebhook={selectedIncomingWebhook}
|
/>
|
||||||
setSelectedIncomingWebhook={
|
}
|
||||||
setSelectedIncomingWebhook
|
/>
|
||||||
}
|
<AvailableIntegrations providers={providers} />
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='*'
|
|
||||||
element={
|
|
||||||
<>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={addons.length > 0}
|
|
||||||
show={
|
|
||||||
<ConfiguredIntegrations
|
|
||||||
addons={addons}
|
|
||||||
providers={providers}
|
|
||||||
loading={loading}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<AvailableIntegrations
|
|
||||||
providers={providers}
|
|
||||||
onNewIncomingWebhook={onNewIncomingWebhook}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Routes>
|
|
||||||
</PageContent>
|
</PageContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -343,7 +343,7 @@ exports[`returns all baseRoutes 1`] = `
|
|||||||
"advanced": true,
|
"advanced": true,
|
||||||
"mobile": true,
|
"mobile": true,
|
||||||
},
|
},
|
||||||
"path": "/integrations/*",
|
"path": "/integrations",
|
||||||
"title": "Integrations",
|
"title": "Integrations",
|
||||||
"type": "protected",
|
"type": "protected",
|
||||||
},
|
},
|
||||||
|
@ -47,6 +47,7 @@ import { AddonRedirect } from 'component/integrations/AddonRedirect/AddonRedirec
|
|||||||
import { ExecutiveDashboard } from 'component/executiveDashboard/ExecutiveDashboard';
|
import { ExecutiveDashboard } from 'component/executiveDashboard/ExecutiveDashboard';
|
||||||
import { FeedbackList } from '../feedbackNew/FeedbackList';
|
import { FeedbackList } from '../feedbackNew/FeedbackList';
|
||||||
import { Application } from 'component/application/Application';
|
import { Application } from 'component/application/Application';
|
||||||
|
import { Signals } from 'component/signals/Signals';
|
||||||
|
|
||||||
export const routes: IRoute[] = [
|
export const routes: IRoute[] = [
|
||||||
// Splash
|
// Splash
|
||||||
@ -351,13 +352,21 @@ export const routes: IRoute[] = [
|
|||||||
menu: {},
|
menu: {},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/integrations/*',
|
path: '/integrations',
|
||||||
title: 'Integrations',
|
title: 'Integrations',
|
||||||
component: IntegrationList,
|
component: IntegrationList,
|
||||||
hidden: false,
|
hidden: false,
|
||||||
type: 'protected',
|
type: 'protected',
|
||||||
menu: { mobile: true, advanced: true },
|
menu: { mobile: true, advanced: true },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/integrations/signals',
|
||||||
|
title: 'Signals',
|
||||||
|
component: Signals,
|
||||||
|
hidden: true,
|
||||||
|
type: 'protected',
|
||||||
|
menu: {},
|
||||||
|
},
|
||||||
|
|
||||||
// Segments
|
// Segments
|
||||||
{
|
{
|
||||||
|
@ -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 (
|
|
||||||
<ProjectActionsEventsDetailsSourceIncomingWebhook
|
|
||||||
observableEvent={observableEvent}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
@ -13,7 +13,7 @@ const StyledDetails = styled('div')(({ theme }) => ({
|
|||||||
export const ProjectActionsEventsDetails = ({
|
export const ProjectActionsEventsDetails = ({
|
||||||
state,
|
state,
|
||||||
actionSet: { actions },
|
actionSet: { actions },
|
||||||
observableEvent,
|
signal,
|
||||||
}: IActionSetEvent) => {
|
}: IActionSetEvent) => {
|
||||||
const stateText =
|
const stateText =
|
||||||
state === 'failed'
|
state === 'failed'
|
||||||
@ -27,9 +27,7 @@ export const ProjectActionsEventsDetails = ({
|
|||||||
<Alert severity={state === 'failed' ? 'error' : 'success'}>
|
<Alert severity={state === 'failed' ? 'error' : 'success'}>
|
||||||
{stateText}
|
{stateText}
|
||||||
</Alert>
|
</Alert>
|
||||||
<ProjectActionsEventsDetailsSource
|
<ProjectActionsEventsDetailsSource signal={signal} />
|
||||||
observableEvent={observableEvent}
|
|
||||||
/>
|
|
||||||
{actions.map((action, i) => (
|
{actions.map((action, i) => (
|
||||||
<ProjectActionsEventsDetailsAction
|
<ProjectActionsEventsDetailsAction
|
||||||
key={action.id}
|
key={action.id}
|
@ -0,0 +1,20 @@
|
|||||||
|
import { ISignal } from 'interfaces/signal';
|
||||||
|
import { ProjectActionsEventsDetailsSourceSignalEndpoint } from './ProjectActionsEventsDetailsSourceSignalEndpoint';
|
||||||
|
|
||||||
|
interface IProjectActionsEventsDetailsSourceProps {
|
||||||
|
signal: ISignal;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ProjectActionsEventsDetailsSource = ({
|
||||||
|
signal,
|
||||||
|
}: IProjectActionsEventsDetailsSourceProps) => {
|
||||||
|
const { source } = signal;
|
||||||
|
|
||||||
|
if (source === 'signal-endpoint') {
|
||||||
|
return (
|
||||||
|
<ProjectActionsEventsDetailsSourceSignalEndpoint signal={signal} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
@ -6,8 +6,8 @@ import {
|
|||||||
IconButton,
|
IconButton,
|
||||||
styled,
|
styled,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useIncomingWebhooks } from 'hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks';
|
import { useSignalEndpoints } from 'hooks/api/getters/useSignalEndpoints/useSignalEndpoints';
|
||||||
import { IObservableEvent } from 'interfaces/action';
|
import { ISignal } from 'interfaces/signal';
|
||||||
import { Suspense, lazy, useMemo } from 'react';
|
import { Suspense, lazy, useMemo } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
@ -28,23 +28,22 @@ const StyledLink = styled(Link)(({ theme }) => ({
|
|||||||
marginLeft: theme.spacing(1),
|
marginLeft: theme.spacing(1),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
interface IProjectActionsEventsDetailsSourceIncomingWebhookProps {
|
interface IProjectActionsEventsDetailsSourceSignalEndpointProps {
|
||||||
observableEvent: IObservableEvent;
|
signal: ISignal;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProjectActionsEventsDetailsSourceIncomingWebhook = ({
|
export const ProjectActionsEventsDetailsSourceSignalEndpoint = ({
|
||||||
observableEvent,
|
signal,
|
||||||
}: IProjectActionsEventsDetailsSourceIncomingWebhookProps) => {
|
}: IProjectActionsEventsDetailsSourceSignalEndpointProps) => {
|
||||||
const { incomingWebhooks } = useIncomingWebhooks();
|
const { signalEndpoints } = useSignalEndpoints();
|
||||||
|
|
||||||
const incomingWebhookName = useMemo(() => {
|
const signalEndpointName = useMemo(() => {
|
||||||
const incomingWebhook = incomingWebhooks.find(
|
const signalEndpoint = signalEndpoints.find(
|
||||||
(incomingWebhook) =>
|
({ id }) => id === signal.sourceId,
|
||||||
incomingWebhook.id === observableEvent.sourceId,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return incomingWebhook?.name;
|
return signalEndpoint?.name;
|
||||||
}, [incomingWebhooks, observableEvent.sourceId]);
|
}, [signalEndpoints, signal.sourceId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledAccordion>
|
<StyledAccordion>
|
||||||
@ -55,15 +54,15 @@ export const ProjectActionsEventsDetailsSourceIncomingWebhook = ({
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Incoming webhook:
|
Signal endpoint:
|
||||||
<StyledLink to='/integrations/incoming-webhooks'>
|
<StyledLink to='/integrations/signals'>
|
||||||
{incomingWebhookName}
|
{signalEndpointName}
|
||||||
</StyledLink>
|
</StyledLink>
|
||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
<AccordionDetails>
|
<AccordionDetails>
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<LazyReactJSONEditor
|
<LazyReactJSONEditor
|
||||||
content={{ json: observableEvent.payload }}
|
content={{ json: signal.payload }}
|
||||||
readOnly
|
readOnly
|
||||||
statusBar={false}
|
statusBar={false}
|
||||||
editorStyle='sidePanel'
|
editorStyle='sidePanel'
|
@ -9,7 +9,7 @@ import { useLocationSettings } from 'hooks/useLocationSettings';
|
|||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import { ProjectActionsEventsStateCell } from './ProjectActionsEventsStateCell';
|
import { ProjectActionsEventsStateCell } from './ProjectActionsEventsStateCell';
|
||||||
import { ProjectActionsEventsDetails } from './ProjectActionsEventsDetails.tsx/ProjectActionsEventsDetails';
|
import { ProjectActionsEventsDetails } from './ProjectActionsEventsDetails/ProjectActionsEventsDetails';
|
||||||
|
|
||||||
const StyledHeader = styled('div')(({ theme }) => ({
|
const StyledHeader = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -86,9 +86,9 @@ export const ProjectActionsEventsModal = ({
|
|||||||
<FormTemplate
|
<FormTemplate
|
||||||
loading={loading && actionEvents.length === 0}
|
loading={loading && actionEvents.length === 0}
|
||||||
modal
|
modal
|
||||||
description='Actions allow you to configure automations based on specific triggers, like incoming webhooks.'
|
description=''
|
||||||
documentationLink='https://docs.getunleash.io/reference/actions'
|
documentationLink=''
|
||||||
documentationLinkLabel='Actions documentation'
|
documentationLinkLabel=''
|
||||||
showGuidance={false}
|
showGuidance={false}
|
||||||
>
|
>
|
||||||
<StyledHeader>
|
<StyledHeader>
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
ProjectActionsFormErrors,
|
ProjectActionsFormErrors,
|
||||||
} from './useProjectActionsForm';
|
} from './useProjectActionsForm';
|
||||||
import { useServiceAccounts } from 'hooks/api/getters/useServiceAccounts/useServiceAccounts';
|
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 { v4 as uuidv4 } from 'uuid';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import GeneralSelect, {} from 'component/common/GeneralSelect/GeneralSelect';
|
import GeneralSelect, {} from 'component/common/GeneralSelect/GeneralSelect';
|
||||||
@ -100,8 +100,8 @@ export const ProjectActionsForm = ({
|
|||||||
const projectId = useRequiredPathParam('projectId');
|
const projectId = useRequiredPathParam('projectId');
|
||||||
const { serviceAccounts, loading: serviceAccountsLoading } =
|
const { serviceAccounts, loading: serviceAccountsLoading } =
|
||||||
useServiceAccounts();
|
useServiceAccounts();
|
||||||
const { incomingWebhooks, loading: incomingWebhooksLoading } =
|
const { signalEndpoints, loading: signalEndpointsLoading } =
|
||||||
useIncomingWebhooks();
|
useSignalEndpoints();
|
||||||
|
|
||||||
const handleOnBlur = (callback: Function) => {
|
const handleOnBlur = (callback: Function) => {
|
||||||
setTimeout(() => callback(), 300);
|
setTimeout(() => callback(), 300);
|
||||||
@ -151,16 +151,16 @@ export const ProjectActionsForm = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const incomingWebhookOptions = useMemo(() => {
|
const signalEndpointOptions = useMemo(() => {
|
||||||
if (incomingWebhooksLoading) {
|
if (signalEndpointsLoading) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return incomingWebhooks.map((webhook) => ({
|
return signalEndpoints.map(({ id, name }) => ({
|
||||||
label: webhook.name,
|
label: name,
|
||||||
key: `${webhook.id}`,
|
key: `${id}`,
|
||||||
}));
|
}));
|
||||||
}, [incomingWebhooksLoading, incomingWebhooks]);
|
}, [signalEndpointsLoading, signalEndpoints]);
|
||||||
|
|
||||||
const serviceAccountOptions = useMemo(() => {
|
const serviceAccountOptions = useMemo(() => {
|
||||||
if (serviceAccountsLoading) {
|
if (serviceAccountsLoading) {
|
||||||
@ -218,15 +218,14 @@ export const ProjectActionsForm = ({
|
|||||||
<ProjectActionsFormStep
|
<ProjectActionsFormStep
|
||||||
name='Trigger'
|
name='Trigger'
|
||||||
resourceLink={
|
resourceLink={
|
||||||
<RouterLink to='/integrations/incoming-webhooks'>
|
<RouterLink to='/integrations/signals'>
|
||||||
Create incoming webhook
|
Create signal endpoint
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<GeneralSelect
|
<GeneralSelect
|
||||||
label='Incoming webhook'
|
label='Source'
|
||||||
name='incoming-webhook'
|
options={signalEndpointOptions}
|
||||||
options={incomingWebhookOptions}
|
|
||||||
value={`${sourceId}`}
|
value={`${sourceId}`}
|
||||||
onChange={(v) => {
|
onChange={(v) => {
|
||||||
setSourceId(parseInt(v));
|
setSourceId(parseInt(v));
|
||||||
|
@ -6,7 +6,7 @@ import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
|||||||
|
|
||||||
enum ErrorField {
|
enum ErrorField {
|
||||||
NAME = 'name',
|
NAME = 'name',
|
||||||
TRIGGER = 'trigger',
|
SOURCE = 'source',
|
||||||
FILTERS = 'filters',
|
FILTERS = 'filters',
|
||||||
ACTOR = 'actor',
|
ACTOR = 'actor',
|
||||||
ACTIONS = 'actions',
|
ACTIONS = 'actions',
|
||||||
@ -27,7 +27,7 @@ export type ActionsActionState = Omit<
|
|||||||
|
|
||||||
const DEFAULT_PROJECT_ACTIONS_FORM_ERRORS = {
|
const DEFAULT_PROJECT_ACTIONS_FORM_ERRORS = {
|
||||||
[ErrorField.NAME]: undefined,
|
[ErrorField.NAME]: undefined,
|
||||||
[ErrorField.TRIGGER]: undefined,
|
[ErrorField.SOURCE]: undefined,
|
||||||
[ErrorField.FILTERS]: undefined,
|
[ErrorField.FILTERS]: undefined,
|
||||||
[ErrorField.ACTOR]: undefined,
|
[ErrorField.ACTOR]: undefined,
|
||||||
[ErrorField.ACTIONS]: undefined,
|
[ErrorField.ACTIONS]: undefined,
|
||||||
@ -127,11 +127,11 @@ export const useProjectActionsForm = (action?: IActionSet) => {
|
|||||||
|
|
||||||
const validateSourceId = (sourceId: number) => {
|
const validateSourceId = (sourceId: number) => {
|
||||||
if (isIdEmpty(sourceId)) {
|
if (isIdEmpty(sourceId)) {
|
||||||
setError(ErrorField.TRIGGER, 'Incoming webhook is required.');
|
setError(ErrorField.SOURCE, 'Source is required.');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
clearError(ErrorField.TRIGGER);
|
clearError(ErrorField.SOURCE);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ export const ProjectActionsModal = ({
|
|||||||
enabled,
|
enabled,
|
||||||
name,
|
name,
|
||||||
match: {
|
match: {
|
||||||
source: 'incoming-webhook',
|
source: 'signal-endpoint',
|
||||||
sourceId,
|
sourceId,
|
||||||
payload: filters
|
payload: filters
|
||||||
.filter((f) => f.parameter.length > 0)
|
.filter((f) => f.parameter.length > 0)
|
||||||
@ -173,7 +173,7 @@ export const ProjectActionsModal = ({
|
|||||||
<FormTemplate
|
<FormTemplate
|
||||||
loading={loading}
|
loading={loading}
|
||||||
modal
|
modal
|
||||||
description='Actions allow you to configure automations based on specific triggers, like incoming webhooks.'
|
description='Actions allow you to configure automations based on specific signals, like the ones originated from signal endpoints.'
|
||||||
documentationLink='https://docs.getunleash.io/reference/actions'
|
documentationLink='https://docs.getunleash.io/reference/actions'
|
||||||
documentationLinkLabel='Actions documentation'
|
documentationLinkLabel='Actions documentation'
|
||||||
formatApiCode={formatApiCode}
|
formatApiCode={formatApiCode}
|
||||||
|
@ -21,7 +21,7 @@ import { ProjectActionsTableActionsCell } from './ProjectActionsTableActionsCell
|
|||||||
import { ProjectActionsModal } from './ProjectActionsModal/ProjectActionsModal';
|
import { ProjectActionsModal } from './ProjectActionsModal/ProjectActionsModal';
|
||||||
import { ProjectActionsDeleteDialog } from './ProjectActionsDeleteDialog';
|
import { ProjectActionsDeleteDialog } from './ProjectActionsDeleteDialog';
|
||||||
import { useServiceAccounts } from 'hooks/api/getters/useServiceAccounts/useServiceAccounts';
|
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 { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
|
import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
|
||||||
import { ProjectActionsEventsModal } from './ProjectActionsEventsModal/ProjectActionsEventsModal';
|
import { ProjectActionsEventsModal } from './ProjectActionsEventsModal/ProjectActionsEventsModal';
|
||||||
@ -47,7 +47,7 @@ export const ProjectActionsTable = ({
|
|||||||
const { actions, refetch } = useActions(projectId);
|
const { actions, refetch } = useActions(projectId);
|
||||||
const { toggleActionSet, removeActionSet } = useActionsApi(projectId);
|
const { toggleActionSet, removeActionSet } = useActionsApi(projectId);
|
||||||
|
|
||||||
const { incomingWebhooks } = useIncomingWebhooks();
|
const { signalEndpoints } = useSignalEndpoints();
|
||||||
const { serviceAccounts } = useServiceAccounts();
|
const { serviceAccounts } = useServiceAccounts();
|
||||||
|
|
||||||
const [eventsModalOpen, setEventsModalOpen] = useState(false);
|
const [eventsModalOpen, setEventsModalOpen] = useState(false);
|
||||||
@ -111,7 +111,7 @@ export const ProjectActionsTable = ({
|
|||||||
}: { row: { original: IActionSet } }) => (
|
}: { row: { original: IActionSet } }) => (
|
||||||
<ProjectActionsTriggerCell
|
<ProjectActionsTriggerCell
|
||||||
action={action}
|
action={action}
|
||||||
incomingWebhooks={incomingWebhooks}
|
signalEndpoints={signalEndpoints}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -202,7 +202,7 @@ export const ProjectActionsTable = ({
|
|||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[incomingWebhooks, serviceAccounts],
|
[signalEndpoints, serviceAccounts],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [initialState] = useState({
|
const [initialState] = useState({
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Avatar, Box, Link, styled } from '@mui/material';
|
import { Avatar, Box, Link, styled } from '@mui/material';
|
||||||
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
||||||
import { IActionSet } from 'interfaces/action';
|
import { IActionSet } from 'interfaces/action';
|
||||||
import { IIncomingWebhook } from 'interfaces/incomingWebhook';
|
import { ISignalEndpoint } from 'interfaces/signal';
|
||||||
import webhooksIcon from 'assets/icons/webhooks.svg';
|
import webhooksIcon from 'assets/icons/webhooks.svg';
|
||||||
import { Link as RouterLink } from 'react-router-dom';
|
import { Link as RouterLink } from 'react-router-dom';
|
||||||
import { ComponentType } from 'react';
|
import { ComponentType } from 'react';
|
||||||
@ -32,15 +32,15 @@ const StyledLink = styled(Link)<{
|
|||||||
|
|
||||||
interface IProjectActionsTriggerCellProps {
|
interface IProjectActionsTriggerCellProps {
|
||||||
action: IActionSet;
|
action: IActionSet;
|
||||||
incomingWebhooks: IIncomingWebhook[];
|
signalEndpoints: ISignalEndpoint[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProjectActionsTriggerCell = ({
|
export const ProjectActionsTriggerCell = ({
|
||||||
action,
|
action,
|
||||||
incomingWebhooks,
|
signalEndpoints,
|
||||||
}: IProjectActionsTriggerCellProps) => {
|
}: IProjectActionsTriggerCellProps) => {
|
||||||
const { sourceId } = action.match;
|
const { sourceId } = action.match;
|
||||||
const trigger = incomingWebhooks.find(({ id }) => id === sourceId);
|
const trigger = signalEndpoints.find(({ id }) => id === sourceId);
|
||||||
|
|
||||||
if (!trigger) {
|
if (!trigger) {
|
||||||
return <TextCell>No trigger</TextCell>;
|
return <TextCell>No trigger</TextCell>;
|
||||||
@ -51,12 +51,12 @@ export const ProjectActionsTriggerCell = ({
|
|||||||
<StyledCell>
|
<StyledCell>
|
||||||
<StyledIcon
|
<StyledIcon
|
||||||
src={formatAssetPath(webhooksIcon)}
|
src={formatAssetPath(webhooksIcon)}
|
||||||
alt='Incoming webhook'
|
alt='Signal endpoint'
|
||||||
variant='rounded'
|
variant='rounded'
|
||||||
/>
|
/>
|
||||||
<StyledLink
|
<StyledLink
|
||||||
component={RouterLink}
|
component={RouterLink}
|
||||||
to='/integrations/incoming-webhooks'
|
to='/integrations/signals'
|
||||||
underline='hover'
|
underline='hover'
|
||||||
>
|
>
|
||||||
{trigger.name}
|
{trigger.name}
|
||||||
|
@ -10,13 +10,13 @@ import {
|
|||||||
import Input from 'component/common/Input/Input';
|
import Input from 'component/common/Input/Input';
|
||||||
import { FormSwitch } from 'component/common/FormSwitch/FormSwitch';
|
import { FormSwitch } from 'component/common/FormSwitch/FormSwitch';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { IIncomingWebhook } from 'interfaces/incomingWebhook';
|
import { ISignalEndpoint } from 'interfaces/signal';
|
||||||
import {
|
import {
|
||||||
IncomingWebhooksFormErrors,
|
SignalEndpointsFormErrors,
|
||||||
TokenGeneration,
|
TokenGeneration,
|
||||||
} from './useIncomingWebhooksForm';
|
} from './useSignalEndpointsForm';
|
||||||
import { IncomingWebhooksFormURL } from './IncomingWebhooksFormURL';
|
import { SignalEndpointsFormURL } from './SignalEndpointsFormURL';
|
||||||
import { IncomingWebhooksTokens } from './IncomingWebhooksTokens/IncomingWebhooksTokens';
|
import { SignalEndpointsTokens } from './SignalEndpointsTokens/SignalEndpointsTokens';
|
||||||
|
|
||||||
const StyledRaisedSection = styled('div')(({ theme }) => ({
|
const StyledRaisedSection = styled('div')(({ theme }) => ({
|
||||||
background: theme.palette.background.elevation1,
|
background: theme.palette.background.elevation1,
|
||||||
@ -59,8 +59,8 @@ const StyledInlineContainer = styled('div')(({ theme }) => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
interface IIncomingWebhooksFormProps {
|
interface ISignalEndpointsFormProps {
|
||||||
incomingWebhook?: IIncomingWebhook;
|
signalEndpoint?: ISignalEndpoint;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
setEnabled: React.Dispatch<React.SetStateAction<boolean>>;
|
setEnabled: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
name: string;
|
name: string;
|
||||||
@ -71,7 +71,7 @@ interface IIncomingWebhooksFormProps {
|
|||||||
setTokenGeneration: React.Dispatch<React.SetStateAction<TokenGeneration>>;
|
setTokenGeneration: React.Dispatch<React.SetStateAction<TokenGeneration>>;
|
||||||
tokenName: string;
|
tokenName: string;
|
||||||
setTokenName: React.Dispatch<React.SetStateAction<string>>;
|
setTokenName: React.Dispatch<React.SetStateAction<string>>;
|
||||||
errors: IncomingWebhooksFormErrors;
|
errors: SignalEndpointsFormErrors;
|
||||||
validateName: (name: string) => boolean;
|
validateName: (name: string) => boolean;
|
||||||
validateTokenName: (
|
validateTokenName: (
|
||||||
tokenGeneration: TokenGeneration,
|
tokenGeneration: TokenGeneration,
|
||||||
@ -80,8 +80,8 @@ interface IIncomingWebhooksFormProps {
|
|||||||
validated: boolean;
|
validated: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IncomingWebhooksForm = ({
|
export const SignalEndpointsForm = ({
|
||||||
incomingWebhook,
|
signalEndpoint,
|
||||||
enabled,
|
enabled,
|
||||||
setEnabled,
|
setEnabled,
|
||||||
name,
|
name,
|
||||||
@ -96,7 +96,7 @@ export const IncomingWebhooksForm = ({
|
|||||||
validateName,
|
validateName,
|
||||||
validateTokenName,
|
validateTokenName,
|
||||||
validated,
|
validated,
|
||||||
}: IIncomingWebhooksFormProps) => {
|
}: ISignalEndpointsFormProps) => {
|
||||||
const handleOnBlur = (callback: Function) => {
|
const handleOnBlur = (callback: Function) => {
|
||||||
setTimeout(() => callback(), 300);
|
setTimeout(() => callback(), 300);
|
||||||
};
|
};
|
||||||
@ -107,15 +107,15 @@ export const IncomingWebhooksForm = ({
|
|||||||
<div>
|
<div>
|
||||||
<StyledRaisedSection>
|
<StyledRaisedSection>
|
||||||
<FormSwitch checked={enabled} setChecked={setEnabled}>
|
<FormSwitch checked={enabled} setChecked={setEnabled}>
|
||||||
Incoming webhook status
|
Signal endpoint status
|
||||||
</FormSwitch>
|
</FormSwitch>
|
||||||
</StyledRaisedSection>
|
</StyledRaisedSection>
|
||||||
<StyledInputDescription>
|
<StyledInputDescription>
|
||||||
What is your new incoming webhook name?
|
What is your new signal endpoint name?
|
||||||
</StyledInputDescription>
|
</StyledInputDescription>
|
||||||
<StyledInput
|
<StyledInput
|
||||||
autoFocus
|
autoFocus
|
||||||
label='Incoming webhook name'
|
label='Signal endpoint name'
|
||||||
error={Boolean(errors.name)}
|
error={Boolean(errors.name)}
|
||||||
errorText={errors.name}
|
errorText={errors.name}
|
||||||
value={name}
|
value={name}
|
||||||
@ -127,23 +127,23 @@ export const IncomingWebhooksForm = ({
|
|||||||
autoComplete='off'
|
autoComplete='off'
|
||||||
/>
|
/>
|
||||||
<StyledInputDescription>
|
<StyledInputDescription>
|
||||||
What is your new incoming webhook description?
|
What is your new signal endpoint description?
|
||||||
</StyledInputDescription>
|
</StyledInputDescription>
|
||||||
<StyledInput
|
<StyledInput
|
||||||
label='Incoming webhook description'
|
label='Signal endpoint description'
|
||||||
value={description}
|
value={description}
|
||||||
onChange={(e) => setDescription(e.target.value)}
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
autoComplete='off'
|
autoComplete='off'
|
||||||
/>
|
/>
|
||||||
<IncomingWebhooksFormURL name={name} />
|
<SignalEndpointsFormURL name={name} />
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={incomingWebhook === undefined}
|
condition={signalEndpoint === undefined}
|
||||||
show={
|
show={
|
||||||
<StyledSecondarySection>
|
<StyledSecondarySection>
|
||||||
<StyledInputDescription>Token</StyledInputDescription>
|
<StyledInputDescription>Token</StyledInputDescription>
|
||||||
<StyledInputSecondaryDescription>
|
<StyledInputSecondaryDescription>
|
||||||
In order to connect your newly created incoming
|
In order to connect your newly created signal
|
||||||
webhook, you will also need a token.{' '}
|
endpoint, you will also need a token.{' '}
|
||||||
<Link
|
<Link
|
||||||
href='https://docs.getunleash.io/reference/api-tokens-and-client-keys'
|
href='https://docs.getunleash.io/reference/api-tokens-and-client-keys'
|
||||||
target='_blank'
|
target='_blank'
|
||||||
@ -184,8 +184,8 @@ export const IncomingWebhooksForm = ({
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
<StyledInlineContainer>
|
<StyledInlineContainer>
|
||||||
<StyledInputSecondaryDescription>
|
<StyledInputSecondaryDescription>
|
||||||
A new incoming webhook token will be generated
|
A new signal endpoint token will be generated
|
||||||
for the incoming webhook, so you can get started
|
for the signal endpoint, so you can get started
|
||||||
right away.
|
right away.
|
||||||
</StyledInputSecondaryDescription>
|
</StyledInputSecondaryDescription>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
@ -221,10 +221,10 @@ export const IncomingWebhooksForm = ({
|
|||||||
elseShow={
|
elseShow={
|
||||||
<>
|
<>
|
||||||
<StyledInputDescription>
|
<StyledInputDescription>
|
||||||
Incoming webhook tokens
|
Signal endpoint tokens
|
||||||
</StyledInputDescription>
|
</StyledInputDescription>
|
||||||
<IncomingWebhooksTokens
|
<SignalEndpointsTokens
|
||||||
incomingWebhook={incomingWebhook!}
|
signalEndpoint={signalEndpoint!}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
@ -4,7 +4,7 @@ import copy from 'copy-to-clipboard';
|
|||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
import useToast from 'hooks/useToast';
|
import useToast from 'hooks/useToast';
|
||||||
|
|
||||||
const StyledIncomingWebhookUrlSection = styled('div')(({ theme }) => ({
|
const StyledSignalEndpointUrlSection = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
padding: theme.spacing(1.5),
|
padding: theme.spacing(1.5),
|
||||||
@ -15,11 +15,11 @@ const StyledIncomingWebhookUrlSection = styled('div')(({ theme }) => ({
|
|||||||
marginTop: theme.spacing(3),
|
marginTop: theme.spacing(3),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledIncomingWebhookUrlSectionDescription = styled('p')(({ theme }) => ({
|
const StyledSignalEndpointUrlSectionDescription = styled('p')(({ theme }) => ({
|
||||||
fontSize: theme.fontSizes.smallBody,
|
fontSize: theme.fontSizes.smallBody,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledIncomingWebhookUrl = styled('div')(({ theme }) => ({
|
const StyledSignalEndpointUrl = styled('div')(({ theme }) => ({
|
||||||
fontSize: theme.fontSizes.smallBody,
|
fontSize: theme.fontSizes.smallBody,
|
||||||
backgroundColor: theme.palette.background.elevation2,
|
backgroundColor: theme.palette.background.elevation2,
|
||||||
padding: theme.spacing(0.5, 1, 0.5, 2),
|
padding: theme.spacing(0.5, 1, 0.5, 2),
|
||||||
@ -31,17 +31,17 @@ const StyledIncomingWebhookUrl = styled('div')(({ theme }) => ({
|
|||||||
wordBreak: 'break-all',
|
wordBreak: 'break-all',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
interface IIncomingWebhooksFormURLProps {
|
interface ISignalEndpointsFormURLProps {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IncomingWebhooksFormURL = ({
|
export const SignalEndpointsFormURL = ({
|
||||||
name,
|
name,
|
||||||
}: IIncomingWebhooksFormURLProps) => {
|
}: ISignalEndpointsFormURLProps) => {
|
||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
const { setToastData } = useToast();
|
const { setToastData } = useToast();
|
||||||
|
|
||||||
const url = `${uiConfig.unleashUrl}/api/incoming-webhook/${name}`;
|
const url = `${uiConfig.unleashUrl}/api/signal-endpoint/${name}`;
|
||||||
|
|
||||||
const onCopyToClipboard = () => {
|
const onCopyToClipboard = () => {
|
||||||
copy(url);
|
copy(url);
|
||||||
@ -52,18 +52,18 @@ export const IncomingWebhooksFormURL = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledIncomingWebhookUrlSection>
|
<StyledSignalEndpointUrlSection>
|
||||||
<StyledIncomingWebhookUrlSectionDescription>
|
<StyledSignalEndpointUrlSectionDescription>
|
||||||
Incoming webhook URL:
|
Signal endpoint URL:
|
||||||
</StyledIncomingWebhookUrlSectionDescription>
|
</StyledSignalEndpointUrlSectionDescription>
|
||||||
<StyledIncomingWebhookUrl>
|
<StyledSignalEndpointUrl>
|
||||||
{url}
|
{url}
|
||||||
<Tooltip title='Copy URL' arrow>
|
<Tooltip title='Copy URL' arrow>
|
||||||
<IconButton onClick={onCopyToClipboard} size='large'>
|
<IconButton onClick={onCopyToClipboard} size='large'>
|
||||||
<CopyIcon />
|
<CopyIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</StyledIncomingWebhookUrl>
|
</StyledSignalEndpointUrl>
|
||||||
</StyledIncomingWebhookUrlSection>
|
</StyledSignalEndpointUrlSection>
|
||||||
);
|
);
|
||||||
};
|
};
|
@ -15,27 +15,24 @@ import { DateCell } from 'component/common/Table/cells/DateCell/DateCell';
|
|||||||
import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell';
|
import { HighlightCell } from 'component/common/Table/cells/HighlightCell/HighlightCell';
|
||||||
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
||||||
import { PAT_LIMIT } from '@server/util/constants';
|
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 { useSearch } from 'hooks/useSearch';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useTable, SortingRule, useSortBy, useFlexLayout } from 'react-table';
|
import { useTable, SortingRule, useSortBy, useFlexLayout } from 'react-table';
|
||||||
import { sortTypes } from 'utils/sortTypes';
|
import { sortTypes } from 'utils/sortTypes';
|
||||||
import { IncomingWebhooksTokensCreateDialog } from './IncomingWebhooksTokensCreateDialog';
|
import { SignalEndpointsTokensCreateDialog } from './SignalEndpointsTokensCreateDialog';
|
||||||
import { IncomingWebhooksTokensDialog } from './IncomingWebhooksTokensDialog';
|
import { SignalEndpointsTokensDialog } from './SignalEndpointsTokensDialog';
|
||||||
import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns';
|
import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||||
import {
|
import {
|
||||||
IncomingWebhookTokenPayload,
|
SignalEndpointTokenPayload,
|
||||||
useIncomingWebhookTokensApi,
|
useSignalEndpointTokensApi,
|
||||||
} from 'hooks/api/actions/useIncomingWebhookTokensApi/useIncomingWebhookTokensApi';
|
} from 'hooks/api/actions/useSignalEndpointTokensApi/useSignalEndpointTokensApi';
|
||||||
import useToast from 'hooks/useToast';
|
import useToast from 'hooks/useToast';
|
||||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
import {
|
import { ISignalEndpoint, ISignalEndpointToken } from 'interfaces/signal';
|
||||||
IIncomingWebhook,
|
import { useSignalEndpoints } from 'hooks/api/getters/useSignalEndpoints/useSignalEndpoints';
|
||||||
IIncomingWebhookToken,
|
|
||||||
} from 'interfaces/incomingWebhook';
|
|
||||||
import { useIncomingWebhooks } from 'hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks';
|
|
||||||
|
|
||||||
const StyledHeader = styled('div')(({ theme }) => ({
|
const StyledHeader = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -74,21 +71,21 @@ export type PageQueryType = Partial<
|
|||||||
|
|
||||||
const defaultSort: SortingRule<string> = { id: 'createdAt', desc: true };
|
const defaultSort: SortingRule<string> = { id: 'createdAt', desc: true };
|
||||||
|
|
||||||
interface IIncomingWebhooksTokensProps {
|
interface ISignalEndpointsTokensProps {
|
||||||
incomingWebhook: IIncomingWebhook;
|
signalEndpoint: ISignalEndpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IncomingWebhooksTokens = ({
|
export const SignalEndpointsTokens = ({
|
||||||
incomingWebhook,
|
signalEndpoint,
|
||||||
}: IIncomingWebhooksTokensProps) => {
|
}: ISignalEndpointsTokensProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { setToastData, setToastApiError } = useToast();
|
const { setToastData, setToastApiError } = useToast();
|
||||||
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
||||||
const { incomingWebhookTokens, refetch: refetchTokens } =
|
const { signalEndpointTokens, refetch: refetchTokens } =
|
||||||
useIncomingWebhookTokens(incomingWebhook.id);
|
useSignalEndpointTokens(signalEndpoint.id);
|
||||||
const { refetch } = useIncomingWebhooks();
|
const { refetch } = useSignalEndpoints();
|
||||||
const { addIncomingWebhookToken, removeIncomingWebhookToken } =
|
const { addSignalEndpointToken, removeSignalEndpointToken } =
|
||||||
useIncomingWebhookTokensApi();
|
useSignalEndpointTokensApi();
|
||||||
|
|
||||||
const [initialState] = useState(() => ({
|
const [initialState] = useState(() => ({
|
||||||
sortBy: [defaultSort],
|
sortBy: [defaultSort],
|
||||||
@ -99,12 +96,12 @@ export const IncomingWebhooksTokens = ({
|
|||||||
const [tokenOpen, setTokenOpen] = useState(false);
|
const [tokenOpen, setTokenOpen] = useState(false);
|
||||||
const [deleteOpen, setDeleteOpen] = useState(false);
|
const [deleteOpen, setDeleteOpen] = useState(false);
|
||||||
const [newToken, setNewToken] = useState('');
|
const [newToken, setNewToken] = useState('');
|
||||||
const [selectedToken, setSelectedToken] = useState<IIncomingWebhookToken>();
|
const [selectedToken, setSelectedToken] = useState<ISignalEndpointToken>();
|
||||||
|
|
||||||
const onCreateClick = async (newToken: IncomingWebhookTokenPayload) => {
|
const onCreateClick = async (newToken: SignalEndpointTokenPayload) => {
|
||||||
try {
|
try {
|
||||||
const { token } = await addIncomingWebhookToken(
|
const { token } = await addSignalEndpointToken(
|
||||||
incomingWebhook.id,
|
signalEndpoint.id,
|
||||||
newToken,
|
newToken,
|
||||||
);
|
);
|
||||||
refetch();
|
refetch();
|
||||||
@ -124,8 +121,8 @@ export const IncomingWebhooksTokens = ({
|
|||||||
const onDeleteClick = async () => {
|
const onDeleteClick = async () => {
|
||||||
if (selectedToken) {
|
if (selectedToken) {
|
||||||
try {
|
try {
|
||||||
await removeIncomingWebhookToken(
|
await removeSignalEndpointToken(
|
||||||
incomingWebhook.id,
|
signalEndpoint.id,
|
||||||
selectedToken.id,
|
selectedToken.id,
|
||||||
);
|
);
|
||||||
refetch();
|
refetch();
|
||||||
@ -186,7 +183,7 @@ export const IncomingWebhooksTokens = ({
|
|||||||
const { data, getSearchText, getSearchContext } = useSearch(
|
const { data, getSearchText, getSearchContext } = useSearch(
|
||||||
columns,
|
columns,
|
||||||
searchValue,
|
searchValue,
|
||||||
incomingWebhookTokens,
|
signalEndpointTokens,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { headerGroups, rows, prepareRow, setHiddenColumns } = useTable(
|
const { headerGroups, rows, prepareRow, setHiddenColumns } = useTable(
|
||||||
@ -226,7 +223,7 @@ export const IncomingWebhooksTokens = ({
|
|||||||
<Button
|
<Button
|
||||||
variant='contained'
|
variant='contained'
|
||||||
color='primary'
|
color='primary'
|
||||||
disabled={incomingWebhookTokens.length >= PAT_LIMIT}
|
disabled={signalEndpointTokens.length >= PAT_LIMIT}
|
||||||
onClick={() => setCreateOpen(true)}
|
onClick={() => setCreateOpen(true)}
|
||||||
>
|
>
|
||||||
New token
|
New token
|
||||||
@ -254,31 +251,31 @@ export const IncomingWebhooksTokens = ({
|
|||||||
elseShow={
|
elseShow={
|
||||||
<StyledTablePlaceholder>
|
<StyledTablePlaceholder>
|
||||||
<StyledPlaceholderTitle>
|
<StyledPlaceholderTitle>
|
||||||
You have no tokens for this incoming webhook
|
You have no tokens for this signal endpoint
|
||||||
yet.
|
yet.
|
||||||
</StyledPlaceholderTitle>
|
</StyledPlaceholderTitle>
|
||||||
<StyledPlaceholderSubtitle>
|
<StyledPlaceholderSubtitle>
|
||||||
Create a token to start using this incoming
|
Create a token to start using this signal
|
||||||
webhook.
|
endpoint.
|
||||||
</StyledPlaceholderSubtitle>
|
</StyledPlaceholderSubtitle>
|
||||||
<Button
|
<Button
|
||||||
variant='outlined'
|
variant='outlined'
|
||||||
onClick={() => setCreateOpen(true)}
|
onClick={() => setCreateOpen(true)}
|
||||||
>
|
>
|
||||||
Create new incoming webhook token
|
Create new signal endpoint token
|
||||||
</Button>
|
</Button>
|
||||||
</StyledTablePlaceholder>
|
</StyledTablePlaceholder>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<IncomingWebhooksTokensCreateDialog
|
<SignalEndpointsTokensCreateDialog
|
||||||
open={createOpen}
|
open={createOpen}
|
||||||
setOpen={setCreateOpen}
|
setOpen={setCreateOpen}
|
||||||
tokens={incomingWebhookTokens}
|
tokens={signalEndpointTokens}
|
||||||
onCreateClick={onCreateClick}
|
onCreateClick={onCreateClick}
|
||||||
/>
|
/>
|
||||||
<IncomingWebhooksTokensDialog
|
<SignalEndpointsTokensDialog
|
||||||
open={tokenOpen}
|
open={tokenOpen}
|
||||||
setOpen={setTokenOpen}
|
setOpen={setTokenOpen}
|
||||||
token={newToken}
|
token={newToken}
|
||||||
@ -296,7 +293,7 @@ export const IncomingWebhooksTokens = ({
|
|||||||
<Typography>
|
<Typography>
|
||||||
Any applications or scripts using this token "
|
Any applications or scripts using this token "
|
||||||
<strong>{selectedToken?.name}</strong>" will no longer be
|
<strong>{selectedToken?.name}</strong>" 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.
|
undo this action.
|
||||||
</Typography>
|
</Typography>
|
||||||
</Dialogue>
|
</Dialogue>
|
@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||||
import { IncomingWebhookTokenPayload } from 'hooks/api/actions/useIncomingWebhookTokensApi/useIncomingWebhookTokensApi';
|
import { SignalEndpointTokenPayload } from 'hooks/api/actions/useSignalEndpointTokensApi/useSignalEndpointTokensApi';
|
||||||
import { IIncomingWebhookToken } from 'interfaces/incomingWebhook';
|
import { ISignalEndpointToken } from 'interfaces/signal';
|
||||||
import { styled } from '@mui/material';
|
import { styled } from '@mui/material';
|
||||||
import Input from 'component/common/Input/Input';
|
import Input from 'component/common/Input/Input';
|
||||||
|
|
||||||
@ -19,19 +19,19 @@ const StyledInput = styled(Input)(({ theme }) => ({
|
|||||||
maxWidth: theme.spacing(50),
|
maxWidth: theme.spacing(50),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
interface IIncomingWebhooksTokensCreateDialogProps {
|
interface ISignalEndpointsTokensCreateDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
tokens: IIncomingWebhookToken[];
|
tokens: ISignalEndpointToken[];
|
||||||
onCreateClick: (newToken: IncomingWebhookTokenPayload) => void;
|
onCreateClick: (newToken: SignalEndpointTokenPayload) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IncomingWebhooksTokensCreateDialog = ({
|
export const SignalEndpointsTokensCreateDialog = ({
|
||||||
open,
|
open,
|
||||||
setOpen,
|
setOpen,
|
||||||
tokens,
|
tokens,
|
||||||
onCreateClick,
|
onCreateClick,
|
||||||
}: IIncomingWebhooksTokensCreateDialogProps) => {
|
}: ISignalEndpointsTokensCreateDialogProps) => {
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
|
|
||||||
const [nameError, setNameError] = useState('');
|
const [nameError, setNameError] = useState('');
|
@ -6,17 +6,17 @@ const StyledAlert = styled(Alert)(({ theme }) => ({
|
|||||||
marginBottom: theme.spacing(3),
|
marginBottom: theme.spacing(3),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
interface IIncomingWebhooksTokensDialogProps {
|
interface ISignalEndpointsTokensDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
token?: string;
|
token?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IncomingWebhooksTokensDialog = ({
|
export const SignalEndpointsTokensDialog = ({
|
||||||
open,
|
open,
|
||||||
setOpen,
|
setOpen,
|
||||||
token,
|
token,
|
||||||
}: IIncomingWebhooksTokensDialogProps) => (
|
}: ISignalEndpointsTokensDialogProps) => (
|
||||||
<Dialogue
|
<Dialogue
|
||||||
open={open}
|
open={open}
|
||||||
secondaryButtonText='Close'
|
secondaryButtonText='Close'
|
||||||
@ -25,10 +25,10 @@ export const IncomingWebhooksTokensDialog = ({
|
|||||||
setOpen(false);
|
setOpen(false);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
title='Incoming webhook token created'
|
title='Signal endpoint token created'
|
||||||
>
|
>
|
||||||
<StyledAlert severity='info'>
|
<StyledAlert severity='info'>
|
||||||
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!
|
to see it again!
|
||||||
</StyledAlert>
|
</StyledAlert>
|
||||||
<Typography variant='body1'>Your token:</Typography>
|
<Typography variant='body1'>Your token:</Typography>
|
@ -1,6 +1,6 @@
|
|||||||
import { URL_SAFE_BASIC } from '@server/util/constants';
|
import { URL_SAFE_BASIC } from '@server/util/constants';
|
||||||
import { useIncomingWebhooks } from 'hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks';
|
import { useSignalEndpoints } from 'hooks/api/getters/useSignalEndpoints/useSignalEndpoints';
|
||||||
import { IIncomingWebhook } from 'interfaces/incomingWebhook';
|
import { ISignalEndpoint } from 'interfaces/signal';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
enum ErrorField {
|
enum ErrorField {
|
||||||
@ -8,20 +8,20 @@ enum ErrorField {
|
|||||||
TOKEN_NAME = 'tokenName',
|
TOKEN_NAME = 'tokenName',
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_INCOMING_WEBHOOKS_FORM_ERRORS = {
|
const DEFAULT_SIGNAL_ENDPOINTS_FORM_ERRORS = {
|
||||||
[ErrorField.NAME]: undefined,
|
[ErrorField.NAME]: undefined,
|
||||||
[ErrorField.TOKEN_NAME]: undefined,
|
[ErrorField.TOKEN_NAME]: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type IncomingWebhooksFormErrors = Record<ErrorField, string | undefined>;
|
export type SignalEndpointsFormErrors = Record<ErrorField, string | undefined>;
|
||||||
|
|
||||||
export enum TokenGeneration {
|
export enum TokenGeneration {
|
||||||
LATER = 'later',
|
LATER = 'later',
|
||||||
NOW = 'now',
|
NOW = 'now',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useIncomingWebhooksForm = (incomingWebhook?: IIncomingWebhook) => {
|
export const useSignalEndpointsForm = (signalEndpoint?: ISignalEndpoint) => {
|
||||||
const { incomingWebhooks } = useIncomingWebhooks();
|
const { signalEndpoints } = useSignalEndpoints();
|
||||||
|
|
||||||
const [enabled, setEnabled] = useState(false);
|
const [enabled, setEnabled] = useState(false);
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
@ -32,21 +32,21 @@ export const useIncomingWebhooksForm = (incomingWebhook?: IIncomingWebhook) => {
|
|||||||
const [tokenName, setTokenName] = useState('');
|
const [tokenName, setTokenName] = useState('');
|
||||||
|
|
||||||
const reloadForm = () => {
|
const reloadForm = () => {
|
||||||
setEnabled(incomingWebhook?.enabled ?? true);
|
setEnabled(signalEndpoint?.enabled ?? true);
|
||||||
setName(incomingWebhook?.name || '');
|
setName(signalEndpoint?.name || '');
|
||||||
setDescription(incomingWebhook?.description || '');
|
setDescription(signalEndpoint?.description || '');
|
||||||
setTokenGeneration(TokenGeneration.LATER);
|
setTokenGeneration(TokenGeneration.LATER);
|
||||||
setTokenName('');
|
setTokenName('');
|
||||||
setValidated(false);
|
setValidated(false);
|
||||||
setErrors(DEFAULT_INCOMING_WEBHOOKS_FORM_ERRORS);
|
setErrors(DEFAULT_SIGNAL_ENDPOINTS_FORM_ERRORS);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
reloadForm();
|
reloadForm();
|
||||||
}, [incomingWebhook]);
|
}, [signalEndpoint]);
|
||||||
|
|
||||||
const [errors, setErrors] = useState<IncomingWebhooksFormErrors>(
|
const [errors, setErrors] = useState<SignalEndpointsFormErrors>(
|
||||||
DEFAULT_INCOMING_WEBHOOKS_FORM_ERRORS,
|
DEFAULT_SIGNAL_ENDPOINTS_FORM_ERRORS,
|
||||||
);
|
);
|
||||||
const [validated, setValidated] = useState(false);
|
const [validated, setValidated] = useState(false);
|
||||||
|
|
||||||
@ -61,8 +61,8 @@ export const useIncomingWebhooksForm = (incomingWebhook?: IIncomingWebhook) => {
|
|||||||
const isEmpty = (value: string) => !value.length;
|
const isEmpty = (value: string) => !value.length;
|
||||||
|
|
||||||
const isNameNotUnique = (value: string) =>
|
const isNameNotUnique = (value: string) =>
|
||||||
incomingWebhooks?.some(
|
signalEndpoints?.some(
|
||||||
({ id, name }) => id !== incomingWebhook?.id && name === value,
|
({ id, name }) => id !== signalEndpoint?.id && name === value,
|
||||||
);
|
);
|
||||||
|
|
||||||
const isNameInvalid = (value: string) => !URL_SAFE_BASIC.test(value);
|
const isNameInvalid = (value: string) => !URL_SAFE_BASIC.test(value);
|
@ -5,18 +5,18 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
|||||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||||
import useToast from 'hooks/useToast';
|
import useToast from 'hooks/useToast';
|
||||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
import { IIncomingWebhook } from 'interfaces/incomingWebhook';
|
import { ISignalEndpoint } from 'interfaces/signal';
|
||||||
import { useIncomingWebhooks } from 'hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks';
|
import { useSignalEndpoints } from 'hooks/api/getters/useSignalEndpoints/useSignalEndpoints';
|
||||||
import {
|
import {
|
||||||
IncomingWebhookPayload,
|
SignalEndpointPayload,
|
||||||
useIncomingWebhooksApi,
|
useSignalEndpointsApi,
|
||||||
} from 'hooks/api/actions/useIncomingWebhooksApi/useIncomingWebhooksApi';
|
} from 'hooks/api/actions/useSignalEndpointsApi/useSignalEndpointsApi';
|
||||||
import { useIncomingWebhookTokensApi } from 'hooks/api/actions/useIncomingWebhookTokensApi/useIncomingWebhookTokensApi';
|
import { useSignalEndpointTokensApi } from 'hooks/api/actions/useSignalEndpointTokensApi/useSignalEndpointTokensApi';
|
||||||
import { IncomingWebhooksForm } from './IncomingWebhooksForm/IncomingWebhooksForm';
|
import { SignalEndpointsForm } from './SignalEndpointsForm/SignalEndpointsForm';
|
||||||
import {
|
import {
|
||||||
TokenGeneration,
|
TokenGeneration,
|
||||||
useIncomingWebhooksForm,
|
useSignalEndpointsForm,
|
||||||
} from './IncomingWebhooksForm/useIncomingWebhooksForm';
|
} from './SignalEndpointsForm/useSignalEndpointsForm';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
|
|
||||||
const StyledHeader = styled('div')(({ theme }) => ({
|
const StyledHeader = styled('div')(({ theme }) => ({
|
||||||
@ -48,25 +48,25 @@ const StyledCancelButton = styled(Button)(({ theme }) => ({
|
|||||||
marginLeft: theme.spacing(3),
|
marginLeft: theme.spacing(3),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
interface IIncomingWebhooksModalProps {
|
interface ISignalEndpointsModalProps {
|
||||||
incomingWebhook?: IIncomingWebhook;
|
signalEndpoint?: ISignalEndpoint;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
newToken: (token: string) => void;
|
newToken: (token: string) => void;
|
||||||
onOpenEvents: () => void;
|
onOpenSignals: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IncomingWebhooksModal = ({
|
export const SignalEndpointsModal = ({
|
||||||
incomingWebhook,
|
signalEndpoint,
|
||||||
open,
|
open,
|
||||||
setOpen,
|
setOpen,
|
||||||
newToken,
|
newToken,
|
||||||
onOpenEvents,
|
onOpenSignals,
|
||||||
}: IIncomingWebhooksModalProps) => {
|
}: ISignalEndpointsModalProps) => {
|
||||||
const { refetch } = useIncomingWebhooks();
|
const { refetch } = useSignalEndpoints();
|
||||||
const { addIncomingWebhook, updateIncomingWebhook, loading } =
|
const { addSignalEndpoint, updateSignalEndpoint, loading } =
|
||||||
useIncomingWebhooksApi();
|
useSignalEndpointsApi();
|
||||||
const { addIncomingWebhookToken } = useIncomingWebhookTokensApi();
|
const { addSignalEndpointToken } = useSignalEndpointTokensApi();
|
||||||
const { setToastData, setToastApiError } = useToast();
|
const { setToastData, setToastApiError } = useToast();
|
||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
|
|
||||||
@ -87,16 +87,16 @@ export const IncomingWebhooksModal = ({
|
|||||||
validate,
|
validate,
|
||||||
validated,
|
validated,
|
||||||
reloadForm,
|
reloadForm,
|
||||||
} = useIncomingWebhooksForm(incomingWebhook);
|
} = useSignalEndpointsForm(signalEndpoint);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
reloadForm();
|
reloadForm();
|
||||||
}, [open]);
|
}, [open]);
|
||||||
|
|
||||||
const editing = incomingWebhook !== undefined;
|
const editing = signalEndpoint !== undefined;
|
||||||
const title = `${editing ? 'Edit' : 'New'} incoming webhook`;
|
const title = `${editing ? 'Edit' : 'New'} signal endpoint`;
|
||||||
|
|
||||||
const payload: IncomingWebhookPayload = {
|
const payload: SignalEndpointPayload = {
|
||||||
enabled,
|
enabled,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
@ -104,8 +104,8 @@ export const IncomingWebhooksModal = ({
|
|||||||
|
|
||||||
const formatApiCode = () => `curl --location --request ${
|
const formatApiCode = () => `curl --location --request ${
|
||||||
editing ? 'PUT' : 'POST'
|
editing ? 'PUT' : 'POST'
|
||||||
} '${uiConfig.unleashUrl}/api/admin/incoming-webhooks${
|
} '${uiConfig.unleashUrl}/api/admin/signal-endpoints${
|
||||||
editing ? `/${incomingWebhook.id}` : ''
|
editing ? `/${signalEndpoint.id}` : ''
|
||||||
}' \\
|
}' \\
|
||||||
--header 'Authorization: INSERT_API_KEY' \\
|
--header 'Authorization: INSERT_API_KEY' \\
|
||||||
--header 'Content-Type: application/json' \\
|
--header 'Content-Type: application/json' \\
|
||||||
@ -118,18 +118,18 @@ export const IncomingWebhooksModal = ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (editing) {
|
if (editing) {
|
||||||
await updateIncomingWebhook(incomingWebhook.id, payload);
|
await updateSignalEndpoint(signalEndpoint.id, payload);
|
||||||
} else {
|
} else {
|
||||||
const { id } = await addIncomingWebhook(payload);
|
const { id } = await addSignalEndpoint(payload);
|
||||||
if (tokenGeneration === TokenGeneration.NOW) {
|
if (tokenGeneration === TokenGeneration.NOW) {
|
||||||
const { token } = await addIncomingWebhookToken(id, {
|
const { token } = await addSignalEndpointToken(id, {
|
||||||
name: tokenName,
|
name: tokenName,
|
||||||
});
|
});
|
||||||
newToken(token);
|
newToken(token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setToastData({
|
setToastData({
|
||||||
title: `Incoming webhook ${
|
title: `Signal endpoint ${
|
||||||
editing ? 'updated' : 'added'
|
editing ? 'updated' : 'added'
|
||||||
} successfully`,
|
} successfully`,
|
||||||
type: 'success',
|
type: 'success',
|
||||||
@ -152,21 +152,21 @@ export const IncomingWebhooksModal = ({
|
|||||||
<FormTemplate
|
<FormTemplate
|
||||||
loading={loading}
|
loading={loading}
|
||||||
modal
|
modal
|
||||||
description='Incoming Webhooks allow third-party services to send observable events to Unleash.'
|
description='Signal endpoints allow third-party services to send signals to Unleash.'
|
||||||
documentationLink='https://docs.getunleash.io/reference/incoming-webhooks'
|
documentationLink='https://docs.getunleash.io/reference/signals'
|
||||||
documentationLinkLabel='Incoming webhooks documentation'
|
documentationLinkLabel='Signals documentation'
|
||||||
formatApiCode={formatApiCode}
|
formatApiCode={formatApiCode}
|
||||||
>
|
>
|
||||||
<StyledHeader>
|
<StyledHeader>
|
||||||
<StyledTitle>{title}</StyledTitle>
|
<StyledTitle>{title}</StyledTitle>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={editing}
|
condition={editing}
|
||||||
show={<Link onClick={onOpenEvents}>View events</Link>}
|
show={<Link onClick={onOpenSignals}>View signals</Link>}
|
||||||
/>
|
/>
|
||||||
</StyledHeader>
|
</StyledHeader>
|
||||||
<StyledForm onSubmit={onSubmit}>
|
<StyledForm onSubmit={onSubmit}>
|
||||||
<IncomingWebhooksForm
|
<SignalEndpointsForm
|
||||||
incomingWebhook={incomingWebhook}
|
signalEndpoint={signalEndpoint}
|
||||||
enabled={enabled}
|
enabled={enabled}
|
||||||
setEnabled={setEnabled}
|
setEnabled={setEnabled}
|
||||||
name={name}
|
name={name}
|
||||||
@ -188,7 +188,7 @@ export const IncomingWebhooksModal = ({
|
|||||||
variant='contained'
|
variant='contained'
|
||||||
color='primary'
|
color='primary'
|
||||||
>
|
>
|
||||||
{editing ? 'Save' : 'Add'} incoming webhook
|
{editing ? 'Save' : 'Add'} signal endpoint
|
||||||
</Button>
|
</Button>
|
||||||
<StyledCancelButton
|
<StyledCancelButton
|
||||||
onClick={() => {
|
onClick={() => {
|
@ -1,7 +1,7 @@
|
|||||||
import { Button, Link, styled } from '@mui/material';
|
import { Button, Link, styled } from '@mui/material';
|
||||||
import { SidebarModal } from 'component/common/SidebarModal/SidebarModal';
|
import { SidebarModal } from 'component/common/SidebarModal/SidebarModal';
|
||||||
import { IIncomingWebhook } from 'interfaces/incomingWebhook';
|
import { ISignalEndpoint } from 'interfaces/signal';
|
||||||
import { useIncomingWebhookEvents } from 'hooks/api/getters/useIncomingWebhookEvents/useIncomingWebhookEvents';
|
import { useSignalEndpointSignals } from 'hooks/api/getters/useSignalEndpointSignals/useSignalEndpointSignals';
|
||||||
import { Suspense, lazy } from 'react';
|
import { Suspense, lazy } from 'react';
|
||||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
@ -55,31 +55,31 @@ const StyledButtonContainer = styled('div')(({ theme }) => ({
|
|||||||
paddingTop: theme.spacing(4),
|
paddingTop: theme.spacing(4),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
interface IIncomingWebhooksEventsModalProps {
|
interface ISignalEndpointsSignalsModalProps {
|
||||||
incomingWebhook?: IIncomingWebhook;
|
signalEndpoint?: ISignalEndpoint;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
onOpenConfiguration: () => void;
|
onOpenConfiguration: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IncomingWebhooksEventsModal = ({
|
export const SignalEndpointsSignalsModal = ({
|
||||||
incomingWebhook,
|
signalEndpoint,
|
||||||
open,
|
open,
|
||||||
setOpen,
|
setOpen,
|
||||||
onOpenConfiguration,
|
onOpenConfiguration,
|
||||||
}: IIncomingWebhooksEventsModalProps) => {
|
}: ISignalEndpointsSignalsModalProps) => {
|
||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
const { locationSettings } = useLocationSettings();
|
const { locationSettings } = useLocationSettings();
|
||||||
const { incomingWebhookEvents, hasMore, loadMore, loading } =
|
const { signalEndpointSignals, hasMore, loadMore, loading } =
|
||||||
useIncomingWebhookEvents(incomingWebhook?.id, 20, {
|
useSignalEndpointSignals(signalEndpoint?.id, 20, {
|
||||||
refreshInterval: 5000,
|
refreshInterval: 5000,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!incomingWebhook) {
|
if (!signalEndpoint) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = `Events: ${incomingWebhook.name}`;
|
const title = `Signals: ${signalEndpoint.name}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarModal
|
<SidebarModal
|
||||||
@ -90,11 +90,11 @@ export const IncomingWebhooksEventsModal = ({
|
|||||||
label={title}
|
label={title}
|
||||||
>
|
>
|
||||||
<FormTemplate
|
<FormTemplate
|
||||||
loading={loading && incomingWebhookEvents.length === 0}
|
loading={loading && signalEndpointSignals.length === 0}
|
||||||
modal
|
modal
|
||||||
description='Incoming Webhooks allow third-party services to send observable events to Unleash.'
|
description=''
|
||||||
documentationLink='https://docs.getunleash.io/reference/incoming-webhooks'
|
documentationLink=''
|
||||||
documentationLinkLabel='Incoming webhooks documentation'
|
documentationLinkLabel=''
|
||||||
showGuidance={false}
|
showGuidance={false}
|
||||||
>
|
>
|
||||||
<StyledHeader>
|
<StyledHeader>
|
||||||
@ -106,32 +106,32 @@ export const IncomingWebhooksEventsModal = ({
|
|||||||
</StyledHeaderRow>
|
</StyledHeaderRow>
|
||||||
<StyledHeaderSubtitle>
|
<StyledHeaderSubtitle>
|
||||||
<p>
|
<p>
|
||||||
{uiConfig.unleashUrl}/api/incoming-webhook/
|
{uiConfig.unleashUrl}/api/signal-endpoint/
|
||||||
{incomingWebhook.name}
|
{signalEndpoint.name}
|
||||||
</p>
|
</p>
|
||||||
<StyledDescription>
|
<StyledDescription>
|
||||||
{incomingWebhook.description}
|
{signalEndpoint.description}
|
||||||
</StyledDescription>
|
</StyledDescription>
|
||||||
</StyledHeaderSubtitle>
|
</StyledHeaderSubtitle>
|
||||||
</StyledHeader>
|
</StyledHeader>
|
||||||
<StyledForm>
|
<StyledForm>
|
||||||
<SidePanelList
|
<SidePanelList
|
||||||
height={960}
|
height={960}
|
||||||
items={incomingWebhookEvents}
|
items={signalEndpointSignals}
|
||||||
columns={[
|
columns={[
|
||||||
{
|
{
|
||||||
header: 'Date',
|
header: 'Date',
|
||||||
maxWidth: 180,
|
maxWidth: 180,
|
||||||
cell: (event) =>
|
cell: ({ createdAt }) =>
|
||||||
formatDateYMDHMS(
|
formatDateYMDHMS(
|
||||||
event.createdAt,
|
createdAt,
|
||||||
locationSettings?.locale,
|
locationSettings?.locale,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Token',
|
header: 'Token',
|
||||||
maxWidth: 350,
|
maxWidth: 350,
|
||||||
cell: (event) => event.tokenName,
|
cell: ({ tokenName }) => tokenName,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
sidePanelHeader='Payload'
|
sidePanelHeader='Payload'
|
||||||
@ -157,11 +157,11 @@ export const IncomingWebhooksEventsModal = ({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={incomingWebhookEvents.length === 0}
|
condition={signalEndpointSignals.length === 0}
|
||||||
show={
|
show={
|
||||||
<p>
|
<p>
|
||||||
No events have been received for this incoming
|
No signals have been received on this signal
|
||||||
webhook.
|
endpoint.
|
||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
@ -23,21 +23,21 @@ const StyledBoxCell = styled(Box)({
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IIncomingWebhooksActionsCellProps {
|
interface ISignalEndpointsActionsCellProps {
|
||||||
incomingWebhookId: number;
|
signalEndpointId: number;
|
||||||
onCopyToClipboard: (event: React.SyntheticEvent) => void;
|
onCopyToClipboard: (event: React.SyntheticEvent) => void;
|
||||||
onOpenEvents: (event: React.SyntheticEvent) => void;
|
onOpenSignals: (event: React.SyntheticEvent) => void;
|
||||||
onEdit: (event: React.SyntheticEvent) => void;
|
onEdit: (event: React.SyntheticEvent) => void;
|
||||||
onDelete: (event: React.SyntheticEvent) => void;
|
onDelete: (event: React.SyntheticEvent) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IncomingWebhooksActionsCell = ({
|
export const SignalEndpointsActionsCell = ({
|
||||||
incomingWebhookId,
|
signalEndpointId,
|
||||||
onCopyToClipboard,
|
onCopyToClipboard,
|
||||||
onOpenEvents,
|
onOpenSignals,
|
||||||
onEdit,
|
onEdit,
|
||||||
onDelete,
|
onDelete,
|
||||||
}: IIncomingWebhooksActionsCellProps) => {
|
}: ISignalEndpointsActionsCellProps) => {
|
||||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
|
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
@ -50,12 +50,12 @@ export const IncomingWebhooksActionsCell = ({
|
|||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const id = `incoming-webhook-${incomingWebhookId}-actions`;
|
const id = `signal-endpoint-${signalEndpointId}-actions`;
|
||||||
const menuId = `${id}-menu`;
|
const menuId = `${id}-menu`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledBoxCell>
|
<StyledBoxCell>
|
||||||
<Tooltip title='Incoming webhook actions' arrow describeChild>
|
<Tooltip title='Signal endpoint actions' arrow describeChild>
|
||||||
<IconButton
|
<IconButton
|
||||||
id={id}
|
id={id}
|
||||||
data-loading
|
data-loading
|
||||||
@ -100,7 +100,7 @@ export const IncomingWebhooksActionsCell = ({
|
|||||||
{({ hasAccess }) => (
|
{({ hasAccess }) => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
sx={defaultBorderRadius}
|
sx={defaultBorderRadius}
|
||||||
onClick={onOpenEvents}
|
onClick={onOpenSignals}
|
||||||
disabled={!hasAccess}
|
disabled={!hasAccess}
|
||||||
>
|
>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
@ -108,7 +108,7 @@ export const IncomingWebhooksActionsCell = ({
|
|||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText>
|
<ListItemText>
|
||||||
<Typography variant='body2'>
|
<Typography variant='body2'>
|
||||||
View events
|
View signals
|
||||||
</Typography>
|
</Typography>
|
||||||
</ListItemText>
|
</ListItemText>
|
||||||
</MenuItem>
|
</MenuItem>
|
@ -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<React.SetStateAction<boolean>>;
|
||||||
|
onConfirm: (signalEndpoint: ISignalEndpoint) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SignalEndpointsDeleteDialog = ({
|
||||||
|
signalEndpoint,
|
||||||
|
open,
|
||||||
|
setOpen,
|
||||||
|
onConfirm,
|
||||||
|
}: ISignalEndpointsDeleteDialogProps) => (
|
||||||
|
<Dialogue
|
||||||
|
title='Delete signal endpoint?'
|
||||||
|
open={open}
|
||||||
|
primaryButtonText='Delete signal endpoint'
|
||||||
|
secondaryButtonText='Cancel'
|
||||||
|
onClick={() => onConfirm(signalEndpoint!)}
|
||||||
|
onClose={() => {
|
||||||
|
setOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
You are about to delete signal endpoint:{' '}
|
||||||
|
<strong>{signalEndpoint?.name}</strong>
|
||||||
|
</p>
|
||||||
|
</Dialogue>
|
||||||
|
);
|
@ -3,65 +3,55 @@ import { TablePlaceholder, VirtualizedTable } from 'component/common/Table';
|
|||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import useToast from 'hooks/useToast';
|
import useToast from 'hooks/useToast';
|
||||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
import { useMediaQuery } from '@mui/material';
|
import { Button, useMediaQuery } from '@mui/material';
|
||||||
import { useFlexLayout, useSortBy, useTable } from 'react-table';
|
import { useFlexLayout, useSortBy, useTable } from 'react-table';
|
||||||
import { sortTypes } from 'utils/sortTypes';
|
import { sortTypes } from 'utils/sortTypes';
|
||||||
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
||||||
import { DateCell } from 'component/common/Table/cells/DateCell/DateCell';
|
import { DateCell } from 'component/common/Table/cells/DateCell/DateCell';
|
||||||
import theme from 'themes/theme';
|
import theme from 'themes/theme';
|
||||||
import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns';
|
import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns';
|
||||||
import { useIncomingWebhooks } from 'hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks';
|
import { useSignalEndpoints } from 'hooks/api/getters/useSignalEndpoints/useSignalEndpoints';
|
||||||
import { useIncomingWebhooksApi } from 'hooks/api/actions/useIncomingWebhooksApi/useIncomingWebhooksApi';
|
import { useSignalEndpointsApi } from 'hooks/api/actions/useSignalEndpointsApi/useSignalEndpointsApi';
|
||||||
import { IIncomingWebhook } from 'interfaces/incomingWebhook';
|
import { ISignalEndpoint } from 'interfaces/signal';
|
||||||
import { IncomingWebhooksActionsCell } from './IncomingWebhooksActionsCell';
|
import { SignalEndpointsActionsCell } from './SignalEndpointsActionsCell';
|
||||||
import { IncomingWebhooksDeleteDialog } from './IncomingWebhooksDeleteDialog';
|
import { SignalEndpointsDeleteDialog } from './SignalEndpointsDeleteDialog';
|
||||||
import { ToggleCell } from 'component/common/Table/cells/ToggleCell/ToggleCell';
|
import { ToggleCell } from 'component/common/Table/cells/ToggleCell/ToggleCell';
|
||||||
import copy from 'copy-to-clipboard';
|
import copy from 'copy-to-clipboard';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
import { IncomingWebhookTokensCell } from './IncomingWebhooksTokensCell';
|
import { SignalEndpointsTokensCell } from './SignalEndpointsTokensCell';
|
||||||
import { IncomingWebhooksModal } from '../IncomingWebhooksModal/IncomingWebhooksModal';
|
import { SignalEndpointsModal } from '../SignalEndpointsModal/SignalEndpointsModal';
|
||||||
import { IncomingWebhooksTokensDialog } from '../IncomingWebhooksModal/IncomingWebhooksForm/IncomingWebhooksTokens/IncomingWebhooksTokensDialog';
|
import { SignalEndpointsTokensDialog } from '../SignalEndpointsModal/SignalEndpointsForm/SignalEndpointsTokens/SignalEndpointsTokensDialog';
|
||||||
import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
|
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 {
|
export const SignalEndpointsTable = () => {
|
||||||
modalOpen: boolean;
|
|
||||||
setModalOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
|
||||||
selectedIncomingWebhook?: IIncomingWebhook;
|
|
||||||
setSelectedIncomingWebhook: React.Dispatch<
|
|
||||||
React.SetStateAction<IIncomingWebhook | undefined>
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const IncomingWebhooksTable = ({
|
|
||||||
modalOpen,
|
|
||||||
setModalOpen,
|
|
||||||
selectedIncomingWebhook,
|
|
||||||
setSelectedIncomingWebhook,
|
|
||||||
}: IIncomingWebhooksTableProps) => {
|
|
||||||
const { setToastData, setToastApiError } = useToast();
|
const { setToastData, setToastApiError } = useToast();
|
||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
|
|
||||||
const { incomingWebhooks, refetch } = useIncomingWebhooks();
|
const { signalEndpoints, refetch } = useSignalEndpoints();
|
||||||
const { toggleIncomingWebhook, removeIncomingWebhook } =
|
const { toggleSignalEndpoint, removeSignalEndpoint } =
|
||||||
useIncomingWebhooksApi();
|
useSignalEndpointsApi();
|
||||||
|
|
||||||
|
const [selectedSignalEndpoint, setSelectedSignalEndpoint] =
|
||||||
|
useState<ISignalEndpoint>();
|
||||||
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
|
|
||||||
const [tokenDialog, setTokenDialog] = useState(false);
|
const [tokenDialog, setTokenDialog] = useState(false);
|
||||||
const [newToken, setNewToken] = useState('');
|
const [newToken, setNewToken] = useState('');
|
||||||
const [deleteOpen, setDeleteOpen] = useState(false);
|
const [deleteOpen, setDeleteOpen] = useState(false);
|
||||||
|
|
||||||
const [eventsModalOpen, setEventsModalOpen] = useState(false);
|
const [signalsModalOpen, setSignalsModalOpen] = useState(false);
|
||||||
|
|
||||||
const onToggleIncomingWebhook = async (
|
const onToggleSignalEndpoint = async (
|
||||||
incomingWebhook: IIncomingWebhook,
|
{ id, name }: ISignalEndpoint,
|
||||||
enabled: boolean,
|
enabled: boolean,
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
await toggleIncomingWebhook(incomingWebhook.id, enabled);
|
await toggleSignalEndpoint(id, enabled);
|
||||||
setToastData({
|
setToastData({
|
||||||
title: `"${incomingWebhook.name}" has been ${
|
title: `"${name}" has been ${enabled ? 'enabled' : 'disabled'}`,
|
||||||
enabled ? 'enabled' : 'disabled'
|
|
||||||
}`,
|
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
refetch();
|
refetch();
|
||||||
@ -70,11 +60,11 @@ export const IncomingWebhooksTable = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDeleteConfirm = async (incomingWebhook: IIncomingWebhook) => {
|
const onDeleteConfirm = async ({ id, name }: ISignalEndpoint) => {
|
||||||
try {
|
try {
|
||||||
await removeIncomingWebhook(incomingWebhook.id);
|
await removeSignalEndpoint(id);
|
||||||
setToastData({
|
setToastData({
|
||||||
title: `"${incomingWebhook.name}" has been deleted`,
|
title: `"${name}" has been deleted`,
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
refetch();
|
refetch();
|
||||||
@ -92,42 +82,42 @@ export const IncomingWebhooksTable = ({
|
|||||||
Header: 'Name',
|
Header: 'Name',
|
||||||
accessor: 'name',
|
accessor: 'name',
|
||||||
Cell: ({
|
Cell: ({
|
||||||
row: { original: incomingWebhook },
|
row: { original: signalEndpoint },
|
||||||
}: { row: { original: IIncomingWebhook } }) => (
|
}: { row: { original: ISignalEndpoint } }) => (
|
||||||
<LinkCell
|
<LinkCell
|
||||||
title={incomingWebhook.name}
|
title={signalEndpoint.name}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedIncomingWebhook(incomingWebhook);
|
setSelectedSignalEndpoint(signalEndpoint);
|
||||||
setModalOpen(true);
|
setModalOpen(true);
|
||||||
}}
|
}}
|
||||||
subtitle={incomingWebhook.description}
|
subtitle={signalEndpoint.description}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
width: 240,
|
width: 240,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'URL',
|
Header: 'URL',
|
||||||
accessor: (row: IIncomingWebhook) =>
|
accessor: (row: ISignalEndpoint) =>
|
||||||
`${uiConfig.unleashUrl}/api/incoming-webhook/${row.name}`,
|
`${uiConfig.unleashUrl}/api/signal-endpoint/${row.name}`,
|
||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'tokens',
|
id: 'tokens',
|
||||||
Header: 'Tokens',
|
Header: 'Tokens',
|
||||||
accessor: (row: IIncomingWebhook) =>
|
accessor: (row: ISignalEndpoint) =>
|
||||||
row.tokens?.map(({ name }) => name).join('\n') || '',
|
row.tokens?.map(({ name }) => name).join('\n') || '',
|
||||||
Cell: ({
|
Cell: ({
|
||||||
row: { original: incomingWebhook },
|
row: { original: signalEndpoint },
|
||||||
value,
|
value,
|
||||||
}: {
|
}: {
|
||||||
row: { original: IIncomingWebhook };
|
row: { original: ISignalEndpoint };
|
||||||
value: string;
|
value: string;
|
||||||
}) => (
|
}) => (
|
||||||
<IncomingWebhookTokensCell
|
<SignalEndpointsTokensCell
|
||||||
incomingWebhook={incomingWebhook}
|
signalEndpoint={signalEndpoint}
|
||||||
value={value}
|
value={value}
|
||||||
onCreateToken={() => {
|
onCreateToken={() => {
|
||||||
setSelectedIncomingWebhook(incomingWebhook);
|
setSelectedSignalEndpoint(signalEndpoint);
|
||||||
setModalOpen(true);
|
setModalOpen(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -146,12 +136,12 @@ export const IncomingWebhooksTable = ({
|
|||||||
Header: 'Enabled',
|
Header: 'Enabled',
|
||||||
accessor: 'enabled',
|
accessor: 'enabled',
|
||||||
Cell: ({
|
Cell: ({
|
||||||
row: { original: incomingWebhook },
|
row: { original: signalEndpoint },
|
||||||
}: { row: { original: IIncomingWebhook } }) => (
|
}: { row: { original: ISignalEndpoint } }) => (
|
||||||
<ToggleCell
|
<ToggleCell
|
||||||
checked={incomingWebhook.enabled}
|
checked={signalEndpoint.enabled}
|
||||||
setChecked={(enabled) =>
|
setChecked={(enabled) =>
|
||||||
onToggleIncomingWebhook(incomingWebhook, enabled)
|
onToggleSignalEndpoint(signalEndpoint, enabled)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@ -164,29 +154,29 @@ export const IncomingWebhooksTable = ({
|
|||||||
id: 'Actions',
|
id: 'Actions',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
Cell: ({
|
Cell: ({
|
||||||
row: { original: incomingWebhook },
|
row: { original: signalEndpoint },
|
||||||
}: { row: { original: IIncomingWebhook } }) => (
|
}: { row: { original: ISignalEndpoint } }) => (
|
||||||
<IncomingWebhooksActionsCell
|
<SignalEndpointsActionsCell
|
||||||
incomingWebhookId={incomingWebhook.id}
|
signalEndpointId={signalEndpoint.id}
|
||||||
onCopyToClipboard={() => {
|
onCopyToClipboard={() => {
|
||||||
copy(
|
copy(
|
||||||
`${uiConfig.unleashUrl}/api/incoming-webhook/${incomingWebhook.name}`,
|
`${uiConfig.unleashUrl}/api/signal-endpoint/${signalEndpoint.name}`,
|
||||||
);
|
);
|
||||||
setToastData({
|
setToastData({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
title: 'Copied to clipboard',
|
title: 'Copied to clipboard',
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
onOpenEvents={() => {
|
onOpenSignals={() => {
|
||||||
setSelectedIncomingWebhook(incomingWebhook);
|
setSelectedSignalEndpoint(signalEndpoint);
|
||||||
setEventsModalOpen(true);
|
setSignalsModalOpen(true);
|
||||||
}}
|
}}
|
||||||
onEdit={() => {
|
onEdit={() => {
|
||||||
setSelectedIncomingWebhook(incomingWebhook);
|
setSelectedSignalEndpoint(signalEndpoint);
|
||||||
setModalOpen(true);
|
setModalOpen(true);
|
||||||
}}
|
}}
|
||||||
onDelete={() => {
|
onDelete={() => {
|
||||||
setSelectedIncomingWebhook(incomingWebhook);
|
setSelectedSignalEndpoint(signalEndpoint);
|
||||||
setDeleteOpen(true);
|
setDeleteOpen(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -205,7 +195,7 @@ export const IncomingWebhooksTable = ({
|
|||||||
const { headerGroups, rows, prepareRow, setHiddenColumns } = useTable(
|
const { headerGroups, rows, prepareRow, setHiddenColumns } = useTable(
|
||||||
{
|
{
|
||||||
columns: columns as any,
|
columns: columns as any,
|
||||||
data: incomingWebhooks,
|
data: signalEndpoints,
|
||||||
initialState,
|
initialState,
|
||||||
sortTypes,
|
sortTypes,
|
||||||
autoResetHiddenColumns: false,
|
autoResetHiddenColumns: false,
|
||||||
@ -232,7 +222,25 @@ export const IncomingWebhooksTable = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<PageContent
|
||||||
|
header={
|
||||||
|
<PageHeader
|
||||||
|
title={`Signal endpoints (${signalEndpoints.length})`}
|
||||||
|
actions={
|
||||||
|
<Button
|
||||||
|
variant='contained'
|
||||||
|
color='primary'
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedSignalEndpoint(undefined);
|
||||||
|
setModalOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
New signal endpoint
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
<VirtualizedTable
|
<VirtualizedTable
|
||||||
rows={rows}
|
rows={rows}
|
||||||
headerGroups={headerGroups}
|
headerGroups={headerGroups}
|
||||||
@ -242,44 +250,44 @@ export const IncomingWebhooksTable = ({
|
|||||||
condition={rows.length === 0}
|
condition={rows.length === 0}
|
||||||
show={
|
show={
|
||||||
<TablePlaceholder>
|
<TablePlaceholder>
|
||||||
No incoming webhooks available. Get started by adding
|
No signal endpoints available. Get started by adding
|
||||||
one.
|
one.
|
||||||
</TablePlaceholder>
|
</TablePlaceholder>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<IncomingWebhooksModal
|
<SignalEndpointsModal
|
||||||
incomingWebhook={selectedIncomingWebhook}
|
signalEndpoint={selectedSignalEndpoint}
|
||||||
open={modalOpen}
|
open={modalOpen}
|
||||||
setOpen={setModalOpen}
|
setOpen={setModalOpen}
|
||||||
newToken={(token: string) => {
|
newToken={(token: string) => {
|
||||||
setNewToken(token);
|
setNewToken(token);
|
||||||
setTokenDialog(true);
|
setTokenDialog(true);
|
||||||
}}
|
}}
|
||||||
onOpenEvents={() => {
|
onOpenSignals={() => {
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
setEventsModalOpen(true);
|
setSignalsModalOpen(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<IncomingWebhooksEventsModal
|
<SignalEndpointsSignalsModal
|
||||||
incomingWebhook={selectedIncomingWebhook}
|
signalEndpoint={selectedSignalEndpoint}
|
||||||
open={eventsModalOpen}
|
open={signalsModalOpen}
|
||||||
setOpen={setEventsModalOpen}
|
setOpen={setSignalsModalOpen}
|
||||||
onOpenConfiguration={() => {
|
onOpenConfiguration={() => {
|
||||||
setEventsModalOpen(false);
|
setSignalsModalOpen(false);
|
||||||
setModalOpen(true);
|
setModalOpen(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<IncomingWebhooksTokensDialog
|
<SignalEndpointsTokensDialog
|
||||||
open={tokenDialog}
|
open={tokenDialog}
|
||||||
setOpen={setTokenDialog}
|
setOpen={setTokenDialog}
|
||||||
token={newToken}
|
token={newToken}
|
||||||
/>
|
/>
|
||||||
<IncomingWebhooksDeleteDialog
|
<SignalEndpointsDeleteDialog
|
||||||
incomingWebhook={selectedIncomingWebhook}
|
signalEndpoint={selectedSignalEndpoint}
|
||||||
open={deleteOpen}
|
open={deleteOpen}
|
||||||
setOpen={setDeleteOpen}
|
setOpen={setDeleteOpen}
|
||||||
onConfirm={onDeleteConfirm}
|
onConfirm={onDeleteConfirm}
|
||||||
/>
|
/>
|
||||||
</>
|
</PageContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
@ -2,7 +2,7 @@ import { styled, Typography } from '@mui/material';
|
|||||||
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
||||||
import { Highlighter } from 'component/common/Highlighter/Highlighter';
|
import { Highlighter } from 'component/common/Highlighter/Highlighter';
|
||||||
import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
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 { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
|
||||||
import { TooltipLink } from 'component/common/TooltipLink/TooltipLink';
|
import { TooltipLink } from 'component/common/TooltipLink/TooltipLink';
|
||||||
|
|
||||||
@ -10,20 +10,20 @@ const StyledItem = styled(Typography)(({ theme }) => ({
|
|||||||
fontSize: theme.fontSizes.smallerBody,
|
fontSize: theme.fontSizes.smallerBody,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
interface IIncomingWebhookTokensCellProps {
|
interface ISignalEndpointsTokensCellProps {
|
||||||
incomingWebhook: IIncomingWebhook;
|
signalEndpoint: ISignalEndpoint;
|
||||||
value: string;
|
value: string;
|
||||||
onCreateToken?: () => void;
|
onCreateToken?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IncomingWebhookTokensCell = ({
|
export const SignalEndpointsTokensCell = ({
|
||||||
incomingWebhook,
|
signalEndpoint: { tokens },
|
||||||
value,
|
value,
|
||||||
onCreateToken,
|
onCreateToken,
|
||||||
}: IIncomingWebhookTokensCellProps) => {
|
}: ISignalEndpointsTokensCellProps) => {
|
||||||
const { searchQuery } = useSearchHighlightContext();
|
const { searchQuery } = useSearchHighlightContext();
|
||||||
|
|
||||||
if (!incomingWebhook.tokens || incomingWebhook.tokens.length === 0) {
|
if (!tokens || tokens.length === 0) {
|
||||||
if (!onCreateToken) return <TextCell>0 tokens</TextCell>;
|
if (!onCreateToken) return <TextCell>0 tokens</TextCell>;
|
||||||
else return <LinkCell title='Create token' onClick={onCreateToken} />;
|
else return <LinkCell title='Create token' onClick={onCreateToken} />;
|
||||||
}
|
}
|
||||||
@ -33,7 +33,7 @@ export const IncomingWebhookTokensCell = ({
|
|||||||
<TooltipLink
|
<TooltipLink
|
||||||
tooltip={
|
tooltip={
|
||||||
<>
|
<>
|
||||||
{incomingWebhook.tokens?.map(({ id, name }) => (
|
{tokens?.map(({ id, name }) => (
|
||||||
<StyledItem key={id}>
|
<StyledItem key={id}>
|
||||||
<Highlighter search={searchQuery}>
|
<Highlighter search={searchQuery}>
|
||||||
{name}
|
{name}
|
||||||
@ -47,9 +47,7 @@ export const IncomingWebhookTokensCell = ({
|
|||||||
value.toLowerCase().includes(searchQuery.toLowerCase())
|
value.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{incomingWebhook.tokens?.length === 1
|
{tokens?.length === 1 ? '1 token' : `${tokens?.length} tokens`}
|
||||||
? '1 token'
|
|
||||||
: `${incomingWebhook.tokens?.length} tokens`}
|
|
||||||
</TooltipLink>
|
</TooltipLink>
|
||||||
</TextCell>
|
</TextCell>
|
||||||
);
|
);
|
21
frontend/src/component/signals/Signals.tsx
Normal file
21
frontend/src/component/signals/Signals.tsx
Normal file
@ -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 <PremiumFeature feature='signals' />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<PermissionGuard permissions={ADMIN}>
|
||||||
|
<SignalEndpointsTable />
|
||||||
|
</PermissionGuard>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -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<IncomingWebhookTokenWithTokenSecret> => {
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
};
|
|
@ -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,
|
|
||||||
};
|
|
||||||
};
|
|
@ -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<SignalEndpointTokenWithTokenSecret> => {
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
};
|
@ -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,
|
||||||
|
};
|
||||||
|
};
|
@ -5,63 +5,62 @@ import useSWRInfinite, {
|
|||||||
import { formatApiPath } from 'utils/formatPath';
|
import { formatApiPath } from 'utils/formatPath';
|
||||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||||
import useUiConfig from '../useUiConfig/useUiConfig';
|
import useUiConfig from '../useUiConfig/useUiConfig';
|
||||||
import { IIncomingWebhookEvent } from 'interfaces/incomingWebhook';
|
import { ISignalEndpointSignal } from 'interfaces/signal';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
|
|
||||||
const ENDPOINT = 'api/admin/incoming-webhooks';
|
const ENDPOINT = 'api/admin/signal-endpoints';
|
||||||
|
|
||||||
type IncomingWebhookEventsResponse = {
|
type SignalsResponse = {
|
||||||
incomingWebhookEvents: IIncomingWebhookEvent[];
|
signalEndpointSignals: ISignalEndpointSignal[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetcher = async (url: string) => {
|
const fetcher = async (url: string) => {
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
await handleErrorResponses('Incoming webhook events')(response);
|
await handleErrorResponses('Signals')(response);
|
||||||
return response.json();
|
return response.json();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useIncomingWebhookEvents = (
|
export const useSignalEndpointSignals = (
|
||||||
incomingWebhookId?: number,
|
signalEndpointId?: number,
|
||||||
limit = 50,
|
limit = 50,
|
||||||
options: SWRInfiniteConfiguration = {},
|
options: SWRInfiniteConfiguration = {},
|
||||||
) => {
|
) => {
|
||||||
const { isEnterprise } = useUiConfig();
|
const { isEnterprise } = useUiConfig();
|
||||||
const incomingWebhooksEnabled = useUiFlag('incomingWebhooks');
|
const signalsEnabled = useUiFlag('signals');
|
||||||
|
|
||||||
const getKey: SWRInfiniteKeyLoader = (
|
const getKey: SWRInfiniteKeyLoader = (
|
||||||
pageIndex: number,
|
pageIndex: number,
|
||||||
previousPageData: IncomingWebhookEventsResponse,
|
previousPageData: SignalsResponse,
|
||||||
) => {
|
) => {
|
||||||
// Does not meet conditions
|
// Does not meet conditions
|
||||||
if (!incomingWebhookId || !isEnterprise || !incomingWebhooksEnabled)
|
if (!signalEndpointId || !isEnterprise || !signalsEnabled) return null;
|
||||||
return null;
|
|
||||||
|
|
||||||
// Reached the end
|
// Reached the end
|
||||||
if (previousPageData && !previousPageData.incomingWebhookEvents.length)
|
if (previousPageData && !previousPageData.signalEndpointSignals.length)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return formatApiPath(
|
return formatApiPath(
|
||||||
`${ENDPOINT}/${incomingWebhookId}/events?limit=${limit}&offset=${
|
`${ENDPOINT}/${signalEndpointId}/signals?limit=${limit}&offset=${
|
||||||
pageIndex * limit
|
pageIndex * limit
|
||||||
}`,
|
}`,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { data, error, size, setSize, mutate } =
|
const { data, error, size, setSize, mutate } =
|
||||||
useSWRInfinite<IncomingWebhookEventsResponse>(getKey, fetcher, {
|
useSWRInfinite<SignalsResponse>(getKey, fetcher, {
|
||||||
...options,
|
...options,
|
||||||
revalidateAll: true,
|
revalidateAll: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const incomingWebhookEvents = data
|
const signalEndpointSignals = data
|
||||||
? data.flatMap(({ incomingWebhookEvents }) => incomingWebhookEvents)
|
? data.flatMap(({ signalEndpointSignals }) => signalEndpointSignals)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const isLoadingInitialData = !data && !error;
|
const isLoadingInitialData = !data && !error;
|
||||||
const isLoadingMore = size > 0 && !data?.[size - 1];
|
const isLoadingMore = size > 0 && !data?.[size - 1];
|
||||||
const loading = isLoadingInitialData || isLoadingMore;
|
const loading = isLoadingInitialData || isLoadingMore;
|
||||||
|
|
||||||
const hasMore = data?.[size - 1]?.incomingWebhookEvents.length === limit;
|
const hasMore = data?.[size - 1]?.signalEndpointSignals.length === limit;
|
||||||
|
|
||||||
const loadMore = () => {
|
const loadMore = () => {
|
||||||
if (loading || !hasMore) return;
|
if (loading || !hasMore) return;
|
||||||
@ -69,7 +68,7 @@ export const useIncomingWebhookEvents = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
incomingWebhookEvents,
|
signalEndpointSignals,
|
||||||
hasMore,
|
hasMore,
|
||||||
loadMore,
|
loadMore,
|
||||||
loading,
|
loading,
|
@ -3,26 +3,31 @@ import { formatApiPath } from 'utils/formatPath';
|
|||||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||||
import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR';
|
import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR';
|
||||||
import useUiConfig from '../useUiConfig/useUiConfig';
|
import useUiConfig from '../useUiConfig/useUiConfig';
|
||||||
import { IIncomingWebhookToken } from 'interfaces/incomingWebhook';
|
import { ISignalEndpointToken } from 'interfaces/signal';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
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 { isEnterprise } = useUiConfig();
|
||||||
const incomingWebhooksEnabled = useUiFlag('incomingWebhooks');
|
const signalsEnabled = useUiFlag('signals');
|
||||||
|
|
||||||
const { data, error, mutate } = useConditionalSWR(
|
const { data, error, mutate } = useConditionalSWR<{
|
||||||
isEnterprise() && incomingWebhooksEnabled,
|
signalEndpointTokens: ISignalEndpointToken[];
|
||||||
{ incomingWebhookTokens: [] },
|
}>(
|
||||||
formatApiPath(`${ENDPOINT}/${incomingWebhookId}/tokens`),
|
isEnterprise() && signalsEnabled,
|
||||||
|
DEFAULT_DATA,
|
||||||
|
formatApiPath(`${ENDPOINT}/${signalEndpointId}/tokens`),
|
||||||
fetcher,
|
fetcher,
|
||||||
);
|
);
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
incomingWebhookTokens: (data?.incomingWebhookTokens ??
|
signalEndpointTokens: data?.signalEndpointTokens ?? [],
|
||||||
[]) as IIncomingWebhookToken[],
|
|
||||||
loading: !error && !data,
|
loading: !error && !data,
|
||||||
refetch: () => mutate(),
|
refetch: () => mutate(),
|
||||||
error,
|
error,
|
||||||
@ -33,6 +38,6 @@ export const useIncomingWebhookTokens = (incomingWebhookId: number) => {
|
|||||||
|
|
||||||
const fetcher = (path: string) => {
|
const fetcher = (path: string) => {
|
||||||
return fetch(path)
|
return fetch(path)
|
||||||
.then(handleErrorResponses('Incoming webhook tokens'))
|
.then(handleErrorResponses('Signal endpoint tokens'))
|
||||||
.then((res) => res.json());
|
.then((res) => res.json());
|
||||||
};
|
};
|
@ -3,23 +3,23 @@ import { formatApiPath } from 'utils/formatPath';
|
|||||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||||
import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR';
|
import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR';
|
||||||
import useUiConfig from '../useUiConfig/useUiConfig';
|
import useUiConfig from '../useUiConfig/useUiConfig';
|
||||||
import { IIncomingWebhook } from 'interfaces/incomingWebhook';
|
import { ISignalEndpoint } from 'interfaces/signal';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
|
|
||||||
const ENDPOINT = 'api/admin/incoming-webhooks';
|
const ENDPOINT = 'api/admin/signal-endpoints';
|
||||||
|
|
||||||
const DEFAULT_DATA = {
|
const DEFAULT_DATA = {
|
||||||
incomingWebhooks: [],
|
signalEndpoints: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useIncomingWebhooks = () => {
|
export const useSignalEndpoints = () => {
|
||||||
const { isEnterprise } = useUiConfig();
|
const { isEnterprise } = useUiConfig();
|
||||||
const incomingWebhooksEnabled = useUiFlag('incomingWebhooks');
|
const signalsEnabled = useUiFlag('signals');
|
||||||
|
|
||||||
const { data, error, mutate } = useConditionalSWR<{
|
const { data, error, mutate } = useConditionalSWR<{
|
||||||
incomingWebhooks: IIncomingWebhook[];
|
signalEndpoints: ISignalEndpoint[];
|
||||||
}>(
|
}>(
|
||||||
isEnterprise() && incomingWebhooksEnabled,
|
isEnterprise() && signalsEnabled,
|
||||||
DEFAULT_DATA,
|
DEFAULT_DATA,
|
||||||
formatApiPath(ENDPOINT),
|
formatApiPath(ENDPOINT),
|
||||||
fetcher,
|
fetcher,
|
||||||
@ -27,7 +27,7 @@ export const useIncomingWebhooks = () => {
|
|||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
incomingWebhooks: data?.incomingWebhooks ?? [],
|
signalEndpoints: data?.signalEndpoints ?? [],
|
||||||
loading: !error && !data,
|
loading: !error && !data,
|
||||||
refetch: () => mutate(),
|
refetch: () => mutate(),
|
||||||
error,
|
error,
|
||||||
@ -38,6 +38,6 @@ export const useIncomingWebhooks = () => {
|
|||||||
|
|
||||||
const fetcher = (path: string) => {
|
const fetcher = (path: string) => {
|
||||||
return fetch(path)
|
return fetch(path)
|
||||||
.then(handleErrorResponses('Incoming webhooks'))
|
.then(handleErrorResponses('Signal endpoints'))
|
||||||
.then((res) => res.json());
|
.then((res) => res.json());
|
||||||
};
|
};
|
@ -1,5 +1,10 @@
|
|||||||
|
import { ISignal, SignalSource } from './signal';
|
||||||
import { IConstraint } from './strategy';
|
import { IConstraint } from './strategy';
|
||||||
|
|
||||||
|
type ActionSetState = 'started' | 'success' | 'failed';
|
||||||
|
|
||||||
|
type ActionState = ActionSetState | 'not started';
|
||||||
|
|
||||||
export interface IActionSet {
|
export interface IActionSet {
|
||||||
id: number;
|
id: number;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
@ -12,15 +17,13 @@ export interface IActionSet {
|
|||||||
createdByUserId: number;
|
createdByUserId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
type MatchSource = 'incoming-webhook';
|
|
||||||
|
|
||||||
export type ParameterMatch = Pick<
|
export type ParameterMatch = Pick<
|
||||||
IConstraint,
|
IConstraint,
|
||||||
'inverted' | 'operator' | 'caseInsensitive' | 'value' | 'values'
|
'inverted' | 'operator' | 'caseInsensitive' | 'value' | 'values'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export interface IMatch {
|
export interface IMatch {
|
||||||
source: MatchSource;
|
source: SignalSource;
|
||||||
sourceId: number;
|
sourceId: number;
|
||||||
payload: Record<string, ParameterMatch>;
|
payload: Record<string, ParameterMatch>;
|
||||||
}
|
}
|
||||||
@ -34,21 +37,6 @@ export interface IAction {
|
|||||||
createdByUserId: number;
|
createdByUserId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ObservableEventSource = 'incoming-webhook';
|
|
||||||
|
|
||||||
export interface IObservableEvent {
|
|
||||||
id: number;
|
|
||||||
source: ObservableEventSource;
|
|
||||||
sourceId: number;
|
|
||||||
createdAt: string;
|
|
||||||
createdByIncomingWebhookTokenId: number;
|
|
||||||
payload: Record<string, unknown>;
|
|
||||||
}
|
|
||||||
|
|
||||||
type ActionSetState = 'started' | 'success' | 'failed';
|
|
||||||
|
|
||||||
type ActionState = ActionSetState | 'not started';
|
|
||||||
|
|
||||||
export interface IActionEvent extends IAction {
|
export interface IActionEvent extends IAction {
|
||||||
state: ActionState;
|
state: ActionState;
|
||||||
details?: string;
|
details?: string;
|
||||||
@ -61,9 +49,9 @@ interface IActionSetEventActionSet extends IActionSet {
|
|||||||
export interface IActionSetEvent {
|
export interface IActionSetEvent {
|
||||||
id: number;
|
id: number;
|
||||||
actionSetId: number;
|
actionSetId: number;
|
||||||
observableEventId: number;
|
signalId: number;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
state: ActionSetState;
|
state: ActionSetState;
|
||||||
observableEvent: IObservableEvent;
|
signal: ISignal;
|
||||||
actionSet: IActionSetEventActionSet;
|
actionSet: IActionSetEventActionSet;
|
||||||
}
|
}
|
||||||
|
@ -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<string, unknown>;
|
|
||||||
createdAt: string;
|
|
||||||
source: ObservableEventSource;
|
|
||||||
sourceId: number;
|
|
||||||
tokenName: string;
|
|
||||||
}
|
|
33
frontend/src/interfaces/signal.ts
Normal file
33
frontend/src/interfaces/signal.ts
Normal file
@ -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<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISignalEndpointSignal
|
||||||
|
extends Omit<ISignal, 'createdBySourceTokenId'> {
|
||||||
|
tokenName: string;
|
||||||
|
}
|
@ -63,7 +63,7 @@ export type UiFlags = {
|
|||||||
doraMetrics?: boolean;
|
doraMetrics?: boolean;
|
||||||
dependentFeatures?: boolean;
|
dependentFeatures?: boolean;
|
||||||
newStrategyConfiguration?: boolean;
|
newStrategyConfiguration?: boolean;
|
||||||
incomingWebhooks?: boolean;
|
signals?: boolean;
|
||||||
automatedActions?: boolean;
|
automatedActions?: boolean;
|
||||||
celebrateUnleash?: boolean;
|
celebrateUnleash?: boolean;
|
||||||
increaseUnleashWidth?: boolean;
|
increaseUnleashWidth?: boolean;
|
||||||
|
@ -117,7 +117,6 @@ exports[`should create default config 1`] = `
|
|||||||
"filterInvalidClientMetrics": false,
|
"filterInvalidClientMetrics": false,
|
||||||
"googleAuthEnabled": false,
|
"googleAuthEnabled": false,
|
||||||
"inMemoryScheduledChangeRequests": false,
|
"inMemoryScheduledChangeRequests": false,
|
||||||
"incomingWebhooks": false,
|
|
||||||
"increaseUnleashWidth": false,
|
"increaseUnleashWidth": false,
|
||||||
"maintenanceMode": false,
|
"maintenanceMode": false,
|
||||||
"messageBanner": {
|
"messageBanner": {
|
||||||
@ -137,6 +136,7 @@ exports[`should create default config 1`] = `
|
|||||||
"scimApi": false,
|
"scimApi": false,
|
||||||
"sdkReporting": false,
|
"sdkReporting": false,
|
||||||
"showInactiveUsers": false,
|
"showInactiveUsers": false,
|
||||||
|
"signals": false,
|
||||||
"strictSchemaValidation": false,
|
"strictSchemaValidation": false,
|
||||||
"stripClientHeadersOn304": false,
|
"stripClientHeadersOn304": false,
|
||||||
"useMemoizedActiveTokens": false,
|
"useMemoizedActiveTokens": false,
|
||||||
@ -176,7 +176,7 @@ exports[`should create default config 1`] = `
|
|||||||
"prometheusApi": undefined,
|
"prometheusApi": undefined,
|
||||||
"publicFolder": undefined,
|
"publicFolder": undefined,
|
||||||
"rateLimiting": {
|
"rateLimiting": {
|
||||||
"callIncomingWebhookMaxPerSecond": 1,
|
"callSignalEndpointMaxPerSecond": 1,
|
||||||
"createUserMaxPerMinute": 20,
|
"createUserMaxPerMinute": 20,
|
||||||
"passwordResetMaxPerMinute": 1,
|
"passwordResetMaxPerMinute": 1,
|
||||||
"simpleLoginMaxPerMinute": 10,
|
"simpleLoginMaxPerMinute": 10,
|
||||||
|
@ -150,8 +150,8 @@ function loadRateLimitingConfig(options: IUnleashOptions): IRateLimiting {
|
|||||||
process.env.PASSWORD_RESET_LIMIT_PER_MINUTE,
|
process.env.PASSWORD_RESET_LIMIT_PER_MINUTE,
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
const callIncomingWebhookMaxPerSecond = parseEnvVarNumber(
|
const callSignalEndpointMaxPerSecond = parseEnvVarNumber(
|
||||||
process.env.INCOMING_WEBHOOK_RATE_LIMIT_PER_SECOND,
|
process.env.SIGNAL_ENDPOINT_RATE_LIMIT_PER_SECOND,
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ function loadRateLimitingConfig(options: IUnleashOptions): IRateLimiting {
|
|||||||
createUserMaxPerMinute,
|
createUserMaxPerMinute,
|
||||||
simpleLoginMaxPerMinute,
|
simpleLoginMaxPerMinute,
|
||||||
passwordResetMaxPerMinute,
|
passwordResetMaxPerMinute,
|
||||||
callIncomingWebhookMaxPerSecond,
|
callSignalEndpointMaxPerSecond,
|
||||||
};
|
};
|
||||||
return mergeAll([defaultRateLimitOptions, options.rateLimiting || {}]);
|
return mergeAll([defaultRateLimitOptions, options.rateLimiting || {}]);
|
||||||
}
|
}
|
||||||
|
@ -372,12 +372,11 @@ export default class MetricsMonitor {
|
|||||||
.set(config.rateLimiting.passwordResetMaxPerMinute);
|
.set(config.rateLimiting.passwordResetMaxPerMinute);
|
||||||
rateLimits
|
rateLimits
|
||||||
.labels({
|
.labels({
|
||||||
endpoint: '/api/incoming-webhook/:name',
|
endpoint: '/api/signal-endpoint/:name',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
})
|
})
|
||||||
.set(
|
.set(
|
||||||
config.rateLimiting.callIncomingWebhookMaxPerSecond *
|
config.rateLimiting.callSignalEndpointMaxPerSecond * 60,
|
||||||
60,
|
|
||||||
);
|
);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
@ -176,16 +176,16 @@ export const BANNER_CREATED = 'banner-created' as const;
|
|||||||
export const BANNER_UPDATED = 'banner-updated' as const;
|
export const BANNER_UPDATED = 'banner-updated' as const;
|
||||||
export const BANNER_DELETED = 'banner-deleted' as const;
|
export const BANNER_DELETED = 'banner-deleted' as const;
|
||||||
|
|
||||||
export const INCOMING_WEBHOOK_CREATED = 'incoming-webhook-created' as const;
|
export const SIGNAL_ENDPOINT_CREATED = 'signal-endpoint-created' as const;
|
||||||
export const INCOMING_WEBHOOK_UPDATED = 'incoming-webhook-updated' as const;
|
export const SIGNAL_ENDPOINT_UPDATED = 'signal-endpoint-updated' as const;
|
||||||
export const INCOMING_WEBHOOK_DELETED = 'incoming-webhook-deleted' as const;
|
export const SIGNAL_ENDPOINT_DELETED = 'signal-endpoint-deleted' as const;
|
||||||
|
|
||||||
export const INCOMING_WEBHOOK_TOKEN_CREATED =
|
export const SIGNAL_ENDPOINT_TOKEN_CREATED =
|
||||||
'incoming-webhook-token-created' as const;
|
'signal-endpoint-token-created' as const;
|
||||||
export const INCOMING_WEBHOOK_TOKEN_UPDATED =
|
export const SIGNAL_ENDPOINT_TOKEN_UPDATED =
|
||||||
'incoming-webhook-token-updated' as const;
|
'signal-endpoint-token-updated' as const;
|
||||||
export const INCOMING_WEBHOOK_TOKEN_DELETED =
|
export const SIGNAL_ENDPOINT_TOKEN_DELETED =
|
||||||
'incoming-webhook-token-deleted' as const;
|
'signal-endpoint-token-deleted' as const;
|
||||||
|
|
||||||
export const ACTIONS_CREATED = 'actions-created' as const;
|
export const ACTIONS_CREATED = 'actions-created' as const;
|
||||||
export const ACTIONS_UPDATED = 'actions-updated' as const;
|
export const ACTIONS_UPDATED = 'actions-updated' as const;
|
||||||
@ -325,12 +325,12 @@ export const IEventTypes = [
|
|||||||
PROJECT_ENVIRONMENT_REMOVED,
|
PROJECT_ENVIRONMENT_REMOVED,
|
||||||
DEFAULT_STRATEGY_UPDATED,
|
DEFAULT_STRATEGY_UPDATED,
|
||||||
SEGMENT_IMPORT,
|
SEGMENT_IMPORT,
|
||||||
INCOMING_WEBHOOK_CREATED,
|
SIGNAL_ENDPOINT_CREATED,
|
||||||
INCOMING_WEBHOOK_UPDATED,
|
SIGNAL_ENDPOINT_UPDATED,
|
||||||
INCOMING_WEBHOOK_DELETED,
|
SIGNAL_ENDPOINT_DELETED,
|
||||||
INCOMING_WEBHOOK_TOKEN_CREATED,
|
SIGNAL_ENDPOINT_TOKEN_CREATED,
|
||||||
INCOMING_WEBHOOK_TOKEN_UPDATED,
|
SIGNAL_ENDPOINT_TOKEN_UPDATED,
|
||||||
INCOMING_WEBHOOK_TOKEN_DELETED,
|
SIGNAL_ENDPOINT_TOKEN_DELETED,
|
||||||
ACTIONS_CREATED,
|
ACTIONS_CREATED,
|
||||||
ACTIONS_UPDATED,
|
ACTIONS_UPDATED,
|
||||||
ACTIONS_DELETED,
|
ACTIONS_DELETED,
|
||||||
|
@ -28,7 +28,7 @@ export type IFlagKey =
|
|||||||
| 'disableMetrics'
|
| 'disableMetrics'
|
||||||
| 'stripClientHeadersOn304'
|
| 'stripClientHeadersOn304'
|
||||||
| 'stripHeadersOnAPI'
|
| 'stripHeadersOnAPI'
|
||||||
| 'incomingWebhooks'
|
| 'signals'
|
||||||
| 'automatedActions'
|
| 'automatedActions'
|
||||||
| 'celebrateUnleash'
|
| 'celebrateUnleash'
|
||||||
| 'increaseUnleashWidth'
|
| 'increaseUnleashWidth'
|
||||||
@ -135,8 +135,8 @@ const flags: IFlags = {
|
|||||||
.UNLEASH_EXPERIMENTAL_DETECT_SEGMENT_USAGE_IN_CHANGE_REQUESTS,
|
.UNLEASH_EXPERIMENTAL_DETECT_SEGMENT_USAGE_IN_CHANGE_REQUESTS,
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
incomingWebhooks: parseEnvVarBoolean(
|
signals: parseEnvVarBoolean(
|
||||||
process.env.UNLEASH_EXPERIMENTAL_INCOMING_WEBHOOKS,
|
process.env.UNLEASH_EXPERIMENTAL_SIGNALS,
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
automatedActions: parseEnvVarBoolean(
|
automatedActions: parseEnvVarBoolean(
|
||||||
|
@ -207,7 +207,7 @@ export interface IRateLimiting {
|
|||||||
createUserMaxPerMinute: number;
|
createUserMaxPerMinute: number;
|
||||||
simpleLoginMaxPerMinute: number;
|
simpleLoginMaxPerMinute: number;
|
||||||
passwordResetMaxPerMinute: number;
|
passwordResetMaxPerMinute: number;
|
||||||
callIncomingWebhookMaxPerSecond: number;
|
callSignalEndpointMaxPerSecond: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUnleashConfig {
|
export interface IUnleashConfig {
|
||||||
|
Loading…
Reference in New Issue
Block a user