From 8c6569be3b4c91abae35c46f9afe9cf5f51e49da Mon Sep 17 00:00:00 2001 From: Marie <51697796+ijreilly@users.noreply.github.com> Date: Wed, 21 May 2025 15:53:25 +0200 Subject: [PATCH] Add relation exceptions (#12185) Introducing a class of RelationException extending CustomException to help grouping those exception in sentries by ExceptionCode. I did not introduce a filter as these are thrown in utils that can be used in multiple places now or in the future, and filters are to be added at resolver-level. --- .../exceptions/relation.exception.ts | 18 ++++++++++++++++++ .../utils/compute-relation-type.util.ts | 6 +++++- .../utils/determine-relation-details.util.ts | 9 ++++++++- .../determine-schema-relation-details.util.ts | 19 ++++++++++++++++--- .../twenty-orm/utils/get-join-column.util.ts | 16 ++++++++++++---- 5 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 packages/twenty-server/src/engine/twenty-orm/exceptions/relation.exception.ts diff --git a/packages/twenty-server/src/engine/twenty-orm/exceptions/relation.exception.ts b/packages/twenty-server/src/engine/twenty-orm/exceptions/relation.exception.ts new file mode 100644 index 000000000..fc35a947a --- /dev/null +++ b/packages/twenty-server/src/engine/twenty-orm/exceptions/relation.exception.ts @@ -0,0 +1,18 @@ +import { CustomException } from 'src/utils/custom-exception'; + +export class RelationException extends CustomException { + declare code: RelationExceptionCode; + constructor(message: string, code: RelationExceptionCode) { + super(message, code); + } +} + +export enum RelationExceptionCode { + RELATION_OBJECT_METADATA_NOT_FOUND = 'RELATION_OBJECT_METADATA_NOT_FOUND', + RELATION_TARGET_FIELD_METADATA_ID_NOT_FOUND = 'RELATION_TARGET_FIELD_METADATA_ID_NOT_FOUND', + RELATION_TARGET_FIELD_METADATA_NOT_FOUND = 'RELATION_TARGET_FIELD_METADATA_NOT_FOUND', + INVALID_RELATION_TYPE = 'INVALID_RELATION_TYPE', + RELATION_JOIN_COLUMN_ON_BOTH_SIDES = 'RELATION_JOIN_COLUMN_ON_BOTH_SIDES', + MISSING_RELATION_JOIN_COLUMN = 'MISSING_RELATION_JOIN_COLUMN', + MULTIPLE_JOIN_COLUMNS_FOUND = 'MULTIPLE_JOIN_COLUMNS_FOUND', +} diff --git a/packages/twenty-server/src/engine/twenty-orm/utils/compute-relation-type.util.ts b/packages/twenty-server/src/engine/twenty-orm/utils/compute-relation-type.util.ts index aeaa2abc0..e32c410df 100644 --- a/packages/twenty-server/src/engine/twenty-orm/utils/compute-relation-type.util.ts +++ b/packages/twenty-server/src/engine/twenty-orm/utils/compute-relation-type.util.ts @@ -1,3 +1,5 @@ +import { ForbiddenException } from '@nestjs/common'; + import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface'; import { @@ -29,6 +31,8 @@ export const computeRelationType = ( case RelationMetadataType.MANY_TO_MANY: return 'many-to-many'; default: - throw new Error('Invalid relation type'); + throw new ForbiddenException( + `Invalid relation type: ${relationMetadata.relationType}`, + ); } }; diff --git a/packages/twenty-server/src/engine/twenty-orm/utils/determine-relation-details.util.ts b/packages/twenty-server/src/engine/twenty-orm/utils/determine-relation-details.util.ts index 3e8dd64e1..15bc0639a 100644 --- a/packages/twenty-server/src/engine/twenty-orm/utils/determine-relation-details.util.ts +++ b/packages/twenty-server/src/engine/twenty-orm/utils/determine-relation-details.util.ts @@ -4,6 +4,10 @@ import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metada import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity'; import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps'; +import { + RelationException, + RelationExceptionCode, +} from 'src/engine/twenty-orm/exceptions/relation.exception'; import { computeRelationType } from 'src/engine/twenty-orm/utils/compute-relation-type.util'; interface RelationDetails { @@ -31,7 +35,10 @@ export async function determineRelationDetails( } if (!fromObjectMetadata || !toObjectMetadata) { - throw new Error('Object metadata not found'); + throw new RelationException( + 'Object metadata not found', + RelationExceptionCode.RELATION_OBJECT_METADATA_NOT_FOUND, + ); } const toFieldMetadata = Object.values(toObjectMetadata.fieldsById).find( diff --git a/packages/twenty-server/src/engine/twenty-orm/utils/determine-schema-relation-details.util.ts b/packages/twenty-server/src/engine/twenty-orm/utils/determine-schema-relation-details.util.ts index a7ed2cef7..bfabbe686 100644 --- a/packages/twenty-server/src/engine/twenty-orm/utils/determine-schema-relation-details.util.ts +++ b/packages/twenty-server/src/engine/twenty-orm/utils/determine-schema-relation-details.util.ts @@ -4,6 +4,10 @@ import { RelationType } from 'typeorm/metadata/types/RelationTypes'; import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface'; import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps'; +import { + RelationException, + RelationExceptionCode, +} from 'src/engine/twenty-orm/exceptions/relation.exception'; import { converRelationTypeToTypeORMRelationType } from 'src/engine/twenty-orm/utils/convert-relation-type-to-typeorm-relation-type.util'; interface RelationDetails { @@ -26,7 +30,10 @@ export async function determineSchemaRelationDetails( ); if (!fieldMetadata.relationTargetObjectMetadataId) { - throw new Error('Relation target object metadata ID is missing'); + throw new RelationException( + 'Relation target object metadata ID is missing', + RelationExceptionCode.RELATION_OBJECT_METADATA_NOT_FOUND, + ); } const sourceObjectMetadata = @@ -35,11 +42,17 @@ export async function determineSchemaRelationDetails( objectMetadataMaps.byId[fieldMetadata.relationTargetObjectMetadataId]; if (!sourceObjectMetadata || !targetObjectMetadata) { - throw new Error(`Object metadata not found for field ${fieldMetadata.id}`); + throw new RelationException( + `Object metadata not found for field ${fieldMetadata.id}`, + RelationExceptionCode.RELATION_OBJECT_METADATA_NOT_FOUND, + ); } if (!fieldMetadata.relationTargetFieldMetadataId) { - throw new Error('Relation target field metadata ID is missing'); + throw new RelationException( + 'Relation target field metadata ID is missing', + RelationExceptionCode.RELATION_TARGET_FIELD_METADATA_ID_NOT_FOUND, + ); } const targetFieldMetadata = diff --git a/packages/twenty-server/src/engine/twenty-orm/utils/get-join-column.util.ts b/packages/twenty-server/src/engine/twenty-orm/utils/get-join-column.util.ts index e9a70d94a..259f9b1d0 100644 --- a/packages/twenty-server/src/engine/twenty-orm/utils/get-join-column.util.ts +++ b/packages/twenty-server/src/engine/twenty-orm/utils/get-join-column.util.ts @@ -2,6 +2,10 @@ import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfa import { WorkspaceJoinColumnsMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-join-columns-metadata-args.interface'; import { WorkspaceRelationMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-relation-metadata-args.interface'; +import { + RelationException, + RelationExceptionCode, +} from 'src/engine/twenty-orm/exceptions/relation.exception'; import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage'; export const getJoinColumn = ( @@ -31,8 +35,9 @@ export const getJoinColumn = ( filteredJoinColumnsMetadataArgsCollection.length > 0 && oppositeFilteredJoinColumnsMetadataArgsCollection.length > 0 ) { - throw new Error( + throw new RelationException( `Join column for ${relationMetadataArgs.name} relation is present on both sides`, + RelationExceptionCode.RELATION_JOIN_COLUMN_ON_BOTH_SIDES, ); } @@ -52,8 +57,9 @@ export const getJoinColumn = ( ); if (!inverseSideRelationMetadataArgs) { - throw new Error( + throw new RelationException( `Inverse side join column of relation ${relationMetadataArgs.name} is missing`, + RelationExceptionCode.MISSING_RELATION_JOIN_COLUMN, ); } @@ -67,16 +73,18 @@ export const getJoinColumn = ( // Check if there are multiple join columns for the relation if (filteredJoinColumnsMetadataArgsCollection.length > 1) { - throw new Error( + throw new RelationException( `Multiple join columns found for relation ${relationMetadataArgs.name}`, + RelationExceptionCode.MULTIPLE_JOIN_COLUMNS_FOUND, ); } const joinColumnsMetadataArgs = filteredJoinColumnsMetadataArgsCollection[0]; if (!joinColumnsMetadataArgs) { - throw new Error( + throw new RelationException( `Join column is missing for relation ${relationMetadataArgs.name}`, + RelationExceptionCode.MISSING_RELATION_JOIN_COLUMN, ); }