add WorkspaceDuplicateCriteria decorator + update duplicate resolver logic (#10128)

## Context

All objects have '...duplicates' resolver but only companies and people
have duplicate criteria (hard coded constant).
Gql schema and resolver should be created only if duplicate criteria
exist.

## Solution

- Add a new @WorkspaceDuplicateCriteria decorator at object level,
defining duplicate criteria for given object.
- Add a new duplicate criteria field in ObjectMetadata table
- Update schema and resolver building logic
- Update front requests for duplicate check (only for object with
criteria defined)



closes https://github.com/twentyhq/twenty/issues/9828
This commit is contained in:
Etienne
2025-02-12 17:32:59 +01:00
committed by GitHub
parent b66289c44c
commit 0609b31c64
29 changed files with 491 additions and 121 deletions
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
import { GraphqlQueryRunnerModule } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-runner.module';
import { WorkspaceQueryRunnerModule } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module';
import { WorkspaceResolverBuilderService } from 'src/engine/api/graphql/workspace-resolver-builder/workspace-resolver-builder.service';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { WorkspaceResolverFactory } from './workspace-resolver.factory';
@@ -14,7 +15,11 @@ import { workspaceResolverBuilderFactories } from './factories/factories';
GraphqlQueryRunnerModule,
FeatureFlagModule,
],
providers: [...workspaceResolverBuilderFactories, WorkspaceResolverFactory],
exports: [WorkspaceResolverFactory],
providers: [
...workspaceResolverBuilderFactories,
WorkspaceResolverFactory,
WorkspaceResolverBuilderService,
],
exports: [WorkspaceResolverFactory, WorkspaceResolverBuilderService],
})
export class WorkspaceResolverBuilderModule {}
@@ -0,0 +1,25 @@
import { Injectable } from '@nestjs/common';
import { isDefined } from 'twenty-shared';
import { WorkspaceResolverBuilderMethodNames } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
import { FindDuplicatesResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/find-duplicates-resolver.factory';
@Injectable()
export class WorkspaceResolverBuilderService {
constructor() {}
shouldBuildResolver(
objectMetadata: ObjectMetadataInterface,
methodName: WorkspaceResolverBuilderMethodNames,
) {
switch (methodName) {
case FindDuplicatesResolverFactory.methodName:
return isDefined(objectMetadata.duplicateCriteria);
default:
return true;
}
}
}
@@ -9,6 +9,7 @@ import { RestoreManyResolverFactory } from 'src/engine/api/graphql/workspace-res
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 { WorkspaceResolverBuilderService } from 'src/engine/api/graphql/workspace-resolver-builder/workspace-resolver-builder.service';
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
import { getResolverName } from 'src/engine/utils/get-resolver-name.util';
@@ -45,6 +46,7 @@ export class WorkspaceResolverFactory {
private readonly restoreManyResolverFactory: RestoreManyResolverFactory,
private readonly destroyManyResolverFactory: DestroyManyResolverFactory,
private readonly searchResolverFactory: SearchResolverFactory,
private readonly workspaceResolverBuilderService: WorkspaceResolverBuilderService,
) {}
async create(
@@ -92,11 +94,18 @@ export class WorkspaceResolverFactory {
throw new Error(`Unknown query resolver type: ${methodName}`);
}
resolvers.Query[resolverName] = resolverFactory.create({
authContext,
objectMetadataMaps,
objectMetadataItemWithFieldMaps: objectMetadata,
});
if (
this.workspaceResolverBuilderService.shouldBuildResolver(
objectMetadata,
methodName,
)
) {
resolvers.Query[resolverName] = resolverFactory.create({
authContext,
objectMetadataMaps,
objectMetadataItemWithFieldMaps: objectMetadata,
});
}
}
// Generate mutation resolvers