From 4dd6edb35a871a305699f2ac1dbcd726445fb918 Mon Sep 17 00:00:00 2001 From: Weiko Date: Fri, 14 Nov 2025 10:59:15 +0100 Subject: [PATCH] Fix authContext missing in ALS WorkspaceContext for global datasource (#15810) Fixes https://github.com/twentyhq/twenty/issues/15809 ## Context We recently introduced a global datasource now consuming workspace context from ALS store however the authContext was missing (only the workspaceId was there) which "broke" event emission, now missing the workspaceMemberId This PR adds the missing authContext so we can access from anywhere in the datasource. We are still passing it as a parameters on repository level for legacy but in theory we should be able to remove it from everywhere and consume the context Screenshot 2025-11-13 at 18 58 16 --- .../common-base-query-runner.service.ts | 2 +- .../global-workspace-datasource.ts | 30 ++++++++++++------- .../global-workspace-orm.manager.ts | 19 +++++++----- .../storage/workspace-context.storage.ts | 16 +++++----- 4 files changed, 40 insertions(+), 27 deletions(-) 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 8358c1299a..92d039d704 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 @@ -134,7 +134,7 @@ export abstract class CommonBaseQueryRunnerService< if (isGlobalDatasourceEnabled) { return this.globalWorkspaceOrmManager.executeInWorkspaceContext( - authContext.workspace.id, + authContext, async () => this.executeQueryAndEnrichResults( processedArgs, diff --git a/packages/twenty-server/src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource.ts b/packages/twenty-server/src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource.ts index 58182579c4..d77707a7d6 100644 --- a/packages/twenty-server/src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource.ts +++ b/packages/twenty-server/src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource.ts @@ -16,10 +16,9 @@ import { EntitySchemaTransformer } from 'typeorm/entity-schema/EntitySchemaTrans import { EntityMetadataNotFoundError } from 'typeorm/error/EntityMetadataNotFoundError'; import { EntityMetadataBuilder } from 'typeorm/metadata-builder/EntityMetadataBuilder'; +import { type WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface'; import { type FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface'; -import { type WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface'; -import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; import { PermissionsException, PermissionsExceptionCode, @@ -67,6 +66,12 @@ export class GlobalWorkspaceDataSource extends DataSource { }); } + get authContext(): WorkspaceAuthContext { + const context = getWorkspaceContext(); + + return context.authContext; + } + get featureFlagMap(): FeatureFlagMap { const context = getWorkspaceContext(); @@ -82,18 +87,18 @@ export class GlobalWorkspaceDataSource extends DataSource { override getRepository( target: EntityTarget, permissionOptions?: RolePermissionConfig, - authContext?: AuthContext, ): WorkspaceRepository { const manager = this.createEntityManager(); - return manager.getRepository(target, permissionOptions, authContext); + return manager.getRepository(target, permissionOptions, this.authContext); } override findMetadata( target: EntityTarget, ): EntityMetadata | undefined { const context = getWorkspaceContext(); - const { workspaceId, metadataVersion } = context; + const { authContext, metadataVersion } = context; + const workspaceId = authContext.workspace.id; const cacheKey = `${workspaceId}-${metadataVersion}`; const cachedEntityMetadata = this.getCachedEntityMetadata(cacheKey); @@ -123,12 +128,17 @@ export class GlobalWorkspaceDataSource extends DataSource { } const context = getWorkspaceContext(); - const fullContext: WorkspaceInternalContext = { - ...context, - eventEmitterService: this.eventEmitterService, - }; - return new WorkspaceEntityManager(fullContext, this, queryRunner); + return new WorkspaceEntityManager( + { + workspaceId: context.authContext.workspace.id, + objectMetadataMaps: context.objectMetadataMaps, + featureFlagsMap: context.featureFlagsMap, + eventEmitterService: this.eventEmitterService, + }, + this, + queryRunner, + ); } override createQueryRunner( 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 9517d4534b..f8c6f179c9 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 @@ -2,13 +2,15 @@ import { Injectable, type Type } from '@nestjs/common'; import { type ObjectLiteral } from 'typeorm'; +import { WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface'; + import { WorkspaceFeatureFlagsMapCacheService } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.service'; import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service'; import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service'; import { GlobalWorkspaceDataSourceService } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource.service'; import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; import { - type WorkspaceContextForStorage, + type WorkspaceContext, withWorkspaceContext, } from 'src/engine/twenty-orm/storage/workspace-context.storage'; import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config'; @@ -64,21 +66,21 @@ export class GlobalWorkspaceOrmManager { } async executeInWorkspaceContext( - workspaceId: string, + authContext: WorkspaceAuthContext, fn: () => T | Promise, ): Promise { - const context = await this.loadWorkspaceContext(workspaceId); + const context = await this.loadWorkspaceContext(authContext); const globalDataSource = this.globalWorkspaceDataSourceService.getGlobalWorkspaceDataSource(); if ( !globalDataSource.hasWorkspaceEntityMetadataCacheForVersion( - workspaceId, + authContext.workspace.id, context.metadataVersion, ) ) { await globalDataSource.buildWorkspaceMetadata( - workspaceId, + authContext.workspace.id, context.metadataVersion, context.objectMetadataMaps, ); @@ -88,8 +90,9 @@ export class GlobalWorkspaceOrmManager { } private async loadWorkspaceContext( - workspaceId: string, - ): Promise { + authContext: WorkspaceAuthContext, + ): Promise { + const workspaceId = authContext.workspace.id; const { objectMetadataMaps, metadataVersion } = await this.workspaceMetadataCacheService.getExistingOrRecomputeMetadataMaps( { @@ -108,7 +111,7 @@ export class GlobalWorkspaceOrmManager { }); return { - workspaceId, + authContext, objectMetadataMaps, metadataVersion, featureFlagsMap, diff --git a/packages/twenty-server/src/engine/twenty-orm/storage/workspace-context.storage.ts b/packages/twenty-server/src/engine/twenty-orm/storage/workspace-context.storage.ts index fbb924a7cf..fc77c58407 100644 --- a/packages/twenty-server/src/engine/twenty-orm/storage/workspace-context.storage.ts +++ b/packages/twenty-server/src/engine/twenty-orm/storage/workspace-context.storage.ts @@ -2,11 +2,13 @@ import { AsyncLocalStorage } from 'async_hooks'; import { type ObjectsPermissionsByRoleId } from 'twenty-shared/types'; +import { type WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface'; + import { type FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps'; -export type WorkspaceContextForStorage = { - workspaceId: string; +export type WorkspaceContext = { + authContext: WorkspaceAuthContext; objectMetadataMaps: ObjectMetadataMaps; metadataVersion: number; featureFlagsMap: Record; @@ -14,9 +16,9 @@ export type WorkspaceContextForStorage = { }; export const workspaceContextStorage = - new AsyncLocalStorage(); + new AsyncLocalStorage(); -export const getWorkspaceContext = (): WorkspaceContextForStorage => { +export const getWorkspaceContext = (): WorkspaceContext => { const context = workspaceContextStorage.getStore(); if (!context) { @@ -29,14 +31,12 @@ export const getWorkspaceContext = (): WorkspaceContextForStorage => { }; export const withWorkspaceContext = ( - context: WorkspaceContextForStorage, + context: WorkspaceContext, fn: () => T | Promise, ): T | Promise => { return workspaceContextStorage.run(context, fn); }; -export const setWorkspaceContext = ( - context: WorkspaceContextForStorage, -): void => { +export const setWorkspaceContext = (context: WorkspaceContext): void => { workspaceContextStorage.enterWith(context); };