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
This commit is contained in:
Paul Rastoin
2025-08-08 16:24:47 +02:00
committed by GitHub
parent 7a90fb2c5f
commit 4523ee45bc
15 changed files with 255 additions and 163 deletions
@@ -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<CreateFieldInput, 'workspaceId'>;
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
workspaceId: string;
@@ -30,12 +31,17 @@ export const fromCreateFieldInputToFlatFieldMetadatasToCreate = async ({
rawCreateFieldInput,
workspaceId,
existingFlatObjectMetadataMaps,
}: FromCreateFieldInputToFlatObjectMetadata): Promise<FlatFieldMetadata[]> => {
}: FromCreateFieldInputToFlatObjectMetadataArgs): Promise<
FieldInputTranspilationResult<FlatFieldMetadata[]>
> => {
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<typeof createFieldInput.type>,
];
return {
status: 'success',
result: [
{
...commonFlatFieldMetadata,
type: createFieldInput.type,
settings: null,
defaultValue: commonFlatFieldMetadata.defaultValue as string, // Could this be improved ?
options: generateRatingOptions(),
} satisfies FlatFieldMetadata<typeof createFieldInput.type>,
],
};
}
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<typeof createFieldInput.type>,
];
return {
status: 'success',
result: [
{
...commonFlatFieldMetadata,
type: createFieldInput.type,
options,
defaultValue: commonFlatFieldMetadata.defaultValue as string, // Could this be improved ?
settings: null,
} satisfies FlatFieldMetadata<typeof createFieldInput.type>,
],
};
}
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');
@@ -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<FlatFieldMetadata[]>
> => {
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<FieldMetadataType.RELATION>[];
return {
status: 'success',
result: [
{
...sourceFlatFieldMetadata,
flatRelationTargetFieldMetadata: targetFlatFieldMetadata,
},
targetFlatFieldMetadata,
] satisfies FlatFieldMetadata<FieldMetadataType.RELATION>[],
};
};
@@ -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 = <T>(
inputTranspilationResults: FieldInputTranspilationResult<T>[],
errorLabel: string,
) => asserts inputTranspilationResults is SuccessfulFieldInputTranspilation<T>[];
export const throwOnFieldInputTranspilationsError: ThrowOnFieldInputTranspilationsErrorArgs =
<T>(
inputTranspilationResults: FieldInputTranspilationResult<T>[],
errorLabel: string,
) => {
const failedInputTranspilationErrors = inputTranspilationResults.flatMap(
(transpilationResult) =>
transpilationResult.status === 'fail' ? transpilationResult.error : [],
);
if (failedInputTranspilationErrors.length > 0) {
throw new MultipleMetadataValidationErrors(
failedInputTranspilationErrors,
errorLabel,
);
}
};