Common API - create common find one query (#14720)

closes https://github.com/twentyhq/core-team-issues/issues/1418

Tested : 
- findOne on Rest and Gql

### Vision
#### Common
- Common is kind of renamed Gql base resolver
- Common handles args (filter, values, ...) validation
- Common accepts depth or raw gql selected fields to compute
selectedFields
- Common is directly called by each CommonQueries (findOne, ...)
service, which extend CommonBaseQuery service
- Common sequence : 
| - Parse & Validate args (args-handlers, to create)
| - Build query (query-parsers : currently in gql-query-parsers, to
move)
| - Execute query
| - Fetch relation + format
#### Rest
- Simple parsing (without metadata validation)
- Calling Common API
- Simple rest response formatting
#### Gql
- Calling Common API
This commit is contained in:
Etienne
2025-10-08 12:18:42 +02:00
committed by GitHub
parent 8914ba9fec
commit c693c4d9cf
49 changed files with 1230 additions and 110 deletions
@@ -7,7 +7,7 @@ import {
type ObjectRecord,
type ObjectRecordFilter,
} from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
import { type WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface';
import { WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface';
import {
type CreateManyResolverArgs,
type CreateOneResolverArgs,
@@ -23,9 +23,9 @@ import {
import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service';
import { RecordInputTransformerService } from 'src/engine/core-modules/record-transformer/services/record-input-transformer.service';
import { WorkspaceNotFoundDefaultError } from 'src/engine/core-modules/workspace/workspace.exception';
import { type FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metadata-map';
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
import { WorkspaceNotFoundDefaultError } from 'src/engine/core-modules/workspace/workspace.exception';
@Injectable()
export class QueryRunnerArgsFactory {
@@ -214,7 +214,7 @@ export class QueryRunnerArgsFactory {
return allOverriddenRecords;
}
private overrideFilterByFieldMetadata(
public overrideFilterByFieldMetadata(
filter: ObjectRecordFilter | undefined,
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
) {
@@ -6,7 +6,7 @@ import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/objec
export interface WorkspaceQueryRunnerOptions {
authContext: AuthContext;
info: GraphQLResolveInfo;
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
objectMetadataMaps: ObjectMetadataMaps;
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
info: GraphQLResolveInfo;
}
@@ -1,5 +1,7 @@
import { type QueryFailedError } from 'typeorm';
import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
import { commonQueryRunnerToGraphqlApiExceptionHandler } from 'src/engine/api/common/common-query-runners/utils/common-query-runner-to-graphql-api-exception-handler.util';
import { GraphqlQueryRunnerException } from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception';
import { graphqlQueryRunnerExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/graphql-query-runner-exception-handler.util';
import { workspaceExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-exception-handler.util';
@@ -18,6 +20,7 @@ import { twentyORMGraphqlApiExceptionHandler } from 'src/engine/twenty-orm/utils
interface QueryFailedErrorWithCode extends QueryFailedError {
code: string;
}
//TODO : Refacto-common - Should be handle first in common api layer
export const workspaceQueryRunnerGraphqlApiExceptionHandler = (
error: QueryFailedErrorWithCode,
@@ -33,6 +36,8 @@ export const workspaceQueryRunnerGraphqlApiExceptionHandler = (
return graphqlQueryRunnerExceptionHandler(error);
case error instanceof TwentyORMException:
return twentyORMGraphqlApiExceptionHandler(error);
case error instanceof CommonQueryRunnerException:
return commonQueryRunnerToGraphqlApiExceptionHandler(error);
case error instanceof AuthException:
return authGraphqlApiExceptionHandler(error);
case error instanceof ApiKeyException:
@@ -5,6 +5,7 @@ import merge from 'lodash.merge';
import { type QueryResultFieldValue } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-field-value';
import { type WorkspaceResolverBuilderMethodNames } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
import { CommonQueryNames } from 'src/engine/api/common/types/common-query-args.type';
import { type WorkspaceQueryHookKey } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/decorators/workspace-query-hook.decorator';
import { WorkspaceQueryHookStorage } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/storage/workspace-query-hook.storage';
import { type WorkspacePreQueryHookPayload } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type';
@@ -18,8 +19,9 @@ export class WorkspaceQueryHookService {
private readonly workspaceQueryHookExplorer: WorkspaceQueryHookExplorer,
) {}
//TODO : Refacto-common - Should be Common
public async executePreQueryHooks<
T extends WorkspaceResolverBuilderMethodNames,
T extends WorkspaceResolverBuilderMethodNames | CommonQueryNames,
>(
authContext: AuthContext,
// TODO: We should allow wildcard for object name
@@ -13,8 +13,8 @@ import { FileModule } from 'src/engine/core-modules/file/file.module';
import { RecordPositionModule } from 'src/engine/core-modules/record-position/record-position.module';
import { RecordTransformerModule } from 'src/engine/core-modules/record-transformer/record-transformer.module';
import { TelemetryModule } from 'src/engine/core-modules/telemetry/telemetry.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { SubscriptionsModule } from 'src/engine/subscriptions/subscriptions.module';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
import { EntityEventsToDbListener } from './listeners/entity-events-to-db.listener';