diff --git a/packages/twenty-server/src/app.module.ts b/packages/twenty-server/src/app.module.ts index d06fa2c5a56..33510d31a2d 100644 --- a/packages/twenty-server/src/app.module.ts +++ b/packages/twenty-server/src/app.module.ts @@ -23,6 +23,7 @@ import { MetricsModule } from 'src/engine/core-modules/metrics/metrics.module'; import { DataloaderModule } from 'src/engine/dataloaders/dataloader.module'; import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module'; import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module'; +import { WorkspaceAuthContextMiddleware } from 'src/engine/core-modules/auth/middlewares/workspace-auth-context.middleware'; import { GraphQLHydrateRequestFromTokenMiddleware } from 'src/engine/middlewares/graphql-hydrate-request-from-token.middleware'; import { MiddlewareModule } from 'src/engine/middlewares/middleware.module'; import { RestCoreMiddleware } from 'src/engine/middlewares/rest-core.middleware'; @@ -111,16 +112,22 @@ export class AppModule { configure(consumer: MiddlewareConsumer) { consumer - .apply(GraphQLHydrateRequestFromTokenMiddleware) + .apply( + GraphQLHydrateRequestFromTokenMiddleware, + WorkspaceAuthContextMiddleware, + ) .forRoutes({ path: 'graphql', method: RequestMethod.ALL }); consumer - .apply(GraphQLHydrateRequestFromTokenMiddleware) + .apply( + GraphQLHydrateRequestFromTokenMiddleware, + WorkspaceAuthContextMiddleware, + ) .forRoutes({ path: 'metadata', method: RequestMethod.ALL }); for (const method of MIGRATED_REST_METHODS) { consumer - .apply(RestCoreMiddleware) + .apply(RestCoreMiddleware, WorkspaceAuthContextMiddleware) .forRoutes({ path: 'rest/*path', method }); } } diff --git a/packages/twenty-server/src/database/commands/command-runners/__tests__/upgrade.command-runner.spec.ts b/packages/twenty-server/src/database/commands/command-runners/__tests__/upgrade.command-runner.spec.ts index 69144983924..3d4ad1513d0 100644 --- a/packages/twenty-server/src/database/commands/command-runners/__tests__/upgrade.command-runner.spec.ts +++ b/packages/twenty-server/src/database/commands/command-runners/__tests__/upgrade.command-runner.spec.ts @@ -123,7 +123,7 @@ const buildUpgradeCommandModule = async ({ getDataSourceForWorkspace: jest.fn(), executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), }, }, { diff --git a/packages/twenty-server/src/database/commands/command-runners/workspaces-migration.command-runner.ts b/packages/twenty-server/src/database/commands/command-runners/workspaces-migration.command-runner.ts index f5a8baa0db1..25093235106 100644 --- a/packages/twenty-server/src/database/commands/command-runners/workspaces-migration.command-runner.ts +++ b/packages/twenty-server/src/database/commands/command-runners/workspaces-migration.command-runner.ts @@ -142,7 +142,6 @@ export abstract class WorkspacesMigrationCommandRunner< const authContext = buildSystemAuthContext(workspaceId); await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workspaceHasDataSource = await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceId( @@ -161,6 +160,7 @@ export abstract class WorkspacesMigrationCommandRunner< total: workspaceIdsToProcess.length, }); }, + authContext, ); this.migrationReport.success.push({ diff --git a/packages/twenty-server/src/engine/api/common/common-query-runners/common-base-query-runner.service.ts b/packages/twenty-server/src/engine/api/common/common-query-runners/common-base-query-runner.service.ts index 32f29b5c4de..39ed915222a 100644 --- a/packages/twenty-server/src/engine/api/common/common-query-runners/common-base-query-runner.service.ts +++ b/packages/twenty-server/src/engine/api/common/common-query-runners/common-base-query-runner.service.ts @@ -141,7 +141,6 @@ export abstract class CommonBaseQueryRunnerService< } as CommonExtendedInput; return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => this.executeQueryAndEnrichResults( processedArgs, @@ -149,6 +148,7 @@ export abstract class CommonBaseQueryRunnerService< queryRunnerContext, commonQueryParser, ), + authContext, ); } diff --git a/packages/twenty-server/src/engine/core-modules/actor/services/__tests__/actor-from-auth-context.service.spec.ts b/packages/twenty-server/src/engine/core-modules/actor/services/__tests__/actor-from-auth-context.service.spec.ts index b7fed676657..8c93ff16cdb 100644 --- a/packages/twenty-server/src/engine/core-modules/actor/services/__tests__/actor-from-auth-context.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/actor/services/__tests__/actor-from-auth-context.service.spec.ts @@ -45,7 +45,7 @@ describe('ActorFromAuthContextService', () => { getRepository: jest.fn().mockResolvedValue(mockWorkspaceMemberRepository), executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), }; beforeEach(async () => { diff --git a/packages/twenty-server/src/engine/core-modules/actor/services/actor-from-auth-context.service.ts b/packages/twenty-server/src/engine/core-modules/actor/services/actor-from-auth-context.service.ts index ebc72673a9a..ba2cdab51f6 100644 --- a/packages/twenty-server/src/engine/core-modules/actor/services/actor-from-auth-context.service.ts +++ b/packages/twenty-server/src/engine/core-modules/actor/services/actor-from-auth-context.service.ts @@ -165,7 +165,6 @@ export class ActorFromAuthContextService { if (isDefined(user)) { return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext as WorkspaceAuthContext, async () => { const workspaceMemberRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -187,6 +186,7 @@ export class ActorFromAuthContextService { workspaceMemberId: workspaceMember.id, }); }, + authContext as WorkspaceAuthContext, ); } diff --git a/packages/twenty-server/src/engine/core-modules/approved-access-domain/approved-access-domain.resolver.ts b/packages/twenty-server/src/engine/core-modules/approved-access-domain/approved-access-domain.resolver.ts index debd49f3a16..0e9e5654d63 100644 --- a/packages/twenty-server/src/engine/core-modules/approved-access-domain/approved-access-domain.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/approved-access-domain/approved-access-domain.resolver.ts @@ -47,7 +47,6 @@ export class ApprovedAccessDomainResolver { const workspaceMember = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workspaceMemberRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -62,6 +61,7 @@ export class ApprovedAccessDomainResolver { }, }); }, + authContext, ); return this.approvedAccessDomainService.createApprovedAccessDomain( diff --git a/packages/twenty-server/src/engine/core-modules/auth/middlewares/workspace-auth-context.middleware.ts b/packages/twenty-server/src/engine/core-modules/auth/middlewares/workspace-auth-context.middleware.ts new file mode 100644 index 00000000000..28a554ec490 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/auth/middlewares/workspace-auth-context.middleware.ts @@ -0,0 +1,33 @@ +import { Injectable, type NestMiddleware } from '@nestjs/common'; + +import { type NextFunction, type Request, type Response } from 'express'; +import { isDefined } from 'twenty-shared/utils'; + +import { type WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface'; + +import { withWorkspaceAuthContext } from 'src/engine/core-modules/auth/storage/workspace-auth-context.storage'; + +@Injectable() +export class WorkspaceAuthContextMiddleware implements NestMiddleware { + use(req: Request, _res: Response, next: NextFunction) { + if (!isDefined(req.workspace)) { + next(); + + return; + } + + const authContext: WorkspaceAuthContext = { + user: req.user, + workspace: req.workspace, + workspaceMemberId: req.workspaceMemberId, + workspaceMember: req.workspaceMember, + userWorkspaceId: req.userWorkspaceId, + apiKey: req.apiKey, + application: req.application, + } as WorkspaceAuthContext; + + withWorkspaceAuthContext(authContext, () => { + next(); + }); + } +} diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/create-calendar-channel.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/create-calendar-channel.service.ts index 9e2fd4b406f..e7ce8c8d21f 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/create-calendar-channel.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/create-calendar-channel.service.ts @@ -42,7 +42,6 @@ export class CreateCalendarChannelService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const calendarChannelRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -70,6 +69,7 @@ export class CreateCalendarChannelService { return newCalendarChannel.id; }, + authContext, ); } } diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/create-connected-account.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/create-connected-account.service.ts index 7c9af5d358a..b174a8c01ae 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/create-connected-account.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/create-connected-account.service.ts @@ -42,29 +42,26 @@ export class CreateConnectedAccountService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const connectedAccountRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'connectedAccount', - ); - - await connectedAccountRepository.save( - { - id: connectedAccountId, - handle, - provider, - accessToken, - refreshToken, - accountOwnerId, - scopes, - }, - {}, - manager, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const connectedAccountRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'connectedAccount', ); - }, - ); + + await connectedAccountRepository.save( + { + id: connectedAccountId, + handle, + provider, + accessToken, + refreshToken, + accountOwnerId, + scopes, + }, + {}, + manager, + ); + }, authContext); } } diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/create-message-channel.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/create-message-channel.service.ts index 42468d24588..09b14bcda9c 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/create-message-channel.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/create-message-channel.service.ts @@ -47,7 +47,6 @@ export class CreateMessageChannelService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const messageChannelRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -93,6 +92,7 @@ export class CreateMessageChannelService { return newMessageChannelId; }, + authContext, ); } } diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/google-apis.service.spec.ts b/packages/twenty-server/src/engine/core-modules/auth/services/google-apis.service.spec.ts index 8be530dc6bf..af5ef9f2575 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/google-apis.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/google-apis.service.spec.ts @@ -98,7 +98,7 @@ describe('GoogleAPIsService', () => { .mockResolvedValue(mockWorkspaceDataSource), executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), }, }, { 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 b1ce2005bed..b880c4791ab 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 @@ -100,7 +100,6 @@ export class GoogleAPIsService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const connectedAccountRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -258,6 +257,7 @@ export class GoogleAPIsService { return newOrExistingConnectedAccountId; }, + authContext, ); } } diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/microsoft-apis.service.spec.ts b/packages/twenty-server/src/engine/core-modules/auth/services/microsoft-apis.service.spec.ts index 7c27cdb3c77..ba5114910a1 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/microsoft-apis.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/microsoft-apis.service.spec.ts @@ -97,7 +97,7 @@ describe('MicrosoftAPIsService', () => { .mockResolvedValue(mockWorkspaceDataSource), executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), }, }, { diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/microsoft-apis.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/microsoft-apis.service.ts index 0e928fb9c9c..f637168ecc5 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/microsoft-apis.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/microsoft-apis.service.ts @@ -81,7 +81,6 @@ export class MicrosoftAPIsService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const connectedAccountRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -247,6 +246,7 @@ export class MicrosoftAPIsService { return newOrExistingConnectedAccountId; }, + authContext, ); } } diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/update-connected-account-on-reconnect.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/update-connected-account-on-reconnect.service.ts index 022dc9b3f4f..2f6c72cec65 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/update-connected-account-on-reconnect.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/update-connected-account-on-reconnect.service.ts @@ -35,28 +35,25 @@ export class UpdateConnectedAccountOnReconnectService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const connectedAccountRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'connectedAccount', - ); - - await connectedAccountRepository.update( - { - id: connectedAccountId, - }, - { - accessToken, - refreshToken, - scopes, - authFailedAt: null, - }, - manager, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const connectedAccountRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'connectedAccount', ); - }, - ); + + await connectedAccountRepository.update( + { + id: connectedAccountId, + }, + { + accessToken, + refreshToken, + scopes, + authFailedAt: null, + }, + manager, + ); + }, authContext); } } diff --git a/packages/twenty-server/src/engine/core-modules/auth/storage/workspace-auth-context.storage.ts b/packages/twenty-server/src/engine/core-modules/auth/storage/workspace-auth-context.storage.ts new file mode 100644 index 00000000000..cb2a19bb4ec --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/auth/storage/workspace-auth-context.storage.ts @@ -0,0 +1,25 @@ +import { AsyncLocalStorage } from 'async_hooks'; + +import { type WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface'; + +export const workspaceAuthContextStorage = + new AsyncLocalStorage(); + +export const getWorkspaceAuthContext = (): WorkspaceAuthContext => { + const context = workspaceAuthContextStorage.getStore(); + + if (!context) { + throw new Error( + 'Workspace auth context not set. Operations must be wrapped with withWorkspaceAuthContext()', + ); + } + + return context; +}; + +export const withWorkspaceAuthContext = ( + context: WorkspaceAuthContext, + fn: () => T | Promise, +): T | Promise => { + return workspaceAuthContextStorage.run(context, fn); +}; diff --git a/packages/twenty-server/src/engine/core-modules/auth/token/services/access-token.service.spec.ts b/packages/twenty-server/src/engine/core-modules/auth/token/services/access-token.service.spec.ts index 08426f27967..fbb271f8b0a 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/token/services/access-token.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/token/services/access-token.service.spec.ts @@ -82,7 +82,7 @@ describe('AccessTokenService', () => { getRepository: jest.fn(), executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), }, }, ], diff --git a/packages/twenty-server/src/engine/core-modules/auth/token/services/access-token.service.ts b/packages/twenty-server/src/engine/core-modules/auth/token/services/access-token.service.ts index 0314994aa64..8f6c3c4340e 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/token/services/access-token.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/token/services/access-token.service.ts @@ -84,7 +84,6 @@ export class AccessTokenService { tokenWorkspaceMemberId = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workspaceMemberRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -112,6 +111,7 @@ export class AccessTokenService { return workspaceMember.id; }, + authContext, ); } const userWorkspace = await this.userWorkspaceRepository.findOne({ diff --git a/packages/twenty-server/src/engine/core-modules/billing/jobs/update-subscription-quantity.job.ts b/packages/twenty-server/src/engine/core-modules/billing/jobs/update-subscription-quantity.job.ts index f64a9fac609..0de58a42629 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/jobs/update-subscription-quantity.job.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/jobs/update-subscription-quantity.job.ts @@ -29,36 +29,33 @@ export class UpdateSubscriptionQuantityJob { async handle(data: UpdateSubscriptionQuantityJobData): Promise { const authContext = buildSystemAuthContext(data.workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const workspaceMemberRepository = - await this.globalWorkspaceOrmManager.getRepository( - data.workspaceId, - 'workspaceMember', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workspaceMemberRepository = + await this.globalWorkspaceOrmManager.getRepository( + data.workspaceId, + 'workspaceMember', + ); - const workspaceMembersCount = await workspaceMemberRepository.count(); + const workspaceMembersCount = await workspaceMemberRepository.count(); - if (!workspaceMembersCount || workspaceMembersCount <= 0) { - return; - } + if (!workspaceMembersCount || workspaceMembersCount <= 0) { + return; + } - try { - await this.billingSubscriptionUpdateService.changeSeats( - data.workspaceId, - workspaceMembersCount, - ); + try { + await this.billingSubscriptionUpdateService.changeSeats( + data.workspaceId, + workspaceMembersCount, + ); - this.logger.log( - `Updating workspace ${data.workspaceId} subscription quantity to ${workspaceMembersCount} members`, - ); - } catch (e) { - this.logger.warn( - `Failed to update workspace ${data.workspaceId} subscription quantity to ${workspaceMembersCount} members. Error: ${e}`, - ); - } - }, - ); + this.logger.log( + `Updating workspace ${data.workspaceId} subscription quantity to ${workspaceMembersCount} members`, + ); + } catch (e) { + this.logger.warn( + `Failed to update workspace ${data.workspaceId} subscription quantity to ${workspaceMembersCount} members. Error: ${e}`, + ); + } + }, authContext); } } diff --git a/packages/twenty-server/src/engine/core-modules/calendar/timeline-calendar-event.service.spec.ts b/packages/twenty-server/src/engine/core-modules/calendar/timeline-calendar-event.service.spec.ts index f1aa453f543..e0a02404c10 100644 --- a/packages/twenty-server/src/engine/core-modules/calendar/timeline-calendar-event.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/calendar/timeline-calendar-event.service.spec.ts @@ -40,7 +40,7 @@ describe('TimelineCalendarEventService', () => { getRepository: jest.fn().mockResolvedValue(mockCalendarEventRepository), executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), }; const module: TestingModule = await Test.createTestingModule({ 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 7a6355f21f0..f2e9ea1d11a 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 @@ -35,7 +35,6 @@ export class TimelineCalendarEventService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const offset = (page - 1) * pageSize; @@ -166,6 +165,7 @@ export class TimelineCalendarEventService { timelineCalendarEvents, }; }, + authContext, ); } @@ -185,7 +185,6 @@ export class TimelineCalendarEventService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const personRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -222,6 +221,7 @@ export class TimelineCalendarEventService { return calendarEvents; }, + authContext, ); } @@ -241,7 +241,6 @@ export class TimelineCalendarEventService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const opportunityRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -276,6 +275,7 @@ export class TimelineCalendarEventService { return calendarEvents; }, + authContext, ); } } diff --git a/packages/twenty-server/src/engine/core-modules/imap-smtp-caldav-connection/services/imap-smtp-caldav-connection.service.ts b/packages/twenty-server/src/engine/core-modules/imap-smtp-caldav-connection/services/imap-smtp-caldav-connection.service.ts index 24d8c9196a8..448384a5273 100644 --- a/packages/twenty-server/src/engine/core-modules/imap-smtp-caldav-connection/services/imap-smtp-caldav-connection.service.ts +++ b/packages/twenty-server/src/engine/core-modules/imap-smtp-caldav-connection/services/imap-smtp-caldav-connection.service.ts @@ -187,7 +187,6 @@ export class ImapSmtpCaldavService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const connectedAccountRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -204,6 +203,7 @@ export class ImapSmtpCaldavService { return connectedAccount; }, + authContext, ); } } diff --git a/packages/twenty-server/src/engine/core-modules/messaging/services/get-messages.service.ts b/packages/twenty-server/src/engine/core-modules/messaging/services/get-messages.service.ts index 492a329f9b3..9967ae8e2c1 100644 --- a/packages/twenty-server/src/engine/core-modules/messaging/services/get-messages.service.ts +++ b/packages/twenty-server/src/engine/core-modules/messaging/services/get-messages.service.ts @@ -77,7 +77,6 @@ export class GetMessagesService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const personRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -113,6 +112,7 @@ export class GetMessagesService { return messageThreads; }, + authContext, ); } @@ -126,7 +126,6 @@ export class GetMessagesService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const opportunityRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -161,6 +160,7 @@ export class GetMessagesService { return messageThreads; }, + authContext, ); } } diff --git a/packages/twenty-server/src/engine/core-modules/messaging/services/timeline-messaging.service.ts b/packages/twenty-server/src/engine/core-modules/messaging/services/timeline-messaging.service.ts index 203868a9ea4..28555ec8e23 100644 --- a/packages/twenty-server/src/engine/core-modules/messaging/services/timeline-messaging.service.ts +++ b/packages/twenty-server/src/engine/core-modules/messaging/services/timeline-messaging.service.ts @@ -35,7 +35,6 @@ export class TimelineMessagingService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const messageThreadRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -99,6 +98,7 @@ export class TimelineMessagingService { totalNumberOfThreads, }; }, + authContext, ); } @@ -111,7 +111,6 @@ export class TimelineMessagingService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const messageParticipantRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -198,6 +197,7 @@ export class TimelineMessagingService { {}, ); }, + authContext, ); } @@ -211,7 +211,6 @@ export class TimelineMessagingService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const messageThreadRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -267,6 +266,7 @@ export class TimelineMessagingService { return threadVisibilityByThreadId; }, + authContext, ); } } diff --git a/packages/twenty-server/src/engine/core-modules/record-position/services/__tests__/record-position.service.spec.ts b/packages/twenty-server/src/engine/core-modules/record-position/services/__tests__/record-position.service.spec.ts index 996d0ed4de1..c883e7763ba 100644 --- a/packages/twenty-server/src/engine/core-modules/record-position/services/__tests__/record-position.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/record-position/services/__tests__/record-position.service.spec.ts @@ -20,7 +20,7 @@ describe('RecordPositionService', () => { getRepository: jest.fn().mockResolvedValue(mockRepository), executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), } as unknown as jest.Mocked; const module: TestingModule = await Test.createTestingModule({ diff --git a/packages/twenty-server/src/engine/core-modules/record-position/services/record-position.service.ts b/packages/twenty-server/src/engine/core-modules/record-position/services/record-position.service.ts index cc102cd3c4d..2abca782c78 100644 --- a/packages/twenty-server/src/engine/core-modules/record-position/services/record-position.service.ts +++ b/packages/twenty-server/src/engine/core-modules/record-position/services/record-position.service.ts @@ -163,7 +163,6 @@ export class RecordPositionService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const repository = await this.globalWorkspaceOrmManager.getRepository( workspaceId, @@ -179,6 +178,7 @@ export class RecordPositionService { return record ? { id: record.id, position: record.position } : null; }, + authContext, ); } @@ -190,22 +190,19 @@ export class RecordPositionService { ): Promise { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const repository = await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - objectMetadata.nameSingular, - { - shouldBypassPermissionChecks: true, - }, - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const repository = await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + objectMetadata.nameSingular, + { + shouldBypassPermissionChecks: true, + }, + ); - await repository.update(recordId, { - position: positionValue, - }); - }, - ); + await repository.update(recordId, { + position: positionValue, + }); + }, authContext); } private async findMinPosition( @@ -216,7 +213,6 @@ export class RecordPositionService { const result = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const repository = await this.globalWorkspaceOrmManager.getRepository( workspaceId, @@ -228,6 +224,7 @@ export class RecordPositionService { return await repository.minimum('position'); }, + authContext, ); return sanitizeNumber(result); @@ -241,7 +238,6 @@ export class RecordPositionService { const result = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const repository = await this.globalWorkspaceOrmManager.getRepository( workspaceId, @@ -253,6 +249,7 @@ export class RecordPositionService { return await repository.maximum('position'); }, + authContext, ); return sanitizeNumber(result); diff --git a/packages/twenty-server/src/engine/core-modules/search/services/search.service.ts b/packages/twenty-server/src/engine/core-modules/search/services/search.service.ts index 46cb552895b..21630733041 100644 --- a/packages/twenty-server/src/engine/core-modules/search/services/search.service.ts +++ b/packages/twenty-server/src/engine/core-modules/search/services/search.service.ts @@ -33,7 +33,6 @@ import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-me import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config'; -import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util'; type LastRanks = { tsRankCD: number; tsRank: number }; @@ -82,13 +81,10 @@ export class SearchService { OBJECT_METADATA_ITEMS_CHUNK_SIZE, ); - const authContext = buildSystemAuthContext(workspaceId); - for (const objectMetadataItemChunk of filteredObjectMetadataItemsChunks) { const recordsWithObjectMetadataItems = await Promise.all( objectMetadataItemChunk.map(async (flatObjectMetadata) => { return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const repository = await this.globalWorkspaceOrmManager.getRepository( diff --git a/packages/twenty-server/src/engine/core-modules/tool/tools/send-email-tool/send-email-tool.ts b/packages/twenty-server/src/engine/core-modules/tool/tools/send-email-tool/send-email-tool.ts index 638dfc842ee..b7505cdff1c 100644 --- a/packages/twenty-server/src/engine/core-modules/tool/tools/send-email-tool/send-email-tool.ts +++ b/packages/twenty-server/src/engine/core-modules/tool/tools/send-email-tool/send-email-tool.ts @@ -65,7 +65,6 @@ export class SendEmailTool implements Tool { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const connectedAccountRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -91,6 +90,7 @@ export class SendEmailTool implements Tool { return connectedAccount; }, + authContext, ); } @@ -100,7 +100,6 @@ export class SendEmailTool implements Tool { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const connectedAccountRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -118,6 +117,7 @@ export class SendEmailTool implements Tool { return allAccounts[0].id; }, + authContext, ); } diff --git a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.spec.ts b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.spec.ts index 90cb9602756..32dc54311aa 100644 --- a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.spec.ts @@ -106,7 +106,9 @@ describe('UserWorkspaceService', () => { useValue: { executeInWorkspaceContext: jest .fn() - .mockImplementation(async (_authContext, callback) => callback()), + .mockImplementation( + async (callback: () => any, _authContext?: any) => callback(), + ), getRepository: jest.fn(), }, }, 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 a9e5f7f352f..0bce22a49df 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 @@ -95,47 +95,44 @@ export class UserWorkspaceService extends TypeOrmQueryService { - const workspaceMemberRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'workspaceMember', - { shouldBypassPermissionChecks: true }, - ); - - const userWorkspace = await this.userWorkspaceRepository.findOneOrFail({ - where: { - userId: user.id, - workspaceId, - }, - }); - - await workspaceMemberRepository.insert({ - name: { - firstName: user.firstName, - lastName: user.lastName, - }, - colorScheme: 'System', - userId: user.id, - userEmail: user.email, - avatarUrl: userWorkspace.defaultAvatarUrl ?? '', - locale: (user.locale ?? SOURCE_LOCALE) as keyof typeof APP_LOCALES, - }); - - const workspaceMember = await workspaceMemberRepository.find({ - where: { - userId: user.id, - }, - }); - - assert( - workspaceMember?.length === 1, - `Error while creating workspace member ${user.email} on workspace ${workspaceId}`, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workspaceMemberRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'workspaceMember', + { shouldBypassPermissionChecks: true }, ); - }, - ); + + const userWorkspace = await this.userWorkspaceRepository.findOneOrFail({ + where: { + userId: user.id, + workspaceId, + }, + }); + + await workspaceMemberRepository.insert({ + name: { + firstName: user.firstName, + lastName: user.lastName, + }, + colorScheme: 'System', + userId: user.id, + userEmail: user.email, + avatarUrl: userWorkspace.defaultAvatarUrl ?? '', + locale: (user.locale ?? SOURCE_LOCALE) as keyof typeof APP_LOCALES, + }); + + const workspaceMember = await workspaceMemberRepository.find({ + where: { + userId: user.id, + }, + }); + + assert( + workspaceMember?.length === 1, + `Error while creating workspace member ${user.email} on workspace ${workspaceId}`, + ); + }, authContext); } async addUserToWorkspaceIfUserNotInWorkspace( @@ -356,7 +353,6 @@ export class UserWorkspaceService extends TypeOrmQueryService { const workspaceMemberRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -377,6 +373,7 @@ export class UserWorkspaceService extends TypeOrmQueryService { - const workspaceMemberRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspace.id, - 'workspaceMember', - { shouldBypassPermissionChecks: true }, - ); - - await workspaceMemberRepository.update( - { userId }, - { userEmail: email }, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workspaceMemberRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspace.id, + 'workspaceMember', + { shouldBypassPermissionChecks: true }, ); - }, - ); + + await workspaceMemberRepository.update({ userId }, { userEmail: email }); + }, authContext); } } diff --git a/packages/twenty-server/src/engine/core-modules/user/services/user.service.spec.ts b/packages/twenty-server/src/engine/core-modules/user/services/user.service.spec.ts index 8a9c835ebc3..edbe483234a 100644 --- a/packages/twenty-server/src/engine/core-modules/user/services/user.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/user/services/user.service.spec.ts @@ -76,7 +76,7 @@ describe('UserService', () => { executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), }, }, { diff --git a/packages/twenty-server/src/engine/core-modules/user/services/user.service.ts b/packages/twenty-server/src/engine/core-modules/user/services/user.service.ts index c469a0b904c..1dc9abd5b9f 100644 --- a/packages/twenty-server/src/engine/core-modules/user/services/user.service.ts +++ b/packages/twenty-server/src/engine/core-modules/user/services/user.service.ts @@ -66,7 +66,6 @@ export class UserService extends TypeOrmQueryService { const authContext = buildSystemAuthContext(workspace.id); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workspaceMemberRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -81,6 +80,7 @@ export class UserService extends TypeOrmQueryService { }, }); }, + authContext, ); } @@ -92,7 +92,6 @@ export class UserService extends TypeOrmQueryService { const authContext = buildSystemAuthContext(workspace.id); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workspaceMemberRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -105,6 +104,7 @@ export class UserService extends TypeOrmQueryService { withDeleted: withDeleted, }); }, + authContext, ); } @@ -116,7 +116,6 @@ export class UserService extends TypeOrmQueryService { const authContext = buildSystemAuthContext(workspace.id); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workspaceMemberRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -130,6 +129,7 @@ export class UserService extends TypeOrmQueryService { withDeleted: true, }); }, + authContext, ); } @@ -202,7 +202,6 @@ export class UserService extends TypeOrmQueryService { const workspaceMembers = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workspaceMemberRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -213,6 +212,7 @@ export class UserService extends TypeOrmQueryService { return workspaceMemberRepository.find(); }, + authContext, ); const userWorkspaceId = userWorkspace.id; @@ -255,21 +255,18 @@ export class UserService extends TypeOrmQueryService { assert(workspaceMember, 'WorkspaceMember not found'); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const workspaceMemberRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'workspaceMember', - { shouldBypassPermissionChecks: true }, - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workspaceMemberRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'workspaceMember', + { shouldBypassPermissionChecks: true }, + ); - await workspaceMemberRepository.delete({ - userId: userWorkspace.userId, - }); - }, - ); + await workspaceMemberRepository.delete({ + userId: userWorkspace.userId, + }); + }, authContext); await this.userWorkspaceService.deleteUserWorkspace({ userWorkspaceId, diff --git a/packages/twenty-server/src/engine/core-modules/user/services/workspace-flat-workspace-member-map-cache.service.ts b/packages/twenty-server/src/engine/core-modules/user/services/workspace-flat-workspace-member-map-cache.service.ts index 75060ca168b..fb4c2e4354f 100644 --- a/packages/twenty-server/src/engine/core-modules/user/services/workspace-flat-workspace-member-map-cache.service.ts +++ b/packages/twenty-server/src/engine/core-modules/user/services/workspace-flat-workspace-member-map-cache.service.ts @@ -20,7 +20,6 @@ export class WorkspaceFlatWorkspaceMemberMapCacheService extends WorkspaceCacheP async computeForCache(workspaceId: string): Promise { const flatWorkspaceMemberMaps = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - buildSystemAuthContext(workspaceId), async () => { const workspaceMemberRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -45,6 +44,7 @@ export class WorkspaceFlatWorkspaceMemberMapCacheService extends WorkspaceCacheP return flatWorkspaceMemberMaps; }, + buildSystemAuthContext(workspaceId), ); return flatWorkspaceMemberMaps; 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 d5a638563cd..bad9bacb0c1 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 @@ -391,7 +391,6 @@ export class UserResolver { const workspaceMemberToDelete = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workspaceMemberRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -406,6 +405,7 @@ export class UserResolver { }, }); }, + authContext, ); if (!isDefined(workspaceMemberToDelete)) { diff --git a/packages/twenty-server/src/engine/core-modules/workflow/controllers/workflow-trigger.controller.ts b/packages/twenty-server/src/engine/core-modules/workflow/controllers/workflow-trigger.controller.ts index e9fdd9922cc..829c728ce2e 100644 --- a/packages/twenty-server/src/engine/core-modules/workflow/controllers/workflow-trigger.controller.ts +++ b/packages/twenty-server/src/engine/core-modules/workflow/controllers/workflow-trigger.controller.ts @@ -93,7 +93,6 @@ export class WorkflowTriggerController { const { workflow } = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workflowRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -156,6 +155,7 @@ export class WorkflowTriggerController { return { workflow, workflowVersion }; }, + authContext, ); const { workflowRunId } = diff --git a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver.ts b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver.ts index 9913dd94d2d..ece7ac691f9 100644 --- a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver.ts @@ -77,7 +77,6 @@ export class WorkflowTriggerResolver { const workspaceMember = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workspaceMemberRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -92,6 +91,7 @@ export class WorkflowTriggerResolver { }, }); }, + authContext, ); return this.workflowTriggerWorkspaceService.runWorkflowVersion({ diff --git a/packages/twenty-server/src/engine/core-modules/workspace-invitation/workspace-invitation.resolver.ts b/packages/twenty-server/src/engine/core-modules/workspace-invitation/workspace-invitation.resolver.ts index 612c521b547..2c22934528f 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace-invitation/workspace-invitation.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace-invitation/workspace-invitation.resolver.ts @@ -60,7 +60,6 @@ export class WorkspaceInvitationResolver { const workspaceMember = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workspaceMemberRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -75,6 +74,7 @@ export class WorkspaceInvitationResolver { }, }); }, + authContext, ); return this.workspaceInvitationService.resendWorkspaceInvitation( @@ -100,7 +100,6 @@ export class WorkspaceInvitationResolver { const workspaceMember = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workspaceMemberRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -115,6 +114,7 @@ export class WorkspaceInvitationResolver { }, }); }, + authContext, ); return await this.workspaceInvitationService.sendInvitations( diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/services/agent-actor-context.service.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/services/agent-actor-context.service.ts index b0f4b436d3c..4c8e658ddf6 100644 --- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/services/agent-actor-context.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/services/agent-actor-context.service.ts @@ -46,7 +46,6 @@ export class AgentActorContextService { const workspaceMember = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workspaceMemberRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -61,6 +60,7 @@ export class AgentActorContextService { }, }); }, + authContext, ); if (!workspaceMember) { diff --git a/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/navigation-menu-item.service.ts b/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/navigation-menu-item.service.ts index d0f559b1a3c..b0a13d0c7e1 100644 --- a/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/navigation-menu-item.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/navigation-menu-item.service.ts @@ -427,7 +427,6 @@ export class NavigationMenuItemService { const record = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const repository = await this.globalWorkspaceOrmManager.getRepository( @@ -462,6 +461,7 @@ export class NavigationMenuItemService { return formattedRecord; }, + authContext, ); if (!isDefined(record)) { diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts index 2f97cd5c340..cbe1a35773c 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts @@ -526,23 +526,20 @@ export class ObjectMetadataService extends TypeOrmQueryService { - const favoriteRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'favorite', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const favoriteRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'favorite', + ); - const favoriteCount = await favoriteRepository.count(); + const favoriteCount = await favoriteRepository.count(); - await favoriteRepository.insert({ - viewId: view.id, - position: favoriteCount, - }); - }, - ); + await favoriteRepository.insert({ + viewId: view.id, + position: favoriteCount, + }); + }, authContext); } public async deleteWorkspaceAllObjectMetadata({ diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout/services/page-layout.service.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout/services/page-layout.service.ts index cb822dc46d8..904c48697ad 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout/services/page-layout.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout/services/page-layout.service.ts @@ -410,26 +410,23 @@ export class PageLayoutService { }): Promise { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const dashboardRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'dashboard', - { shouldBypassPermissionChecks: true }, - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const dashboardRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'dashboard', + { shouldBypassPermissionChecks: true }, + ); - const dashboards = await dashboardRepository.find({ - where: { - pageLayoutId, - }, - }); + const dashboards = await dashboardRepository.find({ + where: { + pageLayoutId, + }, + }); - for (const dashboard of dashboards) { - await dashboardRepository.delete(dashboard.id); - } - }, - ); + for (const dashboard of dashboards) { + await dashboardRepository.delete(dashboard.id); + } + }, authContext); } } diff --git a/packages/twenty-server/src/engine/metadata-modules/user-role/user-role.service.ts b/packages/twenty-server/src/engine/metadata-modules/user-role/user-role.service.ts index 4000b6553f1..6ed0f6758ee 100644 --- a/packages/twenty-server/src/engine/metadata-modules/user-role/user-role.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/user-role/user-role.service.ts @@ -154,7 +154,6 @@ export class UserRoleService { ); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workspaceMemberRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -171,6 +170,7 @@ export class UserRoleService { return workspaceMembers; }, + authContext, ); } diff --git a/packages/twenty-server/src/engine/trash-cleanup/services/__tests__/trash-cleanup.service.spec.ts b/packages/twenty-server/src/engine/trash-cleanup/services/__tests__/trash-cleanup.service.spec.ts index fc6520e1fba..11944d0a795 100644 --- a/packages/twenty-server/src/engine/trash-cleanup/services/__tests__/trash-cleanup.service.spec.ts +++ b/packages/twenty-server/src/engine/trash-cleanup/services/__tests__/trash-cleanup.service.spec.ts @@ -18,7 +18,7 @@ describe('TrashCleanupService', () => { getRepository: jest.fn(), executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), }; const module: TestingModule = await Test.createTestingModule({ diff --git a/packages/twenty-server/src/engine/trash-cleanup/services/trash-cleanup.service.ts b/packages/twenty-server/src/engine/trash-cleanup/services/trash-cleanup.service.ts index 22a76eaa40a..04f38bf5ce1 100644 --- a/packages/twenty-server/src/engine/trash-cleanup/services/trash-cleanup.service.ts +++ b/packages/twenty-server/src/engine/trash-cleanup/services/trash-cleanup.service.ts @@ -100,7 +100,6 @@ export class TrashCleanupService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const repository = await this.globalWorkspaceOrmManager.getRepository( workspaceId, @@ -137,6 +136,7 @@ export class TrashCleanupService { return deleted; }, + authContext, ); } diff --git a/packages/twenty-server/src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager.ts b/packages/twenty-server/src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager.ts index 2f6627697b2..3f9bee83892 100644 --- a/packages/twenty-server/src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager.ts +++ b/packages/twenty-server/src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager.ts @@ -4,6 +4,7 @@ import { type ObjectLiteral } from 'typeorm'; import { type WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface'; +import { getWorkspaceAuthContext } from 'src/engine/core-modules/auth/storage/workspace-auth-context.storage'; import { buildObjectIdByNameMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/build-object-id-by-name-maps.util'; import { GlobalWorkspaceDataSource } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource'; import { GlobalWorkspaceDataSourceService } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource.service'; @@ -67,10 +68,11 @@ export class GlobalWorkspaceOrmManager { } async executeInWorkspaceContext( - authContext: WorkspaceAuthContext, fn: () => T | Promise, + authContext?: WorkspaceAuthContext, ): Promise { - const context = await this.loadWorkspaceContext(authContext); + const resolvedAuthContext = authContext ?? getWorkspaceAuthContext(); + const context = await this.loadWorkspaceContext(resolvedAuthContext); return withWorkspaceContext(context, fn); } diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/services/twenty-standard-application.service.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/services/twenty-standard-application.service.ts index d8264fff1fc..e31f1259392 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/services/twenty-standard-application.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/services/twenty-standard-application.service.ts @@ -41,24 +41,21 @@ export class TwentyStandardApplicationService { }) { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const favoriteRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'favorite', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const favoriteRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'favorite', + ); - const favoriteCount = await favoriteRepository.count(); - const favoriteToCreate = flatViews.map((flatView, index) => ({ - viewId: flatView.id, - position: favoriteCount + index, - })); + const favoriteCount = await favoriteRepository.count(); + const favoriteToCreate = flatViews.map((flatView, index) => ({ + viewId: flatView.id, + position: favoriteCount + index, + })); - await favoriteRepository.insert(favoriteToCreate); - }, - ); + await favoriteRepository.insert(favoriteToCreate); + }, authContext); } private async createManyNavigationMenuItem({ diff --git a/packages/twenty-server/src/modules/blocklist/blocklist-validation-manager/services/blocklist-validation.service.ts b/packages/twenty-server/src/modules/blocklist/blocklist-validation-manager/services/blocklist-validation.service.ts index 33aee25cfdd..efab0c63703 100644 --- a/packages/twenty-server/src/modules/blocklist/blocklist-validation-manager/services/blocklist-validation.service.ts +++ b/packages/twenty-server/src/modules/blocklist/blocklist-validation-manager/services/blocklist-validation.service.ts @@ -88,7 +88,6 @@ export class BlocklistValidationService { const currentWorkspaceMember = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workspaceMemberRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -100,6 +99,7 @@ export class BlocklistValidationService { userId, }); }, + authContext, ); const currentBlocklist = @@ -145,7 +145,6 @@ export class BlocklistValidationService { const currentWorkspaceMember = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workspaceMemberRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -157,6 +156,7 @@ export class BlocklistValidationService { userId, }); }, + authContext, ); const currentBlocklist = diff --git a/packages/twenty-server/src/modules/blocklist/repositories/blocklist.repository.ts b/packages/twenty-server/src/modules/blocklist/repositories/blocklist.repository.ts index 4b9545122c0..8af5f143777 100644 --- a/packages/twenty-server/src/modules/blocklist/repositories/blocklist.repository.ts +++ b/packages/twenty-server/src/modules/blocklist/repositories/blocklist.repository.ts @@ -17,7 +17,6 @@ export class BlocklistRepository { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const blockListRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -32,6 +31,7 @@ export class BlocklistRepository { id, }); }, + authContext, ); } @@ -42,7 +42,6 @@ export class BlocklistRepository { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const blockListRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -56,6 +55,7 @@ export class BlocklistRepository { }, }); }, + authContext, ); } } diff --git a/packages/twenty-server/src/modules/calendar/blocklist-manager/jobs/blocklist-item-delete-calendar-events.job.ts b/packages/twenty-server/src/modules/calendar/blocklist-manager/jobs/blocklist-item-delete-calendar-events.job.ts index 718e922f562..e64e15ff7b4 100644 --- a/packages/twenty-server/src/modules/calendar/blocklist-manager/jobs/blocklist-item-delete-calendar-events.job.ts +++ b/packages/twenty-server/src/modules/calendar/blocklist-manager/jobs/blocklist-item-delete-calendar-events.job.ts @@ -35,126 +35,123 @@ export class BlocklistItemDeleteCalendarEventsJob { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const blocklistItemIds = data.events.map( - (eventPayload) => eventPayload.recordId, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const blocklistItemIds = data.events.map( + (eventPayload) => eventPayload.recordId, + ); + + const blocklistRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'blocklist', ); - const blocklistRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'blocklist', - ); + const blocklist = await blocklistRepository.find({ + where: { + id: Any(blocklistItemIds), + }, + }); - const blocklist = await blocklistRepository.find({ - where: { - id: Any(blocklistItemIds), + const handlesToDeleteByWorkspaceMemberIdMap = blocklist.reduce( + (acc, blocklistItem) => { + const { handle, workspaceMemberId } = blocklistItem; + + if (!acc.has(workspaceMemberId)) { + acc.set(workspaceMemberId, []); + } + + if (!isDefined(handle)) { + return acc; + } + + acc.get(workspaceMemberId)?.push(handle); + + return acc; + }, + new Map(), + ); + + const calendarChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarChannel', + ); + + const calendarChannelEventAssociationRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarChannelEventAssociation', + ); + + for (const workspaceMemberId of handlesToDeleteByWorkspaceMemberIdMap.keys()) { + const handles = + handlesToDeleteByWorkspaceMemberIdMap.get(workspaceMemberId); + + if (!handles) { + continue; + } + + const calendarChannels = await calendarChannelRepository.find({ + select: { + id: true, + handle: true, + connectedAccount: { + handleAliases: true, + }, }, + where: { + connectedAccount: { + accountOwnerId: workspaceMemberId, + }, + }, + relations: ['connectedAccount'], }); - const handlesToDeleteByWorkspaceMemberIdMap = blocklist.reduce( - (acc, blocklistItem) => { - const { handle, workspaceMemberId } = blocklistItem; + for (const calendarChannel of calendarChannels) { + const calendarChannelHandles = [calendarChannel.handle]; - if (!acc.has(workspaceMemberId)) { - acc.set(workspaceMemberId, []); - } + if (calendarChannel.connectedAccount.handleAliases) { + calendarChannelHandles.push( + ...calendarChannel.connectedAccount.handleAliases.split(','), + ); + } - if (!isDefined(handle)) { - return acc; - } + const handleConditions = handles.map((handle) => { + const isHandleDomain = handle.startsWith('@'); - acc.get(workspaceMemberId)?.push(handle); + return isHandleDomain + ? { + handle: And( + Or(ILike(`%${handle}`), ILike(`%.${handle.slice(1)}`)), + Not(In(calendarChannelHandles)), + ), + } + : { handle }; + }); - return acc; - }, - new Map(), - ); + const calendarEventsAssociationsToDelete = + await calendarChannelEventAssociationRepository.find({ + where: { + calendarChannelId: calendarChannel.id, + calendarEvent: { + calendarEventParticipants: handleConditions, + }, + }, + }); - const calendarChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarChannel', - ); - - const calendarChannelEventAssociationRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarChannelEventAssociation', - ); - - for (const workspaceMemberId of handlesToDeleteByWorkspaceMemberIdMap.keys()) { - const handles = - handlesToDeleteByWorkspaceMemberIdMap.get(workspaceMemberId); - - if (!handles) { + if (calendarEventsAssociationsToDelete.length === 0) { continue; } - const calendarChannels = await calendarChannelRepository.find({ - select: { - id: true, - handle: true, - connectedAccount: { - handleAliases: true, - }, - }, - where: { - connectedAccount: { - accountOwnerId: workspaceMemberId, - }, - }, - relations: ['connectedAccount'], - }); - - for (const calendarChannel of calendarChannels) { - const calendarChannelHandles = [calendarChannel.handle]; - - if (calendarChannel.connectedAccount.handleAliases) { - calendarChannelHandles.push( - ...calendarChannel.connectedAccount.handleAliases.split(','), - ); - } - - const handleConditions = handles.map((handle) => { - const isHandleDomain = handle.startsWith('@'); - - return isHandleDomain - ? { - handle: And( - Or(ILike(`%${handle}`), ILike(`%.${handle.slice(1)}`)), - Not(In(calendarChannelHandles)), - ), - } - : { handle }; - }); - - const calendarEventsAssociationsToDelete = - await calendarChannelEventAssociationRepository.find({ - where: { - calendarChannelId: calendarChannel.id, - calendarEvent: { - calendarEventParticipants: handleConditions, - }, - }, - }); - - if (calendarEventsAssociationsToDelete.length === 0) { - continue; - } - - await calendarChannelEventAssociationRepository.delete( - calendarEventsAssociationsToDelete.map(({ id }) => id), - ); - } + await calendarChannelEventAssociationRepository.delete( + calendarEventsAssociationsToDelete.map(({ id }) => id), + ); } + } - await this.calendarEventCleanerService.cleanWorkspaceCalendarEvents( - workspaceId, - ); - }, - ); + await this.calendarEventCleanerService.cleanWorkspaceCalendarEvents( + workspaceId, + ); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/calendar/blocklist-manager/jobs/blocklist-reimport-calendar-events.job.ts b/packages/twenty-server/src/modules/calendar/blocklist-manager/jobs/blocklist-reimport-calendar-events.job.ts index 6cc56b34798..9c6a77dc1c9 100644 --- a/packages/twenty-server/src/modules/calendar/blocklist-manager/jobs/blocklist-reimport-calendar-events.job.ts +++ b/packages/twenty-server/src/modules/calendar/blocklist-manager/jobs/blocklist-reimport-calendar-events.job.ts @@ -36,37 +36,34 @@ export class BlocklistReimportCalendarEventsJob { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const calendarChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const calendarChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarChannel', + ); - for (const eventPayload of data.events) { - const workspaceMemberId = - eventPayload.properties.before.workspaceMemberId; + for (const eventPayload of data.events) { + const workspaceMemberId = + eventPayload.properties.before.workspaceMemberId; - const calendarChannels = await calendarChannelRepository.find({ - select: ['id'], - where: { - connectedAccount: { - accountOwnerId: workspaceMemberId, - }, - syncStage: Not( - CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_PENDING, - ), + const calendarChannels = await calendarChannelRepository.find({ + select: ['id'], + where: { + connectedAccount: { + accountOwnerId: workspaceMemberId, }, - }); + syncStage: Not( + CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_PENDING, + ), + }, + }); - await this.calendarChannelSyncStatusService.resetAndMarkAsCalendarEventListFetchPending( - calendarChannels.map((calendarChannel) => calendarChannel.id), - workspaceId, - ); - } - }, - ); + await this.calendarChannelSyncStatusService.resetAndMarkAsCalendarEventListFetchPending( + calendarChannels.map((calendarChannel) => calendarChannel.id), + workspaceId, + ); + } + }, authContext); } } diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-cleaner/services/calendar-event-cleaner.service.ts b/packages/twenty-server/src/modules/calendar/calendar-event-cleaner/services/calendar-event-cleaner.service.ts index e5373fdfe5e..ac689389e01 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-cleaner/services/calendar-event-cleaner.service.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-cleaner/services/calendar-event-cleaner.service.ts @@ -15,37 +15,34 @@ export class CalendarEventCleanerService { public async cleanWorkspaceCalendarEvents(workspaceId: string) { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const calendarEventRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarEvent', - ); - - await deleteUsingPagination( + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const calendarEventRepository = + await this.globalWorkspaceOrmManager.getRepository( workspaceId, - 500, - async (limit, offset) => { - const nonAssociatedCalendarEvents = - await calendarEventRepository.find({ - where: { - calendarChannelEventAssociations: { - id: IsNull(), - }, - }, - take: limit, - skip: offset, - }); - - return nonAssociatedCalendarEvents.map(({ id }) => id); - }, - async (ids) => { - await calendarEventRepository.delete({ id: Any(ids) }); - }, + 'calendarEvent', ); - }, - ); + + await deleteUsingPagination( + workspaceId, + 500, + async (limit, offset) => { + const nonAssociatedCalendarEvents = + await calendarEventRepository.find({ + where: { + calendarChannelEventAssociations: { + id: IsNull(), + }, + }, + take: limit, + skip: offset, + }); + + return nonAssociatedCalendarEvents.map(({ id }) => id); + }, + async (ids) => { + await calendarEventRepository.delete({ id: Any(ids) }); + }, + ); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/commands/calendar-trigger-event-list-fetch.command.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/commands/calendar-trigger-event-list-fetch.command.ts index 2d65c265387..98ebe02d1f9 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/commands/calendar-trigger-event-list-fetch.command.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/commands/calendar-trigger-event-list-fetch.command.ts @@ -51,64 +51,61 @@ export class CalendarTriggerEventListFetchCommand extends CommandRunner { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const calendarChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const calendarChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarChannel', + ); + + const whereCondition: Record = { + isSyncEnabled: true, + syncStage: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_PENDING, + }; + + if (calendarChannelId) { + whereCondition.id = calendarChannelId; + } + + const calendarChannels = + await calendarChannelRepository.find(whereCondition); + + if (calendarChannels.length === 0) { + this.logger.warn( + 'No calendar channels found with CALENDAR_EVENT_LIST_FETCH_PENDING status', + ); + + return; + } + + this.logger.log( + `Found ${calendarChannels.length} calendar channel(s) to process`, + ); + + for (const calendarChannel of calendarChannels) { + await calendarChannelRepository.update(calendarChannel.id, { + syncStage: + CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_SCHEDULED, + syncStageStartedAt: new Date().toISOString(), + }); + + await this.messageQueueService.add( + CalendarEventListFetchJob.name, + { + calendarChannelId: calendarChannel.id, workspaceId, - 'calendarChannel', - ); - - const whereCondition: Record = { - isSyncEnabled: true, - syncStage: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_PENDING, - }; - - if (calendarChannelId) { - whereCondition.id = calendarChannelId; - } - - const calendarChannels = - await calendarChannelRepository.find(whereCondition); - - if (calendarChannels.length === 0) { - this.logger.warn( - 'No calendar channels found with CALENDAR_EVENT_LIST_FETCH_PENDING status', - ); - - return; - } - - this.logger.log( - `Found ${calendarChannels.length} calendar channel(s) to process`, + }, ); - for (const calendarChannel of calendarChannels) { - await calendarChannelRepository.update(calendarChannel.id, { - syncStage: - CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_SCHEDULED, - syncStageStartedAt: new Date().toISOString(), - }); - - await this.messageQueueService.add( - CalendarEventListFetchJob.name, - { - calendarChannelId: calendarChannel.id, - workspaceId, - }, - ); - - this.logger.log( - `Triggered fetch for calendar channel ${calendarChannel.id}`, - ); - } - this.logger.log( - `Successfully triggered ${calendarChannels.length} calendar event list fetch job(s)`, + `Triggered fetch for calendar channel ${calendarChannel.id}`, ); - }, - ); + } + + this.logger.log( + `Successfully triggered ${calendarChannels.length} calendar event list fetch job(s)`, + ); + }, authContext); } @Option({ diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-event-list-fetch.job.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-event-list-fetch.job.ts index 82dd387a8b8..e66e46d9d9f 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-event-list-fetch.job.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-event-list-fetch.job.ts @@ -35,55 +35,52 @@ export class CalendarEventListFetchJob { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const calendarChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarChannel', - ); - - const calendarChannel = await calendarChannelRepository.findOne({ - where: { - id: calendarChannelId, - isSyncEnabled: true, - }, - relations: ['connectedAccount'], - }); - - if (!calendarChannel) { - return; - } - - if ( - calendarChannel.syncStage !== - CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_SCHEDULED - ) { - return; - } - - if ( - isThrottled( - calendarChannel.syncStageStartedAt, - calendarChannel.throttleFailureCount, - ) - ) { - await this.calendarChannelSyncStatusService.markAsCalendarEventListFetchPending( - [calendarChannel.id], - workspaceId, - true, - ); - - return; - } - - await this.calendarFetchEventsService.fetchCalendarEvents( - calendarChannel, - calendarChannel.connectedAccount, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const calendarChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( workspaceId, + 'calendarChannel', ); - }, - ); + + const calendarChannel = await calendarChannelRepository.findOne({ + where: { + id: calendarChannelId, + isSyncEnabled: true, + }, + relations: ['connectedAccount'], + }); + + if (!calendarChannel) { + return; + } + + if ( + calendarChannel.syncStage !== + CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_SCHEDULED + ) { + return; + } + + if ( + isThrottled( + calendarChannel.syncStageStartedAt, + calendarChannel.throttleFailureCount, + ) + ) { + await this.calendarChannelSyncStatusService.markAsCalendarEventListFetchPending( + [calendarChannel.id], + workspaceId, + true, + ); + + return; + } + + await this.calendarFetchEventsService.fetchCalendarEvents( + calendarChannel, + calendarChannel.connectedAccount, + workspaceId, + ); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-events-import.job.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-events-import.job.ts index c59ebfeb7fa..c9aaa509e3c 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-events-import.job.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-events-import.job.ts @@ -35,54 +35,51 @@ export class CalendarEventsImportJob { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const calendarChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarChannel', - ); - const calendarChannel = await calendarChannelRepository.findOne({ - where: { - id: calendarChannelId, - isSyncEnabled: true, - }, - relations: ['connectedAccount'], - }); - - if (!calendarChannel?.isSyncEnabled) { - return; - } - - if ( - calendarChannel.syncStage !== - CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_SCHEDULED - ) { - return; - } - - if ( - isThrottled( - calendarChannel.syncStageStartedAt, - calendarChannel.throttleFailureCount, - ) - ) { - await this.calendarChannelSyncStatusService.markAsCalendarEventsImportPending( - [calendarChannel.id], - workspaceId, - true, - ); - - return; - } - - await this.calendarEventsImportService.processCalendarEventsImport( - calendarChannel, - calendarChannel.connectedAccount, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const calendarChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( workspaceId, + 'calendarChannel', ); - }, - ); + const calendarChannel = await calendarChannelRepository.findOne({ + where: { + id: calendarChannelId, + isSyncEnabled: true, + }, + relations: ['connectedAccount'], + }); + + if (!calendarChannel?.isSyncEnabled) { + return; + } + + if ( + calendarChannel.syncStage !== + CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_SCHEDULED + ) { + return; + } + + if ( + isThrottled( + calendarChannel.syncStageStartedAt, + calendarChannel.throttleFailureCount, + ) + ) { + await this.calendarChannelSyncStatusService.markAsCalendarEventsImportPending( + [calendarChannel.id], + workspaceId, + true, + ); + + return; + } + + await this.calendarEventsImportService.processCalendarEventsImport( + calendarChannel, + calendarChannel.connectedAccount, + workspaceId, + ); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-ongoing-stale.job.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-ongoing-stale.job.ts index 088ea2ae63c..cae524cbd2f 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-ongoing-stale.job.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-ongoing-stale.job.ts @@ -35,63 +35,60 @@ export class CalendarOngoingStaleJob { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const calendarChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const calendarChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarChannel', + ); + + const calendarChannels = await calendarChannelRepository.find({ + where: { + syncStage: In([ + CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_ONGOING, + CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_ONGOING, + CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_SCHEDULED, + CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_SCHEDULED, + ]), + }, + }); + + for (const calendarChannel of calendarChannels) { + if ( + calendarChannel.syncStageStartedAt && + isSyncStale(calendarChannel.syncStageStartedAt) + ) { + await this.calendarChannelSyncStatusService.resetSyncStageStartedAt( + [calendarChannel.id], workspaceId, - 'calendarChannel', ); - const calendarChannels = await calendarChannelRepository.find({ - where: { - syncStage: In([ - CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_ONGOING, - CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_ONGOING, - CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_SCHEDULED, - CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_SCHEDULED, - ]), - }, - }); - - for (const calendarChannel of calendarChannels) { - if ( - calendarChannel.syncStageStartedAt && - isSyncStale(calendarChannel.syncStageStartedAt) - ) { - await this.calendarChannelSyncStatusService.resetSyncStageStartedAt( - [calendarChannel.id], - workspaceId, - ); - - switch (calendarChannel.syncStage) { - case CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_ONGOING: - case CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_SCHEDULED: - this.logger.log( - `Sync for calendar channel ${calendarChannel.id} and workspace ${workspaceId} is stale. Setting sync stage to CALENDAR_EVENT_LIST_FETCH_PENDING`, - ); - await this.calendarChannelSyncStatusService.markAsCalendarEventListFetchPending( - [calendarChannel.id], - workspaceId, - ); - break; - case CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_ONGOING: - case CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_SCHEDULED: - this.logger.log( - `Sync for calendar channel ${calendarChannel.id} and workspace ${workspaceId} is stale. Setting sync stage to CALENDAR_EVENTS_IMPORT_PENDING`, - ); - await this.calendarChannelSyncStatusService.markAsCalendarEventsImportPending( - [calendarChannel.id], - workspaceId, - ); - break; - default: - break; - } + switch (calendarChannel.syncStage) { + case CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_ONGOING: + case CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_SCHEDULED: + this.logger.log( + `Sync for calendar channel ${calendarChannel.id} and workspace ${workspaceId} is stale. Setting sync stage to CALENDAR_EVENT_LIST_FETCH_PENDING`, + ); + await this.calendarChannelSyncStatusService.markAsCalendarEventListFetchPending( + [calendarChannel.id], + workspaceId, + ); + break; + case CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_ONGOING: + case CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_SCHEDULED: + this.logger.log( + `Sync for calendar channel ${calendarChannel.id} and workspace ${workspaceId} is stale. Setting sync stage to CALENDAR_EVENTS_IMPORT_PENDING`, + ); + await this.calendarChannelSyncStatusService.markAsCalendarEventsImportPending( + [calendarChannel.id], + workspaceId, + ); + break; + default: + break; } } - }, - ); + } + }, authContext); } } diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-relaunch-failed-calendar-channel.job.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-relaunch-failed-calendar-channel.job.ts index 08dfa62ff4d..72a5e697448 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-relaunch-failed-calendar-channel.job.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/jobs/calendar-relaunch-failed-calendar-channel.job.ts @@ -31,41 +31,37 @@ export class CalendarRelaunchFailedCalendarChannelJob { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const calendarChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarChannel', - { shouldBypassPermissionChecks: true }, - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const calendarChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarChannel', + { shouldBypassPermissionChecks: true }, + ); - const calendarChannel = await calendarChannelRepository.findOne({ - where: { - id: calendarChannelId, + const calendarChannel = await calendarChannelRepository.findOne({ + where: { + id: calendarChannelId, + }, + relations: { + connectedAccount: { + accountOwner: true, }, - relations: { - connectedAccount: { - accountOwner: true, - }, - }, - }); + }, + }); - if ( - !calendarChannel || - calendarChannel.syncStage !== CalendarChannelSyncStage.FAILED || - calendarChannel.syncStatus !== - CalendarChannelSyncStatus.FAILED_UNKNOWN - ) { - return; - } + if ( + !calendarChannel || + calendarChannel.syncStage !== CalendarChannelSyncStage.FAILED || + calendarChannel.syncStatus !== CalendarChannelSyncStatus.FAILED_UNKNOWN + ) { + return; + } - await calendarChannelRepository.update(calendarChannelId, { - syncStage: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_PENDING, - syncStatus: CalendarChannelSyncStatus.ACTIVE, - }); - }, - ); + await calendarChannelRepository.update(calendarChannelId, { + syncStage: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_PENDING, + syncStatus: CalendarChannelSyncStatus.ACTIVE, + }); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-event-import-exception-handler.service.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-event-import-exception-handler.service.ts index e7fc0a2f81d..e170a8067a8 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-event-import-exception-handler.service.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-event-import-exception-handler.service.ts @@ -133,26 +133,23 @@ export class CalendarEventImportErrorHandlerService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const calendarChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarChannel', - ); - - await calendarChannelRepository.increment( - { - id: calendarChannel.id, - }, - 'throttleFailureCount', - 1, - undefined, - ['throttleFailureCount', 'id'], + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const calendarChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarChannel', ); - }, - ); + + await calendarChannelRepository.increment( + { + id: calendarChannel.id, + }, + 'throttleFailureCount', + 1, + undefined, + ['throttleFailureCount', 'id'], + ); + }, authContext); switch (syncStep) { case CalendarEventImportSyncStep.CALENDAR_EVENT_LIST_FETCH: diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-events-import.service.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-events-import.service.ts index 6c3eaabe134..cc4109ac30c 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-events-import.service.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-events-import.service.ts @@ -58,121 +58,117 @@ export class CalendarEventsImportService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - let calendarEvents: FetchedCalendarEvent[] = []; + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + let calendarEvents: FetchedCalendarEvent[] = []; - try { - if (fetchedCalendarEvents) { - calendarEvents = fetchedCalendarEvents; - } else { - const eventIdsToFetch: string[] = await this.cacheStorage.setPop( - `calendar-events-to-import:${workspaceId}:${calendarChannel.id}`, - CALENDAR_EVENT_IMPORT_BATCH_SIZE, - ); + try { + if (fetchedCalendarEvents) { + calendarEvents = fetchedCalendarEvents; + } else { + const eventIdsToFetch: string[] = await this.cacheStorage.setPop( + `calendar-events-to-import:${workspaceId}:${calendarChannel.id}`, + CALENDAR_EVENT_IMPORT_BATCH_SIZE, + ); - if (!eventIdsToFetch || eventIdsToFetch.length === 0) { - await this.calendarChannelSyncStatusService.markAsCompletedAndMarkAsCalendarEventListFetchPending( - [calendarChannel.id], - workspaceId, - ); - - return; - } - - switch (connectedAccount.provider) { - case 'microsoft': - calendarEvents = - await this.microsoftCalendarImportEventService.getCalendarEvents( - connectedAccount, - eventIdsToFetch, - ); - break; - default: - break; - } - } - - if (!calendarEvents || calendarEvents?.length === 0) { - await this.calendarChannelSyncStatusService.markAsCalendarEventListFetchPending( + if (!eventIdsToFetch || eventIdsToFetch.length === 0) { + await this.calendarChannelSyncStatusService.markAsCompletedAndMarkAsCalendarEventListFetchPending( [calendarChannel.id], workspaceId, ); + + return; } - const blocklist = - await this.blocklistRepository.getByWorkspaceMemberId( - connectedAccount.accountOwnerId, - workspaceId, - ); - - if ( - !isDefined(connectedAccount.handleAliases) || - !isDefined(calendarChannel.handle) - ) { - throw new CalendarEventImportDriverException( - 'Calendar channel handle or Handle aliases are required', - CalendarEventImportDriverExceptionCode.CHANNEL_MISCONFIGURED, - ); + switch (connectedAccount.provider) { + case 'microsoft': + calendarEvents = + await this.microsoftCalendarImportEventService.getCalendarEvents( + connectedAccount, + eventIdsToFetch, + ); + break; + default: + break; } + } - const { filteredEvents, cancelledEvents } = - filterEventsAndReturnCancelledEvents( - [ - calendarChannel.handle, - ...connectedAccount.handleAliases.split(','), - ], - calendarEvents, - blocklist.map((blocklist) => blocklist.handle ?? ''), - ); - - const cancelledEventExternalIds = cancelledEvents.map( - (event) => event.id, - ); - - const BATCH_SIZE = 1000; - - for (let i = 0; i < filteredEvents.length; i = i + BATCH_SIZE) { - const eventsBatch = filteredEvents.slice(i, i + BATCH_SIZE); - - await this.calendarSaveEventsService.saveCalendarEventsAndEnqueueContactCreationJob( - eventsBatch, - calendarChannel, - connectedAccount, - workspaceId, - ); - } - const calendarChannelEventAssociationRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarChannelEventAssociation', - ); - - await calendarChannelEventAssociationRepository.delete({ - eventExternalId: Any(cancelledEventExternalIds), - calendarChannel: { - id: calendarChannel.id, - }, - }); - - await this.calendarEventCleanerService.cleanWorkspaceCalendarEvents( - workspaceId, - ); - - await this.calendarChannelSyncStatusService.markAsCompletedAndMarkAsCalendarEventListFetchPending( + if (!calendarEvents || calendarEvents?.length === 0) { + await this.calendarChannelSyncStatusService.markAsCalendarEventListFetchPending( [calendarChannel.id], workspaceId, ); - } catch (error) { - await this.calendarEventImportErrorHandlerService.handleDriverException( - error, - CalendarEventImportSyncStep.CALENDAR_EVENTS_IMPORT, + } + + const blocklist = await this.blocklistRepository.getByWorkspaceMemberId( + connectedAccount.accountOwnerId, + workspaceId, + ); + + if ( + !isDefined(connectedAccount.handleAliases) || + !isDefined(calendarChannel.handle) + ) { + throw new CalendarEventImportDriverException( + 'Calendar channel handle or Handle aliases are required', + CalendarEventImportDriverExceptionCode.CHANNEL_MISCONFIGURED, + ); + } + + const { filteredEvents, cancelledEvents } = + filterEventsAndReturnCancelledEvents( + [ + calendarChannel.handle, + ...connectedAccount.handleAliases.split(','), + ], + calendarEvents, + blocklist.map((blocklist) => blocklist.handle ?? ''), + ); + + const cancelledEventExternalIds = cancelledEvents.map( + (event) => event.id, + ); + + const BATCH_SIZE = 1000; + + for (let i = 0; i < filteredEvents.length; i = i + BATCH_SIZE) { + const eventsBatch = filteredEvents.slice(i, i + BATCH_SIZE); + + await this.calendarSaveEventsService.saveCalendarEventsAndEnqueueContactCreationJob( + eventsBatch, calendarChannel, + connectedAccount, workspaceId, ); } - }, - ); + const calendarChannelEventAssociationRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarChannelEventAssociation', + ); + + await calendarChannelEventAssociationRepository.delete({ + eventExternalId: Any(cancelledEventExternalIds), + calendarChannel: { + id: calendarChannel.id, + }, + }); + + await this.calendarEventCleanerService.cleanWorkspaceCalendarEvents( + workspaceId, + ); + + await this.calendarChannelSyncStatusService.markAsCompletedAndMarkAsCalendarEventListFetchPending( + [calendarChannel.id], + workspaceId, + ); + } catch (error) { + await this.calendarEventImportErrorHandlerService.handleDriverException( + error, + CalendarEventImportSyncStep.CALENDAR_EVENTS_IMPORT, + calendarChannel, + workspaceId, + ); + } + }, authContext); } } diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-fetch-events.service.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-fetch-events.service.ts index 9bf68fcb940..ab67cc4ec0d 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-fetch-events.service.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-fetch-events.service.ts @@ -48,68 +48,51 @@ export class CalendarFetchEventsService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - try { - const { accessToken, refreshToken } = - await this.calendarAccountAuthenticationService.validateAndRefreshConnectedAccountAuthentication( - { - connectedAccount, - workspaceId, - calendarChannelId: calendarChannel.id, - }, - ); - - const connectedAccountWithFreshTokens = { - ...connectedAccount, - accessToken, - refreshToken, - }; - - if (!isDefined(calendarChannel.syncCursor)) { - throw new CalendarEventImportDriverException( - 'Sync cursor is required', - CalendarEventImportDriverExceptionCode.SYNC_CURSOR_ERROR, - ); - } - - const getCalendarEventsResponse = - await this.getCalendarEventsService.getCalendarEvents( - connectedAccountWithFreshTokens, - calendarChannel.syncCursor, - ); - - const hasFullEvents = getCalendarEventsResponse.fullEvents; - - const calendarEvents = hasFullEvents - ? getCalendarEventsResponse.calendarEvents - : null; - const calendarEventIds = getCalendarEventsResponse.calendarEventIds; - const nextSyncCursor = getCalendarEventsResponse.nextSyncCursor; - - const calendarChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + try { + const { accessToken, refreshToken } = + await this.calendarAccountAuthenticationService.validateAndRefreshConnectedAccountAuthentication( + { + connectedAccount, workspaceId, - 'calendarChannel', - ); + calendarChannelId: calendarChannel.id, + }, + ); - if (!calendarEvents || calendarEvents?.length === 0) { - await calendarChannelRepository.update( - { - id: calendarChannel.id, - }, - { - syncCursor: nextSyncCursor, - }, - ); + const connectedAccountWithFreshTokens = { + ...connectedAccount, + accessToken, + refreshToken, + }; - await this.calendarChannelSyncStatusService.markAsCalendarEventListFetchPending( - [calendarChannel.id], - workspaceId, - ); - } + if (!isDefined(calendarChannel.syncCursor)) { + throw new CalendarEventImportDriverException( + 'Sync cursor is required', + CalendarEventImportDriverExceptionCode.SYNC_CURSOR_ERROR, + ); + } + const getCalendarEventsResponse = + await this.getCalendarEventsService.getCalendarEvents( + connectedAccountWithFreshTokens, + calendarChannel.syncCursor, + ); + + const hasFullEvents = getCalendarEventsResponse.fullEvents; + + const calendarEvents = hasFullEvents + ? getCalendarEventsResponse.calendarEvents + : null; + const calendarEventIds = getCalendarEventsResponse.calendarEventIds; + const nextSyncCursor = getCalendarEventsResponse.nextSyncCursor; + + const calendarChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarChannel', + ); + + if (!calendarEvents || calendarEvents?.length === 0) { await calendarChannelRepository.update( { id: calendarChannel.id, @@ -119,42 +102,56 @@ export class CalendarFetchEventsService { }, ); - if (hasFullEvents && calendarEvents) { - await this.calendarEventsImportService.processCalendarEventsImport( - calendarChannel, - connectedAccount, - workspaceId, - calendarEvents, - ); - } else if (!hasFullEvents && calendarEventIds) { - await this.cacheStorage.setAdd( - `calendar-events-to-import:${workspaceId}:${calendarChannel.id}`, - calendarEventIds, - ); - - await this.calendarChannelSyncStatusService.markAsCalendarEventsImportPending( - [calendarChannel.id], - workspaceId, - ); - } else { - throw new CalendarEventImportDriverException( - "Expected 'calendarEvents' or 'calendarEventIds' to be present", - CalendarEventImportDriverExceptionCode.UNKNOWN, - ); - } - } catch (error) { - this.logger.log( - `Calendar event fetch error for workspace ${workspaceId} and calendar channel ${calendarChannel.id}`, - ); - this.logger.error(error); - await this.calendarEventImportErrorHandlerService.handleDriverException( - error, - CalendarEventImportSyncStep.CALENDAR_EVENT_LIST_FETCH, - calendarChannel, + await this.calendarChannelSyncStatusService.markAsCalendarEventListFetchPending( + [calendarChannel.id], workspaceId, ); } - }, - ); + + await calendarChannelRepository.update( + { + id: calendarChannel.id, + }, + { + syncCursor: nextSyncCursor, + }, + ); + + if (hasFullEvents && calendarEvents) { + await this.calendarEventsImportService.processCalendarEventsImport( + calendarChannel, + connectedAccount, + workspaceId, + calendarEvents, + ); + } else if (!hasFullEvents && calendarEventIds) { + await this.cacheStorage.setAdd( + `calendar-events-to-import:${workspaceId}:${calendarChannel.id}`, + calendarEventIds, + ); + + await this.calendarChannelSyncStatusService.markAsCalendarEventsImportPending( + [calendarChannel.id], + workspaceId, + ); + } else { + throw new CalendarEventImportDriverException( + "Expected 'calendarEvents' or 'calendarEventIds' to be present", + CalendarEventImportDriverExceptionCode.UNKNOWN, + ); + } + } catch (error) { + this.logger.log( + `Calendar event fetch error for workspace ${workspaceId} and calendar channel ${calendarChannel.id}`, + ); + this.logger.error(error); + await this.calendarEventImportErrorHandlerService.handleDriverException( + error, + CalendarEventImportSyncStep.CALENDAR_EVENT_LIST_FETCH, + calendarChannel, + workspaceId, + ); + } + }, authContext); } } diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-save-events.service.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-save-events.service.ts index c870978ed37..67704eb16b9 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-save-events.service.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-save-events.service.ts @@ -34,248 +34,239 @@ export class CalendarSaveEventsService { ): Promise { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const calendarEventRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarEvent', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const calendarEventRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarEvent', + ); - const calendarChannelEventAssociationRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarChannelEventAssociation', - ); + const calendarChannelEventAssociationRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarChannelEventAssociation', + ); - const workspaceDataSource = - await this.globalWorkspaceOrmManager.getGlobalWorkspaceDataSource(); + const workspaceDataSource = + await this.globalWorkspaceOrmManager.getGlobalWorkspaceDataSource(); - await workspaceDataSource.transaction( - async (transactionManager: WorkspaceEntityManager) => { - const existingCalendarEvents = await calendarEventRepository.find( - { - where: { - iCalUid: Any( - fetchedCalendarEvents.map( - (event) => event.iCalUid as string, - ), - ), - }, + await workspaceDataSource.transaction( + async (transactionManager: WorkspaceEntityManager) => { + const existingCalendarEvents = await calendarEventRepository.find( + { + where: { + iCalUid: Any( + fetchedCalendarEvents.map((event) => event.iCalUid as string), + ), }, - transactionManager, - ); + }, + transactionManager, + ); - const fetchedCalendarEventsWithDBEvents: FetchedCalendarEventWithDBEvent[] = - fetchedCalendarEvents.map( - (event): FetchedCalendarEventWithDBEvent => { - const existingEventWithSameiCalUid = - existingCalendarEvents.find( - (existingEvent) => - existingEvent.iCalUid === event.iCalUid, - ); - - return { - fetchedCalendarEvent: event, - existingCalendarEvent: existingEventWithSameiCalUid ?? null, - newlyCreatedCalendarEvent: null, - }; - }, - ); - - const newCalendarEventsToInsert = fetchedCalendarEventsWithDBEvents - .filter( - ({ existingCalendarEvent }) => existingCalendarEvent === null, - ) - .map(({ fetchedCalendarEvent }) => ({ - id: uuid(), - iCalUid: fetchedCalendarEvent.iCalUid, - title: fetchedCalendarEvent.title, - description: fetchedCalendarEvent.description, - startsAt: fetchedCalendarEvent.startsAt, - endsAt: fetchedCalendarEvent.endsAt, - location: fetchedCalendarEvent.location, - isFullDay: fetchedCalendarEvent.isFullDay, - isCanceled: fetchedCalendarEvent.isCanceled, - conferenceSolution: fetchedCalendarEvent.conferenceSolution, - conferenceLink: { - primaryLinkLabel: fetchedCalendarEvent.conferenceLinkLabel, - primaryLinkUrl: fetchedCalendarEvent.conferenceLinkUrl, - secondaryLinks: [], - }, - externalCreatedAt: fetchedCalendarEvent.externalCreatedAt, - externalUpdatedAt: fetchedCalendarEvent.externalUpdatedAt, - })); - - if (newCalendarEventsToInsert.length > 0) { - await calendarEventRepository.insert( - newCalendarEventsToInsert, - transactionManager, - ); - } - - const fetchedCalendarEventsWithDBEventsEnrichedWithSavedEvents: FetchedCalendarEventWithDBEvent[] = - fetchedCalendarEventsWithDBEvents.map( - ({ fetchedCalendarEvent, existingCalendarEvent }) => { - const savedCalendarEvent = newCalendarEventsToInsert.find( - (inserted) => - inserted.iCalUid === fetchedCalendarEvent.iCalUid, + const fetchedCalendarEventsWithDBEvents: FetchedCalendarEventWithDBEvent[] = + fetchedCalendarEvents.map( + (event): FetchedCalendarEventWithDBEvent => { + const existingEventWithSameiCalUid = + existingCalendarEvents.find( + (existingEvent) => existingEvent.iCalUid === event.iCalUid, ); - return { - fetchedCalendarEvent, - existingCalendarEvent: existingCalendarEvent, - newlyCreatedCalendarEvent: savedCalendarEvent - ? ({ - id: savedCalendarEvent.id, - iCalUid: savedCalendarEvent.iCalUid, - } as CalendarEventWorkspaceEntity) - : null, - }; - }, - ); - - const existingEventsToUpdate = - fetchedCalendarEventsWithDBEventsEnrichedWithSavedEvents - .filter( - ({ existingCalendarEvent }) => existingCalendarEvent !== null, - ) - .map(({ fetchedCalendarEvent, existingCalendarEvent }) => { - if (!existingCalendarEvent) { - throw new Error( - `Existing calendar event with iCalUid ${fetchedCalendarEvent.iCalUid} not found - should never happen`, - ); - } - - return { - criteria: existingCalendarEvent.id, - partialEntity: { - iCalUid: fetchedCalendarEvent.iCalUid, - title: fetchedCalendarEvent.title, - description: fetchedCalendarEvent.description, - startsAt: fetchedCalendarEvent.startsAt, - endsAt: fetchedCalendarEvent.endsAt, - location: fetchedCalendarEvent.location, - isFullDay: fetchedCalendarEvent.isFullDay, - isCanceled: fetchedCalendarEvent.isCanceled, - conferenceSolution: - fetchedCalendarEvent.conferenceSolution, - conferenceLink: { - primaryLinkLabel: - fetchedCalendarEvent.conferenceLinkLabel, - primaryLinkUrl: fetchedCalendarEvent.conferenceLinkUrl, - secondaryLinks: [], - }, - externalCreatedAt: fetchedCalendarEvent.externalCreatedAt, - externalUpdatedAt: fetchedCalendarEvent.externalUpdatedAt, - }, - }; - }); - - if (existingEventsToUpdate.length > 0) { - await calendarEventRepository.updateMany( - existingEventsToUpdate, - transactionManager, - ); - } - - const calendarChannelEventAssociationsToSave: Pick< - CalendarChannelEventAssociationWorkspaceEntity, - | 'calendarEventId' - | 'eventExternalId' - | 'calendarChannelId' - | 'recurringEventExternalId' - >[] = fetchedCalendarEventsWithDBEventsEnrichedWithSavedEvents.map( - ({ - fetchedCalendarEvent, - existingCalendarEvent, - newlyCreatedCalendarEvent, - }) => { - const calendarEventId = - existingCalendarEvent?.id ?? newlyCreatedCalendarEvent?.id; - - if (!calendarEventId) { - throw new Error( - `Calendar event id not found for event with iCalUid ${fetchedCalendarEvent.iCalUid} - should never happen`, - ); - } - return { - calendarEventId, - eventExternalId: fetchedCalendarEvent.id, - calendarChannelId: calendarChannel.id, - recurringEventExternalId: - fetchedCalendarEvent.recurringEventExternalId ?? '', + fetchedCalendarEvent: event, + existingCalendarEvent: existingEventWithSameiCalUid ?? null, + newlyCreatedCalendarEvent: null, }; }, ); - if (calendarChannelEventAssociationsToSave.length > 0) { - await calendarChannelEventAssociationRepository.insert( - calendarChannelEventAssociationsToSave, - transactionManager, - ); - } + const newCalendarEventsToInsert = fetchedCalendarEventsWithDBEvents + .filter( + ({ existingCalendarEvent }) => existingCalendarEvent === null, + ) + .map(({ fetchedCalendarEvent }) => ({ + id: uuid(), + iCalUid: fetchedCalendarEvent.iCalUid, + title: fetchedCalendarEvent.title, + description: fetchedCalendarEvent.description, + startsAt: fetchedCalendarEvent.startsAt, + endsAt: fetchedCalendarEvent.endsAt, + location: fetchedCalendarEvent.location, + isFullDay: fetchedCalendarEvent.isFullDay, + isCanceled: fetchedCalendarEvent.isCanceled, + conferenceSolution: fetchedCalendarEvent.conferenceSolution, + conferenceLink: { + primaryLinkLabel: fetchedCalendarEvent.conferenceLinkLabel, + primaryLinkUrl: fetchedCalendarEvent.conferenceLinkUrl, + secondaryLinks: [], + }, + externalCreatedAt: fetchedCalendarEvent.externalCreatedAt, + externalUpdatedAt: fetchedCalendarEvent.externalUpdatedAt, + })); - const participantsToCreate = - fetchedCalendarEventsWithDBEventsEnrichedWithSavedEvents - .filter( - ({ newlyCreatedCalendarEvent }) => - newlyCreatedCalendarEvent !== null, - ) - .flatMap( - ({ newlyCreatedCalendarEvent, fetchedCalendarEvent }) => { - if (!newlyCreatedCalendarEvent?.id) { - throw new Error( - `Newly created calendar event with iCalUid ${fetchedCalendarEvent.iCalUid} not found - should never happen`, - ); - } + if (newCalendarEventsToInsert.length > 0) { + await calendarEventRepository.insert( + newCalendarEventsToInsert, + transactionManager, + ); + } - return fetchedCalendarEvent.participants.map( - (participant) => ({ - ...participant, - calendarEventId: newlyCreatedCalendarEvent.id, - }), - ); - }, + const fetchedCalendarEventsWithDBEventsEnrichedWithSavedEvents: FetchedCalendarEventWithDBEvent[] = + fetchedCalendarEventsWithDBEvents.map( + ({ fetchedCalendarEvent, existingCalendarEvent }) => { + const savedCalendarEvent = newCalendarEventsToInsert.find( + (inserted) => + inserted.iCalUid === fetchedCalendarEvent.iCalUid, ); - // todo: we should prevent duplicate rows on calendarEventAssociation by creating - // an index on calendarChannelId and calendarEventId - const participantsToUpdate = - fetchedCalendarEventsWithDBEventsEnrichedWithSavedEvents - .filter( - ({ existingCalendarEvent }) => existingCalendarEvent !== null, - ) - .flatMap(({ fetchedCalendarEvent, existingCalendarEvent }) => { - if (!existingCalendarEvent?.id) { + return { + fetchedCalendarEvent, + existingCalendarEvent: existingCalendarEvent, + newlyCreatedCalendarEvent: savedCalendarEvent + ? ({ + id: savedCalendarEvent.id, + iCalUid: savedCalendarEvent.iCalUid, + } as CalendarEventWorkspaceEntity) + : null, + }; + }, + ); + + const existingEventsToUpdate = + fetchedCalendarEventsWithDBEventsEnrichedWithSavedEvents + .filter( + ({ existingCalendarEvent }) => existingCalendarEvent !== null, + ) + .map(({ fetchedCalendarEvent, existingCalendarEvent }) => { + if (!existingCalendarEvent) { + throw new Error( + `Existing calendar event with iCalUid ${fetchedCalendarEvent.iCalUid} not found - should never happen`, + ); + } + + return { + criteria: existingCalendarEvent.id, + partialEntity: { + iCalUid: fetchedCalendarEvent.iCalUid, + title: fetchedCalendarEvent.title, + description: fetchedCalendarEvent.description, + startsAt: fetchedCalendarEvent.startsAt, + endsAt: fetchedCalendarEvent.endsAt, + location: fetchedCalendarEvent.location, + isFullDay: fetchedCalendarEvent.isFullDay, + isCanceled: fetchedCalendarEvent.isCanceled, + conferenceSolution: fetchedCalendarEvent.conferenceSolution, + conferenceLink: { + primaryLinkLabel: + fetchedCalendarEvent.conferenceLinkLabel, + primaryLinkUrl: fetchedCalendarEvent.conferenceLinkUrl, + secondaryLinks: [], + }, + externalCreatedAt: fetchedCalendarEvent.externalCreatedAt, + externalUpdatedAt: fetchedCalendarEvent.externalUpdatedAt, + }, + }; + }); + + if (existingEventsToUpdate.length > 0) { + await calendarEventRepository.updateMany( + existingEventsToUpdate, + transactionManager, + ); + } + + const calendarChannelEventAssociationsToSave: Pick< + CalendarChannelEventAssociationWorkspaceEntity, + | 'calendarEventId' + | 'eventExternalId' + | 'calendarChannelId' + | 'recurringEventExternalId' + >[] = fetchedCalendarEventsWithDBEventsEnrichedWithSavedEvents.map( + ({ + fetchedCalendarEvent, + existingCalendarEvent, + newlyCreatedCalendarEvent, + }) => { + const calendarEventId = + existingCalendarEvent?.id ?? newlyCreatedCalendarEvent?.id; + + if (!calendarEventId) { + throw new Error( + `Calendar event id not found for event with iCalUid ${fetchedCalendarEvent.iCalUid} - should never happen`, + ); + } + + return { + calendarEventId, + eventExternalId: fetchedCalendarEvent.id, + calendarChannelId: calendarChannel.id, + recurringEventExternalId: + fetchedCalendarEvent.recurringEventExternalId ?? '', + }; + }, + ); + + if (calendarChannelEventAssociationsToSave.length > 0) { + await calendarChannelEventAssociationRepository.insert( + calendarChannelEventAssociationsToSave, + transactionManager, + ); + } + + const participantsToCreate = + fetchedCalendarEventsWithDBEventsEnrichedWithSavedEvents + .filter( + ({ newlyCreatedCalendarEvent }) => + newlyCreatedCalendarEvent !== null, + ) + .flatMap( + ({ newlyCreatedCalendarEvent, fetchedCalendarEvent }) => { + if (!newlyCreatedCalendarEvent?.id) { throw new Error( - `Existing calendar event with iCalUid ${fetchedCalendarEvent.iCalUid} not found - should never happen`, + `Newly created calendar event with iCalUid ${fetchedCalendarEvent.iCalUid} not found - should never happen`, ); } return fetchedCalendarEvent.participants.map( (participant) => ({ ...participant, - calendarEventId: existingCalendarEvent.id, + calendarEventId: newlyCreatedCalendarEvent.id, }), ); - }); + }, + ); - await this.calendarEventParticipantService.upsertAndDeleteCalendarEventParticipants( - { - participantsToCreate, - participantsToUpdate, - transactionManager, - calendarChannel, - connectedAccount, - workspaceId, - }, - ); - }, - ); - }, - ); + // todo: we should prevent duplicate rows on calendarEventAssociation by creating + // an index on calendarChannelId and calendarEventId + const participantsToUpdate = + fetchedCalendarEventsWithDBEventsEnrichedWithSavedEvents + .filter( + ({ existingCalendarEvent }) => existingCalendarEvent !== null, + ) + .flatMap(({ fetchedCalendarEvent, existingCalendarEvent }) => { + if (!existingCalendarEvent?.id) { + throw new Error( + `Existing calendar event with iCalUid ${fetchedCalendarEvent.iCalUid} not found - should never happen`, + ); + } + + return fetchedCalendarEvent.participants.map((participant) => ({ + ...participant, + calendarEventId: existingCalendarEvent.id, + })); + }); + + await this.calendarEventParticipantService.upsertAndDeleteCalendarEventParticipants( + { + participantsToCreate, + participantsToUpdate, + transactionManager, + calendarChannel, + connectedAccount, + workspaceId, + }, + ); + }, + ); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-participant-manager/services/calendar-event-participant.service.ts b/packages/twenty-server/src/modules/calendar/calendar-event-participant-manager/services/calendar-event-participant.service.ts index a009213b718..2ab7cebe542 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-participant-manager/services/calendar-event-participant.service.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-participant-manager/services/calendar-event-participant.service.ts @@ -58,131 +58,127 @@ export class CalendarEventParticipantService { }): Promise { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const chunkedParticipantsToUpdate = chunk(participantsToUpdate, 200); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const chunkedParticipantsToUpdate = chunk(participantsToUpdate, 200); - const calendarEventParticipantRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarEventParticipant', - ); + const calendarEventParticipantRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarEventParticipant', + ); - for (const participantsToUpdateChunk of chunkedParticipantsToUpdate) { - const existingCalendarEventParticipants = - await calendarEventParticipantRepository.find({ - where: { - calendarEventId: Any( - participantsToUpdateChunk - .map((participant) => participant.calendarEventId) - .filter(isDefined), - ), - }, - }); - - const { - calendarEventParticipantsToUpdate, - newCalendarEventParticipants, - } = participantsToUpdateChunk.reduce<{ - calendarEventParticipantsToUpdate: FetchedCalendarEventParticipantWithCalendarEventIdAndExistingId[]; - newCalendarEventParticipants: FetchedCalendarEventParticipantWithCalendarEventId[]; - }>( - (acc, calendarEventParticipant) => { - const existingCalendarEventParticipant = - existingCalendarEventParticipants.find( - (existingCalendarEventParticipant) => - existingCalendarEventParticipant.handle === - calendarEventParticipant.handle && - existingCalendarEventParticipant.calendarEventId === - calendarEventParticipant.calendarEventId, - ); - - if (existingCalendarEventParticipant) { - acc.calendarEventParticipantsToUpdate.push({ - ...calendarEventParticipant, - id: existingCalendarEventParticipant.id, - }); - } else { - acc.newCalendarEventParticipants.push(calendarEventParticipant); - } - - return acc; - }, - { - calendarEventParticipantsToUpdate: [], - newCalendarEventParticipants: [], - }, - ); - - const calendarEventParticipantsToDelete = differenceWith( - existingCalendarEventParticipants, - participantsToUpdateChunk, - (existingCalendarEventParticipant, participantToUpdate) => - existingCalendarEventParticipant.handle === - participantToUpdate.handle && - existingCalendarEventParticipant.calendarEventId === - participantToUpdate.calendarEventId, - ); - - await calendarEventParticipantRepository.delete( - { - id: Any( - calendarEventParticipantsToDelete.map( - (calendarEventParticipant) => calendarEventParticipant.id, - ), + for (const participantsToUpdateChunk of chunkedParticipantsToUpdate) { + const existingCalendarEventParticipants = + await calendarEventParticipantRepository.find({ + where: { + calendarEventId: Any( + participantsToUpdateChunk + .map((participant) => participant.calendarEventId) + .filter(isDefined), ), }, - transactionManager, - ); + }); - await calendarEventParticipantRepository.updateMany( - calendarEventParticipantsToUpdate.map((participant) => ({ - criteria: participant.id, - partialEntity: participant, - })), - transactionManager, - ); - participantsToCreate.push(...newCalendarEventParticipants); - } + const { + calendarEventParticipantsToUpdate, + newCalendarEventParticipants, + } = participantsToUpdateChunk.reduce<{ + calendarEventParticipantsToUpdate: FetchedCalendarEventParticipantWithCalendarEventIdAndExistingId[]; + newCalendarEventParticipants: FetchedCalendarEventParticipantWithCalendarEventId[]; + }>( + (acc, calendarEventParticipant) => { + const existingCalendarEventParticipant = + existingCalendarEventParticipants.find( + (existingCalendarEventParticipant) => + existingCalendarEventParticipant.handle === + calendarEventParticipant.handle && + existingCalendarEventParticipant.calendarEventId === + calendarEventParticipant.calendarEventId, + ); - const chunkedParticipantsToCreate = chunk(participantsToCreate, 200); - const savedParticipants: CalendarEventParticipantWorkspaceEntity[] = []; + if (existingCalendarEventParticipant) { + acc.calendarEventParticipantsToUpdate.push({ + ...calendarEventParticipant, + id: existingCalendarEventParticipant.id, + }); + } else { + acc.newCalendarEventParticipants.push(calendarEventParticipant); + } - for (const participantsToCreateChunk of chunkedParticipantsToCreate) { - const savedParticipantsChunk = - await calendarEventParticipantRepository.insert( - participantsToCreateChunk, - transactionManager, - ); + return acc; + }, + { + calendarEventParticipantsToUpdate: [], + newCalendarEventParticipants: [], + }, + ); - savedParticipants.push(...savedParticipantsChunk.raw); - } + const calendarEventParticipantsToDelete = differenceWith( + existingCalendarEventParticipants, + participantsToUpdateChunk, + (existingCalendarEventParticipant, participantToUpdate) => + existingCalendarEventParticipant.handle === + participantToUpdate.handle && + existingCalendarEventParticipant.calendarEventId === + participantToUpdate.calendarEventId, + ); - if (calendarChannel.isContactAutoCreationEnabled) { - await this.messageQueueService.add( - CreateCompanyAndContactJob.name, - { - workspaceId, - connectedAccount, - contactsToCreate: savedParticipants.map((participant) => ({ - handle: participant.handle ?? '', - displayName: - participant.displayName ?? participant.handle ?? '', - })), - source: FieldActorSource.CALENDAR, - }, - ); - } - - await this.matchParticipantService.matchParticipants({ - participants: savedParticipants, - objectMetadataName: 'calendarEventParticipant', + await calendarEventParticipantRepository.delete( + { + id: Any( + calendarEventParticipantsToDelete.map( + (calendarEventParticipant) => calendarEventParticipant.id, + ), + ), + }, transactionManager, - matchWith: 'workspaceMemberAndPerson', - workspaceId, - }); - }, - ); + ); + + await calendarEventParticipantRepository.updateMany( + calendarEventParticipantsToUpdate.map((participant) => ({ + criteria: participant.id, + partialEntity: participant, + })), + transactionManager, + ); + participantsToCreate.push(...newCalendarEventParticipants); + } + + const chunkedParticipantsToCreate = chunk(participantsToCreate, 200); + const savedParticipants: CalendarEventParticipantWorkspaceEntity[] = []; + + for (const participantsToCreateChunk of chunkedParticipantsToCreate) { + const savedParticipantsChunk = + await calendarEventParticipantRepository.insert( + participantsToCreateChunk, + transactionManager, + ); + + savedParticipants.push(...savedParticipantsChunk.raw); + } + + if (calendarChannel.isContactAutoCreationEnabled) { + await this.messageQueueService.add( + CreateCompanyAndContactJob.name, + { + workspaceId, + connectedAccount, + contactsToCreate: savedParticipants.map((participant) => ({ + handle: participant.handle ?? '', + displayName: participant.displayName ?? participant.handle ?? '', + })), + source: FieldActorSource.CALENDAR, + }, + ); + } + + await this.matchParticipantService.matchParticipants({ + participants: savedParticipants, + objectMetadataName: 'calendarEventParticipant', + transactionManager, + matchWith: 'workspaceMemberAndPerson', + workspaceId, + }); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/calendar/common/query-hooks/calendar-event/services/apply-calendar-events-visibility-restrictions.service.spec.ts b/packages/twenty-server/src/modules/calendar/common/query-hooks/calendar-event/services/apply-calendar-events-visibility-restrictions.service.spec.ts index d0e2393cf19..59b2379b733 100644 --- a/packages/twenty-server/src/modules/calendar/common/query-hooks/calendar-event/services/apply-calendar-events-visibility-restrictions.service.spec.ts +++ b/packages/twenty-server/src/modules/calendar/common/query-hooks/calendar-event/services/apply-calendar-events-visibility-restrictions.service.spec.ts @@ -66,7 +66,7 @@ describe('ApplyCalendarEventsVisibilityRestrictionsService', () => { }), executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), }; beforeEach(async () => { diff --git a/packages/twenty-server/src/modules/calendar/common/query-hooks/calendar-event/services/apply-calendar-events-visibility-restrictions.service.ts b/packages/twenty-server/src/modules/calendar/common/query-hooks/calendar-event/services/apply-calendar-events-visibility-restrictions.service.ts index d127a9809b3..1432fe1f8b7 100644 --- a/packages/twenty-server/src/modules/calendar/common/query-hooks/calendar-event/services/apply-calendar-events-visibility-restrictions.service.ts +++ b/packages/twenty-server/src/modules/calendar/common/query-hooks/calendar-event/services/apply-calendar-events-visibility-restrictions.service.ts @@ -27,7 +27,6 @@ export class ApplyCalendarEventsVisibilityRestrictionsService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const calendarChannelEventAssociationRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -117,6 +116,7 @@ export class ApplyCalendarEventsVisibilityRestrictionsService { return calendarEvents; }, + authContext, ); } } diff --git a/packages/twenty-server/src/modules/calendar/common/services/calendar-channel-sync-status.service.ts b/packages/twenty-server/src/modules/calendar/common/services/calendar-channel-sync-status.service.ts index 1363c775ab2..166888abae2 100644 --- a/packages/twenty-server/src/modules/calendar/common/services/calendar-channel-sync-status.service.ts +++ b/packages/twenty-server/src/modules/calendar/common/services/calendar-channel-sync-status.service.ts @@ -39,21 +39,18 @@ export class CalendarChannelSyncStatusService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const calendarChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const calendarChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarChannel', + ); - await calendarChannelRepository.update(calendarChannelIds, { - syncStage: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_PENDING, - ...(!preserveSyncStageStartedAt ? { syncStageStartedAt: null } : {}), - }); - }, - ); + await calendarChannelRepository.update(calendarChannelIds, { + syncStage: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_PENDING, + ...(!preserveSyncStageStartedAt ? { syncStageStartedAt: null } : {}), + }); + }, authContext); } public async markAsCalendarEventListFetchOngoing( @@ -66,22 +63,19 @@ export class CalendarChannelSyncStatusService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const calendarChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const calendarChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarChannel', + ); - await calendarChannelRepository.update(calendarChannelIds, { - syncStage: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_ONGOING, - syncStatus: CalendarChannelSyncStatus.ONGOING, - syncStageStartedAt: new Date().toISOString(), - }); - }, - ); + await calendarChannelRepository.update(calendarChannelIds, { + syncStage: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_ONGOING, + syncStatus: CalendarChannelSyncStatus.ONGOING, + syncStageStartedAt: new Date().toISOString(), + }); + }, authContext); } public async resetAndMarkAsCalendarEventListFetchPending( @@ -100,22 +94,19 @@ export class CalendarChannelSyncStatusService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const calendarChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const calendarChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarChannel', + ); - await calendarChannelRepository.update(calendarChannelIds, { - syncCursor: '', - syncStageStartedAt: null, - throttleFailureCount: 0, - }); - }, - ); + await calendarChannelRepository.update(calendarChannelIds, { + syncCursor: '', + syncStageStartedAt: null, + throttleFailureCount: 0, + }); + }, authContext); await this.markAsCalendarEventListFetchPending( calendarChannelIds, @@ -133,20 +124,17 @@ export class CalendarChannelSyncStatusService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const calendarChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const calendarChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarChannel', + ); - await calendarChannelRepository.update(calendarChannelIds, { - syncStageStartedAt: null, - }); - }, - ); + await calendarChannelRepository.update(calendarChannelIds, { + syncStageStartedAt: null, + }); + }, authContext); } public async markAsCalendarEventsImportPending( @@ -160,21 +148,18 @@ export class CalendarChannelSyncStatusService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const calendarChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const calendarChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarChannel', + ); - await calendarChannelRepository.update(calendarChannelIds, { - syncStage: CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_PENDING, - ...(!preserveSyncStageStartedAt ? { syncStageStartedAt: null } : {}), - }); - }, - ); + await calendarChannelRepository.update(calendarChannelIds, { + syncStage: CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_PENDING, + ...(!preserveSyncStageStartedAt ? { syncStageStartedAt: null } : {}), + }); + }, authContext); } public async markAsCalendarEventsImportOngoing( @@ -187,21 +172,18 @@ export class CalendarChannelSyncStatusService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const calendarChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const calendarChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarChannel', + ); - await calendarChannelRepository.update(calendarChannelIds, { - syncStage: CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_ONGOING, - syncStatus: CalendarChannelSyncStatus.ONGOING, - }); - }, - ); + await calendarChannelRepository.update(calendarChannelIds, { + syncStage: CalendarChannelSyncStage.CALENDAR_EVENTS_IMPORT_ONGOING, + syncStatus: CalendarChannelSyncStatus.ONGOING, + }); + }, authContext); } public async markAsCompletedAndMarkAsCalendarEventListFetchPending( @@ -214,24 +196,21 @@ export class CalendarChannelSyncStatusService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const calendarChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const calendarChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarChannel', + ); - await calendarChannelRepository.update(calendarChannelIds, { - syncStage: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_PENDING, - syncStatus: CalendarChannelSyncStatus.ACTIVE, - throttleFailureCount: 0, - syncStageStartedAt: null, - syncedAt: new Date().toISOString(), - }); - }, - ); + await calendarChannelRepository.update(calendarChannelIds, { + syncStage: CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_PENDING, + syncStatus: CalendarChannelSyncStatus.ACTIVE, + throttleFailureCount: 0, + syncStageStartedAt: null, + syncedAt: new Date().toISOString(), + }); + }, authContext); await this.markAsCalendarEventListFetchPending( calendarChannelIds, @@ -260,21 +239,18 @@ export class CalendarChannelSyncStatusService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const calendarChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const calendarChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarChannel', + ); - await calendarChannelRepository.update(calendarChannelIds, { - syncStatus: CalendarChannelSyncStatus.FAILED_UNKNOWN, - syncStage: CalendarChannelSyncStage.FAILED, - }); - }, - ); + await calendarChannelRepository.update(calendarChannelIds, { + syncStatus: CalendarChannelSyncStatus.FAILED_UNKNOWN, + syncStage: CalendarChannelSyncStage.FAILED, + }); + }, authContext); await this.metricsService.batchIncrementCounter({ key: MetricsKeys.CalendarEventSyncJobFailedUnknown, @@ -298,48 +274,45 @@ export class CalendarChannelSyncStatusService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const calendarChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarChannel', - ); - - await calendarChannelRepository.update(calendarChannelIds, { - syncStatus: CalendarChannelSyncStatus.FAILED_INSUFFICIENT_PERMISSIONS, - syncStage: CalendarChannelSyncStage.FAILED, - }); - - const connectedAccountRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'connectedAccount', - ); - - const calendarChannels = await calendarChannelRepository.find({ - select: ['id', 'connectedAccountId'], - where: { id: Any(calendarChannelIds) }, - }); - - const connectedAccountIds = calendarChannels.map( - (calendarChannel) => calendarChannel.connectedAccountId, - ); - - await connectedAccountRepository.update( - { id: Any(connectedAccountIds) }, - { - authFailedAt: new Date(), - }, - ); - - await this.addToAccountsToReconnect( - calendarChannels.map((calendarChannel) => calendarChannel.id), + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const calendarChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( workspaceId, + 'calendarChannel', ); - }, - ); + + await calendarChannelRepository.update(calendarChannelIds, { + syncStatus: CalendarChannelSyncStatus.FAILED_INSUFFICIENT_PERMISSIONS, + syncStage: CalendarChannelSyncStage.FAILED, + }); + + const connectedAccountRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'connectedAccount', + ); + + const calendarChannels = await calendarChannelRepository.find({ + select: ['id', 'connectedAccountId'], + where: { id: Any(calendarChannelIds) }, + }); + + const connectedAccountIds = calendarChannels.map( + (calendarChannel) => calendarChannel.connectedAccountId, + ); + + await connectedAccountRepository.update( + { id: Any(connectedAccountIds) }, + { + authFailedAt: new Date(), + }, + ); + + await this.addToAccountsToReconnect( + calendarChannels.map((calendarChannel) => calendarChannel.id), + workspaceId, + ); + }, authContext); await this.metricsService.batchIncrementCounter({ key: MetricsKeys.CalendarEventSyncJobFailedInsufficientPermissions, diff --git a/packages/twenty-server/src/modules/connected-account/channel-sync/services/channel-sync.service.ts b/packages/twenty-server/src/modules/connected-account/channel-sync/services/channel-sync.service.ts index a284d085fb5..ccbf6c9776c 100644 --- a/packages/twenty-server/src/modules/connected-account/channel-sync/services/channel-sync.service.ts +++ b/packages/twenty-server/src/modules/connected-account/channel-sync/services/channel-sync.service.ts @@ -53,38 +53,35 @@ export class ChannelSyncService { ): Promise { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); - const messageChannels = await messageChannelRepository.find({ - where: { - connectedAccountId, - syncStage: MessageChannelSyncStage.PENDING_CONFIGURATION, + const messageChannels = await messageChannelRepository.find({ + where: { + connectedAccountId, + syncStage: MessageChannelSyncStage.PENDING_CONFIGURATION, + }, + }); + + for (const messageChannel of messageChannels) { + await this.messageChannelSyncStatusService.markAsMessagesListFetchScheduled( + [messageChannel.id], + workspaceId, + ); + + await this.messageQueueService.add( + MessagingMessageListFetchJob.name, + { + workspaceId, + messageChannelId: messageChannel.id, }, - }); - - for (const messageChannel of messageChannels) { - await this.messageChannelSyncStatusService.markAsMessagesListFetchScheduled( - [messageChannel.id], - workspaceId, - ); - - await this.messageQueueService.add( - MessagingMessageListFetchJob.name, - { - workspaceId, - messageChannelId: messageChannel.id, - }, - ); - } - }, - ); + ); + } + }, authContext); } private async startCalendarChannelSync( @@ -93,38 +90,35 @@ export class ChannelSyncService { ): Promise { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const calendarChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'calendarChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const calendarChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'calendarChannel', + ); - const calendarChannels = await calendarChannelRepository.find({ - where: { - connectedAccountId, - syncStage: CalendarChannelSyncStage.PENDING_CONFIGURATION, - }, + const calendarChannels = await calendarChannelRepository.find({ + where: { + connectedAccountId, + syncStage: CalendarChannelSyncStage.PENDING_CONFIGURATION, + }, + }); + + for (const calendarChannel of calendarChannels) { + await calendarChannelRepository.update(calendarChannel.id, { + syncStage: + CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_SCHEDULED, + syncStatus: CalendarChannelSyncStatus.ONGOING, }); - for (const calendarChannel of calendarChannels) { - await calendarChannelRepository.update(calendarChannel.id, { - syncStage: - CalendarChannelSyncStage.CALENDAR_EVENT_LIST_FETCH_SCHEDULED, - syncStatus: CalendarChannelSyncStatus.ONGOING, - }); - - await this.calendarQueueService.add( - CalendarEventListFetchJob.name, - { - workspaceId, - calendarChannelId: calendarChannel.id, - }, - ); - } - }, - ); + await this.calendarQueueService.add( + CalendarEventListFetchJob.name, + { + workspaceId, + calendarChannelId: calendarChannel.id, + }, + ); + } + }, authContext); } } diff --git a/packages/twenty-server/src/modules/connected-account/email-alias-manager/services/email-alias-manager.service.spec.ts b/packages/twenty-server/src/modules/connected-account/email-alias-manager/services/email-alias-manager.service.spec.ts index 18b45138b7b..9f0862a9708 100644 --- a/packages/twenty-server/src/modules/connected-account/email-alias-manager/services/email-alias-manager.service.spec.ts +++ b/packages/twenty-server/src/modules/connected-account/email-alias-manager/services/email-alias-manager.service.spec.ts @@ -35,7 +35,7 @@ describe('Email Alias Manager Service', () => { .mockResolvedValue(connectedAccountRepository), executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), }, }, EmailAliasManagerService, diff --git a/packages/twenty-server/src/modules/connected-account/email-alias-manager/services/email-alias-manager.service.ts b/packages/twenty-server/src/modules/connected-account/email-alias-manager/services/email-alias-manager.service.ts index beb06710b14..04800e1b51b 100644 --- a/packages/twenty-server/src/modules/connected-account/email-alias-manager/services/email-alias-manager.service.ts +++ b/packages/twenty-server/src/modules/connected-account/email-alias-manager/services/email-alias-manager.service.ts @@ -49,22 +49,19 @@ export class EmailAliasManagerService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const connectedAccountRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'connectedAccount', - ); - - await connectedAccountRepository.update( - { id: connectedAccount.id }, - { - handleAliases: handleAliases.join(','), // TODO: modify handleAliases to be of fieldmetadatatype array - }, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const connectedAccountRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'connectedAccount', ); - }, - ); + + await connectedAccountRepository.update( + { id: connectedAccount.id }, + { + handleAliases: handleAliases.join(','), // TODO: modify handleAliases to be of fieldmetadatatype array + }, + ); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/connected-account/jobs/delete-workspace-member-connected-accounts.job.ts b/packages/twenty-server/src/modules/connected-account/jobs/delete-workspace-member-connected-accounts.job.ts index e926150be56..aebc1b3cd9f 100644 --- a/packages/twenty-server/src/modules/connected-account/jobs/delete-workspace-member-connected-accounts.job.ts +++ b/packages/twenty-server/src/modules/connected-account/jobs/delete-workspace-member-connected-accounts.job.ts @@ -24,19 +24,16 @@ export class DeleteWorkspaceMemberConnectedAccountsCleanupJob { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const connectedAccountRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'connectedAccount', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const connectedAccountRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'connectedAccount', + ); - await connectedAccountRepository.delete({ - accountOwnerId: workspaceMemberId, - }); - }, - ); + await connectedAccountRepository.delete({ + accountOwnerId: workspaceMemberId, + }); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/connected-account/listeners/connected-account.listener.ts b/packages/twenty-server/src/modules/connected-account/listeners/connected-account.listener.ts index 9e922cc733b..297c7eedcb0 100644 --- a/packages/twenty-server/src/modules/connected-account/listeners/connected-account.listener.ts +++ b/packages/twenty-server/src/modules/connected-account/listeners/connected-account.listener.ts @@ -27,36 +27,30 @@ export class ConnectedAccountListener { const workspaceId = payload.workspaceId; const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - for (const eventPayload of payload.events) { - const workspaceMemberId = - eventPayload.properties.before.accountOwnerId; + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + for (const eventPayload of payload.events) { + const workspaceMemberId = eventPayload.properties.before.accountOwnerId; - const workspaceMemberRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'workspaceMember', - { shouldBypassPermissionChecks: true }, - ); - const workspaceMember = await workspaceMemberRepository.findOneOrFail( - { - where: { id: workspaceMemberId }, - }, - ); - - const userId = workspaceMember.userId; - - const connectedAccountId = eventPayload.properties.before.id; - - await this.accountsToReconnectService.removeAccountToReconnect( - userId, + const workspaceMemberRepository = + await this.globalWorkspaceOrmManager.getRepository( workspaceId, - connectedAccountId, + 'workspaceMember', + { shouldBypassPermissionChecks: true }, ); - } - }, - ); + const workspaceMember = await workspaceMemberRepository.findOneOrFail({ + where: { id: workspaceMemberId }, + }); + + const userId = workspaceMember.userId; + + const connectedAccountId = eventPayload.properties.before.id; + + await this.accountsToReconnectService.removeAccountToReconnect( + userId, + workspaceId, + connectedAccountId, + ); + } + }, authContext); } } diff --git a/packages/twenty-server/src/modules/connected-account/query-hooks/connected-account-delete-one.pre-query.hook.ts b/packages/twenty-server/src/modules/connected-account/query-hooks/connected-account-delete-one.pre-query.hook.ts index d7c9cc40aeb..c8ad67843fa 100644 --- a/packages/twenty-server/src/modules/connected-account/query-hooks/connected-account-delete-one.pre-query.hook.ts +++ b/packages/twenty-server/src/modules/connected-account/query-hooks/connected-account-delete-one.pre-query.hook.ts @@ -41,7 +41,6 @@ export class ConnectedAccountDeleteOnePreQueryHook const messageChannels = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext as WorkspaceAuthContext, async () => { const messageChannelRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -53,6 +52,7 @@ export class ConnectedAccountDeleteOnePreQueryHook connectedAccountId, }); }, + authContext as WorkspaceAuthContext, ); const objectMetadataEntity = diff --git a/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/services/connected-account-refresh-tokens.service.spec.ts b/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/services/connected-account-refresh-tokens.service.spec.ts index e7f7b66da16..a182e5a6737 100644 --- a/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/services/connected-account-refresh-tokens.service.spec.ts +++ b/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/services/connected-account-refresh-tokens.service.spec.ts @@ -48,7 +48,7 @@ describe('ConnectedAccountRefreshTokensService', () => { executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), }, }, ], diff --git a/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/services/connected-account-refresh-tokens.service.ts b/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/services/connected-account-refresh-tokens.service.ts index f75b08f56b0..fea8eaaa41b 100644 --- a/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/services/connected-account-refresh-tokens.service.ts +++ b/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/services/connected-account-refresh-tokens.service.ts @@ -77,24 +77,21 @@ export class ConnectedAccountRefreshTokensService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const connectedAccountRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'connectedAccount', - ); - - await connectedAccountRepository.update( - { id: connectedAccount.id }, - { - ...connectedAccountTokens, - lastCredentialsRefreshedAt: new Date(), - }, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const connectedAccountRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'connectedAccount', ); - }, - ); + + await connectedAccountRepository.update( + { id: connectedAccount.id }, + { + ...connectedAccountTokens, + lastCredentialsRefreshedAt: new Date(), + }, + ); + }, authContext); return connectedAccountTokens; } diff --git a/packages/twenty-server/src/modules/connected-account/services/imap-smtp-caldav-apis.service.spec.ts b/packages/twenty-server/src/modules/connected-account/services/imap-smtp-caldav-apis.service.spec.ts index 9503f6f3b18..0df4de9c870 100644 --- a/packages/twenty-server/src/modules/connected-account/services/imap-smtp-caldav-apis.service.spec.ts +++ b/packages/twenty-server/src/modules/connected-account/services/imap-smtp-caldav-apis.service.spec.ts @@ -70,7 +70,7 @@ describe('ImapSmtpCalDavAPIService', () => { executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), }, }, { diff --git a/packages/twenty-server/src/modules/connected-account/services/imap-smtp-caldav-apis.service.ts b/packages/twenty-server/src/modules/connected-account/services/imap-smtp-caldav-apis.service.ts index 074c12bf082..e342939519f 100644 --- a/packages/twenty-server/src/modules/connected-account/services/imap-smtp-caldav-apis.service.ts +++ b/packages/twenty-server/src/modules/connected-account/services/imap-smtp-caldav-apis.service.ts @@ -29,7 +29,6 @@ export class ImapSmtpCalDavAPIService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const connectedAccountRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -41,6 +40,7 @@ export class ImapSmtpCalDavAPIService { where: { id, provider: ConnectedAccountProvider.IMAP_SMTP_CALDAV }, }); }, + authContext, ); } @@ -57,7 +57,6 @@ export class ImapSmtpCalDavAPIService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const connectedAccountRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -147,6 +146,7 @@ export class ImapSmtpCalDavAPIService { return newOrExistingAccountId; }, + authContext, ); } } diff --git a/packages/twenty-server/src/modules/contact-creation-manager/services/__tests__/create-company.service.spec.ts b/packages/twenty-server/src/modules/contact-creation-manager/services/__tests__/create-company.service.spec.ts index a6d3c90911a..377fc85ba6f 100644 --- a/packages/twenty-server/src/modules/contact-creation-manager/services/__tests__/create-company.service.spec.ts +++ b/packages/twenty-server/src/modules/contact-creation-manager/services/__tests__/create-company.service.spec.ts @@ -120,7 +120,7 @@ describe('CreateCompanyService', () => { getRepository: jest.fn().mockResolvedValue(mockCompanyRepository), executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), }, }, { diff --git a/packages/twenty-server/src/modules/contact-creation-manager/services/create-company-and-contact.service.ts b/packages/twenty-server/src/modules/contact-creation-manager/services/create-company-and-contact.service.ts index 884ab2439b7..e14e8982ca2 100644 --- a/packages/twenty-server/src/modules/contact-creation-manager/services/create-company-and-contact.service.ts +++ b/packages/twenty-server/src/modules/contact-creation-manager/services/create-company-and-contact.service.ts @@ -51,7 +51,6 @@ export class CreateCompanyAndPersonService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const personRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -144,6 +143,7 @@ export class CreateCompanyAndPersonService { return { ...createdPeople, ...restoredPeople }; }, + authContext, ); } @@ -160,32 +160,29 @@ export class CreateCompanyAndPersonService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - if (!connectedAccount.accountOwner) { - const workspaceMemberRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - WorkspaceMemberWorkspaceEntity, - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + if (!connectedAccount.accountOwner) { + const workspaceMemberRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + WorkspaceMemberWorkspaceEntity, + ); - const workspaceMember = await workspaceMemberRepository.findOne({ - where: { - id: connectedAccount.accountOwnerId, - }, - }); + const workspaceMember = await workspaceMemberRepository.findOne({ + where: { + id: connectedAccount.accountOwnerId, + }, + }); - if (!workspaceMember) { - throw new Error( - `Workspace member with id ${connectedAccount.accountOwnerId} not found in workspace ${workspaceId}`, - ); - } - - connectedAccount.accountOwner = workspaceMember; + if (!workspaceMember) { + throw new Error( + `Workspace member with id ${connectedAccount.accountOwnerId} not found in workspace ${workspaceId}`, + ); } - }, - ); + + connectedAccount.accountOwner = workspaceMember; + } + }, authContext); for (const contactsBatch of contactsBatches) { try { diff --git a/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts b/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts index 1c33ac2dc79..1d6eadae827 100644 --- a/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts +++ b/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts @@ -56,7 +56,6 @@ export class CreateCompanyService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const companyRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -157,6 +156,7 @@ export class CreateCompanyService { : {}), }; }, + authContext, ); } diff --git a/packages/twenty-server/src/modules/contact-creation-manager/services/create-person.service.ts b/packages/twenty-server/src/modules/contact-creation-manager/services/create-person.service.ts index 07189ceb91f..a1c3d0ab9ed 100644 --- a/packages/twenty-server/src/modules/contact-creation-manager/services/create-person.service.ts +++ b/packages/twenty-server/src/modules/contact-creation-manager/services/create-person.service.ts @@ -22,7 +22,6 @@ export class CreatePersonService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const personRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -47,6 +46,7 @@ export class CreatePersonService { return createdPeople.raw; }, + authContext, ); } @@ -61,7 +61,6 @@ export class CreatePersonService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const personRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -86,6 +85,7 @@ export class CreatePersonService { return restoredPeople.raw; }, + authContext, ); } diff --git a/packages/twenty-server/src/modules/dashboard-sync/services/dashboard-sync.service.ts b/packages/twenty-server/src/modules/dashboard-sync/services/dashboard-sync.service.ts index aae3cb9d091..17dea0213a7 100644 --- a/packages/twenty-server/src/modules/dashboard-sync/services/dashboard-sync.service.ts +++ b/packages/twenty-server/src/modules/dashboard-sync/services/dashboard-sync.service.ts @@ -60,7 +60,6 @@ export class DashboardSyncService { try { await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const dashboardRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -71,6 +70,7 @@ export class DashboardSyncService { await dashboardRepository.update({ pageLayoutId }, { updatedAt }); }, + authContext, ); } catch (error) { this.logger.error( diff --git a/packages/twenty-server/src/modules/dashboard/services/dashboard-duplication.service.ts b/packages/twenty-server/src/modules/dashboard/services/dashboard-duplication.service.ts index a4cb1c91cb2..ce72656ebbc 100644 --- a/packages/twenty-server/src/modules/dashboard/services/dashboard-duplication.service.ts +++ b/packages/twenty-server/src/modules/dashboard/services/dashboard-duplication.service.ts @@ -44,7 +44,6 @@ export class DashboardDuplicationService { const workspaceId = workspace.id; return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext as WorkspaceAuthContext, async () => { const dashboardRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -108,6 +107,7 @@ export class DashboardDuplicationService { throw error; } }, + authContext as WorkspaceAuthContext, ); } diff --git a/packages/twenty-server/src/modules/dashboard/services/dashboard-to-page-layout-sync.service.ts b/packages/twenty-server/src/modules/dashboard/services/dashboard-to-page-layout-sync.service.ts index aec90766cea..398457d8fa8 100644 --- a/packages/twenty-server/src/modules/dashboard/services/dashboard-to-page-layout-sync.service.ts +++ b/packages/twenty-server/src/modules/dashboard/services/dashboard-to-page-layout-sync.service.ts @@ -52,32 +52,29 @@ export class DashboardToPageLayoutSyncService { }): Promise { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const dashboardRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'dashboard', - { shouldBypassPermissionChecks: true }, - ); - - const dashboards = await dashboardRepository.find({ - where: { - id: In(dashboardIds), - }, - withDeleted: true, - }); - - const pageLayoutIds = dashboards - .map((dashboard) => dashboard.pageLayoutId) - .filter(isDefined); - - await this.pageLayoutService.destroyMany({ - ids: pageLayoutIds, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const dashboardRepository = + await this.globalWorkspaceOrmManager.getRepository( workspaceId, - }); - }, - ); + 'dashboard', + { shouldBypassPermissionChecks: true }, + ); + + const dashboards = await dashboardRepository.find({ + where: { + id: In(dashboardIds), + }, + withDeleted: true, + }); + + const pageLayoutIds = dashboards + .map((dashboard) => dashboard.pageLayoutId) + .filter(isDefined); + + await this.pageLayoutService.destroyMany({ + ids: pageLayoutIds, + workspaceId, + }); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/dashboard/tools/create-complete-dashboard.tool.ts b/packages/twenty-server/src/modules/dashboard/tools/create-complete-dashboard.tool.ts index d8c645f6511..2a301a2316e 100644 --- a/packages/twenty-server/src/modules/dashboard/tools/create-complete-dashboard.tool.ts +++ b/packages/twenty-server/src/modules/dashboard/tools/create-complete-dashboard.tool.ts @@ -206,27 +206,24 @@ const createDashboardRecord = async ( ): Promise => { const authContext = buildSystemAuthContext(context.workspaceId); - return deps.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const dashboardRepository = - await deps.globalWorkspaceOrmManager.getRepository( - context.workspaceId, - 'dashboard', - { shouldBypassPermissionChecks: true }, - ); + return deps.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const dashboardRepository = + await deps.globalWorkspaceOrmManager.getRepository( + context.workspaceId, + 'dashboard', + { shouldBypassPermissionChecks: true }, + ); - const position = await deps.recordPositionService.buildRecordPosition({ - value: 'first', - objectMetadata: { isCustom: false, nameSingular: 'dashboard' }, - workspaceId: context.workspaceId, - }); + const position = await deps.recordPositionService.buildRecordPosition({ + value: 'first', + objectMetadata: { isCustom: false, nameSingular: 'dashboard' }, + workspaceId: context.workspaceId, + }); - const dashboard = { id: uuidv4(), title, pageLayoutId, position }; + const dashboard = { id: uuidv4(), title, pageLayoutId, position }; - await dashboardRepository.insert(dashboard); + await dashboardRepository.insert(dashboard); - return dashboard.id; - }, - ); + return dashboard.id; + }, authContext); }; diff --git a/packages/twenty-server/src/modules/dashboard/tools/get-dashboard.tool.ts b/packages/twenty-server/src/modules/dashboard/tools/get-dashboard.tool.ts index 6d79a706782..6abbafae662 100644 --- a/packages/twenty-server/src/modules/dashboard/tools/get-dashboard.tool.ts +++ b/packages/twenty-server/src/modules/dashboard/tools/get-dashboard.tool.ts @@ -27,7 +27,6 @@ export const createGetDashboardTool = ( const dashboard = await deps.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const repo = await deps.globalWorkspaceOrmManager.getRepository( context.workspaceId, @@ -37,6 +36,7 @@ export const createGetDashboardTool = ( return repo.findOne({ where: { id: parameters.dashboardId } }); }, + authContext, ); if (!isDefined(dashboard)) { diff --git a/packages/twenty-server/src/modules/dashboard/tools/list-dashboards.tool.ts b/packages/twenty-server/src/modules/dashboard/tools/list-dashboards.tool.ts index aacd2e869f8..7807ef14a16 100644 --- a/packages/twenty-server/src/modules/dashboard/tools/list-dashboards.tool.ts +++ b/packages/twenty-server/src/modules/dashboard/tools/list-dashboards.tool.ts @@ -30,7 +30,6 @@ export const createListDashboardsTool = ( const dashboards = await deps.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const repo = await deps.globalWorkspaceOrmManager.getRepository( context.workspaceId, @@ -40,6 +39,7 @@ export const createListDashboardsTool = ( return repo.find({ take: limit, order: { position: 'ASC' } }); }, + authContext, ); const dashboardList = dashboards.map((d) => ({ diff --git a/packages/twenty-server/src/modules/favorite-folder/listeners/favorite-folder.listener.ts b/packages/twenty-server/src/modules/favorite-folder/listeners/favorite-folder.listener.ts index eeb7dc63718..c702139240d 100644 --- a/packages/twenty-server/src/modules/favorite-folder/listeners/favorite-folder.listener.ts +++ b/packages/twenty-server/src/modules/favorite-folder/listeners/favorite-folder.listener.ts @@ -25,22 +25,19 @@ export class FavoriteFolderDeletionListener { const workspaceId = payload.workspaceId; const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - for (const eventPayload of payload.events) { - const favoriteRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'favorite', - ); - - await favoriteRepository.update( - { favoriteFolderId: eventPayload.recordId }, - { deletedAt: new Date().toISOString() }, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + for (const eventPayload of payload.events) { + const favoriteRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'favorite', ); - } - }, - ); + + await favoriteRepository.update( + { favoriteFolderId: eventPayload.recordId }, + { deletedAt: new Date().toISOString() }, + ); + } + }, authContext); } } diff --git a/packages/twenty-server/src/modules/favorite/services/favorite-deletion.service.ts b/packages/twenty-server/src/modules/favorite/services/favorite-deletion.service.ts index 0421db9117f..cf131d875d3 100644 --- a/packages/twenty-server/src/modules/favorite/services/favorite-deletion.service.ts +++ b/packages/twenty-server/src/modules/favorite/services/favorite-deletion.service.ts @@ -28,68 +28,65 @@ export class FavoriteDeletionService { ): Promise { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const favoriteRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'favorite', - ); - - const favoriteObjectMetadata = - await this.objectMetadataRepository.findOne({ - where: { - nameSingular: 'favorite', - workspaceId, - }, - }); - - if (!favoriteObjectMetadata) { - throw new Error('Favorite object metadata not found'); - } - - const favoriteFields = await this.fieldMetadataRepository.find({ - where: { - objectMetadataId: favoriteObjectMetadata.id, - type: FieldMetadataType.RELATION, - }, - }); - - const favoritesToDelete = await favoriteRepository.find({ - select: { - id: true, - }, - where: favoriteFields.map((field) => ({ - [`${field.name}Id`]: In(deletedRecordIds), - })), - withDeleted: true, - }); - - if (favoritesToDelete.length === 0) { - return; - } - - const favoriteIdsToDelete = favoritesToDelete.map( - (favorite) => favorite.id, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const favoriteRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'favorite', ); - const batches: string[][] = []; + const favoriteObjectMetadata = + await this.objectMetadataRepository.findOne({ + where: { + nameSingular: 'favorite', + workspaceId, + }, + }); - for ( - let i = 0; - i < favoriteIdsToDelete.length; - i += FAVORITE_DELETION_BATCH_SIZE - ) { - batches.push( - favoriteIdsToDelete.slice(i, i + FAVORITE_DELETION_BATCH_SIZE), - ); - } + if (!favoriteObjectMetadata) { + throw new Error('Favorite object metadata not found'); + } - for (const batch of batches) { - await favoriteRepository.delete(batch); - } - }, - ); + const favoriteFields = await this.fieldMetadataRepository.find({ + where: { + objectMetadataId: favoriteObjectMetadata.id, + type: FieldMetadataType.RELATION, + }, + }); + + const favoritesToDelete = await favoriteRepository.find({ + select: { + id: true, + }, + where: favoriteFields.map((field) => ({ + [`${field.name}Id`]: In(deletedRecordIds), + })), + withDeleted: true, + }); + + if (favoritesToDelete.length === 0) { + return; + } + + const favoriteIdsToDelete = favoritesToDelete.map( + (favorite) => favorite.id, + ); + + const batches: string[][] = []; + + for ( + let i = 0; + i < favoriteIdsToDelete.length; + i += FAVORITE_DELETION_BATCH_SIZE + ) { + batches.push( + favoriteIdsToDelete.slice(i, i + FAVORITE_DELETION_BATCH_SIZE), + ); + } + + for (const batch of batches) { + await favoriteRepository.delete(batch); + } + }, authContext); } } diff --git a/packages/twenty-server/src/modules/match-participant/match-participant.service.ts b/packages/twenty-server/src/modules/match-participant/match-participant.service.ts index 7bec88a6aa9..1379a439fe5 100644 --- a/packages/twenty-server/src/modules/match-participant/match-participant.service.ts +++ b/packages/twenty-server/src/modules/match-participant/match-participant.service.ts @@ -210,36 +210,32 @@ export class MatchParticipantService< }: MatchParticipantsForWorkspaceMembersArgs) { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const participantRepository = await this.getParticipantRepository( - workspaceId, - objectMetadataName, - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const participantRepository = await this.getParticipantRepository( + workspaceId, + objectMetadataName, + ); - const participants = await participantRepository.find({ - where: { - workspaceMemberId: In(participantMatching.workspaceMemberIds), - }, - }); + const participants = await participantRepository.find({ + where: { + workspaceMemberId: In(participantMatching.workspaceMemberIds), + }, + }); - const tobeRematchedParticipants = participants.map((participant) => { - return { - ...participant, - workspaceMemberId: null, - }; - }); + const tobeRematchedParticipants = participants.map((participant) => { + return { + ...participant, + workspaceMemberId: null, + }; + }); - await this.matchParticipants({ - matchWith: 'workspaceMemberOnly', - participants: - tobeRematchedParticipants as ParticipantWorkspaceEntity[], - objectMetadataName, - workspaceId, - }); - }, - ); + await this.matchParticipants({ + matchWith: 'workspaceMemberOnly', + participants: tobeRematchedParticipants as ParticipantWorkspaceEntity[], + objectMetadataName, + workspaceId, + }); + }, authContext); } public async matchParticipantsForPeople({ @@ -249,56 +245,53 @@ export class MatchParticipantService< }: MatchParticipantsForPeopleArgs) { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const participantRepository = await this.getParticipantRepository( - workspaceId, - objectMetadataName, - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const participantRepository = await this.getParticipantRepository( + workspaceId, + objectMetadataName, + ); - let participantsMatchingPersonEmails: ParticipantWorkspaceEntity[] = []; - let participantsMatchingPersonId: ParticipantWorkspaceEntity[] = []; + let participantsMatchingPersonEmails: ParticipantWorkspaceEntity[] = []; + let participantsMatchingPersonId: ParticipantWorkspaceEntity[] = []; - if (participantMatching.personIds.length > 0) { - participantsMatchingPersonId = (await participantRepository.find({ - where: { - personId: In(participantMatching.personIds), - }, - })) as ParticipantWorkspaceEntity[]; - } - - if (participantMatching.personEmails.length > 0) { - participantsMatchingPersonEmails = (await participantRepository.find({ - where: { - handle: In(participantMatching.personEmails), - }, - })) as ParticipantWorkspaceEntity[]; - } - - const uniqueParticipants = [ - ...new Set([ - ...participantsMatchingPersonId, - ...participantsMatchingPersonEmails, - ]), - ]; - - const tobeRematchedParticipants = uniqueParticipants.map( - (participant) => { - return { - ...participant, - personId: null, - }; + if (participantMatching.personIds.length > 0) { + participantsMatchingPersonId = (await participantRepository.find({ + where: { + personId: In(participantMatching.personIds), }, - ); + })) as ParticipantWorkspaceEntity[]; + } - await this.matchParticipants({ - matchWith: 'personOnly', - participants: tobeRematchedParticipants, - objectMetadataName, - workspaceId, - }); - }, - ); + if (participantMatching.personEmails.length > 0) { + participantsMatchingPersonEmails = (await participantRepository.find({ + where: { + handle: In(participantMatching.personEmails), + }, + })) as ParticipantWorkspaceEntity[]; + } + + const uniqueParticipants = [ + ...new Set([ + ...participantsMatchingPersonId, + ...participantsMatchingPersonEmails, + ]), + ]; + + const tobeRematchedParticipants = uniqueParticipants.map( + (participant) => { + return { + ...participant, + personId: null, + }; + }, + ); + + await this.matchParticipants({ + matchWith: 'personOnly', + participants: tobeRematchedParticipants, + objectMetadataName, + workspaceId, + }); + }, authContext); } } 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 b3839d5fea6..c2858b5fe34 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 @@ -36,132 +36,129 @@ export class BlocklistItemDeleteMessagesJob { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const blocklistItemIds = data.events.map( - (eventPayload) => eventPayload.recordId, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const blocklistItemIds = data.events.map( + (eventPayload) => eventPayload.recordId, + ); + + const blocklistRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'blocklist', ); - const blocklistRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'blocklist', - ); + const blocklist = await blocklistRepository.find({ + where: { + id: Any(blocklistItemIds), + }, + }); - const blocklist = await blocklistRepository.find({ - where: { - id: Any(blocklistItemIds), + const handlesToDeleteByWorkspaceMemberIdMap = blocklist.reduce( + (acc, blocklistItem) => { + const { handle, workspaceMemberId } = blocklistItem; + + if (!acc.has(workspaceMemberId)) { + acc.set(workspaceMemberId, []); + } + + if (!isDefined(handle)) { + return acc; + } + + acc.get(workspaceMemberId)?.push(handle); + + return acc; + }, + new Map(), + ); + + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); + + const messageChannelMessageAssociationRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannelMessageAssociation', + ); + + for (const workspaceMemberId of handlesToDeleteByWorkspaceMemberIdMap.keys()) { + const handles = + handlesToDeleteByWorkspaceMemberIdMap.get(workspaceMemberId); + + if (!handles) { + continue; + } + + const rolesToDelete = [ + MessageParticipantRole.FROM, + MessageParticipantRole.TO, + ] as const; + + const messageChannels = await messageChannelRepository.find({ + select: { + id: true, + handle: true, + connectedAccount: { + handleAliases: true, + }, }, + where: { + connectedAccount: { + accountOwnerId: workspaceMemberId, + }, + }, + relations: ['connectedAccount'], }); - const handlesToDeleteByWorkspaceMemberIdMap = blocklist.reduce( - (acc, blocklistItem) => { - const { handle, workspaceMemberId } = blocklistItem; + for (const messageChannel of messageChannels) { + const messageChannelHandles = [messageChannel.handle]; - if (!acc.has(workspaceMemberId)) { - acc.set(workspaceMemberId, []); - } + if (messageChannel.connectedAccount.handleAliases) { + messageChannelHandles.push( + ...messageChannel.connectedAccount.handleAliases.split(','), + ); + } - if (!isDefined(handle)) { - return acc; - } + const handleConditions = handles.map((handle) => { + const isHandleDomain = handle.startsWith('@'); - acc.get(workspaceMemberId)?.push(handle); + return isHandleDomain + ? { + handle: And( + Or(ILike(`%${handle}`), ILike(`%.${handle.slice(1)}`)), + Not(In(messageChannelHandles)), + ), + role: In(rolesToDelete), + } + : { handle, role: In(rolesToDelete) }; + }); - return acc; - }, - new Map(), - ); + const messageChannelMessageAssociationsToDelete = + await messageChannelMessageAssociationRepository.find({ + where: { + messageChannelId: messageChannel.id, + message: { + messageParticipants: handleConditions, + }, + }, + }); - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - ); - - const messageChannelMessageAssociationRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannelMessageAssociation', - ); - - for (const workspaceMemberId of handlesToDeleteByWorkspaceMemberIdMap.keys()) { - const handles = - handlesToDeleteByWorkspaceMemberIdMap.get(workspaceMemberId); - - if (!handles) { + if (messageChannelMessageAssociationsToDelete.length === 0) { continue; } - const rolesToDelete = [ - MessageParticipantRole.FROM, - MessageParticipantRole.TO, - ] as const; - - const messageChannels = await messageChannelRepository.find({ - select: { - id: true, - handle: true, - connectedAccount: { - handleAliases: true, - }, - }, - where: { - connectedAccount: { - accountOwnerId: workspaceMemberId, - }, - }, - relations: ['connectedAccount'], - }); - - for (const messageChannel of messageChannels) { - const messageChannelHandles = [messageChannel.handle]; - - if (messageChannel.connectedAccount.handleAliases) { - messageChannelHandles.push( - ...messageChannel.connectedAccount.handleAliases.split(','), - ); - } - - const handleConditions = handles.map((handle) => { - const isHandleDomain = handle.startsWith('@'); - - return isHandleDomain - ? { - handle: And( - Or(ILike(`%${handle}`), ILike(`%.${handle.slice(1)}`)), - Not(In(messageChannelHandles)), - ), - role: In(rolesToDelete), - } - : { handle, role: In(rolesToDelete) }; - }); - - const messageChannelMessageAssociationsToDelete = - await messageChannelMessageAssociationRepository.find({ - where: { - messageChannelId: messageChannel.id, - message: { - messageParticipants: handleConditions, - }, - }, - }); - - if (messageChannelMessageAssociationsToDelete.length === 0) { - continue; - } - - await messageChannelMessageAssociationRepository.delete( - messageChannelMessageAssociationsToDelete.map(({ id }) => id), - ); - } + await messageChannelMessageAssociationRepository.delete( + messageChannelMessageAssociationsToDelete.map(({ id }) => id), + ); } + } - await this.threadCleanerService.cleanOrphanMessagesAndThreads( - workspaceId, - ); - }, - ); + await this.threadCleanerService.cleanOrphanMessagesAndThreads( + workspaceId, + ); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/messaging/blocklist-manager/jobs/messaging-blocklist-reimport-messages.job.ts b/packages/twenty-server/src/modules/messaging/blocklist-manager/jobs/messaging-blocklist-reimport-messages.job.ts index 72246a5c2db..a2b6d1c9860 100644 --- a/packages/twenty-server/src/modules/messaging/blocklist-manager/jobs/messaging-blocklist-reimport-messages.job.ts +++ b/packages/twenty-server/src/modules/messaging/blocklist-manager/jobs/messaging-blocklist-reimport-messages.job.ts @@ -36,37 +36,32 @@ export class BlocklistReimportMessagesJob { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); - for (const eventPayload of data.events) { - const workspaceMemberId = - eventPayload.properties.before.workspaceMemberId; + for (const eventPayload of data.events) { + const workspaceMemberId = + eventPayload.properties.before.workspaceMemberId; - const messageChannels = await messageChannelRepository.find({ - select: ['id'], - where: { - connectedAccount: { - accountOwnerId: workspaceMemberId, - }, - syncStage: Not( - MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING, - ), + const messageChannels = await messageChannelRepository.find({ + select: ['id'], + where: { + connectedAccount: { + accountOwnerId: workspaceMemberId, }, - }); + syncStage: Not(MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING), + }, + }); - await this.messagingChannelSyncStatusService.resetAndMarkAsMessagesListFetchPending( - messageChannels.map((messageChannel) => messageChannel.id), - workspaceId, - ); - } - }, - ); + await this.messagingChannelSyncStatusService.resetAndMarkAsMessagesListFetchPending( + messageChannels.map((messageChannel) => messageChannel.id), + workspaceId, + ); + } + }, authContext); } } diff --git a/packages/twenty-server/src/modules/messaging/common/query-hooks/message/apply-messages-visibility-restrictions.service.spec.ts b/packages/twenty-server/src/modules/messaging/common/query-hooks/message/apply-messages-visibility-restrictions.service.spec.ts index f88b5f75a30..3392eb1629f 100644 --- a/packages/twenty-server/src/modules/messaging/common/query-hooks/message/apply-messages-visibility-restrictions.service.spec.ts +++ b/packages/twenty-server/src/modules/messaging/common/query-hooks/message/apply-messages-visibility-restrictions.service.spec.ts @@ -56,7 +56,7 @@ describe('ApplyMessagesVisibilityRestrictionsService', () => { }), executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), }; beforeEach(async () => { diff --git a/packages/twenty-server/src/modules/messaging/common/query-hooks/message/apply-messages-visibility-restrictions.service.ts b/packages/twenty-server/src/modules/messaging/common/query-hooks/message/apply-messages-visibility-restrictions.service.ts index c098c50d741..c5e4551cf8f 100644 --- a/packages/twenty-server/src/modules/messaging/common/query-hooks/message/apply-messages-visibility-restrictions.service.ts +++ b/packages/twenty-server/src/modules/messaging/common/query-hooks/message/apply-messages-visibility-restrictions.service.ts @@ -28,7 +28,6 @@ export class ApplyMessagesVisibilityRestrictionsService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const messageChannelMessageAssociationRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -128,6 +127,7 @@ export class ApplyMessagesVisibilityRestrictionsService { return messages; }, + authContext, ); } } diff --git a/packages/twenty-server/src/modules/messaging/common/query-hooks/message/message-channel-update-one.pre-query.hook.ts b/packages/twenty-server/src/modules/messaging/common/query-hooks/message/message-channel-update-one.pre-query.hook.ts index 3eeb1674900..7f4d107c9b4 100644 --- a/packages/twenty-server/src/modules/messaging/common/query-hooks/message/message-channel-update-one.pre-query.hook.ts +++ b/packages/twenty-server/src/modules/messaging/common/query-hooks/message/message-channel-update-one.pre-query.hook.ts @@ -56,7 +56,6 @@ export class MessageChannelUpdateOnePreQueryHook const systemAuthContext = buildSystemAuthContext(workspace.id); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - systemAuthContext, async () => { const messageChannelRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -144,6 +143,7 @@ export class MessageChannelUpdateOnePreQueryHook return payload; }, + systemAuthContext, ); } } diff --git a/packages/twenty-server/src/modules/messaging/common/services/message-channel-sync-status.service.ts b/packages/twenty-server/src/modules/messaging/common/services/message-channel-sync-status.service.ts index ff9a29e4b2d..81d89232609 100644 --- a/packages/twenty-server/src/modules/messaging/common/services/message-channel-sync-status.service.ts +++ b/packages/twenty-server/src/modules/messaging/common/services/message-channel-sync-status.service.ts @@ -44,21 +44,18 @@ export class MessageChannelSyncStatusService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); - await messageChannelRepository.update(messageChannelIds, { - syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING, - ...(!preserveSyncStageStartedAt ? { syncStageStartedAt: null } : {}), - }); - }, - ); + await messageChannelRepository.update(messageChannelIds, { + syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING, + ...(!preserveSyncStageStartedAt ? { syncStageStartedAt: null } : {}), + }); + }, authContext); } public async markAsMessagesImportPending( @@ -72,21 +69,18 @@ export class MessageChannelSyncStatusService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); - await messageChannelRepository.update(messageChannelIds, { - syncStage: MessageChannelSyncStage.MESSAGES_IMPORT_PENDING, - ...(!preserveSyncStageStartedAt ? { syncStageStartedAt: null } : {}), - }); - }, - ); + await messageChannelRepository.update(messageChannelIds, { + syncStage: MessageChannelSyncStage.MESSAGES_IMPORT_PENDING, + ...(!preserveSyncStageStartedAt ? { syncStageStartedAt: null } : {}), + }); + }, authContext); } public async resetAndMarkAsMessagesListFetchPending( @@ -105,37 +99,34 @@ export class MessageChannelSyncStatusService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - ); - - const messageFolderRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageFolder', - ); - - await messageChannelRepository.update(messageChannelIds, { - syncCursor: '', - syncStageStartedAt: null, - throttleFailureCount: 0, - pendingGroupEmailsAction: MessageChannelPendingGroupEmailsAction.NONE, - }); - - await messageFolderRepository.update( - { messageChannelId: In(messageChannelIds) }, - { - syncCursor: '', - pendingSyncAction: MessageFolderPendingSyncAction.NONE, - }, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', ); - }, - ); + + const messageFolderRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageFolder', + ); + + await messageChannelRepository.update(messageChannelIds, { + syncCursor: '', + syncStageStartedAt: null, + throttleFailureCount: 0, + pendingGroupEmailsAction: MessageChannelPendingGroupEmailsAction.NONE, + }); + + await messageFolderRepository.update( + { messageChannelId: In(messageChannelIds) }, + { + syncCursor: '', + pendingSyncAction: MessageFolderPendingSyncAction.NONE, + }, + ); + }, authContext); await this.markAsMessagesListFetchPending(messageChannelIds, workspaceId); } @@ -150,20 +141,17 @@ export class MessageChannelSyncStatusService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); - await messageChannelRepository.update(messageChannelIds, { - syncStageStartedAt: null, - }); - }, - ); + await messageChannelRepository.update(messageChannelIds, { + syncStageStartedAt: null, + }); + }, authContext); } public async markAsMessagesListFetchScheduled( @@ -176,22 +164,19 @@ export class MessageChannelSyncStatusService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); - await messageChannelRepository.update(messageChannelIds, { - syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_SCHEDULED, - syncStatus: MessageChannelSyncStatus.ONGOING, - syncStageStartedAt: new Date().toISOString(), - }); - }, - ); + await messageChannelRepository.update(messageChannelIds, { + syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_SCHEDULED, + syncStatus: MessageChannelSyncStatus.ONGOING, + syncStageStartedAt: new Date().toISOString(), + }); + }, authContext); } public async markAsMessagesListFetchOngoing( @@ -204,21 +189,18 @@ export class MessageChannelSyncStatusService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); - await messageChannelRepository.update(messageChannelIds, { - syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_ONGOING, - syncStatus: MessageChannelSyncStatus.ONGOING, - }); - }, - ); + await messageChannelRepository.update(messageChannelIds, { + syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_ONGOING, + syncStatus: MessageChannelSyncStatus.ONGOING, + }); + }, authContext); } public async markAsCompletedAndMarkAsMessagesListFetchPending( @@ -231,24 +213,21 @@ export class MessageChannelSyncStatusService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); - await messageChannelRepository.update(messageChannelIds, { - syncStatus: MessageChannelSyncStatus.ACTIVE, - syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING, - throttleFailureCount: 0, - syncStageStartedAt: null, - syncedAt: new Date().toISOString(), - }); - }, - ); + await messageChannelRepository.update(messageChannelIds, { + syncStatus: MessageChannelSyncStatus.ACTIVE, + syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING, + throttleFailureCount: 0, + syncStageStartedAt: null, + syncedAt: new Date().toISOString(), + }); + }, authContext); await this.metricsService.batchIncrementCounter({ key: MetricsKeys.MessageChannelSyncJobActive, @@ -266,20 +245,17 @@ export class MessageChannelSyncStatusService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); - await messageChannelRepository.update(messageChannelIds, { - syncStage: MessageChannelSyncStage.MESSAGES_IMPORT_SCHEDULED, - }); - }, - ); + await messageChannelRepository.update(messageChannelIds, { + syncStage: MessageChannelSyncStage.MESSAGES_IMPORT_SCHEDULED, + }); + }, authContext); } public async markAsMessagesImportOngoing( @@ -292,21 +268,18 @@ export class MessageChannelSyncStatusService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); - await messageChannelRepository.update(messageChannelIds, { - syncStage: MessageChannelSyncStage.MESSAGES_IMPORT_ONGOING, - syncStageStartedAt: new Date().toISOString(), - }); - }, - ); + await messageChannelRepository.update(messageChannelIds, { + syncStage: MessageChannelSyncStage.MESSAGES_IMPORT_ONGOING, + syncStageStartedAt: new Date().toISOString(), + }); + }, authContext); } public async markAsFailed( @@ -322,64 +295,59 @@ export class MessageChannelSyncStatusService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); + + await messageChannelRepository.update(messageChannelIds, { + syncStage: MessageChannelSyncStage.FAILED, + syncStatus: syncStatus, + }); + + const metricsKey = + syncStatus === MessageChannelSyncStatus.FAILED_INSUFFICIENT_PERMISSIONS + ? MetricsKeys.MessageChannelSyncJobFailedInsufficientPermissions + : MetricsKeys.MessageChannelSyncJobFailedUnknown; + + await this.metricsService.batchIncrementCounter({ + key: metricsKey, + eventIds: messageChannelIds, + }); + + if ( + syncStatus === MessageChannelSyncStatus.FAILED_INSUFFICIENT_PERMISSIONS + ) { + const connectedAccountRepository = + await this.globalWorkspaceOrmManager.getRepository( workspaceId, - 'messageChannel', + 'connectedAccount', ); - await messageChannelRepository.update(messageChannelIds, { - syncStage: MessageChannelSyncStage.FAILED, - syncStatus: syncStatus, + const messageChannels = await messageChannelRepository.find({ + select: ['id', 'connectedAccountId'], + where: { id: Any(messageChannelIds) }, }); - const metricsKey = - syncStatus === - MessageChannelSyncStatus.FAILED_INSUFFICIENT_PERMISSIONS - ? MetricsKeys.MessageChannelSyncJobFailedInsufficientPermissions - : MetricsKeys.MessageChannelSyncJobFailedUnknown; + const connectedAccountIds = messageChannels.map( + (messageChannel) => messageChannel.connectedAccountId, + ); - await this.metricsService.batchIncrementCounter({ - key: metricsKey, - eventIds: messageChannelIds, - }); + await connectedAccountRepository.update( + { id: Any(connectedAccountIds) }, + { + authFailedAt: new Date(), + }, + ); - if ( - syncStatus === - MessageChannelSyncStatus.FAILED_INSUFFICIENT_PERMISSIONS - ) { - const connectedAccountRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'connectedAccount', - ); - - const messageChannels = await messageChannelRepository.find({ - select: ['id', 'connectedAccountId'], - where: { id: Any(messageChannelIds) }, - }); - - const connectedAccountIds = messageChannels.map( - (messageChannel) => messageChannel.connectedAccountId, - ); - - await connectedAccountRepository.update( - { id: Any(connectedAccountIds) }, - { - authFailedAt: new Date(), - }, - ); - - await this.addToAccountsToReconnect( - messageChannels.map((messageChannel) => messageChannel.id), - workspaceId, - ); - } - }, - ); + await this.addToAccountsToReconnect( + messageChannels.map((messageChannel) => messageChannel.id), + workspaceId, + ); + } + }, authContext); } private async addToAccountsToReconnect( diff --git a/packages/twenty-server/src/modules/messaging/message-cleaner/commands/messaging-reset-channel.command.ts b/packages/twenty-server/src/modules/messaging/message-cleaner/commands/messaging-reset-channel.command.ts index 62f2bb5ad16..152a827828e 100644 --- a/packages/twenty-server/src/modules/messaging/message-cleaner/commands/messaging-reset-channel.command.ts +++ b/packages/twenty-server/src/modules/messaging/message-cleaner/commands/messaging-reset-channel.command.ts @@ -38,52 +38,49 @@ export class MessagingResetChannelCommand extends CommandRunner { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - ); - - this.logger.log( - `No message channel ID provided, resetting all message channels in workspace ${workspaceId}`, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', ); - const messageChannels = await messageChannelRepository.find({ - where: { - ...(isDefined(messageChannelId) ? { id: messageChannelId } : {}), - }, - }); + this.logger.log( + `No message channel ID provided, resetting all message channels in workspace ${workspaceId}`, + ); - if (messageChannels.length === 0) { - this.logger.log( - `No message channels found in workspace ${workspaceId}`, - ); - - return; - } + const messageChannels = await messageChannelRepository.find({ + where: { + ...(isDefined(messageChannelId) ? { id: messageChannelId } : {}), + }, + }); + if (messageChannels.length === 0) { this.logger.log( - `Found ${messageChannels.length} message channels to reset`, + `No message channels found in workspace ${workspaceId}`, ); - for (const messageChannel of messageChannels) { - await this.messagingChannelSyncStatusService.resetAndMarkAsMessagesListFetchPending( - [messageChannel.id], - workspaceId, - ); - await this.messagingMessageCleanerService.cleanOrphanMessagesAndThreads( - workspaceId, - ); - } + return; + } - this.logger.log( - `Successfully reset all ${messageChannels.length} message channels in workspace ${workspaceId}`, + this.logger.log( + `Found ${messageChannels.length} message channels to reset`, + ); + + for (const messageChannel of messageChannels) { + await this.messagingChannelSyncStatusService.resetAndMarkAsMessagesListFetchPending( + [messageChannel.id], + workspaceId, ); - }, - ); + await this.messagingMessageCleanerService.cleanOrphanMessagesAndThreads( + workspaceId, + ); + } + + this.logger.log( + `Successfully reset all ${messageChannels.length} message channels in workspace ${workspaceId}`, + ); + }, authContext); } @Option({ diff --git a/packages/twenty-server/src/modules/messaging/message-cleaner/services/messaging-message-cleaner.service.ts b/packages/twenty-server/src/modules/messaging/message-cleaner/services/messaging-message-cleaner.service.ts index 39661ad09d0..8a22c85db35 100644 --- a/packages/twenty-server/src/modules/messaging/message-cleaner/services/messaging-message-cleaner.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-cleaner/services/messaging-message-cleaner.service.ts @@ -29,197 +29,191 @@ export class MessagingMessageCleanerService { }) { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'message', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'message', + ); - const messageChannelMessageAssociationRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannelMessageAssociation', - ); + const messageChannelMessageAssociationRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannelMessageAssociation', + ); - const messageThreadRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageThread', - ); + const messageThreadRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageThread', + ); - const messageExternalIdsChunks = chunk(messageExternalIds, 500); + const messageExternalIdsChunks = chunk(messageExternalIds, 500); - for (const messageExternalIdsChunk of messageExternalIdsChunks) { - const messageChannelMessageAssociationsToDelete = - await messageChannelMessageAssociationRepository.find({ - where: { - messageExternalId: In(messageExternalIdsChunk), - messageChannelId, - }, - }); - - if (messageChannelMessageAssociationsToDelete.length <= 0) { - continue; - } - - await messageChannelMessageAssociationRepository.delete( - messageChannelMessageAssociationsToDelete.map(({ id }) => id), - ); - - this.logger.log( - `WorkspaceId: ${workspaceId} Deleting ${messageChannelMessageAssociationsToDelete.length} message channel message associations`, - ); - - const orphanMessages = await messageRepository.find({ + for (const messageExternalIdsChunk of messageExternalIdsChunks) { + const messageChannelMessageAssociationsToDelete = + await messageChannelMessageAssociationRepository.find({ where: { - id: In( - messageChannelMessageAssociationsToDelete.map( - ({ messageId }) => messageId, - ), - ), - messageChannelMessageAssociations: { - id: IsNull(), - }, + messageExternalId: In(messageExternalIdsChunk), + messageChannelId, }, }); - if (orphanMessages.length <= 0) { - continue; - } - - this.logger.log( - `WorkspaceId: ${workspaceId} Deleting ${orphanMessages.length} orphan messages`, - ); - - await messageRepository.delete(orphanMessages.map(({ id }) => id)); - - const orphanMessageThreads = await messageThreadRepository.find({ - where: { - id: In( - orphanMessages.map(({ messageThreadId }) => messageThreadId), - ), - messages: { - id: IsNull(), - }, - }, - }); - - if (orphanMessageThreads.length <= 0) { - continue; - } - - this.logger.log( - `WorkspaceId: ${workspaceId} Deleting ${orphanMessageThreads.length} orphan message threads`, - ); - - await messageThreadRepository.delete( - orphanMessageThreads.map(({ id }) => id), - ); + if (messageChannelMessageAssociationsToDelete.length <= 0) { + continue; } - }, - ); + + await messageChannelMessageAssociationRepository.delete( + messageChannelMessageAssociationsToDelete.map(({ id }) => id), + ); + + this.logger.log( + `WorkspaceId: ${workspaceId} Deleting ${messageChannelMessageAssociationsToDelete.length} message channel message associations`, + ); + + const orphanMessages = await messageRepository.find({ + where: { + id: In( + messageChannelMessageAssociationsToDelete.map( + ({ messageId }) => messageId, + ), + ), + messageChannelMessageAssociations: { + id: IsNull(), + }, + }, + }); + + if (orphanMessages.length <= 0) { + continue; + } + + this.logger.log( + `WorkspaceId: ${workspaceId} Deleting ${orphanMessages.length} orphan messages`, + ); + + await messageRepository.delete(orphanMessages.map(({ id }) => id)); + + const orphanMessageThreads = await messageThreadRepository.find({ + where: { + id: In( + orphanMessages.map(({ messageThreadId }) => messageThreadId), + ), + messages: { + id: IsNull(), + }, + }, + }); + + if (orphanMessageThreads.length <= 0) { + continue; + } + + this.logger.log( + `WorkspaceId: ${workspaceId} Deleting ${orphanMessageThreads.length} orphan message threads`, + ); + + await messageThreadRepository.delete( + orphanMessageThreads.map(({ id }) => id), + ); + } + }, authContext); } public async cleanOrphanMessagesAndThreads(workspaceId: string) { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageThreadRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageThread', - ); - - const messageRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'message', - ); - - const workspaceDataSource = - await this.globalWorkspaceOrmManager.getGlobalWorkspaceDataSource(); - - await workspaceDataSource.transaction( - async (transactionManager: WorkspaceEntityManager) => { - await deleteUsingPagination( - workspaceId, - 500, - async ( - limit: number, - offset: number, - _workspaceId: string, - transactionManager: WorkspaceEntityManager, - ) => { - const nonAssociatedMessages = await messageRepository.find( - { - where: { - messageChannelMessageAssociations: { - id: IsNull(), - }, - }, - take: limit, - skip: offset, - relations: ['messageChannelMessageAssociations'], - }, - transactionManager, - ); - - return nonAssociatedMessages.map(({ id }) => id); - }, - async ( - ids: string[], - workspaceId: string, - transactionManager?: WorkspaceEntityManager, - ) => { - this.logger.log( - `WorkspaceId: ${workspaceId} Deleting ${ids.length} messages from message cleaner`, - ); - await messageRepository.delete(ids, transactionManager); - }, - transactionManager, - ); - - await deleteUsingPagination( - workspaceId, - 500, - async ( - limit: number, - offset: number, - _workspaceId: string, - transactionManager?: WorkspaceEntityManager, - ) => { - const orphanThreads = await messageThreadRepository.find( - { - where: { - messages: { - id: IsNull(), - }, - }, - take: limit, - skip: offset, - }, - transactionManager, - ); - - return orphanThreads.map(({ id }) => id); - }, - async ( - ids: string[], - _workspaceId: string, - transactionManager?: WorkspaceEntityManager, - ) => { - await messageThreadRepository.delete(ids, transactionManager); - }, - transactionManager, - ); - }, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageThreadRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageThread', ); - }, - ); + + const messageRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'message', + ); + + const workspaceDataSource = + await this.globalWorkspaceOrmManager.getGlobalWorkspaceDataSource(); + + await workspaceDataSource.transaction( + async (transactionManager: WorkspaceEntityManager) => { + await deleteUsingPagination( + workspaceId, + 500, + async ( + limit: number, + offset: number, + _workspaceId: string, + transactionManager: WorkspaceEntityManager, + ) => { + const nonAssociatedMessages = await messageRepository.find( + { + where: { + messageChannelMessageAssociations: { + id: IsNull(), + }, + }, + take: limit, + skip: offset, + relations: ['messageChannelMessageAssociations'], + }, + transactionManager, + ); + + return nonAssociatedMessages.map(({ id }) => id); + }, + async ( + ids: string[], + workspaceId: string, + transactionManager?: WorkspaceEntityManager, + ) => { + this.logger.log( + `WorkspaceId: ${workspaceId} Deleting ${ids.length} messages from message cleaner`, + ); + await messageRepository.delete(ids, transactionManager); + }, + transactionManager, + ); + + await deleteUsingPagination( + workspaceId, + 500, + async ( + limit: number, + offset: number, + _workspaceId: string, + transactionManager?: WorkspaceEntityManager, + ) => { + const orphanThreads = await messageThreadRepository.find( + { + where: { + messages: { + id: IsNull(), + }, + }, + take: limit, + skip: offset, + }, + transactionManager, + ); + + return orphanThreads.map(({ id }) => id); + }, + async ( + ids: string[], + _workspaceId: string, + transactionManager?: WorkspaceEntityManager, + ) => { + await messageThreadRepository.delete(ids, transactionManager); + }, + transactionManager, + ); + }, + ); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/messaging/message-folder-manager/services/sync-message-folders.service.spec.ts b/packages/twenty-server/src/modules/messaging/message-folder-manager/services/sync-message-folders.service.spec.ts index 1d3ae5cf13c..131d9cc0b23 100644 --- a/packages/twenty-server/src/modules/messaging/message-folder-manager/services/sync-message-folders.service.spec.ts +++ b/packages/twenty-server/src/modules/messaging/message-folder-manager/services/sync-message-folders.service.spec.ts @@ -126,7 +126,9 @@ describe('SyncMessageFoldersService', () => { useValue: { executeInWorkspaceContext: jest .fn() - .mockImplementation((_, callback) => callback()), + .mockImplementation((callback: () => any, _authContext?: any) => + callback(), + ), getRepository: jest.fn().mockResolvedValue(mockRepository), getDataSourceForWorkspace: jest .fn() diff --git a/packages/twenty-server/src/modules/messaging/message-folder-manager/services/sync-message-folders.service.ts b/packages/twenty-server/src/modules/messaging/message-folder-manager/services/sync-message-folders.service.ts index 9eac1f436a3..1dd442bd7ff 100644 --- a/packages/twenty-server/src/modules/messaging/message-folder-manager/services/sync-message-folders.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-folder-manager/services/sync-message-folders.service.ts @@ -133,7 +133,6 @@ export class SyncMessageFoldersService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const messageFolderRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -188,6 +187,7 @@ export class SyncMessageFoldersService { }, ); }, + authContext, ); } } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/commands/messaging-trigger-message-list-fetch.command.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/commands/messaging-trigger-message-list-fetch.command.ts index 10cf4ad08d2..74aa8cbee62 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/commands/messaging-trigger-message-list-fetch.command.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/commands/messaging-trigger-message-list-fetch.command.ts @@ -51,63 +51,60 @@ export class MessagingTriggerMessageListFetchCommand extends CommandRunner { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); + + const whereCondition: Record = { + isSyncEnabled: true, + syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING, + }; + + if (messageChannelId) { + whereCondition.id = messageChannelId; + } + + const messageChannels = + await messageChannelRepository.find(whereCondition); + + if (messageChannels.length === 0) { + this.logger.warn( + 'No message channels found with MESSAGE_LIST_FETCH_PENDING status', + ); + + return; + } + + this.logger.log( + `Found ${messageChannels.length} message channel(s) to process`, + ); + + for (const messageChannel of messageChannels) { + await messageChannelRepository.update(messageChannel.id, { + syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_SCHEDULED, + syncStageStartedAt: new Date().toISOString(), + }); + + await this.messageQueueService.add( + MessagingMessageListFetchJob.name, + { + messageChannelId: messageChannel.id, workspaceId, - 'messageChannel', - ); - - const whereCondition: Record = { - isSyncEnabled: true, - syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING, - }; - - if (messageChannelId) { - whereCondition.id = messageChannelId; - } - - const messageChannels = - await messageChannelRepository.find(whereCondition); - - if (messageChannels.length === 0) { - this.logger.warn( - 'No message channels found with MESSAGE_LIST_FETCH_PENDING status', - ); - - return; - } - - this.logger.log( - `Found ${messageChannels.length} message channel(s) to process`, + }, ); - for (const messageChannel of messageChannels) { - await messageChannelRepository.update(messageChannel.id, { - syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_SCHEDULED, - syncStageStartedAt: new Date().toISOString(), - }); - - await this.messageQueueService.add( - MessagingMessageListFetchJob.name, - { - messageChannelId: messageChannel.id, - workspaceId, - }, - ); - - this.logger.log( - `Triggered fetch for message channel ${messageChannel.id}`, - ); - } - this.logger.log( - `Successfully triggered ${messageChannels.length} message list fetch job(s)`, + `Triggered fetch for message channel ${messageChannel.id}`, ); - }, - ); + } + + this.logger.log( + `Successfully triggered ${messageChannels.length} message list fetch job(s)`, + ); + }, authContext); } @Option({ 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 632ef76cd00..da5a4e7c47f 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 @@ -48,82 +48,79 @@ export class MessagingMessageListFetchJob { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); - const messageChannel = await messageChannelRepository.findOne({ - where: { - id: messageChannelId, - }, - relations: ['connectedAccount', 'messageFolders'], + const messageChannel = await messageChannelRepository.findOne({ + where: { + id: messageChannelId, + }, + relations: ['connectedAccount', 'messageFolders'], + }); + + if (!messageChannel) { + await this.messagingMonitoringService.track({ + eventName: 'message_list_fetch_job.error.message_channel_not_found', + messageChannelId, + workspaceId, }); - if (!messageChannel) { - await this.messagingMonitoringService.track({ - eventName: 'message_list_fetch_job.error.message_channel_not_found', - messageChannelId, - workspaceId, - }); + return; + } - return; - } + if ( + messageChannel.syncStage !== + MessageChannelSyncStage.MESSAGE_LIST_FETCH_SCHEDULED + ) { + return; + } + try { if ( - messageChannel.syncStage !== - MessageChannelSyncStage.MESSAGE_LIST_FETCH_SCHEDULED + isThrottled( + messageChannel.syncStageStartedAt, + messageChannel.throttleFailureCount, + ) ) { + await this.messageChannelSyncStatusService.markAsMessagesListFetchPending( + [messageChannel.id], + workspaceId, + true, + ); + return; } - try { - if ( - isThrottled( - messageChannel.syncStageStartedAt, - messageChannel.throttleFailureCount, - ) - ) { - await this.messageChannelSyncStatusService.markAsMessagesListFetchPending( - [messageChannel.id], - workspaceId, - true, - ); + await this.messagingMonitoringService.track({ + eventName: 'message_list_fetch.started', + workspaceId, + connectedAccountId: messageChannel.connectedAccount.id, + messageChannelId: messageChannel.id, + }); - return; - } + await this.messagingMessageListFetchService.processMessageListFetch( + messageChannel, + workspaceId, + ); - await this.messagingMonitoringService.track({ - eventName: 'message_list_fetch.started', - workspaceId, - connectedAccountId: messageChannel.connectedAccount.id, - messageChannelId: messageChannel.id, - }); - - await this.messagingMessageListFetchService.processMessageListFetch( - messageChannel, - workspaceId, - ); - - await this.messagingMonitoringService.track({ - eventName: 'message_list_fetch.completed', - workspaceId, - connectedAccountId: messageChannel.connectedAccount.id, - messageChannelId: messageChannel.id, - }); - } catch (error) { - await this.messageImportErrorHandlerService.handleDriverException( - error, - MessageImportSyncStep.MESSAGE_LIST_FETCH, - messageChannel, - workspaceId, - ); - } - }, - ); + await this.messagingMonitoringService.track({ + eventName: 'message_list_fetch.completed', + workspaceId, + connectedAccountId: messageChannel.connectedAccount.id, + messageChannelId: messageChannel.id, + }); + } catch (error) { + await this.messageImportErrorHandlerService.handleDriverException( + error, + MessageImportSyncStep.MESSAGE_LIST_FETCH, + messageChannel, + workspaceId, + ); + } + }, authContext); } } 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 fd9bce545a4..d947f0f0e78 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 @@ -43,64 +43,61 @@ export class MessagingMessagesImportJob { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); - const messageChannel = await messageChannelRepository.findOne({ - where: { - id: messageChannelId, - }, - relations: ['connectedAccount'], + const messageChannel = await messageChannelRepository.findOne({ + where: { + id: messageChannelId, + }, + relations: ['connectedAccount'], + }); + + if (!messageChannel) { + await this.messagingMonitoringService.track({ + eventName: 'messages_import.error.message_channel_not_found', + messageChannelId, + workspaceId, }); - if (!messageChannel) { - await this.messagingMonitoringService.track({ - eventName: 'messages_import.error.message_channel_not_found', - messageChannelId, - workspaceId, - }); + return; + } - return; - } + if (!messageChannel?.isSyncEnabled) { + return; + } - if (!messageChannel?.isSyncEnabled) { - return; - } + if ( + messageChannel.syncStage !== + MessageChannelSyncStage.MESSAGES_IMPORT_SCHEDULED + ) { + return; + } - if ( - messageChannel.syncStage !== - MessageChannelSyncStage.MESSAGES_IMPORT_SCHEDULED - ) { - return; - } - - if ( - isThrottled( - messageChannel.syncStageStartedAt, - messageChannel.throttleFailureCount, - ) - ) { - await this.messageChannelSyncStatusService.markAsMessagesImportPending( - [messageChannel.id], - workspaceId, - true, - ); - - return; - } - - await this.messagingMessagesImportService.processMessageBatchImport( - messageChannel, - messageChannel.connectedAccount, + if ( + isThrottled( + messageChannel.syncStageStartedAt, + messageChannel.throttleFailureCount, + ) + ) { + await this.messageChannelSyncStatusService.markAsMessagesImportPending( + [messageChannel.id], workspaceId, + true, ); - }, - ); + + return; + } + + await this.messagingMessagesImportService.processMessageBatchImport( + messageChannel, + messageChannel.connectedAccount, + workspaceId, + ); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-ongoing-stale.job.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-ongoing-stale.job.ts index f013a3942db..701acd366e5 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-ongoing-stale.job.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-ongoing-stale.job.ts @@ -35,63 +35,60 @@ export class MessagingOngoingStaleJob { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); + + const messageChannels = await messageChannelRepository.find({ + where: { + syncStage: In([ + MessageChannelSyncStage.MESSAGES_IMPORT_ONGOING, + MessageChannelSyncStage.MESSAGE_LIST_FETCH_ONGOING, + MessageChannelSyncStage.MESSAGES_IMPORT_SCHEDULED, + MessageChannelSyncStage.MESSAGE_LIST_FETCH_SCHEDULED, + ]), + }, + }); + + for (const messageChannel of messageChannels) { + if ( + messageChannel.syncStageStartedAt && + isSyncStale(messageChannel.syncStageStartedAt) + ) { + await this.messageChannelSyncStatusService.resetSyncStageStartedAt( + [messageChannel.id], workspaceId, - 'messageChannel', ); - const messageChannels = await messageChannelRepository.find({ - where: { - syncStage: In([ - MessageChannelSyncStage.MESSAGES_IMPORT_ONGOING, - MessageChannelSyncStage.MESSAGE_LIST_FETCH_ONGOING, - MessageChannelSyncStage.MESSAGES_IMPORT_SCHEDULED, - MessageChannelSyncStage.MESSAGE_LIST_FETCH_SCHEDULED, - ]), - }, - }); - - for (const messageChannel of messageChannels) { - if ( - messageChannel.syncStageStartedAt && - isSyncStale(messageChannel.syncStageStartedAt) - ) { - await this.messageChannelSyncStatusService.resetSyncStageStartedAt( - [messageChannel.id], - workspaceId, - ); - - switch (messageChannel.syncStage) { - case MessageChannelSyncStage.MESSAGE_LIST_FETCH_ONGOING: - case MessageChannelSyncStage.MESSAGE_LIST_FETCH_SCHEDULED: - this.logger.log( - `Sync for message channel ${messageChannel.id} and workspace ${workspaceId} is stale. Setting sync stage to MESSAGE_LIST_FETCH_PENDING`, - ); - await this.messageChannelSyncStatusService.markAsMessagesListFetchPending( - [messageChannel.id], - workspaceId, - ); - break; - case MessageChannelSyncStage.MESSAGES_IMPORT_ONGOING: - case MessageChannelSyncStage.MESSAGES_IMPORT_SCHEDULED: - this.logger.log( - `Sync for message channel ${messageChannel.id} and workspace ${workspaceId} is stale. Setting sync stage to MESSAGES_IMPORT_PENDING`, - ); - await this.messageChannelSyncStatusService.markAsMessagesImportPending( - [messageChannel.id], - workspaceId, - ); - break; - default: - break; - } + switch (messageChannel.syncStage) { + case MessageChannelSyncStage.MESSAGE_LIST_FETCH_ONGOING: + case MessageChannelSyncStage.MESSAGE_LIST_FETCH_SCHEDULED: + this.logger.log( + `Sync for message channel ${messageChannel.id} and workspace ${workspaceId} is stale. Setting sync stage to MESSAGE_LIST_FETCH_PENDING`, + ); + await this.messageChannelSyncStatusService.markAsMessagesListFetchPending( + [messageChannel.id], + workspaceId, + ); + break; + case MessageChannelSyncStage.MESSAGES_IMPORT_ONGOING: + case MessageChannelSyncStage.MESSAGES_IMPORT_SCHEDULED: + this.logger.log( + `Sync for message channel ${messageChannel.id} and workspace ${workspaceId} is stale. Setting sync stage to MESSAGES_IMPORT_PENDING`, + ); + await this.messageChannelSyncStatusService.markAsMessagesImportPending( + [messageChannel.id], + workspaceId, + ); + break; + default: + break; } } - }, - ); + } + }, authContext); } } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-relaunch-failed-message-channel.job.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-relaunch-failed-message-channel.job.ts index c35b80ff704..d2a03bc7f6b 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-relaunch-failed-message-channel.job.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/jobs/messaging-relaunch-failed-message-channel.job.ts @@ -31,40 +31,37 @@ export class MessagingRelaunchFailedMessageChannelJob { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - { shouldBypassPermissionChecks: true }, - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + { shouldBypassPermissionChecks: true }, + ); - const messageChannel = await messageChannelRepository.findOne({ - where: { - id: messageChannelId, + const messageChannel = await messageChannelRepository.findOne({ + where: { + id: messageChannelId, + }, + relations: { + connectedAccount: { + accountOwner: true, }, - relations: { - connectedAccount: { - accountOwner: true, - }, - }, - }); + }, + }); - if ( - !messageChannel || - messageChannel.syncStage !== MessageChannelSyncStage.FAILED || - messageChannel.syncStatus !== MessageChannelSyncStatus.FAILED_UNKNOWN - ) { - return; - } + if ( + !messageChannel || + messageChannel.syncStage !== MessageChannelSyncStage.FAILED || + messageChannel.syncStatus !== MessageChannelSyncStatus.FAILED_UNKNOWN + ) { + return; + } - await messageChannelRepository.update(messageChannelId, { - syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING, - syncStatus: MessageChannelSyncStatus.ACTIVE, - }); - }, - ); + await messageChannelRepository.update(messageChannelId, { + syncStage: MessageChannelSyncStage.MESSAGE_LIST_FETCH_PENDING, + syncStatus: MessageChannelSyncStatus.ACTIVE, + }); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/__tests__/messaging-message-list-fetch.service.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/__tests__/messaging-message-list-fetch.service.spec.ts index 285917a14ed..c09c46bdee5 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/__tests__/messaging-message-list-fetch.service.spec.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/__tests__/messaging-message-list-fetch.service.spec.ts @@ -213,7 +213,7 @@ describe('MessagingMessageListFetchService', () => { executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), getRepository: jest.fn().mockImplementation((workspaceId, name) => { if (name === 'messageChannel') { return { diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/__tests__/messaging-messages-import.service.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/__tests__/messaging-messages-import.service.spec.ts index 34dea3ba733..c9063227e8a 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/__tests__/messaging-messages-import.service.spec.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/__tests__/messaging-messages-import.service.spec.ts @@ -113,7 +113,7 @@ describe('MessagingMessagesImportService', () => { }), executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), }, }, { diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-cursor.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-cursor.service.ts index 3b6ce11fc30..71c23ac8468 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-cursor.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-cursor.service.ts @@ -19,55 +19,52 @@ export class MessagingCursorService { ) { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - ); - const folderRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageFolder', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); + const folderRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageFolder', + ); - if (!folderId) { - await messageChannelRepository.update( - { - id: messageChannel.id, - }, - { - throttleFailureCount: 0, - syncStageStartedAt: null, - syncCursor: - !messageChannel.syncCursor || - nextSyncCursor > messageChannel.syncCursor - ? nextSyncCursor - : messageChannel.syncCursor, - }, - ); - } else { - await folderRepository.update( - { - id: folderId, - }, - { - syncCursor: nextSyncCursor, - }, - ); - await messageChannelRepository.update( - { - id: messageChannel.id, - }, - { - throttleFailureCount: 0, - syncStageStartedAt: null, - }, - ); - } - }, - ); + if (!folderId) { + await messageChannelRepository.update( + { + id: messageChannel.id, + }, + { + throttleFailureCount: 0, + syncStageStartedAt: null, + syncCursor: + !messageChannel.syncCursor || + nextSyncCursor > messageChannel.syncCursor + ? nextSyncCursor + : messageChannel.syncCursor, + }, + ); + } else { + await folderRepository.update( + { + id: folderId, + }, + { + syncCursor: nextSyncCursor, + }, + ); + await messageChannelRepository.update( + { + id: messageChannel.id, + }, + { + throttleFailureCount: 0, + syncStageStartedAt: null, + }, + ); + } + }, authContext); } } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-delete-group-email-messages.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-delete-group-email-messages.service.ts index 4b7ee1dc494..00eeffc05c2 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-delete-group-email-messages.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-delete-group-email-messages.service.ts @@ -41,7 +41,6 @@ export class MessagingDeleteGroupEmailMessagesService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const messageChannelMessageAssociationRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -146,6 +145,7 @@ export class MessagingDeleteGroupEmailMessagesService { return totalDeletedCount; }, + authContext, ); } } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-import-exception-handler.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-import-exception-handler.service.ts index 48881a17343..45d61c92e9e 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-import-exception-handler.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-import-exception-handler.service.ts @@ -150,24 +150,21 @@ export class MessageImportExceptionHandlerService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - ); - - await messageChannelRepository.increment( - { id: messageChannel.id }, - 'throttleFailureCount', - 1, - undefined, - ['throttleFailureCount', 'id'], + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', ); - }, - ); + + await messageChannelRepository.increment( + { id: messageChannel.id }, + 'throttleFailureCount', + 1, + undefined, + ['throttleFailureCount', 'id'], + ); + }, authContext); switch (syncStep) { case MessageImportSyncStep.MESSAGE_LIST_FETCH: diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message-list-fetch.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message-list-fetch.service.ts index da3e50e40f9..96a71aeaf4b 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message-list-fetch.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message-list-fetch.service.ts @@ -58,254 +58,250 @@ export class MessagingMessageListFetchService { ) { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - try { - const pendingGroupEmailActionsProcessed = - await this.processPendingGroupEmailActions( - messageChannel, - workspaceId, - ); - - const pendingFolderActionsProcessed = - await this.processPendingFolderActions(messageChannel, workspaceId); - - await this.messageChannelSyncStatusService.markAsMessagesListFetchOngoing( - [messageChannel.id], + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + try { + const pendingGroupEmailActionsProcessed = + await this.processPendingGroupEmailActions( + messageChannel, workspaceId, ); - this.logger.log( - `messageChannelId: ${messageChannel.id} Processing message list fetch`, + const pendingFolderActionsProcessed = + await this.processPendingFolderActions(messageChannel, workspaceId); + + await this.messageChannelSyncStatusService.markAsMessagesListFetchOngoing( + [messageChannel.id], + workspaceId, + ); + + this.logger.log( + `messageChannelId: ${messageChannel.id} Processing message list fetch`, + ); + + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', ); - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( + const freshMessageChannel = + pendingGroupEmailActionsProcessed || pendingFolderActionsProcessed + ? await messageChannelRepository.findOne({ + where: { + id: messageChannel.id, + }, + relations: ['connectedAccount', 'messageFolders'], + }) + : messageChannel; + + if (!isDefined(freshMessageChannel)) { + this.logger.error( + `error processing message list fetch: messageChannelId: ${messageChannel.id} Message channel not found`, + ); + + return; + } + + const { accessToken, refreshToken } = + await this.messagingAccountAuthenticationService.validateAndRefreshConnectedAccountAuthentication( + { + connectedAccount: freshMessageChannel.connectedAccount, workspaceId, - 'messageChannel', - ); - - const freshMessageChannel = - pendingGroupEmailActionsProcessed || pendingFolderActionsProcessed - ? await messageChannelRepository.findOne({ - where: { - id: messageChannel.id, - }, - relations: ['connectedAccount', 'messageFolders'], - }) - : messageChannel; - - if (!isDefined(freshMessageChannel)) { - this.logger.error( - `error processing message list fetch: messageChannelId: ${messageChannel.id} Message channel not found`, - ); - - return; - } - - const { accessToken, refreshToken } = - await this.messagingAccountAuthenticationService.validateAndRefreshConnectedAccountAuthentication( - { - connectedAccount: freshMessageChannel.connectedAccount, - workspaceId, - messageChannelId: freshMessageChannel.id, - }, - ); - - const messageChannelWithFreshTokens = { - ...freshMessageChannel, - connectedAccount: { - ...freshMessageChannel.connectedAccount, - accessToken, - refreshToken, + messageChannelId: freshMessageChannel.id, }, - }; + ); - const messageFolders = - await this.syncMessageFoldersService.syncMessageFolders({ - messageChannel: messageChannelWithFreshTokens, - workspaceId, + const messageChannelWithFreshTokens = { + ...freshMessageChannel, + connectedAccount: { + ...freshMessageChannel.connectedAccount, + accessToken, + refreshToken, + }, + }; + + const messageFolders = + await this.syncMessageFoldersService.syncMessageFolders({ + messageChannel: messageChannelWithFreshTokens, + workspaceId, + }); + + const messageFoldersToSync = messageFolders.filter( + (folder) => + folder.pendingSyncAction === MessageFolderPendingSyncAction.NONE, + ); + + const messageLists = + await this.messagingGetMessageListService.getMessageLists( + messageChannelWithFreshTokens, + messageFoldersToSync, + ); + + await this.cacheStorage.del( + `messages-to-import:${workspaceId}:${freshMessageChannel.id}`, + ); + + const messageExternalIds = messageLists.flatMap( + (messageList) => messageList.messageExternalIds, + ); + + const messageExternalIdsToDelete = messageLists.flatMap( + (messageList) => messageList.messageExternalIdsToDelete, + ); + + const isFullSync = + messageLists.every( + (messageList) => !isNonEmptyString(messageList.previousSyncCursor), + ) && !isNonEmptyString(freshMessageChannel.syncCursor); + + let totalMessagesToImportCount = 0; + + this.logger.log( + `messageChannelId: ${freshMessageChannel.id} Is full sync: ${isFullSync} and toImportCount: ${messageExternalIds.length}, toDeleteCount: ${messageExternalIdsToDelete.length}, cursors: ${messageLists.map( + (messageList) => { + messageList.nextSyncCursor; + }, + )}`, + ); + + const messageChannelMessageAssociationRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannelMessageAssociation', + ); + + const messageExternalIdsChunks = chunk(messageExternalIds, 200); + + for (const [ + index, + messageExternalIdsChunk, + ] of messageExternalIdsChunks.entries()) { + const existingMessageChannelMessageAssociations = + await messageChannelMessageAssociationRepository.find({ + where: { + messageChannelId: freshMessageChannel.id, + messageExternalId: In(messageExternalIdsChunk), + }, }); - const messageFoldersToSync = messageFolders.filter( - (folder) => - folder.pendingSyncAction === MessageFolderPendingSyncAction.NONE, - ); - - const messageLists = - await this.messagingGetMessageListService.getMessageLists( - messageChannelWithFreshTokens, - messageFoldersToSync, - ); - - await this.cacheStorage.del( - `messages-to-import:${workspaceId}:${freshMessageChannel.id}`, - ); - - const messageExternalIds = messageLists.flatMap( - (messageList) => messageList.messageExternalIds, - ); - - const messageExternalIdsToDelete = messageLists.flatMap( - (messageList) => messageList.messageExternalIdsToDelete, - ); - - const isFullSync = - messageLists.every( - (messageList) => - !isNonEmptyString(messageList.previousSyncCursor), - ) && !isNonEmptyString(freshMessageChannel.syncCursor); - - let totalMessagesToImportCount = 0; - - this.logger.log( - `messageChannelId: ${freshMessageChannel.id} Is full sync: ${isFullSync} and toImportCount: ${messageExternalIds.length}, toDeleteCount: ${messageExternalIdsToDelete.length}, cursors: ${messageLists.map( - (messageList) => { - messageList.nextSyncCursor; - }, - )}`, - ); - - const messageChannelMessageAssociationRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannelMessageAssociation', - ); - - const messageExternalIdsChunks = chunk(messageExternalIds, 200); - - for (const [ - index, - messageExternalIdsChunk, - ] of messageExternalIdsChunks.entries()) { - const existingMessageChannelMessageAssociations = - await messageChannelMessageAssociationRepository.find({ - where: { - messageChannelId: freshMessageChannel.id, - messageExternalId: In(messageExternalIdsChunk), - }, - }); - - const existingMessageChannelMessageAssociationsExternalIds = - existingMessageChannelMessageAssociations.map( - (messageChannelMessageAssociation) => - messageChannelMessageAssociation.messageExternalId, - ); - - const messageExternalIdsToImport = messageExternalIdsChunk.filter( - (messageExternalId) => - !existingMessageChannelMessageAssociationsExternalIds.includes( - messageExternalId, - ), - ); - - if (messageExternalIdsToImport.length) { - this.logger.log( - `messageChannelId: ${freshMessageChannel.id} Adding ${messageExternalIdsToImport.length} message external ids to import in batch ${index + 1}`, - ); - - totalMessagesToImportCount += messageExternalIdsToImport.length; - - await this.cacheStorage.setAdd( - `messages-to-import:${workspaceId}:${messageChannelWithFreshTokens.id}`, - messageExternalIdsToImport, - ONE_WEEK_IN_MILLISECONDS, - ); - } - } - - for (const messageList of messageLists) { - const { nextSyncCursor, folderId } = messageList; - - await this.messagingCursorService.updateCursor( - messageChannelWithFreshTokens, - nextSyncCursor, - workspaceId, - folderId, - ); - } - - const fullSyncMessageChannelMessageAssociationsToDelete = isFullSync - ? await this.computeFullSyncMessageChannelMessageAssociationsToDelete( - freshMessageChannel, - messageExternalIds, - workspaceId, - ) - : []; - - const allMessageExternalIdsToDelete = [ - ...messageExternalIdsToDelete, - ...fullSyncMessageChannelMessageAssociationsToDelete.map( + const existingMessageChannelMessageAssociationsExternalIds = + existingMessageChannelMessageAssociations.map( (messageChannelMessageAssociation) => messageChannelMessageAssociation.messageExternalId, - ), - ]; + ); - if (allMessageExternalIdsToDelete.length) { + const messageExternalIdsToImport = messageExternalIdsChunk.filter( + (messageExternalId) => + !existingMessageChannelMessageAssociationsExternalIds.includes( + messageExternalId, + ), + ); + + if (messageExternalIdsToImport.length) { this.logger.log( - `messageChannelId: ${freshMessageChannel.id} Deleting ${allMessageExternalIdsToDelete.length} message channel message associations`, + `messageChannelId: ${freshMessageChannel.id} Adding ${messageExternalIdsToImport.length} message external ids to import in batch ${index + 1}`, ); - const toDeleteChunks = chunk(allMessageExternalIdsToDelete, 200); + totalMessagesToImportCount += messageExternalIdsToImport.length; - for (const [index, toDeleteChunk] of toDeleteChunks.entries()) { - this.logger.log( - `messageChannelId: ${freshMessageChannel.id} Deleting ${toDeleteChunk.length} message channel message associations in batch ${index + 1}`, - ); - - await this.messagingMessageCleanerService.deleteMessagesChannelMessageAssociationsAndRelatedOrphans( - { - workspaceId, - messageExternalIds: toDeleteChunk.filter( - (messageExternalId) => isNonEmptyString(messageExternalId), - ), - messageChannelId: messageChannelWithFreshTokens.id, - }, - ); - } + await this.cacheStorage.setAdd( + `messages-to-import:${workspaceId}:${messageChannelWithFreshTokens.id}`, + messageExternalIdsToImport, + ONE_WEEK_IN_MILLISECONDS, + ); } + } - this.logger.log( - `messageChannelId: ${freshMessageChannel.id} Total messages to import count: ${totalMessagesToImportCount}`, + for (const messageList of messageLists) { + const { nextSyncCursor, folderId } = messageList; + + await this.messagingCursorService.updateCursor( + messageChannelWithFreshTokens, + nextSyncCursor, + workspaceId, + folderId, ); + } - if (totalMessagesToImportCount === 0) { - await this.messageChannelSyncStatusService.markAsCompletedAndMarkAsMessagesListFetchPending( - [messageChannelWithFreshTokens.id], + const fullSyncMessageChannelMessageAssociationsToDelete = isFullSync + ? await this.computeFullSyncMessageChannelMessageAssociationsToDelete( + freshMessageChannel, + messageExternalIds, workspaceId, - ); + ) + : []; - return; - } + const allMessageExternalIdsToDelete = [ + ...messageExternalIdsToDelete, + ...fullSyncMessageChannelMessageAssociationsToDelete.map( + (messageChannelMessageAssociation) => + messageChannelMessageAssociation.messageExternalId, + ), + ]; + if (allMessageExternalIdsToDelete.length) { this.logger.log( - `messageChannelId: ${freshMessageChannel.id} Scheduling direct messages import`, + `messageChannelId: ${freshMessageChannel.id} Deleting ${allMessageExternalIdsToDelete.length} message channel message associations`, ); - await this.messageChannelSyncStatusService.markAsMessagesImportScheduled( + const toDeleteChunks = chunk(allMessageExternalIdsToDelete, 200); + + for (const [index, toDeleteChunk] of toDeleteChunks.entries()) { + this.logger.log( + `messageChannelId: ${freshMessageChannel.id} Deleting ${toDeleteChunk.length} message channel message associations in batch ${index + 1}`, + ); + + await this.messagingMessageCleanerService.deleteMessagesChannelMessageAssociationsAndRelatedOrphans( + { + workspaceId, + messageExternalIds: toDeleteChunk.filter((messageExternalId) => + isNonEmptyString(messageExternalId), + ), + messageChannelId: messageChannelWithFreshTokens.id, + }, + ); + } + } + + this.logger.log( + `messageChannelId: ${freshMessageChannel.id} Total messages to import count: ${totalMessagesToImportCount}`, + ); + + if (totalMessagesToImportCount === 0) { + await this.messageChannelSyncStatusService.markAsCompletedAndMarkAsMessagesListFetchPending( [messageChannelWithFreshTokens.id], workspaceId, ); - await this.messagingMessagesImportService.processMessageBatchImport( - { - ...messageChannelWithFreshTokens, - syncStage: MessageChannelSyncStage.MESSAGES_IMPORT_SCHEDULED, - }, - messageChannelWithFreshTokens.connectedAccount, - workspaceId, - ); - } catch (error) { - await this.messageImportErrorHandlerService.handleDriverException( - error, - MessageImportSyncStep.MESSAGE_LIST_FETCH, - messageChannel, - workspaceId, - ); + return; } - }, - ); + + this.logger.log( + `messageChannelId: ${freshMessageChannel.id} Scheduling direct messages import`, + ); + + await this.messageChannelSyncStatusService.markAsMessagesImportScheduled( + [messageChannelWithFreshTokens.id], + workspaceId, + ); + + await this.messagingMessagesImportService.processMessageBatchImport( + { + ...messageChannelWithFreshTokens, + syncStage: MessageChannelSyncStage.MESSAGES_IMPORT_SCHEDULED, + }, + messageChannelWithFreshTokens.connectedAccount, + workspaceId, + ); + } catch (error) { + await this.messageImportErrorHandlerService.handleDriverException( + error, + MessageImportSyncStep.MESSAGE_LIST_FETCH, + messageChannel, + workspaceId, + ); + } + }, authContext); } private async processPendingGroupEmailActions( diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message.service.ts index c2c5cf0880a..7a5c07ebe11 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-message.service.ts @@ -55,7 +55,6 @@ export class MessagingMessageService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const messageChannelMessageAssociationRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -245,6 +244,7 @@ export class MessagingMessageService { messageExternalIdsAndIdsMap, }; }, + authContext, ); } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-messages-import.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-messages-import.service.ts index feecb7b1741..4a9958cee79 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-messages-import.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-messages-import.service.ts @@ -60,169 +60,164 @@ export class MessagingMessagesImportService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - try { - if ( - messageChannel.syncStage !== - MessageChannelSyncStage.MESSAGES_IMPORT_SCHEDULED - ) { - return; - } + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + try { + if ( + messageChannel.syncStage !== + MessageChannelSyncStage.MESSAGES_IMPORT_SCHEDULED + ) { + return; + } - await this.messagingMonitoringService.track({ - eventName: 'messages_import.started', - workspaceId, - connectedAccountId: messageChannel.connectedAccountId, - messageChannelId: messageChannel.id, - }); + await this.messagingMonitoringService.track({ + eventName: 'messages_import.started', + workspaceId, + connectedAccountId: messageChannel.connectedAccountId, + messageChannelId: messageChannel.id, + }); - await this.messageChannelSyncStatusService.markAsMessagesImportOngoing( + await this.messageChannelSyncStatusService.markAsMessagesImportOngoing( + [messageChannel.id], + workspaceId, + ); + + const { accessToken, refreshToken } = + await this.messagingAccountAuthenticationService.validateAndRefreshConnectedAccountAuthentication( + { + connectedAccount, + workspaceId, + messageChannelId: messageChannel.id, + }, + ); + + const connectedAccountWithFreshTokens = { + ...connectedAccount, + accessToken, + refreshToken, + }; + + await this.emailAliasManagerService.refreshHandleAliases( + connectedAccountWithFreshTokens, + workspaceId, + ); + + messageIdsToFetch = await this.cacheStorage.setPop( + `messages-to-import:${workspaceId}:${messageChannel.id}`, + MESSAGING_GMAIL_USERS_MESSAGES_GET_BATCH_SIZE, + ); + + if (!messageIdsToFetch?.length) { + await this.messageChannelSyncStatusService.markAsCompletedAndMarkAsMessagesListFetchPending( [messageChannel.id], workspaceId, ); - const { accessToken, refreshToken } = - await this.messagingAccountAuthenticationService.validateAndRefreshConnectedAccountAuthentication( - { - connectedAccount, - workspaceId, - messageChannelId: messageChannel.id, - }, - ); - - const connectedAccountWithFreshTokens = { - ...connectedAccount, - accessToken, - refreshToken, - }; - - await this.emailAliasManagerService.refreshHandleAliases( - connectedAccountWithFreshTokens, - workspaceId, - ); - - messageIdsToFetch = await this.cacheStorage.setPop( - `messages-to-import:${workspaceId}:${messageChannel.id}`, - MESSAGING_GMAIL_USERS_MESSAGES_GET_BATCH_SIZE, - ); - - if (!messageIdsToFetch?.length) { - await this.messageChannelSyncStatusService.markAsCompletedAndMarkAsMessagesListFetchPending( - [messageChannel.id], - workspaceId, - ); - - return await this.trackMessageImportCompleted( - messageChannel, - workspaceId, - ); - } - - const allMessages = - await this.messagingGetMessagesService.getMessages( - messageIdsToFetch, - connectedAccountWithFreshTokens, - ); - - const blocklist = - await this.blocklistRepository.getByWorkspaceMemberId( - connectedAccountWithFreshTokens.accountOwnerId, - workspaceId, - ); - - if (!isDefined(messageChannel.handle)) { - throw new MessageImportDriverException( - 'Message channel handle is required', - MessageImportDriverExceptionCode.CHANNEL_MISCONFIGURED, - ); - } - - if (!isDefined(connectedAccountWithFreshTokens.handleAliases)) { - throw new MessageImportDriverException( - 'Message channel handle is required', - MessageImportDriverExceptionCode.CHANNEL_MISCONFIGURED, - ); - } - - const messagesToSave = filterEmails( - messageChannel.handle, - [...connectedAccountWithFreshTokens.handleAliases.split(',')], - allMessages, - blocklist - .map((blocklistItem) => blocklistItem.handle) - .filter(isDefined), - messageChannel.excludeGroupEmails, - ); - - if (messagesToSave.length > 0) { - await this.saveMessagesAndEnqueueContactCreationService.saveMessagesAndEnqueueContactCreation( - messagesToSave, - messageChannel, - connectedAccountWithFreshTokens, - workspaceId, - ); - } - - if ( - messageIdsToFetch.length < - MESSAGING_GMAIL_USERS_MESSAGES_GET_BATCH_SIZE - ) { - await this.messageChannelSyncStatusService.markAsCompletedAndMarkAsMessagesListFetchPending( - [messageChannel.id], - workspaceId, - ); - } else { - await this.messageChannelSyncStatusService.markAsMessagesImportPending( - [messageChannel.id], - workspaceId, - ); - } - - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - ); - - await messageChannelRepository.update( - { - id: messageChannel.id, - }, - { - throttleFailureCount: 0, - syncStageStartedAt: null, - }, - ); - - return await this.trackMessageImportCompleted( - messageChannel, - workspaceId, - ); - } catch (error) { - this.logger.error( - `Error (${error.code}) importing messages for workspace ${workspaceId.slice(0, 8)} and account ${connectedAccount.id.slice(0, 8)}: ${error.message} - ${error.body}`, - ); - await this.cacheStorage.setAdd( - `messages-to-import:${workspaceId}:${messageChannel.id}`, - messageIdsToFetch, - ); - - await this.messageImportErrorHandlerService.handleDriverException( - error, - MessageImportSyncStep.MESSAGES_IMPORT_ONGOING, - messageChannel, - workspaceId, - ); - return await this.trackMessageImportCompleted( messageChannel, workspaceId, ); } - }, - ); + + const allMessages = await this.messagingGetMessagesService.getMessages( + messageIdsToFetch, + connectedAccountWithFreshTokens, + ); + + const blocklist = await this.blocklistRepository.getByWorkspaceMemberId( + connectedAccountWithFreshTokens.accountOwnerId, + workspaceId, + ); + + if (!isDefined(messageChannel.handle)) { + throw new MessageImportDriverException( + 'Message channel handle is required', + MessageImportDriverExceptionCode.CHANNEL_MISCONFIGURED, + ); + } + + if (!isDefined(connectedAccountWithFreshTokens.handleAliases)) { + throw new MessageImportDriverException( + 'Message channel handle is required', + MessageImportDriverExceptionCode.CHANNEL_MISCONFIGURED, + ); + } + + const messagesToSave = filterEmails( + messageChannel.handle, + [...connectedAccountWithFreshTokens.handleAliases.split(',')], + allMessages, + blocklist + .map((blocklistItem) => blocklistItem.handle) + .filter(isDefined), + messageChannel.excludeGroupEmails, + ); + + if (messagesToSave.length > 0) { + await this.saveMessagesAndEnqueueContactCreationService.saveMessagesAndEnqueueContactCreation( + messagesToSave, + messageChannel, + connectedAccountWithFreshTokens, + workspaceId, + ); + } + + if ( + messageIdsToFetch.length < + MESSAGING_GMAIL_USERS_MESSAGES_GET_BATCH_SIZE + ) { + await this.messageChannelSyncStatusService.markAsCompletedAndMarkAsMessagesListFetchPending( + [messageChannel.id], + workspaceId, + ); + } else { + await this.messageChannelSyncStatusService.markAsMessagesImportPending( + [messageChannel.id], + workspaceId, + ); + } + + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); + + await messageChannelRepository.update( + { + id: messageChannel.id, + }, + { + throttleFailureCount: 0, + syncStageStartedAt: null, + }, + ); + + return await this.trackMessageImportCompleted( + messageChannel, + workspaceId, + ); + } catch (error) { + this.logger.error( + `Error (${error.code}) importing messages for workspace ${workspaceId.slice(0, 8)} and account ${connectedAccount.id.slice(0, 8)}: ${error.message} - ${error.body}`, + ); + await this.cacheStorage.setAdd( + `messages-to-import:${workspaceId}:${messageChannel.id}`, + messageIdsToFetch, + ); + + await this.messageImportErrorHandlerService.handleDriverException( + error, + MessageImportSyncStep.MESSAGES_IMPORT_ONGOING, + messageChannel, + workspaceId, + ); + + return await this.trackMessageImportCompleted( + messageChannel, + workspaceId, + ); + } + }, authContext); } private async trackMessageImportCompleted( diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-process-folder-actions.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-process-folder-actions.service.ts index 092cefb7186..5042509e724 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-process-folder-actions.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-process-folder-actions.service.ts @@ -90,7 +90,6 @@ export class MessagingProcessFolderActionsService { const authContext = buildSystemAuthContext(workspaceId); await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workspaceDataSource = await this.globalWorkspaceOrmManager.getGlobalWorkspaceDataSource(); @@ -128,6 +127,7 @@ export class MessagingProcessFolderActionsService { }, ); }, + authContext, ); } } diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-process-group-email-actions.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-process-group-email-actions.service.ts index db8cf2bf64f..6db0f46f208 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-process-group-email-actions.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-process-group-email-actions.service.ts @@ -30,25 +30,22 @@ export class MessagingProcessGroupEmailActionsService { ): Promise { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageChannel', - ); - - await messageChannelRepository.update( - { id: messageChannel.id }, - { pendingGroupEmailsAction }, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', ); - this.logger.log( - `WorkspaceId: ${workspaceId}, MessageChannelId: ${messageChannel.id} - Marked message channel as pending group emails action: ${pendingGroupEmailsAction}`, - ); - }, - ); + await messageChannelRepository.update( + { id: messageChannel.id }, + { pendingGroupEmailsAction }, + ); + + this.logger.log( + `WorkspaceId: ${workspaceId}, MessageChannelId: ${messageChannel.id} - Marked message channel as pending group emails action: ${pendingGroupEmailsAction}`, + ); + }, authContext); } async processGroupEmailActions( @@ -70,61 +67,58 @@ export class MessagingProcessGroupEmailActionsService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const workspaceDataSource = - await this.globalWorkspaceOrmManager.getGlobalWorkspaceDataSource(); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workspaceDataSource = + await this.globalWorkspaceOrmManager.getGlobalWorkspaceDataSource(); - await workspaceDataSource?.transaction( - async (transactionManager: WorkspaceEntityManager) => { - try { - const messageChannelRepository = - await this.globalWorkspaceOrmManager.getRepository( + await workspaceDataSource?.transaction( + async (transactionManager: WorkspaceEntityManager) => { + try { + const messageChannelRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageChannel', + ); + + switch (pendingGroupEmailsAction) { + case MessageChannelPendingGroupEmailsAction.GROUP_EMAILS_DELETION: + await this.handleGroupEmailsDeletion( workspaceId, - 'messageChannel', + messageChannel.id, + transactionManager, ); - - switch (pendingGroupEmailsAction) { - case MessageChannelPendingGroupEmailsAction.GROUP_EMAILS_DELETION: - await this.handleGroupEmailsDeletion( - workspaceId, - messageChannel.id, - transactionManager, - ); - break; - case MessageChannelPendingGroupEmailsAction.GROUP_EMAILS_IMPORT: - await this.handleGroupEmailsImport( - workspaceId, - messageChannel.id, - transactionManager, - ); - break; - } - - await messageChannelRepository.update( - { id: messageChannel.id }, - { - pendingGroupEmailsAction: - MessageChannelPendingGroupEmailsAction.NONE, - }, - transactionManager, - ); - - this.logger.log( - `WorkspaceId: ${workspaceId}, MessageChannelId: ${messageChannel.id} - Reset pendingGroupEmailsAction to NONE`, - ); - } catch (error) { - this.logger.error( - `WorkspaceId: ${workspaceId}, MessageChannelId: ${messageChannel.id} - Error processing group email action: ${error.message}`, - error.stack, - ); - throw error; + break; + case MessageChannelPendingGroupEmailsAction.GROUP_EMAILS_IMPORT: + await this.handleGroupEmailsImport( + workspaceId, + messageChannel.id, + transactionManager, + ); + break; } - }, - ); - }, - ); + + await messageChannelRepository.update( + { id: messageChannel.id }, + { + pendingGroupEmailsAction: + MessageChannelPendingGroupEmailsAction.NONE, + }, + transactionManager, + ); + + this.logger.log( + `WorkspaceId: ${workspaceId}, MessageChannelId: ${messageChannel.id} - Reset pendingGroupEmailsAction to NONE`, + ); + } catch (error) { + this.logger.error( + `WorkspaceId: ${workspaceId}, MessageChannelId: ${messageChannel.id} - Error processing group email action: ${error.message}`, + error.stack, + ); + throw error; + } + }, + ); + }, authContext); } private async handleGroupEmailsDeletion( diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.spec.ts index 400b46f47d8..c40762f5723 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.spec.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.spec.ts @@ -159,7 +159,7 @@ describe('MessagingSaveMessagesAndEnqueueContactCreationService', () => { .mockResolvedValue(datasourceInstance), executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), }, }, ], diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts index 0927f2cba8a..505e7f82fbb 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts @@ -47,7 +47,6 @@ export class MessagingSaveMessagesAndEnqueueContactCreationService { const participantsWithMessageId = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workspaceDataSource = await this.globalWorkspaceOrmManager.getGlobalWorkspaceDataSource(); @@ -117,6 +116,7 @@ export class MessagingSaveMessagesAndEnqueueContactCreationService { }, ); }, + authContext, ); if ( diff --git a/packages/twenty-server/src/modules/messaging/message-participant-manager/services/messaging-message-participant.service.ts b/packages/twenty-server/src/modules/messaging/message-participant-manager/services/messaging-message-participant.service.ts index fa7a9635faf..040faa7961b 100644 --- a/packages/twenty-server/src/modules/messaging/message-participant-manager/services/messaging-message-participant.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-participant-manager/services/messaging-message-participant.service.ts @@ -23,60 +23,57 @@ export class MessagingMessageParticipantService { ): Promise { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const messageParticipantRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'messageParticipant', - ); - - const existingParticipantsBasedOnMessageIds = - await messageParticipantRepository.find({ - where: { - messageId: In( - participants.map((participant) => participant.messageId), - ), - }, - }); - - const participantsToCreate: Pick< - MessageParticipantWorkspaceEntity, - 'messageId' | 'handle' | 'displayName' | 'role' - >[] = participants - .filter( - (participant) => - !existingParticipantsBasedOnMessageIds.find( - (existingParticipant) => - existingParticipant.messageId === participant.messageId && - existingParticipant.handle === participant.handle && - existingParticipant.displayName === participant.displayName && - existingParticipant.role === participant.role, - ), - ) - .map((participant) => { - return { - messageId: participant.messageId, - handle: participant.handle, - displayName: participant.displayName, - role: participant.role, - }; - }); - - const createdParticipants = await messageParticipantRepository.insert( - participantsToCreate, - transactionManager, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const messageParticipantRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'messageParticipant', ); - await this.matchParticipantService.matchParticipants({ - participants: createdParticipants.raw ?? [], - objectMetadataName: 'messageParticipant', - transactionManager, - matchWith: 'workspaceMemberAndPerson', - workspaceId, + const existingParticipantsBasedOnMessageIds = + await messageParticipantRepository.find({ + where: { + messageId: In( + participants.map((participant) => participant.messageId), + ), + }, }); - }, - ); + + const participantsToCreate: Pick< + MessageParticipantWorkspaceEntity, + 'messageId' | 'handle' | 'displayName' | 'role' + >[] = participants + .filter( + (participant) => + !existingParticipantsBasedOnMessageIds.find( + (existingParticipant) => + existingParticipant.messageId === participant.messageId && + existingParticipant.handle === participant.handle && + existingParticipant.displayName === participant.displayName && + existingParticipant.role === participant.role, + ), + ) + .map((participant) => { + return { + messageId: participant.messageId, + handle: participant.handle, + displayName: participant.displayName, + role: participant.role, + }; + }); + + const createdParticipants = await messageParticipantRepository.insert( + participantsToCreate, + transactionManager, + ); + + await this.matchParticipantService.matchParticipants({ + participants: createdParticipants.raw ?? [], + objectMetadataName: 'messageParticipant', + transactionManager, + matchWith: 'workspaceMemberAndPerson', + workspaceId, + }); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.job.ts b/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.job.ts index 553f66d6f94..aeed0f0cfc4 100644 --- a/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.job.ts +++ b/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.job.ts @@ -50,7 +50,6 @@ export class MessagingMessageChannelSyncStatusMonitoringCronJob { const authContext = buildSystemAuthContext(activeWorkspace.id); await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const messageChannelRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -76,6 +75,7 @@ export class MessagingMessageChannelSyncStatusMonitoringCronJob { }); } }, + authContext, ); } catch (error) { this.exceptionHandlerService.captureExceptions([error], { diff --git a/packages/twenty-server/src/modules/note/query-hooks/note-post-query-hook.service.ts b/packages/twenty-server/src/modules/note/query-hooks/note-post-query-hook.service.ts index 3f10408ba9a..a416acbc4ee 100644 --- a/packages/twenty-server/src/modules/note/query-hooks/note-post-query-hook.service.ts +++ b/packages/twenty-server/src/modules/note/query-hooks/note-post-query-hook.service.ts @@ -29,20 +29,17 @@ export class NotePostQueryHookService { assertIsDefinedOrThrow(workspace, WorkspaceNotFoundDefaultError); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext as WorkspaceAuthContext, - async () => { - const noteTargetRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspace.id, - 'noteTarget', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const noteTargetRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspace.id, + 'noteTarget', + ); - await noteTargetRepository.softDelete({ - noteId: In(payload.map((note) => note.id)), - }); - }, - ); + await noteTargetRepository.softDelete({ + noteId: In(payload.map((note) => note.id)), + }); + }, authContext as WorkspaceAuthContext); } async handleNoteTargetsRestore( @@ -57,19 +54,16 @@ export class NotePostQueryHookService { assertIsDefinedOrThrow(workspace, WorkspaceNotFoundDefaultError); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext as WorkspaceAuthContext, - async () => { - const noteTargetRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspace.id, - 'noteTarget', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const noteTargetRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspace.id, + 'noteTarget', + ); - await noteTargetRepository.restore({ - noteId: In(payload.map((note) => note.id)), - }); - }, - ); + await noteTargetRepository.restore({ + noteId: In(payload.map((note) => note.id)), + }); + }, authContext as WorkspaceAuthContext); } } diff --git a/packages/twenty-server/src/modules/task/query-hooks/task-post-query-hook.service.ts b/packages/twenty-server/src/modules/task/query-hooks/task-post-query-hook.service.ts index fc312b86c45..a5ed6abcc0b 100644 --- a/packages/twenty-server/src/modules/task/query-hooks/task-post-query-hook.service.ts +++ b/packages/twenty-server/src/modules/task/query-hooks/task-post-query-hook.service.ts @@ -29,20 +29,17 @@ export class TaskPostQueryHookService { assertIsDefinedOrThrow(workspace, WorkspaceNotFoundDefaultError); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext as WorkspaceAuthContext, - async () => { - const taskTargetRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspace.id, - 'taskTarget', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const taskTargetRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspace.id, + 'taskTarget', + ); - await taskTargetRepository.softDelete({ - taskId: In(payload.map((task) => task.id)), - }); - }, - ); + await taskTargetRepository.softDelete({ + taskId: In(payload.map((task) => task.id)), + }); + }, authContext as WorkspaceAuthContext); } async handleTaskTargetsRestore( @@ -57,19 +54,16 @@ export class TaskPostQueryHookService { assertIsDefinedOrThrow(workspace, WorkspaceNotFoundDefaultError); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext as WorkspaceAuthContext, - async () => { - const taskTargetRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspace.id, - 'taskTarget', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const taskTargetRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspace.id, + 'taskTarget', + ); - await taskTargetRepository.restore({ - taskId: In(payload.map((task) => task.id)), - }); - }, - ); + await taskTargetRepository.restore({ + taskId: In(payload.map((task) => task.id)), + }); + }, authContext as WorkspaceAuthContext); } } diff --git a/packages/twenty-server/src/modules/timeline/jobs/upsert-timeline-activity-from-internal-event.job.ts b/packages/twenty-server/src/modules/timeline/jobs/upsert-timeline-activity-from-internal-event.job.ts index 4e46f7643b8..fdaf722e90b 100644 --- a/packages/twenty-server/src/modules/timeline/jobs/upsert-timeline-activity-from-internal-event.job.ts +++ b/packages/twenty-server/src/modules/timeline/jobs/upsert-timeline-activity-from-internal-event.job.ts @@ -38,38 +38,35 @@ export class UpsertTimelineActivityFromInternalEvent { const authContext = buildSystemAuthContext(workspaceEventBatch.workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const workspaceMemberRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceEventBatch.workspaceId, - WorkspaceMemberWorkspaceEntity, - { - shouldBypassPermissionChecks: true, - }, - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workspaceMemberRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceEventBatch.workspaceId, + WorkspaceMemberWorkspaceEntity, + { + shouldBypassPermissionChecks: true, + }, + ); - const userIds = workspaceEventBatch.events - .map((event) => event.userId) - .filter(isDefined); + const userIds = workspaceEventBatch.events + .map((event) => event.userId) + .filter(isDefined); - const workspaceMembers = await workspaceMemberRepository.findBy({ - userId: In(userIds), - }); + const workspaceMembers = await workspaceMemberRepository.findBy({ + userId: In(userIds), + }); - for (const eventData of workspaceEventBatch.events) { - const workspaceMember = workspaceMembers.find( - (workspaceMember) => workspaceMember.userId === eventData.userId, - ); + for (const eventData of workspaceEventBatch.events) { + const workspaceMember = workspaceMembers.find( + (workspaceMember) => workspaceMember.userId === eventData.userId, + ); - if (eventData.userId && workspaceMember) { - eventData.workspaceMemberId = workspaceMember.id; - } + if (eventData.userId && workspaceMember) { + eventData.workspaceMemberId = workspaceMember.id; } + } - await this.timelineActivityService.upsertEvents(workspaceEventBatch); - }, - ); + await this.timelineActivityService.upsertEvents(workspaceEventBatch); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/timeline/repositories/timeline-activity.repository.ts b/packages/twenty-server/src/modules/timeline/repositories/timeline-activity.repository.ts index eafaad9cb75..c9460ac2f57 100644 --- a/packages/twenty-server/src/modules/timeline/repositories/timeline-activity.repository.ts +++ b/packages/twenty-server/src/modules/timeline/repositories/timeline-activity.repository.ts @@ -33,79 +33,74 @@ export class TimelineActivityRepository { }: TimelineActivityPayloadWorkspaceIdAndObjectSingularName) { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const recentTimelineActivities = - await this.findRecentTimelineActivities({ - objectSingularName, - workspaceId, - payloads, - isFeatureFlagTimelineActivityMigrated, - }); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const recentTimelineActivities = await this.findRecentTimelineActivities({ + objectSingularName, + workspaceId, + payloads, + isFeatureFlagTimelineActivityMigrated, + }); - const payloadsWithDiff = payloads - .filter(({ properties }) => { - const isDiffEmpty = - properties.diff !== null && - properties.diff && - Object.keys(properties.diff).length === 0; + const payloadsWithDiff = payloads + .filter(({ properties }) => { + const isDiffEmpty = + properties.diff !== null && + properties.diff && + Object.keys(properties.diff).length === 0; - return !isDiffEmpty; - }) - .map(({ properties, ...rest }) => ({ - ...rest, - properties: isDefined(properties.diff) - ? { diff: properties.diff } - : {}, - })); + return !isDiffEmpty; + }) + .map(({ properties, ...rest }) => ({ + ...rest, + properties: isDefined(properties.diff) + ? { diff: properties.diff } + : {}, + })); - const payloadsToInsert: TimelineActivityPayloadWorkspaceIdAndObjectSingularName['payloads'] = - []; + const payloadsToInsert: TimelineActivityPayloadWorkspaceIdAndObjectSingularName['payloads'] = + []; - const timelineActivityPropertyName = - await this.getTimelineActivityPropertyName( - objectSingularName, - isFeatureFlagTimelineActivityMigrated, - ); - - for (const payload of payloadsWithDiff) { - const recentTimelineActivity = recentTimelineActivities.find( - (timelineActivity) => - timelineActivity[timelineActivityPropertyName] === - payload.recordId && - timelineActivity.workspaceMemberId === - payload.workspaceMemberId && - (!isDefined(payload.linkedRecordId) || - timelineActivity.linkedRecordId === payload.linkedRecordId) && - timelineActivity.name === payload.name, - ); - - if (recentTimelineActivity) { - const mergedProperties = objectRecordDiffMerge( - recentTimelineActivity.properties, - payload.properties, - ); - - await this.updateTimelineActivity({ - id: recentTimelineActivity.id, - properties: mergedProperties, - workspaceMemberId: payload.workspaceMemberId, - workspaceId, - }); - } else { - payloadsToInsert.push(payload); - } - } - - await this.insertTimelineActivities({ + const timelineActivityPropertyName = + await this.getTimelineActivityPropertyName( objectSingularName, - payloads: payloadsToInsert, - workspaceId, isFeatureFlagTimelineActivityMigrated, - }); - }, - ); + ); + + for (const payload of payloadsWithDiff) { + const recentTimelineActivity = recentTimelineActivities.find( + (timelineActivity) => + timelineActivity[timelineActivityPropertyName] === + payload.recordId && + timelineActivity.workspaceMemberId === payload.workspaceMemberId && + (!isDefined(payload.linkedRecordId) || + timelineActivity.linkedRecordId === payload.linkedRecordId) && + timelineActivity.name === payload.name, + ); + + if (recentTimelineActivity) { + const mergedProperties = objectRecordDiffMerge( + recentTimelineActivity.properties, + payload.properties, + ); + + await this.updateTimelineActivity({ + id: recentTimelineActivity.id, + properties: mergedProperties, + workspaceMemberId: payload.workspaceMemberId, + workspaceId, + }); + } else { + payloadsToInsert.push(payload); + } + } + + await this.insertTimelineActivities({ + objectSingularName, + payloads: payloadsToInsert, + workspaceId, + isFeatureFlagTimelineActivityMigrated, + }); + }, authContext); } private async findRecentTimelineActivities({ diff --git a/packages/twenty-server/src/modules/timeline/services/timeline-activity.service.ts b/packages/twenty-server/src/modules/timeline/services/timeline-activity.service.ts index 3dad7e9b87c..6325d14ba38 100644 --- a/packages/twenty-server/src/modules/timeline/services/timeline-activity.service.ts +++ b/packages/twenty-server/src/modules/timeline/services/timeline-activity.service.ts @@ -192,7 +192,6 @@ export class TimelineActivityService { const activityTargets = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const activityTargetRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -209,6 +208,7 @@ export class TimelineActivityService { }, }); }, + authContext, ); if (activityTargets.length === 0) { @@ -277,7 +277,6 @@ export class TimelineActivityService { const activities = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const activityRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -303,6 +302,7 @@ export class TimelineActivityService { }, }); }, + authContext, ); if (activities.length === 0) { diff --git a/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-create-many.post-query.hook.ts b/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-create-many.post-query.hook.ts index 84b2f501790..a862972630e 100644 --- a/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-create-many.post-query.hook.ts +++ b/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-create-many.post-query.hook.ts @@ -36,37 +36,34 @@ export class WorkflowCreateManyPostQueryHook assertIsDefinedOrThrow(workspace, WorkspaceNotFoundDefaultError); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext as WorkspaceAuthContext, - async () => { - const workflowVersionRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspace.id, - 'workflowVersion', - ); - - const position = await this.recordPositionService.buildRecordPosition({ - value: 'first', - objectMetadata: { - isCustom: false, - nameSingular: 'workflowVersion', - }, - workspaceId: workspace.id, - }); - - const workflowVersionsToCreate = payload.map((workflow) => ({ - workflowId: workflow.id, - status: WorkflowVersionStatus.DRAFT, - name: 'v1', - position, - })); - - await Promise.all( - workflowVersionsToCreate.map((workflowVersion) => { - return workflowVersionRepository.insert(workflowVersion); - }), + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workflowVersionRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspace.id, + 'workflowVersion', ); - }, - ); + + const position = await this.recordPositionService.buildRecordPosition({ + value: 'first', + objectMetadata: { + isCustom: false, + nameSingular: 'workflowVersion', + }, + workspaceId: workspace.id, + }); + + const workflowVersionsToCreate = payload.map((workflow) => ({ + workflowId: workflow.id, + status: WorkflowVersionStatus.DRAFT, + name: 'v1', + position, + })); + + await Promise.all( + workflowVersionsToCreate.map((workflowVersion) => { + return workflowVersionRepository.insert(workflowVersion); + }), + ); + }, authContext as WorkspaceAuthContext); } } diff --git a/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-create-one.post-query.hook.ts b/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-create-one.post-query.hook.ts index 566d2bf7232..9843f602e68 100644 --- a/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-create-one.post-query.hook.ts +++ b/packages/twenty-server/src/modules/workflow/common/query-hooks/workflow-create-one.post-query.hook.ts @@ -38,31 +38,28 @@ export class WorkflowCreateOnePostQueryHook const workflow = payload[0]; - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext as WorkspaceAuthContext, - async () => { - const workflowVersionRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspace.id, - 'workflowVersion', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workflowVersionRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspace.id, + 'workflowVersion', + ); - const position = await this.recordPositionService.buildRecordPosition({ - value: 'first', - objectMetadata: { - isCustom: false, - nameSingular: 'workflowVersion', - }, - workspaceId: workspace.id, - }); + const position = await this.recordPositionService.buildRecordPosition({ + value: 'first', + objectMetadata: { + isCustom: false, + nameSingular: 'workflowVersion', + }, + workspaceId: workspace.id, + }); - await workflowVersionRepository.insert({ - workflowId: workflow.id, - status: WorkflowVersionStatus.DRAFT, - name: 'v1', - position, - }); - }, - ); + await workflowVersionRepository.insert({ + workflowId: workflow.id, + status: WorkflowVersionStatus.DRAFT, + name: 'v1', + position, + }); + }, authContext as WorkspaceAuthContext); } } diff --git a/packages/twenty-server/src/modules/workflow/common/workspace-services/workflow-common.workspace-service.ts b/packages/twenty-server/src/modules/workflow/common/workspace-services/workflow-common.workspace-service.ts index 82fa308abd2..e3273a6b5ae 100644 --- a/packages/twenty-server/src/modules/workflow/common/workspace-services/workflow-common.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/common/workspace-services/workflow-common.workspace-service.ts @@ -62,7 +62,6 @@ export class WorkflowCommonWorkspaceService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workflowVersionRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -79,6 +78,7 @@ export class WorkflowCommonWorkspaceService { return this.getValidWorkflowVersionOrFail(workflowVersion); }, + authContext, ); } @@ -165,78 +165,75 @@ export class WorkflowCommonWorkspaceService { }): Promise { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const workflowVersionRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'workflowVersion', - { shouldBypassPermissionChecks: true }, - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workflowVersionRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'workflowVersion', + { shouldBypassPermissionChecks: true }, + ); - const workflowRunRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'workflowRun', - { shouldBypassPermissionChecks: true }, - ); + const workflowRunRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'workflowRun', + { shouldBypassPermissionChecks: true }, + ); - const workflowAutomatedTriggerRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'workflowAutomatedTrigger', - { shouldBypassPermissionChecks: true }, - ); + const workflowAutomatedTriggerRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'workflowAutomatedTrigger', + { shouldBypassPermissionChecks: true }, + ); - for (const workflowId of workflowIds) { - switch (operation) { - case 'delete': - await workflowAutomatedTriggerRepository.softDelete({ - workflowId, - }); + for (const workflowId of workflowIds) { + switch (operation) { + case 'delete': + await workflowAutomatedTriggerRepository.softDelete({ + workflowId, + }); - await workflowRunRepository.softDelete({ - workflowId, - }); + await workflowRunRepository.softDelete({ + workflowId, + }); - await workflowVersionRepository.softDelete({ - workflowId, - }); + await workflowVersionRepository.softDelete({ + workflowId, + }); - break; - case 'restore': - await workflowAutomatedTriggerRepository.restore({ - workflowId, - }); + break; + case 'restore': + await workflowAutomatedTriggerRepository.restore({ + workflowId, + }); - await workflowRunRepository.restore({ - workflowId, - }); + await workflowRunRepository.restore({ + workflowId, + }); - await workflowVersionRepository.restore({ - workflowId, - }); + await workflowVersionRepository.restore({ + workflowId, + }); - break; - } - - await this.deactivateVersionOnDelete({ - workflowVersionRepository, - workflowId, - workspaceId, - operation, - }); - - await this.handleServerlessFunctionSubEntities({ - workflowVersionRepository, - workflowId, - workspaceId, - operation, - }); + break; } - }, - ); + + await this.deactivateVersionOnDelete({ + workflowVersionRepository, + workflowId, + workspaceId, + operation, + }); + + await this.handleServerlessFunctionSubEntities({ + workflowVersionRepository, + workflowId, + workspaceId, + operation, + }); + } + }, authContext); } private async deactivateVersionOnDelete({ diff --git a/packages/twenty-server/src/modules/workflow/common/workspace-services/workflow-version-validation.workspace-service.ts b/packages/twenty-server/src/modules/workflow/common/workspace-services/workflow-version-validation.workspace-service.ts index cea81f7800d..130715207b3 100644 --- a/packages/twenty-server/src/modules/workflow/common/workspace-services/workflow-version-validation.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/common/workspace-services/workflow-version-validation.workspace-service.ts @@ -48,36 +48,33 @@ export class WorkflowVersionValidationWorkspaceService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const workflowVersionRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'workflowVersion', - { shouldBypassPermissionChecks: true }, - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workflowVersionRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'workflowVersion', + { shouldBypassPermissionChecks: true }, + ); - const workflowAlreadyHasDraftVersion = - await workflowVersionRepository.exists({ - where: { - workflowId: payload.data.workflowId, - status: WorkflowVersionStatus.DRAFT, - deletedAt: IsNull(), - }, - }); + const workflowAlreadyHasDraftVersion = + await workflowVersionRepository.exists({ + where: { + workflowId: payload.data.workflowId, + status: WorkflowVersionStatus.DRAFT, + deletedAt: IsNull(), + }, + }); - if (workflowAlreadyHasDraftVersion) { - throw new WorkflowQueryValidationException( - 'Cannot create multiple draft versions for the same workflow', - WorkflowQueryValidationExceptionCode.FORBIDDEN, - { - userFriendlyMessage: msg`Cannot create multiple draft versions for the same workflow`, - }, - ); - } - }, - ); + if (workflowAlreadyHasDraftVersion) { + throw new WorkflowQueryValidationException( + 'Cannot create multiple draft versions for the same workflow', + WorkflowQueryValidationExceptionCode.FORBIDDEN, + { + userFriendlyMessage: msg`Cannot create multiple draft versions for the same workflow`, + }, + ); + } + }, authContext); } async validateWorkflowVersionForUpdateOne({ @@ -130,35 +127,33 @@ export class WorkflowVersionValidationWorkspaceService { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const workflowVersionRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'workflowVersion', - { shouldBypassPermissionChecks: true }, - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workflowVersionRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'workflowVersion', + { shouldBypassPermissionChecks: true }, + ); - const otherWorkflowVersionsExist = - await workflowVersionRepository.exists({ - where: { - workflowId: workflowVersion.workflowId, - deletedAt: IsNull(), - id: Not(workflowVersion.id), - }, - }); + const otherWorkflowVersionsExist = await workflowVersionRepository.exists( + { + where: { + workflowId: workflowVersion.workflowId, + deletedAt: IsNull(), + id: Not(workflowVersion.id), + }, + }, + ); - if (!otherWorkflowVersionsExist) { - throw new WorkflowQueryValidationException( - 'The initial version of a workflow can not be deleted', - WorkflowQueryValidationExceptionCode.FORBIDDEN, - { - userFriendlyMessage: msg`The initial version of a workflow can not be deleted`, - }, - ); - } - }, - ); + if (!otherWorkflowVersionsExist) { + throw new WorkflowQueryValidationException( + 'The initial version of a workflow can not be deleted', + WorkflowQueryValidationExceptionCode.FORBIDDEN, + { + userFriendlyMessage: msg`The initial version of a workflow can not be deleted`, + }, + ); + } + }, authContext); } } diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/__tests__/workflow-version-edge.workspace-service.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/__tests__/workflow-version-edge.workspace-service.spec.ts index 267ea7f9f9c..0a1effb5570 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/__tests__/workflow-version-edge.workspace-service.spec.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/__tests__/workflow-version-edge.workspace-service.spec.ts @@ -91,7 +91,9 @@ describe('WorkflowVersionEdgeWorkspaceService', () => { globalWorkspaceOrmManager = { executeInWorkspaceContext: jest .fn() - .mockImplementation(async (_authContext, callback) => callback()), + .mockImplementation(async (callback: () => any, _authContext?: any) => + callback(), + ), getRepository: jest .fn() .mockResolvedValue(mockWorkflowVersionWorkspaceRepository), diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service.ts index 221444fc1f1..94b3a8fbf9d 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service.ts @@ -45,7 +45,6 @@ export class WorkflowVersionEdgeWorkspaceService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workflowVersionRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -96,6 +95,7 @@ export class WorkflowVersionEdgeWorkspaceService { }); } }, + authContext, ); } @@ -115,7 +115,6 @@ export class WorkflowVersionEdgeWorkspaceService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workflowVersionRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -166,6 +165,7 @@ export class WorkflowVersionEdgeWorkspaceService { }); } }, + authContext, ); } diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/__tests__/workflow-version-step.workspace-service.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/__tests__/workflow-version-step.workspace-service.spec.ts index 8d265c2ab28..15601291095 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/__tests__/workflow-version-step.workspace-service.spec.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/__tests__/workflow-version-step.workspace-service.spec.ts @@ -115,7 +115,7 @@ describe('WorkflowVersionStepWorkspaceService', () => { executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), } as unknown as jest.Mocked; const module: TestingModule = await Test.createTestingModule({ diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-helpers.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-helpers.workspace-service.ts index 644496fd570..28f48497293 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-helpers.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-helpers.workspace-service.ts @@ -46,28 +46,25 @@ export class WorkflowVersionStepHelpersWorkspaceService { }): Promise { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const workflowVersionRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'workflowVersion', - { shouldBypassPermissionChecks: true }, - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workflowVersionRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'workflowVersion', + { shouldBypassPermissionChecks: true }, + ); - const updateData: Partial = {}; + const updateData: Partial = {}; - if (steps !== undefined) { - updateData.steps = steps; - } + if (steps !== undefined) { + updateData.steps = steps; + } - if (trigger !== undefined) { - updateData.trigger = trigger; - } + if (trigger !== undefined) { + updateData.trigger = trigger; + } - await workflowVersionRepository.update(workflowVersionId, updateData); - }, - ); + await workflowVersionRepository.update(workflowVersionId, updateData); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-operations.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-operations.workspace-service.ts index 299799ca993..238da56b1c3 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-operations.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-operations.workspace-service.ts @@ -533,7 +533,6 @@ export class WorkflowVersionStepOperationsWorkspaceService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const responseKeys = Object.keys(response); @@ -598,6 +597,7 @@ export class WorkflowVersionStepOperationsWorkspaceService { return acc; }, {}); }, + authContext, ); } @@ -727,7 +727,6 @@ export class WorkflowVersionStepOperationsWorkspaceService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workflowVersionRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -777,6 +776,7 @@ export class WorkflowVersionStepOperationsWorkspaceService { return emptyNodeStep; }, + authContext, ); } @@ -797,7 +797,6 @@ export class WorkflowVersionStepOperationsWorkspaceService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workflowVersionRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -878,6 +877,7 @@ export class WorkflowVersionStepOperationsWorkspaceService { branches, }; }, + authContext, ); } diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-version.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-version.workspace-service.ts index fc52faa7b2f..1a87c4b9321 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-version.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version/workflow-version.workspace-service.ts @@ -50,7 +50,6 @@ export class WorkflowVersionWorkspaceService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workflowVersionRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -139,6 +138,7 @@ export class WorkflowVersionWorkspaceService { trigger: newWorkflowVersionTrigger, }; }, + authContext, ); } @@ -154,7 +154,6 @@ export class WorkflowVersionWorkspaceService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workflowRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -311,6 +310,7 @@ export class WorkflowVersionWorkspaceService { trigger: remappedTrigger ?? null, }; }, + authContext, ); } @@ -325,61 +325,55 @@ export class WorkflowVersionWorkspaceService { }) { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const workflowVersionRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'workflowVersion', - { shouldBypassPermissionChecks: true }, - ); - - const workflowVersion = await workflowVersionRepository.findOneOrFail({ - where: { - id: workflowVersionId, - }, - }); - - assertWorkflowVersionIsDraft(workflowVersion); - - const triggerPosition = positions.find( - (position) => position.id === TRIGGER_STEP_ID, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workflowVersionRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'workflowVersion', + { shouldBypassPermissionChecks: true }, ); - const updatedTrigger = - isDefined(triggerPosition) && isDefined(workflowVersion.trigger) - ? { - ...workflowVersion.trigger, - position: triggerPosition.position, - } - : undefined; + const workflowVersion = await workflowVersionRepository.findOneOrFail({ + where: { + id: workflowVersionId, + }, + }); - const updatedSteps = workflowVersion.steps?.map((step) => { - const updatedStep = positions.find( - (position) => position.id === step.id, - ); + assertWorkflowVersionIsDraft(workflowVersion); - if (updatedStep) { - return { - ...step, - position: updatedStep.position, - }; - } + const triggerPosition = positions.find( + (position) => position.id === TRIGGER_STEP_ID, + ); - return step; - }); + const updatedTrigger = + isDefined(triggerPosition) && isDefined(workflowVersion.trigger) + ? { + ...workflowVersion.trigger, + position: triggerPosition.position, + } + : undefined; - const updatePayload = { - ...(!isDefined(updatedTrigger) ? {} : { trigger: updatedTrigger }), - ...(!isDefined(updatedSteps) ? {} : { steps: updatedSteps }), - }; - - await workflowVersionRepository.update( - workflowVersionId, - updatePayload, + const updatedSteps = workflowVersion.steps?.map((step) => { + const updatedStep = positions.find( + (position) => position.id === step.id, ); - }, - ); + + if (updatedStep) { + return { + ...step, + position: updatedStep.position, + }; + } + + return step; + }); + + const updatePayload = { + ...(!isDefined(updatedTrigger) ? {} : { trigger: updatedTrigger }), + ...(!isDefined(updatedSteps) ? {} : { steps: updatedSteps }), + }; + + await workflowVersionRepository.update(workflowVersionId, updatePayload); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/delay/jobs/resume-delayed-workflow.job.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/delay/jobs/resume-delayed-workflow.job.ts index 25fb5769916..36d3dda37c0 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/delay/jobs/resume-delayed-workflow.job.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/delay/jobs/resume-delayed-workflow.job.ts @@ -41,72 +41,69 @@ export class ResumeDelayedWorkflowJob { }: ResumeDelayedWorkflowJobData): Promise { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - try { - const workflowRun = - await this.workflowRunWorkspaceService.getWorkflowRunOrFail({ - workflowRunId, - workspaceId, - }); - - if (workflowRun.status !== WorkflowRunStatus.RUNNING) { - return; - } - - const step = workflowRun.state?.flow?.steps?.find( - (step) => step.id === stepId, - ); - - const stepInfo = workflowRun.state?.stepInfos[stepId]; - - if (!step || !isWorkflowDelayAction(step)) { - throw new WorkflowRunException( - 'Step not found or is not a delay action', - WorkflowRunExceptionCode.INVALID_INPUT, - ); - } - - if (stepInfo?.status !== StepStatus.PENDING) { - throw new WorkflowRunException( - 'Step is not pending', - WorkflowRunExceptionCode.INVALID_INPUT, - ); - } - - await this.workflowRunWorkspaceService.updateWorkflowRunStepInfo({ - stepId, - stepInfo: { - status: StepStatus.SUCCESS, - result: { - success: true, - }, - }, - workspaceId, - workflowRunId, - }); - - await this.messageQueueService.add( - RunWorkflowJob.name, - { - workspaceId, - workflowRunId, - lastExecutedStepId: stepId, - }, - ); - } catch (error) { - await this.workflowRunWorkspaceService.endWorkflowRun({ + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + try { + const workflowRun = + await this.workflowRunWorkspaceService.getWorkflowRunOrFail({ workflowRunId, workspaceId, - status: WorkflowRunStatus.FAILED, - error: - error instanceof Error - ? error.message - : 'Unknown error during delay resume', }); + + if (workflowRun.status !== WorkflowRunStatus.RUNNING) { + return; } - }, - ); + + const step = workflowRun.state?.flow?.steps?.find( + (step) => step.id === stepId, + ); + + const stepInfo = workflowRun.state?.stepInfos[stepId]; + + if (!step || !isWorkflowDelayAction(step)) { + throw new WorkflowRunException( + 'Step not found or is not a delay action', + WorkflowRunExceptionCode.INVALID_INPUT, + ); + } + + if (stepInfo?.status !== StepStatus.PENDING) { + throw new WorkflowRunException( + 'Step is not pending', + WorkflowRunExceptionCode.INVALID_INPUT, + ); + } + + await this.workflowRunWorkspaceService.updateWorkflowRunStepInfo({ + stepId, + stepInfo: { + status: StepStatus.SUCCESS, + result: { + success: true, + }, + }, + workspaceId, + workflowRunId, + }); + + await this.messageQueueService.add( + RunWorkflowJob.name, + { + workspaceId, + workflowRunId, + lastExecutedStepId: stepId, + }, + ); + } catch (error) { + await this.workflowRunWorkspaceService.endWorkflowRun({ + workflowRunId, + workspaceId, + status: WorkflowRunStatus.FAILED, + error: + error instanceof Error + ? error.message + : 'Unknown error during delay resume', + }); + } + }, authContext); } } diff --git a/packages/twenty-server/src/modules/workflow/workflow-runner/jobs/run-workflow.job.ts b/packages/twenty-server/src/modules/workflow/workflow-runner/jobs/run-workflow.job.ts index 40892886f89..75211ca8f53 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-runner/jobs/run-workflow.job.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-runner/jobs/run-workflow.job.ts @@ -39,32 +39,29 @@ export class RunWorkflowJob { }: RunWorkflowJobData): Promise { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - try { - if (lastExecutedStepId) { - await this.resumeWorkflowExecution({ - workspaceId, - workflowRunId, - lastExecutedStepId, - }); - } else { - await this.startWorkflowExecution({ - workflowRunId, - workspaceId, - }); - } - } catch (error) { - await this.workflowRunWorkspaceService.endWorkflowRun({ + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + try { + if (lastExecutedStepId) { + await this.resumeWorkflowExecution({ workspaceId, workflowRunId, - status: WorkflowRunStatus.FAILED, - error: error.message, + lastExecutedStepId, + }); + } else { + await this.startWorkflowExecution({ + workflowRunId, + workspaceId, }); } - }, - ); + } catch (error) { + await this.workflowRunWorkspaceService.endWorkflowRun({ + workspaceId, + workflowRunId, + status: WorkflowRunStatus.FAILED, + error: error.message, + }); + } + }, authContext); } private async startWorkflowExecution({ diff --git a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run-queue/cron/jobs/workflow-clean-workflow-runs.cron.job.ts b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run-queue/cron/jobs/workflow-clean-workflow-runs.cron.job.ts index 199ed4b4a39..4f75ef8f03d 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run-queue/cron/jobs/workflow-clean-workflow-runs.cron.job.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run-queue/cron/jobs/workflow-clean-workflow-runs.cron.job.ts @@ -76,11 +76,9 @@ export class WorkflowCleanWorkflowRunsJob { const schemaName = getWorkspaceSchemaName(workspaceId); const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const workflowRunsToDelete = await this.coreDataSource.query( - ` + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workflowRunsToDelete = await this.coreDataSource.query( + ` WITH ranked_runs AS ( SELECT id, ROW_NUMBER() OVER ( @@ -95,23 +93,22 @@ export class WorkflowCleanWorkflowRunsJob { WHERE rn > ${NUMBER_OF_WORKFLOW_RUNS_TO_KEEP} OR "createdAt" < NOW() - INTERVAL '14 days'; `, + ); + + const workflowRunRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + WorkflowRunWorkspaceEntity, + { shouldBypassPermissionChecks: true }, ); - const workflowRunRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - WorkflowRunWorkspaceEntity, - { shouldBypassPermissionChecks: true }, - ); + for (const workflowRunToDelete of workflowRunsToDelete) { + await workflowRunRepository.delete(workflowRunToDelete.id); + } - for (const workflowRunToDelete of workflowRunsToDelete) { - await workflowRunRepository.delete(workflowRunToDelete.id); - } - - this.logger.log( - `Deleted ${workflowRunsToDelete.length} workflow runs for workspace ${workspaceId}`, - ); - }, - ); + this.logger.log( + `Deleted ${workflowRunsToDelete.length} workflow runs for workspace ${workspaceId}`, + ); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run-queue/workspace-services/workflow-handle-staled-runs.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run-queue/workspace-services/workflow-handle-staled-runs.workspace-service.ts index 8275127139a..ed03548ede6 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run-queue/workspace-services/workflow-handle-staled-runs.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run-queue/workspace-services/workflow-handle-staled-runs.workspace-service.ts @@ -51,41 +51,38 @@ export class WorkflowHandleStaledRunsWorkspaceService { private async handleStaledRunsForWorkspace(workspaceId: string) { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const workflowRunRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - WorkflowRunWorkspaceEntity, - { shouldBypassPermissionChecks: true }, - ); - - const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000); - - const staledWorkflowRuns = await workflowRunRepository.find({ - where: { - status: WorkflowRunStatus.ENQUEUED, - enqueuedAt: Or(LessThan(oneHourAgo), IsNull()), - }, - }); - - if (staledWorkflowRuns.length <= 0) { - return; - } - - await workflowRunRepository.update( - staledWorkflowRuns.map((workflowRun) => workflowRun.id), - { - enqueuedAt: null, - status: WorkflowRunStatus.NOT_STARTED, - }, - ); - - await this.workflowThrottlingWorkspaceService.recomputeWorkflowRunNotStartedCount( + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workflowRunRepository = + await this.globalWorkspaceOrmManager.getRepository( workspaceId, + WorkflowRunWorkspaceEntity, + { shouldBypassPermissionChecks: true }, ); - }, - ); + + const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000); + + const staledWorkflowRuns = await workflowRunRepository.find({ + where: { + status: WorkflowRunStatus.ENQUEUED, + enqueuedAt: Or(LessThan(oneHourAgo), IsNull()), + }, + }); + + if (staledWorkflowRuns.length <= 0) { + return; + } + + await workflowRunRepository.update( + staledWorkflowRuns.map((workflowRun) => workflowRun.id), + { + enqueuedAt: null, + status: WorkflowRunStatus.NOT_STARTED, + }, + ); + + await this.workflowThrottlingWorkspaceService.recomputeWorkflowRunNotStartedCount( + workspaceId, + ); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run-queue/workspace-services/workflow-run-enqueue.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run-queue/workspace-services/workflow-run-enqueue.workspace-service.ts index 5a56e06ce82..d19d2e49965 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run-queue/workspace-services/workflow-run-enqueue.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run-queue/workspace-services/workflow-run-enqueue.workspace-service.ts @@ -48,7 +48,6 @@ export class WorkflowRunEnqueueWorkspaceService { const authContext = buildSystemAuthContext(workspaceId); await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workflowRunRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -152,6 +151,7 @@ export class WorkflowRunEnqueueWorkspaceService { ); } }, + authContext, ); } catch (error) { this.metricsService.incrementCounter({ diff --git a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run-queue/workspace-services/workflow-throttling.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run-queue/workspace-services/workflow-throttling.workspace-service.ts index 62c62e51eda..43c1cb2078c 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run-queue/workspace-services/workflow-throttling.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run-queue/workspace-services/workflow-throttling.workspace-service.ts @@ -80,7 +80,6 @@ export class WorkflowThrottlingWorkspaceService { const currentlyNotStartedWorkflowRunCount = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workflowRunRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -95,6 +94,7 @@ export class WorkflowThrottlingWorkspaceService { }, }); }, + authContext, ); await this.setWorkflowRunNotStartedCount( @@ -113,7 +113,6 @@ export class WorkflowThrottlingWorkspaceService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workflowRunRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -128,6 +127,7 @@ export class WorkflowThrottlingWorkspaceService { }, }); }, + authContext, ); } diff --git a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/command/delete-workflow-runs.command.ts b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/command/delete-workflow-runs.command.ts index b7cce596ca1..f55ddca45ef 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/command/delete-workflow-runs.command.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/command/delete-workflow-runs.command.ts @@ -53,38 +53,35 @@ export class DeleteWorkflowRunsCommand extends ActiveOrSuspendedWorkspacesMigrat }: RunOnWorkspaceArgs): Promise { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - try { - const workflowRunRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'workflowRun', - { shouldBypassPermissionChecks: true }, - ); - - const createdAtCondition = { - createdAt: LessThan( - this.createdBeforeDate || new Date().toISOString(), - ), - }; - - const workflowRunCount = await workflowRunRepository.count({ - where: createdAtCondition, - }); - - if (!options.dryRun && workflowRunCount > 0) { - await workflowRunRepository.delete(createdAtCondition); - } - - this.logger.log( - `${options.dryRun ? ' (DRY RUN): ' : ''}Deleted ${workflowRunCount} workflow runs`, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + try { + const workflowRunRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'workflowRun', + { shouldBypassPermissionChecks: true }, ); - } catch (error) { - this.logger.error('Error while deleting workflowRun', error); + + const createdAtCondition = { + createdAt: LessThan( + this.createdBeforeDate || new Date().toISOString(), + ), + }; + + const workflowRunCount = await workflowRunRepository.count({ + where: createdAtCondition, + }); + + if (!options.dryRun && workflowRunCount > 0) { + await workflowRunRepository.delete(createdAtCondition); } - }, - ); + + this.logger.log( + `${options.dryRun ? ' (DRY RUN): ' : ''}Deleted ${workflowRunCount} workflow runs`, + ); + } catch (error) { + this.logger.error('Error while deleting workflowRun', error); + } + }, authContext); } } diff --git a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service.ts index d38438fc779..8ce7fbe623e 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service.ts @@ -57,7 +57,6 @@ export class WorkflowRunWorkspaceService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workflowRunRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -136,6 +135,7 @@ export class WorkflowRunWorkspaceService { return workflowRun.id; }, + authContext, ); } @@ -346,7 +346,6 @@ export class WorkflowRunWorkspaceService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workflowRunRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -359,6 +358,7 @@ export class WorkflowRunWorkspaceService { where: { id: workflowRunId }, }); }, + authContext, ); } @@ -395,35 +395,32 @@ export class WorkflowRunWorkspaceService { }) { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const workflowRunRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'workflowRun', - { shouldBypassPermissionChecks: true }, - ); - - const workflowRunToUpdate = await workflowRunRepository.findOneBy({ - id: workflowRunId, - }); - - if (!workflowRunToUpdate) { - throw new WorkflowRunException( - `workflowRun ${workflowRunId} not found`, - WorkflowRunExceptionCode.WORKFLOW_RUN_NOT_FOUND, - ); - } - - await workflowRunRepository.update( - workflowRunToUpdate.id, - partialUpdate, - undefined, - ['id'], + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workflowRunRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'workflowRun', + { shouldBypassPermissionChecks: true }, ); - }, - ); + + const workflowRunToUpdate = await workflowRunRepository.findOneBy({ + id: workflowRunId, + }); + + if (!workflowRunToUpdate) { + throw new WorkflowRunException( + `workflowRun ${workflowRunId} not found`, + WorkflowRunExceptionCode.WORKFLOW_RUN_NOT_FOUND, + ); + } + + await workflowRunRepository.update( + workflowRunToUpdate.id, + partialUpdate, + undefined, + ['id'], + ); + }, authContext); } private getInitState( diff --git a/packages/twenty-server/src/modules/workflow/workflow-status/jobs/__tests__/workflow-statuses-update.job.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-status/jobs/__tests__/workflow-statuses-update.job.spec.ts index a7facf6c639..da2f8f9218b 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-status/jobs/__tests__/workflow-statuses-update.job.spec.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-status/jobs/__tests__/workflow-statuses-update.job.spec.ts @@ -49,7 +49,7 @@ describe('WorkflowStatusesUpdate', () => { executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), }; const mockServerlessFunctionService = { diff --git a/packages/twenty-server/src/modules/workflow/workflow-status/jobs/workflow-statuses-update.job.ts b/packages/twenty-server/src/modules/workflow/workflow-status/jobs/workflow-statuses-update.job.ts index a428640c3ac..10bbe736f19 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-status/jobs/workflow-statuses-update.job.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-status/jobs/workflow-statuses-update.job.ts @@ -78,36 +78,33 @@ export class WorkflowStatusesUpdateJob { async handle(event: WorkflowVersionBatchEvent): Promise { const authContext = buildSystemAuthContext(event.workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - switch (event.type) { - case WorkflowVersionEventType.CREATE: - case WorkflowVersionEventType.DELETE: - await Promise.all( - event.workflowIds.map((workflowId) => - this.handleWorkflowVersionCreatedOrDeleted({ - workflowId, - workspaceId: event.workspaceId, - }), - ), - ); - break; - case WorkflowVersionEventType.STATUS_UPDATE: - await Promise.all( - event.statusUpdates.map((statusUpdate) => - this.handleWorkflowVersionStatusUpdated({ - statusUpdate, - workspaceId: event.workspaceId, - }), - ), - ); - break; - default: - break; - } - }, - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + switch (event.type) { + case WorkflowVersionEventType.CREATE: + case WorkflowVersionEventType.DELETE: + await Promise.all( + event.workflowIds.map((workflowId) => + this.handleWorkflowVersionCreatedOrDeleted({ + workflowId, + workspaceId: event.workspaceId, + }), + ), + ); + break; + case WorkflowVersionEventType.STATUS_UPDATE: + await Promise.all( + event.statusUpdates.map((statusUpdate) => + this.handleWorkflowVersionStatusUpdated({ + statusUpdate, + workspaceId: event.workspaceId, + }), + ), + ); + break; + default: + break; + } + }, authContext); } private async handleWorkflowVersionCreatedOrDeleted({ diff --git a/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-complete-workflow.tool.ts b/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-complete-workflow.tool.ts index 902a2cd9dad..2b882e8bcb0 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-complete-workflow.tool.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-tools/tools/create-complete-workflow.tool.ts @@ -206,38 +206,35 @@ const createWorkflow = async ({ }): Promise => { const authContext = buildSystemAuthContext(context.workspaceId); - return deps.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const workflowRepository = - await deps.globalWorkspaceOrmManager.getRepository( - context.workspaceId, - 'workflow', - context.rolePermissionConfig, - ); + return deps.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workflowRepository = + await deps.globalWorkspaceOrmManager.getRepository( + context.workspaceId, + 'workflow', + context.rolePermissionConfig, + ); - const workflowPosition = - await deps.recordPositionService.buildRecordPosition({ - value: 'first', - objectMetadata: { - isCustom: false, - nameSingular: 'workflow', - }, - workspaceId: context.workspaceId, - }); + const workflowPosition = + await deps.recordPositionService.buildRecordPosition({ + value: 'first', + objectMetadata: { + isCustom: false, + nameSingular: 'workflow', + }, + workspaceId: context.workspaceId, + }); - const workflow = { - id: uuidv4(), - name, - statuses: [WorkflowStatus.DRAFT], - position: workflowPosition, - }; + const workflow = { + id: uuidv4(), + name, + statuses: [WorkflowStatus.DRAFT], + position: workflowPosition, + }; - await workflowRepository.insert(workflow); + await workflowRepository.insert(workflow); - return workflow.id; - }, - ); + return workflow.id; + }, authContext); }; const createWorkflowVersion = async ({ @@ -255,41 +252,38 @@ const createWorkflowVersion = async ({ }): Promise => { const authContext = buildSystemAuthContext(context.workspaceId); - return deps.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const workflowVersionRepository = - await deps.globalWorkspaceOrmManager.getRepository( - context.workspaceId, - 'workflowVersion', - context.rolePermissionConfig, - ); + return deps.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workflowVersionRepository = + await deps.globalWorkspaceOrmManager.getRepository( + context.workspaceId, + 'workflowVersion', + context.rolePermissionConfig, + ); - const versionPosition = - await deps.recordPositionService.buildRecordPosition({ - value: 'first', - objectMetadata: { - isCustom: false, - nameSingular: 'workflowVersion', - }, - workspaceId: context.workspaceId, - }); + const versionPosition = + await deps.recordPositionService.buildRecordPosition({ + value: 'first', + objectMetadata: { + isCustom: false, + nameSingular: 'workflowVersion', + }, + workspaceId: context.workspaceId, + }); - const workflowVersion = { - id: uuidv4(), - workflowId, - name: 'v1', - status: WorkflowVersionStatus.DRAFT, - trigger, - steps, - position: versionPosition, - }; + const workflowVersion = { + id: uuidv4(), + workflowId, + name: 'v1', + status: WorkflowVersionStatus.DRAFT, + trigger, + steps, + position: versionPosition, + }; - await workflowVersionRepository.insert(workflowVersion); + await workflowVersionRepository.insert(workflowVersion); - return workflowVersion.id; - }, - ); + return workflowVersion.id; + }, authContext); }; const updateWorkflowStatus = async ({ @@ -305,20 +299,17 @@ const updateWorkflowStatus = async ({ }) => { const authContext = buildSystemAuthContext(context.workspaceId); - await deps.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const workflowRepository = - await deps.globalWorkspaceOrmManager.getRepository( - context.workspaceId, - 'workflow', - context.rolePermissionConfig, - ); + await deps.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workflowRepository = + await deps.globalWorkspaceOrmManager.getRepository( + context.workspaceId, + 'workflow', + context.rolePermissionConfig, + ); - await workflowRepository.update(workflowId, { - statuses: [WorkflowStatus.ACTIVE], - lastPublishedVersionId: workflowVersionId, - }); - }, - ); + await workflowRepository.update(workflowId, { + statuses: [WorkflowStatus.ACTIVE], + lastPublishedVersionId: workflowVersionId, + }); + }, authContext); }; diff --git a/packages/twenty-server/src/modules/workflow/workflow-tools/tools/get-workflow-current-version.tool.ts b/packages/twenty-server/src/modules/workflow/workflow-tools/tools/get-workflow-current-version.tool.ts index b9cdbd5cd70..e851b1c978a 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-tools/tools/get-workflow-current-version.tool.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-tools/tools/get-workflow-current-version.tool.ts @@ -35,7 +35,6 @@ export const createGetWorkflowCurrentVersionTool = ( const authContext = buildSystemAuthContext(context.workspaceId); return await deps.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workflowRepository = await deps.globalWorkspaceOrmManager.getRepository( @@ -103,6 +102,7 @@ export const createGetWorkflowCurrentVersionTool = ( }, }; }, + authContext, ); } catch (error) { return { diff --git a/packages/twenty-server/src/modules/workflow/workflow-trigger/automated-trigger/automated-trigger.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-trigger/automated-trigger/automated-trigger.workspace-service.ts index 2673512be8a..2e994f62c26 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-trigger/automated-trigger/automated-trigger.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-trigger/automated-trigger/automated-trigger.workspace-service.ts @@ -27,22 +27,19 @@ export class AutomatedTriggerWorkspaceService { }) { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const workflowAutomatedTriggerRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'workflowAutomatedTrigger', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workflowAutomatedTriggerRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'workflowAutomatedTrigger', + ); - await workflowAutomatedTriggerRepository.insert({ - type, - settings, - workflowId, - }); - }, - ); + await workflowAutomatedTriggerRepository.insert({ + type, + settings, + workflowId, + }); + }, authContext); } async deleteAutomatedTrigger({ @@ -54,17 +51,14 @@ export class AutomatedTriggerWorkspaceService { }) { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const workflowAutomatedTriggerRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - 'workflowAutomatedTrigger', - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workflowAutomatedTriggerRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + 'workflowAutomatedTrigger', + ); - await workflowAutomatedTriggerRepository.delete({ workflowId }); - }, - ); + await workflowAutomatedTriggerRepository.delete({ workflowId }); + }, authContext); } } diff --git a/packages/twenty-server/src/modules/workflow/workflow-trigger/automated-trigger/listeners/__tests__/workflow-database-event-trigger.listener.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-trigger/automated-trigger/listeners/__tests__/workflow-database-event-trigger.listener.spec.ts index 01d0c8d5971..66b5e70b677 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-trigger/automated-trigger/listeners/__tests__/workflow-database-event-trigger.listener.spec.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-trigger/automated-trigger/listeners/__tests__/workflow-database-event-trigger.listener.spec.ts @@ -52,7 +52,7 @@ describe('WorkflowDatabaseEventTriggerListener', () => { getRepository: jest.fn().mockResolvedValue(mockRepository), executeInWorkspaceContext: jest .fn() - .mockImplementation((_authContext: any, fn: () => any) => fn()), + .mockImplementation((fn: () => any, _authContext?: any) => fn()), } as any; messageQueueService = { diff --git a/packages/twenty-server/src/modules/workflow/workflow-trigger/automated-trigger/listeners/workflow-database-event-trigger.listener.ts b/packages/twenty-server/src/modules/workflow/workflow-trigger/automated-trigger/listeners/workflow-database-event-trigger.listener.ts index 3a236d137a9..db4cb59b880 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-trigger/automated-trigger/listeners/workflow-database-event-trigger.listener.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-trigger/automated-trigger/listeners/workflow-database-event-trigger.listener.ts @@ -247,64 +247,60 @@ export class WorkflowDatabaseEventTriggerListener { }) { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const { fieldIdByJoinColumnName } = - buildFieldMapsFromFlatObjectMetadata( - flatFieldMetadataMaps, - flatObjectMetadata, + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const { fieldIdByJoinColumnName } = buildFieldMapsFromFlatObjectMetadata( + flatFieldMetadataMaps, + flatObjectMetadata, + ); + + for (const [joinColumnName, joinFieldId] of Object.entries( + fieldIdByJoinColumnName, + )) { + const joinField = findFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityMaps: flatFieldMetadataMaps, + flatEntityId: joinFieldId, + }); + + const joinRecordIds = records + .map((record) => record[joinColumnName]) + .filter(isDefined); + + if (joinRecordIds.length === 0) { + continue; + } + + const relatedObjectMetadataId = + joinField.relationTargetObjectMetadataId; + + if (!isDefined(relatedObjectMetadataId)) { + continue; + } + + const relatedObjectMetadataNameSingular = + flatObjectMetadataMaps.byId[relatedObjectMetadataId]?.nameSingular; + + if (!isDefined(relatedObjectMetadataNameSingular)) { + continue; + } + + const relatedObjectRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + relatedObjectMetadataNameSingular, + { shouldBypassPermissionChecks: true }, ); - for (const [joinColumnName, joinFieldId] of Object.entries( - fieldIdByJoinColumnName, - )) { - const joinField = findFlatEntityByIdInFlatEntityMapsOrThrow({ - flatEntityMaps: flatFieldMetadataMaps, - flatEntityId: joinFieldId, - }); + const relatedRecords = await relatedObjectRepository.find({ + where: { id: In(joinRecordIds) }, + }); - const joinRecordIds = records - .map((record) => record[joinColumnName]) - .filter(isDefined); - - if (joinRecordIds.length === 0) { - continue; - } - - const relatedObjectMetadataId = - joinField.relationTargetObjectMetadataId; - - if (!isDefined(relatedObjectMetadataId)) { - continue; - } - - const relatedObjectMetadataNameSingular = - flatObjectMetadataMaps.byId[relatedObjectMetadataId]?.nameSingular; - - if (!isDefined(relatedObjectMetadataNameSingular)) { - continue; - } - - const relatedObjectRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - relatedObjectMetadataNameSingular, - { shouldBypassPermissionChecks: true }, - ); - - const relatedRecords = await relatedObjectRepository.find({ - where: { id: In(joinRecordIds) }, - }); - - for (const record of records) { - record[joinField.name] = relatedRecords.find( - (relatedRecord) => relatedRecord.id === record[joinColumnName], - ); - } + for (const record of records) { + record[joinField.name] = relatedRecords.find( + (relatedRecord) => relatedRecord.id === record[joinColumnName], + ); } - }, - ); + } + }, authContext); } private async shouldIgnoreEvent( @@ -339,50 +335,47 @@ export class WorkflowDatabaseEventTriggerListener { const authContext = buildSystemAuthContext(workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - const workflowAutomatedTriggerRepository = - await this.globalWorkspaceOrmManager.getRepository( - workspaceId, - automatedTriggerTableName, - { shouldBypassPermissionChecks: true }, - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + const workflowAutomatedTriggerRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspaceId, + automatedTriggerTableName, + { shouldBypassPermissionChecks: true }, + ); - const eventListeners = await workflowAutomatedTriggerRepository.find({ - where: { - type: AutomatedTriggerType.DATABASE_EVENT, - settings: Raw( - () => - `"${automatedTriggerTableName}"."settings"->>'eventName' = :eventName`, - { eventName: databaseEventName }, - ), - }, - }); + const eventListeners = await workflowAutomatedTriggerRepository.find({ + where: { + type: AutomatedTriggerType.DATABASE_EVENT, + settings: Raw( + () => + `"${automatedTriggerTableName}"."settings"->>'eventName' = :eventName`, + { eventName: databaseEventName }, + ), + }, + }); - for (const eventListener of eventListeners) { - for (const eventPayload of payload.events) { - const shouldTriggerJob = this.shouldTriggerJob({ - eventPayload, - eventListener, - action, - }); + for (const eventListener of eventListeners) { + for (const eventPayload of payload.events) { + const shouldTriggerJob = this.shouldTriggerJob({ + eventPayload, + eventListener, + action, + }); - if (shouldTriggerJob) { - await this.messageQueueService.add( - WorkflowTriggerJob.name, - { - workspaceId, - workflowId: eventListener.workflowId, - payload: eventPayload, - }, - { retryLimit: 3 }, - ); - } + if (shouldTriggerJob) { + await this.messageQueueService.add( + WorkflowTriggerJob.name, + { + workspaceId, + workflowId: eventListener.workflowId, + payload: eventPayload, + }, + { retryLimit: 3 }, + ); } } - }, - ); + } + }, authContext); } private shouldTriggerJob({ diff --git a/packages/twenty-server/src/modules/workflow/workflow-trigger/jobs/workflow-trigger.job.ts b/packages/twenty-server/src/modules/workflow/workflow-trigger/jobs/workflow-trigger.job.ts index 253d36386e7..2ec97cebfd6 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-trigger/jobs/workflow-trigger.job.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-trigger/jobs/workflow-trigger.job.ts @@ -44,81 +44,78 @@ export class WorkflowTriggerJob { async handle(data: WorkflowTriggerJobData): Promise { const authContext = buildSystemAuthContext(data.workspaceId); - await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, - async () => { - try { - const workflowRepository = - await this.globalWorkspaceOrmManager.getRepository( - data.workspaceId, - 'workflow', - { shouldBypassPermissionChecks: true }, - ); + await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => { + try { + const workflowRepository = + await this.globalWorkspaceOrmManager.getRepository( + data.workspaceId, + 'workflow', + { shouldBypassPermissionChecks: true }, + ); - const workflow = await workflowRepository.findOneBy({ - id: data.workflowId, - }); + const workflow = await workflowRepository.findOneBy({ + id: data.workflowId, + }); - if (!workflow) { - throw new WorkflowTriggerException( - `Workflow ${data.workflowId} not found in workspace ${data.workspaceId}`, - WorkflowTriggerExceptionCode.NOT_FOUND, - ); - } - - if (!workflow.lastPublishedVersionId) { - throw new WorkflowTriggerException( - `Workflow ${data.workflowId} has no published version in workspace ${data.workspaceId}`, - WorkflowTriggerExceptionCode.INTERNAL_ERROR, - ); - } - - const workflowVersionRepository = - await this.globalWorkspaceOrmManager.getRepository( - data.workspaceId, - 'workflowVersion', - { shouldBypassPermissionChecks: true }, - ); - - const workflowVersion = await workflowVersionRepository.findOneBy({ - id: workflow.lastPublishedVersionId, - }); - - if (!workflowVersion) { - throw new WorkflowTriggerException( - `Workflow version ${workflow.lastPublishedVersionId} not found in workspace ${data.workspaceId}`, - WorkflowTriggerExceptionCode.NOT_FOUND, - ); - } - if (workflowVersion.status !== WorkflowVersionStatus.ACTIVE) { - throw new WorkflowTriggerException( - `Workflow version ${workflowVersion.id} is not active in workspace ${data.workspaceId}`, - WorkflowTriggerExceptionCode.INTERNAL_ERROR, - ); - } - - await this.workflowRunnerWorkspaceService.run({ - workspaceId: data.workspaceId, - workflowVersionId: workflow.lastPublishedVersionId, - payload: data.payload, - source: { - source: FieldActorSource.WORKFLOW, - name: - isDefined(workflow.name) && !isEmpty(workflow.name) - ? workflow.name - : DEFAULT_WORKFLOW_NAME, - context: {}, - workspaceMemberId: null, - }, - }); - } catch (e) { - await this.messageQueueService.removeCron({ - jobName: WorkflowTriggerJob.name, - jobId: data.workflowId, - }); - handleWorkflowTriggerException(e); + if (!workflow) { + throw new WorkflowTriggerException( + `Workflow ${data.workflowId} not found in workspace ${data.workspaceId}`, + WorkflowTriggerExceptionCode.NOT_FOUND, + ); } - }, - ); + + if (!workflow.lastPublishedVersionId) { + throw new WorkflowTriggerException( + `Workflow ${data.workflowId} has no published version in workspace ${data.workspaceId}`, + WorkflowTriggerExceptionCode.INTERNAL_ERROR, + ); + } + + const workflowVersionRepository = + await this.globalWorkspaceOrmManager.getRepository( + data.workspaceId, + 'workflowVersion', + { shouldBypassPermissionChecks: true }, + ); + + const workflowVersion = await workflowVersionRepository.findOneBy({ + id: workflow.lastPublishedVersionId, + }); + + if (!workflowVersion) { + throw new WorkflowTriggerException( + `Workflow version ${workflow.lastPublishedVersionId} not found in workspace ${data.workspaceId}`, + WorkflowTriggerExceptionCode.NOT_FOUND, + ); + } + if (workflowVersion.status !== WorkflowVersionStatus.ACTIVE) { + throw new WorkflowTriggerException( + `Workflow version ${workflowVersion.id} is not active in workspace ${data.workspaceId}`, + WorkflowTriggerExceptionCode.INTERNAL_ERROR, + ); + } + + await this.workflowRunnerWorkspaceService.run({ + workspaceId: data.workspaceId, + workflowVersionId: workflow.lastPublishedVersionId, + payload: data.payload, + source: { + source: FieldActorSource.WORKFLOW, + name: + isDefined(workflow.name) && !isEmpty(workflow.name) + ? workflow.name + : DEFAULT_WORKFLOW_NAME, + context: {}, + workspaceMemberId: null, + }, + }); + } catch (e) { + await this.messageQueueService.removeCron({ + jobName: WorkflowTriggerJob.name, + jobId: data.workflowId, + }); + handleWorkflowTriggerException(e); + } + }, authContext); } } diff --git a/packages/twenty-server/src/modules/workflow/workflow-trigger/workspace-services/workflow-trigger.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-trigger/workspace-services/workflow-trigger.workspace-service.ts index c93e4c419dd..6df0328dba1 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-trigger/workspace-services/workflow-trigger.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-trigger/workspace-services/workflow-trigger.workspace-service.ts @@ -82,7 +82,6 @@ export class WorkflowTriggerWorkspaceService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workflowVersionRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -132,6 +131,7 @@ export class WorkflowTriggerWorkspaceService { return true; }, + authContext, ); } @@ -142,7 +142,6 @@ export class WorkflowTriggerWorkspaceService { const authContext = buildSystemAuthContext(workspaceId); return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext, async () => { const workflowVersionRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -159,6 +158,7 @@ export class WorkflowTriggerWorkspaceService { return true; }, + authContext, ); } diff --git a/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-delete-one.post-query.hook.ts b/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-delete-one.post-query.hook.ts index 78bc1d7b42f..8c6c41bb897 100644 --- a/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-delete-one.post-query.hook.ts +++ b/packages/twenty-server/src/modules/workspace-member/query-hooks/workspace-member-delete-one.post-query.hook.ts @@ -63,7 +63,6 @@ export class WorkspaceMemberDeleteOnePostQueryHook const workspaceMember = await this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext as WorkspaceAuthContext, async () => { const workspaceMemberRepository = await this.globalWorkspaceOrmManager.getRepository( @@ -78,6 +77,7 @@ export class WorkspaceMemberDeleteOnePostQueryHook withDeleted: true, }); }, + authContext as WorkspaceAuthContext, ); if (!isDefined(workspaceMember)) {