Connect - Update Gql schema generation (#13001)
To test :
```
mutation testMutation {
createPerson(
data: {company: {connect: {where: {domainName: {primaryLinkUrl: "airbnb.com"}}}}}
) {
id
}
}
```
closes https://github.com/twentyhq/core-team-issues/issues/1167
This commit is contained in:
+169
-63
@@ -5,13 +5,16 @@ import {
|
||||
GraphQLOutputType,
|
||||
} from 'graphql';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { InputTypeDefinitionKind } from 'src/engine/api/graphql/workspace-schema-builder/factories/input-type-definition.factory';
|
||||
import { ObjectTypeDefinitionKind } from 'src/engine/api/graphql/workspace-schema-builder/factories/object-type-definition.factory';
|
||||
import { formatRelationConnectInputTarget } from 'src/engine/api/graphql/workspace-schema-builder/factories/relation-connect-input-type-definition.factory';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { isFieldMetadataInterfaceOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
|
||||
|
||||
@@ -30,6 +33,7 @@ type TypeFactory<T extends InputTypeDefinitionKind | ObjectTypeDefinitionKind> =
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
settings: any;
|
||||
isIdField: boolean;
|
||||
isRelationConnectField?: boolean;
|
||||
},
|
||||
) => T extends InputTypeDefinitionKind
|
||||
? GraphQLInputType
|
||||
@@ -38,87 +42,189 @@ type TypeFactory<T extends InputTypeDefinitionKind | ObjectTypeDefinitionKind> =
|
||||
|
||||
export const generateFields = <
|
||||
T extends InputTypeDefinitionKind | ObjectTypeDefinitionKind,
|
||||
>(
|
||||
objectMetadata: ObjectMetadataInterface,
|
||||
kind: T,
|
||||
options: WorkspaceBuildSchemaOptions,
|
||||
typeFactory: TypeFactory<T>,
|
||||
): T extends InputTypeDefinitionKind
|
||||
>({
|
||||
objectMetadata,
|
||||
kind,
|
||||
options,
|
||||
typeFactory,
|
||||
isRelationConnectEnabled = false,
|
||||
}: {
|
||||
objectMetadata: ObjectMetadataInterface;
|
||||
kind: T;
|
||||
options: WorkspaceBuildSchemaOptions;
|
||||
typeFactory: TypeFactory<T>;
|
||||
isRelationConnectEnabled?: boolean;
|
||||
}): T extends InputTypeDefinitionKind
|
||||
? GraphQLInputFieldConfigMap
|
||||
: // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
GraphQLFieldConfigMap<any, any> => {
|
||||
const fields = {};
|
||||
const allGeneratedFields = {};
|
||||
|
||||
for (const fieldMetadata of objectMetadata.fields) {
|
||||
let generatedField;
|
||||
|
||||
if (
|
||||
isFieldMetadataInterfaceOfType(
|
||||
fieldMetadata,
|
||||
FieldMetadataType.RELATION,
|
||||
) &&
|
||||
fieldMetadata.settings?.relationType !== RelationType.MANY_TO_ONE
|
||||
isFieldMetadataInterfaceOfType(fieldMetadata, FieldMetadataType.RELATION)
|
||||
) {
|
||||
continue;
|
||||
generatedField = generateRelationField({
|
||||
fieldMetadata,
|
||||
kind,
|
||||
options,
|
||||
typeFactory,
|
||||
isRelationConnectEnabled,
|
||||
});
|
||||
} else {
|
||||
generatedField = generateField({
|
||||
fieldMetadata,
|
||||
kind,
|
||||
options,
|
||||
typeFactory,
|
||||
});
|
||||
}
|
||||
|
||||
const target = isCompositeFieldMetadataType(fieldMetadata.type)
|
||||
? fieldMetadata.type.toString()
|
||||
: fieldMetadata.id;
|
||||
Object.assign(allGeneratedFields, generatedField);
|
||||
}
|
||||
|
||||
const typeFactoryOptions = isInputTypeDefinitionKind(kind)
|
||||
? {
|
||||
nullable: fieldMetadata.isNullable,
|
||||
defaultValue: fieldMetadata.defaultValue,
|
||||
isArray:
|
||||
kind !== InputTypeDefinitionKind.Filter &&
|
||||
fieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||
settings: fieldMetadata.settings,
|
||||
isIdField: fieldMetadata.name === 'id',
|
||||
}
|
||||
: {
|
||||
nullable: fieldMetadata.isNullable,
|
||||
isArray: fieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||
settings: fieldMetadata.settings,
|
||||
// Scalar type is already defined in the entity itself.
|
||||
isIdField: false,
|
||||
};
|
||||
return allGeneratedFields;
|
||||
};
|
||||
|
||||
const type = typeFactory.create(
|
||||
target,
|
||||
const getTarget = <T extends FieldMetadataType>(
|
||||
fieldMetadata: FieldMetadataInterface<T>,
|
||||
) => {
|
||||
return isCompositeFieldMetadataType(fieldMetadata.type)
|
||||
? fieldMetadata.type.toString()
|
||||
: fieldMetadata.id;
|
||||
};
|
||||
|
||||
const getTypeFactoryOptions = <T extends FieldMetadataType>(
|
||||
fieldMetadata: FieldMetadataInterface<T>,
|
||||
kind: InputTypeDefinitionKind | ObjectTypeDefinitionKind,
|
||||
) => {
|
||||
return isInputTypeDefinitionKind(kind)
|
||||
? {
|
||||
nullable: fieldMetadata.isNullable,
|
||||
defaultValue: fieldMetadata.defaultValue,
|
||||
isArray:
|
||||
kind !== InputTypeDefinitionKind.Filter &&
|
||||
fieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||
settings: fieldMetadata.settings,
|
||||
isIdField: fieldMetadata.name === 'id',
|
||||
}
|
||||
: {
|
||||
nullable: fieldMetadata.isNullable,
|
||||
isArray: fieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||
settings: fieldMetadata.settings,
|
||||
// Scalar type is already defined in the entity itself.
|
||||
isIdField: false,
|
||||
};
|
||||
};
|
||||
|
||||
const generateField = <
|
||||
T extends InputTypeDefinitionKind | ObjectTypeDefinitionKind,
|
||||
>({
|
||||
fieldMetadata,
|
||||
kind,
|
||||
options,
|
||||
typeFactory,
|
||||
}: {
|
||||
fieldMetadata: FieldMetadataInterface;
|
||||
kind: T;
|
||||
options: WorkspaceBuildSchemaOptions;
|
||||
typeFactory: TypeFactory<T>;
|
||||
}) => {
|
||||
const target = getTarget(fieldMetadata);
|
||||
|
||||
const typeFactoryOptions = getTypeFactoryOptions(fieldMetadata, kind);
|
||||
|
||||
const type = typeFactory.create(
|
||||
target,
|
||||
fieldMetadata.type,
|
||||
kind,
|
||||
options,
|
||||
typeFactoryOptions,
|
||||
);
|
||||
|
||||
return {
|
||||
[fieldMetadata.name]: {
|
||||
type,
|
||||
description: fieldMetadata.description,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const generateRelationField = <
|
||||
T extends InputTypeDefinitionKind | ObjectTypeDefinitionKind,
|
||||
>({
|
||||
fieldMetadata,
|
||||
kind,
|
||||
options,
|
||||
typeFactory,
|
||||
isRelationConnectEnabled,
|
||||
}: {
|
||||
fieldMetadata: FieldMetadataInterface<FieldMetadataType.RELATION>;
|
||||
kind: T;
|
||||
options: WorkspaceBuildSchemaOptions;
|
||||
typeFactory: TypeFactory<T>;
|
||||
isRelationConnectEnabled: boolean;
|
||||
}) => {
|
||||
const relationField = {};
|
||||
|
||||
if (fieldMetadata.settings?.relationType === RelationType.ONE_TO_MANY) {
|
||||
return relationField;
|
||||
}
|
||||
|
||||
const joinColumnName = fieldMetadata.settings?.joinColumnName;
|
||||
|
||||
if (!joinColumnName) {
|
||||
throw new Error('Join column name is not defined');
|
||||
}
|
||||
|
||||
const target = getTarget(fieldMetadata);
|
||||
const typeFactoryOptions = getTypeFactoryOptions(fieldMetadata, kind);
|
||||
|
||||
let type = typeFactory.create(
|
||||
target,
|
||||
fieldMetadata.type,
|
||||
kind,
|
||||
options,
|
||||
typeFactoryOptions,
|
||||
);
|
||||
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
relationField[joinColumnName] = {
|
||||
type,
|
||||
description: fieldMetadata.description,
|
||||
};
|
||||
|
||||
if (
|
||||
[InputTypeDefinitionKind.Create, InputTypeDefinitionKind.Update].includes(
|
||||
kind as InputTypeDefinitionKind,
|
||||
) &&
|
||||
isDefined(fieldMetadata.relationTargetObjectMetadataId) &&
|
||||
isRelationConnectEnabled
|
||||
) {
|
||||
type = typeFactory.create(
|
||||
formatRelationConnectInputTarget(
|
||||
fieldMetadata.relationTargetObjectMetadataId,
|
||||
),
|
||||
fieldMetadata.type,
|
||||
kind,
|
||||
options,
|
||||
typeFactoryOptions,
|
||||
{
|
||||
...typeFactoryOptions,
|
||||
isRelationConnectField: true,
|
||||
},
|
||||
);
|
||||
|
||||
if (
|
||||
isFieldMetadataInterfaceOfType(
|
||||
fieldMetadata,
|
||||
FieldMetadataType.RELATION,
|
||||
) &&
|
||||
fieldMetadata.settings?.relationType === RelationType.MANY_TO_ONE
|
||||
) {
|
||||
const joinColumnName = fieldMetadata.settings?.joinColumnName;
|
||||
|
||||
if (!joinColumnName) {
|
||||
throw new Error('Join column name is not defined');
|
||||
}
|
||||
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
fields[joinColumnName] = {
|
||||
type,
|
||||
description: fieldMetadata.description,
|
||||
};
|
||||
}
|
||||
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
fields[fieldMetadata.name] = {
|
||||
type,
|
||||
description: fieldMetadata.description,
|
||||
};
|
||||
}
|
||||
|
||||
return fields;
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
relationField[fieldMetadata.name] = {
|
||||
type: type,
|
||||
description: fieldMetadata.description,
|
||||
};
|
||||
|
||||
return relationField;
|
||||
};
|
||||
|
||||
// Type guard
|
||||
|
||||
Reference in New Issue
Block a user