6254 double creation of contacts when updating calendar event participants (#6269)

Closes #6254
This commit is contained in:
bosiraphael
2024-07-16 10:47:18 +02:00
committed by GitHub
parent 2218e20595
commit d216bfdd4e
4 changed files with 48 additions and 138 deletions

View File

@ -1,7 +1,7 @@
import { calendar_v3 as calendarV3 } from 'googleapis'; import { calendar_v3 as calendarV3 } from 'googleapis';
import { CalendarEventWithParticipants } from 'src/modules/calendar/common/types/calendar-event';
import { CalendarEventParticipantResponseStatus } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity'; import { CalendarEventParticipantResponseStatus } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity';
import { CalendarEventWithParticipants } from 'src/modules/calendar/common/types/calendar-event';
export const formatGoogleCalendarEvents = ( export const formatGoogleCalendarEvents = (
events: calendarV3.Schema$Event[], events: calendarV3.Schema$Event[],
@ -44,7 +44,6 @@ const formatGoogleCalendarEvent = (
recurringEventExternalId: event.recurringEventId ?? '', recurringEventExternalId: event.recurringEventId ?? '',
participants: participants:
event.attendees?.map((attendee) => ({ event.attendees?.map((attendee) => ({
iCalUID: event.iCalUID ?? '',
handle: attendee.email ?? '', handle: attendee.email ?? '',
displayName: attendee.displayName ?? '', displayName: attendee.displayName ?? '',
isOrganizer: attendee.organizer === true, isOrganizer: attendee.organizer === true,

View File

@ -135,7 +135,6 @@ export class CalendarSaveEventsService {
await this.calendarEventParticipantService.upsertAndDeleteCalendarEventParticipants( await this.calendarEventParticipantService.upsertAndDeleteCalendarEventParticipants(
participantsToSave, participantsToSave,
participantsToUpdate, participantsToUpdate,
workspaceId,
transactionManager, transactionManager,
); );
}); });

View File

@ -1,140 +1,28 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter'; import { EventEmitter2 } from '@nestjs/event-emitter';
import { Any, EntityManager } from 'typeorm';
import { isDefined } from 'class-validator'; import { isDefined } from 'class-validator';
import differenceWith from 'lodash.differencewith';
import { Any } from 'typeorm';
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
import { PersonRepository } from 'src/modules/person/repositories/person.repository';
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { getFlattenedValuesAndValuesStringForBatchRawQuery } from 'src/modules/calendar/calendar-event-import-manager/utils/get-flattened-values-and-values-string-for-batch-raw-query.util';
import {
CalendarEventParticipant,
CalendarEventParticipantWithCalendarEventId,
} from 'src/modules/calendar/common/types/calendar-event';
import { AddPersonIdAndWorkspaceMemberIdService } from 'src/modules/calendar-messaging-participant-manager/services/add-person-id-and-workspace-member-id/add-person-id-and-workspace-member-id.service';
import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator'; import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator';
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity'; import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event-participant.workspace-entity';
import { CalendarEventParticipantWithCalendarEventId } from 'src/modules/calendar/common/types/calendar-event';
@Injectable() @Injectable()
export class CalendarEventParticipantService { export class CalendarEventParticipantService {
constructor( constructor(
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
@InjectWorkspaceRepository(CalendarEventParticipantWorkspaceEntity) @InjectWorkspaceRepository(CalendarEventParticipantWorkspaceEntity)
private readonly calendarEventParticipantRepository: WorkspaceRepository<CalendarEventParticipantWorkspaceEntity>, private readonly calendarEventParticipantRepository: WorkspaceRepository<CalendarEventParticipantWorkspaceEntity>,
@InjectObjectMetadataRepository(PersonWorkspaceEntity)
private readonly personRepository: PersonRepository,
private readonly addPersonIdAndWorkspaceMemberIdService: AddPersonIdAndWorkspaceMemberIdService,
private readonly eventEmitter: EventEmitter2, private readonly eventEmitter: EventEmitter2,
) {} ) {}
public async updateCalendarEventParticipantsAfterPeopleCreation(
createdPeople: PersonWorkspaceEntity[],
workspaceId: string,
transactionManager?: EntityManager,
): Promise<CalendarEventParticipantWorkspaceEntity[]> {
const participants = await this.calendarEventParticipantRepository.find({
where: {
handle: Any(createdPeople.map((person) => person.email)),
},
});
if (!participants) return [];
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const handles = participants.map((participant) => participant.handle);
const participantPersonIds = await this.personRepository.getByEmails(
handles,
workspaceId,
transactionManager,
);
const calendarEventParticipantsToUpdate = participants.map(
(participant) => ({
id: participant.id,
personId: participantPersonIds.find(
(e: { id: string; email: string }) => e.email === participant.handle,
)?.id,
}),
);
if (calendarEventParticipantsToUpdate.length === 0) return [];
const { flattenedValues, valuesString } =
getFlattenedValuesAndValuesStringForBatchRawQuery(
calendarEventParticipantsToUpdate,
{
id: 'uuid',
personId: 'uuid',
},
);
return (
await this.workspaceDataSourceService.executeRawQuery(
`UPDATE ${dataSourceSchema}."calendarEventParticipant" AS "calendarEventParticipant" SET "personId" = "data"."personId"
FROM (VALUES ${valuesString}) AS "data"("id", "personId")
WHERE "calendarEventParticipant"."id" = "data"."id"
RETURNING *`,
flattenedValues,
workspaceId,
transactionManager,
)
).flat();
}
public async saveCalendarEventParticipants(
calendarEventParticipants: CalendarEventParticipant[],
workspaceId: string,
transactionManager?: EntityManager,
): Promise<CalendarEventParticipantWorkspaceEntity[]> {
if (calendarEventParticipants.length === 0) {
return [];
}
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const calendarEventParticipantsToSave =
await this.addPersonIdAndWorkspaceMemberIdService.addPersonIdAndWorkspaceMemberId(
calendarEventParticipants,
workspaceId,
transactionManager,
);
const { flattenedValues, valuesString } =
getFlattenedValuesAndValuesStringForBatchRawQuery(
calendarEventParticipantsToSave,
{
calendarEventId: 'uuid',
handle: 'text',
displayName: 'text',
isOrganizer: 'boolean',
responseStatus: `${dataSourceSchema}."calendarEventParticipant_responseStatus_enum"`,
personId: 'uuid',
workspaceMemberId: 'uuid',
},
);
return await this.workspaceDataSourceService.executeRawQuery(
`INSERT INTO ${dataSourceSchema}."calendarEventParticipant" ("calendarEventId", "handle", "displayName", "isOrganizer", "responseStatus", "personId", "workspaceMemberId") VALUES ${valuesString}
RETURNING *`,
flattenedValues,
workspaceId,
transactionManager,
);
}
public async upsertAndDeleteCalendarEventParticipants( public async upsertAndDeleteCalendarEventParticipants(
participantsToSave: CalendarEventParticipantWithCalendarEventId[], participantsToSave: CalendarEventParticipantWithCalendarEventId[],
participantsToUpdate: CalendarEventParticipantWithCalendarEventId[], participantsToUpdate: CalendarEventParticipantWithCalendarEventId[],
workspaceId: string,
transactionManager?: any, transactionManager?: any,
): Promise<CalendarEventParticipantWorkspaceEntity[]> { ): Promise<void> {
const existingCalendarEventParticipants = const existingCalendarEventParticipants =
await this.calendarEventParticipantRepository.find({ await this.calendarEventParticipantRepository.find({
where: { where: {
@ -146,19 +34,21 @@ export class CalendarEventParticipantService {
}, },
}); });
const { calendarEventParticipantsToDelete, newCalendarEventParticipants } = const { calendarEventParticipantsToUpdate, newCalendarEventParticipants } =
participantsToUpdate.reduce( participantsToUpdate.reduce(
(acc, calendarEventParticipant) => { (acc, calendarEventParticipant) => {
const existingCalendarEventParticipant = const existingCalendarEventParticipant =
existingCalendarEventParticipants.find( existingCalendarEventParticipants.find(
(existingCalendarEventParticipant) => (existingCalendarEventParticipant) =>
existingCalendarEventParticipant.handle === existingCalendarEventParticipant.handle ===
calendarEventParticipant.handle, calendarEventParticipant.handle &&
existingCalendarEventParticipant.calendarEventId ===
calendarEventParticipant.calendarEventId,
); );
if (existingCalendarEventParticipant) { if (existingCalendarEventParticipant) {
acc.calendarEventParticipantsToDelete.push( acc.calendarEventParticipantsToUpdate.push(
existingCalendarEventParticipant, calendarEventParticipant,
); );
} else { } else {
acc.newCalendarEventParticipants.push(calendarEventParticipant); acc.newCalendarEventParticipants.push(calendarEventParticipant);
@ -167,28 +57,52 @@ export class CalendarEventParticipantService {
return acc; return acc;
}, },
{ {
calendarEventParticipantsToDelete: calendarEventParticipantsToUpdate:
[] as CalendarEventParticipantWorkspaceEntity[], [] as CalendarEventParticipantWithCalendarEventId[],
newCalendarEventParticipants: newCalendarEventParticipants:
[] as CalendarEventParticipantWithCalendarEventId[], [] as CalendarEventParticipantWithCalendarEventId[],
}, },
); );
await this.calendarEventParticipantRepository.delete({ const calendarEventParticipantsToDelete = differenceWith(
id: Any( existingCalendarEventParticipants,
calendarEventParticipantsToDelete.map( participantsToUpdate,
(calendarEventParticipant) => calendarEventParticipant.id, (existingCalendarEventParticipant, participantToUpdate) =>
), existingCalendarEventParticipant.handle ===
), participantToUpdate.handle &&
}); existingCalendarEventParticipant.calendarEventId ===
participantToUpdate.calendarEventId,
);
await this.calendarEventParticipantRepository.save(participantsToUpdate); await this.calendarEventParticipantRepository.delete(
{
id: Any(
calendarEventParticipantsToDelete.map(
(calendarEventParticipant) => calendarEventParticipant.id,
),
),
},
transactionManager,
);
for (const calendarEventParticipantToUpdate of calendarEventParticipantsToUpdate) {
await this.calendarEventParticipantRepository.update(
{
calendarEventId: calendarEventParticipantToUpdate.calendarEventId,
handle: calendarEventParticipantToUpdate.handle,
},
{
...calendarEventParticipantToUpdate,
},
transactionManager,
);
}
participantsToSave.push(...newCalendarEventParticipants); participantsToSave.push(...newCalendarEventParticipants);
return await this.saveCalendarEventParticipants( await this.calendarEventParticipantRepository.save(
participantsToSave, participantsToSave,
workspaceId, {},
transactionManager, transactionManager,
); );
} }

View File

@ -25,9 +25,7 @@ export type CalendarEventParticipant = Omit<
| 'workspaceMember' | 'workspaceMember'
| 'calendarEvent' | 'calendarEvent'
| 'calendarEventId' | 'calendarEventId'
> & { >;
iCalUID: string;
};
export type CalendarEventParticipantWithCalendarEventId = export type CalendarEventParticipantWithCalendarEventId =
CalendarEventParticipant & { CalendarEventParticipant & {