Integration testing v2 enum field types fail and success path (#14010)

# Introduction
Migrating and improving performances of field enum integrations tests
success and failing tests cases to be using the new v2 api

## Discovered issue
When deleting an object in v1 it will leave related enums until the
object is re-created
Something not done anymore within the create in v2 but in the delete
operation
We should implem an upgrade command to remove such relicas

## Bugs
- Update/create default value multi select runner wrong sql query -> FIX
- Update default value multi select regression, we should allow option
without an id to be inserted -> FIX
- default value compare dynamic json stringify convertion or not in
compare tools for object and fields
This commit is contained in:
Paul Rastoin
2025-08-28 12:23:04 +02:00
committed by GitHub
parent 533d7fe49a
commit 59d90bedaa
72 changed files with 8236 additions and 1616 deletions
@@ -1,36 +1,28 @@
import diff from 'microdiff';
import { FieldMetadataType, type FromTo } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { isDefined, parseJson } from 'twenty-shared/utils';
import { FLAT_FIELD_METADATA_JSONB_PROPERTIES } from 'src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-jsonb-properties.constant';
import { FLAT_FIELD_METADATA_PROPERTIES_TO_COMPARE } from 'src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-properties-to-compare.constant';
import { FLAT_FIELD_METADATA_RELATION_PROPERTIES_TO_COMPARE } from 'src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-relation-properties-to-compare.constant';
import { type FlatFieldMetadataEntityJsonbProperties } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-jsonb-properties.type';
import { type FlatFieldMetadataPropertiesToCompare } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-properties-to-compare.type';
import { type FlatFieldMetadataRelationPropertiesToCompare } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-relation-properties-to-compare.type';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { isFlatFieldMetadataJsonbProperty } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-jsonb-property.util';
import { isStandardMetadata } from 'src/engine/metadata-modules/utils/is-standard-metadata.util';
import { type UpdateFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
const shouldNotOverrideDefaultValue = (type: FieldMetadataType) => {
return [
FieldMetadataType.BOOLEAN,
FieldMetadataType.SELECT,
FieldMetadataType.MULTI_SELECT,
FieldMetadataType.CURRENCY,
FieldMetadataType.PHONES,
FieldMetadataType.ADDRESS,
].includes(type);
};
type GetWorkspaceMigrationUpdateFieldActionArgs = FromTo<FlatFieldMetadata>;
type GetWorkspaceMigrationUpdateFieldActionArgs = FromTo<
FlatFieldMetadata,
'FlatFieldMetadata'
>;
/**
* This comparator handles update on colliding uniqueIdentifier flatFieldMetadata
*/
export const compareTwoFlatFieldMetadata = ({
from,
to,
fromFlatFieldMetadata,
toFlatFieldMetadata,
}: GetWorkspaceMigrationUpdateFieldActionArgs) => {
const transformMetadataForComparisonParameters = {
shouldIgnoreProperty: (
@@ -45,14 +37,6 @@ export const compareTwoFlatFieldMetadata = ({
return true;
}
if (
property === 'defaultValue' &&
isDefined(fieldMetadata.type) &&
shouldNotOverrideDefaultValue(fieldMetadata.type)
) {
return true;
}
// Remove below assertion when we authorize relation edition, see https://github.com/twentyhq/twenty/commit/39f6f3c4bb101272a9014e142a842d0801a3c33b
const isRelationFieldType =
isDefined(fieldMetadata.type) &&
@@ -80,11 +64,11 @@ export const compareTwoFlatFieldMetadata = ({
propertiesToStringify: FLAT_FIELD_METADATA_JSONB_PROPERTIES,
};
const fromCompare = transformMetadataForComparison(
from,
fromFlatFieldMetadata,
transformMetadataForComparisonParameters,
);
const toCompare = transformMetadataForComparison(
to,
toFlatFieldMetadata,
transformMetadataForComparisonParameters,
);
@@ -97,14 +81,15 @@ export const compareTwoFlatFieldMetadata = ({
case 'CHANGE': {
const { oldValue, path, value } = difference;
const property = path[0] as FlatFieldMetadataPropertiesToCompare;
const isJsonb = FLAT_FIELD_METADATA_JSONB_PROPERTIES.includes(
property as FlatFieldMetadataEntityJsonbProperties,
);
const isJsonb = isFlatFieldMetadataJsonbProperty({
flatFieldMetadata: toFlatFieldMetadata,
property,
});
if (isJsonb) {
return {
from: isDefined(oldValue) ? JSON.parse(oldValue) : oldValue,
to: isDefined(value) ? JSON.parse(value) : value,
from: parseJson(oldValue),
to: parseJson(value),
property,
};
}
@@ -100,6 +100,7 @@ export const fromCreateFieldInputToFlatFieldMetadatasToCreate = async ({
const options = (createFieldInput?.options ?? []).map<
FieldMetadataOptions<typeof createFieldInput.type>[number]
>((option) => ({
id: v4(),
...trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
option as FieldMetadataOptions<typeof createFieldInput.type>[number],
['label', 'value', 'id', 'color'],
@@ -3,6 +3,7 @@ import {
extractAndSanitizeObjectStringFields,
isDefined,
} from 'twenty-shared/utils';
import { v4 } from 'uuid';
import { FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/field-metadata/constants/field-metadata-standard-overrides-properties.constant';
import { type UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input';
@@ -132,14 +133,18 @@ export const fromUpdateFieldInputToFlatFieldMetadata = ({
const updatedFlatFieldMetadata = fieldMetadataEditableProperties.reduce(
(acc, property) => {
const isPropertyUpdated =
updatedEditableFieldProperties[property] !== undefined;
let newValue = updatedEditableFieldProperties[property];
if (property === 'options' && isDefined(newValue)) {
newValue = updatedEditableFieldProperties[property]?.map((option) => ({
id: v4(),
...option,
}));
}
return {
...acc,
...(isPropertyUpdated
? { [property]: updatedEditableFieldProperties[property] }
: {}),
...(newValue !== undefined ? { [property]: newValue } : {}),
};
},
relatedFlatFieldMetadata,
@@ -0,0 +1,34 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { FLAT_FIELD_METADATA_JSONB_PROPERTIES } from 'src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-jsonb-properties.constant';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { isFlatFieldMetadataEntityOfTypes } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-types.util';
export const isFlatFieldMetadataJsonbProperty = ({
flatFieldMetadata,
property,
}: {
flatFieldMetadata: FlatFieldMetadata;
property: keyof FlatFieldMetadata;
}): boolean => {
if (property === 'defaultValue') {
return isFlatFieldMetadataEntityOfTypes(flatFieldMetadata, [
FieldMetadataType.LINKS,
FieldMetadataType.PHONES,
FieldMetadataType.EMAILS,
FieldMetadataType.CURRENCY,
FieldMetadataType.FULL_NAME,
FieldMetadataType.ADDRESS,
FieldMetadataType.MULTI_SELECT,
FieldMetadataType.RAW_JSON,
FieldMetadataType.ACTOR,
FieldMetadataType.ARRAY,
]);
}
const isJsonB = FLAT_FIELD_METADATA_JSONB_PROPERTIES.includes(
property as (typeof FLAT_FIELD_METADATA_JSONB_PROPERTIES)[number],
);
return isJsonB;
};