Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
941e92f
todo
guillim Nov 10, 2025
2c9d73e
new fields to allow edition
guillim Nov 12, 2025
0727cad
Error handling in morph renaming inputs
guillim Nov 12, 2025
b413727
handle morph relation columns names
guillim Nov 12, 2025
6f3e9b7
add validation check to void conflicts on field names
guillim Nov 12, 2025
3ab81f5
forgot to add file
guillim Nov 12, 2025
baee0b8
greptile
guillim Nov 14, 2025
884c4e1
fix test
guillim Nov 17, 2025
6d7200b
failing to working test
guillim Nov 17, 2025
6706b79
fixing the issue of related relation migrations that should not been …
guillim Nov 17, 2025
cfc83f5
updating tests
guillim Nov 17, 2025
97f4d1b
snapshot update
guillim Nov 17, 2025
e9a023d
integration test for label sync
guillim Nov 19, 2025
abcb741
more efficient
guillim Nov 20, 2025
ef29eb8
fix : the name should not trigger a rename column. it's the settings …
guillim Nov 20, 2025
caaa20d
better typing
guillim Nov 20, 2025
da23172
no more need of context
guillim Nov 20, 2025
728a0ac
comments
guillim Nov 20, 2025
c566106
refactor test suite for renaming object
guillim Nov 20, 2025
60d476b
fix(server): on field update synchronize updates to other relations m…
prastoin Nov 20, 2025
c89d6a5
lint
prastoin Nov 20, 2025
6041148
test(server): nitpicks
prastoin Nov 20, 2025
d7c5cf7
test(server): adding coverage on field side effect
prastoin Nov 20, 2025
f69f700
test(server): functional test
prastoin Nov 20, 2025
baa4771
improve
prastoin Nov 20, 2025
648491c
fix
prastoin Nov 20, 2025
f3ab6a0
chore(server): remove validation from input transpilation
prastoin Nov 21, 2025
03d5ec9
chore(server): simplify existing index side effect impact on field up…
prastoin Nov 21, 2025
8c38b8e
remove index only if not field relation or morpgh
prastoin Nov 21, 2025
953fce1
feat(server): recompute index on morph field name update
prastoin Nov 21, 2025
ccae7f9
lint
prastoin Nov 21, 2025
09df81b
test(server): wip
prastoin Nov 21, 2025
bc5a53b
lint
prastoin Nov 21, 2025
6d49805
test(server): assert before update
prastoin Nov 21, 2025
85bbe43
test(server): finalize test one to many
prastoin Nov 21, 2025
f303018
lint
prastoin Nov 21, 2025
a506b4e
test(server): many to one
prastoin Nov 21, 2025
fc39e84
chore
prastoin Nov 21, 2025
78aa598
test(server): check double sides
prastoin Nov 21, 2025
103fe1b
test(server): fix
prastoin Nov 21, 2025
c17bc33
fix
prastoin Nov 21, 2025
47f3b18
lint
prastoin Nov 21, 2025
33d60e9
lint
prastoin Nov 21, 2025
d16a2dc
test(server): fix flaky snapshot sorting
prastoin Nov 21, 2025
69b4e15
fixup
prastoin Nov 21, 2025
0a13c7c
snapshot
prastoin Nov 21, 2025
6544c51
snapshot
prastoin Nov 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';

export const FLAT_FIELD_METADATA_MORPH_RELATION_EDITABLE_PROPERTIES_ON_SIBLING_MORPH_RELATION_UPDATE_CONSTANT =
{
custom: [
'defaultValue',
'description',
'icon',
'isActive',
'isLabelSyncedWithName',
'isUnique',
'label',
'options',
],
standard: [
'defaultValue',
'description',
'icon',
'isActive',
'label',
'options',
],
} as const satisfies Record<
'standard' | 'custom',
(keyof FlatFieldMetadata)[]
>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';

export const FLAT_FIELD_METADATA_RELATION_EDITABLE_PROPERTIES_ON_SIBLING_MORPH_OR_RELATION_UPDATE_CONSTANT =
['isActive'] as const satisfies (keyof FlatFieldMetadata)[];
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ export const FLAT_FIELD_METADATA_RELATION_PROPERTIES_TO_COMPARE = [
'isActive',
'standardOverrides',
'icon',
'name',
'settings',
] as const satisfies (typeof ALL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY.fieldMetadata.propertiesToCompare)[number][];
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,14 @@ export const computeFlatFieldMetadataRelatedFlatFieldMetadata = ({
FieldMetadataType.MORPH_RELATION,
)
) {
return findFlatFieldMetadatasRelatedToMorphRelationOrThrow({
flatFieldMetadata,
flatFieldMetadataMaps,
flatObjectMetadata,
});
const { morphRelationFlatFieldMetadatas, relationFlatFieldMetadatas } =
findFlatFieldMetadatasRelatedToMorphRelationOrThrow({
flatFieldMetadata,
flatFieldMetadataMaps,
flatObjectMetadata,
});

return [...morphRelationFlatFieldMetadatas, ...relationFlatFieldMetadatas];
}

