feat: drop calendar repository (#5824)
This PR is replacing and removing all the raw queries and repositories with the new `TwentyORM` and injection system using `@InjectWorkspaceRepository`. Some logic that was contained inside repositories has been moved to the services. In this PR we're only replacing repositories for calendar feature. --------- Co-authored-by: Weiko <corentin@twenty.com> Co-authored-by: bosiraphael <raphael.bosi@gmail.com> Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -1,21 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||
|
||||
@Injectable()
|
||||
export class CommentRepository {
|
||||
constructor(
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
) {}
|
||||
|
||||
async deleteByAuthorId(authorId: string, workspaceId: string): Promise<void> {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`DELETE FROM ${dataSourceSchema}."comment" WHERE "authorId" = $1`,
|
||||
[authorId],
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -36,7 +36,7 @@ export class ActivityTargetWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'activityTargets',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
activity: Relation<ActivityWorkspaceEntity>;
|
||||
activity: Relation<ActivityWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: ACTIVITY_TARGET_STANDARD_FIELD_IDS.person,
|
||||
@ -49,7 +49,7 @@ export class ActivityTargetWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'activityTargets',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
person: Relation<PersonWorkspaceEntity>;
|
||||
person: Relation<PersonWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: ACTIVITY_TARGET_STANDARD_FIELD_IDS.company,
|
||||
@ -62,7 +62,7 @@ export class ActivityTargetWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'activityTargets',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
company: Relation<CompanyWorkspaceEntity>;
|
||||
company: Relation<CompanyWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: ACTIVITY_TARGET_STANDARD_FIELD_IDS.opportunity,
|
||||
@ -75,7 +75,7 @@ export class ActivityTargetWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'activityTargets',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
opportunity: Relation<OpportunityWorkspaceEntity>;
|
||||
opportunity: Relation<OpportunityWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceDynamicRelation({
|
||||
type: RelationMetadataType.MANY_TO_ONE,
|
||||
|
||||
@ -64,7 +64,7 @@ export class ActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconCalendarEvent',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
reminderAt: Date;
|
||||
reminderAt: Date | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: ACTIVITY_STANDARD_FIELD_IDS.dueAt,
|
||||
@ -74,7 +74,7 @@ export class ActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconCalendarEvent',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
dueAt: Date;
|
||||
dueAt: Date | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: ACTIVITY_STANDARD_FIELD_IDS.completedAt,
|
||||
@ -84,7 +84,7 @@ export class ActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconCheck',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
completedAt: Date;
|
||||
completedAt: Date | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: ACTIVITY_STANDARD_FIELD_IDS.activityTargets,
|
||||
@ -134,7 +134,7 @@ export class ActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
joinColumn: 'authorId',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
author: Relation<WorkspaceMemberWorkspaceEntity>;
|
||||
author: Relation<WorkspaceMemberWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: ACTIVITY_STANDARD_FIELD_IDS.assignee,
|
||||
@ -148,5 +148,5 @@ export class ActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
joinColumn: 'assigneeId',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
assignee: Relation<WorkspaceMemberWorkspaceEntity>;
|
||||
assignee: Relation<WorkspaceMemberWorkspaceEntity> | null;
|
||||
}
|
||||
|
||||
@ -45,5 +45,5 @@ export class ApiKeyWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconCalendar',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
revokedAt?: Date;
|
||||
revokedAt?: Date | null;
|
||||
}
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||
|
||||
@Injectable()
|
||||
export class AttachmentRepository {
|
||||
constructor(
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
) {}
|
||||
|
||||
async deleteByAuthorId(authorId: string, workspaceId: string): Promise<void> {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`DELETE FROM ${dataSourceSchema}."attachment" WHERE "authorId" = $1`,
|
||||
[authorId],
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -80,7 +80,7 @@ export class AttachmentWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'attachments',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
activity: Relation<ActivityWorkspaceEntity>;
|
||||
activity: Relation<ActivityWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: ATTACHMENT_STANDARD_FIELD_IDS.person,
|
||||
@ -93,7 +93,7 @@ export class AttachmentWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'attachments',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
person: Relation<PersonWorkspaceEntity>;
|
||||
person: Relation<PersonWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: ATTACHMENT_STANDARD_FIELD_IDS.company,
|
||||
@ -106,7 +106,7 @@ export class AttachmentWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'attachments',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
company: Relation<CompanyWorkspaceEntity>;
|
||||
company: Relation<CompanyWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: ATTACHMENT_STANDARD_FIELD_IDS.opportunity,
|
||||
@ -119,7 +119,7 @@ export class AttachmentWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'attachments',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
opportunity: Relation<OpportunityWorkspaceEntity>;
|
||||
opportunity: Relation<OpportunityWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceDynamicRelation({
|
||||
type: RelationMetadataType.MANY_TO_ONE,
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { Scope } from '@nestjs/common';
|
||||
|
||||
import { Process } from 'src/engine/integrations/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/integrations/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
|
||||
@ -11,7 +13,10 @@ export type MatchParticipantJobData = {
|
||||
workspaceMemberId?: string;
|
||||
};
|
||||
|
||||
@Processor(MessageQueue.messagingQueue)
|
||||
@Processor({
|
||||
queueName: MessageQueue.messagingQueue,
|
||||
scope: Scope.REQUEST,
|
||||
})
|
||||
export class MatchParticipantJob {
|
||||
constructor(
|
||||
private readonly messageParticipantService: MessagingMessageParticipantService,
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { Scope } from '@nestjs/common';
|
||||
|
||||
import { Processor } from 'src/engine/integrations/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
|
||||
import { CalendarEventParticipantService } from 'src/modules/calendar/services/calendar-event-participant/calendar-event-participant.service';
|
||||
@ -11,7 +13,10 @@ export type UnmatchParticipantJobData = {
|
||||
workspaceMemberId?: string;
|
||||
};
|
||||
|
||||
@Processor(MessageQueue.messagingQueue)
|
||||
@Processor({
|
||||
queueName: MessageQueue.messagingQueue,
|
||||
scope: Scope.REQUEST,
|
||||
})
|
||||
export class UnmatchParticipantJob {
|
||||
constructor(
|
||||
private readonly messageParticipantService: MessagingMessageParticipantService,
|
||||
|
||||
@ -1,19 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
|
||||
import { GoogleCalendarSyncCommand } from 'src/modules/calendar/commands/google-calendar-sync.command';
|
||||
import { WorkspaceGoogleCalendarSyncModule } from 'src/modules/calendar/services/workspace-google-calendar-sync/workspace-google-calendar-sync.module';
|
||||
import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-channel.workspace-entity';
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ObjectMetadataRepositoryModule.forFeature([
|
||||
ConnectedAccountWorkspaceEntity,
|
||||
CalendarChannelWorkspaceEntity,
|
||||
]),
|
||||
WorkspaceGoogleCalendarSyncModule,
|
||||
],
|
||||
imports: [WorkspaceGoogleCalendarSyncModule],
|
||||
providers: [GoogleCalendarSyncCommand],
|
||||
})
|
||||
export class CalendarCommandsModule {}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Scope } from '@nestjs/common';
|
||||
|
||||
import { Repository, In } from 'typeorm';
|
||||
|
||||
@ -10,7 +11,10 @@ import { MessageQueue } from 'src/engine/integrations/message-queue/message-queu
|
||||
import { Processor } from 'src/engine/integrations/message-queue/decorators/processor.decorator';
|
||||
import { Process } from 'src/engine/integrations/message-queue/decorators/process.decorator';
|
||||
|
||||
@Processor(MessageQueue.cronQueue)
|
||||
@Processor({
|
||||
queueName: MessageQueue.cronQueue,
|
||||
scope: Scope.REQUEST,
|
||||
})
|
||||
export class GoogleCalendarSyncCronJob {
|
||||
constructor(
|
||||
@InjectRepository(Workspace, 'core')
|
||||
|
||||
@ -1,35 +1,38 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { Logger, Scope } from '@nestjs/common';
|
||||
|
||||
import { Any, ILike } from 'typeorm';
|
||||
|
||||
import { Processor } from 'src/engine/integrations/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
|
||||
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
|
||||
import { CalendarChannelEventAssociationRepository } from 'src/modules/calendar/repositories/calendar-channel-event-association.repository';
|
||||
import { CalendarChannelRepository } from 'src/modules/calendar/repositories/calendar-channel.repository';
|
||||
import { CalendarEventCleanerService } from 'src/modules/calendar/services/calendar-event-cleaner/calendar-event-cleaner.service';
|
||||
import { CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-channel-event-association.workspace-entity';
|
||||
import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-channel.workspace-entity';
|
||||
import { BlocklistRepository } from 'src/modules/connected-account/repositories/blocklist.repository';
|
||||
import { BlocklistWorkspaceEntity } from 'src/modules/connected-account/standard-objects/blocklist.workspace-entity';
|
||||
import { Process } from 'src/engine/integrations/message-queue/decorators/process.decorator';
|
||||
import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator';
|
||||
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
|
||||
export type BlocklistItemDeleteCalendarEventsJobData = {
|
||||
workspaceId: string;
|
||||
blocklistItemId: string;
|
||||
};
|
||||
|
||||
@Processor(MessageQueue.calendarQueue)
|
||||
@Processor({
|
||||
queueName: MessageQueue.calendarQueue,
|
||||
scope: Scope.REQUEST,
|
||||
})
|
||||
export class BlocklistItemDeleteCalendarEventsJob {
|
||||
private readonly logger = new Logger(
|
||||
BlocklistItemDeleteCalendarEventsJob.name,
|
||||
);
|
||||
|
||||
constructor(
|
||||
@InjectObjectMetadataRepository(CalendarChannelWorkspaceEntity)
|
||||
private readonly calendarChannelRepository: CalendarChannelRepository,
|
||||
@InjectObjectMetadataRepository(
|
||||
CalendarChannelEventAssociationWorkspaceEntity,
|
||||
)
|
||||
private readonly calendarChannelEventAssociationRepository: CalendarChannelEventAssociationRepository,
|
||||
@InjectWorkspaceRepository(CalendarChannelWorkspaceEntity)
|
||||
private readonly calendarChannelRepository: WorkspaceRepository<CalendarChannelWorkspaceEntity>,
|
||||
@InjectWorkspaceRepository(CalendarChannelEventAssociationWorkspaceEntity)
|
||||
private readonly calendarChannelEventAssociationRepository: WorkspaceRepository<CalendarChannelEventAssociationWorkspaceEntity>,
|
||||
@InjectObjectMetadataRepository(BlocklistWorkspaceEntity)
|
||||
private readonly blocklistRepository: BlocklistRepository,
|
||||
private readonly calendarEventCleanerService: CalendarEventCleanerService,
|
||||
@ -58,19 +61,39 @@ export class BlocklistItemDeleteCalendarEventsJob {
|
||||
`Deleting calendar events from ${handle} in workspace ${workspaceId} for workspace member ${workspaceMemberId}`,
|
||||
);
|
||||
|
||||
const calendarChannels =
|
||||
await this.calendarChannelRepository.getIdsByWorkspaceMemberId(
|
||||
workspaceMemberId,
|
||||
workspaceId,
|
||||
if (!workspaceMemberId) {
|
||||
throw new Error(
|
||||
`Workspace member ID is undefined for blocklist item ${blocklistItemId} in workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
const calendarChannels = await this.calendarChannelRepository.find({
|
||||
where: {
|
||||
connectedAccount: {
|
||||
accountOwner: {
|
||||
id: workspaceMemberId,
|
||||
},
|
||||
},
|
||||
},
|
||||
relations: ['connectedAccount.accountOwner'],
|
||||
});
|
||||
|
||||
const calendarChannelIds = calendarChannels.map(({ id }) => id);
|
||||
|
||||
await this.calendarChannelEventAssociationRepository.deleteByCalendarEventParticipantHandleAndCalendarChannelIds(
|
||||
handle,
|
||||
calendarChannelIds,
|
||||
workspaceId,
|
||||
);
|
||||
const isHandleDomain = handle.startsWith('@');
|
||||
|
||||
await this.calendarChannelEventAssociationRepository.delete({
|
||||
calendarEvent: {
|
||||
calendarEventParticipants: {
|
||||
handle: isHandleDomain ? ILike(`%${handle}`) : handle,
|
||||
},
|
||||
calendarChannelEventAssociations: {
|
||||
calendarChannel: {
|
||||
id: Any(calendarChannelIds),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await this.calendarEventCleanerService.cleanWorkspaceCalendarEvents(
|
||||
workspaceId,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { Logger, Scope } from '@nestjs/common';
|
||||
|
||||
import { Processor } from 'src/engine/integrations/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
|
||||
@ -14,7 +14,10 @@ export type BlocklistReimportCalendarEventsJobData = {
|
||||
handle: string;
|
||||
};
|
||||
|
||||
@Processor(MessageQueue.calendarQueue)
|
||||
@Processor({
|
||||
queueName: MessageQueue.calendarQueue,
|
||||
scope: Scope.REQUEST,
|
||||
})
|
||||
export class BlocklistReimportCalendarEventsJob {
|
||||
private readonly logger = new Logger(BlocklistReimportCalendarEventsJob.name);
|
||||
|
||||
|
||||
@ -1,35 +1,35 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { Logger, Scope } from '@nestjs/common';
|
||||
|
||||
import { IsNull } from 'typeorm';
|
||||
|
||||
import { Process } from 'src/engine/integrations/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/integrations/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
|
||||
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
|
||||
import { CalendarChannelRepository } from 'src/modules/calendar/repositories/calendar-channel.repository';
|
||||
import { CalendarEventParticipantRepository } from 'src/modules/calendar/repositories/calendar-event-participant.repository';
|
||||
import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-channel.workspace-entity';
|
||||
import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-event-participant.workspace-entity';
|
||||
import { CreateCompanyAndContactService } from 'src/modules/connected-account/auto-companies-and-contacts-creation/services/create-company-and-contact.service';
|
||||
import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository';
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import { Process } from 'src/engine/integrations/message-queue/decorators/process.decorator';
|
||||
import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator';
|
||||
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
|
||||
export type CalendarCreateCompanyAndContactAfterSyncJobData = {
|
||||
workspaceId: string;
|
||||
calendarChannelId: string;
|
||||
};
|
||||
|
||||
@Processor(MessageQueue.calendarQueue)
|
||||
@Processor({
|
||||
queueName: MessageQueue.calendarQueue,
|
||||
scope: Scope.REQUEST,
|
||||
})
|
||||
export class CalendarCreateCompanyAndContactAfterSyncJob {
|
||||
private readonly logger = new Logger(
|
||||
CalendarCreateCompanyAndContactAfterSyncJob.name,
|
||||
);
|
||||
constructor(
|
||||
private readonly createCompanyAndContactService: CreateCompanyAndContactService,
|
||||
@InjectObjectMetadataRepository(CalendarChannelWorkspaceEntity)
|
||||
private readonly calendarChannelService: CalendarChannelRepository,
|
||||
@InjectObjectMetadataRepository(CalendarEventParticipantWorkspaceEntity)
|
||||
private readonly calendarEventParticipantRepository: CalendarEventParticipantRepository,
|
||||
@InjectObjectMetadataRepository(ConnectedAccountWorkspaceEntity)
|
||||
private readonly connectedAccountRepository: ConnectedAccountRepository,
|
||||
@InjectWorkspaceRepository(CalendarChannelWorkspaceEntity)
|
||||
private readonly calendarChannelRepository: WorkspaceRepository<CalendarChannelWorkspaceEntity>,
|
||||
@InjectWorkspaceRepository(CalendarEventParticipantWorkspaceEntity)
|
||||
private readonly calendarEventParticipantRepository: WorkspaceRepository<CalendarEventParticipantWorkspaceEntity>,
|
||||
) {}
|
||||
|
||||
@Process(CalendarCreateCompanyAndContactAfterSyncJob.name)
|
||||
@ -41,40 +41,52 @@ export class CalendarCreateCompanyAndContactAfterSyncJob {
|
||||
);
|
||||
const { workspaceId, calendarChannelId } = data;
|
||||
|
||||
const calendarChannels = await this.calendarChannelService.getByIds(
|
||||
[calendarChannelId],
|
||||
workspaceId,
|
||||
);
|
||||
const calendarChannel = await this.calendarChannelRepository.findOne({
|
||||
where: {
|
||||
id: calendarChannelId,
|
||||
},
|
||||
relations: ['connectedAccount.accountOwner'],
|
||||
});
|
||||
|
||||
if (calendarChannels.length === 0) {
|
||||
if (!calendarChannel) {
|
||||
throw new Error(
|
||||
`Calendar channel with id ${calendarChannelId} not found in workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
const { handle, isContactAutoCreationEnabled, connectedAccountId } =
|
||||
calendarChannels[0];
|
||||
const { handle, isContactAutoCreationEnabled, connectedAccount } =
|
||||
calendarChannel;
|
||||
|
||||
if (!isContactAutoCreationEnabled || !handle) {
|
||||
return;
|
||||
}
|
||||
|
||||
const connectedAccount = await this.connectedAccountRepository.getById(
|
||||
connectedAccountId,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (!connectedAccount) {
|
||||
throw new Error(
|
||||
`Connected account with id ${connectedAccountId} not found in workspace ${workspaceId}`,
|
||||
`Connected account not found in workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
const calendarEventParticipantsWithoutPersonIdAndWorkspaceMemberId =
|
||||
await this.calendarEventParticipantRepository.getByCalendarChannelIdWithoutPersonIdAndWorkspaceMemberId(
|
||||
calendarChannelId,
|
||||
workspaceId,
|
||||
);
|
||||
await this.calendarEventParticipantRepository.find({
|
||||
where: {
|
||||
calendarEvent: {
|
||||
calendarChannelEventAssociations: {
|
||||
calendarChannel: {
|
||||
id: calendarChannelId,
|
||||
},
|
||||
},
|
||||
calendarEventParticipants: {
|
||||
person: IsNull(),
|
||||
workspaceMember: IsNull(),
|
||||
},
|
||||
},
|
||||
},
|
||||
relations: [
|
||||
'calendarEvent.calendarChannelEventAssociations',
|
||||
'calendarEvent.calendarEventParticipants',
|
||||
],
|
||||
});
|
||||
|
||||
await this.createCompanyAndContactService.createCompaniesAndContactsAndUpdateParticipants(
|
||||
connectedAccount,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
|
||||
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
|
||||
import { BlocklistItemDeleteCalendarEventsJob } from 'src/modules/calendar/jobs/blocklist-item-delete-calendar-events.job';
|
||||
import { BlocklistReimportCalendarEventsJob } from 'src/modules/calendar/jobs/blocklist-reimport-calendar-events.job';
|
||||
import { CalendarCreateCompanyAndContactAfterSyncJob } from 'src/modules/calendar/jobs/calendar-create-company-and-contact-after-sync.job';
|
||||
@ -18,10 +19,12 @@ import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/s
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ObjectMetadataRepositoryModule.forFeature([
|
||||
TwentyORMModule.forFeature([
|
||||
CalendarChannelWorkspaceEntity,
|
||||
CalendarChannelEventAssociationWorkspaceEntity,
|
||||
CalendarEventParticipantWorkspaceEntity,
|
||||
]),
|
||||
ObjectMetadataRepositoryModule.forFeature([
|
||||
ConnectedAccountWorkspaceEntity,
|
||||
BlocklistWorkspaceEntity,
|
||||
]),
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { Logger, Scope } from '@nestjs/common';
|
||||
|
||||
import { GoogleAPIRefreshAccessTokenService } from 'src/modules/connected-account/services/google-api-refresh-access-token/google-api-refresh-access-token.service';
|
||||
import { GoogleCalendarSyncService } from 'src/modules/calendar/services/google-calendar-sync/google-calendar-sync.service';
|
||||
@ -11,7 +11,10 @@ export type GoogleCalendarSyncJobData = {
|
||||
connectedAccountId: string;
|
||||
};
|
||||
|
||||
@Processor(MessageQueue.calendarQueue)
|
||||
@Processor({
|
||||
queueName: MessageQueue.calendarQueue,
|
||||
scope: Scope.REQUEST,
|
||||
})
|
||||
export class GoogleCalendarSyncJob {
|
||||
private readonly logger = new Logger(GoogleCalendarSyncJob.name);
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ export class CalendarEventParticipantListener {
|
||||
@OnEvent('calendarEventParticipant.matched')
|
||||
public async handleCalendarEventParticipantMatchedEvent(payload: {
|
||||
workspaceId: string;
|
||||
userId: string;
|
||||
workspaceMemberId: string;
|
||||
calendarEventParticipants: ObjectRecord<CalendarEventParticipantWorkspaceEntity>[];
|
||||
}): Promise<void> {
|
||||
const calendarEventParticipants = payload.calendarEventParticipants ?? [];
|
||||
@ -59,7 +59,7 @@ export class CalendarEventParticipantListener {
|
||||
properties: null,
|
||||
objectName: 'calendarEvent',
|
||||
recordId: participant.personId,
|
||||
workspaceMemberId: payload.userId,
|
||||
workspaceMemberId: payload.workspaceMemberId,
|
||||
workspaceId: payload.workspaceId,
|
||||
linkedObjectMetadataId: calendarEventObjectMetadata.id,
|
||||
linkedRecordId: participant.calendarEventId,
|
||||
|
||||
@ -1,26 +1,20 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
|
||||
import { WorkspacePreQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-pre-query-hook/interfaces/workspace-pre-query-hook.interface';
|
||||
import { FindManyResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
|
||||
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
|
||||
import { CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-channel-event-association.workspace-entity';
|
||||
import { CalendarChannelEventAssociationRepository } from 'src/modules/calendar/repositories/calendar-channel-event-association.repository';
|
||||
import { CanAccessCalendarEventService } from 'src/modules/calendar/query-hooks/calendar-event/services/can-access-calendar-event.service';
|
||||
import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator';
|
||||
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
|
||||
@Injectable()
|
||||
export class CalendarEventFindManyPreQueryHook
|
||||
implements WorkspacePreQueryHook
|
||||
{
|
||||
constructor(
|
||||
@InjectObjectMetadataRepository(
|
||||
CalendarChannelEventAssociationWorkspaceEntity,
|
||||
)
|
||||
private readonly calendarChannelEventAssociationRepository: CalendarChannelEventAssociationRepository,
|
||||
@InjectWorkspaceRepository(CalendarChannelEventAssociationWorkspaceEntity)
|
||||
private readonly calendarChannelEventAssociationRepository: WorkspaceRepository<CalendarChannelEventAssociationWorkspaceEntity>,
|
||||
private readonly canAccessCalendarEventService: CanAccessCalendarEventService,
|
||||
) {}
|
||||
|
||||
@ -33,20 +27,25 @@ export class CalendarEventFindManyPreQueryHook
|
||||
throw new BadRequestException('id filter is required');
|
||||
}
|
||||
|
||||
const calendarChannelCalendarEventAssociations =
|
||||
await this.calendarChannelEventAssociationRepository.getByCalendarEventIds(
|
||||
[payload?.filter?.id?.eq],
|
||||
workspaceId,
|
||||
);
|
||||
// TODO: Re-implement this using twenty ORM
|
||||
// const calendarChannelCalendarEventAssociations =
|
||||
// await this.calendarChannelEventAssociationRepository.find({
|
||||
// where: {
|
||||
// calendarEvent: {
|
||||
// id: payload?.filter?.id?.eq,
|
||||
// },
|
||||
// },
|
||||
// relations: ['calendarChannel.connectedAccount'],
|
||||
// });
|
||||
|
||||
if (calendarChannelCalendarEventAssociations.length === 0) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
// if (calendarChannelCalendarEventAssociations.length === 0) {
|
||||
// throw new NotFoundException();
|
||||
// }
|
||||
|
||||
await this.canAccessCalendarEventService.canAccessCalendarEvent(
|
||||
userId,
|
||||
workspaceId,
|
||||
calendarChannelCalendarEventAssociations,
|
||||
);
|
||||
// await this.canAccessCalendarEventService.canAccessCalendarEvent(
|
||||
// userId,
|
||||
// workspaceId,
|
||||
// calendarChannelCalendarEventAssociations,
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,24 +1,18 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
|
||||
import { WorkspacePreQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-pre-query-hook/interfaces/workspace-pre-query-hook.interface';
|
||||
import { FindOneResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
|
||||
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-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 { CanAccessCalendarEventService } from 'src/modules/calendar/query-hooks/calendar-event/services/can-access-calendar-event.service';
|
||||
import { CalendarChannelEventAssociationRepository } from 'src/modules/calendar/repositories/calendar-channel-event-association.repository';
|
||||
import { CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-channel-event-association.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
export class CalendarEventFindOnePreQueryHook implements WorkspacePreQueryHook {
|
||||
constructor(
|
||||
@InjectObjectMetadataRepository(
|
||||
CalendarChannelEventAssociationWorkspaceEntity,
|
||||
)
|
||||
private readonly calendarChannelEventAssociationRepository: CalendarChannelEventAssociationRepository,
|
||||
@InjectWorkspaceRepository(CalendarChannelEventAssociationWorkspaceEntity)
|
||||
private readonly calendarChannelEventAssociationRepository: WorkspaceRepository<CalendarChannelEventAssociationWorkspaceEntity>,
|
||||
private readonly canAccessCalendarEventService: CanAccessCalendarEventService,
|
||||
) {}
|
||||
|
||||
@ -31,20 +25,24 @@ export class CalendarEventFindOnePreQueryHook implements WorkspacePreQueryHook {
|
||||
throw new BadRequestException('id filter is required');
|
||||
}
|
||||
|
||||
const calendarChannelCalendarEventAssociations =
|
||||
await this.calendarChannelEventAssociationRepository.getByCalendarEventIds(
|
||||
[payload?.filter?.id?.eq],
|
||||
workspaceId,
|
||||
);
|
||||
// TODO: Re-implement this using twenty ORM
|
||||
// const calendarChannelCalendarEventAssociations =
|
||||
// await this.calendarChannelEventAssociationRepository.find({
|
||||
// where: {
|
||||
// calendarEvent: {
|
||||
// id: payload?.filter?.id?.eq,
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
|
||||
if (calendarChannelCalendarEventAssociations.length === 0) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
// if (calendarChannelCalendarEventAssociations.length === 0) {
|
||||
// throw new NotFoundException();
|
||||
// }
|
||||
|
||||
await this.canAccessCalendarEventService.canAccessCalendarEvent(
|
||||
userId,
|
||||
workspaceId,
|
||||
calendarChannelCalendarEventAssociations,
|
||||
);
|
||||
// await this.canAccessCalendarEventService.canAccessCalendarEvent(
|
||||
// userId,
|
||||
// workspaceId,
|
||||
// calendarChannelCalendarEventAssociations,
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { ForbiddenException, Injectable } from '@nestjs/common';
|
||||
|
||||
import groupBy from 'lodash.groupby';
|
||||
import { Any } from 'typeorm';
|
||||
|
||||
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
|
||||
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
|
||||
import { CalendarChannelRepository } from 'src/modules/calendar/repositories/calendar-channel.repository';
|
||||
import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator';
|
||||
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-channel-event-association.workspace-entity';
|
||||
import {
|
||||
CalendarChannelWorkspaceEntity,
|
||||
@ -18,8 +19,8 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
|
||||
@Injectable()
|
||||
export class CanAccessCalendarEventService {
|
||||
constructor(
|
||||
@InjectObjectMetadataRepository(CalendarChannelWorkspaceEntity)
|
||||
private readonly calendarChannelRepository: CalendarChannelRepository,
|
||||
@InjectWorkspaceRepository(CalendarChannelWorkspaceEntity)
|
||||
private readonly calendarChannelRepository: WorkspaceRepository<CalendarChannelWorkspaceEntity>,
|
||||
@InjectObjectMetadataRepository(ConnectedAccountWorkspaceEntity)
|
||||
private readonly connectedAccountRepository: ConnectedAccountRepository,
|
||||
@InjectObjectMetadataRepository(WorkspaceMemberWorkspaceEntity)
|
||||
@ -29,14 +30,17 @@ export class CanAccessCalendarEventService {
|
||||
public async canAccessCalendarEvent(
|
||||
userId: string,
|
||||
workspaceId: string,
|
||||
calendarChannelCalendarEventAssociations: ObjectRecord<CalendarChannelEventAssociationWorkspaceEntity>[],
|
||||
calendarChannelCalendarEventAssociations: CalendarChannelEventAssociationWorkspaceEntity[],
|
||||
) {
|
||||
const calendarChannels = await this.calendarChannelRepository.getByIds(
|
||||
calendarChannelCalendarEventAssociations.map(
|
||||
(association) => association.calendarChannelId,
|
||||
),
|
||||
workspaceId,
|
||||
);
|
||||
const calendarChannels = await this.calendarChannelRepository.find({
|
||||
where: {
|
||||
id: Any(
|
||||
calendarChannelCalendarEventAssociations.map(
|
||||
(association) => association.calendarChannel.id,
|
||||
),
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
const calendarChannelsGroupByVisibility = groupBy(
|
||||
calendarChannels,
|
||||
@ -56,7 +60,7 @@ export class CanAccessCalendarEventService {
|
||||
|
||||
const calendarChannelsConnectedAccounts =
|
||||
await this.connectedAccountRepository.getByIds(
|
||||
calendarChannels.map((channel) => channel.connectedAccountId),
|
||||
calendarChannels.map((channel) => channel.connectedAccount.id),
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
|
||||
@ -8,12 +8,15 @@ import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/standard-ob
|
||||
import { CalendarEventFindManyPreQueryHook } from 'src/modules/calendar/query-hooks/calendar-event/calendar-event-find-many.pre-query.hook';
|
||||
import { CalendarEventFindOnePreQueryHook } from 'src/modules/calendar/query-hooks/calendar-event/calendar-event-find-one.pre-query-hook';
|
||||
import { CanAccessCalendarEventService } from 'src/modules/calendar/query-hooks/calendar-event/services/can-access-calendar-event.service';
|
||||
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ObjectMetadataRepositoryModule.forFeature([
|
||||
TwentyORMModule.forFeature([
|
||||
CalendarChannelEventAssociationWorkspaceEntity,
|
||||
CalendarChannelWorkspaceEntity,
|
||||
]),
|
||||
ObjectMetadataRepositoryModule.forFeature([
|
||||
ConnectedAccountWorkspaceEntity,
|
||||
WorkspaceMemberWorkspaceEntity,
|
||||
]),
|
||||
|
||||
@ -1,205 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { EntityManager } from 'typeorm';
|
||||
|
||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
|
||||
import { CalendarChannelEventAssociationWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-channel-event-association.workspace-entity';
|
||||
import { getFlattenedValuesAndValuesStringForBatchRawQuery } from 'src/modules/calendar/utils/get-flattened-values-and-values-string-for-batch-raw-query.util';
|
||||
|
||||
@Injectable()
|
||||
export class CalendarChannelEventAssociationRepository {
|
||||
constructor(
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
) {}
|
||||
|
||||
public async getByEventExternalIdsAndCalendarChannelId(
|
||||
eventExternalIds: string[],
|
||||
calendarChannelId: string,
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<ObjectRecord<CalendarChannelEventAssociationWorkspaceEntity>[]> {
|
||||
if (eventExternalIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
return await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT * FROM ${dataSourceSchema}."calendarChannelEventAssociation"
|
||||
WHERE "eventExternalId" = ANY($1) AND "calendarChannelId" = $2`,
|
||||
[eventExternalIds, calendarChannelId],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async deleteByEventExternalIdsAndCalendarChannelId(
|
||||
eventExternalIds: string[],
|
||||
calendarChannelId: string,
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
) {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`DELETE FROM ${dataSourceSchema}."calendarChannelEventAssociation" WHERE "eventExternalId" = ANY($1) AND "calendarChannelId" = $2`,
|
||||
[eventExternalIds, calendarChannelId],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async getByCalendarChannelIds(
|
||||
calendarChannelIds: string[],
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<ObjectRecord<CalendarChannelEventAssociationWorkspaceEntity>[]> {
|
||||
if (calendarChannelIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
return await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT * FROM ${dataSourceSchema}."calendarChannelEventAssociation"
|
||||
WHERE "calendarChannelId" = ANY($1)`,
|
||||
[calendarChannelIds],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async deleteByCalendarChannelIds(
|
||||
calendarChannelIds: string[],
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
) {
|
||||
if (calendarChannelIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`DELETE FROM ${dataSourceSchema}."calendarChannelEventAssociation" WHERE "calendarChannelId" = ANY($1)`,
|
||||
[calendarChannelIds],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async deleteByIds(
|
||||
ids: string[],
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
) {
|
||||
if (ids.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`DELETE FROM ${dataSourceSchema}."calendarChannelEventAssociation" WHERE "id" = ANY($1)`,
|
||||
[ids],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async getByCalendarEventIds(
|
||||
calendarEventIds: string[],
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<ObjectRecord<CalendarChannelEventAssociationWorkspaceEntity>[]> {
|
||||
if (calendarEventIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
return await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT * FROM ${dataSourceSchema}."calendarChannelEventAssociation"
|
||||
WHERE "calendarEventId" = ANY($1)`,
|
||||
[calendarEventIds],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async saveCalendarChannelEventAssociations(
|
||||
calendarChannelEventAssociations: Omit<
|
||||
ObjectRecord<CalendarChannelEventAssociationWorkspaceEntity>,
|
||||
'id' | 'createdAt' | 'updatedAt' | 'calendarChannel' | 'calendarEvent'
|
||||
>[],
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
) {
|
||||
if (calendarChannelEventAssociations.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
const {
|
||||
flattenedValues: calendarChannelEventAssociationValues,
|
||||
valuesString,
|
||||
} = getFlattenedValuesAndValuesStringForBatchRawQuery(
|
||||
calendarChannelEventAssociations,
|
||||
{
|
||||
calendarChannelId: 'uuid',
|
||||
calendarEventId: 'uuid',
|
||||
eventExternalId: 'text',
|
||||
},
|
||||
);
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`INSERT INTO ${dataSourceSchema}."calendarChannelEventAssociation" ("calendarChannelId", "calendarEventId", "eventExternalId")
|
||||
VALUES ${valuesString}`,
|
||||
calendarChannelEventAssociationValues,
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async deleteByCalendarEventParticipantHandleAndCalendarChannelIds(
|
||||
calendarEventParticipantHandle: string,
|
||||
calendarChannelIds: string[],
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
) {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
const isHandleDomain = calendarEventParticipantHandle.startsWith('@');
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`DELETE FROM ${dataSourceSchema}."calendarChannelEventAssociation"
|
||||
WHERE "id" IN (
|
||||
SELECT "calendarChannelEventAssociation"."id"
|
||||
FROM ${dataSourceSchema}."calendarChannelEventAssociation" "calendarChannelEventAssociation"
|
||||
JOIN ${dataSourceSchema}."calendarEvent" "calendarEvent" ON "calendarChannelEventAssociation"."calendarEventId" = "calendarEvent"."id"
|
||||
JOIN ${dataSourceSchema}."calendarEventParticipant" "calendarEventParticipant" ON "calendarEvent"."id" = "calendarEventParticipant"."calendarEventId"
|
||||
WHERE "calendarEventParticipant"."handle" ${
|
||||
isHandleDomain ? 'ILIKE' : '='
|
||||
} $1 AND "calendarChannelEventAssociation"."calendarChannelId" = ANY($2)
|
||||
)`,
|
||||
[
|
||||
isHandleDomain
|
||||
? `%${calendarEventParticipantHandle}`
|
||||
: calendarEventParticipantHandle,
|
||||
calendarChannelIds,
|
||||
],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,135 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { EntityManager } from 'typeorm';
|
||||
|
||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||
import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-channel.workspace-entity';
|
||||
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
|
||||
|
||||
@Injectable()
|
||||
export class CalendarChannelRepository {
|
||||
constructor(
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
) {}
|
||||
|
||||
public async getAll(
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<ObjectRecord<CalendarChannelWorkspaceEntity>[]> {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
return await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT * FROM ${dataSourceSchema}."calendarChannel"`,
|
||||
[],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async create(
|
||||
calendarChannel: Pick<
|
||||
ObjectRecord<CalendarChannelWorkspaceEntity>,
|
||||
'id' | 'connectedAccountId' | 'handle' | 'visibility'
|
||||
>,
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<void> {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`INSERT INTO ${dataSourceSchema}."calendarChannel" (id, "connectedAccountId", "handle", "visibility") VALUES ($1, $2, $3, $4)`,
|
||||
[
|
||||
calendarChannel.id,
|
||||
calendarChannel.connectedAccountId,
|
||||
calendarChannel.handle,
|
||||
calendarChannel.visibility,
|
||||
],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async getByConnectedAccountId(
|
||||
connectedAccountId: string,
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<ObjectRecord<CalendarChannelWorkspaceEntity>[]> {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
return await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT * FROM ${dataSourceSchema}."calendarChannel" WHERE "connectedAccountId" = $1 LIMIT 1`,
|
||||
[connectedAccountId],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async getFirstByConnectedAccountId(
|
||||
connectedAccountId: string,
|
||||
workspaceId: string,
|
||||
): Promise<ObjectRecord<CalendarChannelWorkspaceEntity> | undefined> {
|
||||
const calendarChannels = await this.getByConnectedAccountId(
|
||||
connectedAccountId,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return calendarChannels[0];
|
||||
}
|
||||
|
||||
public async getByIds(
|
||||
ids: string[],
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<ObjectRecord<CalendarChannelWorkspaceEntity>[]> {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
return await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT * FROM ${dataSourceSchema}."calendarChannel" WHERE "id" = ANY($1)`,
|
||||
[ids],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async getIdsByWorkspaceMemberId(
|
||||
workspaceMemberId: string,
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<ObjectRecord<CalendarChannelWorkspaceEntity>[]> {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
const calendarChannelIds =
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT "calendarChannel".id FROM ${dataSourceSchema}."calendarChannel" "calendarChannel"
|
||||
JOIN ${dataSourceSchema}."connectedAccount" ON "calendarChannel"."connectedAccountId" = ${dataSourceSchema}."connectedAccount"."id"
|
||||
WHERE ${dataSourceSchema}."connectedAccount"."accountOwnerId" = $1`,
|
||||
[workspaceMemberId],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
return calendarChannelIds;
|
||||
}
|
||||
|
||||
public async updateSyncCursor(
|
||||
syncCursor: string | null,
|
||||
calendarChannelId: string,
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<void> {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`UPDATE ${dataSourceSchema}."calendarChannel" SET "syncCursor" = $1 WHERE "id" = $2`,
|
||||
[syncCursor || '', calendarChannelId],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,305 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { EntityManager } from 'typeorm';
|
||||
import differenceWith from 'lodash.differencewith';
|
||||
|
||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
|
||||
import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-event-participant.workspace-entity';
|
||||
import { getFlattenedValuesAndValuesStringForBatchRawQuery } from 'src/modules/calendar/utils/get-flattened-values-and-values-string-for-batch-raw-query.util';
|
||||
import {
|
||||
CalendarEventParticipant,
|
||||
CalendarEventParticipantWithId,
|
||||
} from 'src/modules/calendar/types/calendar-event';
|
||||
|
||||
@Injectable()
|
||||
export class CalendarEventParticipantRepository {
|
||||
constructor(
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
) {}
|
||||
|
||||
public async getByHandles(
|
||||
handles: string[],
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<ObjectRecord<CalendarEventParticipantWorkspaceEntity>[]> {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
return await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT * FROM ${dataSourceSchema}."calendarEventParticipant" WHERE "handle" = ANY($1)`,
|
||||
[handles],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async updateParticipantsPersonId(
|
||||
participantIds: string[],
|
||||
personId: string,
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
) {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`UPDATE ${dataSourceSchema}."calendarEventParticipant" SET "personId" = $1 WHERE "id" = ANY($2)`,
|
||||
[personId, participantIds],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async updateParticipantsPersonIdAndReturn(
|
||||
participantIds: string[],
|
||||
personId: string,
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<ObjectRecord<CalendarEventParticipantWorkspaceEntity>[]> {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
return await this.workspaceDataSourceService.executeRawQuery(
|
||||
`UPDATE ${dataSourceSchema}."calendarEventParticipant" SET "personId" = $1 WHERE "id" = ANY($2) RETURNING *`,
|
||||
[personId, participantIds],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async updateParticipantsWorkspaceMemberId(
|
||||
participantIds: string[],
|
||||
workspaceMemberId: string,
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
) {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`UPDATE ${dataSourceSchema}."calendarEventParticipant" SET "workspaceMemberId" = $1 WHERE "id" = ANY($2)`,
|
||||
[workspaceMemberId, participantIds],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async removePersonIdByHandle(
|
||||
handle: string,
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
) {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`UPDATE ${dataSourceSchema}."calendarEventParticipant" SET "personId" = NULL WHERE "handle" = $1`,
|
||||
[handle],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async removeWorkspaceMemberIdByHandle(
|
||||
handle: string,
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
) {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`UPDATE ${dataSourceSchema}."calendarEventParticipant" SET "workspaceMemberId" = NULL WHERE "handle" = $1`,
|
||||
[handle],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async getByIds(
|
||||
calendarEventParticipantIds: string[],
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<ObjectRecord<CalendarEventParticipantWorkspaceEntity>[]> {
|
||||
if (calendarEventParticipantIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
return await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT * FROM ${dataSourceSchema}."calendarEventParticipant" WHERE "id" = ANY($1)`,
|
||||
[calendarEventParticipantIds],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async getByCalendarEventIds(
|
||||
calendarEventIds: string[],
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<ObjectRecord<CalendarEventParticipantWorkspaceEntity>[]> {
|
||||
if (calendarEventIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
return await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT * FROM ${dataSourceSchema}."calendarEventParticipant" WHERE "calendarEventId" = ANY($1)`,
|
||||
[calendarEventIds],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async deleteByIds(
|
||||
calendarEventParticipantIds: string[],
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<void> {
|
||||
if (calendarEventParticipantIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`DELETE FROM ${dataSourceSchema}."calendarEventParticipant" WHERE "id" = ANY($1)`,
|
||||
[calendarEventParticipantIds],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async updateCalendarEventParticipantsAndReturnNewOnes(
|
||||
calendarEventParticipants: CalendarEventParticipant[],
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<CalendarEventParticipant[]> {
|
||||
if (calendarEventParticipants.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
const existingCalendarEventParticipants = await this.getByCalendarEventIds(
|
||||
calendarEventParticipants.map(
|
||||
(calendarEventParticipant) => calendarEventParticipant.calendarEventId,
|
||||
),
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
const calendarEventParticipantsToDelete = differenceWith(
|
||||
existingCalendarEventParticipants,
|
||||
calendarEventParticipants,
|
||||
(existingCalendarEventParticipant, calendarEventParticipant) =>
|
||||
existingCalendarEventParticipant.handle ===
|
||||
calendarEventParticipant.handle,
|
||||
);
|
||||
|
||||
const newCalendarEventParticipants = differenceWith(
|
||||
calendarEventParticipants,
|
||||
existingCalendarEventParticipants,
|
||||
(calendarEventParticipant, existingCalendarEventParticipant) =>
|
||||
calendarEventParticipant.handle ===
|
||||
existingCalendarEventParticipant.handle,
|
||||
);
|
||||
|
||||
await this.deleteByIds(
|
||||
calendarEventParticipantsToDelete.map(
|
||||
(calendarEventParticipant) => calendarEventParticipant.id,
|
||||
),
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
const { flattenedValues, valuesString } =
|
||||
getFlattenedValuesAndValuesStringForBatchRawQuery(
|
||||
calendarEventParticipants,
|
||||
{
|
||||
calendarEventId: 'uuid',
|
||||
handle: 'text',
|
||||
displayName: 'text',
|
||||
isOrganizer: 'boolean',
|
||||
responseStatus: `${dataSourceSchema}."calendarEventParticipant_responseStatus_enum"`,
|
||||
},
|
||||
);
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`UPDATE ${dataSourceSchema}."calendarEventParticipant" AS "calendarEventParticipant"
|
||||
SET "displayName" = "newValues"."displayName",
|
||||
"isOrganizer" = "newValues"."isOrganizer",
|
||||
"responseStatus" = "newValues"."responseStatus"
|
||||
FROM (VALUES ${valuesString}) AS "newValues"("calendarEventId", "handle", "displayName", "isOrganizer", "responseStatus")
|
||||
WHERE "calendarEventParticipant"."handle" = "newValues"."handle"
|
||||
AND "calendarEventParticipant"."calendarEventId" = "newValues"."calendarEventId"`,
|
||||
flattenedValues,
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
return newCalendarEventParticipants;
|
||||
}
|
||||
|
||||
public async getWithoutPersonIdAndWorkspaceMemberId(
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<CalendarEventParticipantWithId[]> {
|
||||
if (!workspaceId) {
|
||||
throw new Error('WorkspaceId is required');
|
||||
}
|
||||
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
const calendarEventParticipants: CalendarEventParticipantWithId[] =
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT "calendarEventParticipant".*
|
||||
FROM ${dataSourceSchema}."calendarEventParticipant" AS "calendarEventParticipant"
|
||||
WHERE "calendarEventParticipant"."personId" IS NULL
|
||||
AND "calendarEventParticipant"."workspaceMemberId" IS NULL`,
|
||||
[],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
return calendarEventParticipants;
|
||||
}
|
||||
|
||||
public async getByCalendarChannelIdWithoutPersonIdAndWorkspaceMemberId(
|
||||
calendarChannelId: string,
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<CalendarEventParticipantWithId[]> {
|
||||
if (!workspaceId) {
|
||||
throw new Error('WorkspaceId is required');
|
||||
}
|
||||
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
const calendarEventParticipants: CalendarEventParticipantWithId[] =
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT "calendarEventParticipant".*
|
||||
FROM ${dataSourceSchema}."calendarEventParticipant" AS "calendarEventParticipant"
|
||||
LEFT JOIN ${dataSourceSchema}."calendarEvent" AS "calendarEvent" ON "calendarEventParticipant"."calendarEventId" = "calendarEvent"."id"
|
||||
LEFT JOIN ${dataSourceSchema}."calendarChannelEventAssociation" AS "calendarChannelEventAssociation" ON "calendarEvent"."id" = "calendarChannelEventAssociation"."calendarEventId"
|
||||
WHERE "calendarChannelEventAssociation"."calendarChannelId" = $1
|
||||
AND "calendarEventParticipant"."personId" IS NULL
|
||||
AND "calendarEventParticipant"."workspaceMemberId" IS NULL`,
|
||||
[calendarChannelId],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
return calendarEventParticipants;
|
||||
}
|
||||
}
|
||||
@ -1,227 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { EntityManager } from 'typeorm';
|
||||
|
||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
|
||||
import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-event.workspace-entity';
|
||||
import { getFlattenedValuesAndValuesStringForBatchRawQuery } from 'src/modules/calendar/utils/get-flattened-values-and-values-string-for-batch-raw-query.util';
|
||||
import { CalendarEvent } from 'src/modules/calendar/types/calendar-event';
|
||||
import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-event-participant.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
export class CalendarEventRepository {
|
||||
constructor(
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
) {}
|
||||
|
||||
public async getByIds(
|
||||
calendarEventIds: string[],
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<ObjectRecord<CalendarEventWorkspaceEntity>[]> {
|
||||
if (calendarEventIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
return await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT * FROM ${dataSourceSchema}."calendarEvent" WHERE "id" = ANY($1)`,
|
||||
[calendarEventIds],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async getByICalUIDs(
|
||||
iCalUIDs: string[],
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<ObjectRecord<CalendarEventWorkspaceEntity>[]> {
|
||||
if (iCalUIDs.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
return await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT * FROM ${dataSourceSchema}."calendarEvent" WHERE "iCalUID" = ANY($1)`,
|
||||
[iCalUIDs],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async deleteByIds(
|
||||
calendarEventIds: string[],
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<void> {
|
||||
if (calendarEventIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`DELETE FROM ${dataSourceSchema}."calendarEvent" WHERE "id" = ANY($1)`,
|
||||
[calendarEventIds],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async getNonAssociatedCalendarEventIdsPaginated(
|
||||
limit: number,
|
||||
offset: number,
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<ObjectRecord<CalendarEventParticipantWorkspaceEntity>[]> {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
const nonAssociatedCalendarEvents =
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT m.id FROM ${dataSourceSchema}."calendarEvent" m
|
||||
LEFT JOIN ${dataSourceSchema}."calendarChannelEventAssociation" ccea
|
||||
ON m.id = ccea."calendarEventId"
|
||||
WHERE ccea.id IS NULL
|
||||
LIMIT $1 OFFSET $2`,
|
||||
[limit, offset],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
return nonAssociatedCalendarEvents.map(({ id }) => id);
|
||||
}
|
||||
|
||||
public async getICalUIDCalendarEventIdMap(
|
||||
iCalUIDs: string[],
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<Map<string, string>> {
|
||||
if (iCalUIDs.length === 0) {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
const calendarEvents:
|
||||
| {
|
||||
id: string;
|
||||
iCalUID: string;
|
||||
}[]
|
||||
| undefined = await this.workspaceDataSourceService.executeRawQuery(
|
||||
`SELECT id, "iCalUID" FROM ${dataSourceSchema}."calendarEvent" WHERE "iCalUID" = ANY($1)`,
|
||||
[iCalUIDs],
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
const iCalUIDsCalendarEventIdsMap = new Map<string, string>();
|
||||
|
||||
calendarEvents?.forEach((calendarEvent) => {
|
||||
iCalUIDsCalendarEventIdsMap.set(calendarEvent.iCalUID, calendarEvent.id);
|
||||
});
|
||||
|
||||
return iCalUIDsCalendarEventIdsMap;
|
||||
}
|
||||
|
||||
public async saveCalendarEvents(
|
||||
calendarEvents: CalendarEvent[],
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<void> {
|
||||
if (calendarEvents.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
const { flattenedValues, valuesString } =
|
||||
getFlattenedValuesAndValuesStringForBatchRawQuery(calendarEvents, {
|
||||
id: 'uuid',
|
||||
title: 'text',
|
||||
isCanceled: 'boolean',
|
||||
isFullDay: 'boolean',
|
||||
startsAt: 'timestamptz',
|
||||
endsAt: 'timestamptz',
|
||||
externalCreatedAt: 'timestamptz',
|
||||
externalUpdatedAt: 'timestamptz',
|
||||
description: 'text',
|
||||
location: 'text',
|
||||
iCalUID: 'text',
|
||||
conferenceSolution: 'text',
|
||||
conferenceLinkLabel: 'text',
|
||||
conferenceLinkUrl: 'text',
|
||||
recurringEventExternalId: 'text',
|
||||
});
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`INSERT INTO ${dataSourceSchema}."calendarEvent" ("id", "title", "isCanceled", "isFullDay", "startsAt", "endsAt", "externalCreatedAt", "externalUpdatedAt", "description", "location", "iCalUID", "conferenceSolution", "conferenceLinkLabel", "conferenceLinkUrl", "recurringEventExternalId") VALUES ${valuesString}`,
|
||||
flattenedValues,
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
|
||||
public async updateCalendarEvents(
|
||||
calendarEvents: CalendarEvent[],
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<void> {
|
||||
if (calendarEvents.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
const { flattenedValues, valuesString } =
|
||||
getFlattenedValuesAndValuesStringForBatchRawQuery(calendarEvents, {
|
||||
title: 'text',
|
||||
isCanceled: 'boolean',
|
||||
isFullDay: 'boolean',
|
||||
startsAt: 'timestamptz',
|
||||
endsAt: 'timestamptz',
|
||||
externalCreatedAt: 'timestamptz',
|
||||
externalUpdatedAt: 'timestamptz',
|
||||
description: 'text',
|
||||
location: 'text',
|
||||
iCalUID: 'text',
|
||||
conferenceSolution: 'text',
|
||||
conferenceLinkLabel: 'text',
|
||||
conferenceLinkUrl: 'text',
|
||||
recurringEventExternalId: 'text',
|
||||
});
|
||||
|
||||
await this.workspaceDataSourceService.executeRawQuery(
|
||||
`UPDATE ${dataSourceSchema}."calendarEvent" AS "calendarEvent"
|
||||
SET "title" = "newData"."title",
|
||||
"isCanceled" = "newData"."isCanceled",
|
||||
"isFullDay" = "newData"."isFullDay",
|
||||
"startsAt" = "newData"."startsAt",
|
||||
"endsAt" = "newData"."endsAt",
|
||||
"externalCreatedAt" = "newData"."externalCreatedAt",
|
||||
"externalUpdatedAt" = "newData"."externalUpdatedAt",
|
||||
"description" = "newData"."description",
|
||||
"location" = "newData"."location",
|
||||
"conferenceSolution" = "newData"."conferenceSolution",
|
||||
"conferenceLinkLabel" = "newData"."conferenceLinkLabel",
|
||||
"conferenceLinkUrl" = "newData"."conferenceLinkUrl",
|
||||
"recurringEventExternalId" = "newData"."recurringEventExternalId"
|
||||
FROM (VALUES ${valuesString})
|
||||
AS "newData"("title", "isCanceled", "isFullDay", "startsAt", "endsAt", "externalCreatedAt", "externalUpdatedAt", "description", "location", "iCalUID", "conferenceSolution", "conferenceLinkLabel", "conferenceLinkUrl", "recurringEventExternalId")
|
||||
WHERE "calendarEvent"."iCalUID" = "newData"."iCalUID"`,
|
||||
flattenedValues,
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
|
||||
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
|
||||
import { CalendarEventCleanerService } from 'src/modules/calendar/services/calendar-event-cleaner/calendar-event-cleaner.service';
|
||||
import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-event.workspace-entity';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ObjectMetadataRepositoryModule.forFeature([CalendarEventWorkspaceEntity]),
|
||||
],
|
||||
imports: [TwentyORMModule.forFeature([CalendarEventWorkspaceEntity])],
|
||||
providers: [CalendarEventCleanerService],
|
||||
exports: [CalendarEventCleanerService],
|
||||
})
|
||||
|
||||
@ -1,27 +1,40 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
|
||||
import { CalendarEventRepository } from 'src/modules/calendar/repositories/calendar-event.repository';
|
||||
import { Any, IsNull } from 'typeorm';
|
||||
|
||||
import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator';
|
||||
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-event.workspace-entity';
|
||||
import { deleteUsingPagination } from 'src/modules/messaging/message-cleaner/utils/delete-using-pagination.util';
|
||||
|
||||
@Injectable()
|
||||
export class CalendarEventCleanerService {
|
||||
constructor(
|
||||
@InjectObjectMetadataRepository(CalendarEventWorkspaceEntity)
|
||||
private readonly calendarEventRepository: CalendarEventRepository,
|
||||
@InjectWorkspaceRepository(CalendarEventWorkspaceEntity)
|
||||
private readonly calendarEventRepository: WorkspaceRepository<CalendarEventWorkspaceEntity>,
|
||||
) {}
|
||||
|
||||
public async cleanWorkspaceCalendarEvents(workspaceId: string) {
|
||||
await deleteUsingPagination(
|
||||
workspaceId,
|
||||
500,
|
||||
this.calendarEventRepository.getNonAssociatedCalendarEventIdsPaginated.bind(
|
||||
this.calendarEventRepository,
|
||||
),
|
||||
this.calendarEventRepository.deleteByIds.bind(
|
||||
this.calendarEventRepository,
|
||||
),
|
||||
async (limit, offset) => {
|
||||
const nonAssociatedCalendarEvents =
|
||||
await this.calendarEventRepository.find({
|
||||
where: {
|
||||
calendarChannelEventAssociations: {
|
||||
id: IsNull(),
|
||||
},
|
||||
},
|
||||
take: limit,
|
||||
skip: offset,
|
||||
});
|
||||
|
||||
return nonAssociatedCalendarEvents.map(({ id }) => id);
|
||||
},
|
||||
async (ids) => {
|
||||
await this.calendarEventRepository.delete({ id: Any(ids) });
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,14 +1,17 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
|
||||
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
|
||||
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||
import { AddPersonIdAndWorkspaceMemberIdModule } from 'src/modules/calendar-messaging-participant/services/add-person-id-and-workspace-member-id/add-person-id-and-workspace-member-id.module';
|
||||
import { CalendarEventParticipantService } from 'src/modules/calendar/services/calendar-event-participant/calendar-event-participant.service';
|
||||
import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-event-participant.workspace-entity';
|
||||
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
WorkspaceDataSourceModule,
|
||||
TwentyORMModule.forFeature([CalendarEventParticipantWorkspaceEntity]),
|
||||
ObjectMetadataRepositoryModule.forFeature([PersonWorkspaceEntity]),
|
||||
AddPersonIdAndWorkspaceMemberIdModule,
|
||||
],
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
|
||||
import { EntityManager } from 'typeorm';
|
||||
import { Any, EntityManager } from 'typeorm';
|
||||
|
||||
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
|
||||
import { PersonRepository } from 'src/modules/person/repositories/person.repository';
|
||||
@ -9,17 +9,18 @@ import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/perso
|
||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||
import { getFlattenedValuesAndValuesStringForBatchRawQuery } from 'src/modules/calendar/utils/get-flattened-values-and-values-string-for-batch-raw-query.util';
|
||||
import { CalendarEventParticipant } from 'src/modules/calendar/types/calendar-event';
|
||||
import { CalendarEventParticipantRepository } from 'src/modules/calendar/repositories/calendar-event-participant.repository';
|
||||
import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-event-participant.workspace-entity';
|
||||
import { AddPersonIdAndWorkspaceMemberIdService } from 'src/modules/calendar-messaging-participant/services/add-person-id-and-workspace-member-id/add-person-id-and-workspace-member-id.service';
|
||||
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
|
||||
import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator';
|
||||
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
|
||||
@Injectable()
|
||||
export class CalendarEventParticipantService {
|
||||
constructor(
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
@InjectObjectMetadataRepository(CalendarEventParticipantWorkspaceEntity)
|
||||
private readonly calendarEventParticipantRepository: CalendarEventParticipantRepository,
|
||||
@InjectWorkspaceRepository(CalendarEventParticipantWorkspaceEntity)
|
||||
private readonly calendarEventParticipantRepository: WorkspaceRepository<CalendarEventParticipantWorkspaceEntity>,
|
||||
@InjectObjectMetadataRepository(PersonWorkspaceEntity)
|
||||
private readonly personRepository: PersonRepository,
|
||||
private readonly addPersonIdAndWorkspaceMemberIdService: AddPersonIdAndWorkspaceMemberIdService,
|
||||
@ -31,11 +32,11 @@ export class CalendarEventParticipantService {
|
||||
workspaceId: string,
|
||||
transactionManager?: EntityManager,
|
||||
): Promise<ObjectRecord<CalendarEventParticipantWorkspaceEntity>[]> {
|
||||
const participants =
|
||||
await this.calendarEventParticipantRepository.getByHandles(
|
||||
createdPeople.map((person) => person.email),
|
||||
workspaceId,
|
||||
);
|
||||
const participants = await this.calendarEventParticipantRepository.find({
|
||||
where: {
|
||||
handle: Any(createdPeople.map((person) => person.email)),
|
||||
},
|
||||
});
|
||||
|
||||
if (!participants) return [];
|
||||
|
||||
@ -132,33 +133,50 @@ export class CalendarEventParticipantService {
|
||||
workspaceMemberId?: string,
|
||||
) {
|
||||
const calendarEventParticipantsToUpdate =
|
||||
await this.calendarEventParticipantRepository.getByHandles(
|
||||
[email],
|
||||
workspaceId,
|
||||
);
|
||||
await this.calendarEventParticipantRepository.find({
|
||||
where: {
|
||||
handle: email,
|
||||
},
|
||||
});
|
||||
|
||||
const calendarEventParticipantIdsToUpdate =
|
||||
calendarEventParticipantsToUpdate.map((participant) => participant.id);
|
||||
|
||||
if (personId) {
|
||||
await this.calendarEventParticipantRepository.update(
|
||||
{
|
||||
id: Any(calendarEventParticipantIdsToUpdate),
|
||||
},
|
||||
{
|
||||
person: {
|
||||
id: personId,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const updatedCalendarEventParticipants =
|
||||
await this.calendarEventParticipantRepository.updateParticipantsPersonIdAndReturn(
|
||||
calendarEventParticipantIdsToUpdate,
|
||||
personId,
|
||||
workspaceId,
|
||||
);
|
||||
await this.calendarEventParticipantRepository.find({
|
||||
where: {
|
||||
id: Any(calendarEventParticipantIdsToUpdate),
|
||||
},
|
||||
});
|
||||
|
||||
this.eventEmitter.emit(`calendarEventParticipant.matched`, {
|
||||
workspaceId,
|
||||
userId: null,
|
||||
workspaceMemberId: null,
|
||||
calendarEventParticipants: updatedCalendarEventParticipants,
|
||||
});
|
||||
}
|
||||
if (workspaceMemberId) {
|
||||
await this.calendarEventParticipantRepository.updateParticipantsWorkspaceMemberId(
|
||||
calendarEventParticipantIdsToUpdate,
|
||||
workspaceMemberId,
|
||||
workspaceId,
|
||||
await this.calendarEventParticipantRepository.update(
|
||||
{
|
||||
id: Any(calendarEventParticipantIdsToUpdate),
|
||||
},
|
||||
{
|
||||
workspaceMember: {
|
||||
id: workspaceMemberId,
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -170,15 +188,23 @@ export class CalendarEventParticipantService {
|
||||
workspaceMemberId?: string,
|
||||
) {
|
||||
if (personId) {
|
||||
await this.calendarEventParticipantRepository.removePersonIdByHandle(
|
||||
handle,
|
||||
workspaceId,
|
||||
await this.calendarEventParticipantRepository.update(
|
||||
{
|
||||
handle,
|
||||
},
|
||||
{
|
||||
person: null,
|
||||
},
|
||||
);
|
||||
}
|
||||
if (workspaceMemberId) {
|
||||
await this.calendarEventParticipantRepository.removeWorkspaceMemberIdByHandle(
|
||||
handle,
|
||||
workspaceId,
|
||||
await this.calendarEventParticipantRepository.update(
|
||||
{
|
||||
handle,
|
||||
},
|
||||
{
|
||||
workspaceMember: null,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
|
||||
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
|
||||
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||
import { CalendarEventCleanerModule } from 'src/modules/calendar/services/calendar-event-cleaner/calendar-event-cleaner.module';
|
||||
import { CalendarEventParticipantModule } from 'src/modules/calendar/services/calendar-event-participant/calendar-event-participant.module';
|
||||
@ -20,12 +21,14 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta
|
||||
@Module({
|
||||
imports: [
|
||||
CalendarProvidersModule,
|
||||
ObjectMetadataRepositoryModule.forFeature([
|
||||
ConnectedAccountWorkspaceEntity,
|
||||
TwentyORMModule.forFeature([
|
||||
CalendarEventWorkspaceEntity,
|
||||
CalendarChannelWorkspaceEntity,
|
||||
CalendarChannelEventAssociationWorkspaceEntity,
|
||||
CalendarEventParticipantWorkspaceEntity,
|
||||
]),
|
||||
ObjectMetadataRepositoryModule.forFeature([
|
||||
ConnectedAccountWorkspaceEntity,
|
||||
BlocklistWorkspaceEntity,
|
||||
PersonWorkspaceEntity,
|
||||
WorkspaceMemberWorkspaceEntity,
|
||||
|
||||
@ -2,7 +2,7 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
import { Any, Repository } from 'typeorm';
|
||||
import { calendar_v3 as calendarV3 } from 'googleapis';
|
||||
import { GaxiosError } from 'gaxios';
|
||||
|
||||
@ -13,12 +13,7 @@ import {
|
||||
FeatureFlagKeys,
|
||||
} from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { GoogleCalendarClientProvider } from 'src/modules/calendar/services/providers/google-calendar/google-calendar.provider';
|
||||
import { CalendarChannelEventAssociationRepository } from 'src/modules/calendar/repositories/calendar-channel-event-association.repository';
|
||||
import { CalendarChannelRepository } from 'src/modules/calendar/repositories/calendar-channel.repository';
|
||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||
import { CalendarEventRepository } from 'src/modules/calendar/repositories/calendar-event.repository';
|
||||
import { formatGoogleCalendarEvent } from 'src/modules/calendar/utils/format-google-calendar-event.util';
|
||||
import { CalendarEventParticipantRepository } from 'src/modules/calendar/repositories/calendar-event-participant.repository';
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
|
||||
import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-event.workspace-entity';
|
||||
@ -28,7 +23,10 @@ import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/st
|
||||
import { BlocklistWorkspaceEntity } from 'src/modules/connected-account/standard-objects/blocklist.workspace-entity';
|
||||
import { CalendarEventCleanerService } from 'src/modules/calendar/services/calendar-event-cleaner/calendar-event-cleaner.service';
|
||||
import { CalendarEventParticipantService } from 'src/modules/calendar/services/calendar-event-participant/calendar-event-participant.service';
|
||||
import { CalendarEventWithParticipants } from 'src/modules/calendar/types/calendar-event';
|
||||
import {
|
||||
CalendarEventParticipant,
|
||||
CalendarEventWithParticipants,
|
||||
} from 'src/modules/calendar/types/calendar-event';
|
||||
import { filterOutBlocklistedEvents } from 'src/modules/calendar/utils/filter-out-blocklisted-events.util';
|
||||
import { InjectMessageQueue } from 'src/engine/integrations/message-queue/decorators/message-queue.decorator';
|
||||
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
|
||||
@ -37,7 +35,12 @@ import {
|
||||
CreateCompanyAndContactJob,
|
||||
CreateCompanyAndContactJobData,
|
||||
} from 'src/modules/connected-account/auto-companies-and-contacts-creation/jobs/create-company-and-contact.job';
|
||||
import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator';
|
||||
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
|
||||
import { isDefined } from 'src/utils/is-defined';
|
||||
import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
|
||||
import { InjectWorkspaceDatasource } from 'src/engine/twenty-orm/decorators/inject-workspace-datasource.decorator';
|
||||
|
||||
@Injectable()
|
||||
export class GoogleCalendarSyncService {
|
||||
@ -47,21 +50,20 @@ export class GoogleCalendarSyncService {
|
||||
private readonly googleCalendarClientProvider: GoogleCalendarClientProvider,
|
||||
@InjectObjectMetadataRepository(ConnectedAccountWorkspaceEntity)
|
||||
private readonly connectedAccountRepository: ConnectedAccountRepository,
|
||||
@InjectObjectMetadataRepository(CalendarEventWorkspaceEntity)
|
||||
private readonly calendarEventRepository: CalendarEventRepository,
|
||||
@InjectObjectMetadataRepository(CalendarChannelWorkspaceEntity)
|
||||
private readonly calendarChannelRepository: CalendarChannelRepository,
|
||||
@InjectObjectMetadataRepository(
|
||||
CalendarChannelEventAssociationWorkspaceEntity,
|
||||
)
|
||||
private readonly calendarChannelEventAssociationRepository: CalendarChannelEventAssociationRepository,
|
||||
@InjectObjectMetadataRepository(CalendarEventParticipantWorkspaceEntity)
|
||||
private readonly calendarEventParticipantsRepository: CalendarEventParticipantRepository,
|
||||
@InjectWorkspaceRepository(CalendarEventWorkspaceEntity)
|
||||
private readonly calendarEventRepository: WorkspaceRepository<CalendarEventWorkspaceEntity>,
|
||||
@InjectWorkspaceRepository(CalendarChannelWorkspaceEntity)
|
||||
private readonly calendarChannelRepository: WorkspaceRepository<CalendarChannelWorkspaceEntity>,
|
||||
@InjectWorkspaceRepository(CalendarChannelEventAssociationWorkspaceEntity)
|
||||
private readonly calendarChannelEventAssociationRepository: WorkspaceRepository<CalendarChannelEventAssociationWorkspaceEntity>,
|
||||
@InjectWorkspaceRepository(CalendarEventParticipantWorkspaceEntity)
|
||||
private readonly calendarEventParticipantsRepository: WorkspaceRepository<CalendarEventParticipantWorkspaceEntity>,
|
||||
@InjectObjectMetadataRepository(BlocklistWorkspaceEntity)
|
||||
private readonly blocklistRepository: BlocklistRepository,
|
||||
@InjectRepository(FeatureFlagEntity, 'core')
|
||||
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
@InjectWorkspaceDatasource()
|
||||
private readonly workspaceDataSource: WorkspaceDataSource,
|
||||
private readonly calendarEventCleanerService: CalendarEventCleanerService,
|
||||
private readonly calendarEventParticipantsService: CalendarEventParticipantService,
|
||||
@InjectMessageQueue(MessageQueue.contactCreationQueue)
|
||||
@ -92,11 +94,11 @@ export class GoogleCalendarSyncService {
|
||||
);
|
||||
}
|
||||
|
||||
const calendarChannel =
|
||||
await this.calendarChannelRepository.getFirstByConnectedAccountId(
|
||||
connectedAccountId,
|
||||
workspaceId,
|
||||
);
|
||||
const calendarChannel = await this.calendarChannelRepository.findOneBy({
|
||||
connectedAccount: {
|
||||
id: connectedAccountId,
|
||||
},
|
||||
});
|
||||
|
||||
const syncToken = calendarChannel?.syncCursor || undefined;
|
||||
|
||||
@ -122,6 +124,12 @@ export class GoogleCalendarSyncService {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!workspaceMemberId) {
|
||||
throw new Error(
|
||||
`Workspace member ID is undefined for connected account ${connectedAccountId} in workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
const blocklist = await this.getBlocklist(workspaceMemberId, workspaceId);
|
||||
|
||||
let filteredEvents = filterOutBlocklistedEvents(
|
||||
@ -143,11 +151,18 @@ export class GoogleCalendarSyncService {
|
||||
.filter((event) => event.status === 'cancelled')
|
||||
.map((event) => event.id as string);
|
||||
|
||||
const iCalUIDCalendarEventIdMap =
|
||||
await this.calendarEventRepository.getICalUIDCalendarEventIdMap(
|
||||
filteredEvents.map((calendarEvent) => calendarEvent.iCalUID as string),
|
||||
workspaceId,
|
||||
);
|
||||
const existingCalendarEvents = await this.calendarEventRepository.find({
|
||||
where: {
|
||||
iCalUID: Any(filteredEvents.map((event) => event.iCalUID as string)),
|
||||
},
|
||||
});
|
||||
|
||||
const iCalUIDCalendarEventIdMap = new Map(
|
||||
existingCalendarEvents.map((calendarEvent) => [
|
||||
calendarEvent.iCalUID,
|
||||
calendarEvent.id,
|
||||
]),
|
||||
);
|
||||
|
||||
const formattedEvents = filteredEvents.map((event) =>
|
||||
formatGoogleCalendarEvent(event, iCalUIDCalendarEventIdMap),
|
||||
@ -157,31 +172,34 @@ export class GoogleCalendarSyncService {
|
||||
|
||||
let startTime = Date.now();
|
||||
|
||||
const existingEvents = await this.calendarEventRepository.getByICalUIDs(
|
||||
formattedEvents.map((event) => event.iCalUID),
|
||||
workspaceId,
|
||||
const existingEventsICalUIDs = existingCalendarEvents.map(
|
||||
(calendarEvent) => calendarEvent.iCalUID,
|
||||
);
|
||||
|
||||
const existingEventsICalUIDs = existingEvents.map((event) => event.iCalUID);
|
||||
|
||||
let endTime = Date.now();
|
||||
|
||||
const eventsToSave = formattedEvents.filter(
|
||||
(event) => !existingEventsICalUIDs.includes(event.iCalUID),
|
||||
(calendarEvent) =>
|
||||
!existingEventsICalUIDs.includes(calendarEvent.iCalUID),
|
||||
);
|
||||
|
||||
const eventsToUpdate = formattedEvents.filter((event) =>
|
||||
existingEventsICalUIDs.includes(event.iCalUID),
|
||||
const eventsToUpdate = formattedEvents.filter((calendarEvent) =>
|
||||
existingEventsICalUIDs.includes(calendarEvent.iCalUID),
|
||||
);
|
||||
|
||||
startTime = Date.now();
|
||||
|
||||
const existingCalendarChannelEventAssociations =
|
||||
await this.calendarChannelEventAssociationRepository.getByEventExternalIdsAndCalendarChannelId(
|
||||
formattedEvents.map((event) => event.externalId),
|
||||
calendarChannelId,
|
||||
workspaceId,
|
||||
);
|
||||
await this.calendarChannelEventAssociationRepository.find({
|
||||
where: {
|
||||
eventExternalId: Any(
|
||||
formattedEvents.map((calendarEvent) => calendarEvent.id),
|
||||
),
|
||||
calendarChannel: {
|
||||
id: calendarChannelId,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
endTime = Date.now();
|
||||
|
||||
@ -193,14 +211,14 @@ export class GoogleCalendarSyncService {
|
||||
|
||||
const calendarChannelEventAssociationsToSave = formattedEvents
|
||||
.filter(
|
||||
(event) =>
|
||||
(calendarEvent) =>
|
||||
!existingCalendarChannelEventAssociations.some(
|
||||
(association) => association.eventExternalId === event.id,
|
||||
(association) => association.eventExternalId === calendarEvent.id,
|
||||
),
|
||||
)
|
||||
.map((event) => ({
|
||||
calendarEventId: event.id,
|
||||
eventExternalId: event.externalId,
|
||||
.map((calendarEvent) => ({
|
||||
calendarEventId: calendarEvent.id,
|
||||
eventExternalId: calendarEvent.externalId,
|
||||
calendarChannelId,
|
||||
}));
|
||||
|
||||
@ -216,11 +234,12 @@ export class GoogleCalendarSyncService {
|
||||
|
||||
startTime = Date.now();
|
||||
|
||||
await this.calendarChannelEventAssociationRepository.deleteByEventExternalIdsAndCalendarChannelId(
|
||||
cancelledEventExternalIds,
|
||||
calendarChannelId,
|
||||
workspaceId,
|
||||
);
|
||||
await this.calendarChannelEventAssociationRepository.delete({
|
||||
eventExternalId: Any(cancelledEventExternalIds),
|
||||
calendarChannel: {
|
||||
id: calendarChannelId,
|
||||
},
|
||||
});
|
||||
|
||||
endTime = Date.now();
|
||||
|
||||
@ -257,10 +276,13 @@ export class GoogleCalendarSyncService {
|
||||
|
||||
startTime = Date.now();
|
||||
|
||||
await this.calendarChannelRepository.updateSyncCursor(
|
||||
nextSyncToken,
|
||||
calendarChannel.id,
|
||||
workspaceId,
|
||||
await this.calendarChannelRepository.update(
|
||||
{
|
||||
id: calendarChannel.id,
|
||||
},
|
||||
{
|
||||
syncCursor: nextSyncToken,
|
||||
},
|
||||
);
|
||||
|
||||
endTime = Date.now();
|
||||
@ -337,10 +359,13 @@ export class GoogleCalendarSyncService {
|
||||
throw error;
|
||||
}
|
||||
|
||||
await this.calendarChannelRepository.updateSyncCursor(
|
||||
null,
|
||||
connectedAccountId,
|
||||
workspaceId,
|
||||
await this.calendarChannelRepository.update(
|
||||
{
|
||||
id: connectedAccountId,
|
||||
},
|
||||
{
|
||||
syncCursor: '',
|
||||
},
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
@ -395,11 +420,6 @@ export class GoogleCalendarSyncService {
|
||||
calendarChannel: CalendarChannelWorkspaceEntity,
|
||||
workspaceId: string,
|
||||
): Promise<void> {
|
||||
const dataSourceMetadata =
|
||||
await this.workspaceDataSourceService.connectToWorkspaceDataSource(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const participantsToSave = eventsToSave.flatMap(
|
||||
(event) => event.participants,
|
||||
);
|
||||
@ -415,103 +435,154 @@ export class GoogleCalendarSyncService {
|
||||
[];
|
||||
|
||||
try {
|
||||
await dataSourceMetadata?.transaction(async (transactionManager) => {
|
||||
startTime = Date.now();
|
||||
await this.workspaceDataSource?.transaction(
|
||||
async (transactionManager) => {
|
||||
startTime = Date.now();
|
||||
|
||||
await this.calendarEventRepository.saveCalendarEvents(
|
||||
eventsToSave,
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
await this.calendarEventRepository.save(
|
||||
eventsToSave,
|
||||
{},
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
endTime = Date.now();
|
||||
endTime = Date.now();
|
||||
|
||||
this.logger.log(
|
||||
`google calendar sync for workspace ${workspaceId} and account ${
|
||||
connectedAccount.id
|
||||
}: saving ${eventsToSave.length} events in ${endTime - startTime}ms.`,
|
||||
);
|
||||
this.logger.log(
|
||||
`google calendar sync for workspace ${workspaceId} and account ${
|
||||
connectedAccount.id
|
||||
}: saving ${eventsToSave.length} events in ${
|
||||
endTime - startTime
|
||||
}ms.`,
|
||||
);
|
||||
|
||||
startTime = Date.now();
|
||||
startTime = Date.now();
|
||||
|
||||
await this.calendarEventRepository.updateCalendarEvents(
|
||||
eventsToUpdate,
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
await this.calendarChannelRepository.save(
|
||||
eventsToUpdate,
|
||||
{},
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
endTime = Date.now();
|
||||
endTime = Date.now();
|
||||
|
||||
this.logger.log(
|
||||
`google calendar sync for workspace ${workspaceId} and account ${
|
||||
connectedAccount.id
|
||||
}: updating ${eventsToUpdate.length} events in ${
|
||||
endTime - startTime
|
||||
}ms.`,
|
||||
);
|
||||
this.logger.log(
|
||||
`google calendar sync for workspace ${workspaceId} and account ${
|
||||
connectedAccount.id
|
||||
}: updating ${eventsToUpdate.length} events in ${
|
||||
endTime - startTime
|
||||
}ms.`,
|
||||
);
|
||||
|
||||
startTime = Date.now();
|
||||
startTime = Date.now();
|
||||
|
||||
await this.calendarChannelEventAssociationRepository.saveCalendarChannelEventAssociations(
|
||||
calendarChannelEventAssociationsToSave,
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
await this.calendarChannelEventAssociationRepository.save(
|
||||
calendarChannelEventAssociationsToSave,
|
||||
{},
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
endTime = Date.now();
|
||||
endTime = Date.now();
|
||||
|
||||
this.logger.log(
|
||||
`google calendar sync for workspace ${workspaceId} and account ${
|
||||
connectedAccount.id
|
||||
}: saving calendar channel event associations in ${
|
||||
endTime - startTime
|
||||
}ms.`,
|
||||
);
|
||||
this.logger.log(
|
||||
`google calendar sync for workspace ${workspaceId} and account ${
|
||||
connectedAccount.id
|
||||
}: saving calendar channel event associations in ${
|
||||
endTime - startTime
|
||||
}ms.`,
|
||||
);
|
||||
|
||||
startTime = Date.now();
|
||||
startTime = Date.now();
|
||||
|
||||
const newCalendarEventParticipants =
|
||||
await this.calendarEventParticipantsRepository.updateCalendarEventParticipantsAndReturnNewOnes(
|
||||
const existingCalendarEventParticipants =
|
||||
await this.calendarEventParticipantsRepository.find({
|
||||
where: {
|
||||
calendarEvent: {
|
||||
id: Any(
|
||||
participantsToUpdate
|
||||
.map((participant) => participant.calendarEventId)
|
||||
.filter(isDefined),
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
calendarEventParticipantsToDelete,
|
||||
newCalendarEventParticipants,
|
||||
} = participantsToUpdate.reduce(
|
||||
(acc, calendarEventParticipant) => {
|
||||
const existingCalendarEventParticipant =
|
||||
existingCalendarEventParticipants.find(
|
||||
(existingCalendarEventParticipant) =>
|
||||
existingCalendarEventParticipant.handle ===
|
||||
calendarEventParticipant.handle,
|
||||
);
|
||||
|
||||
if (existingCalendarEventParticipant) {
|
||||
acc.calendarEventParticipantsToDelete.push(
|
||||
existingCalendarEventParticipant,
|
||||
);
|
||||
} else {
|
||||
acc.newCalendarEventParticipants.push(calendarEventParticipant);
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
calendarEventParticipantsToDelete:
|
||||
[] as CalendarEventParticipantWorkspaceEntity[],
|
||||
newCalendarEventParticipants: [] as CalendarEventParticipant[],
|
||||
},
|
||||
);
|
||||
|
||||
await this.calendarEventParticipantsRepository.delete({
|
||||
id: Any(
|
||||
calendarEventParticipantsToDelete.map(
|
||||
(calendarEventParticipant) => calendarEventParticipant.id,
|
||||
),
|
||||
),
|
||||
});
|
||||
|
||||
await this.calendarEventParticipantsRepository.save(
|
||||
participantsToUpdate,
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
endTime = Date.now();
|
||||
endTime = Date.now();
|
||||
|
||||
participantsToSave.push(...newCalendarEventParticipants);
|
||||
participantsToSave.push(...newCalendarEventParticipants);
|
||||
|
||||
this.logger.log(
|
||||
`google calendar sync for workspace ${workspaceId} and account ${
|
||||
connectedAccount.id
|
||||
}: updating participants in ${endTime - startTime}ms.`,
|
||||
);
|
||||
|
||||
startTime = Date.now();
|
||||
|
||||
const savedCalendarEventParticipants =
|
||||
await this.calendarEventParticipantsService.saveCalendarEventParticipants(
|
||||
participantsToSave,
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
this.logger.log(
|
||||
`google calendar sync for workspace ${workspaceId} and account ${
|
||||
connectedAccount.id
|
||||
}: updating participants in ${endTime - startTime}ms.`,
|
||||
);
|
||||
|
||||
savedCalendarEventParticipantsToEmit.push(
|
||||
...savedCalendarEventParticipants,
|
||||
);
|
||||
startTime = Date.now();
|
||||
|
||||
endTime = Date.now();
|
||||
const savedCalendarEventParticipants =
|
||||
await this.calendarEventParticipantsService.saveCalendarEventParticipants(
|
||||
participantsToSave,
|
||||
workspaceId,
|
||||
transactionManager,
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`google calendar sync for workspace ${workspaceId} and account ${
|
||||
connectedAccount.id
|
||||
}: saving participants in ${endTime - startTime}ms.`,
|
||||
);
|
||||
});
|
||||
savedCalendarEventParticipantsToEmit.push(
|
||||
...savedCalendarEventParticipants,
|
||||
);
|
||||
|
||||
endTime = Date.now();
|
||||
|
||||
this.logger.log(
|
||||
`google calendar sync for workspace ${workspaceId} and account ${
|
||||
connectedAccount.id
|
||||
}: saving participants in ${endTime - startTime}ms.`,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
this.eventEmitter.emit(`calendarEventParticipant.matched`, {
|
||||
workspaceId,
|
||||
userId: connectedAccount.accountOwnerId,
|
||||
workspaceMemberId: connectedAccount.accountOwnerId,
|
||||
calendarEventParticipants: savedCalendarEventParticipantsToEmit,
|
||||
});
|
||||
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
|
||||
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
|
||||
import { WorkspaceGoogleCalendarSyncService } from 'src/modules/calendar/services/workspace-google-calendar-sync/workspace-google-calendar-sync.service';
|
||||
import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-channel.workspace-entity';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ObjectMetadataRepositoryModule.forFeature([CalendarChannelWorkspaceEntity]),
|
||||
],
|
||||
imports: [TwentyORMModule.forFeature([CalendarChannelWorkspaceEntity])],
|
||||
providers: [WorkspaceGoogleCalendarSyncService],
|
||||
exports: [WorkspaceGoogleCalendarSyncService],
|
||||
})
|
||||
|
||||
@ -3,19 +3,19 @@ import { Injectable } from '@nestjs/common';
|
||||
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 { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-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 {
|
||||
GoogleCalendarSyncJobData,
|
||||
GoogleCalendarSyncJob,
|
||||
} from 'src/modules/calendar/jobs/google-calendar-sync.job';
|
||||
import { CalendarChannelRepository } from 'src/modules/calendar/repositories/calendar-channel.repository';
|
||||
import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-channel.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceGoogleCalendarSyncService {
|
||||
constructor(
|
||||
@InjectObjectMetadataRepository(CalendarChannelWorkspaceEntity)
|
||||
private readonly calendarChannelRepository: CalendarChannelRepository,
|
||||
@InjectWorkspaceRepository(CalendarChannelWorkspaceEntity)
|
||||
private readonly calendarChannelRepository: WorkspaceRepository<CalendarChannelWorkspaceEntity>,
|
||||
@InjectMessageQueue(MessageQueue.calendarQueue)
|
||||
private readonly messageQueueService: MessageQueueService,
|
||||
) {}
|
||||
@ -23,8 +23,7 @@ export class WorkspaceGoogleCalendarSyncService {
|
||||
public async startWorkspaceGoogleCalendarSync(
|
||||
workspaceId: string,
|
||||
): Promise<void> {
|
||||
const calendarChannels =
|
||||
await this.calendarChannelRepository.getAll(workspaceId);
|
||||
const calendarChannels = await this.calendarChannelRepository.find({});
|
||||
|
||||
for (const calendarChannel of calendarChannels) {
|
||||
if (!calendarChannel?.isSyncEnabled) {
|
||||
@ -35,7 +34,7 @@ export class WorkspaceGoogleCalendarSyncService {
|
||||
GoogleCalendarSyncJob.name,
|
||||
{
|
||||
workspaceId,
|
||||
connectedAccountId: calendarChannel.connectedAccountId,
|
||||
connectedAccountId: calendarChannel.connectedAccount.id,
|
||||
},
|
||||
{
|
||||
retryLimit: 2,
|
||||
|
||||
@ -120,7 +120,7 @@ export class CalendarEventParticipantWorkspaceEntity extends BaseWorkspaceEntity
|
||||
inverseSideFieldKey: 'calendarEventParticipants',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
person: Relation<PersonWorkspaceEntity>;
|
||||
person: Relation<PersonWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: CALENDAR_EVENT_PARTICIPANT_STANDARD_FIELD_IDS.workspaceMember,
|
||||
@ -133,5 +133,5 @@ export class CalendarEventParticipantWorkspaceEntity extends BaseWorkspaceEntity
|
||||
inverseSideFieldKey: 'calendarEventParticipants',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
workspaceMember: Relation<WorkspaceMemberWorkspaceEntity>;
|
||||
workspaceMember: Relation<WorkspaceMemberWorkspaceEntity> | null;
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ export class CompanyWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconUsers',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
employees: number;
|
||||
employees: number | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: COMPANY_STANDARD_FIELD_IDS.linkedinLink,
|
||||
@ -78,7 +78,7 @@ export class CompanyWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconBrandLinkedin',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
linkedinLink: LinkMetadata;
|
||||
linkedinLink: LinkMetadata | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: COMPANY_STANDARD_FIELD_IDS.xLink,
|
||||
@ -88,7 +88,7 @@ export class CompanyWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconBrandX',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
xLink: LinkMetadata;
|
||||
xLink: LinkMetadata | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: COMPANY_STANDARD_FIELD_IDS.annualRecurringRevenue,
|
||||
@ -99,7 +99,7 @@ export class CompanyWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconMoneybag',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
annualRecurringRevenue: CurrencyMetadata;
|
||||
annualRecurringRevenue: CurrencyMetadata | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: COMPANY_STANDARD_FIELD_IDS.idealCustomerProfile,
|
||||
@ -121,7 +121,7 @@ export class CompanyWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
@WorkspaceIsNullable()
|
||||
position: number;
|
||||
position: number | null;
|
||||
|
||||
// Relations
|
||||
@WorkspaceRelation({
|
||||
@ -149,7 +149,7 @@ export class CompanyWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
accountOwner: Relation<WorkspaceMemberWorkspaceEntity>;
|
||||
accountOwner: Relation<WorkspaceMemberWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: COMPANY_STANDARD_FIELD_IDS.activityTargets,
|
||||
|
||||
@ -8,7 +8,6 @@ import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repos
|
||||
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 { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||
import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-event-participant.workspace-entity';
|
||||
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 { MessagingCommonModule } from 'src/modules/messaging/common/messaging-common.module';
|
||||
@ -20,7 +19,6 @@ import { MessagingCommonModule } from 'src/modules/messaging/common/messaging-co
|
||||
ObjectMetadataRepositoryModule.forFeature([
|
||||
PersonWorkspaceEntity,
|
||||
WorkspaceMemberWorkspaceEntity,
|
||||
CalendarEventParticipantWorkspaceEntity,
|
||||
]),
|
||||
MessagingCommonModule,
|
||||
WorkspaceDataSourceModule,
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
import { Process } from 'src/engine/integrations/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/integrations/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
|
||||
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
|
||||
import { CreateCompanyAndContactService } from 'src/modules/connected-account/auto-companies-and-contacts-creation/services/create-company-and-contact.service';
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
|
||||
export type CreateCompanyAndContactJobData = {
|
||||
workspaceId: string;
|
||||
connectedAccount: ObjectRecord<ConnectedAccountWorkspaceEntity>;
|
||||
connectedAccount: ConnectedAccountWorkspaceEntity;
|
||||
contactsToCreate: {
|
||||
displayName: string;
|
||||
handle: string;
|
||||
|
||||
@ -15,14 +15,15 @@ import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/perso
|
||||
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 { Contacts } from 'src/modules/connected-account/auto-companies-and-contacts-creation/types/contact.type';
|
||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.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 { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
|
||||
import { MessagingMessageParticipantService } from 'src/modules/messaging/common/services/messaging-message-participant.service';
|
||||
import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
|
||||
import { MessageParticipantWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-participant.workspace-entity';
|
||||
import { CalendarEventParticipantWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-event-participant.workspace-entity';
|
||||
import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
|
||||
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';
|
||||
|
||||
@Injectable()
|
||||
export class CreateCompanyAndContactService {
|
||||
@ -33,7 +34,8 @@ export class CreateCompanyAndContactService {
|
||||
private readonly personRepository: PersonRepository,
|
||||
@InjectObjectMetadataRepository(WorkspaceMemberWorkspaceEntity)
|
||||
private readonly workspaceMemberRepository: WorkspaceMemberRepository,
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
@InjectWorkspaceDatasource()
|
||||
private readonly workspaceDataSource: WorkspaceDataSource,
|
||||
private readonly messageParticipantService: MessagingMessageParticipantService,
|
||||
private readonly calendarEventParticipantService: CalendarEventParticipantService,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
@ -130,21 +132,16 @@ export class CreateCompanyAndContactService {
|
||||
}
|
||||
|
||||
async createCompaniesAndContactsAndUpdateParticipants(
|
||||
connectedAccount: ObjectRecord<ConnectedAccountWorkspaceEntity>,
|
||||
connectedAccount: ConnectedAccountWorkspaceEntity,
|
||||
contactsToCreate: Contacts,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const { dataSource: workspaceDataSource } =
|
||||
await this.workspaceDataSourceService.connectedToWorkspaceDataSourceAndReturnMetadata(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
let updatedMessageParticipants: ObjectRecord<MessageParticipantWorkspaceEntity>[] =
|
||||
[];
|
||||
let updatedCalendarEventParticipants: ObjectRecord<CalendarEventParticipantWorkspaceEntity>[] =
|
||||
[];
|
||||
|
||||
await workspaceDataSource?.transaction(
|
||||
await this.workspaceDataSource?.transaction(
|
||||
async (transactionManager: EntityManager) => {
|
||||
const createdPeople = await this.createCompaniesAndPeople(
|
||||
connectedAccount.handle,
|
||||
@ -171,13 +168,13 @@ export class CreateCompanyAndContactService {
|
||||
|
||||
this.eventEmitter.emit(`messageParticipant.matched`, {
|
||||
workspaceId,
|
||||
userId: connectedAccount.accountOwnerId,
|
||||
workspaceMemberId: connectedAccount.accountOwnerId,
|
||||
messageParticipants: updatedMessageParticipants,
|
||||
});
|
||||
|
||||
this.eventEmitter.emit(`calendarEventParticipant.matched`, {
|
||||
workspaceId,
|
||||
userId: connectedAccount.accountOwnerId,
|
||||
workspaceMemberId: connectedAccount.accountOwnerId,
|
||||
calendarEventParticipants: updatedCalendarEventParticipants,
|
||||
});
|
||||
}
|
||||
|
||||
@ -80,6 +80,12 @@ export class GoogleAPIRefreshAccessTokenService {
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (!messageChannel.connectedAccountId) {
|
||||
throw new Error(
|
||||
`No connected account ID found for message channel ${messageChannel.id} in workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
await this.connectedAccountRepository.updateAuthFailedAt(
|
||||
messageChannel.connectedAccountId,
|
||||
workspaceId,
|
||||
|
||||
@ -86,7 +86,7 @@ export class ConnectedAccountWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconX',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
authFailedAt: Date;
|
||||
authFailedAt: Date | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: CONNECTED_ACCOUNT_STANDARD_FIELD_IDS.accountOwner,
|
||||
@ -100,6 +100,8 @@ export class ConnectedAccountWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
})
|
||||
accountOwner: Relation<WorkspaceMemberWorkspaceEntity>;
|
||||
|
||||
accountOwnerId: string;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: CONNECTED_ACCOUNT_STANDARD_FIELD_IDS.messageChannels,
|
||||
type: RelationMetadataType.ONE_TO_MANY,
|
||||
|
||||
@ -63,7 +63,7 @@ export class FavoriteWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'favorites',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
person: Relation<PersonWorkspaceEntity>;
|
||||
person: Relation<PersonWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: FAVORITE_STANDARD_FIELD_IDS.company,
|
||||
@ -76,7 +76,7 @@ export class FavoriteWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'favorites',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
company: Relation<CompanyWorkspaceEntity>;
|
||||
company: Relation<CompanyWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: FAVORITE_STANDARD_FIELD_IDS.opportunity,
|
||||
@ -89,7 +89,7 @@ export class FavoriteWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'favorites',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
opportunity: Relation<OpportunityWorkspaceEntity>;
|
||||
opportunity: Relation<OpportunityWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceDynamicRelation({
|
||||
type: RelationMetadataType.MANY_TO_ONE,
|
||||
|
||||
@ -56,6 +56,12 @@ export class BlocklistItemDeleteMessagesJob {
|
||||
`Deleting messages from ${handle} in workspace ${workspaceId} for workspace member ${workspaceMemberId}`,
|
||||
);
|
||||
|
||||
if (!workspaceMemberId) {
|
||||
throw new Error(
|
||||
`Workspace member ID is not defined for blocklist item ${blocklistItemId} in workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
const messageChannels =
|
||||
await this.messageChannelRepository.getIdsByWorkspaceMemberId(
|
||||
workspaceMemberId,
|
||||
|
||||
@ -9,6 +9,7 @@ import { MessageChannelRepository } from 'src/modules/messaging/common/repositor
|
||||
import { MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
|
||||
import { WorkspaceMemberRepository } from 'src/modules/workspace-member/repositories/workspace-member.repository';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
import { isDefined } from 'src/utils/is-defined';
|
||||
|
||||
export class CanAccessMessageThreadService {
|
||||
constructor(
|
||||
@ -46,7 +47,9 @@ export class CanAccessMessageThreadService {
|
||||
|
||||
const messageChannelsConnectedAccounts =
|
||||
await this.connectedAccountRepository.getByIds(
|
||||
messageChannels.map((channel) => channel.connectedAccountId),
|
||||
messageChannels
|
||||
.map((channel) => channel.connectedAccountId)
|
||||
.filter(isDefined),
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
|
||||
@ -211,6 +211,12 @@ export class MessagingErrorHandlingService {
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (!messageChannel.connectedAccountId) {
|
||||
throw new Error(
|
||||
`Connected account ID is not defined for message channel ${messageChannel.id} in workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
await this.connectedAccountRepository.updateAuthFailedAt(
|
||||
messageChannel.connectedAccountId,
|
||||
workspaceId,
|
||||
|
||||
@ -149,7 +149,7 @@ export class MessagingMessageParticipantService {
|
||||
|
||||
this.eventEmitter.emit(`messageParticipant.matched`, {
|
||||
workspaceId,
|
||||
userId: null,
|
||||
workspaceMemberId: null,
|
||||
messageParticipants: updatedMessageParticipants,
|
||||
});
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ export class MessagingSaveMessagesAndEnqueueContactCreationService {
|
||||
|
||||
this.eventEmitter.emit(`messageParticipant.matched`, {
|
||||
workspaceId,
|
||||
userId: connectedAccount.accountOwnerId,
|
||||
workspaceMemberId: connectedAccount.accountOwnerId,
|
||||
messageParticipants: savedMessageParticipants,
|
||||
});
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ export class MessageChannelMessageAssociationWorkspaceEntity extends BaseWorkspa
|
||||
icon: 'IconHash',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
messageExternalId: string;
|
||||
messageExternalId: string | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId:
|
||||
@ -46,7 +46,7 @@ export class MessageChannelMessageAssociationWorkspaceEntity extends BaseWorkspa
|
||||
icon: 'IconHash',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
messageThreadExternalId: string;
|
||||
messageThreadExternalId: string | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId:
|
||||
@ -60,7 +60,7 @@ export class MessageChannelMessageAssociationWorkspaceEntity extends BaseWorkspa
|
||||
inverseSideFieldKey: 'messageChannelMessageAssociations',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
messageChannel: Relation<MessageChannelWorkspaceEntity>;
|
||||
messageChannel: Relation<MessageChannelWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: MESSAGE_CHANNEL_MESSAGE_ASSOCIATION_STANDARD_FIELD_IDS.message,
|
||||
@ -73,7 +73,7 @@ export class MessageChannelMessageAssociationWorkspaceEntity extends BaseWorkspa
|
||||
inverseSideFieldKey: 'messageChannelMessageAssociations',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
message: Relation<MessageWorkspaceEntity>;
|
||||
message: Relation<MessageWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId:
|
||||
@ -87,5 +87,5 @@ export class MessageChannelMessageAssociationWorkspaceEntity extends BaseWorkspa
|
||||
inverseSideFieldKey: 'messageChannelMessageAssociations',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
messageThread: Relation<MessageThreadWorkspaceEntity>;
|
||||
messageThread: Relation<MessageThreadWorkspaceEntity> | null;
|
||||
}
|
||||
|
||||
@ -162,7 +162,7 @@ export class MessageChannelWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconHistory',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
syncedAt: string;
|
||||
syncedAt: string | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: MESSAGE_CHANNEL_STANDARD_FIELD_IDS.syncStatus,
|
||||
@ -224,7 +224,7 @@ export class MessageChannelWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
],
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
syncStatus: MessageChannelSyncStatus;
|
||||
syncStatus: MessageChannelSyncStatus | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: MESSAGE_CHANNEL_STANDARD_FIELD_IDS.syncStage,
|
||||
@ -282,7 +282,7 @@ export class MessageChannelWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconHistory',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
syncStageStartedAt: string;
|
||||
syncStageStartedAt: string | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: MESSAGE_CHANNEL_STANDARD_FIELD_IDS.throttleFailureCount,
|
||||
|
||||
@ -83,7 +83,7 @@ export class MessageParticipantWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'messageParticipants',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
person: Relation<PersonWorkspaceEntity>;
|
||||
person: Relation<PersonWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: MESSAGE_PARTICIPANT_STANDARD_FIELD_IDS.workspaceMember,
|
||||
@ -96,5 +96,5 @@ export class MessageParticipantWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'messageParticipants',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
workspaceMember: Relation<WorkspaceMemberWorkspaceEntity>;
|
||||
workspaceMember: Relation<WorkspaceMemberWorkspaceEntity> | null;
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ export class MessageWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconCalendar',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
receivedAt: string;
|
||||
receivedAt: string | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: MESSAGE_STANDARD_FIELD_IDS.messageThread,
|
||||
@ -92,7 +92,7 @@ export class MessageWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
messageThread: Relation<MessageThreadWorkspaceEntity>;
|
||||
messageThread: Relation<MessageThreadWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: MESSAGE_STANDARD_FIELD_IDS.messageParticipants,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { Logger, Scope } from '@nestjs/common';
|
||||
|
||||
import { Process } from 'src/engine/integrations/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/integrations/message-queue/decorators/processor.decorator';
|
||||
@ -21,7 +21,10 @@ export type MessagingMessageListFetchJobData = {
|
||||
workspaceId: string;
|
||||
};
|
||||
|
||||
@Processor(MessageQueue.messagingQueue)
|
||||
@Processor({
|
||||
queueName: MessageQueue.messagingQueue,
|
||||
scope: Scope.REQUEST,
|
||||
})
|
||||
export class MessagingMessageListFetchJob {
|
||||
private readonly logger = new Logger(MessagingMessageListFetchJob.name);
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { Scope } from '@nestjs/common';
|
||||
|
||||
import { Process } from 'src/engine/integrations/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/integrations/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
|
||||
@ -18,7 +20,10 @@ export type MessagingMessagesImportJobData = {
|
||||
workspaceId: string;
|
||||
};
|
||||
|
||||
@Processor(MessageQueue.messagingQueue)
|
||||
@Processor({
|
||||
queueName: MessageQueue.messagingQueue,
|
||||
scope: Scope.REQUEST,
|
||||
})
|
||||
export class MessagingMessagesImportJob {
|
||||
constructor(
|
||||
@InjectObjectMetadataRepository(ConnectedAccountWorkspaceEntity)
|
||||
|
||||
@ -25,7 +25,7 @@ export class MessageParticipantListener {
|
||||
@OnEvent('messageParticipant.matched')
|
||||
public async handleMessageParticipantMatched(payload: {
|
||||
workspaceId: string;
|
||||
userId: string;
|
||||
workspaceMemberId: string;
|
||||
messageParticipants: ObjectRecord<MessageParticipantWorkspaceEntity>[];
|
||||
}): Promise<void> {
|
||||
const messageParticipants = payload.messageParticipants ?? [];
|
||||
@ -60,7 +60,7 @@ export class MessageParticipantListener {
|
||||
properties: null,
|
||||
objectName: 'message',
|
||||
recordId: participant.personId,
|
||||
workspaceMemberId: payload.userId,
|
||||
workspaceMemberId: payload.workspaceMemberId,
|
||||
workspaceId: payload.workspaceId,
|
||||
linkedObjectMetadataId: messageObjectMetadata.id,
|
||||
linkedRecordId: participant.messageId,
|
||||
|
||||
@ -67,6 +67,9 @@ export class MessagingMessageChannelSyncStatusMonitoringCronJob {
|
||||
await this.messageChannelRepository.getAll(workspaceId);
|
||||
|
||||
for (const messageChannel of messageChannels) {
|
||||
if (!messageChannel.syncStatus) {
|
||||
continue;
|
||||
}
|
||||
await this.messagingTelemetryService.track({
|
||||
eventName: `message_channel.monitoring.sync_status.${snakeCase(
|
||||
messageChannel.syncStatus,
|
||||
|
||||
@ -49,7 +49,7 @@ export class OpportunityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconCurrencyDollar',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
amount: CurrencyMetadata;
|
||||
amount: CurrencyMetadata | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: OPPORTUNITY_STANDARD_FIELD_IDS.closeDate,
|
||||
@ -59,7 +59,7 @@ export class OpportunityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconCalendarEvent',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
closeDate: Date;
|
||||
closeDate: Date | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: OPPORTUNITY_STANDARD_FIELD_IDS.probability,
|
||||
@ -102,7 +102,7 @@ export class OpportunityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
@WorkspaceIsNullable()
|
||||
position: number;
|
||||
position: number | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: OPPORTUNITY_STANDARD_FIELD_IDS.pointOfContact,
|
||||
@ -116,7 +116,7 @@ export class OpportunityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
pointOfContact: Relation<PersonWorkspaceEntity>;
|
||||
pointOfContact: Relation<PersonWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: OPPORTUNITY_STANDARD_FIELD_IDS.company,
|
||||
@ -130,7 +130,7 @@ export class OpportunityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
company: Relation<CompanyWorkspaceEntity>;
|
||||
company: Relation<CompanyWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: OPPORTUNITY_STANDARD_FIELD_IDS.favorites,
|
||||
|
||||
@ -41,7 +41,7 @@ export class PersonWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconUser',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
name: FullNameMetadata;
|
||||
name: FullNameMetadata | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: PERSON_STANDARD_FIELD_IDS.email,
|
||||
@ -60,7 +60,7 @@ export class PersonWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconBrandLinkedin',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
linkedinLink: LinkMetadata;
|
||||
linkedinLink: LinkMetadata | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: PERSON_STANDARD_FIELD_IDS.xLink,
|
||||
@ -70,7 +70,7 @@ export class PersonWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconBrandX',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
xLink: LinkMetadata;
|
||||
xLink: LinkMetadata | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: PERSON_STANDARD_FIELD_IDS.jobTitle,
|
||||
@ -118,7 +118,7 @@ export class PersonWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
})
|
||||
@WorkspaceIsSystem()
|
||||
@WorkspaceIsNullable()
|
||||
position: number;
|
||||
position: number | null;
|
||||
|
||||
// Relations
|
||||
@WorkspaceRelation({
|
||||
@ -132,7 +132,7 @@ export class PersonWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'people',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
company: Relation<CompanyWorkspaceEntity>;
|
||||
company: Relation<CompanyWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: PERSON_STANDARD_FIELD_IDS.pointOfContactForOpportunities,
|
||||
|
||||
@ -151,9 +151,9 @@ export class TimelineActivityRepository {
|
||||
name: string;
|
||||
properties: Record<string, any> | null;
|
||||
workspaceMemberId: string | undefined;
|
||||
recordId: string;
|
||||
recordId: string | null;
|
||||
linkedRecordCachedName: string;
|
||||
linkedRecordId: string | undefined;
|
||||
linkedRecordId: string | null | undefined;
|
||||
linkedObjectMetadataId: string | undefined;
|
||||
}[],
|
||||
workspaceId: string,
|
||||
|
||||
@ -39,7 +39,7 @@ export class AuditLogWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconListDetails',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
properties: JSON;
|
||||
properties: JSON | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: AUDIT_LOGS_STANDARD_FIELD_IDS.context,
|
||||
@ -50,7 +50,7 @@ export class AuditLogWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconListDetails',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
context: JSON;
|
||||
context: JSON | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: AUDIT_LOGS_STANDARD_FIELD_IDS.objectName,
|
||||
@ -78,7 +78,7 @@ export class AuditLogWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconAbc',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
recordId: string;
|
||||
recordId: string | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: AUDIT_LOGS_STANDARD_FIELD_IDS.workspaceMember,
|
||||
@ -91,5 +91,5 @@ export class AuditLogWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'auditLogs',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
workspaceMember: Relation<WorkspaceMemberWorkspaceEntity>;
|
||||
workspaceMember: Relation<WorkspaceMemberWorkspaceEntity> | null;
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ export class BehavioralEventWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconListDetails',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
properties: JSON;
|
||||
properties: JSON | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: BEHAVIORAL_EVENT_STANDARD_FIELD_IDS.context,
|
||||
@ -67,7 +67,7 @@ export class BehavioralEventWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconListDetails',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
context: JSON;
|
||||
context: JSON | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: BEHAVIORAL_EVENT_STANDARD_FIELD_IDS.objectName,
|
||||
@ -86,5 +86,5 @@ export class BehavioralEventWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconAbc',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
recordId: string;
|
||||
recordId: string | null;
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconListDetails',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
properties: JSON;
|
||||
properties: JSON | null;
|
||||
|
||||
// Special objects that don't have their own timeline and are 'link' to the main object
|
||||
@WorkspaceField({
|
||||
@ -76,7 +76,7 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconAbc',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
linkedRecordId: string;
|
||||
linkedRecordId: string | null;
|
||||
|
||||
@WorkspaceField({
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.linkedObjectMetadataId,
|
||||
@ -86,7 +86,7 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
icon: 'IconAbc',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
linkedObjectMetadataId: string;
|
||||
linkedObjectMetadataId: string | null;
|
||||
|
||||
// Who made the action
|
||||
@WorkspaceRelation({
|
||||
@ -100,7 +100,7 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'timelineActivities',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
workspaceMember: Relation<WorkspaceMemberWorkspaceEntity>;
|
||||
workspaceMember: Relation<WorkspaceMemberWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.person,
|
||||
@ -113,7 +113,7 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'timelineActivities',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
person: Relation<PersonWorkspaceEntity>;
|
||||
person: Relation<PersonWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.company,
|
||||
@ -126,7 +126,7 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'timelineActivities',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
company: Relation<CompanyWorkspaceEntity>;
|
||||
company: Relation<CompanyWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceRelation({
|
||||
standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.opportunity,
|
||||
@ -139,7 +139,7 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'timelineActivities',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
opportunity: Relation<OpportunityWorkspaceEntity>;
|
||||
opportunity: Relation<OpportunityWorkspaceEntity> | null;
|
||||
|
||||
@WorkspaceDynamicRelation({
|
||||
type: RelationMetadataType.MANY_TO_ONE,
|
||||
|
||||
@ -72,5 +72,5 @@ export class ViewFieldWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
joinColumn: 'viewId',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
view?: ViewWorkspaceEntity;
|
||||
view?: ViewWorkspaceEntity | null;
|
||||
}
|
||||
|
||||
@ -68,5 +68,5 @@ export class ViewFilterWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'viewFilters',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
view: Relation<ViewWorkspaceEntity>;
|
||||
view: Relation<ViewWorkspaceEntity> | null;
|
||||
}
|
||||
|
||||
@ -53,5 +53,5 @@ export class ViewSortWorkspaceEntity extends BaseWorkspaceEntity {
|
||||
inverseSideFieldKey: 'viewSorts',
|
||||
})
|
||||
@WorkspaceIsNullable()
|
||||
view: Relation<ViewWorkspaceEntity>;
|
||||
view: Relation<ViewWorkspaceEntity> | null;
|
||||
}
|
||||
|
||||
@ -3,10 +3,9 @@ import { Injectable } from '@nestjs/common';
|
||||
import { WorkspacePreQueryHook } from 'src/engine/api/graphql/workspace-query-runner/workspace-pre-query-hook/interfaces/workspace-pre-query-hook.interface';
|
||||
import { DeleteOneResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
|
||||
|
||||
import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator';
|
||||
import { CommentRepository } from 'src/modules/activity/repositories/comment.repository';
|
||||
import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator';
|
||||
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { CommentWorkspaceEntity } from 'src/modules/activity/standard-objects/comment.workspace-entity';
|
||||
import { AttachmentRepository } from 'src/modules/attachment/repositories/attachment.repository';
|
||||
import { AttachmentWorkspaceEntity } from 'src/modules/attachment/standard-objects/attachment.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
@ -14,10 +13,10 @@ export class WorkspaceMemberDeleteOnePreQueryHook
|
||||
implements WorkspacePreQueryHook
|
||||
{
|
||||
constructor(
|
||||
@InjectObjectMetadataRepository(AttachmentWorkspaceEntity)
|
||||
private readonly attachmentRepository: AttachmentRepository,
|
||||
@InjectObjectMetadataRepository(CommentWorkspaceEntity)
|
||||
private readonly commentRepository: CommentRepository,
|
||||
@InjectWorkspaceRepository(AttachmentWorkspaceEntity)
|
||||
private readonly attachmentRepository: WorkspaceRepository<AttachmentWorkspaceEntity>,
|
||||
@InjectWorkspaceRepository(CommentWorkspaceEntity)
|
||||
private readonly commentRepository: WorkspaceRepository<CommentWorkspaceEntity>,
|
||||
) {}
|
||||
|
||||
// There is no need to validate the user's access to the workspace member since we don't have permission yet.
|
||||
@ -26,16 +25,18 @@ export class WorkspaceMemberDeleteOnePreQueryHook
|
||||
workspaceId: string,
|
||||
payload: DeleteOneResolverArgs,
|
||||
): Promise<void> {
|
||||
const workspaceMemberId = payload.id;
|
||||
const authorId = payload.id;
|
||||
|
||||
await this.attachmentRepository.deleteByAuthorId(
|
||||
workspaceMemberId,
|
||||
workspaceId,
|
||||
);
|
||||
await this.attachmentRepository.delete({
|
||||
author: {
|
||||
id: authorId,
|
||||
},
|
||||
});
|
||||
|
||||
await this.commentRepository.deleteByAuthorId(
|
||||
workspaceMemberId,
|
||||
workspaceId,
|
||||
);
|
||||
await this.commentRepository.delete({
|
||||
author: {
|
||||
id: authorId,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
|
||||
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
|
||||
import { CommentWorkspaceEntity } from 'src/modules/activity/standard-objects/comment.workspace-entity';
|
||||
import { AttachmentWorkspaceEntity } from 'src/modules/attachment/standard-objects/attachment.workspace-entity';
|
||||
import { WorkspaceMemberDeleteManyPreQueryHook } from 'src/modules/workspace-member/query-hooks/workspace-member-delete-many.pre-query.hook';
|
||||
@ -8,7 +8,7 @@ import { WorkspaceMemberDeleteOnePreQueryHook } from 'src/modules/workspace-memb
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ObjectMetadataRepositoryModule.forFeature([
|
||||
TwentyORMModule.forFeature([
|
||||
AttachmentWorkspaceEntity,
|
||||
CommentWorkspaceEntity,
|
||||
]),
|
||||
|
||||
Reference in New Issue
Block a user