## Context Deprecating the old objectMetadataMap type in favour of split flat entities to match with our new caching. In the long run, trying to achieve: - Better performance through caching - Consistent data access patterns across the codebase - Reduced database queries Now that everything is based on flat entities, which are cached, we can finish the refactoring of workspace context cache which should already improve performances. Then the last step will be to consume that new cache in the new global datasource to get rid of the many workspace datasources stored in the server
158 lines
5.4 KiB
TypeScript
158 lines
5.4 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
|
|
import { makeExecutableSchema } from '@graphql-tools/schema';
|
|
import { GraphQLSchema, printSchema } from 'graphql';
|
|
import { gql } from 'graphql-tag';
|
|
import { isDefined } from 'twenty-shared/utils';
|
|
|
|
import { ScalarsExplorerService } from 'src/engine/api/graphql/services/scalars-explorer.service';
|
|
import { workspaceResolverBuilderMethodNames } from 'src/engine/api/graphql/workspace-resolver-builder/factories/factories';
|
|
import { WorkspaceResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/workspace-resolver.factory';
|
|
import { WorkspaceGraphQLSchemaGenerator } from 'src/engine/api/graphql/workspace-schema-builder/workspace-graphql-schema.factory';
|
|
import {
|
|
AuthException,
|
|
AuthExceptionCode,
|
|
} from 'src/engine/core-modules/auth/auth.exception';
|
|
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
|
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
|
import {
|
|
FlatEntityMapsException,
|
|
FlatEntityMapsExceptionCode,
|
|
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
|
|
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
|
import { buildObjectIdByNameMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/build-object-id-by-name-maps.util';
|
|
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
|
|
|
@Injectable()
|
|
export class WorkspaceSchemaFactory {
|
|
constructor(
|
|
private readonly dataSourceService: DataSourceService,
|
|
private readonly scalarsExplorerService: ScalarsExplorerService,
|
|
private readonly workspaceGraphQLSchemaGenerator: WorkspaceGraphQLSchemaGenerator,
|
|
private readonly workspaceResolverFactory: WorkspaceResolverFactory,
|
|
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
|
|
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
|
) {}
|
|
|
|
async createGraphQLSchema(authContext: AuthContext): Promise<GraphQLSchema> {
|
|
if (!authContext.workspace?.id) {
|
|
return new GraphQLSchema({});
|
|
}
|
|
|
|
const dataSourcesMetadata =
|
|
await this.dataSourceService.getDataSourcesMetadataFromWorkspaceId(
|
|
authContext.workspace.id,
|
|
);
|
|
|
|
if (!dataSourcesMetadata || dataSourcesMetadata.length === 0) {
|
|
return new GraphQLSchema({});
|
|
}
|
|
|
|
const workspaceId = authContext.workspace.id;
|
|
|
|
if (!workspaceId) {
|
|
throw new AuthException(
|
|
'Unauthenticated',
|
|
AuthExceptionCode.UNAUTHENTICATED,
|
|
);
|
|
}
|
|
|
|
const { flatObjectMetadataMaps, flatFieldMetadataMaps, flatIndexMaps } =
|
|
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
|
{
|
|
workspaceId,
|
|
flatMapsKeys: [
|
|
'flatObjectMetadataMaps',
|
|
'flatFieldMetadataMaps',
|
|
'flatIndexMaps',
|
|
],
|
|
},
|
|
);
|
|
|
|
if (!isDefined(flatObjectMetadataMaps)) {
|
|
throw new FlatEntityMapsException(
|
|
'Object metadata collection not found',
|
|
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
|
|
);
|
|
}
|
|
|
|
if (!isDefined(flatFieldMetadataMaps)) {
|
|
throw new FlatEntityMapsException(
|
|
'Field metadata collection not found',
|
|
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
|
|
);
|
|
}
|
|
|
|
let metadataVersion =
|
|
await this.workspaceCacheStorageService.getMetadataVersion(workspaceId);
|
|
|
|
if (!isDefined(metadataVersion)) {
|
|
metadataVersion = authContext.workspace.metadataVersion ?? 0;
|
|
await this.workspaceCacheStorageService.setMetadataVersion(
|
|
workspaceId,
|
|
metadataVersion,
|
|
);
|
|
}
|
|
|
|
const { idByNameSingular } = buildObjectIdByNameMaps(
|
|
flatObjectMetadataMaps,
|
|
);
|
|
|
|
let typeDefs = await this.workspaceCacheStorageService.getGraphQLTypeDefs(
|
|
authContext.workspace.id,
|
|
metadataVersion,
|
|
);
|
|
let usedScalarNames =
|
|
await this.workspaceCacheStorageService.getGraphQLUsedScalarNames(
|
|
authContext.workspace.id,
|
|
metadataVersion,
|
|
);
|
|
|
|
if (!typeDefs || !usedScalarNames) {
|
|
const autoGeneratedSchema =
|
|
await this.workspaceGraphQLSchemaGenerator.generateSchema({
|
|
flatObjectMetadataMaps,
|
|
flatFieldMetadataMaps,
|
|
flatIndexMaps,
|
|
});
|
|
|
|
usedScalarNames =
|
|
this.scalarsExplorerService.getUsedScalarNames(autoGeneratedSchema);
|
|
typeDefs = printSchema(autoGeneratedSchema);
|
|
|
|
await this.workspaceCacheStorageService.setGraphQLTypeDefs(
|
|
authContext.workspace.id,
|
|
metadataVersion,
|
|
typeDefs,
|
|
);
|
|
await this.workspaceCacheStorageService.setGraphQLUsedScalarNames(
|
|
authContext.workspace.id,
|
|
metadataVersion,
|
|
usedScalarNames,
|
|
);
|
|
}
|
|
|
|
const autoGeneratedResolvers = await this.workspaceResolverFactory.create(
|
|
authContext,
|
|
flatObjectMetadataMaps,
|
|
flatFieldMetadataMaps,
|
|
idByNameSingular,
|
|
workspaceResolverBuilderMethodNames,
|
|
);
|
|
const scalarsResolvers =
|
|
this.scalarsExplorerService.getScalarResolvers(usedScalarNames);
|
|
|
|
const executableSchema = makeExecutableSchema({
|
|
typeDefs: gql`
|
|
${typeDefs}
|
|
`,
|
|
resolvers: {
|
|
...scalarsResolvers,
|
|
...autoGeneratedResolvers,
|
|
},
|
|
});
|
|
|
|
return executableSchema;
|
|
}
|
|
}
|