feat: merge records (#13436)

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
neo773
2025-07-31 15:36:36 +05:30
committed by GitHub
parent 00d12e854a
commit 06e0814ce8
45 changed files with 2047 additions and 29 deletions
@@ -12,4 +12,5 @@ export const RESOLVER_METHOD_NAMES = {
RESTORE_ONE: 'restoreOne',
DESTROY_MANY: 'destroyMany',
DESTROY_ONE: 'destroyOne',
MERGE_MANY: 'mergeMany',
} as const;
@@ -11,6 +11,7 @@ import { DeleteOneResolverFactory } from './delete-one-resolver.factory';
import { FindDuplicatesResolverFactory } from './find-duplicates-resolver.factory';
import { FindManyResolverFactory } from './find-many-resolver.factory';
import { FindOneResolverFactory } from './find-one-resolver.factory';
import { MergeManyResolverFactory } from './merge-many-resolver.factory';
import { UpdateOneResolverFactory } from './update-one-resolver.factory';
export const workspaceResolverBuilderFactories = [
@@ -27,6 +28,7 @@ export const workspaceResolverBuilderFactories = [
DestroyManyResolverFactory,
RestoreOneResolverFactory,
RestoreManyResolverFactory,
MergeManyResolverFactory,
];
export const workspaceResolverBuilderMethodNames = {
@@ -46,5 +48,6 @@ export const workspaceResolverBuilderMethodNames = {
DestroyManyResolverFactory.methodName,
RestoreOneResolverFactory.methodName,
RestoreManyResolverFactory.methodName,
MergeManyResolverFactory.methodName,
],
} as const;
@@ -0,0 +1,45 @@
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 {
MergeManyResolverArgs,
Resolver,
} 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 { GraphqlQueryMergeManyResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-merge-many-resolver.service';
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
@Injectable()
export class MergeManyResolverFactory
implements WorkspaceResolverBuilderFactoryInterface
{
public static methodName = RESOLVER_METHOD_NAMES.MERGE_MANY;
constructor(
private readonly graphqlQueryRunnerService: GraphqlQueryMergeManyResolverService,
) {}
create(
context: WorkspaceSchemaBuilderContext,
): Resolver<MergeManyResolverArgs> {
const internalContext = context;
return async (_source, args, _context, info) => {
const options: WorkspaceQueryRunnerOptions = {
authContext: internalContext.authContext,
info,
objectMetadataMaps: internalContext.objectMetadataMaps,
objectMetadataItemWithFieldMaps:
internalContext.objectMetadataItemWithFieldMaps,
};
return await this.graphqlQueryRunnerService.execute(
args,
options,
MergeManyResolverFactory.methodName,
);
};
}
}
@@ -26,6 +26,7 @@ export enum ResolverArgsType {
RestoreMany = 'RestoreMany',
DestroyMany = 'DestroyMany',
DestroyOne = 'DestroyOne',
MergeMany = 'MergeMany',
}
export interface FindManyResolverArgs<
@@ -99,6 +100,12 @@ export interface RestoreManyResolverArgs<Filter = any> {
filter: Filter;
}
export interface MergeManyResolverArgs {
ids: string[];
conflictPriorityIndex: number;
dryRun?: boolean;
}
export interface DestroyOneResolverArgs {
id: string;
}
@@ -132,6 +139,7 @@ export type ResolverArgs =
| FindDuplicatesResolverArgs
| FindManyResolverArgs
| FindOneResolverArgs
| MergeManyResolverArgs
| RestoreManyResolverArgs
| RestoreOneResolverArgs
| UpdateManyResolverArgs
@@ -5,6 +5,7 @@ import { isDefined } from 'twenty-shared/utils';
import { WorkspaceResolverBuilderMethodNames } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
import { FindDuplicatesResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/find-duplicates-resolver.factory';
import { MergeManyResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/merge-many-resolver.factory';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
@Injectable()
@@ -18,6 +19,8 @@ export class WorkspaceResolverBuilderService {
switch (methodName) {
case FindDuplicatesResolverFactory.methodName:
return isDefined(objectMetadata.duplicateCriteria);
case MergeManyResolverFactory.methodName:
return isDefined(objectMetadata.duplicateCriteria);
default:
return true;
}
@@ -6,6 +6,7 @@ import { isDefined } from 'twenty-shared/utils';
import { DeleteManyResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/delete-many-resolver.factory';
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 { MergeManyResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/merge-many-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 { UpdateManyResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory';
@@ -52,6 +53,7 @@ export class WorkspaceResolverFactory {
private readonly restoreOneResolverFactory: RestoreOneResolverFactory,
private readonly restoreManyResolverFactory: RestoreManyResolverFactory,
private readonly destroyManyResolverFactory: DestroyManyResolverFactory,
private readonly mergeManyResolverFactory: MergeManyResolverFactory,
private readonly workspaceResolverBuilderService: WorkspaceResolverBuilderService,
private readonly featureFlagService: FeatureFlagService,
) {}
@@ -74,6 +76,7 @@ export class WorkspaceResolverFactory {
['findDuplicates', this.findDuplicatesResolverFactory],
['findMany', this.findManyResolverFactory],
['findOne', this.findOneResolverFactory],
['mergeMany', this.mergeManyResolverFactory],
['restoreMany', this.restoreManyResolverFactory],
['restoreOne', this.restoreOneResolverFactory],
['updateMany', this.updateManyResolverFactory],
@@ -154,12 +157,19 @@ export class WorkspaceResolverFactory {
throw new Error(`Unknown mutation resolver type: ${methodName}`);
}
// @ts-expect-error legacy noImplicitAny
resolvers.Mutation[resolverName] = resolverFactory.create({
authContext,
objectMetadataMaps,
objectMetadataItemWithFieldMaps: objectMetadata,
});
if (
this.workspaceResolverBuilderService.shouldBuildResolver(
objectMetadata,
methodName,
)
) {
// @ts-expect-error legacy noImplicitAny
resolvers.Mutation[resolverName] = resolverFactory.create({
authContext,
objectMetadataMaps,
objectMetadataItemWithFieldMaps: objectMetadata,
});
}
}
}