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:
@ -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'],
|
||||
},
|
||||
];
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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],
|
||||
|
||||
Reference in New Issue
Block a user