morph backend CORE api (#13517)
# Morph Relation needs some work on the core API Especially on the Morph types <img width="682" height="394" alt="Screenshot 2025-07-31 at 12 19 40" src="https://github.com/user-attachments/assets/8b185ae3-3fe7-4886-97b1-ec143bb1b0f4" /> ## Focus Areas: - GraphQL schema generation - Query parsing and field selection - Morph relation field support - Input type definition preparation
This commit is contained in:
+1
@@ -37,6 +37,7 @@ export class GraphqlQuerySelectedFieldsRelationParser {
|
||||
const relationAccumulator = fieldParser.parse(
|
||||
fieldValue,
|
||||
targetObjectMetadata,
|
||||
this.objectMetadataMaps,
|
||||
);
|
||||
|
||||
accumulator.select[fieldKey] = {
|
||||
|
||||
+84
-32
@@ -1,14 +1,16 @@
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { GraphqlQuerySelectedFieldsAggregateParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-selected-fields/graphql-selected-fields-aggregate.parser';
|
||||
import { GraphqlQuerySelectedFieldsRelationParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-selected-fields/graphql-selected-fields-relation.parser';
|
||||
import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { computeMorphRelationFieldName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-relation-field-name.util';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { type CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
|
||||
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';
|
||||
import { isFieldMetadataEntityOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
|
||||
|
||||
export type GraphqlQuerySelectedFieldsResult = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -33,6 +35,7 @@ export class GraphqlQuerySelectedFieldsParser {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
graphqlSelectedFields: Partial<Record<string, any>>,
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
): GraphqlQuerySelectedFieldsResult {
|
||||
const accumulator: GraphqlQuerySelectedFieldsResult = {
|
||||
select: {},
|
||||
@@ -45,6 +48,7 @@ export class GraphqlQuerySelectedFieldsParser {
|
||||
graphqlSelectedFields,
|
||||
objectMetadataMapItem,
|
||||
accumulator,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
|
||||
return accumulator;
|
||||
@@ -56,66 +60,108 @@ export class GraphqlQuerySelectedFieldsParser {
|
||||
accumulator,
|
||||
);
|
||||
|
||||
this.parseRecordField(
|
||||
this.parseRecordFields(
|
||||
graphqlSelectedFields,
|
||||
objectMetadataMapItem,
|
||||
accumulator,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
private parseRecordField(
|
||||
private parseRecordFields(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
graphqlSelectedFields: Partial<Record<string, any>>,
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps,
|
||||
accumulator: GraphqlQuerySelectedFieldsResult,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
): void {
|
||||
for (const [fieldKey, fieldValue] of Object.entries(
|
||||
graphqlSelectedFields,
|
||||
for (const fieldMetadata of Object.values(
|
||||
objectMetadataMapItem.fieldsById,
|
||||
)) {
|
||||
const fieldMetadataBasedOnName =
|
||||
objectMetadataMapItem.fieldsById[
|
||||
objectMetadataMapItem.fieldIdByName[fieldKey]
|
||||
];
|
||||
if (
|
||||
isFieldMetadataEntityOfType(fieldMetadata, FieldMetadataType.RELATION)
|
||||
) {
|
||||
const graphqlSelectedFieldValue =
|
||||
graphqlSelectedFields[fieldMetadata.name];
|
||||
|
||||
const isFieldForeignKey =
|
||||
!fieldMetadataBasedOnName && fieldKey.endsWith('Id');
|
||||
|
||||
if (isFieldForeignKey) {
|
||||
const fieldMetadataBasedOnRelationName =
|
||||
objectMetadataMapItem.fieldsById[
|
||||
objectMetadataMapItem.fieldIdByName[fieldKey.slice(0, -2)]
|
||||
];
|
||||
|
||||
if (fieldMetadataBasedOnRelationName) {
|
||||
accumulator.select[fieldKey] = true; // field is not a connection so should not be treated as a relation
|
||||
if (!isDefined(graphqlSelectedFieldValue)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const fieldMetadata = fieldMetadataBasedOnName;
|
||||
this.graphqlQuerySelectedFieldsRelationParser.parseRelationField(
|
||||
fieldMetadata,
|
||||
fieldMetadata.name,
|
||||
graphqlSelectedFieldValue,
|
||||
accumulator,
|
||||
);
|
||||
|
||||
if (!fieldMetadata) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isRelationFieldMetadataType(fieldMetadata.type)) {
|
||||
if (
|
||||
isFieldMetadataEntityOfType(
|
||||
fieldMetadata,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
)
|
||||
) {
|
||||
const targetObjectMetadata =
|
||||
objectMetadataMaps.byId[fieldMetadata.relationTargetObjectMetadataId];
|
||||
|
||||
if (
|
||||
!fieldMetadata.settings?.relationType ||
|
||||
!isDefined(targetObjectMetadata)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const morphRelationFieldName = computeMorphRelationFieldName({
|
||||
fieldName: fieldMetadata.name,
|
||||
relationDirection: fieldMetadata.settings.relationType,
|
||||
targetObjectMetadata,
|
||||
});
|
||||
|
||||
const graphqlSelectedFieldValue =
|
||||
graphqlSelectedFields[morphRelationFieldName];
|
||||
|
||||
if (!isDefined(graphqlSelectedFieldValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.graphqlQuerySelectedFieldsRelationParser.parseRelationField(
|
||||
fieldMetadata,
|
||||
fieldKey,
|
||||
fieldValue,
|
||||
morphRelationFieldName,
|
||||
graphqlSelectedFieldValue,
|
||||
accumulator,
|
||||
);
|
||||
} else if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
const graphqlSelectedFieldValue =
|
||||
graphqlSelectedFields[fieldMetadata.name];
|
||||
|
||||
if (!isDefined(graphqlSelectedFieldValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const compositeResult = this.parseCompositeField(
|
||||
fieldMetadata,
|
||||
fieldValue,
|
||||
graphqlSelectedFieldValue,
|
||||
);
|
||||
|
||||
Object.assign(accumulator.select, compositeResult);
|
||||
} else {
|
||||
accumulator.select[fieldKey] = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const graphqlSelectedFieldValue =
|
||||
graphqlSelectedFields[fieldMetadata.name];
|
||||
|
||||
if (isDefined(graphqlSelectedFieldValue)) {
|
||||
accumulator.select[fieldMetadata.name] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,6 +171,7 @@ export class GraphqlQuerySelectedFieldsParser {
|
||||
graphqlSelectedFields: Partial<Record<string, any>>,
|
||||
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps,
|
||||
accumulator: GraphqlQuerySelectedFieldsResult,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
): void {
|
||||
this.aggregateParser.parse(
|
||||
graphqlSelectedFields,
|
||||
@@ -134,7 +181,12 @@ export class GraphqlQuerySelectedFieldsParser {
|
||||
|
||||
const node = graphqlSelectedFields.edges.node;
|
||||
|
||||
this.parseRecordField(node, objectMetadataMapItem, accumulator);
|
||||
this.parseRecordFields(
|
||||
node,
|
||||
objectMetadataMapItem,
|
||||
accumulator,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
}
|
||||
|
||||
private isRootConnection(
|
||||
|
||||
+2
@@ -119,6 +119,7 @@ export class GraphqlQueryParser {
|
||||
parentObjectMetadata: ObjectMetadataItemWithFieldMaps,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
graphqlSelectedFields: Partial<Record<string, any>>,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
): GraphqlQuerySelectedFieldsResult {
|
||||
const objectMetadataMapItem = getObjectMetadataMapItemByNameSingular(
|
||||
this.objectMetadataMaps,
|
||||
@@ -139,6 +140,7 @@ export class GraphqlQueryParser {
|
||||
return selectedFieldsParser.parse(
|
||||
graphqlSelectedFields,
|
||||
objectMetadataMapItem,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+120
-24
@@ -17,11 +17,13 @@ import { getTargetObjectMetadataOrThrow } from 'src/engine/api/graphql/graphql-q
|
||||
import { type AggregationField } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-available-aggregations-from-object-fields.util';
|
||||
import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { computeMorphRelationFieldName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-relation-field-name.util';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { isFieldMetadataTypeMorphRelation } from 'src/engine/metadata-modules/field-metadata/utils/is-field-metadata-type-morph-relation.util';
|
||||
import { isFieldMetadataTypeRelation } from 'src/engine/metadata-modules/field-metadata/utils/is-field-metadata-type-relation.util';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||
import { type CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
|
||||
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';
|
||||
import { isPlainObject } from 'src/utils/is-plain-object';
|
||||
|
||||
export class ObjectRecordsToGraphqlConnectionHelper {
|
||||
@@ -167,24 +169,42 @@ export class ObjectRecordsToGraphqlConnectionHelper {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const processedObjectRecord: Record<string, any> = {};
|
||||
|
||||
for (const [key, value] of Object.entries(objectRecord)) {
|
||||
const fieldMetadataId = objectMetadata.fieldIdByName[key];
|
||||
const fieldMetadata = objectMetadata.fieldsById[fieldMetadataId];
|
||||
for (const fieldMetadata of Object.values(objectMetadata.fieldsById)) {
|
||||
if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
const objectValue = objectRecord[fieldMetadata.name];
|
||||
|
||||
if (!fieldMetadata) {
|
||||
processedObjectRecord[key] = value;
|
||||
if (!isDefined(objectValue)) {
|
||||
continue;
|
||||
}
|
||||
processedObjectRecord[fieldMetadata.name] = this.processCompositeField(
|
||||
fieldMetadata,
|
||||
objectValue,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isRelationFieldMetadataType(fieldMetadata.type)) {
|
||||
if (Array.isArray(value)) {
|
||||
if (isFieldMetadataTypeRelation(fieldMetadata)) {
|
||||
const fieldMetadataNameWithId = `${fieldMetadata.name}Id`;
|
||||
|
||||
if (isDefined(objectRecord[fieldMetadataNameWithId])) {
|
||||
processedObjectRecord[fieldMetadataNameWithId] =
|
||||
objectRecord[fieldMetadataNameWithId];
|
||||
}
|
||||
|
||||
const objectValue = objectRecord[fieldMetadata.name];
|
||||
|
||||
if (!isDefined(objectValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Array.isArray(objectValue)) {
|
||||
const targetObjectMetadata = getTargetObjectMetadataOrThrow(
|
||||
fieldMetadata,
|
||||
this.objectMetadataMaps,
|
||||
);
|
||||
|
||||
processedObjectRecord[key] = this.createConnection({
|
||||
objectRecords: value,
|
||||
processedObjectRecord[fieldMetadata.name] = this.createConnection({
|
||||
objectRecords: objectValue,
|
||||
parentObjectRecord: objectRecord,
|
||||
objectRecordsAggregatedValues:
|
||||
objectRecordsAggregatedValues[fieldMetadata.name],
|
||||
@@ -194,20 +214,20 @@ export class ObjectRecordsToGraphqlConnectionHelper {
|
||||
take,
|
||||
totalCount:
|
||||
objectRecordsAggregatedValues[fieldMetadata.name]?.totalCount ??
|
||||
value.length,
|
||||
objectValue.length,
|
||||
order,
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
depth: depth + 1,
|
||||
});
|
||||
} else if (isPlainObject(value)) {
|
||||
} else if (isPlainObject(objectValue)) {
|
||||
const targetObjectMetadata = getTargetObjectMetadataOrThrow(
|
||||
fieldMetadata,
|
||||
this.objectMetadataMaps,
|
||||
);
|
||||
|
||||
processedObjectRecord[key] = this.processRecord({
|
||||
objectRecord: value,
|
||||
processedObjectRecord[fieldMetadata.name] = this.processRecord({
|
||||
objectRecord: objectValue,
|
||||
objectRecordsAggregatedValues:
|
||||
objectRecordsAggregatedValues[fieldMetadata.name],
|
||||
selectedAggregatedFields:
|
||||
@@ -219,17 +239,93 @@ export class ObjectRecordsToGraphqlConnectionHelper {
|
||||
depth: depth + 1,
|
||||
});
|
||||
}
|
||||
} else if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||
processedObjectRecord[key] = this.processCompositeField(
|
||||
fieldMetadata,
|
||||
value,
|
||||
);
|
||||
} else {
|
||||
processedObjectRecord[key] = this.formatFieldValue(
|
||||
value,
|
||||
fieldMetadata.type,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isFieldMetadataTypeMorphRelation(fieldMetadata)) {
|
||||
const targetObjectMetadata =
|
||||
this.objectMetadataMaps.byId[
|
||||
fieldMetadata.relationTargetObjectMetadataId
|
||||
];
|
||||
|
||||
if (
|
||||
!fieldMetadata.settings?.relationType ||
|
||||
!isDefined(targetObjectMetadata)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const morphRelationFieldName = computeMorphRelationFieldName({
|
||||
fieldName: fieldMetadata.name,
|
||||
relationDirection: fieldMetadata.settings.relationType,
|
||||
targetObjectMetadata,
|
||||
});
|
||||
|
||||
const fieldMetadataNameWithId = `${fieldMetadata.name}Id`;
|
||||
|
||||
if (isDefined(objectRecord[fieldMetadataNameWithId])) {
|
||||
processedObjectRecord[fieldMetadataNameWithId] =
|
||||
objectRecord[fieldMetadataNameWithId];
|
||||
}
|
||||
|
||||
const objectValue = objectRecord[morphRelationFieldName];
|
||||
|
||||
if (!isDefined(objectValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Array.isArray(objectValue)) {
|
||||
processedObjectRecord[morphRelationFieldName] = this.createConnection(
|
||||
{
|
||||
objectRecords: objectValue,
|
||||
parentObjectRecord: objectRecord,
|
||||
objectRecordsAggregatedValues:
|
||||
objectRecordsAggregatedValues[fieldMetadata.name],
|
||||
selectedAggregatedFields:
|
||||
selectedAggregatedFields[fieldMetadata.name],
|
||||
objectName: targetObjectMetadata.nameSingular,
|
||||
take,
|
||||
totalCount:
|
||||
objectRecordsAggregatedValues[fieldMetadata.name]?.totalCount ??
|
||||
objectValue.length,
|
||||
order,
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
depth: depth + 1,
|
||||
},
|
||||
);
|
||||
} else if (isPlainObject(objectValue)) {
|
||||
const targetObjectMetadata = getTargetObjectMetadataOrThrow(
|
||||
fieldMetadata,
|
||||
this.objectMetadataMaps,
|
||||
);
|
||||
|
||||
processedObjectRecord[morphRelationFieldName] = this.processRecord({
|
||||
objectRecord: objectValue,
|
||||
objectRecordsAggregatedValues:
|
||||
objectRecordsAggregatedValues[fieldMetadata.name],
|
||||
selectedAggregatedFields:
|
||||
selectedAggregatedFields[fieldMetadata.name],
|
||||
objectName: targetObjectMetadata.nameSingular,
|
||||
take,
|
||||
totalCount,
|
||||
order,
|
||||
depth: depth + 1,
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const objectValue = objectRecord[fieldMetadata.name];
|
||||
|
||||
if (!isDefined(objectValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
processedObjectRecord[fieldMetadata.name] = this.formatFieldValue(
|
||||
objectValue,
|
||||
fieldMetadata.type,
|
||||
);
|
||||
}
|
||||
|
||||
return processedObjectRecord as T;
|
||||
|
||||
+18
-9
@@ -12,6 +12,7 @@ import {
|
||||
} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception';
|
||||
import { ProcessAggregateHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-aggregate.helper';
|
||||
import { buildColumnsToSelect } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-select';
|
||||
import { getFieldMetadataFromGraphQLField } from 'src/engine/api/graphql/graphql-query-runner/utils/get-field-metadata-from-graphql-field.util';
|
||||
import { getTargetObjectMetadataOrThrow } from 'src/engine/api/graphql/graphql-query-runner/utils/get-target-object-metadata.util';
|
||||
import { type AggregationField } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-available-aggregations-from-object-fields.util';
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
@@ -110,15 +111,20 @@ export class ProcessNestedRelationsV2Helper {
|
||||
roleId?: string;
|
||||
selectedFields: Record<string, unknown>;
|
||||
}): Promise<void> {
|
||||
const sourceFieldMetadataId =
|
||||
parentObjectMetadataItem.fieldIdByName[sourceFieldName];
|
||||
const sourceFieldMetadata =
|
||||
parentObjectMetadataItem.fieldsById[sourceFieldMetadataId];
|
||||
const sourceFieldMetadata = getFieldMetadataFromGraphQLField({
|
||||
objectMetadataItem: parentObjectMetadataItem,
|
||||
graphQLField: sourceFieldName,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
if (
|
||||
!isFieldMetadataEntityOfType(
|
||||
sourceFieldMetadata,
|
||||
FieldMetadataType.RELATION,
|
||||
) &&
|
||||
!isFieldMetadataEntityOfType(
|
||||
sourceFieldMetadata,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
)
|
||||
) {
|
||||
// TODO: Maybe we should throw an error here ?
|
||||
@@ -154,6 +160,7 @@ export class ProcessNestedRelationsV2Helper {
|
||||
select: selectedFields,
|
||||
relations: nestedRelations,
|
||||
objectMetadataItemWithFieldMaps: targetObjectMetadata,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
targetObjectQueryBuilder = targetObjectQueryBuilder.setFindOptions({
|
||||
@@ -165,7 +172,8 @@ export class ProcessNestedRelationsV2Helper {
|
||||
idField:
|
||||
relationType === RelationType.ONE_TO_MANY
|
||||
? 'id'
|
||||
: `${sourceFieldName}Id`,
|
||||
: (sourceFieldMetadata.settings.joinColumnName ??
|
||||
`${sourceFieldName}Id`),
|
||||
});
|
||||
|
||||
const fieldMetadataTargetRelationColumnName =
|
||||
@@ -246,10 +254,11 @@ export class ProcessNestedRelationsV2Helper {
|
||||
parentObjectMetadataItem: ObjectMetadataItemWithFieldMaps;
|
||||
sourceFieldName: string;
|
||||
}) {
|
||||
const targetFieldMetadataId =
|
||||
parentObjectMetadataItem.fieldIdByName[sourceFieldName];
|
||||
const targetFieldMetadata =
|
||||
parentObjectMetadataItem.fieldsById[targetFieldMetadataId];
|
||||
const targetFieldMetadata = getFieldMetadataFromGraphQLField({
|
||||
objectMetadataItem: parentObjectMetadataItem,
|
||||
graphQLField: sourceFieldName,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
const targetObjectMetadata = getTargetObjectMetadataOrThrow(
|
||||
targetFieldMetadata,
|
||||
|
||||
+1
@@ -174,6 +174,7 @@ export abstract class GraphqlQueryBaseResolverService<
|
||||
graphqlQueryParser.parseSelectedFields(
|
||||
objectMetadataItemWithFieldMaps,
|
||||
selectedFields,
|
||||
options.objectMetadataMaps,
|
||||
);
|
||||
|
||||
const graphqlQueryResolverExecutionArgs = {
|
||||
|
||||
+10
-3
@@ -3,8 +3,8 @@ import { Injectable } from '@nestjs/common';
|
||||
import { QUERY_MAX_RECORDS } from 'twenty-shared/constants';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
type FindOperator,
|
||||
In,
|
||||
type FindOperator,
|
||||
type InsertResult,
|
||||
type ObjectLiteral,
|
||||
} from 'typeorm';
|
||||
@@ -52,6 +52,7 @@ export class GraphqlQueryCreateManyResolverService extends GraphqlQueryBaseResol
|
||||
executionArgs,
|
||||
objectRecords,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
|
||||
await this.processNestedRelationsIfNeeded({
|
||||
@@ -73,12 +74,14 @@ export class GraphqlQueryCreateManyResolverService extends GraphqlQueryBaseResol
|
||||
executionArgs: GraphqlQueryResolverExecutionArgs<CreateManyResolverArgs>,
|
||||
): Promise<InsertResult> {
|
||||
if (!executionArgs.args.upsert) {
|
||||
const { objectMetadataItemWithFieldMaps } = executionArgs.options;
|
||||
const { objectMetadataItemWithFieldMaps, objectMetadataMaps } =
|
||||
executionArgs.options;
|
||||
|
||||
const selectedColumns = buildColumnsToReturn({
|
||||
select: executionArgs.graphqlQuerySelectedFieldsResult.select,
|
||||
relations: executionArgs.graphqlQuerySelectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
return await executionArgs.repository.insert(
|
||||
@@ -94,7 +97,8 @@ export class GraphqlQueryCreateManyResolverService extends GraphqlQueryBaseResol
|
||||
private async performUpsertOperation(
|
||||
executionArgs: GraphqlQueryResolverExecutionArgs<CreateManyResolverArgs>,
|
||||
): Promise<InsertResult> {
|
||||
const { objectMetadataItemWithFieldMaps } = executionArgs.options;
|
||||
const { objectMetadataItemWithFieldMaps, objectMetadataMaps } =
|
||||
executionArgs.options;
|
||||
|
||||
const conflictingFields = this.getConflictingFields(
|
||||
objectMetadataItemWithFieldMaps,
|
||||
@@ -120,6 +124,7 @@ export class GraphqlQueryCreateManyResolverService extends GraphqlQueryBaseResol
|
||||
select: executionArgs.graphqlQuerySelectedFieldsResult.select,
|
||||
relations: executionArgs.graphqlQuerySelectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
if (recordsToUpdate.length > 0) {
|
||||
@@ -375,6 +380,7 @@ export class GraphqlQueryCreateManyResolverService extends GraphqlQueryBaseResol
|
||||
executionArgs: GraphqlQueryResolverExecutionArgs<CreateManyResolverArgs>,
|
||||
objectRecords: InsertResult,
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
): Promise<ObjectRecord[]> {
|
||||
const queryBuilder = executionArgs.repository.createQueryBuilder(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
@@ -384,6 +390,7 @@ export class GraphqlQueryCreateManyResolverService extends GraphqlQueryBaseResol
|
||||
select: executionArgs.graphqlQuerySelectedFieldsResult.select,
|
||||
relations: executionArgs.graphqlQuerySelectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
const upsertedRecords = await queryBuilder
|
||||
|
||||
+2
@@ -34,6 +34,7 @@ export class GraphqlQueryCreateOneResolverService extends GraphqlQueryBaseResolv
|
||||
select: executionArgs.graphqlQuerySelectedFieldsResult.select,
|
||||
relations: executionArgs.graphqlQuerySelectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
const objectRecords: InsertResult = !executionArgs.args.upsert
|
||||
@@ -60,6 +61,7 @@ export class GraphqlQueryCreateOneResolverService extends GraphqlQueryBaseResolv
|
||||
select: executionArgs.graphqlQuerySelectedFieldsResult.select,
|
||||
relations: executionArgs.graphqlQuerySelectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
const upsertedRecords = (await queryBuilder
|
||||
|
||||
+1
@@ -48,6 +48,7 @@ export class GraphqlQueryDeleteManyResolverService extends GraphqlQueryBaseResol
|
||||
select: executionArgs.graphqlQuerySelectedFieldsResult.select,
|
||||
relations: executionArgs.graphqlQuerySelectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
const deletedObjectRecords = await queryBuilder
|
||||
|
||||
+1
@@ -40,6 +40,7 @@ export class GraphqlQueryDeleteOneResolverService extends GraphqlQueryBaseResolv
|
||||
select: executionArgs.graphqlQuerySelectedFieldsResult.select,
|
||||
relations: executionArgs.graphqlQuerySelectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
const deletedObjectRecords = await queryBuilder
|
||||
|
||||
+1
@@ -46,6 +46,7 @@ export class GraphqlQueryDestroyManyResolverService extends GraphqlQueryBaseReso
|
||||
select: executionArgs.graphqlQuerySelectedFieldsResult.select,
|
||||
relations: executionArgs.graphqlQuerySelectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
const deletedObjectRecords = await queryBuilder
|
||||
|
||||
+1
@@ -38,6 +38,7 @@ export class GraphqlQueryDestroyOneResolverService extends GraphqlQueryBaseResol
|
||||
select: executionArgs.graphqlQuerySelectedFieldsResult.select,
|
||||
relations: executionArgs.graphqlQuerySelectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
const deletedObjectRecords = await queryBuilder
|
||||
|
||||
+1
@@ -68,6 +68,7 @@ export class GraphqlQueryFindDuplicatesResolverService extends GraphqlQueryBaseR
|
||||
select: executionArgs.graphqlQuerySelectedFieldsResult.select,
|
||||
relations: executionArgs.graphqlQuerySelectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
if (executionArgs.args.ids) {
|
||||
|
||||
+1
@@ -122,6 +122,7 @@ export class GraphqlQueryFindManyResolverService extends GraphqlQueryBaseResolve
|
||||
select: executionArgs.graphqlQuerySelectedFieldsResult.select,
|
||||
relations: executionArgs.graphqlQuerySelectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
const objectRecords = (await queryBuilder
|
||||
|
||||
+1
@@ -56,6 +56,7 @@ export class GraphqlQueryFindOneResolverService extends GraphqlQueryBaseResolver
|
||||
select: executionArgs.graphqlQuerySelectedFieldsResult.select,
|
||||
relations: executionArgs.graphqlQuerySelectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
const objectRecord = await queryBuilder
|
||||
|
||||
+3
-1
@@ -247,7 +247,8 @@ export class GraphqlQueryMergeManyResolverService extends GraphqlQueryBaseResolv
|
||||
priorityRecordId: string,
|
||||
mergedData: Partial<ObjectRecord>,
|
||||
): Promise<ObjectRecord> {
|
||||
const { objectMetadataItemWithFieldMaps } = executionArgs.options;
|
||||
const { objectMetadataItemWithFieldMaps, objectMetadataMaps } =
|
||||
executionArgs.options;
|
||||
|
||||
const queryBuilder = executionArgs.repository.createQueryBuilder(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
@@ -257,6 +258,7 @@ export class GraphqlQueryMergeManyResolverService extends GraphqlQueryBaseResolv
|
||||
select: executionArgs.graphqlQuerySelectedFieldsResult.select,
|
||||
relations: executionArgs.graphqlQuerySelectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
const updatedObjectRecords = await queryBuilder
|
||||
|
||||
+1
@@ -48,6 +48,7 @@ export class GraphqlQueryRestoreManyResolverService extends GraphqlQueryBaseReso
|
||||
select: executionArgs.graphqlQuerySelectedFieldsResult.select,
|
||||
relations: executionArgs.graphqlQuerySelectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
const restoredObjectRecords = await queryBuilder
|
||||
|
||||
+1
@@ -40,6 +40,7 @@ export class GraphqlQueryRestoreOneResolverService extends GraphqlQueryBaseResol
|
||||
select: executionArgs.graphqlQuerySelectedFieldsResult.select,
|
||||
relations: executionArgs.graphqlQuerySelectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
const restoredObjectRecords = await queryBuilder
|
||||
|
||||
+1
@@ -48,6 +48,7 @@ export class GraphqlQueryUpdateManyResolverService extends GraphqlQueryBaseResol
|
||||
select: executionArgs.graphqlQuerySelectedFieldsResult.select,
|
||||
relations: executionArgs.graphqlQuerySelectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
const updatedObjectRecords = await queryBuilder
|
||||
|
||||
+1
@@ -40,6 +40,7 @@ export class GraphqlQueryUpdateOneResolverService extends GraphqlQueryBaseResolv
|
||||
select: executionArgs.graphqlQuerySelectedFieldsResult.select,
|
||||
relations: executionArgs.graphqlQuerySelectedFieldsResult.relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
});
|
||||
|
||||
const updatedObjectRecords = await queryBuilder
|
||||
|
||||
+16
@@ -101,6 +101,16 @@ describe('buildColumnsToSelect', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const mockObjectMetadataMaps = {
|
||||
byId: {
|
||||
'9af20778-2f2c-4e22-ae83-2e77e479b57c': {
|
||||
id: '9af20778-2f2c-4e22-ae83-2e77e479b57c',
|
||||
nameSingular: 'company',
|
||||
namePlural: 'companies',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
it('should build columns to select with relation fields', () => {
|
||||
const select = {
|
||||
nameFirstName: true,
|
||||
@@ -118,6 +128,7 @@ describe('buildColumnsToSelect', () => {
|
||||
select,
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps: mockObjectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps: mockObjectMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@@ -139,6 +150,7 @@ describe('buildColumnsToSelect', () => {
|
||||
select,
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps: mockObjectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps: mockObjectMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@@ -161,6 +173,7 @@ describe('buildColumnsToSelect', () => {
|
||||
select,
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps: mockObjectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps: mockObjectMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@@ -182,6 +195,7 @@ describe('buildColumnsToSelect', () => {
|
||||
select,
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps: mockObjectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps: mockObjectMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@@ -219,6 +233,7 @@ describe('buildColumnsToSelect', () => {
|
||||
select,
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps: mockObjectMetadataWithOneToMany,
|
||||
objectMetadataMaps: mockObjectMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
@@ -256,6 +271,7 @@ describe('buildColumnsToSelect', () => {
|
||||
select,
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps: mockObjectMetadataWithoutJoinColumn,
|
||||
objectMetadataMaps: mockObjectMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
|
||||
+4
@@ -1,20 +1,24 @@
|
||||
import { buildColumnsToSelect } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-select';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
export const buildColumnsToReturn = ({
|
||||
select,
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
}: {
|
||||
select: Record<string, unknown>;
|
||||
relations: Record<string, unknown>;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
}): string[] => {
|
||||
return Object.entries(
|
||||
buildColumnsToSelect({
|
||||
select,
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
}),
|
||||
)
|
||||
.filter(([_columnName, value]) => value === true)
|
||||
|
||||
+58
-25
@@ -1,23 +1,34 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { InternalServerError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { computeMorphRelationFieldName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-relation-field-name.util';
|
||||
import { isFieldMetadataTypeMorphRelation } from 'src/engine/metadata-modules/field-metadata/utils/is-field-metadata-type-morph-relation.util';
|
||||
import { isFieldMetadataTypeRelation } from 'src/engine/metadata-modules/field-metadata/utils/is-field-metadata-type-relation.util';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { isFieldMetadataEntityOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
|
||||
|
||||
export const buildColumnsToSelect = ({
|
||||
select,
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
}: {
|
||||
select: Record<string, unknown>;
|
||||
relations: Record<string, unknown>;
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
|
||||
objectMetadataMaps: {
|
||||
byId: Partial<
|
||||
Record<
|
||||
string,
|
||||
Pick<ObjectMetadataItemWithFieldMaps, 'nameSingular' | 'namePlural'>
|
||||
>
|
||||
>;
|
||||
};
|
||||
}) => {
|
||||
const requiredRelationColumns = getRequiredRelationColumns(
|
||||
relations,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
|
||||
const fieldsToSelect: Record<string, boolean> = Object.entries(select)
|
||||
@@ -35,37 +46,59 @@ export const buildColumnsToSelect = ({
|
||||
|
||||
const getRequiredRelationColumns = (
|
||||
relations: Record<string, unknown>,
|
||||
objectMetadataItem: ObjectMetadataItemWithFieldMaps,
|
||||
objectMetadataItem: Pick<ObjectMetadataItemWithFieldMaps, 'fieldsById'>,
|
||||
objectMetadataMaps: {
|
||||
byId: Partial<
|
||||
Record<
|
||||
string,
|
||||
Pick<ObjectMetadataItemWithFieldMaps, 'nameSingular' | 'namePlural'>
|
||||
>
|
||||
>;
|
||||
},
|
||||
): string[] => {
|
||||
const requiredColumns: string[] = [];
|
||||
|
||||
for (const [relationFieldName, _] of Object.entries(relations)) {
|
||||
const fieldMetadataId = objectMetadataItem.fieldIdByName[relationFieldName];
|
||||
for (const fieldMetadata of Object.values(objectMetadataItem.fieldsById)) {
|
||||
if (isFieldMetadataTypeRelation(fieldMetadata)) {
|
||||
const relationValue = relations[fieldMetadata.name];
|
||||
|
||||
if (!fieldMetadataId) {
|
||||
throw new InternalServerError(
|
||||
`Field metadata not found for relation field name: ${relationFieldName}`,
|
||||
);
|
||||
if (
|
||||
!isDefined(relationValue) ||
|
||||
!isDefined(fieldMetadata?.settings?.joinColumnName) ||
|
||||
fieldMetadata.settings?.relationType !== RelationType.MANY_TO_ONE
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
requiredColumns.push(fieldMetadata.settings.joinColumnName);
|
||||
}
|
||||
|
||||
const fieldMetadata = objectMetadataItem.fieldsById[fieldMetadataId];
|
||||
if (isFieldMetadataTypeMorphRelation(fieldMetadata)) {
|
||||
const targetObjectMetadata =
|
||||
objectMetadataMaps.byId[fieldMetadata.relationTargetObjectMetadataId];
|
||||
|
||||
if (!fieldMetadata) {
|
||||
throw new InternalServerError(
|
||||
`Field metadata not found for relation field name: ${relationFieldName}`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
!fieldMetadata.settings?.relationType ||
|
||||
!isDefined(targetObjectMetadata)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
!isFieldMetadataEntityOfType(fieldMetadata, FieldMetadataType.RELATION)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const morphRelationFieldName = computeMorphRelationFieldName({
|
||||
fieldName: fieldMetadata.name,
|
||||
relationDirection: fieldMetadata.settings.relationType,
|
||||
targetObjectMetadata,
|
||||
});
|
||||
|
||||
const relationValue = relations[morphRelationFieldName];
|
||||
|
||||
if (
|
||||
!isDefined(relationValue) ||
|
||||
!isDefined(fieldMetadata?.settings?.joinColumnName)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
fieldMetadata.settings?.relationType === RelationType.MANY_TO_ONE &&
|
||||
fieldMetadata.settings?.joinColumnName
|
||||
) {
|
||||
requiredColumns.push(fieldMetadata.settings.joinColumnName);
|
||||
}
|
||||
}
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { getTargetObjectMetadataOrThrow } from 'src/engine/api/graphql/graphql-query-runner/utils/get-target-object-metadata.util';
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { computeMorphRelationFieldName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-relation-field-name.util';
|
||||
import { isFieldMetadataTypeMorphRelation } from 'src/engine/metadata-modules/field-metadata/utils/is-field-metadata-type-morph-relation.util';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
|
||||
type GetFieldMetadataFromGraphQLFieldArgs = {
|
||||
objectMetadataItem: ObjectMetadataItemWithFieldMaps;
|
||||
graphQLField: string;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
};
|
||||
|
||||
export function getFieldMetadataFromGraphQLField({
|
||||
objectMetadataItem,
|
||||
graphQLField,
|
||||
objectMetadataMaps,
|
||||
}: GetFieldMetadataFromGraphQLFieldArgs) {
|
||||
const sourceFieldMetadataId = objectMetadataItem.fieldIdByName[graphQLField];
|
||||
let sourceFieldMetadata =
|
||||
objectMetadataItem.fieldsById[sourceFieldMetadataId];
|
||||
|
||||
// If empty, it could be a morph relation
|
||||
if (!isDefined(sourceFieldMetadata)) {
|
||||
const morphRelationsWithTargetObjectMetadata = Object.values(
|
||||
objectMetadataItem.fieldsById,
|
||||
)
|
||||
.filter(
|
||||
(fieldMetadata) =>
|
||||
fieldMetadata.type === FieldMetadataType.MORPH_RELATION,
|
||||
)
|
||||
.map((fieldMetadata) => {
|
||||
const targetObjectMetadata = getTargetObjectMetadataOrThrow(
|
||||
fieldMetadata,
|
||||
objectMetadataMaps,
|
||||
);
|
||||
|
||||
return {
|
||||
fieldMetadata,
|
||||
targetObjectMetadata,
|
||||
};
|
||||
});
|
||||
|
||||
const possibleGraphQLFieldNames: {
|
||||
graphQLField: string;
|
||||
fieldMetadata: FieldMetadataEntity;
|
||||
targetObjectMetadata: ObjectMetadataItemWithFieldMaps;
|
||||
}[] = [];
|
||||
|
||||
morphRelationsWithTargetObjectMetadata.map((morphRelation) => {
|
||||
if (
|
||||
!isFieldMetadataTypeMorphRelation(morphRelation.fieldMetadata) ||
|
||||
!morphRelation.fieldMetadata.settings?.relationType
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const name = computeMorphRelationFieldName({
|
||||
fieldName: morphRelation.fieldMetadata.name,
|
||||
relationDirection: morphRelation.fieldMetadata.settings.relationType,
|
||||
targetObjectMetadata: morphRelation.targetObjectMetadata,
|
||||
});
|
||||
|
||||
possibleGraphQLFieldNames.push({
|
||||
graphQLField: name,
|
||||
fieldMetadata: morphRelation.fieldMetadata,
|
||||
targetObjectMetadata: morphRelation.targetObjectMetadata,
|
||||
});
|
||||
});
|
||||
|
||||
const fieldMetdata = possibleGraphQLFieldNames.find(
|
||||
(possibleGraphQLFieldName) =>
|
||||
possibleGraphQLFieldName.graphQLField === graphQLField,
|
||||
)?.fieldMetadata;
|
||||
|
||||
if (fieldMetdata) {
|
||||
sourceFieldMetadata = fieldMetdata;
|
||||
}
|
||||
}
|
||||
|
||||
return sourceFieldMetadata;
|
||||
}
|
||||
Reference in New Issue
Block a user