Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import { WorkspacePreQueryHookPayload } from 'src/engine/api/graphql/workspace-q
import { WorkspaceQueryHookService } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/workspace-query-hook.service';
import { ApiKeyRoleService } from 'src/engine/core-modules/api-key/services/api-key-role.service';
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { MetricsService } from 'src/engine/core-modules/metrics/metrics.service';
import { MetricsKeys } from 'src/engine/core-modules/metrics/types/metrics-keys.type';
Expand Down Expand Up @@ -132,32 +131,15 @@ export abstract class CommonBaseQueryRunnerService<
commonQueryParser,
);

const isGlobalDatasourceEnabled =
await this.featureFlagService.isFeatureEnabled(
FeatureFlagKey.IS_GLOBAL_WORKSPACE_DATASOURCE_ENABLED,
authContext.workspace.id,
);

if (isGlobalDatasourceEnabled) {
return this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
async () =>
this.executeQueryAndEnrichResults(
processedArgs,
authContext,
queryRunnerContext,
commonQueryParser,
isGlobalDatasourceEnabled,
),
);
}

return this.executeQueryAndEnrichResults(
processedArgs,
return this.globalWorkspaceOrmManager.executeInWorkspaceContext(
authContext,
queryRunnerContext,
commonQueryParser,
isGlobalDatasourceEnabled,
async () =>
this.executeQueryAndEnrichResults(
processedArgs,
authContext,
queryRunnerContext,
commonQueryParser,
),
);
}

