CreateFieldInput transpilation to FlatFieldMetadata, FlatFieldMetadata validation (#13493)

# Introduction
Following https://github.com/twentyhq/twenty/pull/13420

What has been done:
- `CreateFieldInput` transpilation to `FlatFieldMetadata`
- `FlatFieldMetadata` validator service
- A lof of transpilation utils from `input` to `flatObject` or
`flatField`
- Created dedicated v2 api metadata services
- Introducing `inferDeletionFromMissingObjectFieldIndex` in the builder,
to avoid diffing every object and field of the current workspace we
allow only generating create/update migration operations, usefull when
passing by the api metadata


## We still need to in another PR:
- Implement a strong unit test coverage and critical functions and
services
- Finalize flat field metadata validation exception for `options`
`defaultValue` `settings` and `relations`
- Finalize `flatObjectMetadata` validation and v2 service refactor
- Plug the new service when feature flag is enabled
This commit is contained in:
Paul Rastoin
2025-07-30 15:08:11 +02:00
committed by GitHub
parent 0e4e3acaba
commit 29a4f4d685
41 changed files with 1346 additions and 351 deletions
@@ -23,7 +23,7 @@ export type FlatObjectMetadataPropertiesToCompare =
(typeof flatObjectMetadataPropertiesToCompare)[number];
/**
* This comparator handles update on colliding uniqueIdentifier flatObjectdMetadata
* This comparator handles update on colliding uniqueIdentifier flatObjectMetadata
*/
export const compareTwoFlatObjectMetadata = ({
from,
@@ -0,0 +1,69 @@
import { trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties } from 'twenty-shared/utils';
import { v4 } from 'uuid';
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input';
import {
ObjectMetadataException,
ObjectMetadataExceptionCode,
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
import { buildDefaultFlatFieldMetadataForCustomObject } from 'src/engine/metadata-modules/object-metadata/utils/build-default-fields-for-custom-object.util';
export const fromCreateObjectInputToFlatObjectMetadata = (
rawCreateObjectInput: CreateObjectInput,
): FlatObjectMetadata => {
if (rawCreateObjectInput.isRemote) {
throw new ObjectMetadataException(
'Remote objects are not supported',
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
);
}
const createObjectInput =
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
rawCreateObjectInput,
[
'description',
'icon',
'labelPlural',
'labelSingular',
'namePlural',
'nameSingular',
'shortcut',
],
);
const objectMetadataId = v4();
const baseCustomFlatFieldMetadatas =
buildDefaultFlatFieldMetadataForCustomObject({
objectMetadataId,
workspaceId: createObjectInput.workspaceId,
});
return {
description: createObjectInput.description ?? null,
flatFieldMetadatas: Object.values(baseCustomFlatFieldMetadatas),
flatIndexMetadatas: [],
icon: createObjectInput.icon ?? null,
id: objectMetadataId,
imageIdentifierFieldMetadataId: null,
isActive: true,
isAuditLogged: true,
isCustom: true,
isLabelSyncedWithName: createObjectInput.isLabelSyncedWithName ?? false,
isRemote: false,
isSearchable: true,
isSystem: false,
labelIdentifierFieldMetadataId: baseCustomFlatFieldMetadatas.nameField.id,
labelPlural: createObjectInput.labelPlural ?? null,
labelSingular: createObjectInput.labelSingular ?? null,
namePlural: createObjectInput.namePlural ?? null,
nameSingular: createObjectInput.nameSingular ?? null,
shortcut: createObjectInput.shortcut ?? null,
standardId: null,
standardOverrides: null,
uniqueIdentifier: objectMetadataId,
targetTableName: 'DEPRECATED',
workspaceId: createObjectInput.workspaceId,
};
};
@@ -8,7 +8,7 @@ import { mergeTwoFlatFieldMetadatas } from 'src/engine/metadata-modules/flat-fie
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { ToMerge } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/to-merge.type';
export const mergeTwoFlatFieldObjectMetadatas = ({
export const mergeTwoFlatObjectMetadatas = ({
destFlatObjectMetadatas,
toMergeFlatObjectMetadatas,
}: ToMerge<FlatObjectMetadata[], 'object'>): FlatObjectMetadata[] => {