Deprecate workspace datasoure (#16507)

This commit is contained in:
Weiko
2025-12-11 18:27:49 +01:00
committed by GitHub
parent 3dd2684254
commit 083a038f8a
39 changed files with 200 additions and 1929 deletions
@@ -6,13 +6,19 @@ import { EntityManager } from 'typeorm';
import { EntityPersistExecutor } from 'typeorm/persistence/EntityPersistExecutor';
import { PlainObjectToDatabaseEntityTransformer } from 'typeorm/query-builder/transformer/PlainObjectToDatabaseEntityTransformer';
import { type WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface';
import { type WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { type WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
import { type GlobalWorkspaceDataSource } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource';
import { validateOperationIsPermittedOrThrow } from 'src/engine/twenty-orm/repository/permissions.utils';
import {
setWorkspaceContext,
withWorkspaceContext,
type ORMWorkspaceContext,
} from 'src/engine/twenty-orm/storage/orm-workspace-context.storage';
import { getObjectMetadataFromEntityTarget } from 'src/engine/twenty-orm/utils/get-object-metadata-from-entity-target.util';
import { WorkspaceEntityManager } from './workspace-entity-manager';
@@ -77,12 +83,13 @@ jest.mock('../repository/workspace-select-query-builder', () => ({
describe('WorkspaceEntityManager', () => {
let entityManager: WorkspaceEntityManager;
let mockInternalContext: WorkspaceInternalContext;
let mockDataSource: WorkspaceDataSource;
let mockDataSource: GlobalWorkspaceDataSource;
let mockPermissionOptions: {
shouldBypassPermissionChecks: boolean;
objectRecordsPermissions?: ObjectsPermissions;
};
let mockInternalContext: WorkspaceInternalContext;
let mockWorkspaceContext: ORMWorkspaceContext;
beforeEach(() => {
const mockFlatObjectMetadata: FlatObjectMetadata = {
@@ -234,7 +241,8 @@ describe('WorkspaceEntityManager', () => {
IS_GLOBAL_WORKSPACE_DATASOURCE_ENABLED: false,
},
permissionsPerRoleId: {},
} as WorkspaceDataSource;
eventEmitterService: mockInternalContext.eventEmitterService,
} as GlobalWorkspaceDataSource;
mockPermissionOptions = {
shouldBypassPermissionChecks: false,
@@ -249,6 +257,30 @@ describe('WorkspaceEntityManager', () => {
},
};
const mockAuthContext = {
user: { id: 'user-id' },
workspace: { id: 'test-workspace-id' },
workspaceMemberId: 'workspace-member-id',
userWorkspaceId: 'user-workspace-id',
apiKey: null,
} as unknown as WorkspaceAuthContext;
mockWorkspaceContext = {
authContext: mockAuthContext,
flatObjectMetadataMaps,
flatFieldMetadataMaps,
flatIndexMaps: mockInternalContext.flatIndexMaps,
objectIdByNameSingular: mockInternalContext.objectIdByNameSingular,
featureFlagsMap: mockInternalContext.featureFlagsMap,
permissionsPerRoleId: mockDataSource.permissionsPerRoleId,
entityMetadatas: [],
userWorkspaceRoleMap: {
'user-workspace-id': 'role-id',
},
};
setWorkspaceContext(mockWorkspaceContext);
// Mock TypeORM connection methods
const mockWorkspaceDataSource = {
getMetadata: jest.fn().mockReturnValue({
@@ -258,6 +290,7 @@ describe('WorkspaceEntityManager', () => {
findInheritanceMetadata: jest.fn(),
findColumnWithPropertyPath: jest.fn(),
}),
eventEmitterService: mockInternalContext.eventEmitterService,
createQueryBuilder: jest.fn().mockReturnValue({
delete: jest.fn().mockReturnThis(),
from: jest.fn().mockReturnThis(),
@@ -290,10 +323,7 @@ describe('WorkspaceEntityManager', () => {
}),
};
entityManager = new WorkspaceEntityManager(
mockInternalContext,
mockDataSource,
);
entityManager = new WorkspaceEntityManager(mockDataSource);
Object.defineProperty(entityManager, 'connection', {
get: () => mockWorkspaceDataSource,
@@ -362,7 +392,9 @@ describe('WorkspaceEntityManager', () => {
describe('Query Method', () => {
it('should call validatePermissions and validateOperationIsPermittedOrThrow for find', async () => {
await entityManager.find('test-entity', {}, mockPermissionOptions);
await withWorkspaceContext(mockWorkspaceContext, () =>
entityManager.find('test-entity', {}, mockPermissionOptions),
);
expect(entityManager.createQueryBuilder).toHaveBeenCalledWith(
'test-entity',
@@ -380,11 +412,13 @@ describe('WorkspaceEntityManager', () => {
describe('Save Methods', () => {
it('should call validatePermissions and validateOperationIsPermittedOrThrow for save', async () => {
await entityManager.save(
'test-entity',
{},
{ reload: false },
mockPermissionOptions,
await withWorkspaceContext(mockWorkspaceContext, () =>
entityManager.save(
'test-entity',
{},
{ reload: false },
mockPermissionOptions,
),
);
expect(entityManager['validatePermissions']).toHaveBeenCalledWith({
target: 'test-entity',
@@ -409,7 +443,9 @@ describe('WorkspaceEntityManager', () => {
describe('Update Methods', () => {
it('should call createQueryBuilder with permissionOptions for update', async () => {
await entityManager.update('test-entity', {}, {}, mockPermissionOptions);
await withWorkspaceContext(mockWorkspaceContext, () =>
entityManager.update('test-entity', {}, {}, mockPermissionOptions),
);
expect(entityManager['createQueryBuilder']).toHaveBeenCalledWith(
'test-entity',
undefined,
@@ -421,7 +457,9 @@ describe('WorkspaceEntityManager', () => {
describe('Other Methods', () => {
it('should call validatePermissions and validateOperationIsPermittedOrThrow for clear', async () => {
await entityManager.clear('test-entity', mockPermissionOptions);
await withWorkspaceContext(mockWorkspaceContext, () =>
entityManager.clear('test-entity', mockPermissionOptions),
);
expect(entityManager['validatePermissions']).toHaveBeenCalledWith({
target: 'test-entity',
operationType: 'delete',
@@ -34,7 +34,6 @@ import { type UpsertOptions } from 'typeorm/repository/UpsertOptions';
import { InstanceChecker } from 'typeorm/util/InstanceChecker';
import { type FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface';
import { type WorkspaceDataSourceInterface } from 'src/engine/twenty-orm/interfaces/workspace-datasource.interface';
import { type WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface';
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
@@ -45,7 +44,6 @@ import {
PermissionsException,
PermissionsExceptionCode,
} from 'src/engine/metadata-modules/permissions/permissions.exception';
import { type WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
import { type DeepPartialWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/deep-partial-entity-with-nested-relation-fields.type';
import { type QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type';
import { getEntityTarget } from 'src/engine/twenty-orm/entity-manager/utils/get-entity-target';
@@ -73,52 +71,34 @@ type PermissionOptions = {
};
export class WorkspaceEntityManager extends EntityManager {
private readonly _legacyInternalContext?: WorkspaceInternalContext;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
readonly repositories: Map<string, Repository<any>>;
declare connection: WorkspaceDataSource;
declare connection: GlobalWorkspaceDataSource;
constructor(
legacyInternalContext: WorkspaceInternalContext | undefined,
connection: WorkspaceDataSource | GlobalWorkspaceDataSource,
connection: GlobalWorkspaceDataSource,
queryRunner?: QueryRunner,
) {
super(connection, queryRunner);
this.repositories = new Map();
this._legacyInternalContext = legacyInternalContext;
}
private get eventEmitterService(): WorkspaceEventEmitter {
const isGlobalFlow = (this.connection as WorkspaceDataSourceInterface)
.isGlobalFlow;
if (isGlobalFlow) {
return (this.connection as unknown as GlobalWorkspaceDataSource)
.eventEmitterService;
}
return this._legacyInternalContext!.eventEmitterService;
return this.connection.eventEmitterService;
}
get internalContext(): WorkspaceInternalContext {
const isGlobalFlow = (this.connection as WorkspaceDataSourceInterface)
.isGlobalFlow;
const context = getWorkspaceContext();
if (isGlobalFlow) {
const context = getWorkspaceContext();
return {
workspaceId: context.authContext.workspace.id,
flatObjectMetadataMaps: context.flatObjectMetadataMaps,
flatFieldMetadataMaps: context.flatFieldMetadataMaps,
flatIndexMaps: context.flatIndexMaps,
objectIdByNameSingular: context.objectIdByNameSingular,
featureFlagsMap: context.featureFlagsMap,
eventEmitterService: this.eventEmitterService,
};
}
return this._legacyInternalContext!;
return {
workspaceId: context.authContext.workspace.id,
flatObjectMetadataMaps: context.flatObjectMetadataMaps,
flatFieldMetadataMaps: context.flatFieldMetadataMaps,
flatIndexMaps: context.flatIndexMaps,
objectIdByNameSingular: context.objectIdByNameSingular,
featureFlagsMap: context.featureFlagsMap,
eventEmitterService: this.eventEmitterService,
};
}
getFeatureFlagMap(): FeatureFlagMap {
@@ -487,7 +467,7 @@ export class WorkspaceEntityManager extends EntityManager {
}
private extractTargetNameSingularFromEntityTarget(
target: EntityTarget<unknown>,
target: EntityTarget<ObjectLiteral>,
): string {
return this.connection.getMetadata(target).name;
}