Expand Down Expand Up @@ -217,17 +199,12 @@ export abstract class CommonBaseQueryRunnerService<
authContext: WorkspaceAuthContext,
queryRunnerContext: CommonBaseQueryRunnerContext,
commonQueryParser: GraphqlQueryParser,
isGlobalDatasourceEnabled: boolean,
): Promise<Output> {
const extendedQueryRunnerContext = isGlobalDatasourceEnabled
? await this.prepareExtendedQueryRunnerContextWithGlobalDatasource(
authContext,
queryRunnerContext,
)
: await this.prepareExtendedQueryRunnerContext(
authContext,
queryRunnerContext,
);
const extendedQueryRunnerContext =
await this.prepareExtendedQueryRunnerContextWithGlobalDatasource(
authContext,
queryRunnerContext,
);

const results = await this.run(processedArgs, {
...extendedQueryRunnerContext,
Expand Down Expand Up @@ -312,81 +289,28 @@ export abstract class CommonBaseQueryRunnerService<
}
}

private async getRoleIdAndObjectsPermissions(
private async getRoleIdOrThrow(
authContext: AuthContext,
workspaceId: string,
) {
let roleId: string;

if (
!isDefined(authContext.apiKey) &&
!isDefined(authContext.userWorkspaceId)
) {
throw new PermissionsException(
PermissionsExceptionMessage.NO_AUTHENTICATION_CONTEXT,
PermissionsExceptionCode.NO_AUTHENTICATION_CONTEXT,
);
}

): Promise<string> {
if (isDefined(authContext.apiKey)) {
roleId = await this.apiKeyRoleService.getRoleIdForApiKey(
return this.apiKeyRoleService.getRoleIdForApiKey(
authContext.apiKey.id,
workspaceId,
);
} else {
const userWorkspaceRoleId =
await this.userRoleService.getRoleIdForUserWorkspace({
userWorkspaceId: authContext.userWorkspaceId,
workspaceId,
});

if (!isDefined(userWorkspaceRoleId)) {
throw new PermissionsException(
PermissionsExceptionMessage.NO_ROLE_FOUND_FOR_USER_WORKSPACE,
PermissionsExceptionCode.NO_ROLE_FOUND_FOR_USER_WORKSPACE,
);
}

roleId = userWorkspaceRoleId;
}

const { rolesPermissions } =
await this.workspaceCacheService.getOrRecompute(workspaceId, [
'rolesPermissions',
]);

return { roleId, objectsPermissions: rolesPermissions[roleId] };
}

private async prepareExtendedQueryRunnerContext(
authContext: WorkspaceAuthContext,
queryRunnerContext: CommonBaseQueryRunnerContext,
): Promise<Omit<CommonExtendedQueryRunnerContext, 'commonQueryParser'>> {
const workspaceDataSource =
await this.twentyORMGlobalManager.getDataSourceForWorkspace({
workspaceId: authContext.workspace.id,
});

const { roleId } = await this.getRoleIdAndObjectsPermissions(
authContext,
authContext.workspace.id,
);

const rolePermissionConfig = { unionOf: [roleId] };

const repository = workspaceDataSource.getRepository(
queryRunnerContext.flatObjectMetadata.nameSingular,
rolePermissionConfig,
authContext,
);
if (!isDefined(authContext.userWorkspaceId)) {
throw new CommonQueryRunnerException(
'Invalid auth context',
CommonQueryRunnerExceptionCode.INVALID_AUTH_CONTEXT,
);
}

return {
...queryRunnerContext,
authContext,
workspaceDataSource,
rolePermissionConfig,
repository,
};
return this.userRoleService.getRoleIdForUserWorkspace({
userWorkspaceId: authContext.userWorkspaceId,
workspaceId,
});
}

private async prepareExtendedQueryRunnerContextWithGlobalDatasource(
Expand All @@ -395,10 +319,7 @@ export abstract class CommonBaseQueryRunnerService<
): Promise<Omit<CommonExtendedQueryRunnerContext, 'commonQueryParser'>> {
const workspaceId = authContext.workspace.id;

const { roleId } = await this.getRoleIdAndObjectsPermissions(
authContext,
workspaceId,
);
const roleId = await this.getRoleIdOrThrow(authContext, workspaceId);

const rolePermissionConfig = { unionOf: [roleId] };

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';

import { isDefined } from 'twenty-shared/utils';
import { In, IsNull, Not, Repository } from 'typeorm';

import { ApiKeyEntity } from 'src/engine/core-modules/api-key/api-key.entity';
Expand Down Expand Up @@ -69,7 +70,7 @@ export class ApiKeyRoleService {

const roleId = apiKeyRoleMap[apiKeyId];

if (!roleId) {
if (!isDefined(roleId)) {
throw new ApiKeyException(
`API key ${apiKeyId} has no role assigned`,
ApiKeyExceptionCode.API_KEY_NO_ROLE_ASSIGNED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,18 +63,23 @@ export class UserRoleService {
userWorkspaceId,
}: {
workspaceId: string;
userWorkspaceId?: string;
}): Promise<string | undefined> {
if (!isDefined(userWorkspaceId)) {
return;
}

userWorkspaceId: string;
}): Promise<string> {
const { userWorkspaceRoleMap } =
await this.workspaceCacheService.getOrRecompute(workspaceId, [
'userWorkspaceRoleMap',
]);

return userWorkspaceRoleMap[userWorkspaceId];
const roleId = userWorkspaceRoleMap[userWorkspaceId];

if (!isDefined(roleId)) {
throw new PermissionsException(
`User workspace ${userWorkspaceId} has no role assigned`,
PermissionsExceptionCode.NO_ROLE_FOUND_FOR_USER_WORKSPACE,
);
}

return roleId;
}

public async getRolesByUserWorkspaces({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,28 @@ import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-
import { TwentyConfigModule } from 'src/engine/core-modules/twenty-config/twenty-config.module';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { WorkspaceFeatureFlagsMapCacheModule } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.module';
import { EntitySchemaColumnFactory } from 'src/engine/twenty-orm/factories/entity-schema-column.factory';
import { EntitySchemaRelationFactory } from 'src/engine/twenty-orm/factories/entity-schema-relation.factory';
import { EntitySchemaFactory } from 'src/engine/twenty-orm/factories/entity-schema.factory';
import { GlobalWorkspaceDataSourceService } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-datasource.service';
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
import { WorkspaceEntityMetadatasCacheService } from 'src/engine/twenty-orm/global-workspace-datasource/workspace-entity-metadatas-cache.service';
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
import { WorkspaceEventEmitterModule } from 'src/engine/workspace-event-emitter/workspace-event-emitter.module';

@Global()
@Module({
imports: [
TypeOrmModule.forFeature([WorkspaceEntity]),
TypeOrmModule.forFeature([
WorkspaceEntity,
ObjectMetadataEntity,
FieldMetadataEntity,
]),
DataSourceModule,
WorkspaceCacheStorageModule,
WorkspaceManyOrAllFlatEntityMapsCacheModule,
Expand All @@ -35,6 +42,7 @@ import { WorkspaceEventEmitterModule } from 'src/engine/workspace-event-emitter/
EntitySchemaFactory,
EntitySchemaColumnFactory,
EntitySchemaRelationFactory,
WorkspaceEntityMetadatasCacheService,
],
exports: [GlobalWorkspaceDataSourceService, GlobalWorkspaceOrmManager],
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
import { Injectable, type Type } from '@nestjs/common';

import { isDefined } from 'twenty-shared/utils';
import { EntitySchema, ObjectLiteral } from 'typeorm';
import { EntitySchemaTransformer } from 'typeorm/entity-schema/EntitySchemaTransformer';
import { EntityMetadataBuilder } from 'typeorm/metadata-builder/EntityMetadataBuilder';
import { ObjectLiteral } from 'typeorm';

import { WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface';

import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
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 { buildObjectIdByNameMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/build-object-id-by-name-maps.util';
import { EntitySchemaFactory } from 'src/engine/twenty-orm/factories/entity-schema.factory';
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 {
Expand All @@ -27,8 +19,6 @@ import { convertClassNameToObjectMetadataName } from 'src/engine/workspace-manag
export class GlobalWorkspaceOrmManager {
constructor(
private readonly globalWorkspaceDataSourceService: GlobalWorkspaceDataSourceService,
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
private readonly entitySchemaFactory: EntitySchemaFactory,
private readonly workspaceCacheService: WorkspaceCacheService,
) {}

Expand Down Expand Up @@ -92,26 +82,19 @@ export class GlobalWorkspaceOrmManager {
flatIndexMaps,
featureFlagsMap,
rolesPermissions: permissionsPerRoleId,
entityMetadatas,
} = await this.workspaceCacheService.getOrRecompute(workspaceId, [
'flatObjectMetadataMaps',
'flatFieldMetadataMaps',
'flatIndexMaps',
'featureFlagsMap',
'rolesPermissions',
'userWorkspaceRoleMap',
'entityMetadatas',
]);

const { idByNameSingular: objectIdByNameSingular } =
buildObjectIdByNameMaps(flatObjectMetadataMaps);

const entitySchemas = this.buildEntitySchemas(
workspaceId,
flatObjectMetadataMaps,
flatFieldMetadataMaps,
);

const entityMetadatas = this.buildEntityMetadatas(entitySchemas);

return {
authContext,
flatObjectMetadataMaps,
Expand All @@ -123,33 +106,4 @@ export class GlobalWorkspaceOrmManager {
entityMetadatas,
};
}

private buildEntitySchemas(
workspaceId: string,
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
) {
return Object.values(flatObjectMetadataMaps.byId)
.filter(isDefined)
.map((flatObjectMetadata) =>
this.entitySchemaFactory.create(
workspaceId,
flatObjectMetadata,
flatObjectMetadataMaps,
flatFieldMetadataMaps,
),
);
}

private buildEntityMetadatas(entitySchemas: EntitySchema[]) {
const transformer = new EntitySchemaTransformer();
const metadataArgsStorage = transformer.transform(entitySchemas);

const entityMetadataBuilder = new EntityMetadataBuilder(
this.globalWorkspaceDataSourceService.getGlobalWorkspaceDataSource(),
metadataArgsStorage,
);

return entityMetadataBuilder.build();
}
}
Loading