mirror of
				https://github.com/lingble/twenty.git
				synced 2025-10-31 04:37:56 +00:00 
			
		
		
		
	7154 deleted event is not emitted when calling destroyone (#7159)
Closes #7154
This commit is contained in:
		| @@ -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<ObjectRecord extends IRecord = IRecord>( | ||||
|     args: DestroyOneResolverArgs, | ||||
|     options: WorkspaceQueryRunnerOptions, | ||||
|   ): Promise<ObjectRecord> { | ||||
|   ): Promise<ObjectRecord | undefined> { | ||||
|     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<IRecord>( | ||||
|       [result], | ||||
|       CallWebhookJobsJobOperation.destroy, | ||||
|       options, | ||||
|     ); | ||||
|  | ||||
|     this.emitDestroyEvents<IRecord>([result], authContext, objectMetadataItem); | ||||
|  | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
|   private emitDestroyEvents<BaseRecord extends IRecord = IRecord>( | ||||
|     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<any>; | ||||
|       }), | ||||
|       authContext.workspace.id, | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   private removeNestedProperties<Record extends IRecord = IRecord>( | ||||
|     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; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -20,6 +20,7 @@ export enum CallWebhookJobsJobOperation { | ||||
|   create = 'create', | ||||
|   update = 'update', | ||||
|   delete = 'delete', | ||||
|   destroy = 'destroy', | ||||
| } | ||||
|  | ||||
| export type CallWebhookJobsJobData = { | ||||
|   | ||||
| @@ -49,6 +49,13 @@ export class EntityEventsToDbListener { | ||||
|     return this.handle(payload); | ||||
|   } | ||||
|  | ||||
|   @OnEvent('*.destroyed') | ||||
|   async handleDestroy( | ||||
|     payload: WorkspaceEventBatch<ObjectRecordUpdateEvent<any>>, | ||||
|   ) { | ||||
|     return this.handle(payload); | ||||
|   } | ||||
|  | ||||
|   private async handle(payload: WorkspaceEventBatch<ObjectRecordBaseEvent>) { | ||||
|     const filteredEvents = payload.events.filter( | ||||
|       (event) => event.objectMetadata?.isAuditLogged, | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import { | ||||
|   DeleteManyResolverArgs, | ||||
|   DeleteOneResolverArgs, | ||||
|   DestroyManyResolverArgs, | ||||
|   DestroyOneResolverArgs, | ||||
|   FindDuplicatesResolverArgs, | ||||
|   FindManyResolverArgs, | ||||
|   FindOneResolverArgs, | ||||
| @@ -39,4 +40,6 @@ export type WorkspacePreQueryHookPayload<T> = T extends 'createMany' | ||||
|                     ? RestoreManyResolverArgs | ||||
|                     : T extends 'destroyMany' | ||||
|                       ? DestroyManyResolverArgs | ||||
|                       : never; | ||||
|                       : T extends 'destroyOne' | ||||
|                         ? DestroyOneResolverArgs | ||||
|                         : never; | ||||
|   | ||||
| @@ -22,6 +22,7 @@ export enum ResolverArgsType { | ||||
|   DeleteMany = 'DeleteMany', | ||||
|   RestoreMany = 'RestoreMany', | ||||
|   DestroyMany = 'DestroyMany', | ||||
|   DestroyOne = 'DestroyOne', | ||||
| } | ||||
|  | ||||
| export interface FindManyResolverArgs< | ||||
|   | ||||
| @@ -0,0 +1,7 @@ | ||||
| import { ObjectRecordBaseEvent } from 'src/engine/core-modules/event-emitter/types/object-record.base.event'; | ||||
|  | ||||
| export class ObjectRecordDestroyEvent<T> extends ObjectRecordBaseEvent { | ||||
|   properties: { | ||||
|     before: T; | ||||
|   }; | ||||
| } | ||||
| @@ -19,8 +19,8 @@ export class CalendarEventCleanerConnectedAccountListener { | ||||
|     private readonly calendarQueueService: MessageQueueService, | ||||
|   ) {} | ||||
|  | ||||
|   @OnEvent('connectedAccount.deleted') | ||||
|   async handleDeletedEvent( | ||||
|   @OnEvent('connectedAccount.destroyed') | ||||
|   async handleDestroyedEvent( | ||||
|     payload: WorkspaceEventBatch< | ||||
|       ObjectRecordDeleteEvent<ConnectedAccountWorkspaceEntity> | ||||
|     >, | ||||
|   | ||||
| @@ -15,8 +15,8 @@ export class ConnectedAccountListener { | ||||
|     private readonly accountsToReconnectService: AccountsToReconnectService, | ||||
|   ) {} | ||||
|  | ||||
|   @OnEvent('connectedAccount.deleted') | ||||
|   async handleDeletedEvent( | ||||
|   @OnEvent('connectedAccount.destroyed') | ||||
|   async handleDestroyedEvent( | ||||
|     payload: WorkspaceEventBatch< | ||||
|       ObjectRecordDeleteEvent<ConnectedAccountWorkspaceEntity> | ||||
|     >, | ||||
|   | ||||
| @@ -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) => | ||||
|           ({ | ||||
|   | ||||
| @@ -19,8 +19,8 @@ export class MessagingMessageCleanerConnectedAccountListener { | ||||
|     private readonly messageQueueService: MessageQueueService, | ||||
|   ) {} | ||||
|  | ||||
|   @OnEvent('connectedAccount.deleted') | ||||
|   async handleDeletedEvent( | ||||
|   @OnEvent('connectedAccount.destroyed') | ||||
|   async handleDestroyedEvent( | ||||
|     payload: WorkspaceEventBatch< | ||||
|       ObjectRecordDeleteEvent<ConnectedAccountWorkspaceEntity> | ||||
|     >, | ||||
|   | ||||
| @@ -19,8 +19,8 @@ export class MessagingMessageImportManagerMessageChannelListener { | ||||
|     private readonly messageQueueService: MessageQueueService, | ||||
|   ) {} | ||||
|  | ||||
|   @OnEvent('messageChannel.deleted') | ||||
|   async handleDeletedEvent( | ||||
|   @OnEvent('messageChannel.destroyed') | ||||
|   async handleDestroyedEvent( | ||||
|     payload: WorkspaceEventBatch< | ||||
|       ObjectRecordDeleteEvent<MessageChannelWorkspaceEntity> | ||||
|     >, | ||||
|   | ||||
| @@ -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<ObjectRecordDestroyEvent<any>>, | ||||
|   ) { | ||||
|     await this.handleEvent(payload); | ||||
|   } | ||||
|  | ||||
|   private async handleEvent( | ||||
|     payload: WorkspaceEventBatch< | ||||
|       | ObjectRecordCreateEvent<any> | ||||
|       | ObjectRecordUpdateEvent<any> | ||||
|       | ObjectRecordDeleteEvent<any> | ||||
|       | ObjectRecordDestroyEvent<any> | ||||
|     >, | ||||
|   ) { | ||||
|     const workspaceId = payload.workspaceId; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Raphaël Bosi
					Raphaël Bosi