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:
+26
@@ -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)[]
|
||||
>;
|
||||
+4
@@ -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)[];
|
||||
+2
@@ -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][];
|
||||
|
||||
+8
-5
@@ -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 [];
|
||||
|
||||
+159
@@ -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: [],
|
||||
};
|
||||
};
|
||||
+26
@@ -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,
|
||||
),
|
||||
);
|
||||
};
|
||||
+19
-17
@@ -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';
|
||||
@@ -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,
|
||||
});
|
||||
...morphRelationFlatFieldMetadatas,
|
||||
].map((flatFieldMetadata) =>
|
||||
findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow({
|
||||
flatFieldMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
}),
|
||||
) as FlatFieldMetadata<FieldMetadataType.RELATION>[];
|
||||
|
||||
if (flatFieldMetadata.id === morphRelationFlatFieldMetadata.id) {
|
||||
return [relationTargetFlatFieldMetadata];
|
||||
}
|
||||
|
||||
return [flatFieldMetadata, relationTargetFlatFieldMetadata];
|
||||
});
|
||||
return {
|
||||
morphRelationFlatFieldMetadatas,
|
||||
relationFlatFieldMetadatas,
|
||||
};
|
||||
};
|
||||
|
||||
+85
-121
@@ -3,7 +3,6 @@ import {
|
||||
extractAndSanitizeObjectStringFields,
|
||||
isDefined,
|
||||
} from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input';
|
||||
import {
|
||||
@@ -12,18 +11,14 @@ import {
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { FLAT_FIELD_METADATA_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-editable-properties.constant';
|
||||
import { type FieldInputTranspilationResult } from 'src/engine/metadata-modules/flat-field-metadata/types/field-input-transpilation-result.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { computeFlatFieldMetadataRelatedFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/compute-flat-field-metadata-related-flat-field-metadata.util';
|
||||
import { computeFlatFieldToUpdateAndRelatedFlatFieldToUpdate } from 'src/engine/metadata-modules/flat-field-metadata/utils/compute-flat-field-to-update-and-related-flat-field-to-update.util';
|
||||
import {
|
||||
FLAT_FIELD_METADATA_UPDATE_EMPTY_SIDE_EFFECTS,
|
||||
type FlatFieldMetadataUpdateSideEffects,
|
||||
handleFlatFieldMetadataUpdateSideEffect,
|
||||
} from 'src/engine/metadata-modules/flat-field-metadata/utils/handle-flat-field-metadata-update-side-effect.util';
|
||||
import { sanitizeRawUpdateFieldInput } from 'src/engine/metadata-modules/flat-field-metadata/utils/sanitize-raw-update-field-input';
|
||||
import { isStandardMetadata } from 'src/engine/metadata-modules/utils/is-standard-metadata.util';
|
||||
import { mergeUpdateInExistingRecord } from 'src/utils/merge-update-in-existing-record.util';
|
||||
|
||||
type FromUpdateFieldInputToFlatFieldMetadataArgs = {
|
||||
updateFieldInput: UpdateFieldInput;
|
||||
@@ -73,13 +68,6 @@ export const fromUpdateFieldInputToFlatFieldMetadata = ({
|
||||
};
|
||||
}
|
||||
|
||||
const isStandardField = isStandardMetadata(existingFlatFieldMetadataToUpdate);
|
||||
const { standardOverrides, updatedEditableFieldProperties } =
|
||||
sanitizeRawUpdateFieldInput({
|
||||
existingFlatFieldMetadata: existingFlatFieldMetadataToUpdate,
|
||||
rawUpdateFieldInput,
|
||||
});
|
||||
|
||||
const flatObjectMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: existingFlatFieldMetadataToUpdate.objectMetadataId,
|
||||
flatEntityMaps: existingFlatObjectMetadataMaps,
|
||||
@@ -92,124 +80,100 @@ export const fromUpdateFieldInputToFlatFieldMetadata = ({
|
||||
);
|
||||
}
|
||||
|
||||
const relatedFlatFieldMetadatasToUpdate =
|
||||
computeFlatFieldMetadataRelatedFlatFieldMetadata({
|
||||
flatFieldMetadata: existingFlatFieldMetadataToUpdate,
|
||||
const { flatFieldMetadataFromTo, relatedFlatFieldMetadatasFromTo } =
|
||||
computeFlatFieldToUpdateAndRelatedFlatFieldToUpdate({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
fromFlatFieldMetadata: existingFlatFieldMetadataToUpdate,
|
||||
rawUpdateFieldInput,
|
||||
});
|
||||
|
||||
const flatFieldMetadatasToUpdate = [
|
||||
existingFlatFieldMetadataToUpdate,
|
||||
...relatedFlatFieldMetadatasToUpdate,
|
||||
];
|
||||
|
||||
const initialAccumulator: FlatFieldMetadataAndIndexToUpdate = {
|
||||
...structuredClone(FLAT_FIELD_METADATA_UPDATE_EMPTY_SIDE_EFFECTS),
|
||||
flatFieldMetadatasToUpdate: [],
|
||||
};
|
||||
|
||||
updatedEditableFieldProperties.options = !isDefined(
|
||||
updatedEditableFieldProperties.options,
|
||||
)
|
||||
? updatedEditableFieldProperties.options
|
||||
: updatedEditableFieldProperties.options.map((option) => ({
|
||||
id: v4(),
|
||||
...option,
|
||||
}));
|
||||
const optimisticiallyUpdatedFlatFieldMetadatas = [
|
||||
flatFieldMetadataFromTo,
|
||||
...relatedFlatFieldMetadatasFromTo,
|
||||
].reduce<FlatFieldMetadataAndIndexToUpdate>(
|
||||
(accumulator, { fromFlatFieldMetadata, toFlatFieldMetadata }) => {
|
||||
const {
|
||||
flatViewGroupsToCreate,
|
||||
flatViewGroupsToDelete,
|
||||
flatViewGroupsToUpdate,
|
||||
flatIndexMetadatasToUpdate,
|
||||
flatViewFiltersToDelete,
|
||||
flatViewFiltersToUpdate,
|
||||
flatIndexMetadatasToCreate,
|
||||
flatIndexMetadatasToDelete,
|
||||
flatViewsToDelete,
|
||||
flatViewFieldsToDelete,
|
||||
flatViewsToUpdate,
|
||||
} = handleFlatFieldMetadataUpdateSideEffect({
|
||||
flatViewFilterMaps,
|
||||
flatViewGroupMaps,
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
fromFlatFieldMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
flatIndexMaps,
|
||||
toFlatFieldMetadata,
|
||||
flatViewMaps,
|
||||
flatViewFieldMaps,
|
||||
});
|
||||
|
||||
const optimisticiallyUpdatedFlatFieldMetadatas =
|
||||
flatFieldMetadatasToUpdate.reduce<FlatFieldMetadataAndIndexToUpdate>(
|
||||
(accumulator, fromFlatFieldMetadata) => {
|
||||
const toFlatFieldMetadata = {
|
||||
...mergeUpdateInExistingRecord({
|
||||
existing: fromFlatFieldMetadata,
|
||||
properties:
|
||||
FLAT_FIELD_METADATA_EDITABLE_PROPERTIES[
|
||||
isStandardField ? 'standard' : 'custom'
|
||||
],
|
||||
update: updatedEditableFieldProperties,
|
||||
}),
|
||||
standardOverrides,
|
||||
};
|
||||
|
||||
const {
|
||||
flatViewGroupsToCreate,
|
||||
flatViewGroupsToDelete,
|
||||
flatViewGroupsToUpdate,
|
||||
flatIndexMetadatasToUpdate,
|
||||
flatViewFiltersToDelete,
|
||||
flatViewFiltersToUpdate,
|
||||
flatIndexMetadatasToCreate,
|
||||
flatIndexMetadatasToDelete,
|
||||
flatViewsToDelete,
|
||||
flatViewFieldsToDelete,
|
||||
flatViewsToUpdate,
|
||||
} = handleFlatFieldMetadataUpdateSideEffect({
|
||||
flatViewFilterMaps,
|
||||
flatViewGroupMaps,
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
fromFlatFieldMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
flatIndexMaps,
|
||||
return {
|
||||
flatFieldMetadatasToUpdate: [
|
||||
...accumulator.flatFieldMetadatasToUpdate,
|
||||
toFlatFieldMetadata,
|
||||
flatViewMaps,
|
||||
flatViewFieldMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
flatFieldMetadatasToUpdate: [
|
||||
...accumulator.flatFieldMetadatasToUpdate,
|
||||
toFlatFieldMetadata,
|
||||
],
|
||||
flatIndexMetadatasToUpdate: [
|
||||
...accumulator.flatIndexMetadatasToUpdate,
|
||||
...flatIndexMetadatasToUpdate,
|
||||
],
|
||||
flatViewFiltersToDelete: [
|
||||
...accumulator.flatViewFiltersToDelete,
|
||||
...flatViewFiltersToDelete,
|
||||
],
|
||||
flatViewFiltersToUpdate: [
|
||||
...accumulator.flatViewFiltersToUpdate,
|
||||
...flatViewFiltersToUpdate,
|
||||
],
|
||||
flatViewGroupsToCreate: [
|
||||
...accumulator.flatViewGroupsToCreate,
|
||||
...flatViewGroupsToCreate,
|
||||
],
|
||||
flatViewGroupsToDelete: [
|
||||
...accumulator.flatViewGroupsToDelete,
|
||||
...flatViewGroupsToDelete,
|
||||
],
|
||||
flatViewGroupsToUpdate: [
|
||||
...accumulator.flatViewGroupsToUpdate,
|
||||
...flatViewGroupsToUpdate,
|
||||
],
|
||||
flatIndexMetadatasToDelete: [
|
||||
...accumulator.flatIndexMetadatasToDelete,
|
||||
...flatIndexMetadatasToDelete,
|
||||
],
|
||||
flatIndexMetadatasToCreate: [
|
||||
...accumulator.flatIndexMetadatasToCreate,
|
||||
...flatIndexMetadatasToCreate,
|
||||
],
|
||||
flatViewsToDelete: [
|
||||
...accumulator.flatViewsToDelete,
|
||||
...flatViewsToDelete,
|
||||
],
|
||||
flatViewFieldsToDelete: [
|
||||
...accumulator.flatViewFieldsToDelete,
|
||||
...flatViewFieldsToDelete,
|
||||
],
|
||||
flatViewsToUpdate: [
|
||||
...accumulator.flatViewsToUpdate,
|
||||
...flatViewsToUpdate,
|
||||
],
|
||||
};
|
||||
},
|
||||
initialAccumulator,
|
||||
);
|
||||
],
|
||||
flatIndexMetadatasToUpdate: [
|
||||
...accumulator.flatIndexMetadatasToUpdate,
|
||||
...flatIndexMetadatasToUpdate,
|
||||
],
|
||||
flatViewFiltersToDelete: [
|
||||
...accumulator.flatViewFiltersToDelete,
|
||||
...flatViewFiltersToDelete,
|
||||
],
|
||||
flatViewFiltersToUpdate: [
|
||||
...accumulator.flatViewFiltersToUpdate,
|
||||
...flatViewFiltersToUpdate,
|
||||
],
|
||||
flatViewGroupsToCreate: [
|
||||
...accumulator.flatViewGroupsToCreate,
|
||||
...flatViewGroupsToCreate,
|
||||
],
|
||||
flatViewGroupsToDelete: [
|
||||
...accumulator.flatViewGroupsToDelete,
|
||||
...flatViewGroupsToDelete,
|
||||
],
|
||||
flatViewGroupsToUpdate: [
|
||||
...accumulator.flatViewGroupsToUpdate,
|
||||
...flatViewGroupsToUpdate,
|
||||
],
|
||||
flatIndexMetadatasToDelete: [
|
||||
...accumulator.flatIndexMetadatasToDelete,
|
||||
...flatIndexMetadatasToDelete,
|
||||
],
|
||||
flatIndexMetadatasToCreate: [
|
||||
...accumulator.flatIndexMetadatasToCreate,
|
||||
...flatIndexMetadatasToCreate,
|
||||
],
|
||||
flatViewsToDelete: [
|
||||
...accumulator.flatViewsToDelete,
|
||||
...flatViewsToDelete,
|
||||
],
|
||||
flatViewFieldsToDelete: [
|
||||
...accumulator.flatViewFieldsToDelete,
|
||||
...flatViewFieldsToDelete,
|
||||
],
|
||||
flatViewsToUpdate: [
|
||||
...accumulator.flatViewsToUpdate,
|
||||
...flatViewsToUpdate,
|
||||
],
|
||||
};
|
||||
},
|
||||
initialAccumulator,
|
||||
);
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
|
||||
+7
-26
@@ -1,11 +1,11 @@
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/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 { generateIndexForFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/generate-index-for-flat-field-metadata.util';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.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';
|
||||
@@ -52,7 +52,7 @@ export const handleIndexChangesDuringFieldUpdate = ({
|
||||
flatEntityId: fromFlatFieldMetadata.objectMetadataId,
|
||||
});
|
||||
|
||||
const relatedIndexes = findRelatedIndexes({
|
||||
const relatedIndexes = findFieldRelatedIndexes({
|
||||
flatFieldMetadata: fromFlatFieldMetadata,
|
||||
flatObjectMetadata,
|
||||
flatIndexMaps,
|
||||
@@ -81,27 +81,6 @@ const hasIndexRelevantChanges = ({
|
||||
fromFlatFieldMetadata.name !== toFlatFieldMetadata.name ||
|
||||
fromFlatFieldMetadata.isUnique !== toFlatFieldMetadata.isUnique;
|
||||
|
||||
const findRelatedIndexes = ({
|
||||
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,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const handleNoExistingIndexes = ({
|
||||
toFlatFieldMetadata,
|
||||
flatObjectMetadata,
|
||||
@@ -139,7 +118,10 @@ const handleExistingIndexes = ({
|
||||
FlatFieldMetadata,
|
||||
'flatFieldMetadata'
|
||||
>): FieldMetadataUpdateIndexSideEffect => {
|
||||
if (toFlatFieldMetadata.isUnique === false) {
|
||||
if (
|
||||
toFlatFieldMetadata.isUnique === false &&
|
||||
!isMorphOrRelationFlatFieldMetadata(fromFlatFieldMetadata)
|
||||
) {
|
||||
const expectedUniqueIndex = generateIndexForFlatFieldMetadata({
|
||||
flatFieldMetadata: {
|
||||
...fromFlatFieldMetadata,
|
||||
@@ -160,7 +142,6 @@ const handleExistingIndexes = ({
|
||||
: [],
|
||||
};
|
||||
}
|
||||
|
||||
const updatedIndexes = recomputeIndexOnFlatFieldMetadataNameUpdate({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
|
||||
+4
-6
@@ -42,13 +42,11 @@ export const recomputeIndexOnFlatFieldMetadataNameUpdate = ({
|
||||
},
|
||||
);
|
||||
|
||||
return relatedFlatIndexMetadata.map<FlatIndexMetadata>((flatIndex) => {
|
||||
const newIndex = generateFlatIndexMetadataWithNameOrThrow({
|
||||
return relatedFlatIndexMetadata.map((flatIndex) =>
|
||||
generateFlatIndexMetadataWithNameOrThrow({
|
||||
flatIndex,
|
||||
flatObjectMetadata,
|
||||
objectFlatFieldMetadatas: optimisticObjectFlatFieldMetadatas,
|
||||
});
|
||||
|
||||
return newIndex;
|
||||
});
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
+10
@@ -2,6 +2,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';
|
||||
@@ -33,6 +34,15 @@ export const sanitizeRawUpdateFieldInput = ({
|
||||
],
|
||||
);
|
||||
|
||||
updatedEditableFieldProperties.options = !isDefined(
|
||||
updatedEditableFieldProperties.options,
|
||||
)
|
||||
? updatedEditableFieldProperties.options
|
||||
: updatedEditableFieldProperties.options.map((option) => ({
|
||||
id: v4(),
|
||||
...option,
|
||||
}));
|
||||
|
||||
if (!isStandardField) {
|
||||
return {
|
||||
updatedEditableFieldProperties,
|
||||
|
||||
+1
@@ -88,6 +88,7 @@ export const fromUpdateObjectInputToFlatObjectMetadataAndRelatedFlatEntities =
|
||||
fromFlatObjectMetadata: existingFlatObjectMetadata,
|
||||
toFlatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
flatIndexMaps,
|
||||
flatViewFieldMaps,
|
||||
flatViewMaps,
|
||||
|
||||
+14
-4
@@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
+5
-7
@@ -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 [];
|
||||
|
||||
+109
-37
@@ -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,
|
||||
);
|
||||
};
|
||||
|
||||
+2
@@ -108,6 +108,7 @@ export class FlatFieldMetadataValidatorService {
|
||||
});
|
||||
}
|
||||
|
||||
// Should be moved in relation field validator
|
||||
if (isMorphOrRelationFlatFieldMetadata(flatFieldMetadataToValidate)) {
|
||||
const relationNonEditableUpdatedProperties = updates.flatMap(
|
||||
({ property }) =>
|
||||
@@ -126,6 +127,7 @@ export class FlatFieldMetadataValidatorService {
|
||||
});
|
||||
}
|
||||
}
|
||||
///
|
||||
|
||||
if (updates.some((update) => update.property === 'name')) {
|
||||
validationResult.errors.push(
|
||||
|
||||
+17
-24
@@ -211,19 +211,26 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
optimisticFlatFieldMetadata.options = update.to ?? [];
|
||||
}
|
||||
if (
|
||||
isMorphOrRelationFlatFieldMetadata(optimisticFlatFieldMetadata) &&
|
||||
update.property === 'settings'
|
||||
isPropertyUpdate(update, 'settings') &&
|
||||
isDefined(update.from?.joinColumnName) &&
|
||||
isDefined(update.to?.joinColumnName) &&
|
||||
update.from.joinColumnName !== update.to.joinColumnName &&
|
||||
isMorphOrRelationFlatFieldMetadata(optimisticFlatFieldMetadata)
|
||||
) {
|
||||
await this.handleMorphOrRelationSettingsUpdate({
|
||||
flatFieldMetadata: optimisticFlatFieldMetadata,
|
||||
await this.workspaceSchemaManagerService.columnManager.renameColumn({
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableName,
|
||||
update: update as PropertyUpdate<
|
||||
FlatFieldMetadata<MorphOrRelationFieldMetadataType>,
|
||||
'settings'
|
||||
>,
|
||||
oldColumnName: update.from.joinColumnName,
|
||||
newColumnName: update.to.joinColumnName,
|
||||
});
|
||||
optimisticFlatFieldMetadata = {
|
||||
...optimisticFlatFieldMetadata,
|
||||
settings: {
|
||||
...optimisticFlatFieldMetadata.settings,
|
||||
joinColumnName: update.to.joinColumnName,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -263,20 +270,6 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
newColumnName: toCompositeColumnName,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (isMorphOrRelationFlatFieldMetadata(flatFieldMetadata)) {
|
||||
throw new WorkspaceMigrationRunnerException(
|
||||
'Relation field metadata name update is not supported yet',
|
||||
WorkspaceMigrationRunnerExceptionCode.NOT_SUPPORTED,
|
||||
);
|
||||
}
|
||||
await this.workspaceSchemaManagerService.columnManager.renameColumn({
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableName,
|
||||
oldColumnName: update.from,
|
||||
newColumnName: update.to,
|
||||
});
|
||||
}
|
||||
|
||||
const enumOperations = collectEnumOperationsForField({
|
||||
@@ -440,8 +433,8 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
}
|
||||
|
||||
await this.workspaceSchemaManagerService.columnManager.renameColumn({
|
||||
newColumnName: fromJoinColumnName,
|
||||
oldColumnName: toJoinColumnName,
|
||||
oldColumnName: fromJoinColumnName,
|
||||
newColumnName: toJoinColumnName,
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableName,
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`updateOne FieldMetadataService morph relation fields v2 It should update all morph related flat field metadata and their related field allowing its deletion 1`] = `
|
||||
{
|
||||
"description": [
|
||||
"Description for all",
|
||||
],
|
||||
"isActive": [
|
||||
true,
|
||||
],
|
||||
"label": [
|
||||
"field label",
|
||||
],
|
||||
"name": [
|
||||
"fieldNameOpportunityForMorphRelationSecond",
|
||||
"fieldNamePersonForMorphRelationSecond",
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`updateOne FieldMetadataService morph relation fields v2 It should update all morph related flat field metadata and their related field allowing its deletion 2`] = `
|
||||
{
|
||||
"description": [
|
||||
null,
|
||||
],
|
||||
"isActive": [
|
||||
true,
|
||||
],
|
||||
"label": [
|
||||
"tata",
|
||||
"toto",
|
||||
],
|
||||
"name": [
|
||||
"tata",
|
||||
"toto",
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`updateOne FieldMetadataService morph relation fields v2 It should update all morph related flat field metadata and their related field allowing its deletion 3`] = `
|
||||
{
|
||||
"description": [
|
||||
"new description",
|
||||
],
|
||||
"isActive": [
|
||||
false,
|
||||
],
|
||||
"label": [
|
||||
"new label",
|
||||
],
|
||||
"name": [
|
||||
"fieldNameOpportunityForMorphRelationSecond",
|
||||
"fieldNamePersonForMorphRelationSecond",
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`updateOne FieldMetadataService morph relation fields v2 It should update all morph related flat field metadata and their related field allowing its deletion 4`] = `
|
||||
{
|
||||
"description": [
|
||||
null,
|
||||
],
|
||||
"isActive": [
|
||||
false,
|
||||
],
|
||||
"label": [
|
||||
"tata",
|
||||
"toto",
|
||||
],
|
||||
"name": [
|
||||
"tata",
|
||||
"toto",
|
||||
],
|
||||
}
|
||||
`;
|
||||
+224
-7
@@ -1,13 +1,50 @@
|
||||
import { type DeepPartial } from 'ai';
|
||||
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
||||
import { deleteOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/delete-one-field-metadata.util';
|
||||
import { findManyFieldsMetadata } from 'test/integration/metadata/suites/field-metadata/utils/find-many-fields-metadata.util';
|
||||
import { updateOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata.util';
|
||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||
import { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util';
|
||||
import { jestExpectToBeDefined } from 'test/utils/jest-expect-to-be-defined.util.test';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { type FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
||||
import { type RelationDTO } from 'src/engine/metadata-modules/field-metadata/dtos/relation.dto';
|
||||
|
||||
type AggregatedFieldMetadataDto = {
|
||||
description: (string | null)[];
|
||||
name: string[];
|
||||
label: string[];
|
||||
isActive: boolean[];
|
||||
};
|
||||
const aggregateFieldMetadata = (
|
||||
fieldMetadataDtos: { node: FieldMetadataDTO }[],
|
||||
) => {
|
||||
const initialAcc: AggregatedFieldMetadataDto = {
|
||||
description: [],
|
||||
name: [],
|
||||
label: [],
|
||||
isActive: [],
|
||||
};
|
||||
|
||||
return fieldMetadataDtos.reduce((acc, { node: fieldMetadataDto }) => {
|
||||
return {
|
||||
...acc,
|
||||
description: [
|
||||
...new Set([...acc.description, fieldMetadataDto.description]),
|
||||
].sort(),
|
||||
isActive: [
|
||||
...new Set([...acc.isActive, fieldMetadataDto.isActive]),
|
||||
].sort(),
|
||||
label: [...new Set([...acc.label, fieldMetadataDto.label])].sort(),
|
||||
name: [...new Set([...acc.name, fieldMetadataDto.name])].sort(),
|
||||
};
|
||||
}, initialAcc);
|
||||
};
|
||||
|
||||
describe('updateOne FieldMetadataService morph relation fields v2', () => {
|
||||
let createdObjectMetadataPersonId: string;
|
||||
let createdObjectMetadataOpportunityId: string;
|
||||
@@ -97,6 +134,7 @@ describe('updateOne FieldMetadataService morph relation fields v2', () => {
|
||||
label: 'field label',
|
||||
name: 'fieldName',
|
||||
objectMetadataId: createdObjectMetadataCompanyId,
|
||||
description: 'Description for all',
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
morphRelationsCreationPayload: [
|
||||
{
|
||||
@@ -119,7 +157,125 @@ describe('updateOne FieldMetadataService morph relation fields v2', () => {
|
||||
createdFieldMetadataId = rawCreateOneField.id;
|
||||
});
|
||||
|
||||
it('It should update all morph related flat field metadata allowing its deletion', async () => {
|
||||
it('It should update all morph related flat field metadata and their related field allowing its deletion', async () => {
|
||||
// SETUP
|
||||
const { fields: findResult } = await findManyFieldsMetadata({
|
||||
input: {
|
||||
filter: { id: { eq: createdFieldMetadataId } },
|
||||
paging: { first: 1 },
|
||||
},
|
||||
expectToFail: false,
|
||||
gqlFields: `
|
||||
id
|
||||
type
|
||||
name
|
||||
label
|
||||
isLabelSyncedWithName
|
||||
settings
|
||||
object {
|
||||
id
|
||||
nameSingular
|
||||
}
|
||||
morphRelations {
|
||||
type
|
||||
targetFieldMetadata {
|
||||
id
|
||||
type
|
||||
}
|
||||
sourceFieldMetadata {
|
||||
id
|
||||
type
|
||||
}
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
expect(findResult.length).toBe(1);
|
||||
const createdMorphFromResult = findResult[0].node as FieldMetadataDTO & {
|
||||
morphRelations: RelationDTO[];
|
||||
};
|
||||
|
||||
jestExpectToBeDefined(createdMorphFromResult);
|
||||
|
||||
createdMorphFromResult.morphRelations.map((relationDto) =>
|
||||
expect(relationDto).toMatchObject<DeepPartial<RelationDTO>>({
|
||||
sourceFieldMetadata: {
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
},
|
||||
targetFieldMetadata: {
|
||||
type: FieldMetadataType.RELATION,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const allMorphFieldIds = createdMorphFromResult.morphRelations.map(
|
||||
({ sourceFieldMetadata }) => sourceFieldMetadata.id,
|
||||
);
|
||||
|
||||
const allRelationFieldIds = createdMorphFromResult.morphRelations.map(
|
||||
({ targetFieldMetadata }) => targetFieldMetadata.id,
|
||||
);
|
||||
|
||||
/// ASSERT
|
||||
{
|
||||
const morphRelationFieldsBeforeUpdate = (await findManyFieldsMetadata({
|
||||
input: {
|
||||
filter: { id: { in: allMorphFieldIds } },
|
||||
paging: { first: allMorphFieldIds.length },
|
||||
},
|
||||
gqlFields: `
|
||||
id
|
||||
name
|
||||
description
|
||||
label
|
||||
isActive
|
||||
`,
|
||||
expectToFail: false,
|
||||
})) as { fields: { node: FieldMetadataDTO }[] };
|
||||
|
||||
expect(morphRelationFieldsBeforeUpdate.fields.length).toBe(
|
||||
allMorphFieldIds.length,
|
||||
);
|
||||
const aggregatedMorphFieldMetadataDtos = aggregateFieldMetadata(
|
||||
morphRelationFieldsBeforeUpdate.fields,
|
||||
);
|
||||
|
||||
expect(aggregatedMorphFieldMetadataDtos).toMatchSnapshot();
|
||||
expect(
|
||||
aggregatedMorphFieldMetadataDtos,
|
||||
).toMatchObject<AggregatedFieldMetadataDto>({
|
||||
description: ['Description for all'],
|
||||
isActive: [true],
|
||||
label: ['field label'],
|
||||
name: [expect.any(String), expect.any(String)],
|
||||
});
|
||||
const relationFieldsBeforeUpdate = (await findManyFieldsMetadata({
|
||||
input: {
|
||||
filter: { id: { in: allRelationFieldIds } },
|
||||
paging: { first: allRelationFieldIds.length },
|
||||
},
|
||||
gqlFields: `
|
||||
id
|
||||
name
|
||||
description
|
||||
label
|
||||
isActive
|
||||
`,
|
||||
expectToFail: false,
|
||||
})) as { fields: { node: FieldMetadataDTO }[] };
|
||||
|
||||
expect(relationFieldsBeforeUpdate.fields.length).toBe(
|
||||
allRelationFieldIds.length,
|
||||
);
|
||||
|
||||
const aggregatedRelationFieldMetadataDtos = aggregateFieldMetadata(
|
||||
relationFieldsBeforeUpdate.fields,
|
||||
);
|
||||
|
||||
expect(aggregatedRelationFieldMetadataDtos).toMatchSnapshot();
|
||||
}
|
||||
|
||||
// UPDATE
|
||||
const input = {
|
||||
idToUpdate: createdFieldMetadataId,
|
||||
updatePayload: {
|
||||
@@ -128,21 +284,82 @@ describe('updateOne FieldMetadataService morph relation fields v2', () => {
|
||||
description: 'new description',
|
||||
},
|
||||
};
|
||||
const {
|
||||
data: { updateOneField },
|
||||
} = await updateOneFieldMetadata({
|
||||
|
||||
await updateOneFieldMetadata({
|
||||
expectToFail: false,
|
||||
input,
|
||||
gqlFields: `
|
||||
id
|
||||
isActive
|
||||
description
|
||||
name
|
||||
description
|
||||
label
|
||||
isActive
|
||||
`,
|
||||
});
|
||||
|
||||
expect(updateOneField).toMatchObject(input.updatePayload);
|
||||
//ASSERT
|
||||
{
|
||||
const morphRelationFieldsAfterUpdate = (await findManyFieldsMetadata({
|
||||
input: {
|
||||
filter: { id: { in: allMorphFieldIds } },
|
||||
paging: { first: allMorphFieldIds.length },
|
||||
},
|
||||
gqlFields: `
|
||||
id
|
||||
name
|
||||
description
|
||||
label
|
||||
isActive
|
||||
`,
|
||||
expectToFail: false,
|
||||
})) as { fields: { node: FieldMetadataDTO }[] };
|
||||
|
||||
expect(morphRelationFieldsAfterUpdate.fields.length).toBe(
|
||||
allMorphFieldIds.length,
|
||||
);
|
||||
const aggregatedMorphFieldMetadataDtos = aggregateFieldMetadata(
|
||||
morphRelationFieldsAfterUpdate.fields,
|
||||
);
|
||||
|
||||
expect(aggregatedMorphFieldMetadataDtos).toMatchSnapshot();
|
||||
expect(
|
||||
aggregatedMorphFieldMetadataDtos,
|
||||
).toMatchObject<AggregatedFieldMetadataDto>({
|
||||
description: ['new description'],
|
||||
isActive: [false],
|
||||
label: ['new label'],
|
||||
name: [expect.any(String), expect.any(String)],
|
||||
});
|
||||
|
||||
const allRelationFieldIds = createdMorphFromResult.morphRelations.map(
|
||||
({ targetFieldMetadata }) => targetFieldMetadata.id,
|
||||
);
|
||||
|
||||
const relationFieldsBeforeUpdate = (await findManyFieldsMetadata({
|
||||
input: {
|
||||
filter: { id: { in: allRelationFieldIds } },
|
||||
paging: { first: allRelationFieldIds.length },
|
||||
},
|
||||
gqlFields: `
|
||||
id
|
||||
name
|
||||
description
|
||||
label
|
||||
isActive
|
||||
`,
|
||||
expectToFail: false,
|
||||
})) as { fields: { node: FieldMetadataDTO }[] };
|
||||
|
||||
expect(relationFieldsBeforeUpdate.fields.length).toBe(
|
||||
allRelationFieldIds.length,
|
||||
);
|
||||
|
||||
const aggregatedRelationFieldMetadataDtos = aggregateFieldMetadata(
|
||||
relationFieldsBeforeUpdate.fields,
|
||||
);
|
||||
|
||||
expect(aggregatedRelationFieldMetadataDtos).toMatchSnapshot();
|
||||
}
|
||||
|
||||
await deleteOneFieldMetadata({
|
||||
input: {
|
||||
|
||||
-107
@@ -1,79 +1,5 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Field metadata relation update should fail relation when name is changed 1`] = `
|
||||
[
|
||||
{
|
||||
"extensions": {
|
||||
"code": "METADATA_VALIDATION_FAILED",
|
||||
"errors": {
|
||||
"cronTrigger": [],
|
||||
"databaseEventTrigger": [],
|
||||
"fieldMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"code": "FIELD_MUTATION_NOT_ALLOWED",
|
||||
"message": "Forbidden updated properties for relation field metadata: name",
|
||||
"userFriendlyMessage": "Forbidden updated properties for relation field metadata",
|
||||
},
|
||||
],
|
||||
"flatEntityMinimalInformation": {
|
||||
"id": Any<String>,
|
||||
"name": "newName",
|
||||
"objectMetadataId": Any<String>,
|
||||
},
|
||||
"status": "fail",
|
||||
"type": "update_field",
|
||||
},
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"code": "FIELD_MUTATION_NOT_ALLOWED",
|
||||
"message": "Forbidden updated properties for relation field metadata: name",
|
||||
"userFriendlyMessage": "Forbidden updated properties for relation field metadata",
|
||||
},
|
||||
],
|
||||
"flatEntityMinimalInformation": {
|
||||
"id": Any<String>,
|
||||
"name": "newName",
|
||||
"objectMetadataId": Any<String>,
|
||||
},
|
||||
"status": "fail",
|
||||
"type": "update_field",
|
||||
},
|
||||
],
|
||||
"index": [],
|
||||
"objectMetadata": [],
|
||||
"routeTrigger": [],
|
||||
"serverlessFunction": [],
|
||||
"view": [],
|
||||
"viewField": [],
|
||||
"viewFilter": [],
|
||||
"viewGroup": [],
|
||||
},
|
||||
"message": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
"summary": {
|
||||
"invalidCronTrigger": 0,
|
||||
"invalidDatabaseEventTrigger": 0,
|
||||
"invalidFieldMetadata": 0,
|
||||
"invalidIndex": 0,
|
||||
"invalidObjectMetadata": 0,
|
||||
"invalidRouteTrigger": 0,
|
||||
"invalidServerlessFunction": 0,
|
||||
"invalidView": 0,
|
||||
"invalidViewField": 0,
|
||||
"invalidViewFilter": 0,
|
||||
"invalidViewGroup": 0,
|
||||
"totalErrors": 0,
|
||||
},
|
||||
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
|
||||
},
|
||||
"message": "Multiple validation errors occurred while updating field",
|
||||
"name": "GraphQLError",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Field metadata relation update should fail relation when name is not in camel case 1`] = `
|
||||
[
|
||||
{
|
||||
@@ -85,39 +11,6 @@ exports[`Field metadata relation update should fail relation when name is not in
|
||||
"fieldMetadata": [
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"code": "FIELD_MUTATION_NOT_ALLOWED",
|
||||
"message": "Forbidden updated properties for relation field metadata: name",
|
||||
"userFriendlyMessage": "Forbidden updated properties for relation field metadata",
|
||||
},
|
||||
{
|
||||
"code": "INVALID_FIELD_INPUT",
|
||||
"message": "Name should be in camelCase",
|
||||
"userFriendlyMessage": "Name should be in camelCase",
|
||||
"value": "New Name",
|
||||
},
|
||||
{
|
||||
"code": "INVALID_FIELD_INPUT",
|
||||
"message": "Name is not valid: it must start with lowercase letter and contain only alphanumeric letters",
|
||||
"userFriendlyMessage": "Name is not valid: it must start with lowercase letter and contain only alphanumeric letters",
|
||||
"value": "New Name",
|
||||
},
|
||||
],
|
||||
"flatEntityMinimalInformation": {
|
||||
"id": Any<String>,
|
||||
"name": "New Name",
|
||||
"objectMetadataId": Any<String>,
|
||||
},
|
||||
"status": "fail",
|
||||
"type": "update_field",
|
||||
},
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"code": "FIELD_MUTATION_NOT_ALLOWED",
|
||||
"message": "Forbidden updated properties for relation field metadata: name",
|
||||
"userFriendlyMessage": "Forbidden updated properties for relation field metadata",
|
||||
},
|
||||
{
|
||||
"code": "INVALID_FIELD_INPUT",
|
||||
"message": "Name should be in camelCase",
|
||||
|
||||
+1
-5
@@ -4,9 +4,9 @@ import { createOneObjectMetadata } from 'test/integration/metadata/suites/object
|
||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||
import { getMockCreateObjectInput } from 'test/integration/metadata/suites/object-metadata/utils/generate-mock-create-object-metadata-input';
|
||||
import { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util';
|
||||
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
|
||||
import { type EachTestingContext } from 'twenty-shared/testing';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
@@ -27,10 +27,6 @@ describe('Field metadata relation update should fail', () => {
|
||||
title: 'when name is not in camel case',
|
||||
context: { name: 'New Name' },
|
||||
},
|
||||
{
|
||||
title: 'when name is changed',
|
||||
context: { name: 'newName' },
|
||||
},
|
||||
];
|
||||
|
||||
beforeAll(async () => {
|
||||
|
||||
+38
@@ -1,4 +1,5 @@
|
||||
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
||||
import { findManyFieldsMetadata } from 'test/integration/metadata/suites/field-metadata/utils/find-many-fields-metadata.util';
|
||||
import { updateOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata.util';
|
||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
||||
import { createRelationBetweenObjects } from 'test/integration/metadata/suites/object-metadata/utils/create-relation-between-objects.util';
|
||||
@@ -104,6 +105,43 @@ describe('Field metadata relation update should succeed', () => {
|
||||
expect(data).toBeDefined();
|
||||
expect(data.updateOneField.isActive).toBe(false);
|
||||
});
|
||||
|
||||
it('should successfully update the name of a relation field', async () => {
|
||||
const { fields } = await findManyFieldsMetadata({
|
||||
input: {
|
||||
filter: {
|
||||
id: { eq: globalTestContext.employerFieldMetadataId },
|
||||
},
|
||||
paging: { first: 1 },
|
||||
},
|
||||
gqlFields: `
|
||||
id
|
||||
name
|
||||
`,
|
||||
});
|
||||
|
||||
const field = fields[0]?.node;
|
||||
|
||||
expect(field?.name).toBe('employer');
|
||||
|
||||
const { data, errors } = await updateOneFieldMetadata({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
idToUpdate: globalTestContext.employerFieldMetadataId,
|
||||
updatePayload: {
|
||||
name: 'leadEmployer',
|
||||
},
|
||||
},
|
||||
gqlFields: `
|
||||
id
|
||||
name
|
||||
`,
|
||||
});
|
||||
|
||||
expect(errors).toBeUndefined();
|
||||
expect(data).toBeDefined();
|
||||
expect(data.updateOneField.name).toBe('leadEmployer');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Field metadata self-relation update should succeed', () => {
|
||||
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Rename an object metadata with morph relation should succeed should update indexes on ONE_TO_MANY morph field related target object name update 1`] = `
|
||||
{
|
||||
"indexFieldMetadataList": [
|
||||
{
|
||||
"createdAt": Any<String>,
|
||||
"fieldMetadataId": Any<String>,
|
||||
"id": Any<String>,
|
||||
"order": 0,
|
||||
"updatedAt": Any<String>,
|
||||
},
|
||||
],
|
||||
"indexType": "BTREE",
|
||||
"isCustom": true,
|
||||
"isUnique": false,
|
||||
"name": "IDX_d46ee05346f12228875612c7a2a",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Rename an object metadata with morph relation should succeed should update indexes on ONE_TO_MANY morph field related target object name update 2`] = `
|
||||
{
|
||||
"indexFieldMetadataList": [
|
||||
{
|
||||
"createdAt": Any<String>,
|
||||
"fieldMetadataId": Any<String>,
|
||||
"id": Any<String>,
|
||||
"order": 0,
|
||||
"updatedAt": Any<String>,
|
||||
},
|
||||
],
|
||||
"indexType": "BTREE",
|
||||
"isCustom": true,
|
||||
"isUnique": false,
|
||||
"name": "IDX_270c22872e92d429bb4f07a15dd",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Rename an object metadata with morph relation should succeed should update indexes on ONE_TO_MANY morph field related target object name update 3`] = `
|
||||
{
|
||||
"indexFieldMetadataList": [
|
||||
{
|
||||
"createdAt": Any<String>,
|
||||
"fieldMetadataId": Any<String>,
|
||||
"id": Any<String>,
|
||||
"order": 0,
|
||||
"updatedAt": Any<String>,
|
||||
},
|
||||
],
|
||||
"indexType": "BTREE",
|
||||
"isCustom": true,
|
||||
"isUnique": false,
|
||||
"name": "IDX_aabda09e5917c5a7cbe5b6c84fc",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Rename an object metadata with morph relation should succeed should update indexes on ONE_TO_MANY morph field related target object name update 4`] = `
|
||||
{
|
||||
"indexFieldMetadataList": [
|
||||
{
|
||||
"createdAt": Any<String>,
|
||||
"fieldMetadataId": Any<String>,
|
||||
"id": Any<String>,
|
||||
"order": 0,
|
||||
"updatedAt": Any<String>,
|
||||
},
|
||||
],
|
||||
"indexType": "BTREE",
|
||||
"isCustom": true,
|
||||
"isUnique": false,
|
||||
"name": "IDX_270c22872e92d429bb4f07a15dd",
|
||||
}
|
||||
`;
|
||||
+373
-39
@@ -1,13 +1,95 @@
|
||||
import { findManyFieldsMetadataQueryFactory } from 'test/integration/metadata/suites/field-metadata/utils/find-many-fields-metadata-query-factory.util';
|
||||
import { findManyFieldsMetadata } from 'test/integration/metadata/suites/field-metadata/utils/find-many-fields-metadata.util';
|
||||
import { createMorphRelationBetweenObjects } from 'test/integration/metadata/suites/object-metadata/utils/create-morph-relation-between-objects.util';
|
||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||
import { findManyObjectMetadataWithIndexes } from 'test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata-with-indexes.util';
|
||||
import { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util';
|
||||
import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util';
|
||||
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
|
||||
import { jestExpectToBeDefined } from 'test/utils/jest-expect-to-be-defined.util.test';
|
||||
import {
|
||||
eachTestingContextFilter,
|
||||
type EachTestingContext,
|
||||
} from 'twenty-shared/testing';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { type IndexFieldMetadataDTO } from 'src/engine/metadata-modules/index-metadata/dtos/index-field-metadata.dto';
|
||||
import { type IndexMetadataDTO } from 'src/engine/metadata-modules/index-metadata/dtos/index-metadata.dto';
|
||||
|
||||
const findFieldMetadata = async ({
|
||||
fieldMetadataId,
|
||||
}: {
|
||||
fieldMetadataId: string;
|
||||
}) => {
|
||||
const { fields } = await findManyFieldsMetadata({
|
||||
gqlFields: `
|
||||
id
|
||||
name
|
||||
object { id nameSingular }
|
||||
relation { type targetFieldMetadata { id } targetObjectMetadata { id } }
|
||||
settings
|
||||
`,
|
||||
input: {
|
||||
filter: { id: { eq: fieldMetadataId } },
|
||||
paging: { first: 1 },
|
||||
},
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
return fields[0]?.node;
|
||||
};
|
||||
|
||||
const allTestsUseCases: EachTestingContext<{
|
||||
nameSingular: string;
|
||||
namePlural: string;
|
||||
labelSingular: string;
|
||||
labelPlural: string;
|
||||
isLabelSyncedWithName: boolean;
|
||||
newJoinColumnName: string | undefined;
|
||||
relationType: RelationType;
|
||||
}>[] = [
|
||||
{
|
||||
title:
|
||||
'should rename custom object, and update both the field name and join column name of the morph relation that contains the object name',
|
||||
context: {
|
||||
nameSingular: 'personForRenameSecond2',
|
||||
namePlural: 'peopleForRenameSecond2',
|
||||
labelSingular: 'Person For Rename2',
|
||||
labelPlural: 'People For Rename2',
|
||||
isLabelSyncedWithName: false,
|
||||
newJoinColumnName: 'ownerPersonForRenameSecond2Id',
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
},
|
||||
},
|
||||
{
|
||||
title:
|
||||
'should rename custom object, and update both the field name and join column name of the morph relation that contains the object name if label is sync with name',
|
||||
context: {
|
||||
nameSingular: 'personForRenameSecond3',
|
||||
namePlural: 'peopleForRenameSecond3',
|
||||
labelSingular: 'person For Rename Second3',
|
||||
labelPlural: 'people For Rename Second3',
|
||||
isLabelSyncedWithName: true,
|
||||
newJoinColumnName: 'ownerPersonForRenameSecond3Id',
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
},
|
||||
},
|
||||
{
|
||||
title:
|
||||
'should rename custom object, and update both the field name and join column name of the morph relation that contains the object name with ONE_TO_MANY relation type',
|
||||
context: {
|
||||
nameSingular: 'personForRenameSecond4',
|
||||
namePlural: 'peopleForRenameSecond4',
|
||||
labelSingular: 'Person For Rename Second4',
|
||||
labelPlural: 'People For Rename Second4',
|
||||
isLabelSyncedWithName: true,
|
||||
newJoinColumnName: undefined,
|
||||
relationType: RelationType.ONE_TO_MANY,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
describe('Rename an object metadata with morph relation should succeed', () => {
|
||||
let createdObjectMetadataPersonId: string;
|
||||
let createdObjectMetadataOpportunityId: string;
|
||||
@@ -83,16 +165,26 @@ describe('Rename an object metadata with morph relation should succeed', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it.failing(
|
||||
'should rename custom object, and update both the field name and join column name of the morph relation that contains the object name',
|
||||
async () => {
|
||||
it.each(eachTestingContextFilter(allTestsUseCases))(
|
||||
'$title',
|
||||
async ({ context }) => {
|
||||
const {
|
||||
nameSingular,
|
||||
namePlural,
|
||||
labelSingular,
|
||||
labelPlural,
|
||||
isLabelSyncedWithName,
|
||||
newJoinColumnName,
|
||||
relationType,
|
||||
} = context;
|
||||
|
||||
const morphRelationField = await createMorphRelationBetweenObjects({
|
||||
name: 'owner',
|
||||
objectMetadataId: createdObjectMetadataOpportunityId,
|
||||
firstTargetObjectMetadataId: createdObjectMetadataPersonId,
|
||||
secondTargetObjectMetadataId: createdObjectMetadataCompanyId,
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
relationType,
|
||||
});
|
||||
|
||||
expect(morphRelationField.morphRelations.length).toBe(2);
|
||||
@@ -100,23 +192,24 @@ describe('Rename an object metadata with morph relation should succeed', () => {
|
||||
const { data } = await updateOneObjectMetadata({
|
||||
expectToFail: false,
|
||||
gqlFields: `
|
||||
nameSingular
|
||||
labelSingular
|
||||
namePlural
|
||||
labelPlural
|
||||
`,
|
||||
nameSingular
|
||||
labelSingular
|
||||
namePlural
|
||||
labelPlural
|
||||
`,
|
||||
input: {
|
||||
idToUpdate: createdObjectMetadataPersonId,
|
||||
updatePayload: {
|
||||
nameSingular: 'personForRenameSecond2',
|
||||
namePlural: 'peopleForRenameSecond2',
|
||||
labelSingular: 'Person For Rename2',
|
||||
labelPlural: 'People For Rename2',
|
||||
nameSingular,
|
||||
namePlural,
|
||||
labelSingular,
|
||||
labelPlural,
|
||||
isLabelSyncedWithName,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(data.updateOneObject.nameSingular).toBe('personForRenameSecond2');
|
||||
expect(data.updateOneObject.nameSingular).toBe(nameSingular);
|
||||
|
||||
const ownerFieldMetadataOnPersonId =
|
||||
morphRelationField.morphRelations.find(
|
||||
@@ -136,32 +229,273 @@ describe('Rename an object metadata with morph relation should succeed', () => {
|
||||
});
|
||||
|
||||
expect(fieldAfterRenaming.settings.joinColumnName).toBe(
|
||||
'ownerPersonForRenameSecond2Id',
|
||||
newJoinColumnName,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
const findFieldMetadata = async ({
|
||||
fieldMetadataId,
|
||||
}: {
|
||||
fieldMetadataId: string;
|
||||
}) => {
|
||||
const operation = findManyFieldsMetadataQueryFactory({
|
||||
gqlFields: `
|
||||
id
|
||||
name
|
||||
object { id nameSingular }
|
||||
relation { type targetFieldMetadata { id } targetObjectMetadata { id } }
|
||||
settings
|
||||
`,
|
||||
input: {
|
||||
filter: { id: { eq: fieldMetadataId } },
|
||||
paging: { first: 1 },
|
||||
},
|
||||
it('should update indexes on ONE_TO_MANY morph field related target object name update', async () => {
|
||||
const morphRelationField = await createMorphRelationBetweenObjects({
|
||||
name: 'owner',
|
||||
objectMetadataId: createdObjectMetadataOpportunityId,
|
||||
firstTargetObjectMetadataId: createdObjectMetadataPersonId,
|
||||
secondTargetObjectMetadataId: createdObjectMetadataCompanyId,
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
relationType: RelationType.ONE_TO_MANY,
|
||||
});
|
||||
|
||||
expect(morphRelationField.morphRelations.length).toBe(2);
|
||||
|
||||
const objects = await findManyObjectMetadataWithIndexes({
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
let relationIndexByFieldId: Record<
|
||||
string,
|
||||
IndexMetadataDTO & {
|
||||
indexFieldMetadataList: IndexFieldMetadataDTO[];
|
||||
}
|
||||
> = {};
|
||||
|
||||
const morphParentObject = objects.find(
|
||||
(object) => object.id === createdObjectMetadataOpportunityId,
|
||||
);
|
||||
|
||||
jestExpectToBeDefined(morphParentObject);
|
||||
|
||||
for (const {
|
||||
targetObjectMetadata,
|
||||
targetFieldMetadata,
|
||||
sourceFieldMetadata,
|
||||
} of morphRelationField.morphRelations) {
|
||||
const relatedObject = objects.find(
|
||||
(object) => object.id === targetObjectMetadata.id,
|
||||
);
|
||||
|
||||
jestExpectToBeDefined(relatedObject);
|
||||
|
||||
const objectRelatedIndexes = relatedObject.indexMetadataList.filter(
|
||||
(index) =>
|
||||
index.indexFieldMetadataList.some(
|
||||
(indexField) =>
|
||||
indexField.fieldMetadataId === targetFieldMetadata.id,
|
||||
),
|
||||
);
|
||||
|
||||
expect(objectRelatedIndexes.length).toBe(1);
|
||||
const [relationIndex] = objectRelatedIndexes;
|
||||
|
||||
jestExpectToBeDefined(relationIndex);
|
||||
expect(relationIndex).toMatchSnapshot(
|
||||
extractRecordIdsAndDatesAsExpectAny({ ...relationIndex }),
|
||||
);
|
||||
|
||||
relationIndexByFieldId[targetFieldMetadata.id] = relationIndex;
|
||||
|
||||
const parentRelationIndex = morphParentObject.indexMetadataList.filter(
|
||||
(index) =>
|
||||
index.indexFieldMetadataList.some(
|
||||
(indexField) =>
|
||||
indexField.fieldMetadataId == sourceFieldMetadata.id,
|
||||
),
|
||||
);
|
||||
|
||||
expect(parentRelationIndex.length).toBe(0);
|
||||
}
|
||||
|
||||
await updateOneObjectMetadata({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
idToUpdate: createdObjectMetadataPersonId,
|
||||
updatePayload: {
|
||||
nameSingular: 'personForRenameSecondUpdated',
|
||||
namePlural: 'peopleForRenameSecondUpdated',
|
||||
labelSingular: 'Person For Rename Updated',
|
||||
labelPlural: 'People For Rename Updated',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const updatedObjects = await findManyObjectMetadataWithIndexes({
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
const morphParentObjectAfterUpdate = objects.find(
|
||||
(object) => object.id === createdObjectMetadataOpportunityId,
|
||||
);
|
||||
|
||||
jestExpectToBeDefined(morphParentObjectAfterUpdate);
|
||||
for (const {
|
||||
targetObjectMetadata,
|
||||
targetFieldMetadata,
|
||||
sourceFieldMetadata,
|
||||
} of morphRelationField.morphRelations) {
|
||||
const relatedObject = updatedObjects.find(
|
||||
(object) => object.id === targetObjectMetadata.id,
|
||||
);
|
||||
|
||||
jestExpectToBeDefined(relatedObject);
|
||||
|
||||
const objectRelatedIndexes = relatedObject.indexMetadataList.filter(
|
||||
(index) =>
|
||||
index.indexFieldMetadataList.some(
|
||||
(indexField) =>
|
||||
indexField.fieldMetadataId === targetFieldMetadata.id,
|
||||
),
|
||||
);
|
||||
|
||||
expect(objectRelatedIndexes.length).toBe(1);
|
||||
const [relationIndex] = objectRelatedIndexes;
|
||||
|
||||
jestExpectToBeDefined(relationIndex);
|
||||
expect(relationIndex).toMatchSnapshot(
|
||||
extractRecordIdsAndDatesAsExpectAny({ ...relationIndex }),
|
||||
);
|
||||
const previousIndex = relationIndexByFieldId[targetFieldMetadata.id];
|
||||
|
||||
jestExpectToBeDefined(previousIndex);
|
||||
if (targetObjectMetadata.id === createdObjectMetadataPersonId) {
|
||||
expect(previousIndex.name).not.toBe(relationIndex.name);
|
||||
} else {
|
||||
expect(previousIndex.name).toBe(relationIndex.name);
|
||||
}
|
||||
|
||||
const parentRelationIndex =
|
||||
morphParentObjectAfterUpdate.indexMetadataList.filter((index) =>
|
||||
index.indexFieldMetadataList.some(
|
||||
(indexField) =>
|
||||
indexField.fieldMetadataId == sourceFieldMetadata.id,
|
||||
),
|
||||
);
|
||||
|
||||
expect(parentRelationIndex.length).toBe(0);
|
||||
}
|
||||
});
|
||||
const fields = await makeMetadataAPIRequest(operation);
|
||||
const field = fields.body.data.fields.edges?.[0]?.node;
|
||||
|
||||
return field;
|
||||
};
|
||||
it('should not update morph index on MANY_TO_ONE morph field related target object name update', async () => {
|
||||
const morphRelationField = await createMorphRelationBetweenObjects({
|
||||
name: 'owner',
|
||||
objectMetadataId: createdObjectMetadataOpportunityId,
|
||||
firstTargetObjectMetadataId: createdObjectMetadataPersonId,
|
||||
secondTargetObjectMetadataId: createdObjectMetadataCompanyId,
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
});
|
||||
|
||||
expect(morphRelationField.morphRelations.length).toBe(2);
|
||||
|
||||
const objects = await findManyObjectMetadataWithIndexes({
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
let relationIndexByFieldId: Record<
|
||||
string,
|
||||
IndexMetadataDTO & {
|
||||
indexFieldMetadataList: IndexFieldMetadataDTO[];
|
||||
}
|
||||
> = {};
|
||||
|
||||
const morphParentObject = objects.find(
|
||||
(object) => object.id === createdObjectMetadataOpportunityId,
|
||||
);
|
||||
|
||||
jestExpectToBeDefined(morphParentObject);
|
||||
|
||||
for (const {
|
||||
targetObjectMetadata,
|
||||
targetFieldMetadata,
|
||||
sourceFieldMetadata,
|
||||
} of morphRelationField.morphRelations) {
|
||||
const relatedObject = objects.find(
|
||||
(object) => object.id === targetObjectMetadata.id,
|
||||
);
|
||||
|
||||
jestExpectToBeDefined(relatedObject);
|
||||
|
||||
const objectRelatedIndexes = relatedObject.indexMetadataList.filter(
|
||||
(index) =>
|
||||
index.indexFieldMetadataList.some(
|
||||
(indexField) =>
|
||||
indexField.fieldMetadataId === targetFieldMetadata.id,
|
||||
),
|
||||
);
|
||||
|
||||
expect(objectRelatedIndexes.length).toBe(0);
|
||||
|
||||
const parentRelationIndex = morphParentObject.indexMetadataList.filter(
|
||||
(index) =>
|
||||
index.indexFieldMetadataList.some(
|
||||
(indexField) =>
|
||||
indexField.fieldMetadataId == sourceFieldMetadata.id,
|
||||
),
|
||||
);
|
||||
|
||||
expect(parentRelationIndex.length).toBe(1);
|
||||
const [relationIndex] = parentRelationIndex;
|
||||
|
||||
relationIndexByFieldId[
|
||||
relationIndex.indexFieldMetadataList[0].fieldMetadataId
|
||||
] = relationIndex;
|
||||
}
|
||||
|
||||
await updateOneObjectMetadata({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
idToUpdate: createdObjectMetadataPersonId,
|
||||
updatePayload: {
|
||||
nameSingular: 'personForRenameSecondUpdated',
|
||||
namePlural: 'peopleForRenameSecondUpdated',
|
||||
labelSingular: 'Person For Rename Updated',
|
||||
labelPlural: 'People For Rename Updated',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const updatedObjects = await findManyObjectMetadataWithIndexes({
|
||||
expectToFail: false,
|
||||
});
|
||||
|
||||
const morphParentObjectAfterUpdate = objects.find(
|
||||
(object) => object.id === createdObjectMetadataOpportunityId,
|
||||
);
|
||||
|
||||
jestExpectToBeDefined(morphParentObjectAfterUpdate);
|
||||
|
||||
for (const {
|
||||
targetObjectMetadata,
|
||||
targetFieldMetadata,
|
||||
sourceFieldMetadata,
|
||||
} of morphRelationField.morphRelations) {
|
||||
const relatedObject = updatedObjects.find(
|
||||
(object) => object.id === targetObjectMetadata.id,
|
||||
);
|
||||
|
||||
jestExpectToBeDefined(relatedObject);
|
||||
|
||||
const objectRelatedIndexes = relatedObject.indexMetadataList.filter(
|
||||
(index) =>
|
||||
index.indexFieldMetadataList.some(
|
||||
(indexField) =>
|
||||
indexField.fieldMetadataId === targetFieldMetadata.id,
|
||||
),
|
||||
);
|
||||
|
||||
expect(objectRelatedIndexes.length).toBe(0);
|
||||
|
||||
const parentRelationIndex =
|
||||
morphParentObjectAfterUpdate.indexMetadataList.filter((index) =>
|
||||
index.indexFieldMetadataList.some(
|
||||
(indexField) =>
|
||||
indexField.fieldMetadataId == sourceFieldMetadata.id,
|
||||
),
|
||||
);
|
||||
|
||||
expect(parentRelationIndex.length).toBe(1);
|
||||
|
||||
const [relationIndex] = parentRelationIndex;
|
||||
const previousIndex = relationIndexByFieldId[sourceFieldMetadata.id];
|
||||
|
||||
expect(previousIndex.name).toBe(relationIndex.name);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user