Fix flatFieldMetadata transpiler to fit real cache typing (#13529)

# Introduction
The cache is not accurately typed regarding the `fieldMetadataMaps` they
do not contain relations
In this way we:
- Created dedicated transpilation tools for cache manipulation
- Avoided recursivity in transpilation tools
- Fix and finalized the createMany fields metadata service v2
- Refactor the validator to handle validation :)

## What's next:
- Implem integration tests to make things run 🙃 
- migrate existing settings valdiation
- Finish the create object metadata service
- Handle update input transpilation and validation
This commit is contained in:
Paul Rastoin
2025-08-01 18:39:11 +02:00
committed by GitHub
parent dd47bfe6d5
commit b98d8c4d39
22 changed files with 384 additions and 124 deletions
@@ -24,18 +24,18 @@ type FromCreateFieldInputToFlatObjectMetadata = {
rawCreateFieldInput: CreateFieldInput;
existingFlatObjectMetadatas: FlatObjectMetadata[];
};
export type FlatFieldMetadataAndParentFlatObjectMetadata<
export type FlatFieldAndItsFlatObjectMetadata<
T extends FieldMetadataType = FieldMetadataType,
> = {
flatFieldMetadata: FlatFieldMetadata<T>;
parentFlatObjectMetadata: FlatObjectMetadata;
};
export const fromCreateFieldInputToFlatFieldMetadata = async ({
export const fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata = async ({
existingFlatObjectMetadatas,
rawCreateFieldInput,
}: FromCreateFieldInputToFlatObjectMetadata): Promise<
FlatFieldMetadataAndParentFlatObjectMetadata[]
FlatFieldAndItsFlatObjectMetadata[]
> => {
if (rawCreateFieldInput.isRemoteCreation) {
throw new FieldMetadataException(
@@ -1,32 +1,42 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils';
import {
FieldMetadataType,
RelationAndMorphRelationFieldMetadataType,
} from 'twenty-shared/types';
import { removePropertiesFromRecord } from 'twenty-shared/utils';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import {
FieldMetadataException,
FieldMetadataExceptionCode,
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
import {
FieldMetadataEntityRelationProperties,
FlatFieldMetadata,
fieldMetadataRelationProperties,
} from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { fromObjectMetadataEntityToFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-object-metadata-entity-to-flat-object-metadata.util';
import { FlatRelationTargetFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-relation-target-field-metadata.type';
import { isFieldMetadataEntityOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
import { fromFlatObjectMetadataToFlatObjectMetadataWithoutFields } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/from-flat-object-metadata-to-flat-object-metadata-without-fields.util';
import { fromObjectMetadataEntityToFlatObjectMetadataWithoutFields } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/from-object-metadata-entity-to-flat-object-metadata-without-fields.util';
export const fromFieldMetadataEntityToFlatRelationTargetFieldMetadata = (
fieldMetadataEntity: FieldMetadataEntity<RelationAndMorphRelationFieldMetadataType>,
): FlatRelationTargetFieldMetadata => {
return {
uniqueIdentifier: fieldMetadataEntity.standardId ?? fieldMetadataEntity.id,
...removePropertiesFromRecord(fieldMetadataEntity, [
'relationTargetObjectMetadata',
'relationTargetFieldMetadata',
]),
};
};
// TODO refactor this method should not be recursive but depth 1
export const fromFieldMetadataEntityToFlatFieldMetadata = <
T extends FieldMetadataType,
>(
fieldMetadataEntity: FieldMetadataEntity<T>,
/**
* private depth bottleneck
*/
_depth?: number,
// This is intended to be abstract
): FlatFieldMetadata => {
if (isDefined(_depth) && _depth > 1) {
throw new Error(
'fromFieldMetadataEntityToFlatFieldMetadata entering a possible infinite loop',
);
}
if (
isFieldMetadataEntityOfType(
fieldMetadataEntity,
@@ -44,20 +54,26 @@ export const fromFieldMetadataEntityToFlatFieldMetadata = <
FieldMetadataEntityRelationProperties
>(fieldMetadataEntity, fieldMetadataRelationProperties);
const newDepth = isDefined(_depth) ? _depth + 1 : 1;
const flatRelationTargetFieldMetadata =
fromFieldMetadataEntityToFlatFieldMetadata(
if (
!isFieldMetadataEntityOfType(
fieldMetadataEntity.relationTargetFieldMetadata,
FieldMetadataType.RELATION,
)
) {
throw new FieldMetadataException(
'Relation target field is not a field metadata type relation',
FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED,
);
}
const flatRelationTargetFieldMetadata =
fromFieldMetadataEntityToFlatRelationTargetFieldMetadata(
fieldMetadataEntity.relationTargetFieldMetadata,
newDepth,
);
const flatObjectTargetFieldMetadata =
fromObjectMetadataEntityToFlatObjectMetadata(
fieldMetadataEntity.relationTargetObjectMetadata,
);
const flatRelationTargetObjectMetadata =
fromFlatObjectMetadataToFlatObjectMetadataWithoutFields(
flatObjectTargetFieldMetadata,
fromObjectMetadataEntityToFlatObjectMetadataWithoutFields(
fieldMetadataEntity.relationTargetObjectMetadata,
);
return {
@@ -9,7 +9,7 @@ import {
} 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 { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { FlatFieldMetadataAndParentFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-metadata.util';
import { FlatFieldAndItsFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-and-its-flat-object-metadata.util';
import { getDefaultFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/get-default-flat-field-metadata-from-create-field-input.util';
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { computeMetadataNameFromLabel } from 'src/engine/metadata-modules/utils/validate-name-and-label-are-sync-or-throw.util';
@@ -24,7 +24,7 @@ export const fromRelationCreateFieldInputToFlatFieldMetadata = async ({
sourceParentFlatObjectMetadata,
createFieldInput,
}: FromRelationCreateFieldInputToFlatFieldMetadataArgs): Promise<
FlatFieldMetadataAndParentFlatObjectMetadata[]
FlatFieldAndItsFlatObjectMetadata[]
> => {
const { relationCreationPayload } = createFieldInput;
@@ -104,5 +104,5 @@ export const fromRelationCreateFieldInputToFlatFieldMetadata = async ({
flatFieldMetadata: targetFlatFieldMetadata,
parentFlatObjectMetadata: targetParentFlatObjectMetadata,
},
] satisfies FlatFieldMetadataAndParentFlatObjectMetadata<FieldMetadataType.RELATION>[];
] satisfies FlatFieldAndItsFlatObjectMetadata<FieldMetadataType.RELATION>[];
};