4398 decouple contacts and companies creation from messages import (#4590)

* emit event

* create queue and listener

* filter participants with role 'from'

* create job

* Add job to job module

* Refactoring

* Refactor contact creation in CreateCompanyAndContactService

* update job

* wip

* add getByHandlesWithoutPersonIdAndWorkspaceMemberId to calendar event attendee repository

* refactoring

* refactoring

* Revert "refactoring"

This reverts commit e5434f0b871e45447227aa8d55ba5af381c3ff1c.

* fix nest imports

* add await

* fix contact creation condition

* emit contact creation event after calendar-full-sync

* add await

* add missing transactionManager

* calendar event attendees personId update is working

* messageParticipant and calendarEventAttendee update is working as intended

* rename module

* fix lodash import

* add test

* update package.json
This commit is contained in:
bosiraphael
2024-03-22 18:44:14 +01:00
committed by GitHub
parent 1a763263c9
commit 96cad2accd
29 changed files with 580 additions and 271 deletions

View File

@ -7,7 +7,10 @@ import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/work
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
import { CalendarEventAttendeeObjectMetadata } from 'src/modules/calendar/standard-objects/calendar-event-attendee.object-metadata';
import { getFlattenedValuesAndValuesStringForBatchRawQuery } from 'src/modules/calendar/utils/getFlattenedValuesAndValuesStringForBatchRawQuery.util';
import { CalendarEventAttendee } from 'src/modules/calendar/types/calendar-event';
import {
CalendarEventAttendee,
CalendarEventAttendeeWithId,
} from 'src/modules/calendar/types/calendar-event';
@Injectable()
export class CalendarEventAttendeeRepository {
@ -172,4 +175,29 @@ export class CalendarEventAttendeeRepository {
transactionManager,
);
}
public async getWithoutPersonIdAndWorkspaceMemberId(
workspaceId: string,
transactionManager?: EntityManager,
): Promise<CalendarEventAttendeeWithId[]> {
if (!workspaceId) {
throw new Error('WorkspaceId is required');
}
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const calendarEventAttendees: CalendarEventAttendeeWithId[] =
await this.workspaceDataSourceService.executeRawQuery(
`SELECT "calendarEventAttendee".*
FROM ${dataSourceSchema}."calendarEventAttendee" AS "calendarEventAttendee"
WHERE "calendarEventAttendee"."personId" IS NULL
AND "calendarEventAttendee"."workspaceMemberId" IS NULL`,
[],
workspaceId,
transactionManager,
);
return calendarEventAttendees;
}
}

View File

@ -0,0 +1,16 @@
import { Module } from '@nestjs/common';
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { CalendarEventAttendeeService } from 'src/modules/calendar/services/calendar-event-attendee/calendar-event-attendee.service';
import { PersonObjectMetadata } from 'src/modules/person/standard-objects/person.object-metadata';
@Module({
imports: [
WorkspaceDataSourceModule,
ObjectMetadataRepositoryModule.forFeature([PersonObjectMetadata]),
],
providers: [CalendarEventAttendeeService],
exports: [CalendarEventAttendeeService],
})
export class CalendarEventAttendeeModule {}

View File

@ -0,0 +1,65 @@
import { Injectable } from '@nestjs/common';
import { EntityManager } from 'typeorm';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { PersonRepository } from 'src/modules/person/repositories/person.repository';
import { PersonObjectMetadata } from 'src/modules/person/standard-objects/person.object-metadata';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { getFlattenedValuesAndValuesStringForBatchRawQuery } from 'src/modules/calendar/utils/getFlattenedValuesAndValuesStringForBatchRawQuery.util';
import { CalendarEventAttendeeWithId } from 'src/modules/calendar/types/calendar-event';
@Injectable()
export class CalendarEventAttendeeService {
constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
@InjectObjectMetadataRepository(PersonObjectMetadata)
private readonly personRepository: PersonRepository,
) {}
public async updateCalendarEventAttendeesAfterContactCreation(
attendees: CalendarEventAttendeeWithId[],
workspaceId: string,
transactionManager?: EntityManager,
): Promise<void> {
if (!attendees) return;
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const handles = attendees.map((attendee) => attendee.handle);
const attendeePersonIds = await this.personRepository.getByEmails(
handles,
workspaceId,
transactionManager,
);
const calendarEventAttendeesToUpdate = attendees.map((attendee) => ({
id: attendee.id,
personId: attendeePersonIds.find(
(e: { id: string; email: string }) => e.email === attendee.handle,
)?.id,
}));
if (calendarEventAttendeesToUpdate.length === 0) return;
const { flattenedValues, valuesString } =
getFlattenedValuesAndValuesStringForBatchRawQuery(
calendarEventAttendeesToUpdate,
{
id: 'uuid',
personId: 'uuid',
},
);
await this.workspaceDataSourceService.executeRawQuery(
`UPDATE ${dataSourceSchema}."calendarEventAttendee" AS "calendarEventAttendee" SET "personId" = "data"."personId"
FROM (VALUES ${valuesString}) AS "data"("id", "personId")
WHERE "calendarEventAttendee"."id" = "data"."id"`,
flattenedValues,
workspaceId,
transactionManager,
);
}
}

View File

@ -1,5 +1,6 @@
import { Inject, Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { Repository } from 'typeorm';
@ -53,6 +54,7 @@ export class GoogleCalendarFullSyncService {
@InjectRepository(FeatureFlagEntity, 'core')
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
private readonly eventEmitter: EventEmitter2,
) {}
public async startGoogleCalendarFullSync(
@ -294,6 +296,16 @@ export class GoogleCalendarFullSyncService {
}ms.`,
);
});
if (calendarChannel.isContactAutoCreationEnabled) {
const contactsToCreate = attendeesToSave;
this.eventEmitter.emit(`createContacts`, {
workspaceId,
connectedAccountHandle: connectedAccount.handle,
contactsToCreate,
});
}
} catch (error) {
this.logger.error(
`Error during google calendar full-sync for workspace ${workspaceId} and account ${connectedAccountId}: ${error.message}`,

View File

@ -29,3 +29,7 @@ export type CalendarEventWithAttendees = CalendarEvent & {
externalId: string;
attendees: CalendarEventAttendee[];
};
export type CalendarEventAttendeeWithId = CalendarEventAttendee & {
id: string;
};