From 78c1f4aef5b1ca73095a1fead7a4badb1b318e6e Mon Sep 17 00:00:00 2001 From: Weiko Date: Thu, 21 Nov 2024 01:15:09 +0100 Subject: [PATCH] more refacto --- .../enums/database-event-action.ts | 1 + .../graphql-query-runner.module.ts | 19 ++-- .../process-nested-relations.helper.ts | 2 +- .../interfaces/base-resolver-service.ts | 23 ++-- ...phql-query-create-many-resolver.service.ts | 7 +- ...aphql-query-create-one-resolver.service.ts | 7 +- ...phql-query-delete-many-resolver.service.ts | 100 ++++++++++++++++++ ...aphql-query-delete-one-resolver.service.ts | 96 +++++++++++++++++ ...hql-query-destroy-many-resolver.service.ts | 67 ++++-------- ...phql-query-destroy-one-resolver.service.ts | 63 ++++------- ...-query-find-duplicates-resolver.service.ts | 62 +++++------ ...raphql-query-find-many-resolver.service.ts | 15 ++- ...graphql-query-find-one-resolver.service.ts | 69 ++++-------- ...hql-query-restore-many-resolver.service.ts | 100 ++++++++++++++++++ ...phql-query-restore-one-resolver.service.ts | 96 +++++++++++++++++ .../graphql-query-search-resolver.service.ts | 98 ++++++++--------- ...phql-query-update-many-resolver.service.ts | 76 +++++-------- ...aphql-query-update-one-resolver.service.ts | 68 ++++-------- .../services/api-event-emitter.service.ts | 22 ++++ .../factories/create-many-resolver.factory.ts | 6 +- .../factories/create-one-resolver.factory.ts | 6 +- .../factories/delete-many-resolver.factory.ts | 7 +- .../factories/delete-one-resolver.factory.ts | 10 +- .../destroy-many-resolver.factory.ts | 6 +- .../factories/destroy-one-resolver.factory.ts | 6 +- .../factories/factories.ts | 3 + .../find-duplicates-resolver.factory.ts | 6 +- .../factories/find-many-resolver.factory.ts | 6 +- .../factories/find-one-resolver.factory.ts | 6 +- .../restore-many-resolver.factory.ts | 7 +- .../factories/restore-one-resolver.factory.ts | 49 +++++++++ .../factories/search-resolver-factory.ts | 6 +- .../factories/update-many-resolver.factory.ts | 6 +- .../factories/update-one-resolver.factory.ts | 6 +- .../workspace-resolvers-builder.interface.ts | 21 ++-- .../workspace-resolver.factory.ts | 19 ++-- .../utils/get-resolver-args.util.ts | 7 ++ .../twenty-orm/utils/format-result.util.ts | 4 +- .../engine/utils/get-resolver-name.util.ts | 24 +++-- .../record-crud.workflow-action.ts | 2 +- 40 files changed, 799 insertions(+), 405 deletions(-) create mode 100644 packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-delete-many-resolver.service.ts create mode 100644 packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-delete-one-resolver.service.ts create mode 100644 packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-restore-many-resolver.service.ts create mode 100644 packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-restore-one-resolver.service.ts create mode 100644 packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-one-resolver.factory.ts diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/enums/database-event-action.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/enums/database-event-action.ts index dfe45b33a..ed5d1705b 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/enums/database-event-action.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/enums/database-event-action.ts @@ -3,4 +3,5 @@ export enum DatabaseEventAction { UPDATED = 'updated', DELETED = 'deleted', DESTROYED = 'destroyed', + RESTORED = 'restored', } diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-runner.module.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-runner.module.ts index b82dcfe70..a3384e52a 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-runner.module.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/graphql-query-runner.module.ts @@ -1,13 +1,16 @@ import { Module } from '@nestjs/common'; -import { GraphqlQueryRunnerService } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-runner.service'; import { GraphqlQueryCreateManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-many-resolver.service'; import { GraphqlQueryCreateOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-one-resolver.service'; +import { GraphqlQueryDeleteManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-delete-many-resolver.service'; +import { GraphqlQueryDeleteOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-delete-one-resolver.service'; import { GraphqlQueryDestroyManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-destroy-many-resolver.service'; import { GraphqlQueryDestroyOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-destroy-one-resolver.service'; import { GraphqlQueryFindDuplicatesResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-duplicates-resolver.service'; import { GraphqlQueryFindManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-many-resolver.service'; import { GraphqlQueryFindOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-one-resolver.service'; +import { GraphqlQueryRestoreManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-restore-many-resolver.service'; +import { GraphqlQueryRestoreOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-restore-one-resolver.service'; import { GraphqlQuerySearchResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-search-resolver.service'; import { GraphqlQueryUpdateManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-update-many-resolver.service'; import { GraphqlQueryUpdateOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-update-one-resolver.service'; @@ -17,13 +20,17 @@ import { WorkspaceQueryRunnerModule } from 'src/engine/api/graphql/workspace-que import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module'; const graphqlQueryResolvers = [ - GraphqlQueryCreateOneResolverService, GraphqlQueryCreateManyResolverService, + GraphqlQueryCreateOneResolverService, + GraphqlQueryDeleteManyResolverService, + GraphqlQueryDeleteOneResolverService, GraphqlQueryDestroyManyResolverService, GraphqlQueryDestroyOneResolverService, GraphqlQueryFindDuplicatesResolverService, GraphqlQueryFindManyResolverService, GraphqlQueryFindOneResolverService, + GraphqlQueryRestoreManyResolverService, + GraphqlQueryRestoreOneResolverService, GraphqlQuerySearchResolverService, GraphqlQueryUpdateManyResolverService, GraphqlQueryUpdateOneResolverService, @@ -35,11 +42,7 @@ const graphqlQueryResolvers = [ WorkspaceQueryRunnerModule, FeatureFlagModule, ], - providers: [ - GraphqlQueryRunnerService, - ApiEventEmitterService, - ...graphqlQueryResolvers, - ], - exports: [GraphqlQueryRunnerService, ...graphqlQueryResolvers], + providers: [ApiEventEmitterService, ...graphqlQueryResolvers], + exports: [...graphqlQueryResolvers], }) export class GraphqlQueryRunnerModule {} diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper.ts index 63fa22727..844509cef 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper.ts @@ -378,7 +378,7 @@ export class ProcessNestedRelationsHelper { .take(limit) .getMany(); - const relationResults = formatResult( + const relationResults = formatResult( result, referenceObjectMetadata, objectMetadataMaps, diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service.ts index 91118ac7c..2a69fa359 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service.ts @@ -26,7 +26,7 @@ import { capitalize } from 'src/utils/capitalize'; export type GraphqlQueryResolverExecutionArgs = { args: Input; options: WorkspaceQueryRunnerOptions; - datasource: DataSource; + dataSource: DataSource; repository: WorkspaceRepository; graphqlQueryParser: GraphqlQueryParser; graphqlQuerySelectedFieldsResult: GraphqlQuerySelectedFieldsResult; @@ -52,16 +52,11 @@ export abstract class GraphqlQueryBaseResolverService< @Inject() protected readonly twentyORMGlobalManager: TwentyORMGlobalManager; - protected operationName: WorkspaceResolverBuilderMethodNames; - public async execute( args: Input, options: WorkspaceQueryRunnerOptions, + operationName: WorkspaceResolverBuilderMethodNames, ): Promise { - if (!this.operationName) { - throw new Error('Operation name is not set'); - } - const { authContext, objectMetadataItemWithFieldMaps } = options; await this.validate(args, options); @@ -70,23 +65,23 @@ export abstract class GraphqlQueryBaseResolverService< await this.workspaceQueryHookService.executePreQueryHooks( authContext, objectMetadataItemWithFieldMaps.nameSingular, - this.operationName, + operationName, args, ); const computedArgs = (await this.queryRunnerArgsFactory.create( hookedArgs, options, - ResolverArgsType[capitalize(this.operationName)], + ResolverArgsType[capitalize(operationName)], )) as Input; - const datasource = + const dataSource = await this.twentyORMGlobalManager.getDataSourceForWorkspace( authContext.workspace.id, ); - const repository = datasource.getRepository( - objectMetadataItemWithFieldMaps.namePlural, + const repository = dataSource.getRepository( + objectMetadataItemWithFieldMaps.nameSingular, ); const graphqlQueryParser = new GraphqlQueryParser( @@ -105,7 +100,7 @@ export abstract class GraphqlQueryBaseResolverService< const graphqlQueryResolverExecutionArgs = { args: computedArgs, options, - datasource, + dataSource, repository, graphqlQueryParser, graphqlQuerySelectedFieldsResult, @@ -127,7 +122,7 @@ export abstract class GraphqlQueryBaseResolverService< await this.workspaceQueryHookService.executePostQueryHooks( authContext, objectMetadataItemWithFieldMaps.nameSingular, - this.operationName, + operationName, resultWithGettersArray, ); diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-many-resolver.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-many-resolver.service.ts index bf8e63334..070cc21a7 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-many-resolver.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-many-resolver.service.ts @@ -22,11 +22,6 @@ export class GraphqlQueryCreateManyResolverService extends GraphqlQueryBaseResol CreateManyResolverArgs, ObjectRecord[] > { - constructor() { - super(); - this.operationName = 'createMany'; - } - async resolve( executionArgs: GraphqlQueryResolverExecutionArgs, ): Promise { @@ -73,7 +68,7 @@ export class GraphqlQueryCreateManyResolverService extends GraphqlQueryBaseResol relations: executionArgs.graphqlQuerySelectedFieldsResult.relations, limit: QUERY_MAX_RECORDS, authContext, - dataSource: executionArgs.datasource, + dataSource: executionArgs.dataSource, }); } diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-one-resolver.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-one-resolver.service.ts index 8abba75db..bce181745 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-one-resolver.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-create-one-resolver.service.ts @@ -22,11 +22,6 @@ export class GraphqlQueryCreateOneResolverService extends GraphqlQueryBaseResolv CreateOneResolverArgs, ObjectRecord > { - constructor() { - super(); - this.operationName = 'createOne'; - } - async resolve( executionArgs: GraphqlQueryResolverExecutionArgs, ): Promise { @@ -73,7 +68,7 @@ export class GraphqlQueryCreateOneResolverService extends GraphqlQueryBaseResolv relations: executionArgs.graphqlQuerySelectedFieldsResult.relations, limit: QUERY_MAX_RECORDS, authContext, - dataSource: executionArgs.datasource, + dataSource: executionArgs.dataSource, }); } diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-delete-many-resolver.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-delete-many-resolver.service.ts new file mode 100644 index 000000000..377d7d6a6 --- /dev/null +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-delete-many-resolver.service.ts @@ -0,0 +1,100 @@ +import { Injectable } from '@nestjs/common'; + +import { + GraphqlQueryBaseResolverService, + GraphqlQueryResolverExecutionArgs, +} from 'src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service'; +import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; +import { WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface'; +import { DeleteManyResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; + +import { QUERY_MAX_RECORDS } from 'src/engine/api/graphql/graphql-query-runner/constants/query-max-records.constant'; +import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper'; +import { ProcessNestedRelationsHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper'; +import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util'; +import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util'; +import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util'; +import { computeTableName } from 'src/engine/utils/compute-table-name.util'; + +@Injectable() +export class GraphqlQueryDeleteManyResolverService extends GraphqlQueryBaseResolverService< + DeleteManyResolverArgs, + ObjectRecord[] +> { + async resolve( + executionArgs: GraphqlQueryResolverExecutionArgs, + ): Promise { + const { authContext, objectMetadataItemWithFieldMaps, objectMetadataMaps } = + executionArgs.options; + + const queryBuilder = executionArgs.repository.createQueryBuilder( + objectMetadataItemWithFieldMaps.nameSingular, + ); + + const tableName = computeTableName( + objectMetadataItemWithFieldMaps.nameSingular, + objectMetadataItemWithFieldMaps.isCustom, + ); + + executionArgs.graphqlQueryParser.applyFilterToBuilder( + queryBuilder, + tableName, + executionArgs.args.filter, + ); + + const nonFormattedDeletedObjectRecords = await queryBuilder + .softDelete() + .returning('*') + .execute(); + + const formattedDeletedRecords = formatResult( + nonFormattedDeletedObjectRecords.raw, + objectMetadataItemWithFieldMaps, + objectMetadataMaps, + ); + + this.apiEventEmitterService.emitDeletedEvents( + formattedDeletedRecords, + authContext, + objectMetadataItemWithFieldMaps, + ); + + const processNestedRelationsHelper = new ProcessNestedRelationsHelper(); + + if (executionArgs.graphqlQuerySelectedFieldsResult.relations) { + await processNestedRelationsHelper.processNestedRelations({ + objectMetadataMaps, + parentObjectMetadataItem: objectMetadataItemWithFieldMaps, + parentObjectRecords: formattedDeletedRecords, + relations: executionArgs.graphqlQuerySelectedFieldsResult.relations, + limit: QUERY_MAX_RECORDS, + authContext, + dataSource: executionArgs.dataSource, + }); + } + + const typeORMObjectRecordsParser = + new ObjectRecordsToGraphqlConnectionHelper(objectMetadataMaps); + + return formattedDeletedRecords.map((record: ObjectRecord) => + typeORMObjectRecordsParser.processRecord({ + objectRecord: record, + objectName: objectMetadataItemWithFieldMaps.nameSingular, + take: 1, + totalCount: 1, + }), + ); + } + + async validate( + args: DeleteManyResolverArgs, + options: WorkspaceQueryRunnerOptions, + ): Promise { + assertMutationNotOnRemoteObject(options.objectMetadataItemWithFieldMaps); + if (!args.filter) { + throw new Error('Filter is required'); + } + + args.filter.id?.in?.forEach((id: string) => assertIsValidUuid(id)); + } +} diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-delete-one-resolver.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-delete-one-resolver.service.ts new file mode 100644 index 000000000..e7c183e02 --- /dev/null +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-delete-one-resolver.service.ts @@ -0,0 +1,96 @@ +import { Injectable } from '@nestjs/common'; + +import { + GraphqlQueryBaseResolverService, + GraphqlQueryResolverExecutionArgs, +} from 'src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service'; +import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; +import { WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface'; +import { DeleteOneResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; + +import { QUERY_MAX_RECORDS } from 'src/engine/api/graphql/graphql-query-runner/constants/query-max-records.constant'; +import { + GraphqlQueryRunnerException, + GraphqlQueryRunnerExceptionCode, +} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception'; +import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper'; +import { ProcessNestedRelationsHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper'; +import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util'; +import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util'; +import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util'; + +@Injectable() +export class GraphqlQueryDeleteOneResolverService extends GraphqlQueryBaseResolverService< + DeleteOneResolverArgs, + ObjectRecord +> { + async resolve( + executionArgs: GraphqlQueryResolverExecutionArgs, + ): Promise { + const { authContext, objectMetadataItemWithFieldMaps, objectMetadataMaps } = + executionArgs.options; + + const queryBuilder = executionArgs.repository.createQueryBuilder( + objectMetadataItemWithFieldMaps.nameSingular, + ); + + const nonFormattedDeletedObjectRecords = await queryBuilder + .where({ id: executionArgs.args.id }) + .softDelete() + .returning('*') + .execute(); + + const formattedDeletedRecords = formatResult( + nonFormattedDeletedObjectRecords.raw, + objectMetadataItemWithFieldMaps, + objectMetadataMaps, + ); + + this.apiEventEmitterService.emitDeletedEvents( + formattedDeletedRecords, + authContext, + objectMetadataItemWithFieldMaps, + ); + + if (formattedDeletedRecords.length === 0) { + throw new GraphqlQueryRunnerException( + 'Record not found', + GraphqlQueryRunnerExceptionCode.RECORD_NOT_FOUND, + ); + } + + const deletedRecord = formattedDeletedRecords[0]; + + const processNestedRelationsHelper = new ProcessNestedRelationsHelper(); + + if (executionArgs.graphqlQuerySelectedFieldsResult.relations) { + await processNestedRelationsHelper.processNestedRelations({ + objectMetadataMaps, + parentObjectMetadataItem: objectMetadataItemWithFieldMaps, + parentObjectRecords: [deletedRecord], + relations: executionArgs.graphqlQuerySelectedFieldsResult.relations, + limit: QUERY_MAX_RECORDS, + authContext, + dataSource: executionArgs.dataSource, + }); + } + + const typeORMObjectRecordsParser = + new ObjectRecordsToGraphqlConnectionHelper(objectMetadataMaps); + + return typeORMObjectRecordsParser.processRecord({ + objectRecord: deletedRecord, + objectName: objectMetadataItemWithFieldMaps.nameSingular, + take: 1, + totalCount: 1, + }); + } + + async validate( + args: DeleteOneResolverArgs, + options: WorkspaceQueryRunnerOptions, + ): Promise { + assertMutationNotOnRemoteObject(options.objectMetadataItemWithFieldMaps); + assertIsValidUuid(args.id); + } +} diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-destroy-many-resolver.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-destroy-many-resolver.service.ts index ced1d1229..abd5acc6d 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-destroy-many-resolver.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-destroy-many-resolver.service.ts @@ -1,76 +1,51 @@ import { Injectable } from '@nestjs/common'; -import graphqlFields from 'graphql-fields'; - -import { GraphqlQueryBaseResolverService } from 'src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service'; +import { + GraphqlQueryBaseResolverService, + GraphqlQueryResolverExecutionArgs, +} from 'src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service'; import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; import { WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface'; import { DestroyManyResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; import { QUERY_MAX_RECORDS } from 'src/engine/api/graphql/graphql-query-runner/constants/query-max-records.constant'; -import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser'; import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper'; import { ProcessNestedRelationsHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper'; import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util'; +import { computeTableName } from 'src/engine/utils/compute-table-name.util'; @Injectable() export class GraphqlQueryDestroyManyResolverService extends GraphqlQueryBaseResolverService< DestroyManyResolverArgs, ObjectRecord[] > { - constructor() { - super(); - this.operationName = 'destroyMany'; - } - async resolve( - args: DestroyManyResolverArgs, - options: WorkspaceQueryRunnerOptions, + executionArgs: GraphqlQueryResolverExecutionArgs, ): Promise { - const { - authContext, - objectMetadataItemWithFieldMaps, - objectMetadataMaps, - info, - } = options; + const { authContext, objectMetadataItemWithFieldMaps, objectMetadataMaps } = + executionArgs.options; - const dataSource = - await this.twentyORMGlobalManager.getDataSourceForWorkspace( - authContext.workspace.id, - ); - - const repository = dataSource.getRepository( + const queryBuilder = executionArgs.repository.createQueryBuilder( objectMetadataItemWithFieldMaps.nameSingular, ); - const graphqlQueryParser = new GraphqlQueryParser( - objectMetadataItemWithFieldMaps.fieldsByName, - objectMetadataMaps, - ); - - const selectedFields = graphqlFields(info); - - const { relations } = graphqlQueryParser.parseSelectedFields( - objectMetadataItemWithFieldMaps, - selectedFields, - ); - - const queryBuilder = repository.createQueryBuilder( + const tableName = computeTableName( objectMetadataItemWithFieldMaps.nameSingular, + objectMetadataItemWithFieldMaps.isCustom, ); - const withFilterQueryBuilder = graphqlQueryParser.applyFilterToBuilder( + executionArgs.graphqlQueryParser.applyFilterToBuilder( queryBuilder, - objectMetadataItemWithFieldMaps.nameSingular, - args.filter, + tableName, + executionArgs.args.filter, ); - const nonFormattedDeletedObjectRecords = await withFilterQueryBuilder + const nonFormattedDeletedObjectRecords = await queryBuilder .delete() .returning('*') .execute(); - const deletedRecords = formatResult( + const deletedRecords = formatResult( nonFormattedDeletedObjectRecords.raw, objectMetadataItemWithFieldMaps, objectMetadataMaps, @@ -78,21 +53,21 @@ export class GraphqlQueryDestroyManyResolverService extends GraphqlQueryBaseReso this.apiEventEmitterService.emitDestroyEvents( deletedRecords, - options.authContext, - options.objectMetadataItemWithFieldMaps, + authContext, + objectMetadataItemWithFieldMaps, ); const processNestedRelationsHelper = new ProcessNestedRelationsHelper(); - if (relations) { + if (executionArgs.graphqlQuerySelectedFieldsResult.relations) { await processNestedRelationsHelper.processNestedRelations({ objectMetadataMaps, parentObjectMetadataItem: objectMetadataItemWithFieldMaps, parentObjectRecords: deletedRecords, - relations, + relations: executionArgs.graphqlQuerySelectedFieldsResult.relations, limit: QUERY_MAX_RECORDS, authContext, - dataSource, + dataSource: executionArgs.dataSource, }); } diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-destroy-one-resolver.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-destroy-one-resolver.service.ts index 98ffcecd9..c477263d0 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-destroy-one-resolver.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-destroy-one-resolver.service.ts @@ -1,8 +1,9 @@ import { Injectable } from '@nestjs/common'; -import graphqlFields from 'graphql-fields'; - -import { GraphqlQueryBaseResolverService } from 'src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service'; +import { + GraphqlQueryBaseResolverService, + GraphqlQueryResolverExecutionArgs, +} from 'src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service'; import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; import { WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface'; import { DestroyOneResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; @@ -12,60 +13,34 @@ import { GraphqlQueryRunnerException, GraphqlQueryRunnerExceptionCode, } 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 { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper'; import { ProcessNestedRelationsHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper'; import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util'; +import { computeTableName } from 'src/engine/utils/compute-table-name.util'; @Injectable() export class GraphqlQueryDestroyOneResolverService extends GraphqlQueryBaseResolverService< DestroyOneResolverArgs, ObjectRecord > { - constructor() { - super(); - this.operationName = 'destroyOne'; - } - async resolve( - args: DestroyOneResolverArgs, - options: WorkspaceQueryRunnerOptions, + executionArgs: GraphqlQueryResolverExecutionArgs, ): Promise { - const { - authContext, - objectMetadataItemWithFieldMaps, - objectMetadataMaps, - info, - } = options; + const { authContext, objectMetadataItemWithFieldMaps, objectMetadataMaps } = + executionArgs.options; - const dataSource = - await this.twentyORMGlobalManager.getDataSourceForWorkspace( - authContext.workspace.id, - ); - - const repository = dataSource.getRepository( + const queryBuilder = executionArgs.repository.createQueryBuilder( objectMetadataItemWithFieldMaps.nameSingular, ); - const graphqlQueryParser = new GraphqlQueryParser( - objectMetadataItemWithFieldMaps.fieldsByName, - objectMetadataMaps, - ); - - const selectedFields = graphqlFields(info); - - const { relations } = graphqlQueryParser.parseSelectedFields( - objectMetadataItemWithFieldMaps, - selectedFields, - ); - - const queryBuilder = repository.createQueryBuilder( + const tableName = computeTableName( objectMetadataItemWithFieldMaps.nameSingular, + objectMetadataItemWithFieldMaps.isCustom, ); const nonFormattedDeletedObjectRecords = await queryBuilder - .where(`"${objectMetadataItemWithFieldMaps.nameSingular}".id = :id`, { - id: args.id, + .where(`"${tableName}".id = :id`, { + id: executionArgs.args.id, }) .take(1) .delete() @@ -79,7 +54,7 @@ export class GraphqlQueryDestroyOneResolverService extends GraphqlQueryBaseResol ); } - const deletedRecords = formatResult( + const deletedRecords = formatResult( nonFormattedDeletedObjectRecords.raw, objectMetadataItemWithFieldMaps, objectMetadataMaps, @@ -87,21 +62,21 @@ export class GraphqlQueryDestroyOneResolverService extends GraphqlQueryBaseResol this.apiEventEmitterService.emitDestroyEvents( deletedRecords, - options.authContext, - options.objectMetadataItemWithFieldMaps, + authContext, + objectMetadataItemWithFieldMaps, ); const processNestedRelationsHelper = new ProcessNestedRelationsHelper(); - if (relations) { + if (executionArgs.graphqlQuerySelectedFieldsResult.relations) { await processNestedRelationsHelper.processNestedRelations({ objectMetadataMaps, parentObjectMetadataItem: objectMetadataItemWithFieldMaps, parentObjectRecords: deletedRecords, - relations, + relations: executionArgs.graphqlQuerySelectedFieldsResult.relations, limit: QUERY_MAX_RECORDS, authContext, - dataSource, + dataSource: executionArgs.dataSource, }); } diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-duplicates-resolver.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-duplicates-resolver.service.ts index 8172b9f8c..2a1dc6309 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-duplicates-resolver.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-duplicates-resolver.service.ts @@ -3,7 +3,10 @@ import { Injectable } from '@nestjs/common'; import isEmpty from 'lodash.isempty'; import { In } from 'typeorm'; -import { GraphqlQueryBaseResolverService } from 'src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service'; +import { + GraphqlQueryBaseResolverService, + GraphqlQueryResolverExecutionArgs, +} from 'src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service'; import { ObjectRecord, ObjectRecordFilter, @@ -24,37 +27,23 @@ import { DUPLICATE_CRITERIA_COLLECTION } from 'src/engine/core-modules/duplicate import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps'; import { formatData } from 'src/engine/twenty-orm/utils/format-data.util'; import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util'; +import { computeTableName } from 'src/engine/utils/compute-table-name.util'; @Injectable() export class GraphqlQueryFindDuplicatesResolverService extends GraphqlQueryBaseResolverService< FindDuplicatesResolverArgs, IConnection[] > { - constructor() { - super(); - this.operationName = 'findDuplicates'; - } - async resolve( - args: FindDuplicatesResolverArgs>, - options: WorkspaceQueryRunnerOptions, + executionArgs: GraphqlQueryResolverExecutionArgs, ): Promise[]> { - const { authContext, objectMetadataItemWithFieldMaps, objectMetadataMaps } = - options; + const { objectMetadataItemWithFieldMaps, objectMetadataMaps } = + executionArgs.options; - const dataSource = - await this.twentyORMGlobalManager.getDataSourceForWorkspace( - authContext.workspace.id, + const existingRecordsQueryBuilder = + executionArgs.repository.createQueryBuilder( + objectMetadataItemWithFieldMaps.nameSingular, ); - const repository = dataSource.getRepository( - objectMetadataItemWithFieldMaps.nameSingular, - ); - const existingRecordsQueryBuilder = repository.createQueryBuilder( - objectMetadataItemWithFieldMaps.nameSingular, - ); - const duplicateRecordsQueryBuilder = repository.createQueryBuilder( - objectMetadataItemWithFieldMaps.nameSingular, - ); const graphqlQueryParser = new GraphqlQueryParser( objectMetadataMaps.byNameSingular[ @@ -68,9 +57,9 @@ export class GraphqlQueryFindDuplicatesResolverService extends GraphqlQueryBaseR let objectRecords: Partial[] = []; - if (args.ids) { + if (executionArgs.args.ids) { const nonFormattedObjectRecords = (await existingRecordsQueryBuilder - .where({ id: In(args.ids) }) + .where({ id: In(executionArgs.args.ids) }) .getMany()) as ObjectRecord[]; objectRecords = formatResult( @@ -78,8 +67,11 @@ export class GraphqlQueryFindDuplicatesResolverService extends GraphqlQueryBaseR objectMetadataItemWithFieldMaps, objectMetadataMaps, ); - } else if (args.data && !isEmpty(args.data)) { - objectRecords = formatData(args.data, objectMetadataItemWithFieldMaps); + } else if (executionArgs.args.data && !isEmpty(executionArgs.args.data)) { + objectRecords = formatData( + executionArgs.args.data, + objectMetadataItemWithFieldMaps, + ); } const duplicateConnections: IConnection[] = await Promise.all( @@ -102,16 +94,26 @@ export class GraphqlQueryFindDuplicatesResolverService extends GraphqlQueryBaseR }); } - const withFilterQueryBuilder = graphqlQueryParser.applyFilterToBuilder( - duplicateRecordsQueryBuilder, + const duplicateRecordsQueryBuilder = + executionArgs.repository.createQueryBuilder( + objectMetadataItemWithFieldMaps.nameSingular, + ); + + const tableName = computeTableName( objectMetadataItemWithFieldMaps.nameSingular, + objectMetadataItemWithFieldMaps.isCustom, + ); + + graphqlQueryParser.applyFilterToBuilder( + duplicateRecordsQueryBuilder, + tableName, duplicateConditions, ); const nonFormattedDuplicates = - (await withFilterQueryBuilder.getMany()) as ObjectRecord[]; + (await duplicateRecordsQueryBuilder.getMany()) as ObjectRecord[]; - const duplicates = formatResult( + const duplicates = formatResult( nonFormattedDuplicates, objectMetadataItemWithFieldMaps, objectMetadataMaps, diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-many-resolver.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-many-resolver.service.ts index 8e0484671..dcb529fdd 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-many-resolver.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-many-resolver.service.ts @@ -30,6 +30,7 @@ import { 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 { formatResult } from 'src/engine/twenty-orm/utils/format-result.util'; +import { computeTableName } from 'src/engine/utils/compute-table-name.util'; import { isDefined } from 'src/utils/is-defined'; @Injectable() @@ -39,7 +40,6 @@ export class GraphqlQueryFindManyResolverService extends GraphqlQueryBaseResolve > { constructor(private readonly featureFlagService: FeatureFlagService) { super(); - this.operationName = 'findMany'; } async resolve( @@ -57,9 +57,14 @@ export class GraphqlQueryFindManyResolverService extends GraphqlQueryBaseResolve let appliedFilters = executionArgs.args.filter ?? ({} as ObjectRecordFilter); + const tableName = computeTableName( + objectMetadataItemWithFieldMaps.nameSingular, + objectMetadataItemWithFieldMaps.isCustom, + ); + executionArgs.graphqlQueryParser.applyFilterToBuilder( aggregateQueryBuilder, - objectMetadataItemWithFieldMaps.nameSingular, + tableName, appliedFilters, ); @@ -94,14 +99,14 @@ export class GraphqlQueryFindManyResolverService extends GraphqlQueryBaseResolve executionArgs.graphqlQueryParser.applyFilterToBuilder( queryBuilder, - objectMetadataItemWithFieldMaps.nameSingular, + tableName, appliedFilters, ); executionArgs.graphqlQueryParser.applyOrderToBuilder( queryBuilder, orderByWithIdCondition, - objectMetadataItemWithFieldMaps.nameSingular, + tableName, isForwardPagination, ); @@ -169,7 +174,7 @@ export class GraphqlQueryFindManyResolverService extends GraphqlQueryBaseResolve aggregate: executionArgs.graphqlQuerySelectedFieldsResult.aggregate, limit, authContext, - dataSource: executionArgs.datasource, + dataSource: executionArgs.dataSource, }); } diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-one-resolver.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-one-resolver.service.ts index 01a8bcb26..8f11c354f 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-one-resolver.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-one-resolver.service.ts @@ -1,8 +1,9 @@ import { Injectable } from '@nestjs/common'; -import graphqlFields from 'graphql-fields'; - -import { GraphqlQueryBaseResolverService } from 'src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service'; +import { + GraphqlQueryBaseResolverService, + GraphqlQueryResolverExecutionArgs, +} from 'src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service'; import { ObjectRecord, ObjectRecordFilter, @@ -15,7 +16,6 @@ import { GraphqlQueryRunnerException, GraphqlQueryRunnerExceptionCode, } 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 { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper'; import { ProcessNestedRelationsHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper'; import { @@ -23,67 +23,42 @@ import { WorkspaceQueryRunnerExceptionCode, } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.exception'; import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util'; +import { computeTableName } from 'src/engine/utils/compute-table-name.util'; @Injectable() export class GraphqlQueryFindOneResolverService extends GraphqlQueryBaseResolverService< FindOneResolverArgs, ObjectRecord > { - constructor() { - super(); - this.operationName = 'findOne'; - } - async resolve( - args: FindOneResolverArgs, - options: WorkspaceQueryRunnerOptions, + executionArgs: GraphqlQueryResolverExecutionArgs, ): Promise { - const { - authContext, - objectMetadataItemWithFieldMaps, - info, - objectMetadataMaps, - } = options; + const { authContext, objectMetadataItemWithFieldMaps, objectMetadataMaps } = + executionArgs.options; - const dataSource = - await this.twentyORMGlobalManager.getDataSourceForWorkspace( - authContext.workspace.id, - ); - - const repository = dataSource.getRepository( + const queryBuilder = executionArgs.repository.createQueryBuilder( objectMetadataItemWithFieldMaps.nameSingular, ); - const queryBuilder = repository.createQueryBuilder( + const tableName = computeTableName( objectMetadataItemWithFieldMaps.nameSingular, + objectMetadataItemWithFieldMaps.isCustom, ); - const graphqlQueryParser = new GraphqlQueryParser( - objectMetadataItemWithFieldMaps.fieldsByName, - objectMetadataMaps, - ); - - const selectedFields = graphqlFields(info); - - const { relations } = graphqlQueryParser.parseSelectedFields( - objectMetadataItemWithFieldMaps, - selectedFields, - ); - - const withFilterQueryBuilder = graphqlQueryParser.applyFilterToBuilder( + executionArgs.graphqlQueryParser.applyFilterToBuilder( queryBuilder, - objectMetadataItemWithFieldMaps.nameSingular, - args.filter ?? ({} as ObjectRecordFilter), + tableName, + executionArgs.args.filter ?? ({} as ObjectRecordFilter), ); - const withDeletedQueryBuilder = graphqlQueryParser.applyDeletedAtToBuilder( - withFilterQueryBuilder, - args.filter ?? ({} as ObjectRecordFilter), + executionArgs.graphqlQueryParser.applyDeletedAtToBuilder( + queryBuilder, + executionArgs.args.filter ?? ({} as ObjectRecordFilter), ); - const nonFormattedObjectRecord = await withDeletedQueryBuilder.getOne(); + const nonFormattedObjectRecord = await queryBuilder.getOne(); - const objectRecord = formatResult( + const objectRecord = formatResult( nonFormattedObjectRecord, objectMetadataItemWithFieldMaps, objectMetadataMaps, @@ -100,15 +75,15 @@ export class GraphqlQueryFindOneResolverService extends GraphqlQueryBaseResolver const objectRecords = [objectRecord]; - if (relations) { + if (executionArgs.graphqlQuerySelectedFieldsResult.relations) { await processNestedRelationsHelper.processNestedRelations({ objectMetadataMaps, parentObjectMetadataItem: objectMetadataItemWithFieldMaps, parentObjectRecords: objectRecords, - relations, + relations: executionArgs.graphqlQuerySelectedFieldsResult.relations, limit: QUERY_MAX_RECORDS, authContext, - dataSource, + dataSource: executionArgs.dataSource, }); } diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-restore-many-resolver.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-restore-many-resolver.service.ts new file mode 100644 index 000000000..8ed96cf1c --- /dev/null +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-restore-many-resolver.service.ts @@ -0,0 +1,100 @@ +import { Injectable } from '@nestjs/common'; + +import { + GraphqlQueryBaseResolverService, + GraphqlQueryResolverExecutionArgs, +} from 'src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service'; +import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; +import { WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface'; +import { RestoreManyResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; + +import { QUERY_MAX_RECORDS } from 'src/engine/api/graphql/graphql-query-runner/constants/query-max-records.constant'; +import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper'; +import { ProcessNestedRelationsHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper'; +import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util'; +import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util'; +import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util'; +import { computeTableName } from 'src/engine/utils/compute-table-name.util'; + +@Injectable() +export class GraphqlQueryRestoreManyResolverService extends GraphqlQueryBaseResolverService< + RestoreManyResolverArgs, + ObjectRecord[] +> { + async resolve( + executionArgs: GraphqlQueryResolverExecutionArgs, + ): Promise { + const { authContext, objectMetadataItemWithFieldMaps, objectMetadataMaps } = + executionArgs.options; + + const queryBuilder = executionArgs.repository.createQueryBuilder( + objectMetadataItemWithFieldMaps.nameSingular, + ); + + const tableName = computeTableName( + objectMetadataItemWithFieldMaps.nameSingular, + objectMetadataItemWithFieldMaps.isCustom, + ); + + executionArgs.graphqlQueryParser.applyFilterToBuilder( + queryBuilder, + tableName, + executionArgs.args.filter, + ); + + const nonFormattedRestoredObjectRecords = await queryBuilder + .restore() + .returning('*') + .execute(); + + const formattedRestoredRecords = formatResult( + nonFormattedRestoredObjectRecords.raw, + objectMetadataItemWithFieldMaps, + objectMetadataMaps, + ); + + this.apiEventEmitterService.emitRestoreEvents( + formattedRestoredRecords, + authContext, + objectMetadataItemWithFieldMaps, + ); + + const processNestedRelationsHelper = new ProcessNestedRelationsHelper(); + + if (executionArgs.graphqlQuerySelectedFieldsResult.relations) { + await processNestedRelationsHelper.processNestedRelations({ + objectMetadataMaps, + parentObjectMetadataItem: objectMetadataItemWithFieldMaps, + parentObjectRecords: formattedRestoredRecords, + relations: executionArgs.graphqlQuerySelectedFieldsResult.relations, + limit: QUERY_MAX_RECORDS, + authContext, + dataSource: executionArgs.dataSource, + }); + } + + const typeORMObjectRecordsParser = + new ObjectRecordsToGraphqlConnectionHelper(objectMetadataMaps); + + return formattedRestoredRecords.map((record: ObjectRecord) => + typeORMObjectRecordsParser.processRecord({ + objectRecord: record, + objectName: objectMetadataItemWithFieldMaps.nameSingular, + take: 1, + totalCount: 1, + }), + ); + } + + async validate( + args: RestoreManyResolverArgs, + options: WorkspaceQueryRunnerOptions, + ): Promise { + assertMutationNotOnRemoteObject(options.objectMetadataItemWithFieldMaps); + if (!args.filter) { + throw new Error('Filter is required'); + } + + args.filter.id?.in?.forEach((id: string) => assertIsValidUuid(id)); + } +} diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-restore-one-resolver.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-restore-one-resolver.service.ts new file mode 100644 index 000000000..d596f7e14 --- /dev/null +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-restore-one-resolver.service.ts @@ -0,0 +1,96 @@ +import { Injectable } from '@nestjs/common'; + +import { + GraphqlQueryBaseResolverService, + GraphqlQueryResolverExecutionArgs, +} from 'src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service'; +import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; +import { WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface'; +import { RestoreOneResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; + +import { QUERY_MAX_RECORDS } from 'src/engine/api/graphql/graphql-query-runner/constants/query-max-records.constant'; +import { + GraphqlQueryRunnerException, + GraphqlQueryRunnerExceptionCode, +} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception'; +import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper'; +import { ProcessNestedRelationsHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper'; +import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util'; +import { assertMutationNotOnRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/assert-mutation-not-on-remote-object.util'; +import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util'; + +@Injectable() +export class GraphqlQueryRestoreOneResolverService extends GraphqlQueryBaseResolverService< + RestoreOneResolverArgs, + ObjectRecord +> { + async resolve( + executionArgs: GraphqlQueryResolverExecutionArgs, + ): Promise { + const { authContext, objectMetadataItemWithFieldMaps, objectMetadataMaps } = + executionArgs.options; + + const queryBuilder = executionArgs.repository.createQueryBuilder( + objectMetadataItemWithFieldMaps.nameSingular, + ); + + const nonFormattedRestoredObjectRecords = await queryBuilder + .where({ id: executionArgs.args.id }) + .restore() + .returning('*') + .execute(); + + const formattedRestoredRecords = formatResult( + nonFormattedRestoredObjectRecords.raw, + objectMetadataItemWithFieldMaps, + objectMetadataMaps, + ); + + this.apiEventEmitterService.emitRestoreEvents( + formattedRestoredRecords, + authContext, + objectMetadataItemWithFieldMaps, + ); + + if (formattedRestoredRecords.length === 0) { + throw new GraphqlQueryRunnerException( + 'Record not found', + GraphqlQueryRunnerExceptionCode.RECORD_NOT_FOUND, + ); + } + + const restoredRecord = formattedRestoredRecords[0]; + + const processNestedRelationsHelper = new ProcessNestedRelationsHelper(); + + if (executionArgs.graphqlQuerySelectedFieldsResult.relations) { + await processNestedRelationsHelper.processNestedRelations({ + objectMetadataMaps, + parentObjectMetadataItem: objectMetadataItemWithFieldMaps, + parentObjectRecords: [restoredRecord], + relations: executionArgs.graphqlQuerySelectedFieldsResult.relations, + limit: QUERY_MAX_RECORDS, + authContext, + dataSource: executionArgs.dataSource, + }); + } + + const typeORMObjectRecordsParser = + new ObjectRecordsToGraphqlConnectionHelper(objectMetadataMaps); + + return typeORMObjectRecordsParser.processRecord({ + objectRecord: restoredRecord, + objectName: objectMetadataItemWithFieldMaps.nameSingular, + take: 1, + totalCount: 1, + }); + } + + async validate( + args: RestoreOneResolverArgs, + options: WorkspaceQueryRunnerOptions, + ): Promise { + assertMutationNotOnRemoteObject(options.objectMetadataItemWithFieldMaps); + assertIsValidUuid(args.id); + } +} diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-search-resolver.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-search-resolver.service.ts index cc6848704..b607e8667 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-search-resolver.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-search-resolver.service.ts @@ -1,9 +1,11 @@ import { Injectable } from '@nestjs/common'; -import graphqlFields from 'graphql-fields'; import { Brackets } from 'typeorm'; -import { GraphqlQueryBaseResolverService } from 'src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service'; +import { + GraphqlQueryBaseResolverService, + GraphqlQueryResolverExecutionArgs, +} from 'src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service'; import { ObjectRecord, ObjectRecordFilter, @@ -14,11 +16,11 @@ import { WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-qu import { SearchResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; import { QUERY_MAX_RECORDS } from 'src/engine/api/graphql/graphql-query-runner/constants/query-max-records.constant'; -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 { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper'; import { ProcessNestedRelationsHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper'; import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/constants/search-vector-field.constants'; +import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util'; +import { computeTableName } from 'src/engine/utils/compute-table-name.util'; import { isDefined } from 'src/utils/is-defined'; @Injectable() @@ -26,32 +28,16 @@ export class GraphqlQuerySearchResolverService extends GraphqlQueryBaseResolverS SearchResolverArgs, IConnection > { - constructor() { - super(); - this.operationName = 'search'; - } - async resolve( - args: SearchResolverArgs, - options: WorkspaceQueryRunnerOptions, + executionArgs: GraphqlQueryResolverExecutionArgs, ): Promise> { - const { - authContext, - objectMetadataMaps, - objectMetadataItemWithFieldMaps, - info, - } = options; - - const repository = - await this.twentyORMGlobalManager.getRepositoryForWorkspace( - authContext.workspace.id, - objectMetadataItemWithFieldMaps.nameSingular, - ); + const { authContext, objectMetadataMaps, objectMetadataItemWithFieldMaps } = + executionArgs.options; const typeORMObjectRecordsParser = new ObjectRecordsToGraphqlConnectionHelper(objectMetadataMaps); - if (!isDefined(args.searchInput)) { + if (!isDefined(executionArgs.args.searchInput)) { return typeORMObjectRecordsParser.createConnection({ objectRecords: [], objectName: objectMetadataItemWithFieldMaps.nameSingular, @@ -63,26 +49,35 @@ export class GraphqlQuerySearchResolverService extends GraphqlQueryBaseResolverS }); } - const searchTerms = this.formatSearchTerms(args.searchInput, 'and'); - const searchTermsOr = this.formatSearchTerms(args.searchInput, 'or'); + const searchTerms = this.formatSearchTerms( + executionArgs.args.searchInput, + 'and', + ); + const searchTermsOr = this.formatSearchTerms( + executionArgs.args.searchInput, + 'or', + ); - const limit = args?.limit ?? QUERY_MAX_RECORDS; + const limit = executionArgs.args?.limit ?? QUERY_MAX_RECORDS; - const queryBuilder = repository.createQueryBuilder( + const queryBuilder = executionArgs.repository.createQueryBuilder( objectMetadataItemWithFieldMaps.nameSingular, ); - const graphqlQueryParser = new GraphqlQueryParser( - objectMetadataItemWithFieldMaps.fieldsByName, - objectMetadataMaps, + + const tableName = computeTableName( + objectMetadataItemWithFieldMaps.nameSingular, + objectMetadataItemWithFieldMaps.isCustom, ); - const queryBuilderWithFilter = graphqlQueryParser.applyFilterToBuilder( + executionArgs.graphqlQueryParser.applyFilterToBuilder( queryBuilder, - objectMetadataItemWithFieldMaps.nameSingular, - args.filter ?? ({} as ObjectRecordFilter), + tableName, + executionArgs.args.filter ?? ({} as ObjectRecordFilter), ); - const resultsWithTsVector = (await queryBuilderWithFilter + const countQueryBuilder = queryBuilder.clone(); + + const resultsWithTsVector = (await queryBuilder .andWhere( new Brackets((qb) => { qb.where( @@ -111,38 +106,31 @@ export class GraphqlQuerySearchResolverService extends GraphqlQueryBaseResolverS .take(limit) .getMany()) as ObjectRecord[]; - const objectRecords = await repository.formatResult(resultsWithTsVector); + const objectRecords = formatResult( + resultsWithTsVector, + objectMetadataItemWithFieldMaps, + objectMetadataMaps, + ); - const selectedFields = graphqlFields(info); - - const graphqlQuerySelectedFieldsResult: GraphqlQuerySelectedFieldsResult = - graphqlQueryParser.parseSelectedFields( - objectMetadataItemWithFieldMaps, - selectedFields, - ); - - const totalCount = isDefined(selectedFields.totalCount) - ? await queryBuilderWithFilter.getCount() + const totalCount = isDefined( + executionArgs.graphqlQuerySelectedFieldsResult.aggregate.totalCount, + ) + ? await countQueryBuilder.getCount() : 0; const order = undefined; const processNestedRelationsHelper = new ProcessNestedRelationsHelper(); - const dataSource = - await this.twentyORMGlobalManager.getDataSourceForWorkspace( - authContext.workspace.id, - ); - - if (graphqlQuerySelectedFieldsResult.relations) { + if (executionArgs.graphqlQuerySelectedFieldsResult.relations) { await processNestedRelationsHelper.processNestedRelations({ objectMetadataMaps, parentObjectMetadataItem: objectMetadataItemWithFieldMaps, parentObjectRecords: objectRecords, - relations: graphqlQuerySelectedFieldsResult.relations, - aggregate: graphqlQuerySelectedFieldsResult.aggregate, + relations: executionArgs.graphqlQuerySelectedFieldsResult.relations, + aggregate: executionArgs.graphqlQuerySelectedFieldsResult.aggregate, limit, authContext, - dataSource, + dataSource: executionArgs.dataSource, }); } diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-update-many-resolver.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-update-many-resolver.service.ts index 9b6b74241..1f194f18d 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-update-many-resolver.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-update-many-resolver.service.ts @@ -1,14 +1,14 @@ import { Injectable } from '@nestjs/common'; -import graphqlFields from 'graphql-fields'; - -import { GraphqlQueryBaseResolverService } from 'src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service'; +import { + GraphqlQueryBaseResolverService, + GraphqlQueryResolverExecutionArgs, +} from 'src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service'; import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; import { WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface'; import { UpdateManyResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; import { QUERY_MAX_RECORDS } from 'src/engine/api/graphql/graphql-query-runner/constants/query-max-records.constant'; -import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser'; import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper'; import { ProcessNestedRelationsHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper'; import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util'; @@ -22,44 +22,13 @@ export class GraphqlQueryUpdateManyResolverService extends GraphqlQueryBaseResol UpdateManyResolverArgs, ObjectRecord[] > { - constructor() { - super(); - this.operationName = 'updateMany'; - } - async resolve( - args: UpdateManyResolverArgs>, - options: WorkspaceQueryRunnerOptions, + executionArgs: GraphqlQueryResolverExecutionArgs, ): Promise { - const { - authContext, - objectMetadataItemWithFieldMaps, - objectMetadataMaps, - info, - } = options; + const { authContext, objectMetadataItemWithFieldMaps, objectMetadataMaps } = + executionArgs.options; - const dataSource = - await this.twentyORMGlobalManager.getDataSourceForWorkspace( - authContext.workspace.id, - ); - - const repository = dataSource.getRepository( - objectMetadataItemWithFieldMaps.nameSingular, - ); - - const graphqlQueryParser = new GraphqlQueryParser( - objectMetadataItemWithFieldMaps.fieldsByName, - objectMetadataMaps, - ); - - const selectedFields = graphqlFields(info); - - const { relations } = graphqlQueryParser.parseSelectedFields( - objectMetadataItemWithFieldMaps, - selectedFields, - ); - - const queryBuilder = repository.createQueryBuilder( + const queryBuilder = executionArgs.repository.createQueryBuilder( objectMetadataItemWithFieldMaps.nameSingular, ); @@ -68,30 +37,33 @@ export class GraphqlQueryUpdateManyResolverService extends GraphqlQueryBaseResol objectMetadataItemWithFieldMaps.isCustom, ); - const withFilterQueryBuilder = graphqlQueryParser.applyFilterToBuilder( + executionArgs.graphqlQueryParser.applyFilterToBuilder( queryBuilder, tableName, - args.filter, + executionArgs.args.filter, ); - const existingRecordsBuilder = withFilterQueryBuilder.clone(); + const existingRecordsBuilder = queryBuilder.clone(); const existingRecords = await existingRecordsBuilder.getMany(); - const formattedExistingRecords = formatResult( + const formattedExistingRecords = formatResult( existingRecords, objectMetadataItemWithFieldMaps, objectMetadataMaps, ); - const data = formatData(args.data, objectMetadataItemWithFieldMaps); + const data = formatData( + executionArgs.args.data, + objectMetadataItemWithFieldMaps, + ); - const nonFormattedUpdatedObjectRecords = await withFilterQueryBuilder + const nonFormattedUpdatedObjectRecords = await queryBuilder .update(data) .returning('*') .execute(); - const formattedUpdatedRecords = formatResult( + const formattedUpdatedRecords = formatResult( nonFormattedUpdatedObjectRecords.raw, objectMetadataItemWithFieldMaps, objectMetadataMaps, @@ -100,22 +72,22 @@ export class GraphqlQueryUpdateManyResolverService extends GraphqlQueryBaseResol this.apiEventEmitterService.emitUpdateEvents( formattedExistingRecords, formattedUpdatedRecords, - Object.keys(args.data), - options.authContext, - options.objectMetadataItemWithFieldMaps, + Object.keys(executionArgs.args.data), + authContext, + objectMetadataItemWithFieldMaps, ); const processNestedRelationsHelper = new ProcessNestedRelationsHelper(); - if (relations) { + if (executionArgs.graphqlQuerySelectedFieldsResult.relations) { await processNestedRelationsHelper.processNestedRelations({ objectMetadataMaps, parentObjectMetadataItem: objectMetadataItemWithFieldMaps, parentObjectRecords: formattedUpdatedRecords, - relations, + relations: executionArgs.graphqlQuerySelectedFieldsResult.relations, limit: QUERY_MAX_RECORDS, authContext, - dataSource, + dataSource: executionArgs.dataSource, }); } diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-update-one-resolver.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-update-one-resolver.service.ts index e3dd37cd8..4feea3166 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-update-one-resolver.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-update-one-resolver.service.ts @@ -1,8 +1,9 @@ import { Injectable } from '@nestjs/common'; -import graphqlFields from 'graphql-fields'; - -import { GraphqlQueryBaseResolverService } from 'src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service'; +import { + GraphqlQueryBaseResolverService, + GraphqlQueryResolverExecutionArgs, +} from 'src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service'; import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; import { WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface'; import { UpdateOneResolverArgs } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; @@ -12,7 +13,6 @@ import { GraphqlQueryRunnerException, GraphqlQueryRunnerExceptionCode, } 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 { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper'; import { ProcessNestedRelationsHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper'; import { assertIsValidUuid } from 'src/engine/api/graphql/workspace-query-runner/utils/assert-is-valid-uuid.util'; @@ -25,56 +25,28 @@ export class GraphqlQueryUpdateOneResolverService extends GraphqlQueryBaseResolv UpdateOneResolverArgs, ObjectRecord > { - constructor() { - super(); - this.operationName = 'updateOne'; - } - async resolve( - args: UpdateOneResolverArgs>, - options: WorkspaceQueryRunnerOptions, + executionArgs: GraphqlQueryResolverExecutionArgs, ): Promise { - const { - authContext, - objectMetadataItemWithFieldMaps, - objectMetadataMaps, - info, - } = options; + const { authContext, objectMetadataItemWithFieldMaps, objectMetadataMaps } = + executionArgs.options; - const dataSource = - await this.twentyORMGlobalManager.getDataSourceForWorkspace( - authContext.workspace.id, - ); - - const repository = dataSource.getRepository( + const queryBuilder = executionArgs.repository.createQueryBuilder( objectMetadataItemWithFieldMaps.nameSingular, ); - const graphqlQueryParser = new GraphqlQueryParser( - objectMetadataItemWithFieldMaps.fieldsByName, - objectMetadataMaps, - ); - - const selectedFields = graphqlFields(info); - - const { relations } = graphqlQueryParser.parseSelectedFields( + const data = formatData( + executionArgs.args.data, objectMetadataItemWithFieldMaps, - selectedFields, ); - const queryBuilder = repository.createQueryBuilder( - objectMetadataItemWithFieldMaps.nameSingular, - ); - - const data = formatData(args.data, objectMetadataItemWithFieldMaps); - const existingRecordBuilder = queryBuilder.clone(); const existingRecords = (await existingRecordBuilder - .where({ id: args.id }) + .where({ id: executionArgs.args.id }) .getMany()) as ObjectRecord[]; - const formattedExistingRecords = formatResult( + const formattedExistingRecords = formatResult( existingRecords, objectMetadataItemWithFieldMaps, objectMetadataMaps, @@ -82,11 +54,11 @@ export class GraphqlQueryUpdateOneResolverService extends GraphqlQueryBaseResolv const nonFormattedUpdatedObjectRecords = await queryBuilder .update(data) - .where({ id: args.id }) + .where({ id: executionArgs.args.id }) .returning('*') .execute(); - const formattedUpdatedRecords = formatResult( + const formattedUpdatedRecords = formatResult( nonFormattedUpdatedObjectRecords.raw, objectMetadataItemWithFieldMaps, objectMetadataMaps, @@ -95,9 +67,9 @@ export class GraphqlQueryUpdateOneResolverService extends GraphqlQueryBaseResolv this.apiEventEmitterService.emitUpdateEvents( formattedExistingRecords, formattedUpdatedRecords, - Object.keys(args.data), - options.authContext, - options.objectMetadataItemWithFieldMaps, + Object.keys(executionArgs.args.data), + authContext, + objectMetadataItemWithFieldMaps, ); if (formattedUpdatedRecords.length === 0) { @@ -111,15 +83,15 @@ export class GraphqlQueryUpdateOneResolverService extends GraphqlQueryBaseResolv const processNestedRelationsHelper = new ProcessNestedRelationsHelper(); - if (relations) { + if (executionArgs.graphqlQuerySelectedFieldsResult.relations) { await processNestedRelationsHelper.processNestedRelations({ objectMetadataMaps, parentObjectMetadataItem: objectMetadataItemWithFieldMaps, parentObjectRecords: [updatedRecord], - relations, + relations: executionArgs.graphqlQuerySelectedFieldsResult.relations, limit: QUERY_MAX_RECORDS, authContext, - dataSource, + dataSource: executionArgs.dataSource, }); } diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/services/api-event-emitter.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/services/api-event-emitter.service.ts index 6ab80d119..323001036 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/services/api-event-emitter.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/services/api-event-emitter.service.ts @@ -97,6 +97,28 @@ export class ApiEventEmitterService { ); } + public emitRestoreEvents( + records: T[], + authContext: AuthContext, + objectMetadataItem: ObjectMetadataInterface, + ): void { + this.workspaceEventEmitter.emit( + `${objectMetadataItem.nameSingular}.${DatabaseEventAction.RESTORED}`, + records.map((record) => { + return { + userId: authContext.user?.id, + recordId: record.id, + objectMetadata: objectMetadataItem, + properties: { + before: null, + after: record, + }, + }; + }), + authContext.workspace.id, + ); + } + public emitDestroyEvents( records: T[], authContext: AuthContext, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-many-resolver.factory.ts index f8aa1e421..3a158d68e 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-many-resolver.factory.ts @@ -36,7 +36,11 @@ export class CreateManyResolverFactory internalContext.objectMetadataItemWithFieldMaps, }; - return await this.graphqlQueryRunnerService.execute(args, options); + return await this.graphqlQueryRunnerService.execute( + args, + options, + CreateManyResolverFactory.methodName, + ); } catch (error) { workspaceQueryRunnerGraphqlApiExceptionHandler(error, context); } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-one-resolver.factory.ts index 2233decad..d6f2719c5 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-one-resolver.factory.ts @@ -36,7 +36,11 @@ export class CreateOneResolverFactory internalContext.objectMetadataItemWithFieldMaps, }; - return await this.graphqlQueryRunnerService.execute(args, options); + return await this.graphqlQueryRunnerService.execute( + args, + options, + CreateOneResolverFactory.methodName, + ); } catch (error) { workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-many-resolver.factory.ts index 54c0d6abb..aad99e565 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-many-resolver.factory.ts @@ -8,6 +8,7 @@ import { } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; +import { GraphqlQueryDeleteManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-delete-many-resolver.service'; import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; @Injectable() @@ -35,7 +36,11 @@ export class DeleteManyResolverFactory internalContext.objectMetadataItemWithFieldMaps, }; - return await this.graphqlQueryRunnerService.execute(args, options); + return await this.graphqlQueryRunnerService.execute( + args, + options, + DeleteManyResolverFactory.methodName, + ); } catch (error) { workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-one-resolver.factory.ts index 596a1d4db..d97e1946f 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-one-resolver.factory.ts @@ -8,7 +8,7 @@ import { } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; -import { GraphqlQueryRunnerService } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-runner.service'; +import { GraphqlQueryDeleteOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-delete-one-resolver.service'; import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; @Injectable() @@ -18,7 +18,7 @@ export class DeleteOneResolverFactory public static methodName = 'deleteOne' as const; constructor( - private readonly graphqlQueryRunnerService: GraphqlQueryRunnerService, + private readonly graphqlQueryRunnerService: GraphqlQueryDeleteOneResolverService, ) {} create( @@ -36,7 +36,11 @@ export class DeleteOneResolverFactory internalContext.objectMetadataItemWithFieldMaps, }; - return await this.graphqlQueryRunnerService.deleteOne(args, options); + return await this.graphqlQueryRunnerService.execute( + args, + options, + DeleteOneResolverFactory.methodName, + ); } catch (error) { workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-many-resolver.factory.ts index e9ed3d77a..2199d050c 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-many-resolver.factory.ts @@ -36,7 +36,11 @@ export class DestroyManyResolverFactory internalContext.objectMetadataItemWithFieldMaps, }; - return await this.graphqlQueryRunnerService.execute(args, options); + return await this.graphqlQueryRunnerService.execute( + args, + options, + DestroyManyResolverFactory.methodName, + ); } catch (error) { workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-one-resolver.factory.ts index a0216535e..ca0e46dae 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/destroy-one-resolver.factory.ts @@ -36,7 +36,11 @@ export class DestroyOneResolverFactory internalContext.objectMetadataItemWithFieldMaps, }; - return await this.graphQLQueryRunnerService.execute(args, options); + return await this.graphQLQueryRunnerService.execute( + args, + options, + DestroyOneResolverFactory.methodName, + ); } catch (error) { workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/factories.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/factories.ts index b728ef898..7c951ad5b 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/factories.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/factories.ts @@ -1,6 +1,7 @@ import { DestroyManyResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/destroy-many-resolver.factory'; import { DestroyOneResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/destroy-one-resolver.factory'; import { RestoreManyResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/restore-many-resolver.factory'; +import { RestoreOneResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/restore-one-resolver.factory'; import { SearchResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/search-resolver-factory'; import { UpdateManyResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory'; @@ -25,6 +26,7 @@ export const workspaceResolverBuilderFactories = [ DeleteManyResolverFactory, DestroyOneResolverFactory, DestroyManyResolverFactory, + RestoreOneResolverFactory, RestoreManyResolverFactory, SearchResolverFactory, ]; @@ -45,6 +47,7 @@ export const workspaceResolverBuilderMethodNames = { DeleteManyResolverFactory.methodName, DestroyOneResolverFactory.methodName, DestroyManyResolverFactory.methodName, + RestoreOneResolverFactory.methodName, RestoreManyResolverFactory.methodName, ], } as const; diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-duplicates-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-duplicates-resolver.factory.ts index b05fca5c5..aaef6fe92 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-duplicates-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-duplicates-resolver.factory.ts @@ -36,7 +36,11 @@ export class FindDuplicatesResolverFactory internalContext.objectMetadataItemWithFieldMaps, }; - return await this.graphqlQueryRunnerService.execute(args, options); + return await this.graphqlQueryRunnerService.execute( + args, + options, + FindDuplicatesResolverFactory.methodName, + ); } catch (error) { workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-many-resolver.factory.ts index 88f9a6ec6..5d06d9292 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-many-resolver.factory.ts @@ -36,7 +36,11 @@ export class FindManyResolverFactory internalContext.objectMetadataItemWithFieldMaps, }; - return await this.graphqlQueryRunnerService.execute(args, options); + return await this.graphqlQueryRunnerService.execute( + args, + options, + FindManyResolverFactory.methodName, + ); } catch (error) { workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-one-resolver.factory.ts index 7b95f8e87..6785198dc 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-one-resolver.factory.ts @@ -36,7 +36,11 @@ export class FindOneResolverFactory internalContext.objectMetadataItemWithFieldMaps, }; - return await this.graphqlQueryRunnerService.execute(args, options); + return await this.graphqlQueryRunnerService.execute( + args, + options, + FindOneResolverFactory.methodName, + ); } catch (error) { workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-many-resolver.factory.ts index 8d643e5b3..2dbfd5ff0 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-many-resolver.factory.ts @@ -8,6 +8,7 @@ import { } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; +import { GraphqlQueryRestoreManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-restore-many-resolver.service'; import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; @Injectable() @@ -35,7 +36,11 @@ export class RestoreManyResolverFactory internalContext.objectMetadataItemWithFieldMaps, }; - return await this.graphqlQueryRunnerService.restoreMany(args, options); + return await this.graphqlQueryRunnerService.execute( + args, + options, + RestoreManyResolverFactory.methodName, + ); } catch (error) { workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-one-resolver.factory.ts new file mode 100644 index 000000000..aa5c65123 --- /dev/null +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/restore-one-resolver.factory.ts @@ -0,0 +1,49 @@ +import { Injectable } from '@nestjs/common'; + +import { WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface'; +import { WorkspaceResolverBuilderFactoryInterface } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolver-builder-factory.interface'; +import { + Resolver, + RestoreOneResolverArgs, +} from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; +import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; + +import { GraphqlQueryRestoreOneResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-restore-one-resolver.service'; +import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; + +@Injectable() +export class RestoreOneResolverFactory + implements WorkspaceResolverBuilderFactoryInterface +{ + public static methodName = 'restoreOne' as const; + + constructor( + private readonly graphqlQueryRunnerService: GraphqlQueryRestoreOneResolverService, + ) {} + + create( + context: WorkspaceSchemaBuilderContext, + ): Resolver { + const internalContext = context; + + return async (_source, args, context, info) => { + try { + const options: WorkspaceQueryRunnerOptions = { + authContext: internalContext.authContext, + info, + objectMetadataMaps: internalContext.objectMetadataMaps, + objectMetadataItemWithFieldMaps: + internalContext.objectMetadataItemWithFieldMaps, + }; + + return await this.graphqlQueryRunnerService.execute( + args, + options, + RestoreOneResolverFactory.methodName, + ); + } catch (error) { + workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); + } + }; + } +} diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/search-resolver-factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/search-resolver-factory.ts index 3e784066b..cb9c946be 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/search-resolver-factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/search-resolver-factory.ts @@ -34,7 +34,11 @@ export class SearchResolverFactory internalContext.objectMetadataItemWithFieldMaps, }; - return await this.graphqlQueryRunnerService.execute(args, options); + return await this.graphqlQueryRunnerService.execute( + args, + options, + SearchResolverFactory.methodName, + ); } catch (error) { workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory.ts index d6fbdb0b8..05ab767a6 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory.ts @@ -36,7 +36,11 @@ export class UpdateManyResolverFactory internalContext.objectMetadataItemWithFieldMaps, }; - return await this.graphqlQueryRunnerService.execute(args, options); + return await this.graphqlQueryRunnerService.execute( + args, + options, + UpdateManyResolverFactory.methodName, + ); } catch (error) { workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-one-resolver.factory.ts index 15103363f..ebe752e96 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-one-resolver.factory.ts @@ -36,7 +36,11 @@ export class UpdateOneResolverFactory internalContext.objectMetadataItemWithFieldMaps, }; - return await this.graphqlQueryRunnerService.execute(args, options); + return await this.graphqlQueryRunnerService.execute( + args, + options, + UpdateOneResolverFactory.methodName, + ); } catch (error) { workspaceQueryRunnerGraphqlApiExceptionHandler(error, internalContext); } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface.ts index 2551a8b3f..d216dfda6 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface.ts @@ -1,9 +1,9 @@ import { GraphQLFieldResolver } from 'graphql'; import { - ObjectRecord, - ObjectRecordFilter, - ObjectRecordOrderBy, + ObjectRecord, + ObjectRecordFilter, + ObjectRecordOrderBy, } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; import { workspaceResolverBuilderMethodNames } from 'src/engine/api/graphql/workspace-resolver-builder/factories/factories'; @@ -93,6 +93,10 @@ export interface DeleteManyResolverArgs { filter: Filter; } +export interface RestoreOneResolverArgs { + id: string; +} + export interface RestoreManyResolverArgs { filter: Filter; } @@ -125,11 +129,12 @@ export type ResolverArgs = | CreateOneResolverArgs | DeleteManyResolverArgs | DeleteOneResolverArgs + | DestroyManyResolverArgs + | FindDuplicatesResolverArgs | FindManyResolverArgs | FindOneResolverArgs - | FindDuplicatesResolverArgs - | UpdateManyResolverArgs - | UpdateOneResolverArgs - | DestroyManyResolverArgs | RestoreManyResolverArgs - | SearchResolverArgs; + | RestoreOneResolverArgs + | SearchResolverArgs + | UpdateManyResolverArgs + | UpdateOneResolverArgs; diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/workspace-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/workspace-resolver.factory.ts index 9d5370546..badbe097a 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/workspace-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/workspace-resolver.factory.ts @@ -6,6 +6,7 @@ import { DeleteManyResolverFactory } from 'src/engine/api/graphql/workspace-reso import { DestroyManyResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/destroy-many-resolver.factory'; import { DestroyOneResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/destroy-one-resolver.factory'; import { RestoreManyResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/restore-many-resolver.factory'; +import { RestoreOneResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/restore-one-resolver.factory'; import { SearchResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/search-resolver-factory'; import { UpdateManyResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory'; import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; @@ -40,6 +41,7 @@ export class WorkspaceResolverFactory { private readonly destroyOneResolverFactory: DestroyOneResolverFactory, private readonly updateManyResolverFactory: UpdateManyResolverFactory, private readonly deleteManyResolverFactory: DeleteManyResolverFactory, + private readonly restoreOneResolverFactory: RestoreOneResolverFactory, private readonly restoreManyResolverFactory: RestoreManyResolverFactory, private readonly destroyManyResolverFactory: DestroyManyResolverFactory, private readonly searchResolverFactory: SearchResolverFactory, @@ -54,19 +56,20 @@ export class WorkspaceResolverFactory { WorkspaceResolverBuilderMethodNames, WorkspaceResolverBuilderFactoryInterface >([ - ['findMany', this.findManyResolverFactory], - ['findOne', this.findOneResolverFactory], - ['findDuplicates', this.findDuplicatesResolverFactory], ['createMany', this.createManyResolverFactory], ['createOne', this.createOneResolverFactory], - ['updateOne', this.updateOneResolverFactory], - ['deleteOne', this.deleteOneResolverFactory], - ['destroyOne', this.destroyOneResolverFactory], - ['updateMany', this.updateManyResolverFactory], ['deleteMany', this.deleteManyResolverFactory], - ['restoreMany', this.restoreManyResolverFactory], + ['deleteOne', this.deleteOneResolverFactory], ['destroyMany', this.destroyManyResolverFactory], + ['destroyOne', this.destroyOneResolverFactory], + ['findDuplicates', this.findDuplicatesResolverFactory], + ['findMany', this.findManyResolverFactory], + ['findOne', this.findOneResolverFactory], + ['restoreMany', this.restoreManyResolverFactory], + ['restoreOne', this.restoreOneResolverFactory], ['search', this.searchResolverFactory], + ['updateMany', this.updateManyResolverFactory], + ['updateOne', this.updateOneResolverFactory], ]); const resolvers: IResolvers = { Query: {}, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/utils/get-resolver-args.util.ts b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/utils/get-resolver-args.util.ts index b50047e62..990d77f4a 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/utils/get-resolver-args.util.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/utils/get-resolver-args.util.ts @@ -130,6 +130,13 @@ export const getResolverArgs = ( isNullable: false, }, }; + case 'restoreOne': + return { + id: { + type: GraphQLID, + isNullable: false, + }, + }; case 'destroyMany': return { filter: { diff --git a/packages/twenty-server/src/engine/twenty-orm/utils/format-result.util.ts b/packages/twenty-server/src/engine/twenty-orm/utils/format-result.util.ts index df56ee2bf..ceb2f427c 100644 --- a/packages/twenty-server/src/engine/twenty-orm/utils/format-result.util.ts +++ b/packages/twenty-server/src/engine/twenty-orm/utils/format-result.util.ts @@ -1,7 +1,5 @@ import { isPlainObject } from '@nestjs/common/utils/shared.utils'; -import { ObjectLiteral } from 'typeorm'; - import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface'; import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types'; @@ -15,7 +13,7 @@ import { getCompositeFieldMetadataCollection } from 'src/engine/twenty-orm/utils import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util'; export function formatResult( - data: ObjectLiteral | ObjectLiteral[], + data: any, ObjectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps, objectMetadataMaps: ObjectMetadataMaps, ): T { diff --git a/packages/twenty-server/src/engine/utils/get-resolver-name.util.ts b/packages/twenty-server/src/engine/utils/get-resolver-name.util.ts index f274475e7..c4b140ad2 100644 --- a/packages/twenty-server/src/engine/utils/get-resolver-name.util.ts +++ b/packages/twenty-server/src/engine/utils/get-resolver-name.util.ts @@ -15,24 +15,32 @@ export const getResolverName = ( return `${camelCase(objectMetadata.nameSingular)}`; case 'findDuplicates': return `${camelCase(objectMetadata.nameSingular)}Duplicates`; - case 'createMany': - return `create${pascalCase(objectMetadata.namePlural)}`; + case 'createOne': return `create${pascalCase(objectMetadata.nameSingular)}`; + case 'createMany': + return `create${pascalCase(objectMetadata.namePlural)}`; + case 'updateOne': return `update${pascalCase(objectMetadata.nameSingular)}`; - case 'deleteOne': - return `delete${pascalCase(objectMetadata.nameSingular)}`; - case 'destroyOne': - return `destroy${pascalCase(objectMetadata.nameSingular)}`; case 'updateMany': return `update${pascalCase(objectMetadata.namePlural)}`; - case 'restoreMany': - return `restore${pascalCase(objectMetadata.namePlural)}`; + + case 'deleteOne': + return `delete${pascalCase(objectMetadata.nameSingular)}`; case 'deleteMany': return `delete${pascalCase(objectMetadata.namePlural)}`; + + case 'destroyOne': + return `destroy${pascalCase(objectMetadata.nameSingular)}`; case 'destroyMany': return `destroy${pascalCase(objectMetadata.namePlural)}`; + + case 'restoreOne': + return `restore${pascalCase(objectMetadata.nameSingular)}`; + case 'restoreMany': + return `restore${pascalCase(objectMetadata.namePlural)}`; + case 'search': return `search${pascalCase(objectMetadata.namePlural)}`; default: diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/record-crud.workflow-action.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/record-crud.workflow-action.ts index 39f688de4..1657c0ff1 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/record-crud.workflow-action.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/record-crud/record-crud.workflow-action.ts @@ -249,7 +249,7 @@ export class RecordCRUDWorkflowAction implements WorkflowAction { .take(workflowActionInput.limit ?? QUERY_MAX_RECORDS) .getMany(); - return formatResult( + return formatResult( nonFormattedObjectRecords, objectMetadataItemWithFieldsMaps, objectMetadataMaps,