From 69a00eaaf600550f12e269b7a98037bb0541b975 Mon Sep 17 00:00:00 2001 From: Thomas Trompette Date: Mon, 14 Apr 2025 13:49:19 +0200 Subject: [PATCH] Emit message event with actual message object (#11548) Events were emitted with the wrong object. We were using messageWithParticipant instead of actual message object --- .../services/messaging-message.service.ts | 33 +++-- ...d-enqueue-contact-creation.service.spec.ts | 10 +- ...es-and-enqueue-contact-creation.service.ts | 130 +++++++++--------- 3 files changed, 95 insertions(+), 78 deletions(-) diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message.service.ts index df753ddf3..6540b3c6b 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message.service.ts @@ -17,7 +17,10 @@ export class MessagingMessageService { messages: MessageWithParticipants[], messageChannelId: string, transactionManager: EntityManager, - ): Promise> { + ): Promise<{ + createdMessages: Partial[]; + messageExternalIdsAndIdsMap: Map; + }> { const messageChannelMessageAssociationRepository = await this.twentyORMManager.getRepository( 'messageChannelMessageAssociation', @@ -34,6 +37,7 @@ export class MessagingMessageService { ); const messageExternalIdsAndIdsMap = new Map(); + const createdMessages: Partial[] = []; for (const message of messages) { const existingMessageChannelMessageAssociation = @@ -101,18 +105,18 @@ export class MessagingMessageService { } const newMessageId = v4(); + const messageToCreate = { + id: newMessageId, + headerMessageId: message.headerMessageId, + subject: message.subject, + receivedAt: message.receivedAt, + text: message.text, + messageThreadId: newOrExistingMessageThreadId, + }; - await messageRepository.insert( - { - id: newMessageId, - headerMessageId: message.headerMessageId, - subject: message.subject, - receivedAt: message.receivedAt, - text: message.text, - messageThreadId: newOrExistingMessageThreadId, - }, - transactionManager, - ); + await messageRepository.insert(messageToCreate, transactionManager); + + createdMessages.push(messageToCreate); messageExternalIdsAndIdsMap.set(message.externalId, newMessageId); @@ -128,6 +132,9 @@ export class MessagingMessageService { ); } - return messageExternalIdsAndIdsMap; + return { + createdMessages, + messageExternalIdsAndIdsMap, + }; } } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.spec.ts index 27127e6ff..a5e00125e 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.spec.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.spec.ts @@ -119,12 +119,16 @@ describe('MessagingSaveMessagesAndEnqueueContactCreationService', () => { { provide: MessagingMessageService, useValue: { - saveMessagesWithinTransaction: jest.fn().mockResolvedValue( - new Map([ + saveMessagesWithinTransaction: jest.fn().mockResolvedValue({ + messageExternalIdsAndIdsMap: new Map([ ['message-1', 'db-message-id-1'], ['message-2', 'db-message-id-2'], ]), - ), + createdMessages: [ + { id: 'db-message-id-1' }, + { id: 'db-message-id-2' }, + ], + }), }, }, { diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts index 7d7047e6b..4c1adcd3e 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts @@ -53,70 +53,76 @@ export class MessagingSaveMessagesAndEnqueueContactCreationService { const workspaceDataSource = await this.twentyORMManager.getDatasource(); - const participantsWithMessageId = await workspaceDataSource?.transaction( - async (transactionManager: EntityManager) => { - const messageExternalIdsAndIdsMap = - await this.messageService.saveMessagesWithinTransaction( - messagesToSave, - messageChannel.id, + const createdMessagesWithParticipants = + await workspaceDataSource?.transaction( + async (transactionManager: EntityManager) => { + const { messageExternalIdsAndIdsMap, createdMessages } = + await this.messageService.saveMessagesWithinTransaction( + messagesToSave, + messageChannel.id, + transactionManager, + ); + + const participantsWithMessageId: (ParticipantWithMessageId & { + shouldCreateContact: boolean; + })[] = messagesToSave.flatMap((message) => { + const messageId = messageExternalIdsAndIdsMap.get( + message.externalId, + ); + + return messageId + ? message.participants.map((participant: Participant) => { + const fromHandle = + message.participants.find((p) => p.role === 'from') + ?.handle || ''; + + const isMessageSentByConnectedAccount = + handleAliases.includes(fromHandle) || + fromHandle === connectedAccount.handle; + + const isParticipantConnectedAccount = + handleAliases.includes(participant.handle) || + participant.handle === connectedAccount.handle; + + const isExcludedByNonProfessionalEmails = + messageChannel.excludeNonProfessionalEmails && + !isWorkEmail(participant.handle); + + const isExcludedByGroupEmails = + messageChannel.excludeGroupEmails && + isGroupEmail(participant.handle); + + const shouldCreateContact = + !!participant.handle && + !isParticipantConnectedAccount && + !isExcludedByNonProfessionalEmails && + !isExcludedByGroupEmails && + (messageChannel.contactAutoCreationPolicy === + MessageChannelContactAutoCreationPolicy.SENT_AND_RECEIVED || + (messageChannel.contactAutoCreationPolicy === + MessageChannelContactAutoCreationPolicy.SENT && + isMessageSentByConnectedAccount)); + + return { + ...participant, + messageId, + shouldCreateContact, + }; + }) + : []; + }); + + await this.messageParticipantService.saveMessageParticipants( + participantsWithMessageId, transactionManager, ); - const participantsWithMessageId: (ParticipantWithMessageId & { - shouldCreateContact: boolean; - })[] = messagesToSave.flatMap((message) => { - const messageId = messageExternalIdsAndIdsMap.get(message.externalId); + return { participantsWithMessageId, createdMessages }; + }, + ); - return messageId - ? message.participants.map((participant: Participant) => { - const fromHandle = - message.participants.find((p) => p.role === 'from')?.handle || - ''; - - const isMessageSentByConnectedAccount = - handleAliases.includes(fromHandle) || - fromHandle === connectedAccount.handle; - - const isParticipantConnectedAccount = - handleAliases.includes(participant.handle) || - participant.handle === connectedAccount.handle; - - const isExcludedByNonProfessionalEmails = - messageChannel.excludeNonProfessionalEmails && - !isWorkEmail(participant.handle); - - const isExcludedByGroupEmails = - messageChannel.excludeGroupEmails && - isGroupEmail(participant.handle); - - const shouldCreateContact = - !!participant.handle && - !isParticipantConnectedAccount && - !isExcludedByNonProfessionalEmails && - !isExcludedByGroupEmails && - (messageChannel.contactAutoCreationPolicy === - MessageChannelContactAutoCreationPolicy.SENT_AND_RECEIVED || - (messageChannel.contactAutoCreationPolicy === - MessageChannelContactAutoCreationPolicy.SENT && - isMessageSentByConnectedAccount)); - - return { - ...participant, - messageId, - shouldCreateContact, - }; - }) - : []; - }); - - await this.messageParticipantService.saveMessageParticipants( - participantsWithMessageId, - transactionManager, - ); - - return participantsWithMessageId; - }, - ); + const { participantsWithMessageId, createdMessages } = + createdMessagesWithParticipants; const messageMetadata = await this.objectMetadataRepository.findOneOrFail({ where: { nameSingular: 'message', workspaceId }, @@ -125,9 +131,9 @@ export class MessagingSaveMessagesAndEnqueueContactCreationService { this.workspaceEventEmitter.emitDatabaseBatchEvent({ objectMetadataNameSingular: 'message', action: DatabaseEventAction.CREATED, - events: messagesToSave.map((message) => { + events: createdMessages.map((message) => { return { - recordId: message.headerMessageId, + recordId: message.id ?? '', objectMetadata: messageMetadata, properties: { after: message,