From a5b41e09f5ad4b8d1496eeb700b5fc965fb261c9 Mon Sep 17 00:00:00 2001 From: Lucas Bordeau Date: Mon, 11 Mar 2024 15:04:52 +0100 Subject: [PATCH] Added a RelationFromOneSide ResolveField in FieldMetadata GraphQL Resolver (#4378) * Added a ResolveField for relationDefinition on a FieldMetadataItem --- .../dtos/relation-definition.dto.ts | 43 ++++++++++ .../field-metadata/field-metadata.module.ts | 6 +- .../field-metadata/field-metadata.resolver.ts | 16 +++- .../field-metadata/field-metadata.service.ts | 79 ++++++++++++++++++- .../dtos/object-metadata.dto.ts | 4 +- 5 files changed, 143 insertions(+), 5 deletions(-) create mode 100644 packages/twenty-server/src/metadata/field-metadata/dtos/relation-definition.dto.ts diff --git a/packages/twenty-server/src/metadata/field-metadata/dtos/relation-definition.dto.ts b/packages/twenty-server/src/metadata/field-metadata/dtos/relation-definition.dto.ts new file mode 100644 index 0000000000..3501109476 --- /dev/null +++ b/packages/twenty-server/src/metadata/field-metadata/dtos/relation-definition.dto.ts @@ -0,0 +1,43 @@ +import { Field, ObjectType, registerEnumType } from '@nestjs/graphql'; + +import { IsEnum, IsNotEmpty } from 'class-validator'; + +import { FieldMetadataDTO } from 'src/metadata/field-metadata/dtos/field-metadata.dto'; +import { ObjectMetadataDTO } from 'src/metadata/object-metadata/dtos/object-metadata.dto'; +import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity'; + +export enum RelationDefinitionType { + ONE_TO_ONE = RelationMetadataType.ONE_TO_ONE, + ONE_TO_MANY = RelationMetadataType.ONE_TO_MANY, + MANY_TO_MANY = RelationMetadataType.MANY_TO_MANY, + MANY_TO_ONE = 'MANY_TO_ONE', +} + +registerEnumType(RelationDefinitionType, { + name: 'RelationDefinitionType', + description: 'Relation definition type', +}); + +@ObjectType('RelationDefinition') +export class RelationDefinitionDTO { + @IsNotEmpty() + @Field(() => ObjectMetadataDTO) + sourceObjectMetadata: ObjectMetadataDTO; + + @IsNotEmpty() + @Field(() => ObjectMetadataDTO) + targetObjectMetadata: ObjectMetadataDTO; + + @IsNotEmpty() + @Field(() => FieldMetadataDTO) + sourceFieldMetadata: FieldMetadataDTO; + + @IsNotEmpty() + @Field(() => FieldMetadataDTO) + targetFieldMetadata: FieldMetadataDTO; + + @IsEnum(RelationDefinitionType) + @IsNotEmpty() + @Field(() => RelationDefinitionType) + direction: RelationDefinitionType; +} diff --git a/packages/twenty-server/src/metadata/field-metadata/field-metadata.module.ts b/packages/twenty-server/src/metadata/field-metadata/field-metadata.module.ts index b94e96ef58..be44027872 100644 --- a/packages/twenty-server/src/metadata/field-metadata/field-metadata.module.ts +++ b/packages/twenty-server/src/metadata/field-metadata/field-metadata.module.ts @@ -17,6 +17,7 @@ import { IsFieldMetadataDefaultValue } from 'src/metadata/field-metadata/validat import { FieldMetadataResolver } from 'src/metadata/field-metadata/field-metadata.resolver'; import { FieldMetadataDTO } from 'src/metadata/field-metadata/dtos/field-metadata.dto'; import { IsFieldMetadataOptions } from 'src/metadata/field-metadata/validators/is-field-metadata-options.validator'; +import { RelationMetadataEntity } from 'src/metadata/relation-metadata/relation-metadata.entity'; import { FieldMetadataService } from './field-metadata.service'; import { FieldMetadataEntity } from './field-metadata.entity'; @@ -28,7 +29,10 @@ import { UpdateFieldInput } from './dtos/update-field.input'; imports: [ NestjsQueryGraphQLModule.forFeature({ imports: [ - NestjsQueryTypeOrmModule.forFeature([FieldMetadataEntity], 'metadata'), + NestjsQueryTypeOrmModule.forFeature( + [FieldMetadataEntity, RelationMetadataEntity], + 'metadata', + ), WorkspaceMigrationModule, WorkspaceMigrationRunnerModule, ObjectMetadataModule, diff --git a/packages/twenty-server/src/metadata/field-metadata/field-metadata.resolver.ts b/packages/twenty-server/src/metadata/field-metadata/field-metadata.resolver.ts index 91b7dd6130..5c519fca72 100644 --- a/packages/twenty-server/src/metadata/field-metadata/field-metadata.resolver.ts +++ b/packages/twenty-server/src/metadata/field-metadata/field-metadata.resolver.ts @@ -1,11 +1,18 @@ import { UseGuards } from '@nestjs/common'; -import { Args, Mutation, Resolver } from '@nestjs/graphql'; +import { + Args, + Mutation, + Parent, + ResolveField, + Resolver, +} from '@nestjs/graphql'; import { Workspace } from 'src/core/workspace/workspace.entity'; import { AuthWorkspace } from 'src/decorators/auth/auth-workspace.decorator'; import { JwtAuthGuard } from 'src/guards/jwt.auth.guard'; import { CreateOneFieldMetadataInput } from 'src/metadata/field-metadata/dtos/create-field.input'; import { FieldMetadataDTO } from 'src/metadata/field-metadata/dtos/field-metadata.dto'; +import { RelationDefinitionDTO } from 'src/metadata/field-metadata/dtos/relation-definition.dto'; import { UpdateOneFieldMetadataInput } from 'src/metadata/field-metadata/dtos/update-field.input'; import { FieldMetadataService } from 'src/metadata/field-metadata/field-metadata.service'; @@ -35,4 +42,11 @@ export class FieldMetadataResolver { workspaceId, }); } + + @ResolveField(() => RelationDefinitionDTO, { nullable: true }) + async relationDefinition( + @Parent() fieldMetadata: FieldMetadataDTO, + ): Promise { + return await this.fieldMetadataService.getRelationDefinition(fieldMetadata); + } } diff --git a/packages/twenty-server/src/metadata/field-metadata/field-metadata.service.ts b/packages/twenty-server/src/metadata/field-metadata/field-metadata.service.ts index ce17833e89..2a618e0cdc 100644 --- a/packages/twenty-server/src/metadata/field-metadata/field-metadata.service.ts +++ b/packages/twenty-server/src/metadata/field-metadata/field-metadata.service.ts @@ -26,6 +26,15 @@ import { WorkspaceMigrationFactory } from 'src/metadata/workspace-migration/work import { computeObjectTargetTable } from 'src/workspace/utils/compute-object-target-table.util'; import { generateMigrationName } from 'src/metadata/workspace-migration/utils/generate-migration-name.util'; import { generateNullable } from 'src/metadata/field-metadata/utils/generate-nullable'; +import { FieldMetadataDTO } from 'src/metadata/field-metadata/dtos/field-metadata.dto'; +import { + RelationDefinitionDTO, + RelationDefinitionType, +} from 'src/metadata/field-metadata/dtos/relation-definition.dto'; +import { + RelationMetadataEntity, + RelationMetadataType, +} from 'src/metadata/relation-metadata/relation-metadata.entity'; import { FieldMetadataEntity, @@ -41,7 +50,8 @@ export class FieldMetadataService extends TypeOrmQueryService, - + @InjectRepository(RelationMetadataEntity, 'metadata') + private readonly relationMetadataRepository: Repository, private readonly objectMetadataService: ObjectMetadataService, private readonly workspaceMigrationFactory: WorkspaceMigrationFactory, private readonly workspaceMigrationService: WorkspaceMigrationService, @@ -340,4 +350,71 @@ export class FieldMetadataService extends TypeOrmQueryService { + if (fieldMetadata.type !== FieldMetadataType.RELATION) { + return null; + } + + const foundRelationMetadata = await this.relationMetadataRepository.findOne( + { + where: [ + { fromFieldMetadataId: fieldMetadata.id }, + { toFieldMetadataId: fieldMetadata.id }, + ], + relations: [ + 'fromObjectMetadata', + 'toObjectMetadata', + 'fromFieldMetadata', + 'toFieldMetadata', + ], + }, + ); + + if (!foundRelationMetadata) { + throw new Error('RelationMetadata not found'); + } + + const isRelationFromSource = + foundRelationMetadata.fromFieldMetadata.id === fieldMetadata.id; + + // TODO: implement MANY_TO_MANY + if ( + foundRelationMetadata.relationType === RelationMetadataType.MANY_TO_MANY + ) { + throw new Error(` + Relation type ${foundRelationMetadata.relationType} not supported + `); + } + + if (isRelationFromSource) { + const direction = + foundRelationMetadata.relationType === RelationMetadataType.ONE_TO_ONE + ? RelationDefinitionType.ONE_TO_ONE + : RelationDefinitionType.ONE_TO_MANY; + + return { + sourceObjectMetadata: foundRelationMetadata.fromObjectMetadata, + sourceFieldMetadata: foundRelationMetadata.fromFieldMetadata, + targetObjectMetadata: foundRelationMetadata.toObjectMetadata, + targetFieldMetadata: foundRelationMetadata.toFieldMetadata, + direction, + }; + } else { + const direction = + foundRelationMetadata.relationType === RelationMetadataType.ONE_TO_ONE + ? RelationDefinitionType.ONE_TO_ONE + : RelationDefinitionType.MANY_TO_ONE; + + return { + sourceObjectMetadata: foundRelationMetadata.toObjectMetadata, + sourceFieldMetadata: foundRelationMetadata.toFieldMetadata, + targetObjectMetadata: foundRelationMetadata.fromObjectMetadata, + targetFieldMetadata: foundRelationMetadata.fromFieldMetadata, + direction, + }; + } + } } diff --git a/packages/twenty-server/src/metadata/object-metadata/dtos/object-metadata.dto.ts b/packages/twenty-server/src/metadata/object-metadata/dtos/object-metadata.dto.ts index 857ff0d648..2f6a646317 100644 --- a/packages/twenty-server/src/metadata/object-metadata/dtos/object-metadata.dto.ts +++ b/packages/twenty-server/src/metadata/object-metadata/dtos/object-metadata.dto.ts @@ -69,8 +69,8 @@ export class ObjectMetadataDTO { updatedAt: Date; @Field({ nullable: true }) - labelIdentifierFieldMetadataId: string; + labelIdentifierFieldMetadataId?: string; @Field({ nullable: true }) - imageIdentifierFieldMetadataId: string; + imageIdentifierFieldMetadataId?: string; }