From 18778c55ac0241c3216d3149e371c06caab6a217 Mon Sep 17 00:00:00 2001 From: nitin <142569587+ehconitin@users.noreply.github.com> Date: Wed, 23 Oct 2024 21:27:46 +0530 Subject: [PATCH] Multiple operations on webhooks (#7807) fixes #7792 WIP :) https://github.com/user-attachments/assets/91f16744-c002-4f24-9cdd-cff79743cab1 --------- Co-authored-by: martmull --- .../developers/types/webhook/Webhook.ts | 1 + .../modules/ui/input/components/Select.tsx | 4 +- .../SettingsDevelopersWebhookDetail.tsx | 202 +++++++++++++----- .../SettingsDevelopersWebhooksNew.tsx | 2 + .../constants/WebhookEmptyOperation.ts | 6 + .../webhooks/types/WebhookOperationsType.ts | 4 + ...bhook-operation-into-operations-command.ts | 56 +++++ .../0-32/0-32-upgrade-version.module.ts | 2 + .../jobs/call-webhook-jobs.job.ts | 17 +- .../dtos/default-value.input.ts | 6 + .../field-metadata-default-value.interface.ts | 2 + .../constants/standard-field-ids.ts | 1 + .../webhook.workspace-entity.ts | 12 ++ .../search-webhooks.integration-spec.ts | 4 +- .../webhooks.integration-spec.ts | 4 +- .../display/icon/components/TablerIcons.ts | 2 + 16 files changed, 257 insertions(+), 68 deletions(-) create mode 100644 packages/twenty-front/src/pages/settings/developers/webhooks/constants/WebhookEmptyOperation.ts create mode 100644 packages/twenty-front/src/pages/settings/developers/webhooks/types/WebhookOperationsType.ts create mode 100644 packages/twenty-server/src/database/commands/upgrade-version/0-32/0-32-copy-webhook-operation-into-operations-command.ts diff --git a/packages/twenty-front/src/modules/settings/developers/types/webhook/Webhook.ts b/packages/twenty-front/src/modules/settings/developers/types/webhook/Webhook.ts index fea0808b1c..6e202c9a79 100644 --- a/packages/twenty-front/src/modules/settings/developers/types/webhook/Webhook.ts +++ b/packages/twenty-front/src/modules/settings/developers/types/webhook/Webhook.ts @@ -3,5 +3,6 @@ export type Webhook = { targetUrl: string; description?: string; operation: string; + operations: string[]; __typename: 'Webhook'; }; diff --git a/packages/twenty-front/src/modules/ui/input/components/Select.tsx b/packages/twenty-front/src/modules/ui/input/components/Select.tsx index ec574e083d..905fa1a9ef 100644 --- a/packages/twenty-front/src/modules/ui/input/components/Select.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/Select.tsx @@ -11,6 +11,7 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { isDefined } from '~/utils/isDefined'; +import { EllipsisDisplay } from '@/ui/field/display/components/EllipsisDisplay'; import { SelectHotkeyScope } from '../types/SelectHotkeyScope'; export type SelectOption = { @@ -73,6 +74,7 @@ const StyledLabel = styled.span` const StyledControlLabel = styled.div` align-items: center; display: flex; + overflow: hidden; gap: ${({ theme }) => theme.spacing(1)}; `; @@ -136,7 +138,7 @@ export const Select = ({ stroke={theme.icon.stroke.sm} /> )} - {selectedOption?.label} + {selectedOption?.label} diff --git a/packages/twenty-front/src/pages/settings/developers/webhooks/components/SettingsDevelopersWebhookDetail.tsx b/packages/twenty-front/src/pages/settings/developers/webhooks/components/SettingsDevelopersWebhookDetail.tsx index 1a2316a0a3..7063102961 100644 --- a/packages/twenty-front/src/pages/settings/developers/webhooks/components/SettingsDevelopersWebhookDetail.tsx +++ b/packages/twenty-front/src/pages/settings/developers/webhooks/components/SettingsDevelopersWebhookDetail.tsx @@ -1,7 +1,16 @@ import styled from '@emotion/styled'; -import { useState } from 'react'; +import { useMemo, useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; -import { H2Title, IconTrash } from 'twenty-ui'; +import { + H2Title, + IconBox, + IconNorthStar, + IconPlus, + IconRefresh, + IconTrash, + isDefined, + useIcons, +} from 'twenty-ui'; import { isAnalyticsEnabledState } from '@/client-config/states/isAnalyticsEnabledState'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; @@ -17,7 +26,8 @@ import { SettingsDevelopersWebhookUsageGraphEffect } from '@/settings/developers import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; import { Button } from '@/ui/input/button/components/Button'; -import { Select } from '@/ui/input/components/Select'; +import { IconButton } from '@/ui/input/button/components/IconButton'; +import { Select, SelectOption } from '@/ui/input/components/Select'; import { TextArea } from '@/ui/input/components/TextArea'; import { TextInput } from '@/ui/input/components/TextInput'; import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; @@ -25,11 +35,23 @@ import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBa import { Section } from '@/ui/layout/section/components/Section'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useRecoilValue } from 'recoil'; +import { WEBHOOK_EMPTY_OPERATION } from '~/pages/settings/developers/webhooks/constants/WebhookEmptyOperation'; +import { WebhookOperationType } from '~/pages/settings/developers/webhooks/types/WebhookOperationsType'; + +const OBJECT_DROPDOWN_WIDTH = 340; +const ACTION_DROPDOWN_WIDTH = 140; const StyledFilterRow = styled.div` - display: flex; - flex-direction: row; + display: grid; + grid-template-columns: ${OBJECT_DROPDOWN_WIDTH}px ${ACTION_DROPDOWN_WIDTH}px auto; gap: ${({ theme }) => theme.spacing(2)}; + margin-bottom: ${({ theme }) => theme.spacing(2)}; + align-items: center; +`; + +const StyledPlaceholder = styled.div` + height: ${({ theme }) => theme.spacing(8)}; + width: ${({ theme }) => theme.spacing(8)}; `; export const SettingsDevelopersWebhooksDetail = () => { @@ -41,20 +63,33 @@ export const SettingsDevelopersWebhooksDetail = () => { const [isDeleteWebhookModalOpen, setIsDeleteWebhookModalOpen] = useState(false); - const [description, setDescription] = useState(''); - const [operationObjectSingularName, setOperationObjectSingularName] = - useState(''); - const [operationAction, setOperationAction] = useState(''); + const [operations, setOperations] = useState([ + WEBHOOK_EMPTY_OPERATION, + ]); const [isDirty, setIsDirty] = useState(false); + const { getIcon } = useIcons(); const { record: webhookData } = useFindOneRecord({ objectNameSingular: CoreObjectNameSingular.Webhook, objectRecordId: webhookId, onCompleted: (data) => { setDescription(data?.description ?? ''); - setOperationObjectSingularName(data?.operation.split('.')[0] ?? ''); - setOperationAction(data?.operation.split('.')[1] ?? ''); + 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], + }, + ] + : []; + + setOperations(addEmptyOperationIfNecessary(baseOperations)); setIsDirty(false); }, }); @@ -72,30 +107,87 @@ export const SettingsDevelopersWebhooksDetail = () => { const isAnalyticsV2Enabled = useIsFeatureEnabled('IS_ANALYTICS_V2_ENABLED'); - const fieldTypeOptions = [ - { value: '*', label: 'All Objects' }, - ...objectMetadataItems.map((item) => ({ - value: item.nameSingular, - label: item.labelSingular, - })), + const fieldTypeOptions: SelectOption[] = useMemo( + () => [ + { value: '*', label: 'All Objects', Icon: IconNorthStar }, + ...objectMetadataItems.map((item) => ({ + value: item.nameSingular, + label: item.labelPlural, + Icon: getIcon(item.icon), + })), + ], + [objectMetadataItems, getIcon], + ); + + const actionOptions: SelectOption[] = [ + { value: '*', label: 'All Actions', Icon: IconNorthStar }, + { value: 'create', label: 'Created', Icon: IconPlus }, + { value: 'update', label: 'Updated', Icon: IconRefresh }, + { value: 'delete', label: 'Deleted', Icon: IconTrash }, ]; const { updateOneRecord } = useUpdateOneRecord({ objectNameSingular: CoreObjectNameSingular.Webhook, }); + 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 handleSave = async () => { + const cleanedOperations = cleanAndFormatOperations(operations); setIsDirty(false); await updateOneRecord({ idToUpdate: webhookId, updateOneRecordInput: { - operation: `${operationObjectSingularName}.${operationAction}`, + operation: cleanedOperations?.[0], + operations: cleanedOperations, description: description, }, }); navigate(developerPath); }; + const addEmptyOperationIfNecessary = ( + newOperations: WebhookOperationType[], + ) => { + if ( + !newOperations.some((op) => op.object === '*' && op.action === '*') && + !newOperations.some((op) => op.object === null) + ) { + return [...newOperations, WEBHOOK_EMPTY_OPERATION]; + } + return newOperations; + }; + + const updateOperation = ( + index: number, + field: 'object' | 'action', + value: string | null, + ) => { + const newOperations = [...operations]; + + newOperations[index] = { + ...newOperations[index], + [field]: value, + }; + + setOperations(addEmptyOperationIfNecessary(newOperations)); + setIsDirty(true); + }; + + const removeOperation = (index: number) => { + const newOperations = operations.filter((_, i) => i !== index); + setOperations(addEmptyOperationIfNecessary(newOperations)); + setIsDirty(true); + }; + if (!webhookData?.targetUrl) { return <>; } @@ -108,10 +200,7 @@ export const SettingsDevelopersWebhooksDetail = () => { children: 'Workspace', href: getSettingsPagePath(SettingsPath.Workspace), }, - { - children: 'Developers', - href: developerPath, - }, + { children: 'Developers', href: developerPath }, { children: 'Webhook' }, ]} actionButton={ @@ -152,43 +241,50 @@ export const SettingsDevelopersWebhooksDetail = () => {
- - { - setIsDirty(true); - setOperationAction(operationAction); - }} - options={[ - { value: '*', label: 'All Actions' }, - { value: 'create', label: 'Create' }, - { value: 'update', label: 'Update' }, - { value: 'delete', label: 'Delete' }, - ]} - /> - + {operations.map((operation, index) => ( + + updateOperation(index, 'action', action)} + options={actionOptions} + /> + + {index < operations.length - 1 ? ( + removeOperation(index)} + variant="tertiary" + size="medium" + Icon={IconTrash} + /> + ) : ( + + )} + + ))}
- {isAnalyticsEnabled && isAnalyticsV2Enabled ? ( + {isAnalyticsEnabled && isAnalyticsV2Enabled && ( <> - ) : ( - <> )}
diff --git a/packages/twenty-front/src/pages/settings/developers/webhooks/components/SettingsDevelopersWebhooksNew.tsx b/packages/twenty-front/src/pages/settings/developers/webhooks/components/SettingsDevelopersWebhooksNew.tsx index 4d0e7bb47a..92dcb49d98 100644 --- a/packages/twenty-front/src/pages/settings/developers/webhooks/components/SettingsDevelopersWebhooksNew.tsx +++ b/packages/twenty-front/src/pages/settings/developers/webhooks/components/SettingsDevelopersWebhooksNew.tsx @@ -19,9 +19,11 @@ export const SettingsDevelopersWebhooksNew = () => { const [formValues, setFormValues] = useState<{ targetUrl: string; operation: string; + operations: string[]; }>({ targetUrl: '', operation: '*.*', + operations: ['*.*'], }); const [isTargetUrlValid, setIsTargetUrlValid] = useState(true); diff --git a/packages/twenty-front/src/pages/settings/developers/webhooks/constants/WebhookEmptyOperation.ts b/packages/twenty-front/src/pages/settings/developers/webhooks/constants/WebhookEmptyOperation.ts new file mode 100644 index 0000000000..218645a0b4 --- /dev/null +++ b/packages/twenty-front/src/pages/settings/developers/webhooks/constants/WebhookEmptyOperation.ts @@ -0,0 +1,6 @@ +import { WebhookOperationType } from '~/pages/settings/developers/webhooks/types/WebhookOperationsType'; + +export const WEBHOOK_EMPTY_OPERATION: WebhookOperationType = { + object: null, + action: '*', +}; diff --git a/packages/twenty-front/src/pages/settings/developers/webhooks/types/WebhookOperationsType.ts b/packages/twenty-front/src/pages/settings/developers/webhooks/types/WebhookOperationsType.ts new file mode 100644 index 0000000000..a3b81332fa --- /dev/null +++ b/packages/twenty-front/src/pages/settings/developers/webhooks/types/WebhookOperationsType.ts @@ -0,0 +1,4 @@ +export type WebhookOperationType = { + object: string | null; + action: string; +}; diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-32/0-32-copy-webhook-operation-into-operations-command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-32/0-32-copy-webhook-operation-into-operations-command.ts new file mode 100644 index 0000000000..44d044fded --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-32/0-32-copy-webhook-operation-into-operations-command.ts @@ -0,0 +1,56 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import { Command } from 'nest-commander'; +import { Repository } from 'typeorm'; +import chalk from 'chalk'; + +import { ActiveWorkspacesCommandRunner } from 'src/database/commands/active-workspaces.command'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; +import { BaseCommandOptions } from 'src/database/commands/base.command'; + +@Command({ + name: 'upgrade-0.32:copy-webhook-operation-into-operations', + description: + 'Read, transform and copy webhook from deprecated column operation into newly created column operations', +}) +export class CopyWebhookOperationIntoOperationsCommand extends ActiveWorkspacesCommandRunner { + constructor( + @InjectRepository(Workspace, 'core') + protected readonly workspaceRepository: Repository, + private readonly twentyORMGlobalManager: TwentyORMGlobalManager, + ) { + super(workspaceRepository); + } + + async executeActiveWorkspacesCommand( + passedParams: string[], + options: BaseCommandOptions, + activeWorkspaceIds: string[], + ): Promise { + this.logger.log('Running command to copy operation to operations'); + + for (const workspaceId of activeWorkspaceIds) { + this.logger.log(`Running command for workspace ${workspaceId}`); + + const webhookRepository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + 'webhook', + ); + + const webhooks = await webhookRepository.find(); + + for (const webhook of webhooks) { + if ('operation' in webhook) { + await webhookRepository.update(webhook.id, { + operations: [webhook.operation], + }); + this.logger.log( + chalk.yellow(`Copied webhook operation to operations`), + ); + } + } + } + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-32/0-32-upgrade-version.module.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-32/0-32-upgrade-version.module.ts index 2018c4563a..0d0f59548f 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-32/0-32-upgrade-version.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-32/0-32-upgrade-version.module.ts @@ -10,6 +10,7 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat import { SearchModule } from 'src/engine/metadata-modules/search/search.module'; import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module'; import { WorkspaceSyncMetadataCommandsModule } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module'; +import { CopyWebhookOperationIntoOperationsCommand } from 'src/database/commands/upgrade-version/0-32/0-32-copy-webhook-operation-into-operations-command'; @Module({ imports: [ @@ -25,6 +26,7 @@ import { WorkspaceSyncMetadataCommandsModule } from 'src/engine/workspace-manage providers: [ UpgradeTo0_32Command, EnforceUniqueConstraintsCommand, + CopyWebhookOperationIntoOperationsCommand, SimplifySearchVectorExpressionCommand, ], }) diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/jobs/call-webhook-jobs.job.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/jobs/call-webhook-jobs.job.ts index a1b43eb220..8705d35547 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/jobs/call-webhook-jobs.job.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/jobs/call-webhook-jobs.job.ts @@ -1,6 +1,6 @@ import { Logger } from '@nestjs/common'; -import { Like } from 'typeorm'; +import { ArrayContains } from 'typeorm'; import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface'; @@ -54,10 +54,10 @@ export class CallWebhookJobsJob { const webhooks = await webhookRepository.find({ where: [ - { operation: Like(`%${eventName}%`) }, - { operation: Like(`%*.${operation}%`) }, - { operation: Like(`%${nameSingular}.*%`) }, - { operation: Like('%*.*%') }, + { operations: ArrayContains([eventName]) }, + { operations: ArrayContains([`*.${operation}`]) }, + { operations: ArrayContains([`${nameSingular}.*`]) }, + { operations: ArrayContains(['*.*']) }, ], }); @@ -80,12 +80,9 @@ export class CallWebhookJobsJob { ); }); - if (webhooks.length) { + webhooks.length > 0 && this.logger.log( - `CallWebhookJobsJob on eventName '${eventName}' called on webhooks ids [\n"${webhooks - .map((webhook) => webhook.id) - .join('",\n"')}"\n]`, + `CallWebhookJobsJob on eventName '${eventName}' triggered webhooks with ids [\n"${webhooks.map((webhook) => webhook.id).join('",\n"')}"\n]`, ); - } } } diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts index 42d98b4d6e..679a68f713 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts @@ -189,3 +189,9 @@ export class FieldMetadataDefaultValuePhones { @IsObject() additionalPhones: object | null; } + +export class FieldMetadataDefaultArray { + @ValidateIf((_object, value) => value !== null) + @IsArray() + value: string[] | null; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts index 262cd9f227..8acf362373 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts @@ -1,5 +1,6 @@ import { FieldMetadataDefaultActor, + FieldMetadataDefaultArray, FieldMetadataDefaultValueAddress, FieldMetadataDefaultValueBoolean, FieldMetadataDefaultValueCurrency, @@ -48,6 +49,7 @@ type FieldMetadataDefaultValueMapping = { [FieldMetadataType.RAW_JSON]: FieldMetadataDefaultValueRawJson; [FieldMetadataType.RICH_TEXT]: FieldMetadataDefaultValueRichText; [FieldMetadataType.ACTOR]: FieldMetadataDefaultActor; + [FieldMetadataType.ARRAY]: FieldMetadataDefaultArray; }; export type FieldMetadataClassValidation = diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids.ts index 25949019e3..ac9d921310 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids.ts @@ -400,6 +400,7 @@ export const VIEW_STANDARD_FIELD_IDS = { export const WEBHOOK_STANDARD_FIELD_IDS = { targetUrl: '20202020-1229-45a8-8cf4-85c9172aae12', operation: '20202020-15b7-458e-bf30-74770a54410c', + operations: '20202020-15b7-458e-bf30-74770a54411c', description: '20202020-15b7-458e-bf30-74770a54410d', }; diff --git a/packages/twenty-server/src/modules/webhook/standard-objects/webhook.workspace-entity.ts b/packages/twenty-server/src/modules/webhook/standard-objects/webhook.workspace-entity.ts index 70507cddc6..a52f0e8fe7 100644 --- a/packages/twenty-server/src/modules/webhook/standard-objects/webhook.workspace-entity.ts +++ b/packages/twenty-server/src/modules/webhook/standard-objects/webhook.workspace-entity.ts @@ -7,6 +7,7 @@ import { WorkspaceIsNullable } from 'src/engine/twenty-orm/decorators/workspace- import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator'; import { WEBHOOK_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; +import { WorkspaceIsDeprecated } from 'src/engine/twenty-orm/decorators/workspace-is-deprecated.decorator'; @WorkspaceEntity({ standardId: STANDARD_OBJECT_IDS.webhook, @@ -36,8 +37,19 @@ export class WebhookWorkspaceEntity extends BaseWorkspaceEntity { description: 'Webhook operation', icon: 'IconCheckbox', }) + @WorkspaceIsDeprecated() operation: string; + @WorkspaceField({ + standardId: WEBHOOK_STANDARD_FIELD_IDS.operations, + type: FieldMetadataType.ARRAY, + label: 'Operations', + description: 'Webhook operations', + icon: 'IconCheckbox', + defaultValue: ['*.*'], + }) + operations: string[]; + @WorkspaceField({ standardId: WEBHOOK_STANDARD_FIELD_IDS.description, type: FieldMetadataType.TEXT, diff --git a/packages/twenty-server/test/integration/graphql/suites/object-generated/search-webhooks.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/object-generated/search-webhooks.integration-spec.ts index d5a93db25e..89d7bce89e 100644 --- a/packages/twenty-server/test/integration/graphql/suites/object-generated/search-webhooks.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/object-generated/search-webhooks.integration-spec.ts @@ -12,7 +12,7 @@ describe('searchWebhooksResolver (e2e)', () => { node { id targetUrl - operation + operations description createdAt updatedAt @@ -46,7 +46,7 @@ describe('searchWebhooksResolver (e2e)', () => { expect(searchWebhooks).toHaveProperty('id'); expect(searchWebhooks).toHaveProperty('targetUrl'); - expect(searchWebhooks).toHaveProperty('operation'); + expect(searchWebhooks).toHaveProperty('operations'); expect(searchWebhooks).toHaveProperty('description'); expect(searchWebhooks).toHaveProperty('createdAt'); expect(searchWebhooks).toHaveProperty('updatedAt'); diff --git a/packages/twenty-server/test/integration/graphql/suites/object-generated/webhooks.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/object-generated/webhooks.integration-spec.ts index aaf181bf38..f1ddfd1a48 100644 --- a/packages/twenty-server/test/integration/graphql/suites/object-generated/webhooks.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/object-generated/webhooks.integration-spec.ts @@ -12,7 +12,7 @@ describe('webhooksResolver (e2e)', () => { node { id targetUrl - operation + operations description createdAt updatedAt @@ -46,7 +46,7 @@ describe('webhooksResolver (e2e)', () => { expect(webhooks).toHaveProperty('id'); expect(webhooks).toHaveProperty('targetUrl'); - expect(webhooks).toHaveProperty('operation'); + expect(webhooks).toHaveProperty('operations'); expect(webhooks).toHaveProperty('description'); expect(webhooks).toHaveProperty('createdAt'); expect(webhooks).toHaveProperty('updatedAt'); diff --git a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts index 5244849f69..f85046affd 100644 --- a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts +++ b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts @@ -134,6 +134,7 @@ export { IconH1, IconH2, IconH3, + IconHandClick, IconHeadphones, IconHeart, IconHeartOff, @@ -166,6 +167,7 @@ export { IconMoneybag, IconMoodSmile, IconMouse2, + IconNorthStar, IconNotes, IconNumbers, IconPaperclip,