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:
@ -0,0 +1,93 @@
|
||||
import { FieldMetadataType } from 'twenty-shared';
|
||||
|
||||
import { WorkspaceEntityDuplicateCriteria } from 'src/engine/api/graphql/workspace-query-builder/types/workspace-entity-duplicate-criteria.type';
|
||||
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
|
||||
export const mockPersonObjectMetadata = (
|
||||
duplicateCriteria: WorkspaceEntityDuplicateCriteria[],
|
||||
): ObjectMetadataItemWithFieldMaps => ({
|
||||
id: '',
|
||||
standardId: '',
|
||||
nameSingular: 'person',
|
||||
namePlural: 'people',
|
||||
labelSingular: 'Person',
|
||||
labelPlural: 'People',
|
||||
description: 'A person',
|
||||
targetTableName: 'DEPRECATED',
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isAuditLogged: true,
|
||||
duplicateCriteria: duplicateCriteria,
|
||||
fromRelations: [],
|
||||
toRelations: [],
|
||||
labelIdentifierFieldMetadataId: '',
|
||||
imageIdentifierFieldMetadataId: '',
|
||||
workspaceId: '',
|
||||
fields: [],
|
||||
indexMetadatas: [],
|
||||
fieldsById: {},
|
||||
fieldsByName: {
|
||||
name: {
|
||||
id: '',
|
||||
objectMetadataId: '',
|
||||
type: FieldMetadataType.FULL_NAME,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
defaultValue: {
|
||||
lastName: "''",
|
||||
firstName: "''",
|
||||
},
|
||||
description: 'Contact’s name',
|
||||
isCustom: false,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '',
|
||||
},
|
||||
emails: {
|
||||
id: '',
|
||||
objectMetadataId: '',
|
||||
type: FieldMetadataType.EMAILS,
|
||||
name: 'emails',
|
||||
label: 'Emails',
|
||||
defaultValue: {
|
||||
primaryEmail: "''",
|
||||
additionalEmails: null,
|
||||
},
|
||||
description: 'Contact’s Emails',
|
||||
isCustom: false,
|
||||
workspaceId: '',
|
||||
},
|
||||
linkedinLink: {
|
||||
id: '',
|
||||
objectMetadataId: '',
|
||||
type: FieldMetadataType.LINKS,
|
||||
name: 'linkedinLink',
|
||||
label: 'Linkedin',
|
||||
defaultValue: {
|
||||
primaryLinkUrl: "''",
|
||||
secondaryLinks: "'[]'",
|
||||
primaryLinkLabel: "''",
|
||||
},
|
||||
description: 'Contact’s Linkedin account',
|
||||
isCustom: false,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '',
|
||||
},
|
||||
jobTitle: {
|
||||
id: '',
|
||||
objectMetadataId: '',
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'jobTitle',
|
||||
label: 'Job Title',
|
||||
defaultValue: "''",
|
||||
description: 'Contact’s job title',
|
||||
isCustom: false,
|
||||
isNullable: false,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -0,0 +1,20 @@
|
||||
import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
||||
|
||||
export const mockPersonRecords: Partial<ObjectRecord>[] = [
|
||||
{
|
||||
name: {
|
||||
firstName: 'Testfirst',
|
||||
lastName: 'Testlast',
|
||||
},
|
||||
emails: {
|
||||
primaryEmail: 'test@test.fr',
|
||||
additionalEmails: [],
|
||||
},
|
||||
linkedinLink: {
|
||||
primaryLinkLabel: '',
|
||||
primaryLinkUrl: '',
|
||||
secondaryLinks: [],
|
||||
},
|
||||
jobTitle: 'Test job',
|
||||
},
|
||||
];
|
||||
@ -0,0 +1,125 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { mockPersonObjectMetadata } from 'src/engine/api/graphql/graphql-query-runner/__mocks__/mockPersonObjectMetadata';
|
||||
import { mockPersonRecords } from 'src/engine/api/graphql/graphql-query-runner/__mocks__/mockPersonRecords';
|
||||
import { ProcessNestedRelationsHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper';
|
||||
import { GraphqlQueryFindDuplicatesResolverService } from 'src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-find-duplicates-resolver.service';
|
||||
import { ApiEventEmitterService } from 'src/engine/api/graphql/graphql-query-runner/services/api-event-emitter.service';
|
||||
import { QueryResultGettersFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/query-result-getters.factory';
|
||||
import { QueryRunnerArgsFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory';
|
||||
import { WorkspaceQueryHookService } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/workspace-query-hook.service';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
|
||||
describe('GraphqlQueryFindDuplicatesResolverService', () => {
|
||||
let service: GraphqlQueryFindDuplicatesResolverService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
GraphqlQueryFindDuplicatesResolverService,
|
||||
WorkspaceQueryHookService,
|
||||
QueryRunnerArgsFactory,
|
||||
QueryResultGettersFactory,
|
||||
ApiEventEmitterService,
|
||||
TwentyORMGlobalManager,
|
||||
ProcessNestedRelationsHelper,
|
||||
FeatureFlagService,
|
||||
PermissionsService,
|
||||
],
|
||||
})
|
||||
.overrideProvider(WorkspaceQueryHookService)
|
||||
.useValue({})
|
||||
.overrideProvider(QueryRunnerArgsFactory)
|
||||
.useValue({})
|
||||
.overrideProvider(QueryResultGettersFactory)
|
||||
.useValue({})
|
||||
.overrideProvider(ApiEventEmitterService)
|
||||
.useValue({})
|
||||
.overrideProvider(TwentyORMGlobalManager)
|
||||
.useValue({})
|
||||
.overrideProvider(ProcessNestedRelationsHelper)
|
||||
.useValue({})
|
||||
.overrideProvider(FeatureFlagService)
|
||||
.useValue({})
|
||||
.overrideProvider(PermissionsService)
|
||||
.useValue({})
|
||||
.compile();
|
||||
|
||||
service = module.get<GraphqlQueryFindDuplicatesResolverService>(
|
||||
GraphqlQueryFindDuplicatesResolverService,
|
||||
);
|
||||
});
|
||||
|
||||
describe('buildDuplicateConditions', () => {
|
||||
it('should build conditions based on duplicate criteria from composite field', () => {
|
||||
const duplicateConditons = service.buildDuplicateConditions(
|
||||
mockPersonObjectMetadata([['emailsPrimaryEmail']]),
|
||||
mockPersonRecords,
|
||||
'recordId',
|
||||
);
|
||||
|
||||
expect(duplicateConditons).toEqual({
|
||||
or: [
|
||||
{
|
||||
emailsPrimaryEmail: {
|
||||
eq: 'test@test.fr',
|
||||
},
|
||||
},
|
||||
],
|
||||
id: {
|
||||
neq: 'recordId',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should build conditions based on duplicate criteria from basic field', () => {
|
||||
const duplicateConditons = service.buildDuplicateConditions(
|
||||
mockPersonObjectMetadata([['jobTitle']]),
|
||||
mockPersonRecords,
|
||||
'recordId',
|
||||
);
|
||||
|
||||
expect(duplicateConditons).toEqual({
|
||||
or: [
|
||||
{
|
||||
jobTitle: {
|
||||
eq: 'Test job',
|
||||
},
|
||||
},
|
||||
],
|
||||
id: {
|
||||
neq: 'recordId',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should not build conditions based on duplicate criteria if record value is null or too small', () => {
|
||||
const duplicateConditons = service.buildDuplicateConditions(
|
||||
mockPersonObjectMetadata([['linkedinLinkPrimaryLinkUrl']]),
|
||||
mockPersonRecords,
|
||||
'recordId',
|
||||
);
|
||||
|
||||
expect(duplicateConditons).toEqual({});
|
||||
});
|
||||
|
||||
it('should build conditions based on duplicate criteria and without recordId filter', () => {
|
||||
const duplicateConditons = service.buildDuplicateConditions(
|
||||
mockPersonObjectMetadata([['jobTitle']]),
|
||||
mockPersonRecords,
|
||||
);
|
||||
|
||||
expect(duplicateConditons).toEqual({
|
||||
or: [
|
||||
{
|
||||
jobTitle: {
|
||||
eq: 'Test job',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -23,11 +23,13 @@ import {
|
||||
import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
|
||||
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
|
||||
import { settings } from 'src/engine/constants/settings';
|
||||
import { DUPLICATE_CRITERIA_COLLECTION } from 'src/engine/core-modules/duplicate/constants/duplicate-criteria.constants';
|
||||
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||
import { formatData } from 'src/engine/twenty-orm/utils/format-data.util';
|
||||
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
|
||||
import {
|
||||
formatResult,
|
||||
getCompositeFieldMetadataMap,
|
||||
} from 'src/engine/twenty-orm/utils/format-result.util';
|
||||
|
||||
@Injectable()
|
||||
export class GraphqlQueryFindDuplicatesResolverService extends GraphqlQueryBaseResolverService<
|
||||
@ -149,7 +151,7 @@ export class GraphqlQueryFindDuplicatesResolverService extends GraphqlQueryBaseR
|
||||
return duplicateConnections;
|
||||
}
|
||||
|
||||
private buildDuplicateConditions(
|
||||
buildDuplicateConditions(
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
|
||||
records?: Partial<ObjectRecord>[] | undefined,
|
||||
filteringByExistingRecordId?: string,
|
||||
@ -158,13 +160,21 @@ export class GraphqlQueryFindDuplicatesResolverService extends GraphqlQueryBaseR
|
||||
return {};
|
||||
}
|
||||
|
||||
const criteriaCollection = this.getApplicableDuplicateCriteriaCollection(
|
||||
const criteriaCollection =
|
||||
objectMetadataItemWithFieldMaps.duplicateCriteria || [];
|
||||
|
||||
const formattedRecords = formatData(
|
||||
records,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
);
|
||||
|
||||
const conditions = records.flatMap((record) => {
|
||||
const compositeFieldMetadataMap = getCompositeFieldMetadataMap(
|
||||
objectMetadataItemWithFieldMaps,
|
||||
);
|
||||
|
||||
const conditions = formattedRecords.flatMap((record) => {
|
||||
const criteriaWithMatchingArgs = criteriaCollection.filter((criteria) =>
|
||||
criteria.columnNames.every((columnName) => {
|
||||
criteria.every((columnName) => {
|
||||
const value = record[columnName] as string | undefined;
|
||||
|
||||
return (
|
||||
@ -176,8 +186,18 @@ export class GraphqlQueryFindDuplicatesResolverService extends GraphqlQueryBaseR
|
||||
return criteriaWithMatchingArgs.map((criteria) => {
|
||||
const condition = {};
|
||||
|
||||
criteria.columnNames.forEach((columnName) => {
|
||||
condition[columnName] = { eq: record[columnName] };
|
||||
criteria.forEach((columnName) => {
|
||||
const compositeFieldMetadata =
|
||||
compositeFieldMetadataMap.get(columnName);
|
||||
|
||||
if (compositeFieldMetadata) {
|
||||
condition[compositeFieldMetadata.parentField] = {
|
||||
...condition[compositeFieldMetadata.parentField],
|
||||
[compositeFieldMetadata.name]: { eq: record[columnName] },
|
||||
};
|
||||
} else {
|
||||
condition[columnName] = { eq: record[columnName] };
|
||||
}
|
||||
});
|
||||
|
||||
return condition;
|
||||
@ -197,16 +217,6 @@ export class GraphqlQueryFindDuplicatesResolverService extends GraphqlQueryBaseR
|
||||
return filter;
|
||||
}
|
||||
|
||||
private getApplicableDuplicateCriteriaCollection(
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
|
||||
) {
|
||||
return DUPLICATE_CRITERIA_COLLECTION.filter(
|
||||
(duplicateCriteria) =>
|
||||
duplicateCriteria.objectName ===
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
);
|
||||
}
|
||||
|
||||
async validate(
|
||||
args: FindDuplicatesResolverArgs,
|
||||
_options: WorkspaceQueryRunnerOptions,
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
type columnName = string;
|
||||
|
||||
export type WorkspaceEntityDuplicateCriteria = columnName[];
|
||||
@ -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
|
||||
|
||||
@ -6,6 +6,7 @@ import { WorkspaceResolverBuilderMethodNames } from 'src/engine/api/graphql/work
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import { WorkspaceResolverBuilderService } from 'src/engine/api/graphql/workspace-resolver-builder/workspace-resolver-builder.service';
|
||||
import { TypeMapperService } from 'src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service';
|
||||
import { TypeDefinitionsStorage } from 'src/engine/api/graphql/workspace-schema-builder/storages/type-definitions.storage';
|
||||
import { getResolverArgs } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-resolver-args.util';
|
||||
@ -28,6 +29,7 @@ export class RootTypeFactory {
|
||||
private readonly typeDefinitionsStorage: TypeDefinitionsStorage,
|
||||
private readonly typeMapperService: TypeMapperService,
|
||||
private readonly argsFactory: ArgsFactory,
|
||||
private readonly workspaceResolverBuilderService: WorkspaceResolverBuilderService,
|
||||
) {}
|
||||
|
||||
create(
|
||||
@ -70,53 +72,60 @@ export class RootTypeFactory {
|
||||
|
||||
for (const objectMetadata of objectMetadataCollection) {
|
||||
for (const methodName of workspaceResolverMethodNames) {
|
||||
const name = getResolverName(objectMetadata, methodName);
|
||||
const args = getResolverArgs(methodName);
|
||||
const objectType = this.typeDefinitionsStorage.getObjectTypeByKey(
|
||||
objectMetadata.id,
|
||||
this.getObjectTypeDefinitionKindByMethodName(methodName),
|
||||
);
|
||||
const argsType = this.argsFactory.create(
|
||||
{
|
||||
args,
|
||||
objectMetadataId: objectMetadata.id,
|
||||
},
|
||||
options,
|
||||
);
|
||||
|
||||
if (!objectType) {
|
||||
this.logger.error(
|
||||
`Could not find a GraphQL type for ${objectMetadata.id} for method ${methodName}`,
|
||||
if (
|
||||
this.workspaceResolverBuilderService.shouldBuildResolver(
|
||||
objectMetadata,
|
||||
methodName,
|
||||
)
|
||||
) {
|
||||
const name = getResolverName(objectMetadata, methodName);
|
||||
const args = getResolverArgs(methodName);
|
||||
const objectType = this.typeDefinitionsStorage.getObjectTypeByKey(
|
||||
objectMetadata.id,
|
||||
this.getObjectTypeDefinitionKindByMethodName(methodName),
|
||||
);
|
||||
const argsType = this.argsFactory.create(
|
||||
{
|
||||
objectMetadata,
|
||||
methodName,
|
||||
options,
|
||||
args,
|
||||
objectMetadataId: objectMetadata.id,
|
||||
},
|
||||
options,
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
`Could not find a GraphQL type for ${objectMetadata.id} for method ${methodName}`,
|
||||
);
|
||||
if (!objectType) {
|
||||
this.logger.error(
|
||||
`Could not find a GraphQL type for ${objectMetadata.id} for method ${methodName}`,
|
||||
{
|
||||
objectMetadata,
|
||||
methodName,
|
||||
options,
|
||||
},
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
`Could not find a GraphQL type for ${objectMetadata.id} for method ${methodName}`,
|
||||
);
|
||||
}
|
||||
|
||||
const allowedMethodNames = [
|
||||
'updateMany',
|
||||
'deleteMany',
|
||||
'createMany',
|
||||
'findDuplicates',
|
||||
'restoreMany',
|
||||
'destroyMany',
|
||||
];
|
||||
|
||||
const outputType = this.typeMapperService.mapToGqlType(objectType, {
|
||||
isArray: allowedMethodNames.includes(methodName),
|
||||
});
|
||||
|
||||
fieldConfigMap[name] = {
|
||||
type: outputType,
|
||||
args: argsType,
|
||||
resolve: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const allowedMethodNames = [
|
||||
'updateMany',
|
||||
'deleteMany',
|
||||
'createMany',
|
||||
'findDuplicates',
|
||||
'restoreMany',
|
||||
'destroyMany',
|
||||
];
|
||||
|
||||
const outputType = this.typeMapperService.mapToGqlType(objectType, {
|
||||
isArray: allowedMethodNames.includes(methodName),
|
||||
});
|
||||
|
||||
fieldConfigMap[name] = {
|
||||
type: outputType,
|
||||
args: argsType,
|
||||
resolve: undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceResolverBuilderModule } from 'src/engine/api/graphql/workspace-resolver-builder/workspace-resolver-builder.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
||||
|
||||
@ -11,7 +12,11 @@ import { TypeMapperService } from './services/type-mapper.service';
|
||||
import { TypeDefinitionsStorage } from './storages/type-definitions.storage';
|
||||
|
||||
@Module({
|
||||
imports: [ObjectMetadataModule, FeatureFlagModule],
|
||||
imports: [
|
||||
ObjectMetadataModule,
|
||||
FeatureFlagModule,
|
||||
WorkspaceResolverBuilderModule,
|
||||
],
|
||||
providers: [
|
||||
TypeDefinitionsStorage,
|
||||
TypeMapperService,
|
||||
|
||||
Reference in New Issue
Block a user