Emit message event with actual message object (#11548)

Events were emitted with the wrong object. We were using
messageWithParticipant instead of actual message object
This commit is contained in:
Thomas Trompette
2025-04-14 13:49:19 +02:00
committed by GitHub
parent abecdbafc1
commit 69a00eaaf6
3 changed files with 95 additions and 78 deletions

View File

@ -17,7 +17,10 @@ export class MessagingMessageService {
messages: MessageWithParticipants[], messages: MessageWithParticipants[],
messageChannelId: string, messageChannelId: string,
transactionManager: EntityManager, transactionManager: EntityManager,
): Promise<Map<string, string>> { ): Promise<{
createdMessages: Partial<MessageWorkspaceEntity>[];
messageExternalIdsAndIdsMap: Map<string, string>;
}> {
const messageChannelMessageAssociationRepository = const messageChannelMessageAssociationRepository =
await this.twentyORMManager.getRepository<MessageChannelMessageAssociationWorkspaceEntity>( await this.twentyORMManager.getRepository<MessageChannelMessageAssociationWorkspaceEntity>(
'messageChannelMessageAssociation', 'messageChannelMessageAssociation',
@ -34,6 +37,7 @@ export class MessagingMessageService {
); );
const messageExternalIdsAndIdsMap = new Map<string, string>(); const messageExternalIdsAndIdsMap = new Map<string, string>();
const createdMessages: Partial<MessageWorkspaceEntity>[] = [];
for (const message of messages) { for (const message of messages) {
const existingMessageChannelMessageAssociation = const existingMessageChannelMessageAssociation =
@ -101,18 +105,18 @@ export class MessagingMessageService {
} }
const newMessageId = v4(); const newMessageId = v4();
const messageToCreate = {
id: newMessageId,
headerMessageId: message.headerMessageId,
subject: message.subject,
receivedAt: message.receivedAt,
text: message.text,
messageThreadId: newOrExistingMessageThreadId,
};
await messageRepository.insert( await messageRepository.insert(messageToCreate, transactionManager);
{
id: newMessageId, createdMessages.push(messageToCreate);
headerMessageId: message.headerMessageId,
subject: message.subject,
receivedAt: message.receivedAt,
text: message.text,
messageThreadId: newOrExistingMessageThreadId,
},
transactionManager,
);
messageExternalIdsAndIdsMap.set(message.externalId, newMessageId); messageExternalIdsAndIdsMap.set(message.externalId, newMessageId);
@ -128,6 +132,9 @@ export class MessagingMessageService {
); );
} }
return messageExternalIdsAndIdsMap; return {
createdMessages,
messageExternalIdsAndIdsMap,
};
} }
} }

View File

@ -119,12 +119,16 @@ describe('MessagingSaveMessagesAndEnqueueContactCreationService', () => {
{ {
provide: MessagingMessageService, provide: MessagingMessageService,
useValue: { useValue: {
saveMessagesWithinTransaction: jest.fn().mockResolvedValue( saveMessagesWithinTransaction: jest.fn().mockResolvedValue({
new Map([ messageExternalIdsAndIdsMap: new Map([
['message-1', 'db-message-id-1'], ['message-1', 'db-message-id-1'],
['message-2', 'db-message-id-2'], ['message-2', 'db-message-id-2'],
]), ]),
), createdMessages: [
{ id: 'db-message-id-1' },
{ id: 'db-message-id-2' },
],
}),
}, },
}, },
{ {

View File

@ -53,70 +53,76 @@ export class MessagingSaveMessagesAndEnqueueContactCreationService {
const workspaceDataSource = await this.twentyORMManager.getDatasource(); const workspaceDataSource = await this.twentyORMManager.getDatasource();
const participantsWithMessageId = await workspaceDataSource?.transaction( const createdMessagesWithParticipants =
async (transactionManager: EntityManager) => { await workspaceDataSource?.transaction(
const messageExternalIdsAndIdsMap = async (transactionManager: EntityManager) => {
await this.messageService.saveMessagesWithinTransaction( const { messageExternalIdsAndIdsMap, createdMessages } =
messagesToSave, await this.messageService.saveMessagesWithinTransaction(
messageChannel.id, 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, transactionManager,
); );
const participantsWithMessageId: (ParticipantWithMessageId & { return { participantsWithMessageId, createdMessages };
shouldCreateContact: boolean; },
})[] = messagesToSave.flatMap((message) => { );
const messageId = messageExternalIdsAndIdsMap.get(message.externalId);
return messageId const { participantsWithMessageId, createdMessages } =
? message.participants.map((participant: Participant) => { createdMessagesWithParticipants;
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 messageMetadata = await this.objectMetadataRepository.findOneOrFail({ const messageMetadata = await this.objectMetadataRepository.findOneOrFail({
where: { nameSingular: 'message', workspaceId }, where: { nameSingular: 'message', workspaceId },
@ -125,9 +131,9 @@ export class MessagingSaveMessagesAndEnqueueContactCreationService {
this.workspaceEventEmitter.emitDatabaseBatchEvent({ this.workspaceEventEmitter.emitDatabaseBatchEvent({
objectMetadataNameSingular: 'message', objectMetadataNameSingular: 'message',
action: DatabaseEventAction.CREATED, action: DatabaseEventAction.CREATED,
events: messagesToSave.map((message) => { events: createdMessages.map((message) => {
return { return {
recordId: message.headerMessageId, recordId: message.id ?? '',
objectMetadata: messageMetadata, objectMetadata: messageMetadata,
properties: { properties: {
after: message, after: message,