import { Inject, Injectable } from '@nestjs/common'; import graphqlFields from 'graphql-fields'; import { capitalize } from 'twenty-shared'; import { DataSource, ObjectLiteral } from 'typeorm'; import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; import { IConnection } from 'src/engine/api/graphql/workspace-query-runner/interfaces/connection.interface'; import { IEdge } from 'src/engine/api/graphql/workspace-query-runner/interfaces/edge.interface'; import { WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface'; import { ResolverArgs, ResolverArgsType, WorkspaceResolverBuilderMethodNames, } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; import { GraphqlQuerySelectedFieldsResult } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-selected-fields/graphql-selected-fields.parser'; import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser'; import { ApiEventEmitterService } from 'src/engine/api/graphql/graphql-query-runner/services/api-event-emitter.service'; import { QueryResultGettersFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/query-result-getters.factory'; import { QueryRunnerArgsFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory'; import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; import { WorkspaceQueryHookService } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/workspace-query-hook.service'; import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; export type GraphqlQueryResolverExecutionArgs = { args: Input; options: WorkspaceQueryRunnerOptions; dataSource: DataSource; repository: WorkspaceRepository; graphqlQueryParser: GraphqlQueryParser; graphqlQuerySelectedFieldsResult: GraphqlQuerySelectedFieldsResult; }; @Injectable() export abstract class GraphqlQueryBaseResolverService< Input extends ResolverArgs, Response extends | ObjectRecord | ObjectRecord[] | IConnection> | IConnection>[], > { @Inject() protected readonly workspaceQueryHookService: WorkspaceQueryHookService; @Inject() protected readonly queryRunnerArgsFactory: QueryRunnerArgsFactory; @Inject() protected readonly queryResultGettersFactory: QueryResultGettersFactory; @Inject() protected readonly apiEventEmitterService: ApiEventEmitterService; @Inject() protected readonly twentyORMGlobalManager: TwentyORMGlobalManager; public async execute( args: Input, options: WorkspaceQueryRunnerOptions, operationName: WorkspaceResolverBuilderMethodNames, ): Promise { try { const { authContext, objectMetadataItemWithFieldMaps } = options; await this.validate(args, options); const hookedArgs = await this.workspaceQueryHookService.executePreQueryHooks( authContext, objectMetadataItemWithFieldMaps.nameSingular, operationName, args, ); const computedArgs = (await this.queryRunnerArgsFactory.create( hookedArgs, options, ResolverArgsType[capitalize(operationName)], )) as Input; const dataSource = await this.twentyORMGlobalManager.getDataSourceForWorkspace( authContext.workspace.id, ); const repository = dataSource.getRepository( objectMetadataItemWithFieldMaps.nameSingular, ); const graphqlQueryParser = new GraphqlQueryParser( objectMetadataItemWithFieldMaps.fieldsByName, options.objectMetadataMaps, ); const selectedFields = graphqlFields(options.info); const graphqlQuerySelectedFieldsResult = graphqlQueryParser.parseSelectedFields( objectMetadataItemWithFieldMaps, selectedFields, ); const graphqlQueryResolverExecutionArgs = { args: computedArgs, options, dataSource, repository, graphqlQueryParser, graphqlQuerySelectedFieldsResult, }; const results = await this.resolve(graphqlQueryResolverExecutionArgs); const resultWithGetters = await this.queryResultGettersFactory.create( results, objectMetadataItemWithFieldMaps, authContext.workspace.id, options.objectMetadataMaps, ); const resultWithGettersArray = Array.isArray(resultWithGetters) ? resultWithGetters : [resultWithGetters]; await this.workspaceQueryHookService.executePostQueryHooks( authContext, objectMetadataItemWithFieldMaps.nameSingular, operationName, resultWithGettersArray, ); return resultWithGetters; } catch (error) { workspaceQueryRunnerGraphqlApiExceptionHandler(error, options); } } protected abstract resolve( executionArgs: GraphqlQueryResolverExecutionArgs, ): Promise; protected abstract validate( args: Input, options: WorkspaceQueryRunnerOptions, ): Promise; }