feat: find duplicate objects init (#4038)

* feat: find duplicate objects backend init

* refactor: move duplicate criteria to constants

* fix: correct constant usage after type change

* feat: skip query generation in case its not necessary

* feat: filter out existing duplicate

* feat: FE queries and hooks

* feat: show duplicates on FE

* refactor: should-skip-query moved to workspace utils

* refactor: naming improvements

* refactor: current record typings/parsing improvements

* refactor: throw error if existing record not found

* fix: domain -> domainName duplicate criteria

* refactor: fieldNames -> columnNames

* docs: add explanation to duplicate criteria collection

* feat: add person linkedinLinkUrl as duplicate criteria

* feat: throw early when bot id and data are empty

* refactor: trying to improve readability of filter criteria query

* refactor: naming improvements

* refactor: remove shouldSkipQuery

* feat: resolve empty array in case of empty filter

* feat: hide whole section in case of no duplicates

* feat: FE display list the same way as relations

* test: basic unit test coverage

* Refactor Record detail section front

* Use Create as input argument of findDuplicates

* Improve coverage

* Fix

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
rostaklein
2024-02-24 19:12:21 +01:00
committed by GitHub
parent 05c206073d
commit 1b04dfe3c6
30 changed files with 875 additions and 100 deletions

View File

@ -0,0 +1,30 @@
import { RecordDuplicateCriteria } from 'src/workspace/workspace-query-builder/interfaces/record.interface';
/**
* objectName: directly reference the name of the object from the metadata tables.
* columnNames: reference the column names not the field names.
* So if we need to reference a custom field, we should directly add the column name like `_customColumn`.
* If we need to terence a composite field, we should add all children of the composite like `nameFirstName` and `nameLastName`
*/
export const duplicateCriteriaCollection: RecordDuplicateCriteria[] = [
{
objectName: 'company',
columnNames: ['domainName'],
},
{
objectName: 'company',
columnNames: ['name'],
},
{
objectName: 'person',
columnNames: ['nameFirstName', 'nameLastName'],
},
{
objectName: 'person',
columnNames: ['linkedinLinkUrl'],
},
{
objectName: 'person',
columnNames: ['email'],
},
];

View File

@ -1,5 +1,6 @@
import { UpdateManyResolverFactory } from 'src/workspace/workspace-resolver-builder/factories/update-many-resolver.factory';
import { FindDuplicatesResolverFactory } from './find-duplicates-resolver.factory';
import { FindManyResolverFactory } from './find-many-resolver.factory';
import { FindOneResolverFactory } from './find-one-resolver.factory';
import { CreateManyResolverFactory } from './create-many-resolver.factory';
@ -12,6 +13,7 @@ import { ExecuteQuickActionOnOneResolverFactory } from './execute-quick-action-o
export const workspaceResolverBuilderFactories = [
FindManyResolverFactory,
FindOneResolverFactory,
FindDuplicatesResolverFactory,
CreateManyResolverFactory,
CreateOneResolverFactory,
UpdateOneResolverFactory,
@ -25,6 +27,7 @@ export const workspaceResolverBuilderMethodNames = {
queries: [
FindManyResolverFactory.methodName,
FindOneResolverFactory.methodName,
FindDuplicatesResolverFactory.methodName,
],
mutations: [
CreateManyResolverFactory.methodName,

View File

@ -0,0 +1,38 @@
import { Injectable } from '@nestjs/common';
import {
FindDuplicatesResolverArgs,
Resolver,
} from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface';
import { WorkspaceSchemaBuilderContext } from 'src/workspace/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface';
import { WorkspaceResolverBuilderFactoryInterface } from 'src/workspace/workspace-resolver-builder/interfaces/workspace-resolver-builder-factory.interface';
import { WorkspaceQueryRunnerService } from 'src/workspace/workspace-query-runner/workspace-query-runner.service';
@Injectable()
export class FindDuplicatesResolverFactory
implements WorkspaceResolverBuilderFactoryInterface
{
public static methodName = 'findDuplicates' as const;
constructor(
private readonly workspaceQueryRunnerService: WorkspaceQueryRunnerService,
) {}
create(
context: WorkspaceSchemaBuilderContext,
): Resolver<FindDuplicatesResolverArgs> {
const internalContext = context;
return (_source, args, context, info) => {
return this.workspaceQueryRunnerService.findDuplicates(args, {
objectMetadataItem: internalContext.objectMetadataItem,
workspaceId: internalContext.workspaceId,
userId: internalContext.userId,
info,
fieldMetadataCollection: internalContext.fieldMetadataCollection,
objectMetadataCollection: internalContext.objectMetadataCollection,
});
};
}
}

View File

@ -26,6 +26,11 @@ export interface FindOneResolverArgs<Filter = any> {
filter?: Filter;
}
export interface FindDuplicatesResolverArgs<Data extends Record = Record> {
id?: string;
data?: Data;
}
export interface CreateOneResolverArgs<Data extends Record = Record> {
data: Data;
}
@ -81,5 +86,6 @@ export type ResolverArgs =
| DeleteOneResolverArgs
| FindManyResolverArgs
| FindOneResolverArgs
| FindDuplicatesResolverArgs
| UpdateManyResolverArgs
| UpdateOneResolverArgs;

View File

@ -9,6 +9,7 @@ import { UpdateManyResolverFactory } from 'src/workspace/workspace-resolver-buil
import { DeleteManyResolverFactory } from 'src/workspace/workspace-resolver-builder/factories/delete-many-resolver.factory';
import { ExecuteQuickActionOnOneResolverFactory } from 'src/workspace/workspace-resolver-builder/factories/execute-quick-action-on-one-resolver.factory';
import { FindDuplicatesResolverFactory } from './factories/find-duplicates-resolver.factory';
import { FindManyResolverFactory } from './factories/find-many-resolver.factory';
import { FindOneResolverFactory } from './factories/find-one-resolver.factory';
import { CreateManyResolverFactory } from './factories/create-many-resolver.factory';
@ -28,6 +29,7 @@ export class WorkspaceResolverFactory {
constructor(
private readonly findManyResolverFactory: FindManyResolverFactory,
private readonly findOneResolverFactory: FindOneResolverFactory,
private readonly findDuplicatesResolverFactory: FindDuplicatesResolverFactory,
private readonly createManyResolverFactory: CreateManyResolverFactory,
private readonly createOneResolverFactory: CreateOneResolverFactory,
private readonly updateOneResolverFactory: UpdateOneResolverFactory,
@ -49,6 +51,7 @@ export class WorkspaceResolverFactory {
>([
['findMany', this.findManyResolverFactory],
['findOne', this.findOneResolverFactory],
['findDuplicates', this.findDuplicatesResolverFactory],
['createMany', this.createManyResolverFactory],
['createOne', this.createOneResolverFactory],
['updateOne', this.updateOneResolverFactory],