5901 refactor email and calendar auto contact creation to create them by batch (#6038)

Closes #5901
This commit is contained in:
bosiraphael 2024-06-27 16:37:34 +02:00 committed by GitHub
parent d1bb0fb822
commit 4f9527c860
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 86 additions and 54 deletions

View File

@ -59,6 +59,7 @@
"@types/dompurify": "^3.0.5", "@types/dompurify": "^3.0.5",
"@types/facepaint": "^1.2.5", "@types/facepaint": "^1.2.5",
"@types/lodash.camelcase": "^4.3.7", "@types/lodash.camelcase": "^4.3.7",
"@types/lodash.chunk": "^4.2.9",
"@types/lodash.merge": "^4.6.7", "@types/lodash.merge": "^4.6.7",
"@types/lodash.pick": "^4.3.7", "@types/lodash.pick": "^4.3.7",
"@types/nodemailer": "^6.4.14", "@types/nodemailer": "^6.4.14",
@ -114,6 +115,7 @@
"jsonwebtoken": "^9.0.0", "jsonwebtoken": "^9.0.0",
"libphonenumber-js": "^1.10.26", "libphonenumber-js": "^1.10.26",
"lodash.camelcase": "^4.3.0", "lodash.camelcase": "^4.3.0",
"lodash.chunk": "^4.2.0",
"lodash.compact": "^3.0.1", "lodash.compact": "^3.0.1",
"lodash.debounce": "^4.0.8", "lodash.debounce": "^4.0.8",
"lodash.groupby": "^4.6.0", "lodash.groupby": "^4.6.0",

View File

@ -11,6 +11,7 @@ import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/works
import { CalendarEventParticipantModule } from 'src/modules/calendar/services/calendar-event-participant/calendar-event-participant.module'; import { CalendarEventParticipantModule } from 'src/modules/calendar/services/calendar-event-participant/calendar-event-participant.module';
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { MessagingCommonModule } from 'src/modules/messaging/common/messaging-common.module'; import { MessagingCommonModule } from 'src/modules/messaging/common/messaging-common.module';
import { AutoCompaniesAndContactsCreationMessageChannelListener } from 'src/modules/connected-account/auto-companies-and-contacts-creation/listeners/auto-companies-and-contacts-creation-message-channel.listener';
@Module({ @Module({
imports: [ imports: [
@ -25,7 +26,10 @@ import { MessagingCommonModule } from 'src/modules/messaging/common/messaging-co
CalendarEventParticipantModule, CalendarEventParticipantModule,
TypeOrmModule.forFeature([FeatureFlagEntity], 'core'), TypeOrmModule.forFeature([FeatureFlagEntity], 'core'),
], ],
providers: [CreateCompanyAndContactService], providers: [
CreateCompanyAndContactService,
AutoCompaniesAndContactsCreationMessageChannelListener,
],
exports: [CreateCompanyAndContactService], exports: [CreateCompanyAndContactService],
}) })
export class AutoCompaniesAndContactsCreationModule {} export class AutoCompaniesAndContactsCreationModule {}

View File

@ -13,9 +13,9 @@ import {
} from 'src/modules/messaging/message-participants-manager/jobs/messaging-create-company-and-contact-after-sync.job'; } from 'src/modules/messaging/message-participants-manager/jobs/messaging-create-company-and-contact-after-sync.job';
@Injectable() @Injectable()
export class MessagingMessageChannelListener { export class AutoCompaniesAndContactsCreationMessageChannelListener {
constructor( constructor(
@InjectMessageQueue(MessageQueue.messagingQueue) @InjectMessageQueue(MessageQueue.contactCreationQueue)
private readonly messageQueueService: MessageQueueService, private readonly messageQueueService: MessageQueueService,
) {} ) {}

View File

@ -3,6 +3,7 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
import { EntityManager } from 'typeorm'; import { EntityManager } from 'typeorm';
import compact from 'lodash.compact'; import compact from 'lodash.compact';
import chunk from 'lodash.chunk';
import { getDomainNameFromHandle } from 'src/modules/calendar-messaging-participant/utils/get-domain-name-from-handle.util'; import { getDomainNameFromHandle } from 'src/modules/calendar-messaging-participant/utils/get-domain-name-from-handle.util';
import { CreateCompanyService } from 'src/modules/connected-account/auto-companies-and-contacts-creation/create-company/create-company.service'; import { CreateCompanyService } from 'src/modules/connected-account/auto-companies-and-contacts-creation/create-company/create-company.service';
@ -14,7 +15,6 @@ import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repos
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity'; import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
import { getUniqueContactsAndHandles } from 'src/modules/connected-account/auto-companies-and-contacts-creation/utils/get-unique-contacts-and-handles.util'; import { getUniqueContactsAndHandles } from 'src/modules/connected-account/auto-companies-and-contacts-creation/utils/get-unique-contacts-and-handles.util';
import { Contacts } from 'src/modules/connected-account/auto-companies-and-contacts-creation/types/contact.type';
import { CalendarEventParticipantService } from 'src/modules/calendar/services/calendar-event-participant/calendar-event-participant.service'; import { CalendarEventParticipantService } from 'src/modules/calendar/services/calendar-event-participant/calendar-event-participant.service';
import { filterOutContactsFromCompanyOrWorkspace } from 'src/modules/connected-account/auto-companies-and-contacts-creation/utils/filter-out-contacts-from-company-or-workspace.util'; import { filterOutContactsFromCompanyOrWorkspace } from 'src/modules/connected-account/auto-companies-and-contacts-creation/utils/filter-out-contacts-from-company-or-workspace.util';
import { MessagingMessageParticipantService } from 'src/modules/messaging/common/services/messaging-message-participant.service'; import { MessagingMessageParticipantService } from 'src/modules/messaging/common/services/messaging-message-participant.service';
@ -23,6 +23,8 @@ import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/st
import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource'; import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
import { InjectWorkspaceDatasource } from 'src/engine/twenty-orm/decorators/inject-workspace-datasource.decorator'; import { InjectWorkspaceDatasource } from 'src/engine/twenty-orm/decorators/inject-workspace-datasource.decorator';
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity'; import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { CONTACTS_CREATION_BATCH_SIZE } from 'src/modules/connected-account/auto-companies-and-contacts-creation/constants/contacts-creation-batch-size.constant';
import { Contact } from 'src/modules/connected-account/auto-companies-and-contacts-creation/types/contact.type';
@Injectable() @Injectable()
export class CreateCompanyAndContactService { export class CreateCompanyAndContactService {
@ -42,7 +44,7 @@ export class CreateCompanyAndContactService {
async createCompaniesAndPeople( async createCompaniesAndPeople(
connectedAccountHandle: string, connectedAccountHandle: string,
contactsToCreate: Contacts, contactsToCreate: Contact[],
workspaceId: string, workspaceId: string,
transactionManager?: EntityManager, transactionManager?: EntityManager,
): Promise<PersonWorkspaceEntity[]> { ): Promise<PersonWorkspaceEntity[]> {
@ -132,48 +134,55 @@ export class CreateCompanyAndContactService {
async createCompaniesAndContactsAndUpdateParticipants( async createCompaniesAndContactsAndUpdateParticipants(
connectedAccount: ConnectedAccountWorkspaceEntity, connectedAccount: ConnectedAccountWorkspaceEntity,
contactsToCreate: Contacts, contactsToCreate: Contact[],
workspaceId: string, workspaceId: string,
) { ) {
let updatedMessageParticipants: MessageParticipantWorkspaceEntity[] = []; const contactsBatches = chunk(
let updatedCalendarEventParticipants: CalendarEventParticipantWorkspaceEntity[] = contactsToCreate,
[]; CONTACTS_CREATION_BATCH_SIZE,
await this.workspaceDataSource?.transaction(
async (transactionManager: EntityManager) => {
const createdPeople = await this.createCompaniesAndPeople(
connectedAccount.handle,
contactsToCreate,
workspaceId,
transactionManager,
);
updatedMessageParticipants =
await this.messageParticipantService.updateMessageParticipantsAfterPeopleCreation(
createdPeople,
workspaceId,
transactionManager,
);
updatedCalendarEventParticipants =
await this.calendarEventParticipantService.updateCalendarEventParticipantsAfterPeopleCreation(
createdPeople,
workspaceId,
transactionManager,
);
},
); );
this.eventEmitter.emit(`messageParticipant.matched`, { for (const contactsBatch of contactsBatches) {
workspaceId, let updatedMessageParticipants: MessageParticipantWorkspaceEntity[] = [];
workspaceMemberId: connectedAccount.accountOwnerId, let updatedCalendarEventParticipants: CalendarEventParticipantWorkspaceEntity[] =
messageParticipants: updatedMessageParticipants, [];
});
this.eventEmitter.emit(`calendarEventParticipant.matched`, { await this.workspaceDataSource?.transaction(
workspaceId, async (transactionManager: EntityManager) => {
workspaceMemberId: connectedAccount.accountOwnerId, const createdPeople = await this.createCompaniesAndPeople(
calendarEventParticipants: updatedCalendarEventParticipants, connectedAccount.handle,
}); contactsBatch,
workspaceId,
transactionManager,
);
updatedMessageParticipants =
await this.messageParticipantService.updateMessageParticipantsAfterPeopleCreation(
createdPeople,
workspaceId,
transactionManager,
);
updatedCalendarEventParticipants =
await this.calendarEventParticipantService.updateCalendarEventParticipantsAfterPeopleCreation(
createdPeople,
workspaceId,
transactionManager,
);
},
);
this.eventEmitter.emit(`messageParticipant.matched`, {
workspaceId,
workspaceMemberId: connectedAccount.accountOwnerId,
messageParticipants: updatedMessageParticipants,
});
this.eventEmitter.emit(`calendarEventParticipant.matched`, {
workspaceId,
workspaceMemberId: connectedAccount.accountOwnerId,
calendarEventParticipants: updatedCalendarEventParticipants,
});
}
} }
} }

View File

@ -2,5 +2,3 @@ export type Contact = {
handle: string; handle: string;
displayName: string; displayName: string;
}; };
export type Contacts = Contact[];

View File

@ -1,9 +1,9 @@
import { Contacts } from 'src/modules/connected-account/auto-companies-and-contacts-creation/types/contact.type'; import { Contact } from 'src/modules/connected-account/auto-companies-and-contacts-creation/types/contact.type';
import { getUniqueContactsAndHandles } from 'src/modules/connected-account/auto-companies-and-contacts-creation/utils/get-unique-contacts-and-handles.util'; import { getUniqueContactsAndHandles } from 'src/modules/connected-account/auto-companies-and-contacts-creation/utils/get-unique-contacts-and-handles.util';
describe('getUniqueContactsAndHandles', () => { describe('getUniqueContactsAndHandles', () => {
it('should return empty arrays when contacts is empty', () => { it('should return empty arrays when contacts is empty', () => {
const contacts: Contacts = []; const contacts: Contact[] = [];
const result = getUniqueContactsAndHandles(contacts); const result = getUniqueContactsAndHandles(contacts);
expect(result.uniqueContacts).toEqual([]); expect(result.uniqueContacts).toEqual([]);
@ -11,7 +11,7 @@ describe('getUniqueContactsAndHandles', () => {
}); });
it('should return unique contacts and handles', () => { it('should return unique contacts and handles', () => {
const contacts: Contacts = [ const contacts: Contact[] = [
{ handle: 'john@twenty.com', displayName: 'John Doe' }, { handle: 'john@twenty.com', displayName: 'John Doe' },
{ handle: 'john@twenty.com', displayName: 'John Doe' }, { handle: 'john@twenty.com', displayName: 'John Doe' },
{ handle: 'jane@twenty.com', displayName: 'Jane Smith' }, { handle: 'jane@twenty.com', displayName: 'Jane Smith' },

View File

@ -1,12 +1,12 @@
import { getDomainNameFromHandle } from 'src/modules/calendar-messaging-participant/utils/get-domain-name-from-handle.util'; import { getDomainNameFromHandle } from 'src/modules/calendar-messaging-participant/utils/get-domain-name-from-handle.util';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
import { Contacts } from 'src/modules/connected-account/auto-companies-and-contacts-creation/types/contact.type'; import { Contact } from 'src/modules/connected-account/auto-companies-and-contacts-creation/types/contact.type';
export function filterOutContactsFromCompanyOrWorkspace( export function filterOutContactsFromCompanyOrWorkspace(
contacts: Contacts, contacts: Contact[],
selfHandle: string, selfHandle: string,
workspaceMembers: WorkspaceMemberWorkspaceEntity[], workspaceMembers: WorkspaceMemberWorkspaceEntity[],
): Contacts { ): Contact[] {
const selfDomainName = getDomainNameFromHandle(selfHandle); const selfDomainName = getDomainNameFromHandle(selfHandle);
const workspaceMembersMap = workspaceMembers.reduce( const workspaceMembersMap = workspaceMembers.reduce(

View File

@ -1,10 +1,10 @@
import uniq from 'lodash.uniq'; import uniq from 'lodash.uniq';
import uniqBy from 'lodash.uniqby'; import uniqBy from 'lodash.uniqby';
import { Contacts } from 'src/modules/connected-account/auto-companies-and-contacts-creation/types/contact.type'; import { Contact } from 'src/modules/connected-account/auto-companies-and-contacts-creation/types/contact.type';
export function getUniqueContactsAndHandles(contacts: Contacts): { export function getUniqueContactsAndHandles(contacts: Contact[]): {
uniqueContacts: Contacts; uniqueContacts: Contact[];
uniqueHandles: string[]; uniqueHandles: string[];
} { } {
if (contacts.length === 0) { if (contacts.length === 0) {

View File

@ -17265,6 +17265,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/lodash.chunk@npm:^4.2.9":
version: 4.2.9
resolution: "@types/lodash.chunk@npm:4.2.9"
dependencies:
"@types/lodash": "npm:*"
checksum: 5759b3d969c5db4b0893b70261ae40d4b9a6466c984c16de6fa1d3945b3199cc09f948a444a3b4e6cfa0dd984044cf937cbc8dab5fe0ac8da67244ed74d9e4e4
languageName: node
linkType: hard
"@types/lodash.compact@npm:^3.0.9": "@types/lodash.compact@npm:^3.0.9":
version: 3.0.9 version: 3.0.9
resolution: "@types/lodash.compact@npm:3.0.9" resolution: "@types/lodash.compact@npm:3.0.9"
@ -34530,6 +34539,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"lodash.chunk@npm:^4.2.0":
version: 4.2.0
resolution: "lodash.chunk@npm:4.2.0"
checksum: f9f99969561ad2f62af1f9a96c5bd0af776f000292b0d8db3126c28eb3b32e210d7c31b49c18d0d7901869bd769057046dc134b60cfa0c2c4ce017823a26bb23
languageName: node
linkType: hard
"lodash.clonedeep@npm:^4.5.0": "lodash.clonedeep@npm:^4.5.0":
version: 4.5.0 version: 4.5.0
resolution: "lodash.clonedeep@npm:4.5.0" resolution: "lodash.clonedeep@npm:4.5.0"
@ -47351,6 +47367,7 @@ __metadata:
"@types/jest": "npm:^29.5.11" "@types/jest": "npm:^29.5.11"
"@types/js-cookie": "npm:^3.0.3" "@types/js-cookie": "npm:^3.0.3"
"@types/lodash.camelcase": "npm:^4.3.7" "@types/lodash.camelcase": "npm:^4.3.7"
"@types/lodash.chunk": "npm:^4.2.9"
"@types/lodash.compact": "npm:^3.0.9" "@types/lodash.compact": "npm:^3.0.9"
"@types/lodash.debounce": "npm:^4.0.7" "@types/lodash.debounce": "npm:^4.0.7"
"@types/lodash.groupby": "npm:^4.6.9" "@types/lodash.groupby": "npm:^4.6.9"
@ -47462,6 +47479,7 @@ __metadata:
jsonwebtoken: "npm:^9.0.0" jsonwebtoken: "npm:^9.0.0"
libphonenumber-js: "npm:^1.10.26" libphonenumber-js: "npm:^1.10.26"
lodash.camelcase: "npm:^4.3.0" lodash.camelcase: "npm:^4.3.0"
lodash.chunk: "npm:^4.2.0"
lodash.compact: "npm:^3.0.1" lodash.compact: "npm:^3.0.1"
lodash.debounce: "npm:^4.0.8" lodash.debounce: "npm:^4.0.8"
lodash.groupby: "npm:^4.6.0" lodash.groupby: "npm:^4.6.0"