Optimize metadata queries (#7013)
In this PR: 1. Refactor guards to avoid duplicated queries: WorkspaceAuthGuard and UserAuthGuard only check for existence of workspace and user in the request without querying the database
This commit is contained in:
committed by
Charles Bochet
parent
cf8b1161cc
commit
523df5398a
@ -17,12 +17,12 @@ import { WorkspaceSchemaFactory } from 'src/engine/api/graphql/workspace-schema.
|
||||
import { TokenService } from 'src/engine/core-modules/auth/services/token.service';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { CoreEngineModule } from 'src/engine/core-modules/core-engine.module';
|
||||
import { useGraphQLErrorHandlerHook } from 'src/engine/core-modules/graphql/hooks/use-graphql-error-handler.hook';
|
||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
|
||||
import { useSentryTracing } from 'src/engine/core-modules/exception-handler/hooks/use-sentry-tracing';
|
||||
import { useGraphQLErrorHandlerHook } from 'src/engine/core-modules/graphql/hooks/use-graphql-error-handler.hook';
|
||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { handleExceptionAndConvertToGraphQLError } from 'src/engine/utils/global-exception-handler.util';
|
||||
import { renderApolloPlayground } from 'src/engine/utils/render-apollo-playground.util';
|
||||
|
||||
@ -69,13 +69,18 @@ export class GraphQLConfigService
|
||||
let workspace: Workspace | undefined;
|
||||
|
||||
try {
|
||||
if (!this.tokenService.isTokenPresent(context.req)) {
|
||||
const { user, workspace, apiKey, workspaceMemberId } = context.req;
|
||||
|
||||
if (!workspace) {
|
||||
return new GraphQLSchema({});
|
||||
}
|
||||
|
||||
const data = await this.tokenService.validateToken(context.req);
|
||||
|
||||
return await this.createSchema(context, data);
|
||||
return await this.createSchema(context, {
|
||||
user,
|
||||
workspace,
|
||||
apiKey,
|
||||
workspaceMemberId,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof UnauthorizedException) {
|
||||
throw new GraphQLError('Unauthenticated', {
|
||||
|
||||
@ -2,7 +2,7 @@ import { FindOptionsWhere, ObjectLiteral } from 'typeorm';
|
||||
|
||||
import { RecordFilter } from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface';
|
||||
|
||||
import { FieldMetadataMap } from 'src/engine/api/graphql/graphql-query-runner/utils/convert-object-metadata-to-map.util';
|
||||
import { FieldMetadataMap } from 'src/engine/metadata-modules/utils/generate-object-metadata-map.util';
|
||||
|
||||
import { GraphqlQueryFilterFieldParser } from './graphql-query-filter-field.parser';
|
||||
|
||||
|
||||
@ -3,9 +3,9 @@ import { FindOptionsWhere, Not, ObjectLiteral } from 'typeorm';
|
||||
import { RecordFilter } from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface';
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
import { FieldMetadataMap } from 'src/engine/api/graphql/graphql-query-runner/utils/convert-object-metadata-to-map.util';
|
||||
import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { FieldMetadataMap } from 'src/engine/metadata-modules/utils/generate-object-metadata-map.util';
|
||||
import { CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
|
||||
import { capitalize } from 'src/utils/capitalize';
|
||||
import { isPlainObject } from 'src/utils/is-plain-object';
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { OrderByDirection } from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface';
|
||||
|
||||
import { GraphqlQueryOrderFieldParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-order/graphql-query-order.parser';
|
||||
import { FieldMetadataMap } from 'src/engine/api/graphql/graphql-query-runner/utils/convert-object-metadata-to-map.util';
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { FieldMetadataMap } from 'src/engine/metadata-modules/utils/generate-object-metadata-map.util';
|
||||
|
||||
describe('GraphqlQueryOrderFieldParser', () => {
|
||||
let parser: GraphqlQueryOrderFieldParser;
|
||||
|
||||
@ -10,12 +10,11 @@ import {
|
||||
GraphqlQueryRunnerException,
|
||||
GraphqlQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception';
|
||||
import { FieldMetadataMap } from 'src/engine/api/graphql/graphql-query-runner/utils/convert-object-metadata-to-map.util';
|
||||
import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { FieldMetadataMap } from 'src/engine/metadata-modules/utils/generate-object-metadata-map.util';
|
||||
import { CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
|
||||
import { capitalize } from 'src/utils/capitalize';
|
||||
|
||||
export class GraphqlQueryOrderFieldParser {
|
||||
private fieldMetadataMap: FieldMetadataMap;
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
import { GraphqlQuerySelectedFieldsParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-selected-fields/graphql-selected-fields.parser';
|
||||
import { ObjectMetadataMap } from 'src/engine/api/graphql/graphql-query-runner/utils/convert-object-metadata-to-map.util';
|
||||
import { getRelationObjectMetadata } from 'src/engine/api/graphql/graphql-query-runner/utils/get-relation-object-metadata.util';
|
||||
import { ObjectMetadataMap } from 'src/engine/metadata-modules/utils/generate-object-metadata-map.util';
|
||||
|
||||
export class GraphqlQuerySelectedFieldsRelationParser {
|
||||
private objectMetadataMap: ObjectMetadataMap;
|
||||
|
||||
@ -5,9 +5,9 @@ import {
|
||||
GraphqlQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception';
|
||||
import { GraphqlQuerySelectedFieldsRelationParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-selected-fields/graphql-selected-fields-relation.parser';
|
||||
import { ObjectMetadataMap } from 'src/engine/api/graphql/graphql-query-runner/utils/convert-object-metadata-to-map.util';
|
||||
import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { ObjectMetadataMap } from 'src/engine/metadata-modules/utils/generate-object-metadata-map.util';
|
||||
import { CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
|
||||
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';
|
||||
import { capitalize } from 'src/utils/capitalize';
|
||||
|
||||
@ -17,7 +17,7 @@ import { GraphqlQuerySelectedFieldsParser } from 'src/engine/api/graphql/graphql
|
||||
import {
|
||||
FieldMetadataMap,
|
||||
ObjectMetadataMap,
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/utils/convert-object-metadata-to-map.util';
|
||||
} from 'src/engine/metadata-modules/utils/generate-object-metadata-map.util';
|
||||
|
||||
export class GraphqlQueryParser {
|
||||
private fieldMetadataMap: FieldMetadataMap;
|
||||
|
||||
@ -9,12 +9,12 @@ import {
|
||||
GraphqlQueryRunnerException,
|
||||
GraphqlQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception';
|
||||
import { ObjectMetadataMap } from 'src/engine/api/graphql/graphql-query-runner/utils/convert-object-metadata-to-map.util';
|
||||
import { encodeCursor } from 'src/engine/api/graphql/graphql-query-runner/utils/cursors.util';
|
||||
import { getRelationObjectMetadata } from 'src/engine/api/graphql/graphql-query-runner/utils/get-relation-object-metadata.util';
|
||||
import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types';
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { ObjectMetadataMap } from 'src/engine/metadata-modules/utils/generate-object-metadata-map.util';
|
||||
import { CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
|
||||
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';
|
||||
import { isPlainObject } from 'src/utils/is-plain-object';
|
||||
|
||||
@ -19,11 +19,9 @@ import {
|
||||
import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
|
||||
import { ObjectRecordsToGraphqlConnectionMapper } from 'src/engine/api/graphql/graphql-query-runner/orm-mappers/object-records-to-graphql-connection.mapper';
|
||||
import { applyRangeFilter } from 'src/engine/api/graphql/graphql-query-runner/utils/apply-range-filter.util';
|
||||
import {
|
||||
convertObjectMetadataToMap,
|
||||
getObjectMetadata,
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/utils/convert-object-metadata-to-map.util';
|
||||
import { decodeCursor } from 'src/engine/api/graphql/graphql-query-runner/utils/cursors.util';
|
||||
import { getObjectMetadataOrThrow } from 'src/engine/api/graphql/graphql-query-runner/utils/get-object-metadata-or-throw.util';
|
||||
import { generateObjectMetadataMap } from 'src/engine/metadata-modules/utils/generate-object-metadata-map.util';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
|
||||
export class GraphqlQueryFindManyResolverService {
|
||||
@ -51,10 +49,10 @@ export class GraphqlQueryFindManyResolverService {
|
||||
authContext.workspace.id,
|
||||
objectMetadataItem.nameSingular,
|
||||
);
|
||||
const objectMetadataMap = convertObjectMetadataToMap(
|
||||
const objectMetadataMap = generateObjectMetadataMap(
|
||||
objectMetadataCollection,
|
||||
);
|
||||
const objectMetadata = getObjectMetadata(
|
||||
const objectMetadata = getObjectMetadataOrThrow(
|
||||
objectMetadataMap,
|
||||
objectMetadataItem.nameSingular,
|
||||
);
|
||||
|
||||
@ -13,10 +13,8 @@ import {
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception';
|
||||
import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
|
||||
import { ObjectRecordsToGraphqlConnectionMapper } from 'src/engine/api/graphql/graphql-query-runner/orm-mappers/object-records-to-graphql-connection.mapper';
|
||||
import {
|
||||
convertObjectMetadataToMap,
|
||||
getObjectMetadata,
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/utils/convert-object-metadata-to-map.util';
|
||||
import { getObjectMetadataOrThrow } from 'src/engine/api/graphql/graphql-query-runner/utils/get-object-metadata-or-throw.util';
|
||||
import { generateObjectMetadataMap } from 'src/engine/metadata-modules/utils/generate-object-metadata-map.util';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
|
||||
export class GraphqlQueryFindOneResolverService {
|
||||
@ -40,10 +38,11 @@ export class GraphqlQueryFindOneResolverService {
|
||||
authContext.workspace.id,
|
||||
objectMetadataItem.nameSingular,
|
||||
);
|
||||
const objectMetadataMap = convertObjectMetadataToMap(
|
||||
const objectMetadataMap = generateObjectMetadataMap(
|
||||
objectMetadataCollection,
|
||||
);
|
||||
const objectMetadata = getObjectMetadata(
|
||||
|
||||
const objectMetadata = getObjectMetadataOrThrow(
|
||||
objectMetadataMap,
|
||||
objectMetadataItem.nameSingular,
|
||||
);
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import {
|
||||
GraphqlQueryRunnerException,
|
||||
GraphqlQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception';
|
||||
|
||||
export type FieldMetadataMap = Record<string, FieldMetadataInterface>;
|
||||
|
||||
export type ObjectMetadataMapItem = Omit<ObjectMetadataInterface, 'fields'> & {
|
||||
fields: FieldMetadataMap;
|
||||
};
|
||||
|
||||
export type ObjectMetadataMap = Record<string, ObjectMetadataMapItem>;
|
||||
|
||||
export const convertObjectMetadataToMap = (
|
||||
objectMetadataCollection: ObjectMetadataInterface[],
|
||||
): ObjectMetadataMap => {
|
||||
const objectMetadataMap: ObjectMetadataMap = {};
|
||||
|
||||
for (const objectMetadata of objectMetadataCollection) {
|
||||
const fieldsMap: FieldMetadataMap = {};
|
||||
|
||||
for (const fieldMetadata of objectMetadata.fields) {
|
||||
fieldsMap[fieldMetadata.name] = fieldMetadata;
|
||||
}
|
||||
|
||||
const processedObjectMetadata: ObjectMetadataMapItem = {
|
||||
...objectMetadata,
|
||||
fields: fieldsMap,
|
||||
};
|
||||
|
||||
objectMetadataMap[objectMetadata.id] = processedObjectMetadata;
|
||||
objectMetadataMap[objectMetadata.nameSingular] = processedObjectMetadata;
|
||||
objectMetadataMap[objectMetadata.namePlural] = processedObjectMetadata;
|
||||
}
|
||||
|
||||
return objectMetadataMap;
|
||||
};
|
||||
|
||||
export const getObjectMetadata = (
|
||||
objectMetadataMap: Record<string, any>,
|
||||
objectName: string,
|
||||
): ObjectMetadataMapItem => {
|
||||
const objectMetadata = objectMetadataMap[objectName];
|
||||
|
||||
if (!objectMetadata) {
|
||||
throw new GraphqlQueryRunnerException(
|
||||
`Object metadata not found for ${objectName}`,
|
||||
GraphqlQueryRunnerExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return objectMetadata;
|
||||
};
|
||||
@ -0,0 +1,21 @@
|
||||
import {
|
||||
GraphqlQueryRunnerException,
|
||||
GraphqlQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception';
|
||||
import { ObjectMetadataMapItem } from 'src/engine/metadata-modules/utils/generate-object-metadata-map.util';
|
||||
|
||||
export const getObjectMetadataOrThrow = (
|
||||
objectMetadataMap: Record<string, any>,
|
||||
objectName: string,
|
||||
): ObjectMetadataMapItem => {
|
||||
const objectMetadata = objectMetadataMap[objectName];
|
||||
|
||||
if (!objectMetadata) {
|
||||
throw new GraphqlQueryRunnerException(
|
||||
`Object metadata not found for ${objectName}`,
|
||||
GraphqlQueryRunnerExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return objectMetadata;
|
||||
};
|
||||
@ -1,6 +1,6 @@
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
import { ObjectMetadataMap } from 'src/engine/api/graphql/graphql-query-runner/utils/convert-object-metadata-to-map.util';
|
||||
import { ObjectMetadataMap } from 'src/engine/metadata-modules/utils/generate-object-metadata-map.util';
|
||||
import {
|
||||
deduceRelationDirection,
|
||||
RelationDirection,
|
||||
|
||||
@ -4,11 +4,11 @@ import GraphQLJSON from 'graphql-type-json';
|
||||
import { useCachedMetadata } from 'src/engine/api/graphql/graphql-config/hooks/use-cached-metadata';
|
||||
import { useThrottler } from 'src/engine/api/graphql/graphql-config/hooks/use-throttler';
|
||||
import { MetadataGraphQLApiModule } from 'src/engine/api/graphql/metadata-graphql-api.module';
|
||||
import { useGraphQLErrorHandlerHook } from 'src/engine/core-modules/graphql/hooks/use-graphql-error-handler.hook';
|
||||
import { DataloaderService } from 'src/engine/dataloaders/dataloader.service';
|
||||
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/cache-storage.service';
|
||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
|
||||
import { useGraphQLErrorHandlerHook } from 'src/engine/core-modules/graphql/hooks/use-graphql-error-handler.hook';
|
||||
import { DataloaderService } from 'src/engine/dataloaders/dataloader.service';
|
||||
import { renderApolloPlayground } from 'src/engine/utils/render-apollo-playground.util';
|
||||
|
||||
export const metadataModuleFactory = async (
|
||||
|
||||
@ -56,13 +56,13 @@ export class WorkspaceSchemaFactory {
|
||||
);
|
||||
}
|
||||
|
||||
const objectMetadataCollection =
|
||||
await this.workspaceCacheStorageService.getObjectMetadataCollection(
|
||||
const objectMetadataMap =
|
||||
await this.workspaceCacheStorageService.getObjectMetadataMap(
|
||||
authContext.workspace.id,
|
||||
currentCacheVersion,
|
||||
);
|
||||
|
||||
if (!objectMetadataCollection) {
|
||||
if (!objectMetadataMap) {
|
||||
await this.workspaceMetadataCacheService.recomputeMetadataCache(
|
||||
authContext.workspace.id,
|
||||
);
|
||||
@ -72,6 +72,13 @@ export class WorkspaceSchemaFactory {
|
||||
);
|
||||
}
|
||||
|
||||
const objectMetadataCollection = Object.values(objectMetadataMap).map(
|
||||
(objectMetadataItem) => ({
|
||||
...objectMetadataItem,
|
||||
fields: Object.values(objectMetadataItem.fields),
|
||||
}),
|
||||
);
|
||||
|
||||
// Get typeDefs from cache
|
||||
let typeDefs = await this.workspaceCacheStorageService.getGraphQLTypeDefs(
|
||||
authContext.workspace.id,
|
||||
|
||||
Reference in New Issue
Block a user