diff --git a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindDuplicateRecords.test.tsx b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindDuplicateRecords.test.tsx index 98d3cad285c08..61b5393c8ca2d 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindDuplicateRecords.test.tsx +++ b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useFindDuplicateRecords.test.tsx @@ -42,7 +42,7 @@ describe('useFindDuplicateRecords', () => { const { result } = renderHook( () => useFindDuplicateRecords({ - objectRecordId, + objectRecordIds: [objectRecordId], objectNameSingular, }), { diff --git a/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicateRecords.ts b/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicateRecords.ts index 4bf2d0c1ebfd4..33f83cec9c336 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicateRecords.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicateRecords.ts @@ -14,11 +14,11 @@ import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { logError } from '~/utils/logError'; export const useFindDuplicateRecords = ({ - objectRecordId = '', + objectRecordIds = [], objectNameSingular, onCompleted, }: ObjectMetadataItemIdentifier & { - objectRecordId: string | undefined; + objectRecordIds: string[] | undefined; onCompleted?: (data: RecordGqlConnection) => void; skip?: boolean; }) => { @@ -42,7 +42,7 @@ export const useFindDuplicateRecords = ({ findDuplicateRecordsQuery, { variables: { - id: objectRecordId, + ids: objectRecordIds, }, onCompleted: (data) => { onCompleted?.(data[queryResponseField]); diff --git a/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicatesRecordsQuery.ts b/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicatesRecordsQuery.ts index 9968d2d46a725..95c26a04e541e 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicatesRecordsQuery.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useFindDuplicatesRecordsQuery.ts @@ -22,10 +22,10 @@ export const useFindDuplicateRecordsQuery = ({ const findDuplicateRecordsQuery = gql` query FindDuplicate${capitalize( objectMetadataItem.nameSingular, - )}($id: ID!) { + )}($ids: [ID]!) { ${getFindDuplicateRecordsQueryResponseField( objectMetadataItem.nameSingular, - )}(id: $id) { + )}(ids: $ids) { edges { node ${mapObjectMetadataToGraphQLQuery({ objectMetadataItems, diff --git a/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailDuplicatesSection.tsx b/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailDuplicatesSection.tsx index 3a82313b87e7c..6830f6a02dce4 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailDuplicatesSection.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailDuplicatesSection.tsx @@ -13,7 +13,7 @@ export const RecordDetailDuplicatesSection = ({ objectNameSingular: string; }) => { const { records: duplicateRecords } = useFindDuplicateRecords({ - objectRecordId, + objectRecordIds: [objectRecordId], objectNameSingular, }); diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/factories/find-duplicates-query.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/factories/find-duplicates-query.factory.ts index 5d02736a375b5..5281dc7be5e71 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/factories/find-duplicates-query.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/factories/find-duplicates-query.factory.ts @@ -26,7 +26,7 @@ export class FindDuplicatesQueryFactory { async create( args: FindDuplicatesResolverArgs, options: WorkspaceQueryBuilderOptions, - currentRecord?: Record, + existingRecords?: Record[], ) { const fieldsString = await this.fieldsStringFactory.create( options.info, @@ -34,9 +34,15 @@ export class FindDuplicatesQueryFactory { options.objectMetadataCollection, ); - if (currentRecord) { + if (existingRecords) { + const query = existingRecords.reduce((acc, record, index) => { + return ( + acc + this.buildQuery(fieldsString, options, undefined, record, index) + ); + }, ''); + return `query { - ${this.buildQuery(fieldsString, options, undefined, currentRecord)} + ${query} }`; } @@ -67,14 +73,14 @@ export class FindDuplicatesQueryFactory { fieldsString: string, options: WorkspaceQueryBuilderOptions, data?: Record, - currentRecord?: Record, + existingRecord?: Record, index?: number, ) { const duplicateCondition = this.duplicateService.buildDuplicateConditionForGraphQL( options.objectMetadataItem, - data ?? currentRecord, - currentRecord?.id, + data ?? existingRecord, + existingRecord?.id, ); const filters = stringifyWithoutKeyQuote(duplicateCondition); diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/workspace-query-builder.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/workspace-query-builder.factory.ts index 1d67b58f4c1b0..3a86bced32398 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/workspace-query-builder.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-builder/workspace-query-builder.factory.ts @@ -67,12 +67,12 @@ export class WorkspaceQueryBuilderFactory { findDuplicates( args: FindDuplicatesResolverArgs, options: WorkspaceQueryBuilderOptions, - existingRecord?: IRecord, + existingRecords?: IRecord[], ): Promise { return this.findDuplicatesQueryFactory.create( args, options, - existingRecord, + existingRecords, ); } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts index 5d461e448f507..5b0b390fffd62 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts @@ -75,10 +75,10 @@ export class QueryRunnerArgsFactory { case ResolverArgsType.FindDuplicates: return { ...args, - id: await this.overrideValueByFieldMetadata( - 'id', - (args as FindDuplicatesResolverArgs).id, - fieldMetadataMap, + ids: await Promise.all( + (args as FindDuplicatesResolverArgs).ids?.map((id) => + this.overrideValueByFieldMetadata('id', id, fieldMetadataMap), + ) ?? [], ), data: await Promise.all( (args as FindDuplicatesResolverArgs).data?.map((arg, index) => diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.service.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.service.ts index 7d085b2dd7f9d..a09dd2af04261 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.service.ts @@ -172,13 +172,13 @@ export class WorkspaceQueryRunnerService { args: FindDuplicatesResolverArgs, options: WorkspaceQueryRunnerOptions, ): Promise | undefined> { - if (!args.data && !args.id) { + if (!args.data && !args.ids) { throw new BadRequestException( 'You have to provide either "data" or "id" argument', ); } - if (!args.id && isEmpty(args.data)) { + if (!args.ids && isEmpty(args.data)) { throw new BadRequestException( 'The "data" condition can not be empty when ID input not provided', ); @@ -192,24 +192,24 @@ export class WorkspaceQueryRunnerService { ResolverArgsType.FindDuplicates, )) as FindDuplicatesResolverArgs; - let existingRecord: Record | null = null; + let existingRecords: IRecord[] | undefined = undefined; - if (computedArgs.id) { - existingRecord = await this.duplicateService.findExistingRecords( - [computedArgs.id], + if (computedArgs.ids) { + existingRecords = await this.duplicateService.findExistingRecords( + computedArgs.ids, objectMetadataItem, workspaceId, ); - if (existingRecord && existingRecord.length != 1) { - throw new NotFoundError(`Object with id ${args.id} not found`); + if (!existingRecords || existingRecords.length === 0) { + throw new NotFoundError(`Object with id ${args.ids} not found`); } } const query = await this.workspaceQueryBuilderFactory.findDuplicates( computedArgs, options, - existingRecord ? existingRecord[0] : null, + existingRecords, ); await this.workspacePreQueryHookService.executePreHooks( @@ -672,9 +672,6 @@ export class WorkspaceQueryRunnerService { [], )[0], }; - - console.log('result', result); - const errors = graphqlResult?.[0]?.resolve?.errors; if ( @@ -705,8 +702,6 @@ export class WorkspaceQueryRunnerService { objectMetadataItem, ); - console.log(resultWithGetters); - return parseResult(resultWithGetters); } 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 19754c670b3d0..9cba4a9079757 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 @@ -40,7 +40,7 @@ export interface FindOneResolverArgs { } export interface FindDuplicatesResolverArgs { - id?: string; + ids?: string[]; data?: Data[]; } 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 e2e07c7c97014..609f268fd81fc 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 @@ -87,9 +87,10 @@ export const getResolverArgs = ( }; case 'findDuplicates': return { - id: { + ids: { type: GraphQLID, isNullable: true, + isArray: true, }, data: { kind: InputTypeDefinitionKind.Create, diff --git a/packages/twenty-server/src/engine/core-modules/duplicate/duplicate.service.ts b/packages/twenty-server/src/engine/core-modules/duplicate/duplicate.service.ts index d2a16f3f4a047..aa2fa2ee5f720 100644 --- a/packages/twenty-server/src/engine/core-modules/duplicate/duplicate.service.ts +++ b/packages/twenty-server/src/engine/core-modules/duplicate/duplicate.service.ts @@ -1,6 +1,7 @@ import { Injectable } from '@nestjs/common'; import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface'; +import { Record as IRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface'; import { settings } from 'src/engine/constants/settings'; import { DUPLICATE_CRITERIA_COLLECTION } from 'src/engine/core-modules/duplicate/constants/duplicate-criteria.constants'; @@ -38,7 +39,7 @@ export class DuplicateService { workspaceId, ); - return results as Record[] | null; + return results as IRecord[]; } async findDuplicate(