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:
+30
-3
@@ -1,9 +1,9 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import {
|
||||
GraphQLObjectType,
|
||||
type GraphQLFieldConfigArgumentMap,
|
||||
type GraphQLFieldConfigMap,
|
||||
GraphQLObjectType,
|
||||
} from 'graphql';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
@@ -14,6 +14,7 @@ import { RelationTypeV2Factory } from 'src/engine/api/graphql/workspace-schema-b
|
||||
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';
|
||||
import { objectContainsRelationField } from 'src/engine/api/graphql/workspace-schema-builder/utils/object-contains-relation-field';
|
||||
import { computeMorphRelationFieldName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-relation-field-name.util';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { isFieldMetadataEntityOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
|
||||
|
||||
@@ -46,6 +47,7 @@ export class ExtendObjectTypeDefinitionV2Factory {
|
||||
public create(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
options: WorkspaceBuildSchemaOptions,
|
||||
objectMetadataCollection: ObjectMetadataEntity[],
|
||||
): ObjectTypeDefinition {
|
||||
const kind = ObjectTypeDefinitionKind.Plain;
|
||||
const gqlType = this.typeDefinitionsStorage.getObjectTypeByKey(
|
||||
@@ -94,7 +96,11 @@ export class ExtendObjectTypeDefinitionV2Factory {
|
||||
...config,
|
||||
fields: () => ({
|
||||
...config.fields,
|
||||
...this.generateFields(objectMetadata, options),
|
||||
...this.generateFields(
|
||||
objectMetadata,
|
||||
options,
|
||||
objectMetadataCollection,
|
||||
),
|
||||
}),
|
||||
}),
|
||||
};
|
||||
@@ -103,6 +109,7 @@ export class ExtendObjectTypeDefinitionV2Factory {
|
||||
private generateFields(
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
options: WorkspaceBuildSchemaOptions,
|
||||
objectMetadataCollection: ObjectMetadataEntity[],
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
): GraphQLFieldConfigMap<any, any> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -151,7 +158,27 @@ export class ExtendObjectTypeDefinitionV2Factory {
|
||||
);
|
||||
}
|
||||
|
||||
fields[fieldMetadata.name] = {
|
||||
const objectMetadataTarget = objectMetadataCollection.find(
|
||||
(objectMetadata) =>
|
||||
objectMetadata.id === fieldMetadata.relationTargetObjectMetadataId,
|
||||
);
|
||||
|
||||
if (!objectMetadataTarget) {
|
||||
throw new Error(
|
||||
`Object Metadata Target not found for Id: ${fieldMetadata.relationTargetObjectMetadataId} on fieldMetadata name: ${fieldMetadata.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
const fieldName =
|
||||
fieldMetadata.type === FieldMetadataType.MORPH_RELATION
|
||||
? computeMorphRelationFieldName({
|
||||
fieldName: fieldMetadata.name,
|
||||
relationDirection: fieldMetadata.settings.relationType,
|
||||
targetObjectMetadata: objectMetadataTarget,
|
||||
})
|
||||
: fieldMetadata.name;
|
||||
|
||||
fields[fieldName] = {
|
||||
type: relationType,
|
||||
args: argsType,
|
||||
description: fieldMetadata.description,
|
||||
|
||||
+7
-1
@@ -8,6 +8,7 @@ import { CompositeObjectTypeDefinitionFactory } from 'src/engine/api/graphql/wor
|
||||
import { EnumTypeDefinitionFactory } from 'src/engine/api/graphql/workspace-schema-builder/factories/enum-type-definition.factory';
|
||||
import { ExtendObjectTypeDefinitionV2Factory } from 'src/engine/api/graphql/workspace-schema-builder/factories/extend-object-type-definition-v2.factory';
|
||||
import { RelationConnectInputTypeDefinitionFactory } from 'src/engine/api/graphql/workspace-schema-builder/factories/relation-connect-input-type-definition.factory';
|
||||
import { objectContainsMorphRelationField } from 'src/engine/api/graphql/workspace-schema-builder/utils/object-contains-morph-relation-field.util';
|
||||
import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
|
||||
@@ -269,7 +270,11 @@ export class TypeDefinitionsGenerator {
|
||||
) {
|
||||
// Generate extended object type defs only for objects that contain composite fields
|
||||
const objectMetadataCollectionWithCompositeFields =
|
||||
objectMetadataCollection.filter(objectContainsRelationField);
|
||||
objectMetadataCollection.filter(
|
||||
(obj) =>
|
||||
objectContainsRelationField(obj) ||
|
||||
objectContainsMorphRelationField(obj),
|
||||
);
|
||||
const workspaceId =
|
||||
objectMetadataCollectionWithCompositeFields[0]?.workspaceId;
|
||||
|
||||
@@ -282,6 +287,7 @@ export class TypeDefinitionsGenerator {
|
||||
this.extendObjectTypeDefinitionV2Factory.create(
|
||||
objectMetadata,
|
||||
options,
|
||||
objectMetadataCollection,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
+12
-11
@@ -167,10 +167,10 @@ const generateRelationField = <
|
||||
options: WorkspaceBuildSchemaOptions;
|
||||
typeFactory: TypeFactory<T>;
|
||||
}) => {
|
||||
const relationField = {};
|
||||
const relationFields = {};
|
||||
|
||||
if (fieldMetadata.settings?.relationType === RelationType.ONE_TO_MANY) {
|
||||
return relationField;
|
||||
return relationFields;
|
||||
}
|
||||
|
||||
const { joinColumnName, fieldMetadataName } =
|
||||
@@ -188,14 +188,14 @@ const generateRelationField = <
|
||||
);
|
||||
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
relationField[joinColumnName] = {
|
||||
relationFields[joinColumnName] = {
|
||||
type,
|
||||
description: fieldMetadata.description,
|
||||
};
|
||||
|
||||
//TODO : temporary - continue ej/1278 branch (https://github.com/twentyhq/core-team-issues/issues/1278 issue) before removing this
|
||||
if (fieldMetadata.type === FieldMetadataType.MORPH_RELATION)
|
||||
return relationField;
|
||||
return relationFields;
|
||||
|
||||
if (
|
||||
[InputTypeDefinitionKind.Create, InputTypeDefinitionKind.Update].includes(
|
||||
@@ -215,15 +215,16 @@ const generateRelationField = <
|
||||
isRelationConnectField: true,
|
||||
},
|
||||
);
|
||||
|
||||
// todo @guillim
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
relationFields[fieldMetadataName] = {
|
||||
type: type,
|
||||
description: fieldMetadata.description,
|
||||
};
|
||||
}
|
||||
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
relationField[fieldMetadataName] = {
|
||||
type: type,
|
||||
description: fieldMetadata.description,
|
||||
};
|
||||
|
||||
return relationField;
|
||||
return relationFields;
|
||||
};
|
||||
|
||||
// Type guard
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { isMorphRelationFieldMetadataType } from 'src/engine/utils/is-morph-relation-field-metadata-type.util';
|
||||
|
||||
export const objectContainsMorphRelationField = (
|
||||
objectMetadata: ObjectMetadataEntity,
|
||||
): boolean => {
|
||||
return objectMetadata.fields.some((field) =>
|
||||
isMorphRelationFieldMetadataType(field.type),
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user