diff --git a/packages/twenty-server/src/engine/core-modules/analytics/analytics.service.ts b/packages/twenty-server/src/engine/core-modules/analytics/analytics.service.ts index fd685851c5..73851f71ea 100644 --- a/packages/twenty-server/src/engine/core-modules/analytics/analytics.service.ts +++ b/packages/twenty-server/src/engine/core-modules/analytics/analytics.service.ts @@ -19,8 +19,8 @@ export class AnalyticsService { async create( createEventInput: CreateEventInput, - userId: string | undefined, - workspaceId: string | undefined, + userId: string | null | undefined, + workspaceId: string | null | undefined, workspaceDisplayName: string | undefined, workspaceDomainName: string | undefined, hostName: string | undefined, diff --git a/packages/twenty-server/src/engine/core-modules/auth/auth.module.ts b/packages/twenty-server/src/engine/core-modules/auth/auth.module.ts index 999d317507..6a4d1c9d26 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/auth.module.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/auth.module.ts @@ -28,6 +28,8 @@ import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/s import { CalendarChannelWorkspaceEntity } from 'src/modules/calendar/standard-objects/calendar-channel.workspace-entity'; import { MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity'; import { OnboardingModule } from 'src/engine/core-modules/onboarding/onboarding.module'; +import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module'; +import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; import { AuthResolver } from './auth.resolver'; @@ -60,11 +62,12 @@ const jwtModule = JwtModule.registerAsync({ ObjectMetadataRepositoryModule.forFeature([ ConnectedAccountWorkspaceEntity, MessageChannelWorkspaceEntity, - CalendarChannelWorkspaceEntity, ]), HttpModule, UserWorkspaceModule, OnboardingModule, + TwentyORMModule.forFeature([CalendarChannelWorkspaceEntity]), + WorkspaceDataSourceModule, ], controllers: [ GoogleAuthController, diff --git a/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts b/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts index 2c6466eeea..13ecbec165 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts @@ -138,6 +138,7 @@ export class AuthResolver { } const transientToken = await this.tokenService.generateTransientToken( workspaceMember.id, + user.id, user.defaultWorkspace.id, ); diff --git a/packages/twenty-server/src/engine/core-modules/auth/controllers/google-apis-auth.controller.ts b/packages/twenty-server/src/engine/core-modules/auth/controllers/google-apis-auth.controller.ts index 0a36fe5d01..da56c086e1 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/controllers/google-apis-auth.controller.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/controllers/google-apis-auth.controller.ts @@ -16,9 +16,7 @@ import { GoogleAPIsService } from 'src/engine/core-modules/auth/services/google- import { TokenService } from 'src/engine/core-modules/auth/services/token.service'; import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service'; -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 { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator'; +import { LoadServiceWithWorkspaceContext } from 'src/engine/twenty-orm/context/load-service-with-workspace.context'; @Controller('auth/google-apis') export class GoogleAPIsAuthController { @@ -27,8 +25,7 @@ export class GoogleAPIsAuthController { private readonly tokenService: TokenService, private readonly environmentService: EnvironmentService, private readonly onboardingService: OnboardingService, - @InjectObjectMetadataRepository(WorkspaceMemberWorkspaceEntity) - private readonly workspaceMemberService: WorkspaceMemberRepository, + private readonly loadServiceWithWorkspaceContext: LoadServiceWithWorkspaceContext, ) {} @Get() @@ -56,7 +53,7 @@ export class GoogleAPIsAuthController { messageVisibility, } = user; - const { workspaceMemberId, workspaceId } = + const { workspaceMemberId, userId, workspaceId } = await this.tokenService.verifyTransientToken(transientToken); const demoWorkspaceIds = this.environmentService.get('DEMO_WORKSPACE_IDS'); @@ -71,7 +68,13 @@ export class GoogleAPIsAuthController { throw new Error('Workspace not found'); } - await this.googleAPIsService.refreshGoogleRefreshToken({ + const googleAPIsServiceInstance = + await this.loadServiceWithWorkspaceContext.load( + this.googleAPIsService, + workspaceId, + ); + + await googleAPIsServiceInstance.refreshGoogleRefreshToken({ handle: email, workspaceMemberId: workspaceMemberId, workspaceId: workspaceId, @@ -81,12 +84,14 @@ export class GoogleAPIsAuthController { messageVisibility, }); - const userId = ( - await this.workspaceMemberService.find(workspaceMemberId, workspaceId) - )?.userId; - if (userId) { - await this.onboardingService.skipSyncEmailOnboardingStep( + const onboardingServiceInstance = + await this.loadServiceWithWorkspaceContext.load( + this.onboardingService, + workspaceId, + ); + + await onboardingServiceInstance.skipSyncEmailOnboardingStep( userId, workspaceId, ); diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/google-apis.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/google-apis.service.ts index f580a34df3..665dea7037 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/google-apis.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/google-apis.service.ts @@ -3,17 +3,14 @@ import { Injectable } from '@nestjs/common'; import { EntityManager } from 'typeorm'; import { v4 } from 'uuid'; -import { TypeORMService } from 'src/database/typeorm/typeorm.service'; import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants'; import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service'; -import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator'; import { GoogleCalendarSyncJobData, GoogleCalendarSyncJob, } from 'src/modules/calendar/jobs/google-calendar-sync.job'; -import { CalendarChannelRepository } from 'src/modules/calendar/repositories/calendar-channel.repository'; import { CalendarChannelWorkspaceEntity, CalendarChannelVisibility, @@ -35,12 +32,16 @@ import { MessagingMessageListFetchJobData, } from 'src/modules/messaging/message-import-manager/jobs/messaging-message-list-fetch.job'; import { InjectMessageQueue } from 'src/engine/integrations/message-queue/decorators/message-queue.decorator'; +import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator'; +import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; +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 GoogleAPIsService { constructor( - private readonly dataSourceService: DataSourceService, - private readonly typeORMService: TypeORMService, + @InjectWorkspaceDatasource() + private readonly workspaceDataSource: WorkspaceDataSource, @InjectMessageQueue(MessageQueue.messagingQueue) private readonly messageQueueService: MessageQueueService, @InjectMessageQueue(MessageQueue.calendarQueue) @@ -50,8 +51,8 @@ export class GoogleAPIsService { private readonly connectedAccountRepository: ConnectedAccountRepository, @InjectObjectMetadataRepository(MessageChannelWorkspaceEntity) private readonly messageChannelRepository: MessageChannelRepository, - @InjectObjectMetadataRepository(CalendarChannelWorkspaceEntity) - private readonly calendarChannelRepository: CalendarChannelRepository, + @InjectWorkspaceRepository(CalendarChannelWorkspaceEntity) + private readonly calendarChannelRepository: WorkspaceRepository, ) {} async refreshGoogleRefreshToken(input: { @@ -71,14 +72,6 @@ export class GoogleAPIsService { messageVisibility, } = input; - const dataSourceMetadata = - await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail( - workspaceId, - ); - - const workspaceDataSource = - await this.typeORMService.connectToDataSource(dataSourceMetadata); - const isCalendarEnabled = this.environmentService.get( 'CALENDAR_PROVIDER_GOOGLE_ENABLED', ); @@ -93,65 +86,67 @@ export class GoogleAPIsService { const existingAccountId = connectedAccounts?.[0]?.id; const newOrExistingConnectedAccountId = existingAccountId ?? v4(); - await workspaceDataSource?.transaction(async (manager: EntityManager) => { - if (!existingAccountId) { - await this.connectedAccountRepository.create( - { - id: newOrExistingConnectedAccountId, - handle, - provider: ConnectedAccountProvider.GOOGLE, - accessToken: input.accessToken, - refreshToken: input.refreshToken, - accountOwnerId: workspaceMemberId, - }, - workspaceId, - manager, - ); - - await this.messageChannelRepository.create( - { - id: v4(), - connectedAccountId: newOrExistingConnectedAccountId, - type: MessageChannelType.EMAIL, - handle, - visibility: - messageVisibility || MessageChannelVisibility.SHARE_EVERYTHING, - syncStatus: MessageChannelSyncStatus.ONGOING, - }, - workspaceId, - manager, - ); - - if (isCalendarEnabled) { - await this.calendarChannelRepository.create( + await this.workspaceDataSource.transaction( + async (manager: EntityManager) => { + if (!existingAccountId) { + await this.connectedAccountRepository.create( { - id: v4(), - connectedAccountId: newOrExistingConnectedAccountId, + id: newOrExistingConnectedAccountId, handle, - visibility: - calendarVisibility || - CalendarChannelVisibility.SHARE_EVERYTHING, + provider: ConnectedAccountProvider.GOOGLE, + accessToken: input.accessToken, + refreshToken: input.refreshToken, + accountOwnerId: workspaceMemberId, }, workspaceId, manager, ); - } - } else { - await this.connectedAccountRepository.updateAccessTokenAndRefreshToken( - input.accessToken, - input.refreshToken, - newOrExistingConnectedAccountId, - workspaceId, - manager, - ); - await this.messageChannelRepository.resetSync( - newOrExistingConnectedAccountId, - workspaceId, - manager, - ); - } - }); + await this.messageChannelRepository.create( + { + id: v4(), + connectedAccountId: newOrExistingConnectedAccountId, + type: MessageChannelType.EMAIL, + handle, + visibility: + messageVisibility || MessageChannelVisibility.SHARE_EVERYTHING, + syncStatus: MessageChannelSyncStatus.ONGOING, + }, + workspaceId, + manager, + ); + + if (isCalendarEnabled) { + await this.calendarChannelRepository.save( + { + id: v4(), + connectedAccountId: newOrExistingConnectedAccountId, + handle, + visibility: + calendarVisibility || + CalendarChannelVisibility.SHARE_EVERYTHING, + }, + {}, + manager, + ); + } + } else { + await this.connectedAccountRepository.updateAccessTokenAndRefreshToken( + input.accessToken, + input.refreshToken, + newOrExistingConnectedAccountId, + workspaceId, + manager, + ); + + await this.messageChannelRepository.resetSync( + newOrExistingConnectedAccountId, + workspaceId, + manager, + ); + } + }, + ); if (this.environmentService.get('MESSAGING_PROVIDER_GMAIL_ENABLED')) { const messageChannels = diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/token.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/token.service.ts index c8a8a85dfe..85bfcebc79 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/token.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/token.service.ts @@ -147,6 +147,7 @@ export class TokenService { async generateTransientToken( workspaceMemberId: string, + userId: string, workspaceId: string, ): Promise { const secret = this.environmentService.get('LOGIN_TOKEN_SECRET'); @@ -158,6 +159,7 @@ export class TokenService { const expiresAt = addMilliseconds(new Date().getTime(), ms(expiresIn)); const jwtPayload = { sub: workspaceMemberId, + userId, workspaceId, }; @@ -234,6 +236,7 @@ export class TokenService { async verifyTransientToken(transientToken: string): Promise<{ workspaceMemberId: string; + userId: string; workspaceId: string; }> { const transientTokenSecret = @@ -243,6 +246,7 @@ export class TokenService { return { workspaceMemberId: payload.sub, + userId: payload.userId, workspaceId: payload.workspaceId, }; } diff --git a/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts b/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts index 8228788f46..f4494c0eaa 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts @@ -203,9 +203,7 @@ export class BillingService { : frontBaseUrl; const quantity = - (await this.userWorkspaceService.getWorkspaceMemberCount( - user.defaultWorkspaceId, - )) || 1; + (await this.userWorkspaceService.getWorkspaceMemberCount()) || 1; const stripeCustomerId = ( await this.billingSubscriptionRepository.findOneBy({ diff --git a/packages/twenty-server/src/engine/core-modules/billing/jobs/update-subscription.job.ts b/packages/twenty-server/src/engine/core-modules/billing/jobs/update-subscription.job.ts index 33f690fddd..84388027d3 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/jobs/update-subscription.job.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/jobs/update-subscription.job.ts @@ -1,4 +1,4 @@ -import { Logger } from '@nestjs/common'; +import { Logger, Scope } from '@nestjs/common'; import { BillingService } from 'src/engine/core-modules/billing/billing.service'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; @@ -8,7 +8,10 @@ import { MessageQueue } from 'src/engine/integrations/message-queue/message-queu import { Process } from 'src/engine/integrations/message-queue/decorators/process.decorator'; export type UpdateSubscriptionJobData = { workspaceId: string }; -@Processor(MessageQueue.billingQueue) +@Processor({ + queueName: MessageQueue.billingQueue, + scope: Scope.REQUEST, +}) export class UpdateSubscriptionJob { protected readonly logger = new Logger(UpdateSubscriptionJob.name); @@ -21,7 +24,7 @@ export class UpdateSubscriptionJob { @Process(UpdateSubscriptionJob.name) async handle(data: UpdateSubscriptionJobData): Promise { const workspaceMembersCount = - await this.userWorkspaceService.getWorkspaceMemberCount(data.workspaceId); + await this.userWorkspaceService.getWorkspaceMemberCount(); if (!workspaceMembersCount || workspaceMembersCount <= 0) { return; diff --git a/packages/twenty-server/src/engine/core-modules/calendar/dtos/timeline-calendar-event-participant.dto.ts b/packages/twenty-server/src/engine/core-modules/calendar/dtos/timeline-calendar-event-participant.dto.ts index 1d5d997208..6e9a3189a2 100644 --- a/packages/twenty-server/src/engine/core-modules/calendar/dtos/timeline-calendar-event-participant.dto.ts +++ b/packages/twenty-server/src/engine/core-modules/calendar/dtos/timeline-calendar-event-participant.dto.ts @@ -5,10 +5,10 @@ import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/ @ObjectType('TimelineCalendarEventParticipant') export class TimelineCalendarEventParticipant { @Field(() => UUIDScalarType, { nullable: true }) - personId: string; + personId: string | null; @Field(() => UUIDScalarType, { nullable: true }) - workspaceMemberId: string; + workspaceMemberId: string | null; @Field() firstName: string; diff --git a/packages/twenty-server/src/engine/core-modules/calendar/timeline-calendar-event.service.ts b/packages/twenty-server/src/engine/core-modules/calendar/timeline-calendar-event.service.ts index 810869e2a7..1a2b903a99 100644 --- a/packages/twenty-server/src/engine/core-modules/calendar/timeline-calendar-event.service.ts +++ b/packages/twenty-server/src/engine/core-modules/calendar/timeline-calendar-event.service.ts @@ -81,19 +81,19 @@ export class TimelineCalendarEventService { const participants = event.calendarEventParticipants.map( (participant) => ({ calendarEventId: event.id, - personId: participant.person?.id, - workspaceMemberId: participant.workspaceMember?.id, + personId: participant.person?.id ?? null, + workspaceMemberId: participant.workspaceMember?.id ?? null, firstName: - participant.person?.name.firstName || + participant.person?.name?.firstName || participant.workspaceMember?.name.firstName || '', lastName: - participant.person?.name.lastName || + participant.person?.name?.lastName || participant.workspaceMember?.name.lastName || '', displayName: - participant.person?.name.firstName || - participant.person?.name.lastName || + participant.person?.name?.firstName || + participant.person?.name?.lastName || participant.workspaceMember?.name.firstName || participant.workspaceMember?.name.lastName || '', diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index 1709f3d789..2b6fa3674b 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -56,7 +56,7 @@ export class OnboardingService { const isInviteTeamSkipped = inviteTeamValue === OnboardingStepValues.SKIPPED; const workspaceMemberCount = - await this.userWorkspaceService.getWorkspaceMemberCount(workspace.id); + await this.userWorkspaceService.getWorkspaceMemberCount(); return ( !isInviteTeamSkipped && diff --git a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.module.ts b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.module.ts index 09d3c621fc..1fd86d27cb 100644 --- a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.module.ts +++ b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.module.ts @@ -9,6 +9,8 @@ import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/use import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module'; import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; import { User } from 'src/engine/core-modules/user/user.entity'; +import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module'; +import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; @Module({ imports: [ @@ -21,6 +23,7 @@ import { User } from 'src/engine/core-modules/user/user.entity'; ], services: [UserWorkspaceService], }), + TwentyORMModule.forFeature([WorkspaceMemberWorkspaceEntity]), ], exports: [UserWorkspaceService], providers: [UserWorkspaceService], diff --git a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts index bdeaa75357..1ba4e66269 100644 --- a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts +++ b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts @@ -8,11 +8,12 @@ import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-works import { TypeORMService } from 'src/database/typeorm/typeorm.service'; import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; import { User } from 'src/engine/core-modules/user/user.entity'; -import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; import { ObjectRecordCreateEvent } from 'src/engine/integrations/event-emitter/types/object-record-create.event'; import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; import { assert } from 'src/utils/assert'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator'; +import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; export class UserWorkspaceService extends TypeOrmQueryService { constructor( @@ -20,9 +21,10 @@ export class UserWorkspaceService extends TypeOrmQueryService { private readonly userWorkspaceRepository: Repository, @InjectRepository(User, 'core') private readonly userRepository: Repository, + @InjectWorkspaceRepository(WorkspaceMemberWorkspaceEntity) + private readonly workspaceMemberRepository: WorkspaceRepository, private readonly dataSourceService: DataSourceService, private readonly typeORMService: TypeORMService, - private readonly workspaceDataSourceService: WorkspaceDataSourceService, private eventEmitter: EventEmitter2, ) { super(userWorkspaceRepository); @@ -99,23 +101,10 @@ export class UserWorkspaceService extends TypeOrmQueryService { }); } - public async getWorkspaceMemberCount( - workspaceId: string, - ): Promise { - try { - const dataSourceSchema = - this.workspaceDataSourceService.getSchemaName(workspaceId); + public async getWorkspaceMemberCount(): Promise { + const workspaceMemberCount = await this.workspaceMemberRepository.count(); - return ( - await this.workspaceDataSourceService.executeRawQuery( - `SELECT * FROM ${dataSourceSchema}."workspaceMember"`, - [], - workspaceId, - ) - ).length; - } catch { - return undefined; - } + return workspaceMemberCount; } async checkUserWorkspaceExists( diff --git a/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts b/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts index ef596d19d0..154405d8ee 100644 --- a/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts @@ -29,6 +29,7 @@ import { User } from 'src/engine/core-modules/user/user.entity'; import { WorkspaceMember } from 'src/engine/core-modules/user/dtos/workspace-member.dto'; import { OnboardingStep } from 'src/engine/core-modules/onboarding/enums/onboarding-step.enum'; import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service'; +import { LoadServiceWithWorkspaceContext } from 'src/engine/twenty-orm/context/load-service-with-workspace.context'; const getHMACKey = (email?: string, key?: string | null) => { if (!email || !key) return null; @@ -48,6 +49,7 @@ export class UserResolver { private readonly environmentService: EnvironmentService, private readonly fileUploadService: FileUploadService, private readonly onboardingService: OnboardingService, + private readonly loadServiceWithWorkspaceContext: LoadServiceWithWorkspaceContext, ) {} @Query(() => User) @@ -122,9 +124,11 @@ export class UserResolver { return null; } - return this.onboardingService.getOnboardingStep( - user, - user.defaultWorkspace, + const contextInstance = await this.loadServiceWithWorkspaceContext.load( + this.onboardingService, + user.defaultWorkspaceId, ); + + return contextInstance.getOnboardingStep(user, user.defaultWorkspace); } } diff --git a/packages/twenty-server/src/engine/integrations/message-queue/drivers/pg-boss.driver.ts b/packages/twenty-server/src/engine/integrations/message-queue/drivers/pg-boss.driver.ts index 8f875f75f1..ca3a0cd5f9 100644 --- a/packages/twenty-server/src/engine/integrations/message-queue/drivers/pg-boss.driver.ts +++ b/packages/twenty-server/src/engine/integrations/message-queue/drivers/pg-boss.driver.ts @@ -47,7 +47,11 @@ export class PgBossDriver } : {}, async (job) => { - await handler({ data: job.data, id: job.id, name: job.name }); + await handler({ + data: job.data, + id: job.id, + name: job.name.split('.')[1], + }); }, ); } diff --git a/packages/twenty-server/src/engine/integrations/message-queue/message-queue.explorer.ts b/packages/twenty-server/src/engine/integrations/message-queue/message-queue.explorer.ts index 838143f09d..1d5cbf473b 100644 --- a/packages/twenty-server/src/engine/integrations/message-queue/message-queue.explorer.ts +++ b/packages/twenty-server/src/engine/integrations/message-queue/message-queue.explorer.ts @@ -156,7 +156,7 @@ export class MessageQueueExplorer implements OnModuleInit { }), ); - if (isRequestScoped) { + if (isRequestScoped && job.data) { const contextId = createContextId(); if (this.moduleRef.registerRequestByContextId) { diff --git a/packages/twenty-server/src/engine/object-metadata-repository/metadata-to-repository.mapping.ts b/packages/twenty-server/src/engine/object-metadata-repository/metadata-to-repository.mapping.ts index 17b4d0e802..0d264fcd37 100644 --- a/packages/twenty-server/src/engine/object-metadata-repository/metadata-to-repository.mapping.ts +++ b/packages/twenty-server/src/engine/object-metadata-repository/metadata-to-repository.mapping.ts @@ -1,15 +1,9 @@ -import { CalendarChannelEventAssociationRepository } from 'src/modules/calendar/repositories/calendar-channel-event-association.repository'; -import { CalendarChannelRepository } from 'src/modules/calendar/repositories/calendar-channel.repository'; -import { CalendarEventParticipantRepository } from 'src/modules/calendar/repositories/calendar-event-participant.repository'; -import { CalendarEventRepository } from 'src/modules/calendar/repositories/calendar-event.repository'; import { CompanyRepository } from 'src/modules/company/repositories/company.repository'; import { BlocklistRepository } from 'src/modules/connected-account/repositories/blocklist.repository'; import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository'; import { AuditLogRepository } from 'src/modules/timeline/repositiories/audit-log.repository'; import { TimelineActivityRepository } from 'src/modules/timeline/repositiories/timeline-activity.repository'; import { WorkspaceMemberRepository } from 'src/modules/workspace-member/repositories/workspace-member.repository'; -import { AttachmentRepository } from 'src/modules/attachment/repositories/attachment.repository'; -import { CommentRepository } from 'src/modules/activity/repositories/comment.repository'; import { MessageChannelMessageAssociationRepository } from 'src/modules/messaging/common/repositories/message-channel-message-association.repository'; import { MessageChannelRepository } from 'src/modules/messaging/common/repositories/message-channel.repository'; import { MessageParticipantRepository } from 'src/modules/messaging/common/repositories/message-participant.repository'; @@ -20,11 +14,6 @@ import { PersonRepository } from 'src/modules/person/repositories/person.reposit export const metadataToRepositoryMapping = { AuditLogWorkspaceEntity: AuditLogRepository, BlocklistWorkspaceEntity: BlocklistRepository, - CalendarChannelEventAssociationWorkspaceEntity: - CalendarChannelEventAssociationRepository, - CalendarChannelWorkspaceEntity: CalendarChannelRepository, - CalendarEventParticipantWorkspaceEntity: CalendarEventParticipantRepository, - CalendarEventWorkspaceEntity: CalendarEventRepository, CompanyWorkspaceEntity: CompanyRepository, ConnectedAccountWorkspaceEntity: ConnectedAccountRepository, MessageChannelMessageAssociationWorkspaceEntity: @@ -36,6 +25,4 @@ export const metadataToRepositoryMapping = { PersonWorkspaceEntity: PersonRepository, TimelineActivityWorkspaceEntity: TimelineActivityRepository, WorkspaceMemberWorkspaceEntity: WorkspaceMemberRepository, - AttachmentWorkspaceEntity: AttachmentRepository, - CommentWorkspaceEntity: CommentRepository, }; diff --git a/packages/twenty-server/src/engine/twenty-orm/context/load-service-with-workspace.context.ts b/packages/twenty-server/src/engine/twenty-orm/context/load-service-with-workspace.context.ts new file mode 100644 index 0000000000..02af978050 --- /dev/null +++ b/packages/twenty-server/src/engine/twenty-orm/context/load-service-with-workspace.context.ts @@ -0,0 +1,39 @@ +import { Inject, Type } from '@nestjs/common'; +import { ModuleRef, createContextId } from '@nestjs/core'; +import { Injector } from '@nestjs/core/injector/injector'; + +export class LoadServiceWithWorkspaceContext { + private readonly injector = new Injector(); + + constructor( + @Inject(ModuleRef) + private readonly moduleRef: ModuleRef, + ) {} + + async load(service: T, workspaceId: string): Promise { + const modules = this.moduleRef['container'].getModules(); + const host = [...modules.values()].find((module) => + module.providers.has((service as Type).constructor), + ); + + if (!host) { + throw new Error('Host module not found for the service'); + } + + const contextId = createContextId(); + + if (this.moduleRef.registerRequestByContextId) { + this.moduleRef.registerRequestByContextId( + { req: { workspaceId } }, + contextId, + ); + } + + return this.injector.loadPerContext( + service, + host, + new Map(host.providers), + contextId, + ); + } +} diff --git a/packages/twenty-server/src/engine/twenty-orm/custom.workspace-entity.ts b/packages/twenty-server/src/engine/twenty-orm/custom.workspace-entity.ts index ee7740f065..56f9238d24 100644 --- a/packages/twenty-server/src/engine/twenty-orm/custom.workspace-entity.ts +++ b/packages/twenty-server/src/engine/twenty-orm/custom.workspace-entity.ts @@ -36,7 +36,7 @@ export class CustomWorkspaceEntity extends BaseWorkspaceEntity { }) @WorkspaceIsNullable() @WorkspaceIsSystem() - position: number; + position: number | null; @WorkspaceRelation({ standardId: CUSTOM_OBJECT_STANDARD_FIELD_IDS.activityTargets, diff --git a/packages/twenty-server/src/engine/twenty-orm/datasource/workspace.datasource.ts b/packages/twenty-server/src/engine/twenty-orm/datasource/workspace.datasource.ts index ab51a24927..71ca219090 100644 --- a/packages/twenty-server/src/engine/twenty-orm/datasource/workspace.datasource.ts +++ b/packages/twenty-server/src/engine/twenty-orm/datasource/workspace.datasource.ts @@ -6,8 +6,8 @@ import { QueryRunner, } from 'typeorm'; -import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/entity.manager'; +import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; export class WorkspaceDataSource extends DataSource { readonly manager: WorkspaceEntityManager; diff --git a/packages/twenty-server/src/engine/twenty-orm/factories/workspace-datasource.factory.ts b/packages/twenty-server/src/engine/twenty-orm/factories/workspace-datasource.factory.ts index 27aead734a..f7447a1318 100644 --- a/packages/twenty-server/src/engine/twenty-orm/factories/workspace-datasource.factory.ts +++ b/packages/twenty-server/src/engine/twenty-orm/factories/workspace-datasource.factory.ts @@ -32,10 +32,9 @@ export class WorkspaceDatasourceFactory { dataSourceMetadata.url ?? this.environmentService.get('PG_DATABASE_URL'), type: 'postgres', - // logging: this.environmentService.get('DEBUG_MODE') - // ? ['query', 'error'] - // : ['error'], - logging: 'all', + logging: this.environmentService.get('DEBUG_MODE') + ? ['query', 'error'] + : ['error'], schema: dataSourceMetadata.schema, entities, ssl: this.environmentService.get('PG_SSL_ALLOW_SELF_SIGNED') diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts index f03123e073..0f33676858 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts @@ -1,6 +1,7 @@ import { DeepPartial, DeleteResult, + EntityManager, FindManyOptions, FindOneOptions, FindOptionsWhere, @@ -29,9 +30,13 @@ export class WorkspaceRepository< /** * FIND METHODS */ - override async find(options?: FindManyOptions): Promise { + override async find( + options?: FindManyOptions, + entityManager?: EntityManager, + ): Promise { + const manager = entityManager || this.manager; const computedOptions = this.transformOptions(options); - const result = await super.find(computedOptions); + const result = await manager.find(this.target, computedOptions); const formattedResult = this.formatResult(result); return formattedResult; @@ -39,9 +44,11 @@ export class WorkspaceRepository< override async findBy( where: FindOptionsWhere | FindOptionsWhere[], + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; const computedOptions = this.transformOptions({ where }); - const result = await super.findBy(computedOptions.where); + const result = await manager.findBy(this.target, computedOptions.where); const formattedResult = this.formatResult(result); return formattedResult; @@ -49,9 +56,11 @@ export class WorkspaceRepository< override async findAndCount( options?: FindManyOptions, + entityManager?: EntityManager, ): Promise<[Entity[], number]> { + const manager = entityManager || this.manager; const computedOptions = this.transformOptions(options); - const result = await super.findAndCount(computedOptions); + const result = await manager.findAndCount(this.target, computedOptions); const formattedResult = this.formatResult(result); return formattedResult; @@ -59,9 +68,14 @@ export class WorkspaceRepository< override async findAndCountBy( where: FindOptionsWhere | FindOptionsWhere[], + entityManager?: EntityManager, ): Promise<[Entity[], number]> { + const manager = entityManager || this.manager; const computedOptions = this.transformOptions({ where }); - const result = await super.findAndCountBy(computedOptions.where); + const result = await manager.findAndCountBy( + this.target, + computedOptions.where, + ); const formattedResult = this.formatResult(result); return formattedResult; @@ -69,9 +83,11 @@ export class WorkspaceRepository< override async findOne( options: FindOneOptions, + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; const computedOptions = this.transformOptions(options); - const result = await super.findOne(computedOptions); + const result = await manager.findOne(this.target, computedOptions); const formattedResult = this.formatResult(result); return formattedResult; @@ -79,9 +95,11 @@ export class WorkspaceRepository< override async findOneBy( where: FindOptionsWhere | FindOptionsWhere[], + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; const computedOptions = this.transformOptions({ where }); - const result = await super.findOneBy(computedOptions.where); + const result = await manager.findOneBy(this.target, computedOptions.where); const formattedResult = this.formatResult(result); return formattedResult; @@ -89,9 +107,11 @@ export class WorkspaceRepository< override async findOneOrFail( options: FindOneOptions, + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; const computedOptions = this.transformOptions(options); - const result = await super.findOneOrFail(computedOptions); + const result = await manager.findOneOrFail(this.target, computedOptions); const formattedResult = this.formatResult(result); return formattedResult; @@ -99,9 +119,14 @@ export class WorkspaceRepository< override async findOneByOrFail( where: FindOptionsWhere | FindOptionsWhere[], + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; const computedOptions = this.transformOptions({ where }); - const result = await super.findOneByOrFail(computedOptions.where); + const result = await manager.findOneByOrFail( + this.target, + computedOptions.where, + ); const formattedResult = this.formatResult(result); return formattedResult; @@ -113,29 +138,40 @@ export class WorkspaceRepository< override save>( entities: T[], options: SaveOptions & { reload: false }, + entityManager?: EntityManager, ): Promise; override save>( entities: T[], options?: SaveOptions, + entityManager?: EntityManager, ): Promise<(T & Entity)[]>; override save>( entity: T, options: SaveOptions & { reload: false }, + entityManager?: EntityManager, ): Promise; override save>( entity: T, options?: SaveOptions, + entityManager?: EntityManager, ): Promise; override async save>( entityOrEntities: T | T[], options?: SaveOptions, + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; const formattedEntityOrEntities = this.formatData(entityOrEntities); - const result = await super.save(formattedEntityOrEntities as any, options); + const result = await manager.save( + this.target, + formattedEntityOrEntities as any, + options, + ); + const formattedResult = this.formatResult(result); return formattedResult; @@ -147,15 +183,27 @@ export class WorkspaceRepository< override remove( entities: Entity[], options?: RemoveOptions, + entityManager?: EntityManager, ): Promise; - override remove(entity: Entity, options?: RemoveOptions): Promise; + override remove( + entity: Entity, + options?: RemoveOptions, + entityManager?: EntityManager, + ): Promise; override async remove( entityOrEntities: Entity | Entity[], + options?: RemoveOptions, + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; const formattedEntityOrEntities = this.formatData(entityOrEntities); - const result = await super.remove(formattedEntityOrEntities as any); + const result = await manager.remove( + this.target, + formattedEntityOrEntities, + options, + ); const formattedResult = this.formatResult(result); return formattedResult; @@ -172,40 +220,50 @@ export class WorkspaceRepository< | ObjectId | ObjectId[] | FindOptionsWhere, + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; + if (typeof criteria === 'object' && 'where' in criteria) { criteria = this.transformOptions(criteria); } - return this.delete(criteria); + return manager.delete(this.target, criteria); } override softRemove>( entities: T[], options: SaveOptions & { reload: false }, + entityManager?: EntityManager, ): Promise; override softRemove>( entities: T[], options?: SaveOptions, + entityManager?: EntityManager, ): Promise<(T & Entity)[]>; override softRemove>( entity: T, options: SaveOptions & { reload: false }, + entityManager?: EntityManager, ): Promise; override softRemove>( entity: T, options?: SaveOptions, + entityManager?: EntityManager, ): Promise; override async softRemove>( entityOrEntities: T | T[], options?: SaveOptions, + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; const formattedEntityOrEntities = this.formatData(entityOrEntities); - const result = await super.softRemove( + const result = await manager.softRemove( + this.target, formattedEntityOrEntities as any, options, ); @@ -225,12 +283,15 @@ export class WorkspaceRepository< | ObjectId | ObjectId[] | FindOptionsWhere, + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; + if (typeof criteria === 'object' && 'where' in criteria) { criteria = this.transformOptions(criteria); } - return this.softDelete(criteria); + return manager.softDelete(this.target, criteria); } /** @@ -239,29 +300,36 @@ export class WorkspaceRepository< override recover>( entities: T[], options: SaveOptions & { reload: false }, + entityManager?: EntityManager, ): Promise; override recover>( entities: T[], options?: SaveOptions, + entityManager?: EntityManager, ): Promise<(T & Entity)[]>; override recover>( entity: T, options: SaveOptions & { reload: false }, + entityManager?: EntityManager, ): Promise; override recover>( entity: T, options?: SaveOptions, + entityManager?: EntityManager, ): Promise; override async recover>( entityOrEntities: T | T[], options?: SaveOptions, + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; const formattedEntityOrEntities = this.formatData(entityOrEntities); - const result = await super.recover( + const result = await manager.recover( + this.target, formattedEntityOrEntities as any, options, ); @@ -281,12 +349,15 @@ export class WorkspaceRepository< | ObjectId | ObjectId[] | FindOptionsWhere, + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; + if (typeof criteria === 'object' && 'where' in criteria) { criteria = this.transformOptions(criteria); } - return this.restore(criteria); + return manager.restore(this.target, criteria); } /** @@ -294,9 +365,11 @@ export class WorkspaceRepository< */ override async insert( entity: QueryDeepPartialEntity | QueryDeepPartialEntity[], + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; const formatedEntity = this.formatData(entity); - const result = await super.insert(formatedEntity); + const result = await manager.insert(this.target, formatedEntity); const formattedResult = this.formatResult(result); return formattedResult; @@ -317,12 +390,15 @@ export class WorkspaceRepository< | ObjectId[] | FindOptionsWhere, partialEntity: QueryDeepPartialEntity, + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; + if (typeof criteria === 'object' && 'where' in criteria) { criteria = this.transformOptions(criteria); } - return this.update(criteria, partialEntity); + return manager.update(this.target, criteria, partialEntity); } override upsert( @@ -330,50 +406,63 @@ export class WorkspaceRepository< | QueryDeepPartialEntity | QueryDeepPartialEntity[], conflictPathsOrOptions: string[] | UpsertOptions, + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; + const formattedEntityOrEntities = this.formatData(entityOrEntities); - return this.upsert(formattedEntityOrEntities, conflictPathsOrOptions); + return manager.upsert( + this.target, + formattedEntityOrEntities, + conflictPathsOrOptions, + ); } /** * EXIST METHODS */ - override exist(options?: FindManyOptions): Promise { + override exists( + options?: FindManyOptions, + entityManager?: EntityManager, + ): Promise { + const manager = entityManager || this.manager; const computedOptions = this.transformOptions(options); - return super.exist(computedOptions); - } - - override exists(options?: FindManyOptions): Promise { - const computedOptions = this.transformOptions(options); - - return super.exists(computedOptions); + return manager.exists(this.target, computedOptions); } override existsBy( where: FindOptionsWhere | FindOptionsWhere[], + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; const computedOptions = this.transformOptions({ where }); - return super.existsBy(computedOptions.where); + return manager.existsBy(this.target, computedOptions.where); } /** * COUNT METHODS */ - override count(options?: FindManyOptions): Promise { + override count( + options?: FindManyOptions, + entityManager?: EntityManager, + ): Promise { + const manager = entityManager || this.manager; const computedOptions = this.transformOptions(options); - return super.count(computedOptions); + return manager.count(this.target, computedOptions); } override countBy( where: FindOptionsWhere | FindOptionsWhere[], + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; const computedOptions = this.transformOptions({ where }); - return super.countBy(computedOptions.where); + return manager.countBy(this.target, computedOptions.where); } /** @@ -382,57 +471,79 @@ export class WorkspaceRepository< override sum( columnName: PickKeysByType, where?: FindOptionsWhere | FindOptionsWhere[], + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; const computedOptions = this.transformOptions({ where }); - return super.sum(columnName, computedOptions.where); + return manager.sum(this.target, columnName, computedOptions.where); } override average( columnName: PickKeysByType, where?: FindOptionsWhere | FindOptionsWhere[], + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; const computedOptions = this.transformOptions({ where }); - return super.average(columnName, computedOptions.where); + return manager.average(this.target, columnName, computedOptions.where); } override minimum( columnName: PickKeysByType, where?: FindOptionsWhere | FindOptionsWhere[], + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; const computedOptions = this.transformOptions({ where }); - return super.minimum(columnName, computedOptions.where); + return manager.minimum(this.target, columnName, computedOptions.where); } override maximum( columnName: PickKeysByType, where?: FindOptionsWhere | FindOptionsWhere[], + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; const computedOptions = this.transformOptions({ where }); - return super.maximum(columnName, computedOptions.where); + return manager.maximum(this.target, columnName, computedOptions.where); } override increment( conditions: FindOptionsWhere, propertyPath: string, value: number | string, + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; const computedConditions = this.transformOptions({ where: conditions }); - return this.increment(computedConditions.where, propertyPath, value); + return manager.increment( + this.target, + computedConditions.where, + propertyPath, + value, + ); } override decrement( conditions: FindOptionsWhere, propertyPath: string, value: number | string, + entityManager?: EntityManager, ): Promise { + const manager = entityManager || this.manager; const computedConditions = this.transformOptions({ where: conditions }); - return this.decrement(computedConditions.where, propertyPath, value); + return manager.decrement( + this.target, + computedConditions.where, + propertyPath, + value, + ); } /** diff --git a/packages/twenty-server/src/engine/twenty-orm/twenty-orm-core.module.ts b/packages/twenty-server/src/engine/twenty-orm/twenty-orm-core.module.ts index 353c3e5a50..efed8ca7bc 100644 --- a/packages/twenty-server/src/engine/twenty-orm/twenty-orm-core.module.ts +++ b/packages/twenty-server/src/engine/twenty-orm/twenty-orm-core.module.ts @@ -30,12 +30,21 @@ import { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN, } from 'src/engine/twenty-orm/twenty-orm.module-definition'; +import { LoadServiceWithWorkspaceContext } from 'src/engine/twenty-orm/context/load-service-with-workspace.context'; @Global() @Module({ imports: [DataSourceModule], - providers: [...entitySchemaFactories, TwentyORMManager], - exports: [EntitySchemaFactory, TwentyORMManager], + providers: [ + ...entitySchemaFactories, + TwentyORMManager, + LoadServiceWithWorkspaceContext, + ], + exports: [ + EntitySchemaFactory, + TwentyORMManager, + LoadServiceWithWorkspaceContext, + ], }) export class TwentyORMCoreModule extends ConfigurableModuleClass diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/types/object-record.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/types/object-record.ts index d517a6aeda..659f072560 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/types/object-record.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/types/object-record.ts @@ -1,19 +1,20 @@ -import { ObjectLiteral } from 'typeorm'; - import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity'; -export type ObjectRecord = { - [K in keyof T as T[K] extends BaseWorkspaceEntity - ? `${Extract}Id` - : K]: T[K] extends BaseWorkspaceEntity - ? string - : T[K] extends BaseWorkspaceEntity[] - ? string[] - : T[K]; -} & { - [K in keyof T]: T[K] extends BaseWorkspaceEntity - ? ObjectRecord - : T[K] extends BaseWorkspaceEntity[] - ? ObjectRecord[] - : T[K]; +type RelationKeys = { + [K in keyof T]: NonNullable extends BaseWorkspaceEntity ? K : never; +}[keyof T]; + +type ForeignKeyMap = { + [K in RelationKeys as `${K & string}Id`]: string; }; + +type RecursiveObjectRecord = { + [P in keyof T]: NonNullable extends BaseWorkspaceEntity + ? ObjectRecord> & ForeignKeyMap> + : T[P]; +}; + +// TODO: We should get rid of that it's causing too much issues +// Some relations can be null or undefined because they're not mendatory and other cannot +// This utility type put as defined all the joinColumn, so it's not well typed +export type ObjectRecord = RecursiveObjectRecord & ForeignKeyMap; diff --git a/packages/twenty-server/src/modules/activity/repositories/comment.repository.ts b/packages/twenty-server/src/modules/activity/repositories/comment.repository.ts deleted file mode 100644 index e6bf07b2b8..0000000000 --- a/packages/twenty-server/src/modules/activity/repositories/comment.repository.ts +++ /dev/null @@ -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 { - const dataSourceSchema = - this.workspaceDataSourceService.getSchemaName(workspaceId); - - await this.workspaceDataSourceService.executeRawQuery( - `DELETE FROM ${dataSourceSchema}."comment" WHERE "authorId" = $1`, - [authorId], - workspaceId, - ); - } -} diff --git a/packages/twenty-server/src/modules/activity/standard-objects/activity-target.workspace-entity.ts b/packages/twenty-server/src/modules/activity/standard-objects/activity-target.workspace-entity.ts index c99325d137..a72330bfd2 100644 --- a/packages/twenty-server/src/modules/activity/standard-objects/activity-target.workspace-entity.ts +++ b/packages/twenty-server/src/modules/activity/standard-objects/activity-target.workspace-entity.ts @@ -36,7 +36,7 @@ export class ActivityTargetWorkspaceEntity extends BaseWorkspaceEntity { inverseSideFieldKey: 'activityTargets', }) @WorkspaceIsNullable() - activity: Relation; + activity: Relation | null; @WorkspaceRelation({ standardId: ACTIVITY_TARGET_STANDARD_FIELD_IDS.person, @@ -49,7 +49,7 @@ export class ActivityTargetWorkspaceEntity extends BaseWorkspaceEntity { inverseSideFieldKey: 'activityTargets', }) @WorkspaceIsNullable() - person: Relation; + person: Relation | null; @WorkspaceRelation({ standardId: ACTIVITY_TARGET_STANDARD_FIELD_IDS.company, @@ -62,7 +62,7 @@ export class ActivityTargetWorkspaceEntity extends BaseWorkspaceEntity { inverseSideFieldKey: 'activityTargets', }) @WorkspaceIsNullable() - company: Relation; + company: Relation | null; @WorkspaceRelation({ standardId: ACTIVITY_TARGET_STANDARD_FIELD_IDS.opportunity, @@ -75,7 +75,7 @@ export class ActivityTargetWorkspaceEntity extends BaseWorkspaceEntity { inverseSideFieldKey: 'activityTargets', }) @WorkspaceIsNullable() - opportunity: Relation; + opportunity: Relation | null; @WorkspaceDynamicRelation({ type: RelationMetadataType.MANY_TO_ONE, diff --git a/packages/twenty-server/src/modules/activity/standard-objects/activity.workspace-entity.ts b/packages/twenty-server/src/modules/activity/standard-objects/activity.workspace-entity.ts index 8c6460eb45..d27fd026f5 100644 --- a/packages/twenty-server/src/modules/activity/standard-objects/activity.workspace-entity.ts +++ b/packages/twenty-server/src/modules/activity/standard-objects/activity.workspace-entity.ts @@ -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; + author: Relation | null; @WorkspaceRelation({ standardId: ACTIVITY_STANDARD_FIELD_IDS.assignee, @@ -148,5 +148,5 @@ export class ActivityWorkspaceEntity extends BaseWorkspaceEntity { joinColumn: 'assigneeId', }) @WorkspaceIsNullable() - assignee: Relation; + assignee: Relation | null; } diff --git a/packages/twenty-server/src/modules/api-key/standard-objects/api-key.workspace-entity.ts b/packages/twenty-server/src/modules/api-key/standard-objects/api-key.workspace-entity.ts index 40159e093a..82ca9d107d 100644 --- a/packages/twenty-server/src/modules/api-key/standard-objects/api-key.workspace-entity.ts +++ b/packages/twenty-server/src/modules/api-key/standard-objects/api-key.workspace-entity.ts @@ -45,5 +45,5 @@ export class ApiKeyWorkspaceEntity extends BaseWorkspaceEntity { icon: 'IconCalendar', }) @WorkspaceIsNullable() - revokedAt?: Date; + revokedAt?: Date | null; } diff --git a/packages/twenty-server/src/modules/attachment/repositories/attachment.repository.ts b/packages/twenty-server/src/modules/attachment/repositories/attachment.repository.ts deleted file mode 100644 index 2d340d2cba..0000000000 --- a/packages/twenty-server/src/modules/attachment/repositories/attachment.repository.ts +++ /dev/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 { - const dataSourceSchema = - this.workspaceDataSourceService.getSchemaName(workspaceId); - - await this.workspaceDataSourceService.executeRawQuery( - `DELETE FROM ${dataSourceSchema}."attachment" WHERE "authorId" = $1`, - [authorId], - workspaceId, - ); - } -} diff --git a/packages/twenty-server/src/modules/attachment/standard-objects/attachment.workspace-entity.ts b/packages/twenty-server/src/modules/attachment/standard-objects/attachment.workspace-entity.ts index 42af628b9e..3b4b9f9afc 100644 --- a/packages/twenty-server/src/modules/attachment/standard-objects/attachment.workspace-entity.ts +++ b/packages/twenty-server/src/modules/attachment/standard-objects/attachment.workspace-entity.ts @@ -80,7 +80,7 @@ export class AttachmentWorkspaceEntity extends BaseWorkspaceEntity { inverseSideFieldKey: 'attachments', }) @WorkspaceIsNullable() - activity: Relation; + activity: Relation | null; @WorkspaceRelation({ standardId: ATTACHMENT_STANDARD_FIELD_IDS.person, @@ -93,7 +93,7 @@ export class AttachmentWorkspaceEntity extends BaseWorkspaceEntity { inverseSideFieldKey: 'attachments', }) @WorkspaceIsNullable() - person: Relation; + person: Relation | null; @WorkspaceRelation({ standardId: ATTACHMENT_STANDARD_FIELD_IDS.company, @@ -106,7 +106,7 @@ export class AttachmentWorkspaceEntity extends BaseWorkspaceEntity { inverseSideFieldKey: 'attachments', }) @WorkspaceIsNullable() - company: Relation; + company: Relation | null; @WorkspaceRelation({ standardId: ATTACHMENT_STANDARD_FIELD_IDS.opportunity, @@ -119,7 +119,7 @@ export class AttachmentWorkspaceEntity extends BaseWorkspaceEntity { inverseSideFieldKey: 'attachments', }) @WorkspaceIsNullable() - opportunity: Relation; + opportunity: Relation | null; @WorkspaceDynamicRelation({ type: RelationMetadataType.MANY_TO_ONE, diff --git a/packages/twenty-server/src/modules/calendar-messaging-participant/jobs/match-participant.job.ts b/packages/twenty-server/src/modules/calendar-messaging-participant/jobs/match-participant.job.ts index 97f639e8d7..f8ccadfa44 100644 --- a/packages/twenty-server/src/modules/calendar-messaging-participant/jobs/match-participant.job.ts +++ b/packages/twenty-server/src/modules/calendar-messaging-participant/jobs/match-participant.job.ts @@ -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, diff --git a/packages/twenty-server/src/modules/calendar-messaging-participant/jobs/unmatch-participant.job.ts b/packages/twenty-server/src/modules/calendar-messaging-participant/jobs/unmatch-participant.job.ts index 34b0e06f31..a2b8c9044a 100644 --- a/packages/twenty-server/src/modules/calendar-messaging-participant/jobs/unmatch-participant.job.ts +++ b/packages/twenty-server/src/modules/calendar-messaging-participant/jobs/unmatch-participant.job.ts @@ -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, diff --git a/packages/twenty-server/src/modules/calendar/commands/calendar-commands.module.ts b/packages/twenty-server/src/modules/calendar/commands/calendar-commands.module.ts index 20b187004c..0c0acf8128 100644 --- a/packages/twenty-server/src/modules/calendar/commands/calendar-commands.module.ts +++ b/packages/twenty-server/src/modules/calendar/commands/calendar-commands.module.ts @@ -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 {} diff --git a/packages/twenty-server/src/modules/calendar/crons/jobs/google-calendar-sync.cron.job.ts b/packages/twenty-server/src/modules/calendar/crons/jobs/google-calendar-sync.cron.job.ts index f9b39cfaba..b2c6e43da7 100644 --- a/packages/twenty-server/src/modules/calendar/crons/jobs/google-calendar-sync.cron.job.ts +++ b/packages/twenty-server/src/modules/calendar/crons/jobs/google-calendar-sync.cron.job.ts @@ -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') diff --git a/packages/twenty-server/src/modules/calendar/jobs/blocklist-item-delete-calendar-events.job.ts b/packages/twenty-server/src/modules/calendar/jobs/blocklist-item-delete-calendar-events.job.ts index 5b9b9ddc9d..6dc9eef1ce 100644 --- a/packages/twenty-server/src/modules/calendar/jobs/blocklist-item-delete-calendar-events.job.ts +++ b/packages/twenty-server/src/modules/calendar/jobs/blocklist-item-delete-calendar-events.job.ts @@ -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, + @InjectWorkspaceRepository(CalendarChannelEventAssociationWorkspaceEntity) + private readonly calendarChannelEventAssociationRepository: WorkspaceRepository, @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, diff --git a/packages/twenty-server/src/modules/calendar/jobs/blocklist-reimport-calendar-events.job.ts b/packages/twenty-server/src/modules/calendar/jobs/blocklist-reimport-calendar-events.job.ts index 73069a36a8..7c34e0898c 100644 --- a/packages/twenty-server/src/modules/calendar/jobs/blocklist-reimport-calendar-events.job.ts +++ b/packages/twenty-server/src/modules/calendar/jobs/blocklist-reimport-calendar-events.job.ts @@ -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); diff --git a/packages/twenty-server/src/modules/calendar/jobs/calendar-create-company-and-contact-after-sync.job.ts b/packages/twenty-server/src/modules/calendar/jobs/calendar-create-company-and-contact-after-sync.job.ts index 0dec9f8e8f..77cf65b231 100644 --- a/packages/twenty-server/src/modules/calendar/jobs/calendar-create-company-and-contact-after-sync.job.ts +++ b/packages/twenty-server/src/modules/calendar/jobs/calendar-create-company-and-contact-after-sync.job.ts @@ -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, + @InjectWorkspaceRepository(CalendarEventParticipantWorkspaceEntity) + private readonly calendarEventParticipantRepository: WorkspaceRepository, ) {} @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, diff --git a/packages/twenty-server/src/modules/calendar/jobs/calendar-job.module.ts b/packages/twenty-server/src/modules/calendar/jobs/calendar-job.module.ts index 1c3abdaa05..b419764970 100644 --- a/packages/twenty-server/src/modules/calendar/jobs/calendar-job.module.ts +++ b/packages/twenty-server/src/modules/calendar/jobs/calendar-job.module.ts @@ -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, ]), diff --git a/packages/twenty-server/src/modules/calendar/jobs/google-calendar-sync.job.ts b/packages/twenty-server/src/modules/calendar/jobs/google-calendar-sync.job.ts index 46240cca07..38f90f8b98 100644 --- a/packages/twenty-server/src/modules/calendar/jobs/google-calendar-sync.job.ts +++ b/packages/twenty-server/src/modules/calendar/jobs/google-calendar-sync.job.ts @@ -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); diff --git a/packages/twenty-server/src/modules/calendar/listeners/calendar-event-participant.listener.ts b/packages/twenty-server/src/modules/calendar/listeners/calendar-event-participant.listener.ts index 79025878c5..7f8e6e3c0e 100644 --- a/packages/twenty-server/src/modules/calendar/listeners/calendar-event-participant.listener.ts +++ b/packages/twenty-server/src/modules/calendar/listeners/calendar-event-participant.listener.ts @@ -25,7 +25,7 @@ export class CalendarEventParticipantListener { @OnEvent('calendarEventParticipant.matched') public async handleCalendarEventParticipantMatchedEvent(payload: { workspaceId: string; - userId: string; + workspaceMemberId: string; calendarEventParticipants: ObjectRecord[]; }): Promise { 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, diff --git a/packages/twenty-server/src/modules/calendar/query-hooks/calendar-event/calendar-event-find-many.pre-query.hook.ts b/packages/twenty-server/src/modules/calendar/query-hooks/calendar-event/calendar-event-find-many.pre-query.hook.ts index a5c8b06aa6..ee20809427 100644 --- a/packages/twenty-server/src/modules/calendar/query-hooks/calendar-event/calendar-event-find-many.pre-query.hook.ts +++ b/packages/twenty-server/src/modules/calendar/query-hooks/calendar-event/calendar-event-find-many.pre-query.hook.ts @@ -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, 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, + // ); } } diff --git a/packages/twenty-server/src/modules/calendar/query-hooks/calendar-event/calendar-event-find-one.pre-query-hook.ts b/packages/twenty-server/src/modules/calendar/query-hooks/calendar-event/calendar-event-find-one.pre-query-hook.ts index 082e1b2801..34642b128e 100644 --- a/packages/twenty-server/src/modules/calendar/query-hooks/calendar-event/calendar-event-find-one.pre-query-hook.ts +++ b/packages/twenty-server/src/modules/calendar/query-hooks/calendar-event/calendar-event-find-one.pre-query-hook.ts @@ -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, 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, + // ); } } diff --git a/packages/twenty-server/src/modules/calendar/query-hooks/calendar-event/services/can-access-calendar-event.service.ts b/packages/twenty-server/src/modules/calendar/query-hooks/calendar-event/services/can-access-calendar-event.service.ts index 0a971c32dc..b86a3ca1cd 100644 --- a/packages/twenty-server/src/modules/calendar/query-hooks/calendar-event/services/can-access-calendar-event.service.ts +++ b/packages/twenty-server/src/modules/calendar/query-hooks/calendar-event/services/can-access-calendar-event.service.ts @@ -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, @InjectObjectMetadataRepository(ConnectedAccountWorkspaceEntity) private readonly connectedAccountRepository: ConnectedAccountRepository, @InjectObjectMetadataRepository(WorkspaceMemberWorkspaceEntity) @@ -29,14 +30,17 @@ export class CanAccessCalendarEventService { public async canAccessCalendarEvent( userId: string, workspaceId: string, - calendarChannelCalendarEventAssociations: ObjectRecord[], + 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, ); diff --git a/packages/twenty-server/src/modules/calendar/query-hooks/calendar-query-hook.module.ts b/packages/twenty-server/src/modules/calendar/query-hooks/calendar-query-hook.module.ts index 0bed2914aa..555b10719f 100644 --- a/packages/twenty-server/src/modules/calendar/query-hooks/calendar-query-hook.module.ts +++ b/packages/twenty-server/src/modules/calendar/query-hooks/calendar-query-hook.module.ts @@ -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, ]), diff --git a/packages/twenty-server/src/modules/calendar/repositories/calendar-channel-event-association.repository.ts b/packages/twenty-server/src/modules/calendar/repositories/calendar-channel-event-association.repository.ts deleted file mode 100644 index 89a9a28b48..0000000000 --- a/packages/twenty-server/src/modules/calendar/repositories/calendar-channel-event-association.repository.ts +++ /dev/null @@ -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[]> { - 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[]> { - 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[]> { - 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, - '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, - ); - } -} diff --git a/packages/twenty-server/src/modules/calendar/repositories/calendar-channel.repository.ts b/packages/twenty-server/src/modules/calendar/repositories/calendar-channel.repository.ts deleted file mode 100644 index d0cde3ca82..0000000000 --- a/packages/twenty-server/src/modules/calendar/repositories/calendar-channel.repository.ts +++ /dev/null @@ -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[]> { - const dataSourceSchema = - this.workspaceDataSourceService.getSchemaName(workspaceId); - - return await this.workspaceDataSourceService.executeRawQuery( - `SELECT * FROM ${dataSourceSchema}."calendarChannel"`, - [], - workspaceId, - transactionManager, - ); - } - - public async create( - calendarChannel: Pick< - ObjectRecord, - 'id' | 'connectedAccountId' | 'handle' | 'visibility' - >, - workspaceId: string, - transactionManager?: EntityManager, - ): Promise { - 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[]> { - 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 | undefined> { - const calendarChannels = await this.getByConnectedAccountId( - connectedAccountId, - workspaceId, - ); - - return calendarChannels[0]; - } - - public async getByIds( - ids: string[], - workspaceId: string, - transactionManager?: EntityManager, - ): Promise[]> { - 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[]> { - 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 { - const dataSourceSchema = - this.workspaceDataSourceService.getSchemaName(workspaceId); - - await this.workspaceDataSourceService.executeRawQuery( - `UPDATE ${dataSourceSchema}."calendarChannel" SET "syncCursor" = $1 WHERE "id" = $2`, - [syncCursor || '', calendarChannelId], - workspaceId, - transactionManager, - ); - } -} diff --git a/packages/twenty-server/src/modules/calendar/repositories/calendar-event-participant.repository.ts b/packages/twenty-server/src/modules/calendar/repositories/calendar-event-participant.repository.ts deleted file mode 100644 index f095e100e7..0000000000 --- a/packages/twenty-server/src/modules/calendar/repositories/calendar-event-participant.repository.ts +++ /dev/null @@ -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[]> { - 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[]> { - 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[]> { - 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[]> { - 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 { - 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 { - 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 { - 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 { - 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; - } -} diff --git a/packages/twenty-server/src/modules/calendar/repositories/calendar-event.repository.ts b/packages/twenty-server/src/modules/calendar/repositories/calendar-event.repository.ts deleted file mode 100644 index f6f28918dd..0000000000 --- a/packages/twenty-server/src/modules/calendar/repositories/calendar-event.repository.ts +++ /dev/null @@ -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[]> { - 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[]> { - 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 { - 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[]> { - 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> { - 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(); - - calendarEvents?.forEach((calendarEvent) => { - iCalUIDsCalendarEventIdsMap.set(calendarEvent.iCalUID, calendarEvent.id); - }); - - return iCalUIDsCalendarEventIdsMap; - } - - public async saveCalendarEvents( - calendarEvents: CalendarEvent[], - workspaceId: string, - transactionManager?: EntityManager, - ): Promise { - 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 { - 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, - ); - } -} diff --git a/packages/twenty-server/src/modules/calendar/services/calendar-event-cleaner/calendar-event-cleaner.module.ts b/packages/twenty-server/src/modules/calendar/services/calendar-event-cleaner/calendar-event-cleaner.module.ts index 644371db16..12f92a4ec8 100644 --- a/packages/twenty-server/src/modules/calendar/services/calendar-event-cleaner/calendar-event-cleaner.module.ts +++ b/packages/twenty-server/src/modules/calendar/services/calendar-event-cleaner/calendar-event-cleaner.module.ts @@ -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], }) diff --git a/packages/twenty-server/src/modules/calendar/services/calendar-event-cleaner/calendar-event-cleaner.service.ts b/packages/twenty-server/src/modules/calendar/services/calendar-event-cleaner/calendar-event-cleaner.service.ts index 9f3f5d5202..77a347a0d6 100644 --- a/packages/twenty-server/src/modules/calendar/services/calendar-event-cleaner/calendar-event-cleaner.service.ts +++ b/packages/twenty-server/src/modules/calendar/services/calendar-event-cleaner/calendar-event-cleaner.service.ts @@ -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, ) {} 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) }); + }, ); } } diff --git a/packages/twenty-server/src/modules/calendar/services/calendar-event-participant/calendar-event-participant.module.ts b/packages/twenty-server/src/modules/calendar/services/calendar-event-participant/calendar-event-participant.module.ts index 4eabeedb99..212a087dbf 100644 --- a/packages/twenty-server/src/modules/calendar/services/calendar-event-participant/calendar-event-participant.module.ts +++ b/packages/twenty-server/src/modules/calendar/services/calendar-event-participant/calendar-event-participant.module.ts @@ -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, ], diff --git a/packages/twenty-server/src/modules/calendar/services/calendar-event-participant/calendar-event-participant.service.ts b/packages/twenty-server/src/modules/calendar/services/calendar-event-participant/calendar-event-participant.service.ts index 4b524de0de..dee6a2aead 100644 --- a/packages/twenty-server/src/modules/calendar/services/calendar-event-participant/calendar-event-participant.service.ts +++ b/packages/twenty-server/src/modules/calendar/services/calendar-event-participant/calendar-event-participant.service.ts @@ -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, @InjectObjectMetadataRepository(PersonWorkspaceEntity) private readonly personRepository: PersonRepository, private readonly addPersonIdAndWorkspaceMemberIdService: AddPersonIdAndWorkspaceMemberIdService, @@ -31,11 +32,11 @@ export class CalendarEventParticipantService { workspaceId: string, transactionManager?: EntityManager, ): Promise[]> { - 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, + }, ); } } diff --git a/packages/twenty-server/src/modules/calendar/services/google-calendar-sync/google-calendar-sync.module.ts b/packages/twenty-server/src/modules/calendar/services/google-calendar-sync/google-calendar-sync.module.ts index fcd5e495ce..11017c7f4c 100644 --- a/packages/twenty-server/src/modules/calendar/services/google-calendar-sync/google-calendar-sync.module.ts +++ b/packages/twenty-server/src/modules/calendar/services/google-calendar-sync/google-calendar-sync.module.ts @@ -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, diff --git a/packages/twenty-server/src/modules/calendar/services/google-calendar-sync/google-calendar-sync.service.ts b/packages/twenty-server/src/modules/calendar/services/google-calendar-sync/google-calendar-sync.service.ts index 17681f17e3..a7d653967a 100644 --- a/packages/twenty-server/src/modules/calendar/services/google-calendar-sync/google-calendar-sync.service.ts +++ b/packages/twenty-server/src/modules/calendar/services/google-calendar-sync/google-calendar-sync.service.ts @@ -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, + @InjectWorkspaceRepository(CalendarChannelWorkspaceEntity) + private readonly calendarChannelRepository: WorkspaceRepository, + @InjectWorkspaceRepository(CalendarChannelEventAssociationWorkspaceEntity) + private readonly calendarChannelEventAssociationRepository: WorkspaceRepository, + @InjectWorkspaceRepository(CalendarEventParticipantWorkspaceEntity) + private readonly calendarEventParticipantsRepository: WorkspaceRepository, @InjectObjectMetadataRepository(BlocklistWorkspaceEntity) private readonly blocklistRepository: BlocklistRepository, @InjectRepository(FeatureFlagEntity, 'core') private readonly featureFlagRepository: Repository, - 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 { - 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, }); diff --git a/packages/twenty-server/src/modules/calendar/services/workspace-google-calendar-sync/workspace-google-calendar-sync.module.ts b/packages/twenty-server/src/modules/calendar/services/workspace-google-calendar-sync/workspace-google-calendar-sync.module.ts index 877de36e89..f2c009caf2 100644 --- a/packages/twenty-server/src/modules/calendar/services/workspace-google-calendar-sync/workspace-google-calendar-sync.module.ts +++ b/packages/twenty-server/src/modules/calendar/services/workspace-google-calendar-sync/workspace-google-calendar-sync.module.ts @@ -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], }) diff --git a/packages/twenty-server/src/modules/calendar/services/workspace-google-calendar-sync/workspace-google-calendar-sync.service.ts b/packages/twenty-server/src/modules/calendar/services/workspace-google-calendar-sync/workspace-google-calendar-sync.service.ts index 8d68c01d88..9b2fd6c5af 100644 --- a/packages/twenty-server/src/modules/calendar/services/workspace-google-calendar-sync/workspace-google-calendar-sync.service.ts +++ b/packages/twenty-server/src/modules/calendar/services/workspace-google-calendar-sync/workspace-google-calendar-sync.service.ts @@ -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, @InjectMessageQueue(MessageQueue.calendarQueue) private readonly messageQueueService: MessageQueueService, ) {} @@ -23,8 +23,7 @@ export class WorkspaceGoogleCalendarSyncService { public async startWorkspaceGoogleCalendarSync( workspaceId: string, ): Promise { - 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, diff --git a/packages/twenty-server/src/modules/calendar/standard-objects/calendar-event-participant.workspace-entity.ts b/packages/twenty-server/src/modules/calendar/standard-objects/calendar-event-participant.workspace-entity.ts index ee271b4379..6e6d81f225 100644 --- a/packages/twenty-server/src/modules/calendar/standard-objects/calendar-event-participant.workspace-entity.ts +++ b/packages/twenty-server/src/modules/calendar/standard-objects/calendar-event-participant.workspace-entity.ts @@ -120,7 +120,7 @@ export class CalendarEventParticipantWorkspaceEntity extends BaseWorkspaceEntity inverseSideFieldKey: 'calendarEventParticipants', }) @WorkspaceIsNullable() - person: Relation; + person: Relation | null; @WorkspaceRelation({ standardId: CALENDAR_EVENT_PARTICIPANT_STANDARD_FIELD_IDS.workspaceMember, @@ -133,5 +133,5 @@ export class CalendarEventParticipantWorkspaceEntity extends BaseWorkspaceEntity inverseSideFieldKey: 'calendarEventParticipants', }) @WorkspaceIsNullable() - workspaceMember: Relation; + workspaceMember: Relation | null; } diff --git a/packages/twenty-server/src/modules/company/standard-objects/company.workspace-entity.ts b/packages/twenty-server/src/modules/company/standard-objects/company.workspace-entity.ts index 867b11e023..490a33919c 100644 --- a/packages/twenty-server/src/modules/company/standard-objects/company.workspace-entity.ts +++ b/packages/twenty-server/src/modules/company/standard-objects/company.workspace-entity.ts @@ -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; + accountOwner: Relation | null; @WorkspaceRelation({ standardId: COMPANY_STANDARD_FIELD_IDS.activityTargets, diff --git a/packages/twenty-server/src/modules/connected-account/auto-companies-and-contacts-creation/auto-companies-and-contacts-creation.module.ts b/packages/twenty-server/src/modules/connected-account/auto-companies-and-contacts-creation/auto-companies-and-contacts-creation.module.ts index 5e6d0fa68a..15a6131d1d 100644 --- a/packages/twenty-server/src/modules/connected-account/auto-companies-and-contacts-creation/auto-companies-and-contacts-creation.module.ts +++ b/packages/twenty-server/src/modules/connected-account/auto-companies-and-contacts-creation/auto-companies-and-contacts-creation.module.ts @@ -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, diff --git a/packages/twenty-server/src/modules/connected-account/auto-companies-and-contacts-creation/jobs/create-company-and-contact.job.ts b/packages/twenty-server/src/modules/connected-account/auto-companies-and-contacts-creation/jobs/create-company-and-contact.job.ts index 744dcf294c..7124d1467d 100644 --- a/packages/twenty-server/src/modules/connected-account/auto-companies-and-contacts-creation/jobs/create-company-and-contact.job.ts +++ b/packages/twenty-server/src/modules/connected-account/auto-companies-and-contacts-creation/jobs/create-company-and-contact.job.ts @@ -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; + connectedAccount: ConnectedAccountWorkspaceEntity; contactsToCreate: { displayName: string; handle: string; diff --git a/packages/twenty-server/src/modules/connected-account/auto-companies-and-contacts-creation/services/create-company-and-contact.service.ts b/packages/twenty-server/src/modules/connected-account/auto-companies-and-contacts-creation/services/create-company-and-contact.service.ts index 7fd4685103..abe0f2603a 100644 --- a/packages/twenty-server/src/modules/connected-account/auto-companies-and-contacts-creation/services/create-company-and-contact.service.ts +++ b/packages/twenty-server/src/modules/connected-account/auto-companies-and-contacts-creation/services/create-company-and-contact.service.ts @@ -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, + connectedAccount: ConnectedAccountWorkspaceEntity, contactsToCreate: Contacts, workspaceId: string, ) { - const { dataSource: workspaceDataSource } = - await this.workspaceDataSourceService.connectedToWorkspaceDataSourceAndReturnMetadata( - workspaceId, - ); - let updatedMessageParticipants: ObjectRecord[] = []; let updatedCalendarEventParticipants: ObjectRecord[] = []; - 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, }); } diff --git a/packages/twenty-server/src/modules/connected-account/services/google-api-refresh-access-token/google-api-refresh-access-token.service.ts b/packages/twenty-server/src/modules/connected-account/services/google-api-refresh-access-token/google-api-refresh-access-token.service.ts index dc687e1a4c..31fc052ce6 100644 --- a/packages/twenty-server/src/modules/connected-account/services/google-api-refresh-access-token/google-api-refresh-access-token.service.ts +++ b/packages/twenty-server/src/modules/connected-account/services/google-api-refresh-access-token/google-api-refresh-access-token.service.ts @@ -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, diff --git a/packages/twenty-server/src/modules/connected-account/standard-objects/connected-account.workspace-entity.ts b/packages/twenty-server/src/modules/connected-account/standard-objects/connected-account.workspace-entity.ts index 481f319703..5302d08ee9 100644 --- a/packages/twenty-server/src/modules/connected-account/standard-objects/connected-account.workspace-entity.ts +++ b/packages/twenty-server/src/modules/connected-account/standard-objects/connected-account.workspace-entity.ts @@ -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; + accountOwnerId: string; + @WorkspaceRelation({ standardId: CONNECTED_ACCOUNT_STANDARD_FIELD_IDS.messageChannels, type: RelationMetadataType.ONE_TO_MANY, diff --git a/packages/twenty-server/src/modules/favorite/standard-objects/favorite.workspace-entity.ts b/packages/twenty-server/src/modules/favorite/standard-objects/favorite.workspace-entity.ts index 6c1f81b6b3..9995a4af7f 100644 --- a/packages/twenty-server/src/modules/favorite/standard-objects/favorite.workspace-entity.ts +++ b/packages/twenty-server/src/modules/favorite/standard-objects/favorite.workspace-entity.ts @@ -63,7 +63,7 @@ export class FavoriteWorkspaceEntity extends BaseWorkspaceEntity { inverseSideFieldKey: 'favorites', }) @WorkspaceIsNullable() - person: Relation; + person: Relation | null; @WorkspaceRelation({ standardId: FAVORITE_STANDARD_FIELD_IDS.company, @@ -76,7 +76,7 @@ export class FavoriteWorkspaceEntity extends BaseWorkspaceEntity { inverseSideFieldKey: 'favorites', }) @WorkspaceIsNullable() - company: Relation; + company: Relation | null; @WorkspaceRelation({ standardId: FAVORITE_STANDARD_FIELD_IDS.opportunity, @@ -89,7 +89,7 @@ export class FavoriteWorkspaceEntity extends BaseWorkspaceEntity { inverseSideFieldKey: 'favorites', }) @WorkspaceIsNullable() - opportunity: Relation; + opportunity: Relation | null; @WorkspaceDynamicRelation({ type: RelationMetadataType.MANY_TO_ONE, diff --git a/packages/twenty-server/src/modules/messaging/blocklist-manager/jobs/messaging-blocklist-item-delete-messages.job.ts b/packages/twenty-server/src/modules/messaging/blocklist-manager/jobs/messaging-blocklist-item-delete-messages.job.ts index 89b7200406..00f146f7ec 100644 --- a/packages/twenty-server/src/modules/messaging/blocklist-manager/jobs/messaging-blocklist-item-delete-messages.job.ts +++ b/packages/twenty-server/src/modules/messaging/blocklist-manager/jobs/messaging-blocklist-item-delete-messages.job.ts @@ -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, diff --git a/packages/twenty-server/src/modules/messaging/common/query-hooks/message/can-access-message-thread.service.ts b/packages/twenty-server/src/modules/messaging/common/query-hooks/message/can-access-message-thread.service.ts index 095523ee63..f79c972c61 100644 --- a/packages/twenty-server/src/modules/messaging/common/query-hooks/message/can-access-message-thread.service.ts +++ b/packages/twenty-server/src/modules/messaging/common/query-hooks/message/can-access-message-thread.service.ts @@ -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, ); diff --git a/packages/twenty-server/src/modules/messaging/common/services/messaging-error-handling.service.ts b/packages/twenty-server/src/modules/messaging/common/services/messaging-error-handling.service.ts index c8cdce1d02..fb3c7b99d2 100644 --- a/packages/twenty-server/src/modules/messaging/common/services/messaging-error-handling.service.ts +++ b/packages/twenty-server/src/modules/messaging/common/services/messaging-error-handling.service.ts @@ -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, diff --git a/packages/twenty-server/src/modules/messaging/common/services/messaging-message-participant.service.ts b/packages/twenty-server/src/modules/messaging/common/services/messaging-message-participant.service.ts index 78a5623bba..527a3951c2 100644 --- a/packages/twenty-server/src/modules/messaging/common/services/messaging-message-participant.service.ts +++ b/packages/twenty-server/src/modules/messaging/common/services/messaging-message-participant.service.ts @@ -149,7 +149,7 @@ export class MessagingMessageParticipantService { this.eventEmitter.emit(`messageParticipant.matched`, { workspaceId, - userId: null, + workspaceMemberId: null, messageParticipants: updatedMessageParticipants, }); } diff --git a/packages/twenty-server/src/modules/messaging/common/services/messaging-save-messages-and-enqueue-contact-creation.service.ts b/packages/twenty-server/src/modules/messaging/common/services/messaging-save-messages-and-enqueue-contact-creation.service.ts index 9b288566a9..1493acb8a5 100644 --- a/packages/twenty-server/src/modules/messaging/common/services/messaging-save-messages-and-enqueue-contact-creation.service.ts +++ b/packages/twenty-server/src/modules/messaging/common/services/messaging-save-messages-and-enqueue-contact-creation.service.ts @@ -107,7 +107,7 @@ export class MessagingSaveMessagesAndEnqueueContactCreationService { this.eventEmitter.emit(`messageParticipant.matched`, { workspaceId, - userId: connectedAccount.accountOwnerId, + workspaceMemberId: connectedAccount.accountOwnerId, messageParticipants: savedMessageParticipants, }); diff --git a/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel-message-association.workspace-entity.ts b/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel-message-association.workspace-entity.ts index 91e9fa8282..8792293d6e 100644 --- a/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel-message-association.workspace-entity.ts +++ b/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel-message-association.workspace-entity.ts @@ -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; + messageChannel: Relation | 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; + message: Relation | null; @WorkspaceRelation({ standardId: @@ -87,5 +87,5 @@ export class MessageChannelMessageAssociationWorkspaceEntity extends BaseWorkspa inverseSideFieldKey: 'messageChannelMessageAssociations', }) @WorkspaceIsNullable() - messageThread: Relation; + messageThread: Relation | null; } diff --git a/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel.workspace-entity.ts b/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel.workspace-entity.ts index 92175c6afb..b2bf40b1bc 100644 --- a/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel.workspace-entity.ts +++ b/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel.workspace-entity.ts @@ -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, diff --git a/packages/twenty-server/src/modules/messaging/common/standard-objects/message-participant.workspace-entity.ts b/packages/twenty-server/src/modules/messaging/common/standard-objects/message-participant.workspace-entity.ts index b8c1e09f64..32068791b1 100644 --- a/packages/twenty-server/src/modules/messaging/common/standard-objects/message-participant.workspace-entity.ts +++ b/packages/twenty-server/src/modules/messaging/common/standard-objects/message-participant.workspace-entity.ts @@ -83,7 +83,7 @@ export class MessageParticipantWorkspaceEntity extends BaseWorkspaceEntity { inverseSideFieldKey: 'messageParticipants', }) @WorkspaceIsNullable() - person: Relation; + person: Relation | null; @WorkspaceRelation({ standardId: MESSAGE_PARTICIPANT_STANDARD_FIELD_IDS.workspaceMember, @@ -96,5 +96,5 @@ export class MessageParticipantWorkspaceEntity extends BaseWorkspaceEntity { inverseSideFieldKey: 'messageParticipants', }) @WorkspaceIsNullable() - workspaceMember: Relation; + workspaceMember: Relation | null; } diff --git a/packages/twenty-server/src/modules/messaging/common/standard-objects/message.workspace-entity.ts b/packages/twenty-server/src/modules/messaging/common/standard-objects/message.workspace-entity.ts index a3ad1ea48c..3b2c899ca2 100644 --- a/packages/twenty-server/src/modules/messaging/common/standard-objects/message.workspace-entity.ts +++ b/packages/twenty-server/src/modules/messaging/common/standard-objects/message.workspace-entity.ts @@ -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; + messageThread: Relation | null; @WorkspaceRelation({ standardId: MESSAGE_STANDARD_FIELD_IDS.messageParticipants, diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-message-list-fetch.job.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-message-list-fetch.job.ts index a22126de9e..90ee6dbf5d 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-message-list-fetch.job.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-message-list-fetch.job.ts @@ -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); diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-messages-import.job.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-messages-import.job.ts index f0a543d807..46f3b89dcd 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-messages-import.job.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-messages-import.job.ts @@ -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) diff --git a/packages/twenty-server/src/modules/messaging/message-participants-manager/listeners/message-participant.listener.ts b/packages/twenty-server/src/modules/messaging/message-participants-manager/listeners/message-participant.listener.ts index 92853ba5d3..346dcf64fb 100644 --- a/packages/twenty-server/src/modules/messaging/message-participants-manager/listeners/message-participant.listener.ts +++ b/packages/twenty-server/src/modules/messaging/message-participants-manager/listeners/message-participant.listener.ts @@ -25,7 +25,7 @@ export class MessageParticipantListener { @OnEvent('messageParticipant.matched') public async handleMessageParticipantMatched(payload: { workspaceId: string; - userId: string; + workspaceMemberId: string; messageParticipants: ObjectRecord[]; }): Promise { 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, diff --git a/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.ts b/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.ts index ee420467c5..584792ac4f 100644 --- a/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.ts +++ b/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.ts @@ -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, diff --git a/packages/twenty-server/src/modules/opportunity/standard-objects/opportunity.workspace-entity.ts b/packages/twenty-server/src/modules/opportunity/standard-objects/opportunity.workspace-entity.ts index 5ec506eb57..8592632616 100644 --- a/packages/twenty-server/src/modules/opportunity/standard-objects/opportunity.workspace-entity.ts +++ b/packages/twenty-server/src/modules/opportunity/standard-objects/opportunity.workspace-entity.ts @@ -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; + pointOfContact: Relation | null; @WorkspaceRelation({ standardId: OPPORTUNITY_STANDARD_FIELD_IDS.company, @@ -130,7 +130,7 @@ export class OpportunityWorkspaceEntity extends BaseWorkspaceEntity { onDelete: RelationOnDeleteAction.SET_NULL, }) @WorkspaceIsNullable() - company: Relation; + company: Relation | null; @WorkspaceRelation({ standardId: OPPORTUNITY_STANDARD_FIELD_IDS.favorites, diff --git a/packages/twenty-server/src/modules/person/standard-objects/person.workspace-entity.ts b/packages/twenty-server/src/modules/person/standard-objects/person.workspace-entity.ts index 83353993df..6340c84297 100644 --- a/packages/twenty-server/src/modules/person/standard-objects/person.workspace-entity.ts +++ b/packages/twenty-server/src/modules/person/standard-objects/person.workspace-entity.ts @@ -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; + company: Relation | null; @WorkspaceRelation({ standardId: PERSON_STANDARD_FIELD_IDS.pointOfContactForOpportunities, diff --git a/packages/twenty-server/src/modules/timeline/repositiories/timeline-activity.repository.ts b/packages/twenty-server/src/modules/timeline/repositiories/timeline-activity.repository.ts index 8d9a99c7d9..a311dd1f8a 100644 --- a/packages/twenty-server/src/modules/timeline/repositiories/timeline-activity.repository.ts +++ b/packages/twenty-server/src/modules/timeline/repositiories/timeline-activity.repository.ts @@ -151,9 +151,9 @@ export class TimelineActivityRepository { name: string; properties: Record | null; workspaceMemberId: string | undefined; - recordId: string; + recordId: string | null; linkedRecordCachedName: string; - linkedRecordId: string | undefined; + linkedRecordId: string | null | undefined; linkedObjectMetadataId: string | undefined; }[], workspaceId: string, diff --git a/packages/twenty-server/src/modules/timeline/standard-objects/audit-log.workspace-entity.ts b/packages/twenty-server/src/modules/timeline/standard-objects/audit-log.workspace-entity.ts index d7b42a75b0..b0b801db8c 100644 --- a/packages/twenty-server/src/modules/timeline/standard-objects/audit-log.workspace-entity.ts +++ b/packages/twenty-server/src/modules/timeline/standard-objects/audit-log.workspace-entity.ts @@ -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; + workspaceMember: Relation | null; } diff --git a/packages/twenty-server/src/modules/timeline/standard-objects/behavioral-event.workspace-entity.ts b/packages/twenty-server/src/modules/timeline/standard-objects/behavioral-event.workspace-entity.ts index 3460768e4f..a74069685b 100644 --- a/packages/twenty-server/src/modules/timeline/standard-objects/behavioral-event.workspace-entity.ts +++ b/packages/twenty-server/src/modules/timeline/standard-objects/behavioral-event.workspace-entity.ts @@ -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; } diff --git a/packages/twenty-server/src/modules/timeline/standard-objects/timeline-activity.workspace-entity.ts b/packages/twenty-server/src/modules/timeline/standard-objects/timeline-activity.workspace-entity.ts index 40cd493076..2e366378ff 100644 --- a/packages/twenty-server/src/modules/timeline/standard-objects/timeline-activity.workspace-entity.ts +++ b/packages/twenty-server/src/modules/timeline/standard-objects/timeline-activity.workspace-entity.ts @@ -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; + workspaceMember: Relation | null; @WorkspaceRelation({ standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.person, @@ -113,7 +113,7 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity { inverseSideFieldKey: 'timelineActivities', }) @WorkspaceIsNullable() - person: Relation; + person: Relation | null; @WorkspaceRelation({ standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.company, @@ -126,7 +126,7 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity { inverseSideFieldKey: 'timelineActivities', }) @WorkspaceIsNullable() - company: Relation; + company: Relation | null; @WorkspaceRelation({ standardId: TIMELINE_ACTIVITY_STANDARD_FIELD_IDS.opportunity, @@ -139,7 +139,7 @@ export class TimelineActivityWorkspaceEntity extends BaseWorkspaceEntity { inverseSideFieldKey: 'timelineActivities', }) @WorkspaceIsNullable() - opportunity: Relation; + opportunity: Relation | null; @WorkspaceDynamicRelation({ type: RelationMetadataType.MANY_TO_ONE, diff --git a/packages/twenty-server/src/modules/view/standard-objects/view-field.workspace-entity.ts b/packages/twenty-server/src/modules/view/standard-objects/view-field.workspace-entity.ts index 424f4299ce..474fedb825 100644 --- a/packages/twenty-server/src/modules/view/standard-objects/view-field.workspace-entity.ts +++ b/packages/twenty-server/src/modules/view/standard-objects/view-field.workspace-entity.ts @@ -72,5 +72,5 @@ export class ViewFieldWorkspaceEntity extends BaseWorkspaceEntity { joinColumn: 'viewId', }) @WorkspaceIsNullable() - view?: ViewWorkspaceEntity; + view?: ViewWorkspaceEntity | null; } diff --git a/packages/twenty-server/src/modules/view/standard-objects/view-filter.workspace-entity.ts b/packages/twenty-server/src/modules/view/standard-objects/view-filter.workspace-entity.ts index bce3e944af..5d0f5598fd 100644 --- a/packages/twenty-server/src/modules/view/standard-objects/view-filter.workspace-entity.ts +++ b/packages/twenty-server/src/modules/view/standard-objects/view-filter.workspace-entity.ts @@ -68,5 +68,5 @@ export class ViewFilterWorkspaceEntity extends BaseWorkspaceEntity { inverseSideFieldKey: 'viewFilters', }) @WorkspaceIsNullable() - view: Relation; + view: Relation | null; } diff --git a/packages/twenty-server/src/modules/view/standard-objects/view-sort.workspace-entity.ts b/packages/twenty-server/src/modules/view/standard-objects/view-sort.workspace-entity.ts index 458d4d3168..6ecc1b4fe3 100644 --- a/packages/twenty-server/src/modules/view/standard-objects/view-sort.workspace-entity.ts +++ b/packages/twenty-server/src/modules/view/standard-objects/view-sort.workspace-entity.ts @@ -53,5 +53,5 @@ export class ViewSortWorkspaceEntity extends BaseWorkspaceEntity { inverseSideFieldKey: 'viewSorts', }) @WorkspaceIsNullable() - view: Relation; + view: Relation | null; } diff --git a/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-delete-one.pre-query.hook.ts b/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-delete-one.pre-query.hook.ts index efa6cd863c..67f7d3f9e6 100644 --- a/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-delete-one.pre-query.hook.ts +++ b/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-delete-one.pre-query.hook.ts @@ -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, + @InjectWorkspaceRepository(CommentWorkspaceEntity) + private readonly commentRepository: WorkspaceRepository, ) {} // 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 { - 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, + }, + }); } } diff --git a/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-query-hook.module.ts b/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-query-hook.module.ts index 14c1ef5e53..44f2362c83 100644 --- a/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-query-hook.module.ts +++ b/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-query-hook.module.ts @@ -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, ]), diff --git a/packages/twenty-server/src/utils/is-defined.ts b/packages/twenty-server/src/utils/is-defined.ts new file mode 100644 index 0000000000..1be478d76d --- /dev/null +++ b/packages/twenty-server/src/utils/is-defined.ts @@ -0,0 +1,3 @@ +export const isDefined = (value: T | null | undefined): value is T => { + return value !== null && value !== undefined; +}; diff --git a/packages/twenty-server/src/utils/typed-reflect.ts b/packages/twenty-server/src/utils/typed-reflect.ts index 6e9fdb8193..c8a25cee93 100644 --- a/packages/twenty-server/src/utils/typed-reflect.ts +++ b/packages/twenty-server/src/utils/typed-reflect.ts @@ -8,6 +8,7 @@ export interface ReflectMetadataTypeMap { ['workspace:is-system-metadata-args']: true; ['workspace:is-audit-logged-metadata-args']: false; ['workspace:is-primary-field-metadata-args']: true; + ['workspace:join-column']: true; } export class TypedReflect {