6619 modify event emitter to emit an array of events (#6625)

Closes #6619

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Raphaël Bosi
2024-08-20 19:44:29 +02:00
committed by GitHub
parent 17a1760afd
commit 091c0f83be
41 changed files with 1005 additions and 722 deletions

View File

@ -9,6 +9,7 @@ import { MessageQueue } from 'src/engine/integrations/message-queue/message-queu
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/workspace-event.type';
import { BlocklistWorkspaceEntity } from 'src/modules/blocklist/standard-objects/blocklist.workspace-entity';
import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
@ -32,90 +33,109 @@ export class MessagingBlocklistListener {
@OnEvent('blocklist.created')
async handleCreatedEvent(
payload: ObjectRecordCreateEvent<BlocklistWorkspaceEntity>,
payload: WorkspaceEventBatch<
ObjectRecordCreateEvent<BlocklistWorkspaceEntity>
>,
) {
await this.messageQueueService.add<BlocklistItemDeleteMessagesJobData>(
BlocklistItemDeleteMessagesJob.name,
{
workspaceId: payload.workspaceId,
blocklistItemId: payload.recordId,
},
await Promise.all(
payload.events.map((eventPayload) =>
// TODO: modify to pass an array of blocklist items
this.messageQueueService.add<BlocklistItemDeleteMessagesJobData>(
BlocklistItemDeleteMessagesJob.name,
{
workspaceId: payload.workspaceId,
blocklistItemId: eventPayload.recordId,
},
),
),
);
}
@OnEvent('blocklist.deleted')
async handleDeletedEvent(
payload: ObjectRecordDeleteEvent<BlocklistWorkspaceEntity>,
payload: WorkspaceEventBatch<
ObjectRecordDeleteEvent<BlocklistWorkspaceEntity>
>,
) {
const workspaceMemberId = payload.properties.before.workspaceMember.id;
const workspaceId = payload.workspaceId;
const connectedAccount =
await this.connectedAccountRepository.getAllByWorkspaceMemberId(
workspaceMemberId,
for (const eventPayload of payload.events) {
const workspaceMemberId =
eventPayload.properties.before.workspaceMember.id;
const connectedAccount =
await this.connectedAccountRepository.getAllByWorkspaceMemberId(
workspaceMemberId,
workspaceId,
);
if (!connectedAccount || connectedAccount.length === 0) {
return;
}
const messageChannelRepository =
await this.twentyORMManager.getRepository<MessageChannelWorkspaceEntity>(
'messageChannel',
);
const messageChannel = await messageChannelRepository.findOneOrFail({
where: {
connectedAccountId: connectedAccount[0].id,
},
});
await this.messagingChannelSyncStatusService.resetAndScheduleFullMessageListFetch(
messageChannel.id,
workspaceId,
);
if (!connectedAccount || connectedAccount.length === 0) {
return;
}
const messageChannelRepository =
await this.twentyORMManager.getRepository<MessageChannelWorkspaceEntity>(
'messageChannel',
);
const messageChannel = await messageChannelRepository.findOneOrFail({
where: {
connectedAccountId: connectedAccount[0].id,
},
});
await this.messagingChannelSyncStatusService.resetAndScheduleFullMessageListFetch(
messageChannel.id,
workspaceId,
);
}
@OnEvent('blocklist.updated')
async handleUpdatedEvent(
payload: ObjectRecordUpdateEvent<BlocklistWorkspaceEntity>,
payload: WorkspaceEventBatch<
ObjectRecordUpdateEvent<BlocklistWorkspaceEntity>
>,
) {
const workspaceMemberId = payload.properties.before.workspaceMember.id;
const workspaceId = payload.workspaceId;
await this.messageQueueService.add<BlocklistItemDeleteMessagesJobData>(
BlocklistItemDeleteMessagesJob.name,
{
workspaceId,
blocklistItemId: payload.recordId,
},
);
for (const eventPayload of payload.events) {
const workspaceMemberId =
eventPayload.properties.before.workspaceMember.id;
const connectedAccount =
await this.connectedAccountRepository.getAllByWorkspaceMemberId(
workspaceMemberId,
workspaceId,
await this.messageQueueService.add<BlocklistItemDeleteMessagesJobData>(
BlocklistItemDeleteMessagesJob.name,
{
workspaceId,
blocklistItemId: eventPayload.recordId,
},
);
if (!connectedAccount || connectedAccount.length === 0) {
return;
const connectedAccount =
await this.connectedAccountRepository.getAllByWorkspaceMemberId(
workspaceMemberId,
workspaceId,
);
if (!connectedAccount || connectedAccount.length === 0) {
continue;
}
const messageChannelRepository =
await this.twentyORMManager.getRepository<MessageChannelWorkspaceEntity>(
'messageChannel',
);
const messageChannel = await messageChannelRepository.findOneOrFail({
where: {
connectedAccountId: connectedAccount[0].id,
},
});
await this.messagingChannelSyncStatusService.resetAndScheduleFullMessageListFetch(
messageChannel.id,
workspaceId,
);
}
const messageChannelRepository =
await this.twentyORMManager.getRepository<MessageChannelWorkspaceEntity>(
'messageChannel',
);
const messageChannel = await messageChannelRepository.findOneOrFail({
where: {
connectedAccountId: connectedAccount[0].id,
},
});
await this.messagingChannelSyncStatusService.resetAndScheduleFullMessageListFetch(
messageChannel.id,
workspaceId,
);
}
}

View File

@ -2,46 +2,39 @@ import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { ObjectRecordDeleteEvent } from 'src/engine/integrations/event-emitter/types/object-record-delete.event';
import { InjectMessageQueue } from 'src/engine/integrations/message-queue/decorators/message-queue.decorator';
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/workspace-event.type';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import {
MessagingConnectedAccountDeletionCleanupJob,
MessagingConnectedAccountDeletionCleanupJobData,
} from 'src/modules/messaging/message-cleaner/jobs/messaging-connected-account-deletion-cleanup.job';
import { InjectMessageQueue } from 'src/engine/integrations/message-queue/decorators/message-queue.decorator';
import {
DeleteConnectedAccountAssociatedCalendarDataJobData,
DeleteConnectedAccountAssociatedCalendarDataJob,
} from 'src/modules/calendar/calendar-event-cleaner/jobs/delete-connected-account-associated-calendar-data.job';
@Injectable()
export class MessagingMessageCleanerConnectedAccountListener {
constructor(
@InjectMessageQueue(MessageQueue.messagingQueue)
private readonly messageQueueService: MessageQueueService,
@InjectMessageQueue(MessageQueue.calendarQueue)
private readonly calendarQueueService: MessageQueueService,
) {}
@OnEvent('connectedAccount.deleted')
async handleDeletedEvent(
payload: ObjectRecordDeleteEvent<ConnectedAccountWorkspaceEntity>,
payload: WorkspaceEventBatch<
ObjectRecordDeleteEvent<ConnectedAccountWorkspaceEntity>
>,
) {
await this.messageQueueService.add<MessagingConnectedAccountDeletionCleanupJobData>(
MessagingConnectedAccountDeletionCleanupJob.name,
{
workspaceId: payload.workspaceId,
connectedAccountId: payload.recordId,
},
);
await this.calendarQueueService.add<DeleteConnectedAccountAssociatedCalendarDataJobData>(
DeleteConnectedAccountAssociatedCalendarDataJob.name,
{
workspaceId: payload.workspaceId,
connectedAccountId: payload.recordId,
},
await Promise.all(
payload.events.map((eventPayload) =>
this.messageQueueService.add<MessagingConnectedAccountDeletionCleanupJobData>(
MessagingConnectedAccountDeletionCleanupJob.name,
{
workspaceId: payload.workspaceId,
connectedAccountId: eventPayload.recordId,
},
),
),
);
}
}

View File

@ -1,19 +1,11 @@
import { Module } from '@nestjs/common';
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
import { MessageThreadWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-thread.workspace-entity';
import { MessageWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message.workspace-entity';
import { MessagingConnectedAccountDeletionCleanupJob } from 'src/modules/messaging/message-cleaner/jobs/messaging-connected-account-deletion-cleanup.job';
import { MessagingMessageCleanerConnectedAccountListener } from 'src/modules/messaging/message-cleaner/listeners/messaging-message-cleaner-connected-account.listener';
import { MessagingMessageCleanerService } from 'src/modules/messaging/message-cleaner/services/messaging-message-cleaner.service';
@Module({
imports: [
TwentyORMModule.forFeature([
MessageWorkspaceEntity,
MessageThreadWorkspaceEntity,
]),
],
imports: [],
providers: [
MessagingMessageCleanerService,
MessagingConnectedAccountDeletionCleanupJob,

View File

@ -2,9 +2,10 @@ import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { ObjectRecordDeleteEvent } from 'src/engine/integrations/event-emitter/types/object-record-delete.event';
import { InjectMessageQueue } from 'src/engine/integrations/message-queue/decorators/message-queue.decorator';
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';
import { InjectMessageQueue } from 'src/engine/integrations/message-queue/decorators/message-queue.decorator';
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/workspace-event.type';
import { MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
import {
MessagingCleanCacheJob,
@ -20,14 +21,20 @@ export class MessagingMessageImportManagerMessageChannelListener {
@OnEvent('messageChannel.deleted')
async handleDeletedEvent(
payload: ObjectRecordDeleteEvent<MessageChannelWorkspaceEntity>,
payload: WorkspaceEventBatch<
ObjectRecordDeleteEvent<MessageChannelWorkspaceEntity>
>,
) {
await this.messageQueueService.add<MessagingCleanCacheJobData>(
MessagingCleanCacheJob.name,
{
workspaceId: payload.workspaceId,
messageChannelId: payload.recordId,
},
await Promise.all(
payload.events.map((eventPayload) =>
this.messageQueueService.add<MessagingCleanCacheJobData>(
MessagingCleanCacheJob.name,
{
workspaceId: payload.workspaceId,
messageChannelId: eventPayload.recordId,
},
),
),
);
}
}

View File

@ -7,13 +7,14 @@ import { objectRecordChangedProperties as objectRecordUpdateEventChangedProperti
import { InjectMessageQueue } from 'src/engine/integrations/message-queue/decorators/message-queue.decorator';
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/workspace-event.type';
import {
MessageParticipantMatchParticipantJobData,
MessageParticipantMatchParticipantJob,
MessageParticipantMatchParticipantJobData,
} from 'src/modules/messaging/message-participant-manager/jobs/message-participant-match-participant.job';
import {
MessageParticipantUnmatchParticipantJobData,
MessageParticipantUnmatchParticipantJob,
MessageParticipantUnmatchParticipantJobData,
} from 'src/modules/messaging/message-participant-manager/jobs/message-participant-unmatch-participant.job';
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
@ -26,49 +27,57 @@ export class MessageParticipantPersonListener {
@OnEvent('person.created')
async handleCreatedEvent(
payload: ObjectRecordCreateEvent<PersonWorkspaceEntity>,
payload: WorkspaceEventBatch<
ObjectRecordCreateEvent<PersonWorkspaceEntity>
>,
) {
if (payload.properties.after.email === null) {
return;
}
await this.messageQueueService.add<MessageParticipantMatchParticipantJobData>(
MessageParticipantMatchParticipantJob.name,
{
workspaceId: payload.workspaceId,
email: payload.properties.after.email,
personId: payload.recordId,
},
);
}
@OnEvent('person.updated')
async handleUpdatedEvent(
payload: ObjectRecordUpdateEvent<PersonWorkspaceEntity>,
) {
if (
objectRecordUpdateEventChangedProperties(
payload.properties.before,
payload.properties.after,
).includes('email')
) {
await this.messageQueueService.add<MessageParticipantUnmatchParticipantJobData>(
MessageParticipantUnmatchParticipantJob.name,
{
workspaceId: payload.workspaceId,
email: payload.properties.before.email,
personId: payload.recordId,
},
);
for (const eventPayload of payload.events) {
if (eventPayload.properties.after.email === null) {
continue;
}
await this.messageQueueService.add<MessageParticipantMatchParticipantJobData>(
MessageParticipantMatchParticipantJob.name,
{
workspaceId: payload.workspaceId,
email: payload.properties.after.email,
personId: payload.recordId,
email: eventPayload.properties.after.email,
personId: eventPayload.recordId,
},
);
}
}
@OnEvent('person.updated')
async handleUpdatedEvent(
payload: WorkspaceEventBatch<
ObjectRecordUpdateEvent<PersonWorkspaceEntity>
>,
) {
for (const eventPayload of payload.events) {
if (
objectRecordUpdateEventChangedProperties(
eventPayload.properties.before,
eventPayload.properties.after,
).includes('email')
) {
await this.messageQueueService.add<MessageParticipantUnmatchParticipantJobData>(
MessageParticipantUnmatchParticipantJob.name,
{
workspaceId: payload.workspaceId,
email: eventPayload.properties.before.email,
personId: eventPayload.recordId,
},
);
await this.messageQueueService.add<MessageParticipantMatchParticipantJobData>(
MessageParticipantMatchParticipantJob.name,
{
workspaceId: payload.workspaceId,
email: eventPayload.properties.after.email,
personId: eventPayload.recordId,
},
);
}
}
}
}

View File

@ -14,6 +14,7 @@ import { objectRecordChangedProperties as objectRecordUpdateEventChangedProperti
import { InjectMessageQueue } from 'src/engine/integrations/message-queue/decorators/message-queue.decorator';
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/workspace-event.type';
import {
MessageParticipantMatchParticipantJob,
MessageParticipantMatchParticipantJobData,
@ -35,7 +36,9 @@ export class MessageParticipantWorkspaceMemberListener {
@OnEvent('workspaceMember.created')
async handleCreatedEvent(
payload: ObjectRecordCreateEvent<WorkspaceMemberWorkspaceEntity>,
payload: WorkspaceEventBatch<
ObjectRecordCreateEvent<WorkspaceMemberWorkspaceEntity>
>,
) {
const workspace = await this.workspaceRepository.findOneBy({
id: payload.workspaceId,
@ -48,47 +51,53 @@ export class MessageParticipantWorkspaceMemberListener {
return;
}
if (payload.properties.after.userEmail === null) {
return;
}
await this.messageQueueService.add<MessageParticipantMatchParticipantJobData>(
MessageParticipantMatchParticipantJob.name,
{
workspaceId: payload.workspaceId,
email: payload.properties.after.userEmail,
workspaceMemberId: payload.properties.after.id,
},
);
}
@OnEvent('workspaceMember.updated')
async handleUpdatedEvent(
payload: ObjectRecordUpdateEvent<WorkspaceMemberWorkspaceEntity>,
) {
if (
objectRecordUpdateEventChangedProperties<WorkspaceMemberWorkspaceEntity>(
payload.properties.before,
payload.properties.after,
).includes('userEmail')
) {
await this.messageQueueService.add<MessageParticipantUnmatchParticipantJobData>(
MessageParticipantUnmatchParticipantJob.name,
{
workspaceId: payload.workspaceId,
email: payload.properties.before.userEmail,
personId: payload.recordId,
},
);
for (const eventPayload of payload.events) {
if (eventPayload.properties.after.userEmail === null) {
continue;
}
await this.messageQueueService.add<MessageParticipantMatchParticipantJobData>(
MessageParticipantMatchParticipantJob.name,
{
workspaceId: payload.workspaceId,
email: payload.properties.after.userEmail,
workspaceMemberId: payload.recordId,
email: eventPayload.properties.after.userEmail,
workspaceMemberId: eventPayload.recordId,
},
);
}
}
@OnEvent('workspaceMember.updated')
async handleUpdatedEvent(
payload: WorkspaceEventBatch<
ObjectRecordUpdateEvent<WorkspaceMemberWorkspaceEntity>
>,
) {
for (const eventPayload of payload.events) {
if (
objectRecordUpdateEventChangedProperties<WorkspaceMemberWorkspaceEntity>(
eventPayload.properties.before,
eventPayload.properties.after,
).includes('userEmail')
) {
await this.messageQueueService.add<MessageParticipantUnmatchParticipantJobData>(
MessageParticipantUnmatchParticipantJob.name,
{
workspaceId: payload.workspaceId,
email: eventPayload.properties.before.userEmail,
personId: eventPayload.recordId,
},
);
await this.messageQueueService.add<MessageParticipantMatchParticipantJobData>(
MessageParticipantMatchParticipantJob.name,
{
workspaceId: payload.workspaceId,
email: eventPayload.properties.after.userEmail,
workspaceMemberId: eventPayload.recordId,
},
);
}
}
}
}

View File

@ -7,6 +7,7 @@ import { Repository } from 'typeorm';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/workspace-event.type';
import { MessageParticipantWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-participant.workspace-entity';
import { TimelineActivityRepository } from 'src/modules/timeline/repositiories/timeline-activity.repository';
import { TimelineActivityWorkspaceEntity } from 'src/modules/timeline/standard-objects/timeline-activity.workspace-entity';
@ -22,50 +23,54 @@ export class MessageParticipantListener {
) {}
@OnEvent('messageParticipant.matched')
public async handleMessageParticipantMatched(payload: {
workspaceId: string;
workspaceMemberId: string;
participants: MessageParticipantWorkspaceEntity[];
}): Promise<void> {
const messageParticipants = payload.participants ?? [];
public async handleMessageParticipantMatched(
payload: WorkspaceEventBatch<{
workspaceMemberId: string;
participants: MessageParticipantWorkspaceEntity[];
}>,
): Promise<void> {
// TODO: Refactor to insertTimelineActivitiesForObject once
for (const eventPayload of payload.events) {
const messageParticipants = eventPayload.participants ?? [];
// TODO: move to a job?
// TODO: move to a job?
const dataSourceSchema = this.workspaceDataSourceService.getSchemaName(
payload.workspaceId,
);
const dataSourceSchema = this.workspaceDataSourceService.getSchemaName(
payload.workspaceId,
);
const messageObjectMetadata =
await this.objectMetadataRepository.findOneOrFail({
where: {
nameSingular: 'message',
const messageObjectMetadata =
await this.objectMetadataRepository.findOneOrFail({
where: {
nameSingular: 'message',
workspaceId: payload.workspaceId,
},
});
const messageParticipantsWithPersonId = messageParticipants.filter(
(participant) => participant.personId,
);
if (messageParticipantsWithPersonId.length === 0) {
return;
}
await this.timelineActivityRepository.insertTimelineActivitiesForObject(
'person',
messageParticipantsWithPersonId.map((participant) => ({
dataSourceSchema,
name: 'message.linked',
properties: null,
objectName: 'message',
recordId: participant.personId,
workspaceMemberId: eventPayload.workspaceMemberId,
workspaceId: payload.workspaceId,
},
});
const messageParticipantsWithPersonId = messageParticipants.filter(
(participant) => participant.personId,
);
if (messageParticipantsWithPersonId.length === 0) {
return;
linkedObjectMetadataId: messageObjectMetadata.id,
linkedRecordId: participant.messageId,
linkedRecordCachedName: '',
})),
payload.workspaceId,
);
}
await this.timelineActivityRepository.insertTimelineActivitiesForObject(
'person',
messageParticipantsWithPersonId.map((participant) => ({
dataSourceSchema,
name: 'message.linked',
properties: null,
objectName: 'message',
recordId: participant.personId,
workspaceMemberId: payload.workspaceMemberId,
workspaceId: payload.workspaceId,
linkedObjectMetadataId: messageObjectMetadata.id,
linkedRecordId: participant.messageId,
linkedRecordCachedName: '',
})),
payload.workspaceId,
);
}
}