From c9c2f32922adda277d1f695dcbca8c55c7535ba8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Bosi?= <71827178+bosiraphael@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:52:04 +0200 Subject: [PATCH] 7154 deleted event is not emitted when calling destroyone (#7159) Closes #7154 --- .../graphql-query-runner.service.ts | 89 ++++++++++++++++++- .../jobs/call-webhook-jobs.job.ts | 1 + .../listeners/entity-events-to-db.listener.ts | 7 ++ .../types/workspace-query-hook.type.ts | 5 +- .../workspace-resolvers-builder.interface.ts | 1 + .../types/object-record-destroy.event.ts | 7 ++ ...vent-cleaner-connected-account.listener.ts | 4 +- .../listeners/connected-account.listener.ts | 4 +- ...ected-account-delete-one.pre-query.hook.ts | 4 +- ...sage-cleaner-connected-account.listener.ts | 4 +- ...import-manager-message-channel.listener.ts | 4 +- .../database-event-trigger.listener.ts | 13 ++- 12 files changed, 128 insertions(+), 15 deletions(-) create mode 100644 packages/twenty-server/src/engine/core-modules/event-emitter/types/object-record-destroy.event.ts diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-runner.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-runner.service.ts index cd1337b458c7..8eb4a614add5 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-runner.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-runner.service.ts @@ -35,6 +35,7 @@ import { } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.exception'; import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; import { ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event'; +import { ObjectRecordDeleteEvent } from 'src/engine/core-modules/event-emitter/types/object-record-delete.event'; import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator'; import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service'; @@ -284,10 +285,94 @@ export class GraphqlQueryRunnerService { async destroyOne( args: DestroyOneResolverArgs, options: WorkspaceQueryRunnerOptions, - ): Promise { + ): Promise { const graphqlQueryDestroyOneResolverService = new GraphqlQueryDestroyOneResolverService(this.twentyORMGlobalManager); - return graphqlQueryDestroyOneResolverService.destroyOne(args, options); + const { authContext, objectMetadataItem } = options; + + assertMutationNotOnRemoteObject(objectMetadataItem); + assertIsValidUuid(args.id); + + const hookedArgs = + await this.workspaceQueryHookService.executePreQueryHooks( + authContext, + objectMetadataItem.nameSingular, + 'destroyOne', + args, + ); + + const computedArgs = (await this.queryRunnerArgsFactory.create( + hookedArgs, + options, + ResolverArgsType.DestroyOne, + )) as DestroyOneResolverArgs; + + const result = (await graphqlQueryDestroyOneResolverService.destroyOne( + computedArgs, + options, + )) as ObjectRecord; + + await this.workspaceQueryHookService.executePostQueryHooks( + authContext, + objectMetadataItem.nameSingular, + 'destroyOne', + [result], + ); + + await this.triggerWebhooks( + [result], + CallWebhookJobsJobOperation.destroy, + options, + ); + + this.emitDestroyEvents([result], authContext, objectMetadataItem); + + return result; + } + + private emitDestroyEvents( + records: BaseRecord[], + authContext: AuthContext, + objectMetadataItem: ObjectMetadataInterface, + ) { + this.workspaceEventEmitter.emit( + `${objectMetadataItem.nameSingular}.destroyed`, + records.map((record) => { + return { + userId: authContext.user?.id, + recordId: record.id, + objectMetadata: objectMetadataItem, + properties: { + before: this.removeNestedProperties(record), + }, + } satisfies ObjectRecordDeleteEvent; + }), + authContext.workspace.id, + ); + } + + private removeNestedProperties( + record: Record, + ) { + if (!record) { + return; + } + + const sanitizedRecord = {}; + + for (const [key, value] of Object.entries(record)) { + if (value && typeof value === 'object' && value['edges']) { + continue; + } + + if (key === '__typename') { + continue; + } + + sanitizedRecord[key] = value; + } + + return sanitizedRecord; } } 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 d0bbc6872c06..a1b43eb22034 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 @@ -20,6 +20,7 @@ export enum CallWebhookJobsJobOperation { create = 'create', update = 'update', delete = 'delete', + destroy = 'destroy', } export type CallWebhookJobsJobData = { diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/listeners/entity-events-to-db.listener.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/listeners/entity-events-to-db.listener.ts index eb9ddbf06a6f..8eb8be61f774 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/listeners/entity-events-to-db.listener.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/listeners/entity-events-to-db.listener.ts @@ -49,6 +49,13 @@ export class EntityEventsToDbListener { return this.handle(payload); } + @OnEvent('*.destroyed') + async handleDestroy( + payload: WorkspaceEventBatch>, + ) { + return this.handle(payload); + } + private async handle(payload: WorkspaceEventBatch) { const filteredEvents = payload.events.filter( (event) => event.objectMetadata?.isAuditLogged, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type.ts index f155475877e0..b75c939d1ac7 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type.ts @@ -4,6 +4,7 @@ import { DeleteManyResolverArgs, DeleteOneResolverArgs, DestroyManyResolverArgs, + DestroyOneResolverArgs, FindDuplicatesResolverArgs, FindManyResolverArgs, FindOneResolverArgs, @@ -39,4 +40,6 @@ export type WorkspacePreQueryHookPayload = T extends 'createMany' ? RestoreManyResolverArgs : T extends 'destroyMany' ? DestroyManyResolverArgs - : never; + : T extends 'destroyOne' + ? DestroyOneResolverArgs + : never; diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface.ts index 4e2a0af85196..22c07059737d 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface.ts @@ -22,6 +22,7 @@ export enum ResolverArgsType { DeleteMany = 'DeleteMany', RestoreMany = 'RestoreMany', DestroyMany = 'DestroyMany', + DestroyOne = 'DestroyOne', } export interface FindManyResolverArgs< diff --git a/packages/twenty-server/src/engine/core-modules/event-emitter/types/object-record-destroy.event.ts b/packages/twenty-server/src/engine/core-modules/event-emitter/types/object-record-destroy.event.ts new file mode 100644 index 000000000000..f12b1e17547f --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/event-emitter/types/object-record-destroy.event.ts @@ -0,0 +1,7 @@ +import { ObjectRecordBaseEvent } from 'src/engine/core-modules/event-emitter/types/object-record.base.event'; + +export class ObjectRecordDestroyEvent extends ObjectRecordBaseEvent { + properties: { + before: T; + }; +} diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-cleaner/listeners/calendar-event-cleaner-connected-account.listener.ts b/packages/twenty-server/src/modules/calendar/calendar-event-cleaner/listeners/calendar-event-cleaner-connected-account.listener.ts index 387dc0743c68..1406d442d02c 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-cleaner/listeners/calendar-event-cleaner-connected-account.listener.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-cleaner/listeners/calendar-event-cleaner-connected-account.listener.ts @@ -19,8 +19,8 @@ export class CalendarEventCleanerConnectedAccountListener { private readonly calendarQueueService: MessageQueueService, ) {} - @OnEvent('connectedAccount.deleted') - async handleDeletedEvent( + @OnEvent('connectedAccount.destroyed') + async handleDestroyedEvent( payload: WorkspaceEventBatch< ObjectRecordDeleteEvent >, diff --git a/packages/twenty-server/src/modules/connected-account/listeners/connected-account.listener.ts b/packages/twenty-server/src/modules/connected-account/listeners/connected-account.listener.ts index da8bededdb03..62ee28d1bede 100644 --- a/packages/twenty-server/src/modules/connected-account/listeners/connected-account.listener.ts +++ b/packages/twenty-server/src/modules/connected-account/listeners/connected-account.listener.ts @@ -15,8 +15,8 @@ export class ConnectedAccountListener { private readonly accountsToReconnectService: AccountsToReconnectService, ) {} - @OnEvent('connectedAccount.deleted') - async handleDeletedEvent( + @OnEvent('connectedAccount.destroyed') + async handleDestroyedEvent( payload: WorkspaceEventBatch< ObjectRecordDeleteEvent >, diff --git a/packages/twenty-server/src/modules/connected-account/query-hooks/connected-account-delete-one.pre-query.hook.ts b/packages/twenty-server/src/modules/connected-account/query-hooks/connected-account-delete-one.pre-query.hook.ts index c04999bea385..f49db465d04b 100644 --- a/packages/twenty-server/src/modules/connected-account/query-hooks/connected-account-delete-one.pre-query.hook.ts +++ b/packages/twenty-server/src/modules/connected-account/query-hooks/connected-account-delete-one.pre-query.hook.ts @@ -8,7 +8,7 @@ import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager'; import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter'; import { MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity'; -@WorkspaceQueryHook(`connectedAccount.deleteOne`) +@WorkspaceQueryHook(`connectedAccount.destroyOne`) export class ConnectedAccountDeleteOnePreQueryHook implements WorkspaceQueryHookInstance { @@ -34,7 +34,7 @@ export class ConnectedAccountDeleteOnePreQueryHook }); this.workspaceEventEmitter.emit( - 'messageChannel.deleted', + 'messageChannel.destroyed', messageChannels.map( (messageChannel) => ({ diff --git a/packages/twenty-server/src/modules/messaging/message-cleaner/listeners/messaging-message-cleaner-connected-account.listener.ts b/packages/twenty-server/src/modules/messaging/message-cleaner/listeners/messaging-message-cleaner-connected-account.listener.ts index 8e837cce6a59..c5c3a033dedc 100644 --- a/packages/twenty-server/src/modules/messaging/message-cleaner/listeners/messaging-message-cleaner-connected-account.listener.ts +++ b/packages/twenty-server/src/modules/messaging/message-cleaner/listeners/messaging-message-cleaner-connected-account.listener.ts @@ -19,8 +19,8 @@ export class MessagingMessageCleanerConnectedAccountListener { private readonly messageQueueService: MessageQueueService, ) {} - @OnEvent('connectedAccount.deleted') - async handleDeletedEvent( + @OnEvent('connectedAccount.destroyed') + async handleDestroyedEvent( payload: WorkspaceEventBatch< ObjectRecordDeleteEvent >, diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/listeners/messaging-import-manager-message-channel.listener.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/listeners/messaging-import-manager-message-channel.listener.ts index 513e2672adf4..80802c3fb2c4 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/listeners/messaging-import-manager-message-channel.listener.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/listeners/messaging-import-manager-message-channel.listener.ts @@ -19,8 +19,8 @@ export class MessagingMessageImportManagerMessageChannelListener { private readonly messageQueueService: MessageQueueService, ) {} - @OnEvent('messageChannel.deleted') - async handleDeletedEvent( + @OnEvent('messageChannel.destroyed') + async handleDestroyedEvent( payload: WorkspaceEventBatch< ObjectRecordDeleteEvent >, diff --git a/packages/twenty-server/src/modules/workflow/workflow-trigger/database-event-trigger/listeners/database-event-trigger.listener.ts b/packages/twenty-server/src/modules/workflow/workflow-trigger/database-event-trigger/listeners/database-event-trigger.listener.ts index e5b62d59afc6..5ea3f82d4781 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-trigger/database-event-trigger/listeners/database-event-trigger.listener.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-trigger/database-event-trigger/listeners/database-event-trigger.listener.ts @@ -1,11 +1,12 @@ import { Injectable, Logger } from '@nestjs/common'; import { OnEvent } from '@nestjs/event-emitter'; -import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; -import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service'; import { ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event'; import { ObjectRecordDeleteEvent } from 'src/engine/core-modules/event-emitter/types/object-record-delete.event'; +import { ObjectRecordDestroyEvent } from 'src/engine/core-modules/event-emitter/types/object-record-destroy.event'; import { ObjectRecordUpdateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-update.event'; +import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; +import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service'; import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator'; import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service'; @@ -49,11 +50,19 @@ export class DatabaseEventTriggerListener { await this.handleEvent(payload); } + @OnEvent('*.destroyed') + async handleObjectRecordDestroyEvent( + payload: WorkspaceEventBatch>, + ) { + await this.handleEvent(payload); + } + private async handleEvent( payload: WorkspaceEventBatch< | ObjectRecordCreateEvent | ObjectRecordUpdateEvent | ObjectRecordDeleteEvent + | ObjectRecordDestroyEvent >, ) { const workspaceId = payload.workspaceId;