From 4523ee45bc6c392cab1033b6c9f542d43def4793 Mon Sep 17 00:00:00 2001 From: Paul Rastoin <45004772+prastoin@users.noreply.github.com> Date: Fri, 8 Aug 2025 16:24:47 +0200 Subject: [PATCH] Multiple errors on field input transpilation improvement (#13774) # Introduction - ~~returning deleted field on deleteOneField~~ postponed for later cannot build dto from flat atm - refactor createFieldInput transpilation to handle multiple errors instead of throwing - renamed extract function --- .../field-metadata-relation.service.ts | 2 +- .../services/field-metadata.service-v2.ts | 40 ++++---- ...elation-creation-payload-or-throw.util.ts} | 0 .../field-input-transpilation-result.type.ts | 13 +++ ...-to-flat-field-metadatas-to-create.util.ts | 96 +++++++++++-------- ...field-input-to-flat-field-metadata.util.ts | 73 ++++++++++---- ...n-field-input-transpilations-error.util.ts | 27 ++++++ ...object-metadata-maps-or-throw.spec.ts.snap | 5 - ...object-metadata-maps-or-throw.spec.ts.snap | 5 + ...lat-object-metadata-maps-or-throw.spec.ts} | 27 +++--- ...get-sub-flat-object-metadata-maps.spec.ts} | 16 ++-- ...flat-object-metadata-maps-or-throw.util.ts | 42 -------- ...s-out-of-flat-object-metadata-maps.util.ts | 17 ---- ...flat-object-metadata-maps-or-throw.util.ts | 40 ++++++++ .../get-sub-flat-object-metadata-maps.util.ts | 15 +++ 15 files changed, 255 insertions(+), 163 deletions(-) rename packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/{validate-relation-creation-payload.util.ts => validate-relation-creation-payload-or-throw.util.ts} (100%) create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/types/field-input-transpilation-result.type.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/throw-on-field-input-transpilations-error.util.ts delete mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/__snapshots__/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.spec.ts.snap create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/__snapshots__/get-sub-flat-object-metadata-maps-or-throw.spec.ts.snap rename packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/{extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.spec.ts => get-sub-flat-object-metadata-maps-or-throw.spec.ts} (80%) rename packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/{extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps.spec.ts => get-sub-flat-object-metadata-maps.spec.ts} (62%) delete mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.util.ts delete mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps-or-throw.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps.util.ts diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata-relation.service.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata-relation.service.ts index fb06c6f653..22219b6481 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata-relation.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata-relation.service.ts @@ -19,7 +19,7 @@ import { } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception'; import { computeRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-relation-field-join-column-name.util'; import { prepareCustomFieldMetadataForCreation } from 'src/engine/metadata-modules/field-metadata/utils/prepare-field-metadata-for-creation.util'; -import { validateRelationCreationPayloadOrThrow } from 'src/engine/metadata-modules/field-metadata/utils/validate-relation-creation-payload.util'; +import { validateRelationCreationPayloadOrThrow } from 'src/engine/metadata-modules/field-metadata/utils/validate-relation-creation-payload-or-throw.util'; import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { RelationOnDeleteAction } from 'src/engine/metadata-modules/relation-metadata/relation-on-delete-action.type'; import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps'; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service-v2.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service-v2.ts index 636c73334c..b9f5a33e2b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service-v2.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service-v2.ts @@ -22,12 +22,13 @@ import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-m import { fromCreateFieldInputToFlatFieldMetadatasToCreate } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-metadatas-to-create.util'; import { fromDeleteFieldInputToFlatFieldMetadatasToDelete } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-delete-field-input-to-flat-field-metadatas-to-delete.util'; import { isFlatFieldMetadataEntityOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util'; +import { throwOnFieldInputTranspilationsError } from 'src/engine/metadata-modules/flat-field-metadata/utils/throw-on-field-input-transpilations-error.util'; import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type'; import { addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util'; import { addFlatFieldMetadataInFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-field-metadata-in-flat-object-metadata-maps.util'; import { deleteFieldFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-field-from-flat-object-metadata-maps-or-throw.util'; -import { extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.util'; -import { extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps.util'; +import { getSubFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps-or-throw.util'; +import { getSubFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps.util'; import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service'; import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service'; import { WorkspaceMigrationRunnerV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/workspace-migration-runner-v2.service'; @@ -105,7 +106,7 @@ export class FieldMetadataServiceV2 { } const flatObjectMetadataMapsWithImpactedObject = - extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow({ + getSubFlatObjectMetadataMapsOrThrow({ flatObjectMetadataMaps: existingFlatObjectMetadataMaps, objectMetadataIds: flatFieldMetadatasToDelete.map( (flatFieldMetadataToDelete) => @@ -132,7 +133,8 @@ export class FieldMetadataServiceV2 { await this.workspaceMigrationRunnerV2Service.run(workspaceMigration); - return; + // TODO to be discussed with coco regarding storing dates in flat in order to build dtos + // return flatFieldMetadatasToDelete[0]; } private computeOtherFlatObjectMetadataMapsToValidate({ @@ -171,7 +173,7 @@ export class FieldMetadataServiceV2 { } const flatObjectMetadataMapsWithRelatedObjectMetadata = - extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMaps({ + getSubFlatObjectMetadataMaps({ flatObjectMetadataMaps, objectMetadataIds: [relatedFlatFieldMetadataToCreate.objectMetadataId], }); @@ -202,7 +204,7 @@ export class FieldMetadataServiceV2 { { workspaceId }, ); - const flatFieldMetadatasToCreate = ( + const allInputTranspilationsResults = ( await Promise.all( fieldMetadataInputs.map( async (fieldMetadataInput) => @@ -215,6 +217,14 @@ export class FieldMetadataServiceV2 { ) ).flat(); + throwOnFieldInputTranspilationsError( + allInputTranspilationsResults, + 'Multiple validation errors occurred while creating field', + ); + const flatFieldMetadatasToCreate = allInputTranspilationsResults.flatMap( + ({ result }) => result, + ); + const allValidationErrors: FailedFlatFieldMetadataValidationExceptions[] = []; let optimisticFlatObjectMetadataMaps = structuredClone( @@ -274,16 +284,14 @@ export class FieldMetadataServiceV2 { ); try { - const fromFlatObjectMetadataMaps = - extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow({ - flatObjectMetadataMaps: existingFlatObjectMetadataMaps, - objectMetadataIds: impactedObjectMetadataIds, - }); - const toFlatObjectMetadataMaps = - extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow({ - flatObjectMetadataMaps: optimisticFlatObjectMetadataMaps, - objectMetadataIds: impactedObjectMetadataIds, - }); + const fromFlatObjectMetadataMaps = getSubFlatObjectMetadataMapsOrThrow({ + flatObjectMetadataMaps: existingFlatObjectMetadataMaps, + objectMetadataIds: impactedObjectMetadataIds, + }); + const toFlatObjectMetadataMaps = getSubFlatObjectMetadataMapsOrThrow({ + flatObjectMetadataMaps: optimisticFlatObjectMetadataMaps, + objectMetadataIds: impactedObjectMetadataIds, + }); const workspaceMigration = this.workspaceMigrationBuilderV2.build({ fromFlatObjectMetadataMaps, toFlatObjectMetadataMaps, diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-relation-creation-payload.util.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-relation-creation-payload-or-throw.util.ts similarity index 100% rename from packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-relation-creation-payload.util.ts rename to packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-relation-creation-payload-or-throw.util.ts diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/types/field-input-transpilation-result.type.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/types/field-input-transpilation-result.type.ts new file mode 100644 index 0000000000..1d42c575bd --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/types/field-input-transpilation-result.type.ts @@ -0,0 +1,13 @@ +import { type FailedFlatFieldMetadataValidationExceptions } from 'src/engine/metadata-modules/flat-field-metadata/types/failed-flat-field-metadata-validation.type'; + +export type FailedFieldInputTranspilation = { + status: 'fail'; + error: FailedFlatFieldMetadataValidationExceptions; +}; +export type SuccessfulFieldInputTranspilation = { + status: 'success'; + result: T; +}; +export type FieldInputTranspilationResult = + | FailedFieldInputTranspilation + | SuccessfulFieldInputTranspilation; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-metadatas-to-create.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-metadatas-to-create.util.ts index ad043a5849..aa0226a656 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-metadatas-to-create.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-metadatas-to-create.util.ts @@ -15,12 +15,13 @@ import { FieldMetadataExceptionCode, } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception'; import { generateRatingOptions } from 'src/engine/metadata-modules/field-metadata/utils/generate-rating-optionts.util'; +import { type FieldInputTranspilationResult } from 'src/engine/metadata-modules/flat-field-metadata/types/field-input-transpilation-result.type'; import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; import { fromRelationCreateFieldInputToFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-relation-create-field-input-to-flat-field-metadata.util'; import { getDefaultFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/get-default-flat-field-metadata-from-create-field-input.util'; import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type'; -type FromCreateFieldInputToFlatObjectMetadata = { +type FromCreateFieldInputToFlatObjectMetadataArgs = { rawCreateFieldInput: Omit; existingFlatObjectMetadataMaps: FlatObjectMetadataMaps; workspaceId: string; @@ -30,12 +31,17 @@ export const fromCreateFieldInputToFlatFieldMetadatasToCreate = async ({ rawCreateFieldInput, workspaceId, existingFlatObjectMetadataMaps, -}: FromCreateFieldInputToFlatObjectMetadata): Promise => { +}: FromCreateFieldInputToFlatObjectMetadataArgs): Promise< + FieldInputTranspilationResult +> => { if (rawCreateFieldInput.isRemoteCreation) { - throw new FieldMetadataException( - "Remote fields aren't supported", - FieldMetadataExceptionCode.INVALID_FIELD_INPUT, - ); + return { + status: 'fail', + error: new FieldMetadataException( + "Remote fields aren't supported", + FieldMetadataExceptionCode.INVALID_FIELD_INPUT, + ), + }; } const createFieldInput = trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties( @@ -46,14 +52,17 @@ export const fromCreateFieldInputToFlatFieldMetadatasToCreate = async ({ existingFlatObjectMetadataMaps.byId[createFieldInput.objectMetadataId]; if (!isDefined(parentFlatObjectMetadata)) { - throw new FieldMetadataException( - 'Provided object metadata id does not exist', - FieldMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND, - { - userFriendlyMessage: - 'Created field metadata, parent object metadata not found', - }, - ); + return { + status: 'fail', + error: new FieldMetadataException( + 'Provided object metadata id does not exist', + FieldMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND, + { + userFriendlyMessage: + 'Created field metadata, parent object metadata not found', + }, + ), + }; } const fieldMetadataId = v4(); @@ -71,7 +80,7 @@ export const fromCreateFieldInputToFlatFieldMetadatasToCreate = async ({ ); } case FieldMetadataType.RELATION: { - return fromRelationCreateFieldInputToFlatFieldMetadata({ + return await fromRelationCreateFieldInputToFlatFieldMetadata({ existingFlatObjectMetadataMaps, sourceParentFlatObjectMetadata: parentFlatObjectMetadata, createFieldInput, @@ -79,15 +88,18 @@ export const fromCreateFieldInputToFlatFieldMetadatasToCreate = async ({ }); } case FieldMetadataType.RATING: { - return [ - { - ...commonFlatFieldMetadata, - type: createFieldInput.type, - settings: null, - defaultValue: commonFlatFieldMetadata.defaultValue as string, // Could this be improved ? - options: generateRatingOptions(), - } satisfies FlatFieldMetadata, - ]; + return { + status: 'success', + result: [ + { + ...commonFlatFieldMetadata, + type: createFieldInput.type, + settings: null, + defaultValue: commonFlatFieldMetadata.defaultValue as string, // Could this be improved ? + options: generateRatingOptions(), + } satisfies FlatFieldMetadata, + ], + }; } case FieldMetadataType.SELECT: case FieldMetadataType.MULTI_SELECT: { @@ -100,15 +112,18 @@ export const fromCreateFieldInputToFlatFieldMetadatasToCreate = async ({ ), })); - return [ - { - ...commonFlatFieldMetadata, - type: createFieldInput.type, - options, - defaultValue: commonFlatFieldMetadata.defaultValue as string, // Could this be improved ? - settings: null, - } satisfies FlatFieldMetadata, - ]; + return { + status: 'success', + result: [ + { + ...commonFlatFieldMetadata, + type: createFieldInput.type, + options, + defaultValue: commonFlatFieldMetadata.defaultValue as string, // Could this be improved ? + settings: null, + } satisfies FlatFieldMetadata, + ], + }; } case FieldMetadataType.UUID: case FieldMetadataType.TEXT: @@ -130,12 +145,15 @@ export const fromCreateFieldInputToFlatFieldMetadatasToCreate = async ({ case FieldMetadataType.ACTOR: case FieldMetadataType.ARRAY: case FieldMetadataType.TS_VECTOR: { - return [ - { - ...commonFlatFieldMetadata, - type: createFieldInput.type, - }, - ]; + return { + status: 'success', + result: [ + { + ...commonFlatFieldMetadata, + type: createFieldInput.type, + }, + ], + }; } default: { assertUnreachable(createFieldInput.type, 'Encountered an uncovered'); diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-relation-create-field-input-to-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-relation-create-field-input-to-flat-field-metadata.util.ts index 1d4638d6f0..3560a5e0ad 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-relation-create-field-input-to-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-relation-create-field-input-to-flat-field-metadata.util.ts @@ -1,5 +1,8 @@ import { FieldMetadataType } from 'twenty-shared/types'; -import { isDefined } from 'twenty-shared/utils'; +import { + isDefined, + trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties, +} from 'twenty-shared/utils'; import { v4 } from 'uuid'; import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface'; @@ -9,7 +12,8 @@ import { FieldMetadataException, FieldMetadataExceptionCode, } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception'; -import { validateRelationCreationPayloadOrThrow } from 'src/engine/metadata-modules/field-metadata/utils/validate-relation-creation-payload.util'; +import { validateRelationCreationPayloadOrThrow } from 'src/engine/metadata-modules/field-metadata/utils/validate-relation-creation-payload-or-throw.util'; +import { type FieldInputTranspilationResult } from 'src/engine/metadata-modules/flat-field-metadata/types/field-input-transpilation-result.type'; import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; import { getDefaultFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/get-default-flat-field-metadata-from-create-field-input.util'; import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type'; @@ -50,17 +54,38 @@ export const fromRelationCreateFieldInputToFlatFieldMetadata = async ({ createFieldInput, workspaceId, }: FromRelationCreateFieldInputToFlatFieldMetadataArgs): Promise< - FlatFieldMetadata[] + FieldInputTranspilationResult > => { - const { relationCreationPayload } = createFieldInput; + const rawCreationPayload = createFieldInput.relationCreationPayload; - if (!isDefined(relationCreationPayload)) { - throw new FieldMetadataException( - `Relation creation payload is required`, - FieldMetadataExceptionCode.INVALID_FIELD_INPUT, - ); + if (!isDefined(rawCreationPayload)) { + return { + status: 'fail', + error: new FieldMetadataException( + `Relation creation payload is required`, + FieldMetadataExceptionCode.INVALID_FIELD_INPUT, + ), + }; + } + + const relationCreationPayload = + trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties( + rawCreationPayload, + ['targetFieldIcon', 'targetFieldLabel', 'targetObjectMetadataId', 'type'], + ); + + try { + await validateRelationCreationPayloadOrThrow(relationCreationPayload); + } catch (error) { + if (error instanceof FieldMetadataException) { + return { + status: 'fail', + error, + }; + } else { + throw error; + } } - await validateRelationCreationPayloadOrThrow(relationCreationPayload); const targetParentFlatObjectMetadata = existingFlatObjectMetadataMaps.byId[ @@ -68,10 +93,13 @@ export const fromRelationCreateFieldInputToFlatFieldMetadata = async ({ ]; if (!isDefined(targetParentFlatObjectMetadata)) { - throw new FieldMetadataException( - `Object metadata relation target not found for relation creation payload`, - FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED, - ); + return { + status: 'fail', + error: new FieldMetadataException( + `Object metadata relation target not found for relation creation payload`, + FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED, + ), + }; } const sourceFlatFieldMetadataSettings = @@ -135,11 +163,14 @@ export const fromRelationCreateFieldInputToFlatFieldMetadata = async ({ flatRelationTargetObjectMetadata: sourceParentFlatObjectMetadata, }; - return [ - { - ...sourceFlatFieldMetadata, - flatRelationTargetFieldMetadata: targetFlatFieldMetadata, - }, - targetFlatFieldMetadata, - ] satisfies FlatFieldMetadata[]; + return { + status: 'success', + result: [ + { + ...sourceFlatFieldMetadata, + flatRelationTargetFieldMetadata: targetFlatFieldMetadata, + }, + targetFlatFieldMetadata, + ] satisfies FlatFieldMetadata[], + }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/throw-on-field-input-transpilations-error.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/throw-on-field-input-transpilations-error.util.ts new file mode 100644 index 0000000000..5b5bde8b3f --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/throw-on-field-input-transpilations-error.util.ts @@ -0,0 +1,27 @@ +import { MultipleMetadataValidationErrors } from 'src/engine/core-modules/error/multiple-metadata-validation-errors'; +import { + type FieldInputTranspilationResult, + type SuccessfulFieldInputTranspilation, +} from 'src/engine/metadata-modules/flat-field-metadata/types/field-input-transpilation-result.type'; + +type ThrowOnFieldInputTranspilationsErrorArgs = ( + inputTranspilationResults: FieldInputTranspilationResult[], + errorLabel: string, +) => asserts inputTranspilationResults is SuccessfulFieldInputTranspilation[]; +export const throwOnFieldInputTranspilationsError: ThrowOnFieldInputTranspilationsErrorArgs = + ( + inputTranspilationResults: FieldInputTranspilationResult[], + errorLabel: string, + ) => { + const failedInputTranspilationErrors = inputTranspilationResults.flatMap( + (transpilationResult) => + transpilationResult.status === 'fail' ? transpilationResult.error : [], + ); + + if (failedInputTranspilationErrors.length > 0) { + throw new MultipleMetadataValidationErrors( + failedInputTranspilationErrors, + errorLabel, + ); + } + }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/__snapshots__/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.spec.ts.snap b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/__snapshots__/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.spec.ts.snap deleted file mode 100644 index adfbb9a079..0000000000 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/__snapshots__/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.spec.ts.snap +++ /dev/null @@ -1,5 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow should throw when extracting twice the same object 1`] = `"addFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadataMapsOrThrow: flat object metadata with fields maps to add already exists"`; - -exports[`extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow should throw when object metadata id is not found 1`] = `"extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow object metadata not found"`; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/__snapshots__/get-sub-flat-object-metadata-maps-or-throw.spec.ts.snap b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/__snapshots__/get-sub-flat-object-metadata-maps-or-throw.spec.ts.snap new file mode 100644 index 0000000000..1ba2a586ad --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/__snapshots__/get-sub-flat-object-metadata-maps-or-throw.spec.ts.snap @@ -0,0 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`getSubFlatObjectMetadataMapsOrThrow should throw when extracting twice the same object 1`] = `"addFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadataMapsOrThrow: flat object metadata with fields maps to add already exists"`; + +exports[`getSubFlatObjectMetadataMapsOrThrow should throw when object metadata id is not found 1`] = `"getSubFlatObjectMetadataMapsOrThrow object metadata not found"`; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.spec.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/get-sub-flat-object-metadata-maps-or-throw.spec.ts similarity index 80% rename from packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.spec.ts rename to packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/get-sub-flat-object-metadata-maps-or-throw.spec.ts index 94c43035d4..a73ffef01e 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.spec.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/get-sub-flat-object-metadata-maps-or-throw.spec.ts @@ -1,27 +1,27 @@ import { jestExpectToBeDefined } from 'test/utils/expect-to-be-defined.util.test'; import { - type EachTestingContext, eachTestingContextFilter, + type EachTestingContext, } from 'twenty-shared/testing'; import { FLAT_OBJECT_METADATA_MAPS_MOCKS } from 'src/engine/metadata-modules/flat-object-metadata-maps/mocks/flat-object-metadata-maps.mock'; import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type'; import { - type ExtractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrowArgs, - extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow, -} from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.util'; + getSubFlatObjectMetadataMapsOrThrow, + type GetSubFlatObjectMetadataMapsOrThrowArgs, +} from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps-or-throw.util'; import { PET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/pet-flat-object.mock'; import { ROCKET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/rocket-flat-object.mock'; import { fromFlatObjectMetadatasToFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadatas-to-flat-object-metadata-maps.util'; -type ExtractFlatObjectMetadataMapsTestCase = { - input: ExtractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrowArgs; +type GetSubObjectMetadataMapsOrThrowTestCase = { + input: GetSubFlatObjectMetadataMapsOrThrowArgs; shouldThrow?: true; expected?: FlatObjectMetadataMaps; }; -describe('extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow', () => { - const testCases: EachTestingContext[] = +describe('getSubFlatObjectMetadataMapsOrThrow', () => { + const testCases: EachTestingContext[] = [ { title: 'should throw when object metadata id is not found', @@ -89,18 +89,17 @@ describe('extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow', () = }) => { if (shouldThrow) { expect(() => - extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow({ + getSubFlatObjectMetadataMapsOrThrow({ objectMetadataIds, flatObjectMetadataMaps, }), ).toThrowErrorMatchingSnapshot(); } else { jestExpectToBeDefined(expected); - const result = - extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow({ - objectMetadataIds, - flatObjectMetadataMaps, - }); + const result = getSubFlatObjectMetadataMapsOrThrow({ + objectMetadataIds, + flatObjectMetadataMaps, + }); expect(result).toEqual(expected); } diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps.spec.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/get-sub-flat-object-metadata-maps.spec.ts similarity index 62% rename from packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps.spec.ts rename to packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/get-sub-flat-object-metadata-maps.spec.ts index 124b3177d7..3c5b52be40 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps.spec.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/get-sub-flat-object-metadata-maps.spec.ts @@ -1,19 +1,19 @@ import { - type EachTestingContext, eachTestingContextFilter, + type EachTestingContext, } from 'twenty-shared/testing'; import { FLAT_OBJECT_METADATA_MAPS_MOCKS } from 'src/engine/metadata-modules/flat-object-metadata-maps/mocks/flat-object-metadata-maps.mock'; -import { type ExtractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrowArgs } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.util'; -import { extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps.util'; +import { type GetSubFlatObjectMetadataMapsOrThrowArgs } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps-or-throw.util'; +import { getSubFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps.util'; import { PET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/pet-flat-object.mock'; -type ExtractFlatObjectMetadataMapsTestCase = { - input: ExtractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrowArgs; +type GetSubFlatObjectMetadataMapsTestCase = { + input: GetSubFlatObjectMetadataMapsOrThrowArgs; }; -describe('extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMaps', () => { - const testCases: EachTestingContext[] = +describe('getSubFlatObjectMetadataMaps', () => { + const testCases: EachTestingContext[] = [ { title: 'should throw when object metadata id is not found', @@ -47,7 +47,7 @@ describe('extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMaps', () => { input: { objectMetadataIds, flatObjectMetadataMaps }, }, }) => { - const result = extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMaps({ + const result = getSubFlatObjectMetadataMaps({ objectMetadataIds, flatObjectMetadataMaps, }); diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.util.ts deleted file mode 100644 index a2cc116ed4..0000000000 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.util.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { isDefined } from 'twenty-shared/utils'; - -import { EMPTY_FLAT_OBJECT_METADATA_MAPS } from 'src/engine/metadata-modules/flat-object-metadata-maps/constant/empty-flat-object-metadata-maps.constant'; -import { - FlatObjectMetadataMapsException, - FlatObjectMetadataMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-object-metadata-maps/flat-object-metadata-maps.exception'; -import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type'; -import { addFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-object-metadata-with-field-maps-to-flat-object-metadata-maps-or-throw.util'; - -export type ExtractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrowArgs = - { - objectMetadataIds: string[]; - flatObjectMetadataMaps: FlatObjectMetadataMaps; - }; -export const extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow = - ({ - flatObjectMetadataMaps: sourceFlatObjectMetadataMaps, - objectMetadataIds, - }: ExtractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrowArgs): FlatObjectMetadataMaps => { - return objectMetadataIds.reduce( - (flatObjectMetadataMaps, objectMetadataId) => { - const flatObjectMetadataWithFlatFieldMaps = - sourceFlatObjectMetadataMaps.byId[objectMetadataId]; - - if (!isDefined(flatObjectMetadataWithFlatFieldMaps)) { - throw new FlatObjectMetadataMapsException( - 'extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow object metadata not found', - FlatObjectMetadataMapsExceptionCode.OBJECT_METADATA_NOT_FOUND, - ); - } - - return addFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadataMapsOrThrow( - { - flatObjectMetadataMaps, - flatObjectMetadataWithFlatFieldMaps, - }, - ); - }, - EMPTY_FLAT_OBJECT_METADATA_MAPS, - ); - }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps.util.ts deleted file mode 100644 index f79031de34..0000000000 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps.util.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type'; -import { - type ExtractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrowArgs, - extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow, -} from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.util'; - -export const extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMaps = ( - args: ExtractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrowArgs, -): FlatObjectMetadataMaps | undefined => { - try { - return extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow( - args, - ); - } catch { - return undefined; - } -}; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps-or-throw.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps-or-throw.util.ts new file mode 100644 index 0000000000..72548b9fe9 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps-or-throw.util.ts @@ -0,0 +1,40 @@ +import { isDefined } from 'twenty-shared/utils'; + +import { EMPTY_FLAT_OBJECT_METADATA_MAPS } from 'src/engine/metadata-modules/flat-object-metadata-maps/constant/empty-flat-object-metadata-maps.constant'; +import { + FlatObjectMetadataMapsException, + FlatObjectMetadataMapsExceptionCode, +} from 'src/engine/metadata-modules/flat-object-metadata-maps/flat-object-metadata-maps.exception'; +import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type'; +import { addFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-object-metadata-with-field-maps-to-flat-object-metadata-maps-or-throw.util'; + +export type GetSubFlatObjectMetadataMapsOrThrowArgs = { + objectMetadataIds: string[]; + flatObjectMetadataMaps: FlatObjectMetadataMaps; +}; +export const getSubFlatObjectMetadataMapsOrThrow = ({ + flatObjectMetadataMaps: sourceFlatObjectMetadataMaps, + objectMetadataIds, +}: GetSubFlatObjectMetadataMapsOrThrowArgs): FlatObjectMetadataMaps => { + return objectMetadataIds.reduce( + (flatObjectMetadataMaps, objectMetadataId) => { + const flatObjectMetadataWithFlatFieldMaps = + sourceFlatObjectMetadataMaps.byId[objectMetadataId]; + + if (!isDefined(flatObjectMetadataWithFlatFieldMaps)) { + throw new FlatObjectMetadataMapsException( + 'getSubFlatObjectMetadataMapsOrThrow object metadata not found', + FlatObjectMetadataMapsExceptionCode.OBJECT_METADATA_NOT_FOUND, + ); + } + + return addFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadataMapsOrThrow( + { + flatObjectMetadataMaps, + flatObjectMetadataWithFlatFieldMaps, + }, + ); + }, + EMPTY_FLAT_OBJECT_METADATA_MAPS, + ); +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps.util.ts new file mode 100644 index 0000000000..72dae99081 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps.util.ts @@ -0,0 +1,15 @@ +import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type'; +import { + getSubFlatObjectMetadataMapsOrThrow, + type GetSubFlatObjectMetadataMapsOrThrowArgs, +} from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps-or-throw.util'; + +export const getSubFlatObjectMetadataMaps = ( + args: GetSubFlatObjectMetadataMapsOrThrowArgs, +): FlatObjectMetadataMaps | undefined => { + try { + return getSubFlatObjectMetadataMapsOrThrow(args); + } catch { + return undefined; + } +};