Api keys and webhook migration to core (#13011)
TODO: check Zapier trigger records work as expected --------- Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
+220
-89
@@ -5,16 +5,15 @@ import { MemoryRouter } from 'react-router-dom';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { WebhookFormMode } from '@/settings/developers/constants/WebhookFormMode';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import { CREATE_WEBHOOK } from '@/settings/developers/graphql/mutations/createWebhook';
|
||||
import { DELETE_WEBHOOK } from '@/settings/developers/graphql/mutations/deleteWebhook';
|
||||
import { UPDATE_WEBHOOK } from '@/settings/developers/graphql/mutations/updateWebhook';
|
||||
import { GET_WEBHOOK } from '@/settings/developers/graphql/queries/getWebhook';
|
||||
import { useWebhookForm } from '../useWebhookForm';
|
||||
|
||||
// Mock dependencies
|
||||
const mockNavigateSettings = jest.fn();
|
||||
const mockEnqueueSuccessSnackBar = jest.fn();
|
||||
const mockEnqueueErrorSnackBar = jest.fn();
|
||||
const mockCreateOneRecord = jest.fn();
|
||||
const mockUpdateOneRecord = jest.fn();
|
||||
const mockDeleteOneRecord = jest.fn();
|
||||
|
||||
jest.mock('~/hooks/useNavigateSettings', () => ({
|
||||
useNavigateSettings: () => mockNavigateSettings,
|
||||
@@ -27,32 +26,108 @@ jest.mock('@/ui/feedback/snack-bar-manager/hooks/useSnackBar', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('@/object-record/hooks/useCreateOneRecord', () => ({
|
||||
useCreateOneRecord: () => ({
|
||||
createOneRecord: mockCreateOneRecord,
|
||||
}),
|
||||
}));
|
||||
const createMockWebhookData = (overrides = {}) => ({
|
||||
id: 'test-webhook-id',
|
||||
targetUrl: 'https://test.com/webhook',
|
||||
operations: ['person.created'],
|
||||
description: 'Test webhook',
|
||||
secret: 'test-secret',
|
||||
...overrides,
|
||||
});
|
||||
|
||||
jest.mock('@/object-record/hooks/useUpdateOneRecord', () => ({
|
||||
useUpdateOneRecord: () => ({
|
||||
updateOneRecord: mockUpdateOneRecord,
|
||||
}),
|
||||
}));
|
||||
const createSuccessfulCreateMock = (webhookData = {}) => ({
|
||||
request: {
|
||||
query: CREATE_WEBHOOK,
|
||||
variables: {
|
||||
input: {
|
||||
targetUrl: 'https://test.com/webhook',
|
||||
operations: ['person.created'],
|
||||
description: 'Test webhook',
|
||||
secret: 'test-secret',
|
||||
...webhookData,
|
||||
},
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
createWebhook: createMockWebhookData(webhookData),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
jest.mock('@/object-record/hooks/useDeleteOneRecord', () => ({
|
||||
useDeleteOneRecord: () => ({
|
||||
deleteOneRecord: mockDeleteOneRecord,
|
||||
}),
|
||||
}));
|
||||
const createSuccessfulUpdateMock = (webhookId: string, webhookData = {}) => ({
|
||||
request: {
|
||||
query: UPDATE_WEBHOOK,
|
||||
variables: {
|
||||
input: {
|
||||
id: webhookId,
|
||||
targetUrl: 'https://updated.com/webhook',
|
||||
operations: ['person.updated'],
|
||||
description: 'Updated webhook',
|
||||
secret: 'updated-secret',
|
||||
...webhookData,
|
||||
},
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
updateWebhook: createMockWebhookData({
|
||||
id: webhookId,
|
||||
targetUrl: 'https://updated.com/webhook',
|
||||
operations: ['person.updated'],
|
||||
description: 'Updated webhook',
|
||||
secret: 'updated-secret',
|
||||
...webhookData,
|
||||
}),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
jest.mock('@/object-record/hooks/useFindOneRecord', () => ({
|
||||
useFindOneRecord: () => ({
|
||||
loading: false,
|
||||
}),
|
||||
}));
|
||||
const createSuccessfulDeleteMock = (webhookId: string) => ({
|
||||
request: {
|
||||
query: DELETE_WEBHOOK,
|
||||
variables: {
|
||||
input: {
|
||||
id: webhookId,
|
||||
},
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
deleteWebhook: {
|
||||
id: webhookId,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<MockedProvider addTypename={false}>
|
||||
const createGetWebhookMock = (webhookId: string, webhookData = {}) => ({
|
||||
request: {
|
||||
query: GET_WEBHOOK,
|
||||
variables: {
|
||||
input: {
|
||||
id: webhookId,
|
||||
},
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
webhook: createMockWebhookData({
|
||||
id: webhookId,
|
||||
...webhookData,
|
||||
}),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const Wrapper = ({
|
||||
children,
|
||||
mocks = [],
|
||||
}: {
|
||||
children: ReactNode;
|
||||
mocks?: any[];
|
||||
}) => (
|
||||
<MockedProvider mocks={mocks} addTypename={false}>
|
||||
<RecoilRoot>
|
||||
<MemoryRouter>{children}</MemoryRouter>
|
||||
</RecoilRoot>
|
||||
@@ -68,7 +143,7 @@ describe('useWebhookForm', () => {
|
||||
it('should initialize with default values in create mode', () => {
|
||||
const { result } = renderHook(
|
||||
() => useWebhookForm({ mode: WebhookFormMode.Create }),
|
||||
{ wrapper: Wrapper },
|
||||
{ wrapper: ({ children }) => <Wrapper>{children}</Wrapper> },
|
||||
);
|
||||
|
||||
expect(result.current.isCreationMode).toBe(true);
|
||||
@@ -81,15 +156,15 @@ describe('useWebhookForm', () => {
|
||||
});
|
||||
|
||||
it('should handle webhook creation successfully', async () => {
|
||||
const mockCreatedWebhook = {
|
||||
id: 'new-webhook-id',
|
||||
targetUrl: 'https://test.com/webhook',
|
||||
};
|
||||
mockCreateOneRecord.mockResolvedValue(mockCreatedWebhook);
|
||||
const mocks = [createSuccessfulCreateMock()];
|
||||
|
||||
const { result } = renderHook(
|
||||
() => useWebhookForm({ mode: WebhookFormMode.Create }),
|
||||
{ wrapper: Wrapper },
|
||||
{
|
||||
wrapper: ({ children }) => (
|
||||
<Wrapper mocks={mocks}>{children}</Wrapper>
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
const formData = {
|
||||
@@ -103,28 +178,36 @@ describe('useWebhookForm', () => {
|
||||
await result.current.handleSave(formData);
|
||||
});
|
||||
|
||||
expect(mockCreateOneRecord).toHaveBeenCalledWith({
|
||||
id: expect.any(String),
|
||||
targetUrl: 'https://test.com/webhook',
|
||||
description: 'Test webhook',
|
||||
operations: ['person.created'],
|
||||
secret: 'test-secret',
|
||||
});
|
||||
|
||||
expect(mockEnqueueSuccessSnackBar).toHaveBeenCalledWith({
|
||||
message: 'Webhook https://test.com/webhook created successfully',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle creation errors', async () => {
|
||||
const error = new ApolloError({
|
||||
graphQLErrors: [{ message: 'Creation failed' }],
|
||||
});
|
||||
mockCreateOneRecord.mockRejectedValue(error);
|
||||
const errorMock = {
|
||||
request: {
|
||||
query: CREATE_WEBHOOK,
|
||||
variables: {
|
||||
input: {
|
||||
targetUrl: 'https://test.com/webhook',
|
||||
operations: ['person.created'],
|
||||
description: 'Test webhook',
|
||||
secret: 'test-secret',
|
||||
},
|
||||
},
|
||||
},
|
||||
error: new Error('Creation failed'),
|
||||
};
|
||||
|
||||
const mocks = [errorMock];
|
||||
|
||||
const { result } = renderHook(
|
||||
() => useWebhookForm({ mode: WebhookFormMode.Create }),
|
||||
{ wrapper: Wrapper },
|
||||
{
|
||||
wrapper: ({ children }) => (
|
||||
<Wrapper mocks={mocks}>{children}</Wrapper>
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
const formData = {
|
||||
@@ -139,16 +222,24 @@ describe('useWebhookForm', () => {
|
||||
});
|
||||
|
||||
expect(mockEnqueueErrorSnackBar).toHaveBeenCalledWith({
|
||||
apolloError: error,
|
||||
apolloError: expect.any(Error),
|
||||
});
|
||||
});
|
||||
|
||||
it('should clean and format operations correctly', async () => {
|
||||
mockCreateOneRecord.mockResolvedValue({ id: 'test-id' });
|
||||
const mocks = [
|
||||
createSuccessfulCreateMock({
|
||||
operations: ['person.created', 'company.updated'],
|
||||
}),
|
||||
];
|
||||
|
||||
const { result } = renderHook(
|
||||
() => useWebhookForm({ mode: WebhookFormMode.Create }),
|
||||
{ wrapper: Wrapper },
|
||||
{
|
||||
wrapper: ({ children }) => (
|
||||
<Wrapper mocks={mocks}>{children}</Wrapper>
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
const formData = {
|
||||
@@ -167,12 +258,8 @@ describe('useWebhookForm', () => {
|
||||
await result.current.handleSave(formData);
|
||||
});
|
||||
|
||||
expect(mockCreateOneRecord).toHaveBeenCalledWith({
|
||||
id: expect.any(String),
|
||||
targetUrl: 'https://test.com/webhook',
|
||||
description: 'Test webhook',
|
||||
operations: ['person.created', 'company.updated'],
|
||||
secret: 'test-secret',
|
||||
expect(mockEnqueueSuccessSnackBar).toHaveBeenCalledWith({
|
||||
message: 'Webhook https://test.com/webhook created successfully',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -181,20 +268,29 @@ describe('useWebhookForm', () => {
|
||||
const webhookId = 'test-webhook-id';
|
||||
|
||||
it('should initialize correctly in edit mode', () => {
|
||||
const mocks = [createGetWebhookMock(webhookId)];
|
||||
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useWebhookForm({
|
||||
mode: WebhookFormMode.Edit,
|
||||
webhookId,
|
||||
}),
|
||||
{ wrapper: Wrapper },
|
||||
{
|
||||
wrapper: ({ children }) => (
|
||||
<Wrapper mocks={mocks}>{children}</Wrapper>
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.current.isCreationMode).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle webhook update successfully', async () => {
|
||||
mockUpdateOneRecord.mockResolvedValue({});
|
||||
const mocks = [
|
||||
createGetWebhookMock(webhookId),
|
||||
createSuccessfulUpdateMock(webhookId),
|
||||
];
|
||||
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
@@ -202,7 +298,11 @@ describe('useWebhookForm', () => {
|
||||
mode: WebhookFormMode.Edit,
|
||||
webhookId,
|
||||
}),
|
||||
{ wrapper: Wrapper },
|
||||
{
|
||||
wrapper: ({ children }) => (
|
||||
<Wrapper mocks={mocks}>{children}</Wrapper>
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
const formData = {
|
||||
@@ -216,22 +316,30 @@ describe('useWebhookForm', () => {
|
||||
await result.current.handleSave(formData);
|
||||
});
|
||||
|
||||
expect(mockUpdateOneRecord).toHaveBeenCalledWith({
|
||||
idToUpdate: webhookId,
|
||||
updateOneRecordInput: {
|
||||
targetUrl: 'https://updated.com/webhook',
|
||||
description: 'Updated webhook',
|
||||
operations: ['person.updated'],
|
||||
secret: 'updated-secret',
|
||||
},
|
||||
expect(mockEnqueueSuccessSnackBar).toHaveBeenCalledWith({
|
||||
message: 'Webhook https://updated.com/webhook updated successfully',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle update errors', async () => {
|
||||
const error = new ApolloError({
|
||||
graphQLErrors: [{ message: 'Update failed' }],
|
||||
});
|
||||
mockUpdateOneRecord.mockRejectedValue(error);
|
||||
const getWebhookMock = createGetWebhookMock(webhookId);
|
||||
const updateErrorMock = {
|
||||
request: {
|
||||
query: UPDATE_WEBHOOK,
|
||||
variables: {
|
||||
input: {
|
||||
id: webhookId,
|
||||
targetUrl: 'https://test.com/webhook',
|
||||
operations: ['person.created'],
|
||||
description: 'Test webhook',
|
||||
secret: 'test-secret',
|
||||
},
|
||||
},
|
||||
},
|
||||
error: new Error('Update failed'),
|
||||
};
|
||||
|
||||
const mocks = [getWebhookMock, updateErrorMock];
|
||||
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
@@ -239,7 +347,11 @@ describe('useWebhookForm', () => {
|
||||
mode: WebhookFormMode.Edit,
|
||||
webhookId,
|
||||
}),
|
||||
{ wrapper: Wrapper },
|
||||
{
|
||||
wrapper: ({ children }) => (
|
||||
<Wrapper mocks={mocks}>{children}</Wrapper>
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
const formData = {
|
||||
@@ -254,7 +366,7 @@ describe('useWebhookForm', () => {
|
||||
});
|
||||
|
||||
expect(mockEnqueueErrorSnackBar).toHaveBeenCalledWith({
|
||||
apolloError: error,
|
||||
apolloError: expect.any(Error),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -263,7 +375,7 @@ describe('useWebhookForm', () => {
|
||||
it('should update operations correctly', () => {
|
||||
const { result } = renderHook(
|
||||
() => useWebhookForm({ mode: WebhookFormMode.Create }),
|
||||
{ wrapper: Wrapper },
|
||||
{ wrapper: ({ children }) => <Wrapper>{children}</Wrapper> },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
@@ -277,7 +389,7 @@ describe('useWebhookForm', () => {
|
||||
it('should remove operations correctly', () => {
|
||||
const { result } = renderHook(
|
||||
() => useWebhookForm({ mode: WebhookFormMode.Create }),
|
||||
{ wrapper: Wrapper },
|
||||
{ wrapper: ({ children }) => <Wrapper>{children}</Wrapper> },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
@@ -305,7 +417,10 @@ describe('useWebhookForm', () => {
|
||||
const webhookId = 'test-webhook-id';
|
||||
|
||||
it('should delete webhook successfully', async () => {
|
||||
mockDeleteOneRecord.mockResolvedValue({});
|
||||
const mocks = [
|
||||
createGetWebhookMock(webhookId),
|
||||
createSuccessfulDeleteMock(webhookId),
|
||||
];
|
||||
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
@@ -313,14 +428,17 @@ describe('useWebhookForm', () => {
|
||||
mode: WebhookFormMode.Edit,
|
||||
webhookId,
|
||||
}),
|
||||
{ wrapper: Wrapper },
|
||||
{
|
||||
wrapper: ({ children }) => (
|
||||
<Wrapper mocks={mocks}>{children}</Wrapper>
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.deleteWebhook();
|
||||
await result.current.handleDelete();
|
||||
});
|
||||
|
||||
expect(mockDeleteOneRecord).toHaveBeenCalledWith(webhookId);
|
||||
expect(mockEnqueueSuccessSnackBar).toHaveBeenCalledWith({
|
||||
message: 'Webhook deleted successfully',
|
||||
});
|
||||
@@ -329,11 +447,11 @@ describe('useWebhookForm', () => {
|
||||
it('should handle deletion without webhookId', async () => {
|
||||
const { result } = renderHook(
|
||||
() => useWebhookForm({ mode: WebhookFormMode.Create }),
|
||||
{ wrapper: Wrapper },
|
||||
{ wrapper: ({ children }) => <Wrapper>{children}</Wrapper> },
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.deleteWebhook();
|
||||
await result.current.handleDelete();
|
||||
});
|
||||
|
||||
expect(mockEnqueueErrorSnackBar).toHaveBeenCalledWith({
|
||||
@@ -342,10 +460,19 @@ describe('useWebhookForm', () => {
|
||||
});
|
||||
|
||||
it('should handle deletion errors', async () => {
|
||||
const error = new ApolloError({
|
||||
graphQLErrors: [{ message: 'Deletion failed' }],
|
||||
});
|
||||
mockDeleteOneRecord.mockRejectedValue(error);
|
||||
const errorMock = {
|
||||
request: {
|
||||
query: DELETE_WEBHOOK,
|
||||
variables: {
|
||||
input: {
|
||||
id: webhookId,
|
||||
},
|
||||
},
|
||||
},
|
||||
error: new Error('Deletion failed'),
|
||||
};
|
||||
|
||||
const mocks = [createGetWebhookMock(webhookId), errorMock];
|
||||
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
@@ -353,15 +480,19 @@ describe('useWebhookForm', () => {
|
||||
mode: WebhookFormMode.Edit,
|
||||
webhookId,
|
||||
}),
|
||||
{ wrapper: Wrapper },
|
||||
{
|
||||
wrapper: ({ children }) => (
|
||||
<Wrapper mocks={mocks}>{children}</Wrapper>
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.deleteWebhook();
|
||||
await result.current.handleDelete();
|
||||
});
|
||||
|
||||
expect(mockEnqueueErrorSnackBar).toHaveBeenCalledWith({
|
||||
apolloError: error,
|
||||
apolloError: expect.any(Error),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -370,7 +501,7 @@ describe('useWebhookForm', () => {
|
||||
it('should validate canSave property', () => {
|
||||
const { result } = renderHook(
|
||||
() => useWebhookForm({ mode: WebhookFormMode.Create }),
|
||||
{ wrapper: Wrapper },
|
||||
{ wrapper: ({ children }) => <Wrapper>{children}</Wrapper> },
|
||||
);
|
||||
|
||||
// Initially canSave should be false (form is not valid)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { WebhookFormMode } from '@/settings/developers/constants/WebhookFormMode';
|
||||
import { Webhook } from '@/settings/developers/types/webhook/Webhook';
|
||||
import { addEmptyOperationIfNecessary } from '@/settings/developers/utils/addEmptyOperationIfNecessary';
|
||||
import {
|
||||
createWebhookCreateInput,
|
||||
createWebhookUpdateInput,
|
||||
} from '@/settings/developers/utils/createWebhookInput';
|
||||
import { parseOperationsFromStrings } from '@/settings/developers/utils/parseOperationsFromStrings';
|
||||
import {
|
||||
webhookFormSchema,
|
||||
WebhookFormValues,
|
||||
@@ -16,100 +16,66 @@ import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { ApolloError } from '@apollo/client';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
import {
|
||||
useCreateWebhookMutation,
|
||||
useDeleteWebhookMutation,
|
||||
useGetWebhookQuery,
|
||||
useUpdateWebhookMutation,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
import { WEBHOOK_EMPTY_OPERATION } from '~/pages/settings/developers/webhooks/constants/WebhookEmptyOperation';
|
||||
import { WebhookOperationType } from '~/pages/settings/developers/webhooks/types/WebhookOperationsType';
|
||||
|
||||
type UseWebhookFormProps = {
|
||||
webhookId?: string;
|
||||
mode: WebhookFormMode;
|
||||
};
|
||||
|
||||
const DEFAULT_FORM_VALUES: WebhookFormValues = {
|
||||
targetUrl: '',
|
||||
description: '',
|
||||
operations: [{ object: '*', action: '*' }],
|
||||
secret: '',
|
||||
};
|
||||
|
||||
export const useWebhookForm = ({ webhookId, mode }: UseWebhookFormProps) => {
|
||||
const navigate = useNavigateSettings();
|
||||
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
|
||||
|
||||
const isCreationMode = mode === WebhookFormMode.Create;
|
||||
|
||||
const { createOneRecord } = useCreateOneRecord<Webhook>({
|
||||
objectNameSingular: CoreObjectNameSingular.Webhook,
|
||||
});
|
||||
|
||||
const { updateOneRecord } = useUpdateOneRecord<Webhook>({
|
||||
objectNameSingular: CoreObjectNameSingular.Webhook,
|
||||
});
|
||||
|
||||
const { deleteOneRecord: deleteOneWebhook } = useDeleteOneRecord({
|
||||
objectNameSingular: CoreObjectNameSingular.Webhook,
|
||||
});
|
||||
const [createWebhook] = useCreateWebhookMutation();
|
||||
const [updateWebhook] = useUpdateWebhookMutation();
|
||||
const [deleteWebhook] = useDeleteWebhookMutation();
|
||||
|
||||
const formConfig = useForm<WebhookFormValues>({
|
||||
mode: isCreationMode ? 'onSubmit' : 'onTouched',
|
||||
resolver: zodResolver(webhookFormSchema),
|
||||
defaultValues: {
|
||||
targetUrl: '',
|
||||
description: '',
|
||||
operations: [
|
||||
{
|
||||
object: '*',
|
||||
action: '*',
|
||||
},
|
||||
],
|
||||
secret: '',
|
||||
},
|
||||
defaultValues: DEFAULT_FORM_VALUES,
|
||||
});
|
||||
|
||||
const addEmptyOperationIfNecessary = (
|
||||
newOperations: WebhookOperationType[],
|
||||
): WebhookOperationType[] => {
|
||||
if (
|
||||
!newOperations.some((op) => op.object === '*' && op.action === '*') &&
|
||||
!newOperations.some((op) => op.object === null)
|
||||
) {
|
||||
return [...newOperations, WEBHOOK_EMPTY_OPERATION];
|
||||
}
|
||||
return newOperations;
|
||||
};
|
||||
|
||||
const cleanAndFormatOperations = (operations: WebhookOperationType[]) => {
|
||||
return Array.from(
|
||||
new Set(
|
||||
operations
|
||||
.filter((op) => isDefined(op.object) && isDefined(op.action))
|
||||
.map((op) => `${op.object}.${op.action}`),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const { loading, error } = useFindOneRecord({
|
||||
skip: isCreationMode,
|
||||
objectNameSingular: CoreObjectNameSingular.Webhook,
|
||||
objectRecordId: webhookId || '',
|
||||
const { loading, error } = useGetWebhookQuery({
|
||||
skip: isCreationMode || !webhookId,
|
||||
variables: {
|
||||
input: { id: webhookId || '' },
|
||||
},
|
||||
onCompleted: (data) => {
|
||||
if (!data) return;
|
||||
const webhook = data.webhook;
|
||||
if (!webhook) return;
|
||||
|
||||
const baseOperations = data?.operations
|
||||
? data.operations.map((op: string) => {
|
||||
const [object, action] = op.split('.');
|
||||
return { object, action };
|
||||
})
|
||||
: data?.operation
|
||||
? [
|
||||
{
|
||||
object: data.operation.split('.')[0],
|
||||
action: data.operation.split('.')[1],
|
||||
},
|
||||
]
|
||||
: [];
|
||||
const baseOperations = webhook?.operations?.length
|
||||
? parseOperationsFromStrings(webhook.operations)
|
||||
: [];
|
||||
const operations = addEmptyOperationIfNecessary(baseOperations);
|
||||
|
||||
formConfig.reset({
|
||||
targetUrl: data.targetUrl || '',
|
||||
description: data.description || '',
|
||||
targetUrl: webhook.targetUrl || '',
|
||||
description: webhook.description || '',
|
||||
operations,
|
||||
secret: data.secret || '',
|
||||
secret: webhook.secret || '',
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Failed to load webhook`,
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -121,19 +87,9 @@ export const useWebhookForm = ({ webhookId, mode }: UseWebhookFormProps) => {
|
||||
|
||||
const handleCreate = async (formValues: WebhookFormValues) => {
|
||||
try {
|
||||
const cleanedOperations = cleanAndFormatOperations(formValues.operations);
|
||||
|
||||
const webhookData = {
|
||||
targetUrl: formValues.targetUrl.trim(),
|
||||
operations: cleanedOperations,
|
||||
description: formValues.description,
|
||||
secret: formValues.secret,
|
||||
};
|
||||
|
||||
const createdWebhook = await createOneRecord({
|
||||
id: v4(),
|
||||
...webhookData,
|
||||
});
|
||||
const input = createWebhookCreateInput(formValues);
|
||||
const { data } = await createWebhook({ variables: { input } });
|
||||
const createdWebhook = data?.createWebhook;
|
||||
|
||||
const targetUrl = createdWebhook?.targetUrl
|
||||
? `${createdWebhook?.targetUrl}`
|
||||
@@ -163,23 +119,15 @@ export const useWebhookForm = ({ webhookId, mode }: UseWebhookFormProps) => {
|
||||
}
|
||||
|
||||
try {
|
||||
const cleanedOperations = cleanAndFormatOperations(formValues.operations);
|
||||
|
||||
const webhookData = {
|
||||
targetUrl: formValues.targetUrl.trim(),
|
||||
operations: cleanedOperations,
|
||||
description: formValues.description,
|
||||
secret: formValues.secret,
|
||||
};
|
||||
|
||||
await updateOneRecord({
|
||||
idToUpdate: webhookId,
|
||||
updateOneRecordInput: webhookData,
|
||||
});
|
||||
const input = createWebhookUpdateInput(formValues, webhookId);
|
||||
const { data } = await updateWebhook({ variables: { input } });
|
||||
const updatedWebhook = data?.updateWebhook;
|
||||
|
||||
formConfig.reset(formValues);
|
||||
|
||||
const targetUrl = webhookData.targetUrl ? `${webhookData.targetUrl}` : '';
|
||||
const targetUrl = updatedWebhook?.targetUrl
|
||||
? `${updatedWebhook.targetUrl}`
|
||||
: '';
|
||||
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Webhook ${targetUrl} updated successfully`,
|
||||
@@ -224,7 +172,7 @@ export const useWebhookForm = ({ webhookId, mode }: UseWebhookFormProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const deleteWebhook = async () => {
|
||||
const handleDelete = async () => {
|
||||
if (!webhookId) {
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Webhook ID is required for deletion`,
|
||||
@@ -233,7 +181,9 @@ export const useWebhookForm = ({ webhookId, mode }: UseWebhookFormProps) => {
|
||||
}
|
||||
|
||||
try {
|
||||
await deleteOneWebhook(webhookId);
|
||||
await deleteWebhook({
|
||||
variables: { input: { id: webhookId } },
|
||||
});
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Webhook deleted successfully`,
|
||||
});
|
||||
@@ -253,7 +203,7 @@ export const useWebhookForm = ({ webhookId, mode }: UseWebhookFormProps) => {
|
||||
handleSave,
|
||||
updateOperation,
|
||||
removeOperation,
|
||||
deleteWebhook,
|
||||
handleDelete,
|
||||
isCreationMode,
|
||||
error,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user