Update Name of relation fieldMetadata (#15749)

UpdateOne of a Relation that involves a CustomObject, because the
nameSingular needs to be updated in the fieldMetadata
- nameSingular and namePlural must be provided since they are necessary
for morph name computation
  - label sync should be false


Interesting files to look at:

-
packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/build-default-relation-flat-field-metadatas-for-custom-object.util.ts

- UPDATE =>
packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/rename-related-morph-field-on-object-names-update.util.ts
( also update relation indexes ) needs v2 refactor to handle field
relation name update
- CREATE =>
packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/build-default-relation-flat-field-metadatas-for-custom-object.util.ts
( handle morph instead of previous classic relation )
- DELETE => DONE

Edit:
closes https://github.com/twentyhq/core-team-issues/issues/1897

---------

Co-authored-by: prastoin <paul@twenty.com>
This commit is contained in:
Guillim
2025-11-23 21:45:30 +01:00
committed by GitHub
parent 6203b7b3e6
commit 4848bc03f3
24 changed files with 1282 additions and 405 deletions
@@ -88,6 +88,7 @@ export const fromUpdateObjectInputToFlatObjectMetadataAndRelatedFlatEntities =
fromFlatObjectMetadata: existingFlatObjectMetadata,
toFlatObjectMetadata,
flatFieldMetadataMaps,
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
flatIndexMaps,
flatViewFieldMaps,
flatViewMaps,
@@ -24,6 +24,7 @@ type HandleFlatObjectMetadataUpdateSideEffectArgs = FromTo<
Pick<
AllFlatEntityMaps,
| 'flatFieldMetadataMaps'
| 'flatObjectMetadataMaps'
| 'flatViewFieldMaps'
| 'flatIndexMaps'
| 'flatViewMaps'
@@ -32,20 +33,26 @@ type HandleFlatObjectMetadataUpdateSideEffectArgs = FromTo<
export const handleFlatObjectMetadataUpdateSideEffect = ({
flatIndexMaps,
flatFieldMetadataMaps,
flatObjectMetadataMaps,
flatViewFieldMaps,
flatViewMaps,
fromFlatObjectMetadata,
toFlatObjectMetadata,
}: HandleFlatObjectMetadataUpdateSideEffectArgs): FlatObjectMetadataUpdateSideEffects => {
const otherObjectFlatFieldMetadatasToUpdate =
const { morphRelatedFlatIndexesToUpdate, morphFlatFieldMetadatasToUpdate } =
fromFlatObjectMetadata.nameSingular !== toFlatObjectMetadata.nameSingular ||
fromFlatObjectMetadata.namePlural !== toFlatObjectMetadata.namePlural
? renameRelatedMorphFieldOnObjectNamesUpdate({
flatFieldMetadataMaps,
fromFlatObjectMetadata,
toFlatObjectMetadata,
flatObjectMetadataMaps,
flatIndexMaps,
})
: [];
: {
morphRelatedFlatIndexesToUpdate: [],
morphFlatFieldMetadatasToUpdate: [],
};
const flatIndexMetadatasToUpdate =
fromFlatObjectMetadata.nameSingular !== toFlatObjectMetadata.nameSingular
@@ -75,9 +82,12 @@ export const handleFlatObjectMetadataUpdateSideEffect = ({
};
return {
flatIndexMetadatasToUpdate,
flatIndexMetadatasToUpdate: [
...morphRelatedFlatIndexesToUpdate,
...flatIndexMetadatasToUpdate,
],
flatViewFieldsToCreate,
flatViewFieldsToUpdate,
otherObjectFlatFieldMetadatasToUpdate,
otherObjectFlatFieldMetadatasToUpdate: morphFlatFieldMetadatasToUpdate,
};
};
@@ -1,5 +1,3 @@
import { isDefined } from 'twenty-shared/utils';
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-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 FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
@@ -16,11 +14,11 @@ export const recomputeIndexAfterFlatObjectMetadataSingularNameUpdate = ({
updatedSingularName,
flatFieldMetadataMaps,
}: RecomputeIndexAfterFlatObjectMetadataSingularNameUpdateArgs): FlatIndexMetadata[] => {
const allRelatedFlatIndexMetadata = Object.values(flatIndexMaps.byId).filter(
(flatIndexMetadata): flatIndexMetadata is FlatIndexMetadata =>
isDefined(flatIndexMetadata) &&
flatIndexMetadata.objectMetadataId === existingFlatObjectMetadata.id,
);
const allRelatedFlatIndexMetadata =
findManyFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityIds: existingFlatObjectMetadata.indexMetadataIds,
flatEntityMaps: flatIndexMaps,
});
if (allRelatedFlatIndexMetadata.length === 0) {
return [];
@@ -7,69 +7,141 @@ import { computeMorphRelationFieldName } from 'twenty-shared/utils';
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
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 { findFieldRelatedIndexes } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-field-related-index.util';
import { recomputeIndexOnFlatFieldMetadataNameUpdate } from 'src/engine/metadata-modules/flat-field-metadata/utils/recompute-index-on-flat-field-metadata-name-update.util';
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';
import { getFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-object-metadata/utils/get-flat-object-metadata-many-to-one-target-morph-relation-flat-field-metadatas-or-throw.util';
import { getMorphNameFromMorphFieldMetadataName } from 'src/engine/metadata-modules/flat-object-metadata/utils/get-morph-name-from-morph-field-metadata-name.util';
type UpdateMorphFlatFieldNameArgs = FromTo<
FlatObjectMetadata,
'relationTargetFlatObjectMetadata'
> & {
fromMorphFlatFieldMetadata: FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>;
};
const updateMorphFlatFieldName = ({
fromMorphFlatFieldMetadata,
fromRelationTargetFlatObjectMetadata,
toRelationTargetFlatObjectMetadata,
}: UpdateMorphFlatFieldNameArgs): FlatFieldMetadata<FieldMetadataType.MORPH_RELATION> => {
const isManyToOneRelationType =
fromMorphFlatFieldMetadata.settings.relationType ===
RelationType.MANY_TO_ONE;
const initialMorphRelationFieldName = getMorphNameFromMorphFieldMetadataName({
morphRelationFlatFieldMetadata: fromMorphFlatFieldMetadata,
nameSingular: fromRelationTargetFlatObjectMetadata.nameSingular,
namePlural: fromRelationTargetFlatObjectMetadata.namePlural,
});
const newMorphFieldName = computeMorphRelationFieldName({
fieldName: initialMorphRelationFieldName,
relationType: fromMorphFlatFieldMetadata.settings.relationType,
targetObjectMetadataNameSingular:
toRelationTargetFlatObjectMetadata.nameSingular,
targetObjectMetadataNamePlural:
toRelationTargetFlatObjectMetadata.namePlural,
});
const newJoinColumnName = isManyToOneRelationType
? computeMorphOrRelationFieldJoinColumnName({
name: newMorphFieldName,
})
: undefined;
return {
...fromMorphFlatFieldMetadata,
name: newMorphFieldName,
settings: {
...fromMorphFlatFieldMetadata.settings,
joinColumnName: newJoinColumnName,
},
};
};
type RenameRelatedMorphFieldOnObjectNamesUpdateArgs = FromTo<
FlatObjectMetadata,
'flatObjectMetadata'
> &
Pick<AllFlatEntityMaps, 'flatFieldMetadataMaps'>;
// TODO We should recompute each index here too
Pick<
AllFlatEntityMaps,
'flatFieldMetadataMaps' | 'flatObjectMetadataMaps' | 'flatIndexMaps'
>;
type RenameRelatedMorphFieldOnObjectNamesUpdateReturnType = {
morphFlatFieldMetadatasToUpdate: FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[];
morphRelatedFlatIndexesToUpdate: FlatIndexMetadata[];
};
export const renameRelatedMorphFieldOnObjectNamesUpdate = ({
fromFlatObjectMetadata,
toFlatObjectMetadata,
flatFieldMetadataMaps,
}: RenameRelatedMorphFieldOnObjectNamesUpdateArgs): FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[] => {
flatObjectMetadataMaps,
flatIndexMaps,
}: RenameRelatedMorphFieldOnObjectNamesUpdateArgs): RenameRelatedMorphFieldOnObjectNamesUpdateReturnType => {
const objectFlatFieldMetadatas =
findManyFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityMaps: flatFieldMetadataMaps,
flatEntityIds: fromFlatObjectMetadata.fieldMetadataIds,
});
const manyToOneMorphRelationFlatFieldMetadatas =
const allMorphRelationFlatFieldMetadatas =
getFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrow({
flatFieldMetadataMaps,
objectFlatFieldMetadatas,
});
const updatedFlatFieldMetadatas =
manyToOneMorphRelationFlatFieldMetadatas.map(
(morphRelationFlatFieldMetadata) => {
const isManyToOneRelationType =
morphRelationFlatFieldMetadata.settings.relationType ===
RelationType.MANY_TO_ONE;
const initialMorphRelationFieldName =
getMorphNameFromMorphFieldMetadataName({
morphRelationFlatFieldMetadata,
nameSingular: fromFlatObjectMetadata.nameSingular,
namePlural: fromFlatObjectMetadata.namePlural,
});
const initialAccumulator: RenameRelatedMorphFieldOnObjectNamesUpdateReturnType =
{
morphRelatedFlatIndexesToUpdate: [],
morphFlatFieldMetadatasToUpdate: [],
};
const newMorphFieldName = computeMorphRelationFieldName({
fieldName: initialMorphRelationFieldName,
relationType: morphRelationFlatFieldMetadata.settings.relationType,
targetObjectMetadataNameSingular: toFlatObjectMetadata.nameSingular,
targetObjectMetadataNamePlural: toFlatObjectMetadata.namePlural,
return allMorphRelationFlatFieldMetadatas.reduce(
(acc, fromMorphFlatFieldMetadata) => {
const morphFlatFieldMetadataTo = updateMorphFlatFieldName({
fromMorphFlatFieldMetadata,
fromRelationTargetFlatObjectMetadata: fromFlatObjectMetadata,
toRelationTargetFlatObjectMetadata: toFlatObjectMetadata,
});
const morphFieldParentFlatObject =
findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityId: fromMorphFlatFieldMetadata.objectMetadataId,
flatEntityMaps: flatObjectMetadataMaps,
});
const newJoinColumnName = isManyToOneRelationType
? computeMorphOrRelationFieldJoinColumnName({
name: newMorphFieldName,
})
: undefined;
const relatedIndexes = findFieldRelatedIndexes({
flatFieldMetadata: fromMorphFlatFieldMetadata,
flatObjectMetadata: morphFieldParentFlatObject,
flatIndexMaps,
});
return {
...morphRelationFlatFieldMetadata,
name: newMorphFieldName,
settings: {
...morphRelationFlatFieldMetadata.settings,
joinColumnName: newJoinColumnName,
},
};
},
);
const flatIndexesToUpdate = recomputeIndexOnFlatFieldMetadataNameUpdate({
flatFieldMetadataMaps,
flatObjectMetadata: morphFieldParentFlatObject,
fromFlatFieldMetadata: fromMorphFlatFieldMetadata,
toFlatFieldMetadata: {
name: morphFlatFieldMetadataTo.name,
isUnique: morphFlatFieldMetadataTo.isUnique,
},
relatedFlatIndexMetadata: relatedIndexes,
});
return updatedFlatFieldMetadatas;
return {
...acc,
morphRelatedFlatIndexesToUpdate: [
...acc.morphRelatedFlatIndexesToUpdate,
...flatIndexesToUpdate,
],
morphFlatFieldMetadatasToUpdate: [
...acc.morphFlatFieldMetadatasToUpdate,
morphFlatFieldMetadataTo,
],
};
},
initialAccumulator,
);
};