return [];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { FieldMetadataType, type FromTo } from 'twenty-shared/types';

import { type UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input';
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
import { FLAT_FIELD_METADATA_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-editable-properties.constant';
import { FLAT_FIELD_METADATA_MORPH_RELATION_EDITABLE_PROPERTIES_ON_SIBLING_MORPH_RELATION_UPDATE_CONSTANT } from 'src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-morph-relation-editable-properties-on-sibling-morph-relation-update.constant';
import { FLAT_FIELD_METADATA_RELATION_EDITABLE_PROPERTIES_ON_SIBLING_MORPH_OR_RELATION_UPDATE_CONSTANT } from 'src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-relation-editable-properties-on-sibling-morph-or-relation-update.constant';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { findFlatFieldMetadatasRelatedToMorphRelationOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-flat-field-metadatas-related-to-morph-relation-or-throw.util';
import { findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-relation-flat-field-metadatas-target-flat-field-metadata-or-throw.util';
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
import { sanitizeRawUpdateFieldInput } from 'src/engine/metadata-modules/flat-field-metadata/utils/sanitize-raw-update-field-input';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { isStandardMetadata } from 'src/engine/metadata-modules/utils/is-standard-metadata.util';
import { mergeUpdateInExistingRecord } from 'src/utils/merge-update-in-existing-record.util';

type ComputeFlatFieldToUpdateAndRelatedFlatFieldToUpdateReturnType = {
flatFieldMetadataFromTo: FromTo<FlatFieldMetadata, 'flatFieldMetadata'>;
relatedFlatFieldMetadatasFromTo: FromTo<
FlatFieldMetadata,
'flatFieldMetadata'
>[];
};

type ComputeFlatFieldToUpdateAndRelatedFlatFieldToUpdateArgs = {
rawUpdateFieldInput: UpdateFieldInput;
fromFlatFieldMetadata: FlatFieldMetadata;
flatObjectMetadata: FlatObjectMetadata;
} & Pick<AllFlatEntityMaps, 'flatFieldMetadataMaps'>;
// Note: Standard override is way too complex we should land a smoother implemenentation once we standardize
// them across every flat entities
export const computeFlatFieldToUpdateAndRelatedFlatFieldToUpdate = ({
fromFlatFieldMetadata,
rawUpdateFieldInput,
flatFieldMetadataMaps,
flatObjectMetadata,
}: ComputeFlatFieldToUpdateAndRelatedFlatFieldToUpdateArgs): ComputeFlatFieldToUpdateAndRelatedFlatFieldToUpdateReturnType => {
const { standardOverrides, updatedEditableFieldProperties } =
sanitizeRawUpdateFieldInput({
existingFlatFieldMetadata: fromFlatFieldMetadata,
rawUpdateFieldInput,
});

const isStandardField = isStandardMetadata(fromFlatFieldMetadata);

const toFlatFieldMetadata = {
...mergeUpdateInExistingRecord({
existing: fromFlatFieldMetadata,
properties:
FLAT_FIELD_METADATA_EDITABLE_PROPERTIES[
isStandardField ? 'standard' : 'custom'
],
update: updatedEditableFieldProperties,
}),
standardOverrides,
};

if (
isFlatFieldMetadataOfType(fromFlatFieldMetadata, FieldMetadataType.RELATION)
) {
const relatedFlatFieldMetadataFrom =
findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow({
flatFieldMetadata: fromFlatFieldMetadata,
flatFieldMetadataMaps,
});

const relatedFlatFieldMetadataTo = mergeUpdateInExistingRecord({
existing: relatedFlatFieldMetadataFrom as FlatFieldMetadata,
properties:
FLAT_FIELD_METADATA_RELATION_EDITABLE_PROPERTIES_ON_SIBLING_MORPH_OR_RELATION_UPDATE_CONSTANT,
update: updatedEditableFieldProperties,
});

return {
flatFieldMetadataFromTo: {
fromFlatFieldMetadata,
toFlatFieldMetadata,
},
relatedFlatFieldMetadatasFromTo: [
{
fromFlatFieldMetadata: relatedFlatFieldMetadataFrom,
toFlatFieldMetadata: relatedFlatFieldMetadataTo,
},
],
};
}

if (
isFlatFieldMetadataOfType(
fromFlatFieldMetadata,
FieldMetadataType.MORPH_RELATION,
)
) {
const { morphRelationFlatFieldMetadatas, relationFlatFieldMetadatas } =
findFlatFieldMetadatasRelatedToMorphRelationOrThrow({
flatFieldMetadata: fromFlatFieldMetadata,
flatFieldMetadataMaps,
flatObjectMetadata,
});

const relatedMorphRelationFlatFieldMetdataTo =
morphRelationFlatFieldMetadatas.map<
FromTo<FlatFieldMetadata, 'flatFieldMetadata'>
>((relatedFlatFieldMetadataFrom) => {
const relatedMorphPropertiesToUpdateTo =
FLAT_FIELD_METADATA_MORPH_RELATION_EDITABLE_PROPERTIES_ON_SIBLING_MORPH_RELATION_UPDATE_CONSTANT[
isStandardField ? 'standard' : 'custom'
];
const relatedFlatFieldMetadataTo = {
...mergeUpdateInExistingRecord({
existing: relatedFlatFieldMetadataFrom as FlatFieldMetadata,
properties: relatedMorphPropertiesToUpdateTo,
update: updatedEditableFieldProperties,
}),
standardOverrides,
};

return {
fromFlatFieldMetadata: relatedFlatFieldMetadataFrom,
toFlatFieldMetadata: relatedFlatFieldMetadataTo,
};
});

const relatedRelationFlatFieldMetadataTo = relationFlatFieldMetadatas.map<
FromTo<FlatFieldMetadata, 'flatFieldMetadata'>
>((relatedFlatFieldMetadataFrom) => {
const relatedFlatFieldMetadataTo = mergeUpdateInExistingRecord({
existing: relatedFlatFieldMetadataFrom as FlatFieldMetadata,
properties:
FLAT_FIELD_METADATA_RELATION_EDITABLE_PROPERTIES_ON_SIBLING_MORPH_OR_RELATION_UPDATE_CONSTANT,
update: updatedEditableFieldProperties,
});

return {
fromFlatFieldMetadata: relatedFlatFieldMetadataFrom,
toFlatFieldMetadata: relatedFlatFieldMetadataTo,
};
});

return {
flatFieldMetadataFromTo: {
fromFlatFieldMetadata,
toFlatFieldMetadata,
},
relatedFlatFieldMetadatasFromTo: [
...relatedMorphRelationFlatFieldMetdataTo,
...relatedRelationFlatFieldMetadataTo,
],
};
}

return {
flatFieldMetadataFromTo: {
fromFlatFieldMetadata,
toFlatFieldMetadata,
},
relatedFlatFieldMetadatasFromTo: [],
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';

export const findFieldRelatedIndexes = ({
flatFieldMetadata,
flatObjectMetadata,
flatIndexMaps,
}: {
flatFieldMetadata: FlatFieldMetadata;
flatObjectMetadata: FlatObjectMetadata;
flatIndexMaps: FlatEntityMaps<FlatIndexMetadata>;
}): FlatIndexMetadata[] => {
const objectIndexes = findManyFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityMaps: flatIndexMaps,
flatEntityIds: flatObjectMetadata.indexMetadataIds,
});

return objectIndexes.filter((index) =>
index.flatIndexFieldMetadatas.some(
(indexField) => indexField.fieldMetadataId === flatFieldMetadata.id,
),
);
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { type FieldMetadataType } from 'twenty-shared/types';

import { type MorphOrRelationFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/morph-or-relation-field-metadata-type.type';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { findAllOthersMorphRelationFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-all-others-morph-relation-flat-field-metadatas-or-throw.util';
Expand All @@ -12,32 +11,35 @@ export type FindFlatFieldMetadatasRelatedToMorphRelationOrThrowArgs = {
flatFieldMetadata: FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>;
flatObjectMetadata: FlatObjectMetadata;
};

type FindFlatFieldMetadatasRelatedToMorphRelationOrThrowReturnType = {
morphRelationFlatFieldMetadatas: FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[];
relationFlatFieldMetadatas: FlatFieldMetadata<FieldMetadataType.RELATION>[];
};
export const findFlatFieldMetadatasRelatedToMorphRelationOrThrow = ({
flatFieldMetadataMaps,
flatFieldMetadata: morphRelationFlatFieldMetadata,
flatObjectMetadata,
}: FindFlatFieldMetadatasRelatedToMorphRelationOrThrowArgs): FlatFieldMetadata<MorphOrRelationFieldMetadataType>[] => {
const allMorphFlatFieldMetadatas =
}: FindFlatFieldMetadatasRelatedToMorphRelationOrThrowArgs): FindFlatFieldMetadatasRelatedToMorphRelationOrThrowReturnType => {
const morphRelationFlatFieldMetadatas =
findAllOthersMorphRelationFlatFieldMetadatasOrThrow({
flatFieldMetadata: morphRelationFlatFieldMetadata,
flatFieldMetadataMaps,
flatObjectMetadata,
});

return [
const relationFlatFieldMetadatas = [
morphRelationFlatFieldMetadata,
...allMorphFlatFieldMetadatas,
].flatMap((flatFieldMetadata) => {
const relationTargetFlatFieldMetadata =
findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow({
flatFieldMetadata,
flatFieldMetadataMaps,
});

if (flatFieldMetadata.id === morphRelationFlatFieldMetadata.id) {
return [relationTargetFlatFieldMetadata];
}
...morphRelationFlatFieldMetadatas,
].map((flatFieldMetadata) =>
findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow({
flatFieldMetadata,
flatFieldMetadataMaps,
}),
) as FlatFieldMetadata<FieldMetadataType.RELATION>[];

return [flatFieldMetadata, relationTargetFlatFieldMetadata];
});
return {
morphRelationFlatFieldMetadatas,
relationFlatFieldMetadatas,
};
};
Loading