[WHEN_RELEASED_REQUIRES_CACHE_FLUSH] Object related record logic in v2 (#14937)

# Introduction
Initial motivation here was to migrate the object related records logic
from v1 to v2, please note that now in v2 views aren't records anymore
but core engine entities

## What's done
- Added specific label identifier targeting view field logic
- Handled side effects on viewField creation with lowest position on
object label identifier mutation
- Added viewField relations in field metadate entity + handled
optimistic in builder v2
- Added view relations in object metadata entity + handled optimistic in
builder v2
- Added integration tests covering the side effects and new validation
exceptions
- Sandardized cache computation
- Coverage on object metadata creation side effect on views and view
fields

## Coverage
```ts
 PASS  test/integration/graphql/suites/view/view-field/object-identifier-update-side-effect-on-view-field.integration-spec.ts
  View Field Resolver - Successful object metadata identifier update side effect on view field
    ✓ should create a view field on label identifier object metadata update if it does not exist on view (7 ms)
    ✓ Should not allow deleting a label identifier view field (17 ms)
    ✓ Should not allow destroying a label identifier view field (6 ms)
    ✓ Should not allow updating a label identifier view field visibility to false (8 ms)
    ✓ Should not allow creating a view field with a position lower than the label idenfitier view field (180 ms)
    ✓ Should not allow updated labelIdentifier view field with a position higher than existing other view field (346 ms)
    ✓ Should allow updated labelIdentifier view field with a position higher than existing other view field (434 ms)

Test Suites: 1 passed, 1 total
Tests:       7 passed, 7 total
Snapshots:   5 passed, 5 total
Time:        4.571 s, estimated 5 s
```

close https://github.com/twentyhq/core-team-issues/issues/1664
This commit is contained in:
Paul Rastoin
2025-10-08 16:23:37 +02:00
committed by GitHub
parent 1c1739ec80
commit 4ecc9c622d
80 changed files with 6858 additions and 627 deletions
@@ -12,6 +12,7 @@ export const getFlatObjectMetadataMock = (
const createdAt = '2024-01-01T00:00:00.000Z' as unknown as Date;
return {
viewIds: [],
indexMetadataIds: [],
fieldMetadataIds: [],
description: 'default flat object metadata description',
@@ -39,7 +39,18 @@ export class WorkspaceFlatObjectMetadataMapCacheService extends WorkspaceFlatMap
workspaceId,
},
withDeleted: true,
relations: ['fields', 'indexMetadatas'],
select: {
fields: {
id: true,
},
indexMetadatas: {
id: true,
},
views: {
id: true,
},
},
relations: ['fields', 'indexMetadatas', 'views'],
});
const flatObjectMetadataMaps = objectMetadatas.reduce<
@@ -10,6 +10,7 @@ export const objectMetadataEntityRelationProperties = [
'application',
'objectPermissions',
'fieldPermissions',
'views',
] as const satisfies ObjectMetadataRelationProperties[];
type ObjectMetadataRelationProperties = ExtractRecordTypeOrmRelationProperties<
@@ -24,4 +25,5 @@ export type FlatObjectMetadata = Omit<
universalIdentifier: string;
fieldMetadataIds: string[];
indexMetadataIds: string[];
viewIds: string[];
};
@@ -54,6 +54,7 @@ export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCre
const createdAt = new Date();
const flatObjectMetadataToCreate: FlatObjectMetadata = {
fieldMetadataIds: [],
viewIds: [],
indexMetadataIds: [],
createdAt,
updatedAt: createdAt,
@@ -5,8 +5,8 @@ import {
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/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 { findObjectFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-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 { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
@@ -52,10 +52,11 @@ export const fromDeleteObjectInputToFlatFieldMetadatasToDelete = ({
);
}
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
flatFieldMetadataMaps,
flatObjectMetadata: flatObjectMetadataToDelete,
});
const objectFlatFieldMetadatas =
findManyFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityMaps: flatFieldMetadataMaps,
flatEntityIds: flatObjectMetadataToDelete.fieldMetadataIds,
});
const flatFieldMetadatasToDelete = objectFlatFieldMetadatas.flatMap(
(flatFieldMetadata) => {
if (isMorphOrRelationFlatFieldMetadata(flatFieldMetadata)) {
@@ -16,6 +16,7 @@ export const fromObjectMetadataEntityToFlatObjectMetadata = (
return {
...objectMetadataEntityWithoutRelations,
viewIds: objectMetadataEntity.views.map((viewEntity) => viewEntity.id),
indexMetadataIds: objectMetadataEntity.indexMetadatas.map(
(indexEntity) => indexEntity.id,
),
@@ -0,0 +1,231 @@
import {
extractAndSanitizeObjectStringFields,
isDefined,
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties,
} from 'twenty-shared/utils';
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
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 { FLAT_OBJECT_METADATA_PROPERTIES_TO_COMPARE } from 'src/engine/metadata-modules/flat-object-metadata/constants/flat-object-metadata-properties-to-compare.constant';
import { type FlatObjectMetadataPropertiesToCompare } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata-properties-to-compare.type';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { recomputeIndexAfterFlatObjectMetadataSingularNameUpdate } from 'src/engine/metadata-modules/flat-object-metadata/utils/recompute-index-after-flat-object-metadata-singular-name-update.util';
import { recomputeViewFieldIdentifierAfterFlatObjectIdentifierUpdate } from 'src/engine/metadata-modules/flat-object-metadata/utils/recompute-view-field-identifier-after-flat-object-identifier-update.util';
import { renameRelatedMorphFieldOnObjectNamesUpdate } from 'src/engine/metadata-modules/flat-object-metadata/utils/rename-related-morph-field-on-object-names-update.util';
import { OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/object-metadata/constants/object-metadata-standard-overrides-properties.constant';
import { type UpdateOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input';
import {
ObjectMetadataException,
ObjectMetadataExceptionCode,
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
import { type ObjectMetadataStandardOverridesProperties } from 'src/engine/metadata-modules/object-metadata/types/object-metadata-standard-overrides-properties.types';
import { isStandardMetadata } from 'src/engine/metadata-modules/utils/is-standard-metadata.util';
type FromUpdateObjectInputToFlatObjectMetadataArgs = {
updateObjectInput: UpdateOneObjectInput;
} & Pick<
AllFlatEntityMaps,
| 'flatIndexMaps'
| 'flatObjectMetadataMaps'
| 'flatFieldMetadataMaps'
| 'flatViewFieldMaps'
| 'flatViewMaps'
>;
const objectMetadataEditableProperties =
FLAT_OBJECT_METADATA_PROPERTIES_TO_COMPARE.filter(
(
property,
): property is Exclude<
FlatObjectMetadataPropertiesToCompare,
'standardOverrides'
> => property !== 'standardOverrides',
);
type UpdatedFlatObjectAndRelatedFlatEntities = {
flatObjectMetadata: FlatObjectMetadata;
otherObjectFlatFieldMetadataToUpdate: FlatFieldMetadata[];
flatIndexMetadataToUpdate: FlatIndexMetadata[];
flatViewFieldToUpdate: FlatViewField[];
flatViewFieldToCreate: FlatViewField[];
};
export const fromUpdateObjectInputToFlatObjectMetadataAndRelatedFlatEntities =
({
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
updateObjectInput: rawUpdateObjectInput,
flatIndexMaps,
flatFieldMetadataMaps,
flatViewFieldMaps,
flatViewMaps,
}: FromUpdateObjectInputToFlatObjectMetadataArgs): UpdatedFlatObjectAndRelatedFlatEntities => {
const { id: objectMetadataIdToUpdate } =
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
rawUpdateObjectInput,
['id'],
);
const updatedEditableObjectProperties =
extractAndSanitizeObjectStringFields(
rawUpdateObjectInput.update,
objectMetadataEditableProperties,
);
const flatObjectMetadataToUpdate = findFlatEntityByIdInFlatEntityMaps({
flatEntityMaps: existingFlatObjectMetadataMaps,
flatEntityId: objectMetadataIdToUpdate,
});
if (!isDefined(flatObjectMetadataToUpdate)) {
throw new ObjectMetadataException(
'Object to update not found',
ObjectMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND,
);
}
if (isStandardMetadata(flatObjectMetadataToUpdate)) {
const invalidUpdatedProperties = Object.keys(
updatedEditableObjectProperties,
).filter(
(property) =>
!OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES.includes(
property as ObjectMetadataStandardOverridesProperties,
),
);
if (invalidUpdatedProperties.length > 0) {
throw new ObjectMetadataException(
`Cannot edit standard object metadata properties: ${invalidUpdatedProperties.join(', ')}`,
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
);
}
const updatedStandardFlatObjectMetadata =
OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES.reduce(
(acc, property) => {
const isPropertyUpdated =
updatedEditableObjectProperties[property] !== undefined;
return {
...acc,
standardOverrides: {
...acc.standardOverrides,
...(isPropertyUpdated
? { [property]: updatedEditableObjectProperties[property] }
: {}),
},
};
},
flatObjectMetadataToUpdate,
);
return {
flatObjectMetadata: updatedStandardFlatObjectMetadata,
otherObjectFlatFieldMetadataToUpdate: [],
flatIndexMetadataToUpdate: [],
flatViewFieldToUpdate: [],
flatViewFieldToCreate: [],
};
}
const initialAccumulator: UpdatedFlatObjectAndRelatedFlatEntities = {
flatObjectMetadata: flatObjectMetadataToUpdate,
otherObjectFlatFieldMetadataToUpdate: [],
flatIndexMetadataToUpdate: [],
flatViewFieldToUpdate: [],
flatViewFieldToCreate: [],
};
return objectMetadataEditableProperties.reduce<UpdatedFlatObjectAndRelatedFlatEntities>(
(
{
flatObjectMetadata,
otherObjectFlatFieldMetadataToUpdate,
flatIndexMetadataToUpdate,
flatViewFieldToUpdate,
flatViewFieldToCreate,
},
property,
) => {
const updatedPropertyValue = updatedEditableObjectProperties[property];
const isPropertyUpdated =
updatedPropertyValue !== undefined &&
flatObjectMetadata[property] !== updatedPropertyValue;
if (!isPropertyUpdated) {
return {
flatObjectMetadata,
otherObjectFlatFieldMetadataToUpdate,
flatIndexMetadataToUpdate,
flatViewFieldToUpdate,
flatViewFieldToCreate,
};
}
const updatedFlatObjectMetadata = {
...flatObjectMetadata,
[property]: updatedPropertyValue,
};
const newUpdatedOtherObjectFlatFieldMetadatas =
property === 'nameSingular' || property === 'namePlural'
? renameRelatedMorphFieldOnObjectNamesUpdate({
flatFieldMetadataMaps,
fromFlatObjectMetadata: updatedFlatObjectMetadata,
toFlatObjectMetadata: updatedFlatObjectMetadata,
})
: [];
const newUpdatedFlatIndexMetadatas =
property === 'nameSingular'
? recomputeIndexAfterFlatObjectMetadataSingularNameUpdate({
flatFieldMetadataMaps,
existingFlatObjectMetadata: flatObjectMetadataToUpdate,
flatIndexMaps,
updatedSingularName: updatedFlatObjectMetadata.nameSingular,
})
: [];
const {
flatViewFieldToCreate: newFlatViewFieldToCreate,
flatViewFieldToUpdate: newFlatViewFieldToUpdate,
} =
property === 'labelIdentifierFieldMetadataId' &&
isDefined(updatedFlatObjectMetadata.labelIdentifierFieldMetadataId)
? recomputeViewFieldIdentifierAfterFlatObjectIdentifierUpdate({
existingFlatObjectMetadata: flatObjectMetadataToUpdate,
flatViewFieldMaps,
flatViewMaps,
updatedLabelIdentifierFieldMetadataId:
updatedFlatObjectMetadata.labelIdentifierFieldMetadataId,
})
: {
flatViewFieldToCreate: [],
flatViewFieldToUpdate: [],
};
return {
flatObjectMetadata: updatedFlatObjectMetadata,
otherObjectFlatFieldMetadataToUpdate: [
...otherObjectFlatFieldMetadataToUpdate,
...newUpdatedOtherObjectFlatFieldMetadatas,
],
flatViewFieldToUpdate: [
...flatViewFieldToUpdate,
...newFlatViewFieldToUpdate,
],
flatViewFieldToCreate: [
...flatViewFieldToCreate,
...newFlatViewFieldToCreate,
],
flatIndexMetadataToUpdate: [
...flatIndexMetadataToUpdate,
...newUpdatedFlatIndexMetadatas,
],
};
},
initialAccumulator,
);
};
@@ -1,182 +0,0 @@
import {
extractAndSanitizeObjectStringFields,
isDefined,
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties,
} from 'twenty-shared/utils';
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps.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 { FLAT_OBJECT_METADATA_PROPERTIES_TO_COMPARE } from 'src/engine/metadata-modules/flat-object-metadata/constants/flat-object-metadata-properties-to-compare.constant';
import { type FlatObjectMetadataPropertiesToCompare } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata-properties-to-compare.type';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { recomputeIndexAfterFlatObjectMetadataSingularNameUpdate } from 'src/engine/metadata-modules/flat-object-metadata/utils/recompute-index-after-flat-object-metadata-singular-name-update.util';
import { renameRelatedMorphFieldOnObjectNamesUpdate } from 'src/engine/metadata-modules/flat-object-metadata/utils/rename-related-morph-field-on-object-names-update.util';
import { OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/object-metadata/constants/object-metadata-standard-overrides-properties.constant';
import { type UpdateOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input';
import {
ObjectMetadataException,
ObjectMetadataExceptionCode,
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
import { type ObjectMetadataStandardOverridesProperties } from 'src/engine/metadata-modules/object-metadata/types/object-metadata-standard-overrides-properties.types';
import { isStandardMetadata } from 'src/engine/metadata-modules/utils/is-standard-metadata.util';
type FromUpdateObjectInputToFlatObjectMetadataArgs = {
updateObjectInput: UpdateOneObjectInput;
} & Pick<
AllFlatEntityMaps,
'flatIndexMaps' | 'flatObjectMetadataMaps' | 'flatFieldMetadataMaps'
>;
const objectMetadataEditableProperties =
FLAT_OBJECT_METADATA_PROPERTIES_TO_COMPARE.filter(
(
property,
): property is Exclude<
FlatObjectMetadataPropertiesToCompare,
'standardOverrides'
> => property !== 'standardOverrides',
);
type UpdatedFlatObjectAndOtherObjectFieldMetadatas = {
flatObjectMetadata: FlatObjectMetadata;
otherObjectFlatFieldMetadataToUpdate: FlatFieldMetadata[];
flatIndexMetadataToUpdate: FlatIndexMetadata[];
};
export const fromUpdateObjectInputToFlatObjectMetadata = ({
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
updateObjectInput: rawUpdateObjectInput,
flatIndexMaps,
flatFieldMetadataMaps,
}: FromUpdateObjectInputToFlatObjectMetadataArgs): UpdatedFlatObjectAndOtherObjectFieldMetadatas => {
const { id: objectMetadataIdToUpdate } =
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
rawUpdateObjectInput,
['id'],
);
const updatedEditableObjectProperties = extractAndSanitizeObjectStringFields(
rawUpdateObjectInput.update,
objectMetadataEditableProperties,
);
const flatObjectMetadataToUpdate = findFlatEntityByIdInFlatEntityMaps({
flatEntityMaps: existingFlatObjectMetadataMaps,
flatEntityId: objectMetadataIdToUpdate,
});
if (!isDefined(flatObjectMetadataToUpdate)) {
throw new ObjectMetadataException(
'Object to update not found',
ObjectMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND,
);
}
if (isStandardMetadata(flatObjectMetadataToUpdate)) {
const invalidUpdatedProperties = Object.keys(
updatedEditableObjectProperties,
).filter(
(property) =>
!OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES.includes(
property as ObjectMetadataStandardOverridesProperties,
),
);
if (invalidUpdatedProperties.length > 0) {
throw new ObjectMetadataException(
`Cannot edit standard object metadata properties: ${invalidUpdatedProperties.join(', ')}`,
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
);
}
const updatedStandardFlatObjectdMetadata =
OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES.reduce((acc, property) => {
const isPropertyUpdated =
updatedEditableObjectProperties[property] !== undefined;
return {
...acc,
standardOverrides: {
...acc.standardOverrides,
...(isPropertyUpdated
? { [property]: updatedEditableObjectProperties[property] }
: {}),
},
};
}, flatObjectMetadataToUpdate);
return {
flatObjectMetadata: updatedStandardFlatObjectdMetadata,
otherObjectFlatFieldMetadataToUpdate: [],
flatIndexMetadataToUpdate: [],
};
}
const initialAccumulator: UpdatedFlatObjectAndOtherObjectFieldMetadatas = {
flatObjectMetadata: flatObjectMetadataToUpdate,
otherObjectFlatFieldMetadataToUpdate: [],
flatIndexMetadataToUpdate: [],
};
return objectMetadataEditableProperties.reduce<UpdatedFlatObjectAndOtherObjectFieldMetadatas>(
(
{
flatObjectMetadata,
otherObjectFlatFieldMetadataToUpdate: otherObjectFlatFieldMetadatas,
flatIndexMetadataToUpdate,
},
property,
) => {
const updatedPropertyValue = updatedEditableObjectProperties[property];
const isPropertyUpdated =
updatedPropertyValue !== undefined &&
flatObjectMetadata[property] !== updatedPropertyValue;
if (!isPropertyUpdated) {
return {
flatObjectMetadata,
otherObjectFlatFieldMetadataToUpdate: otherObjectFlatFieldMetadatas,
flatIndexMetadataToUpdate,
};
}
const updatedFlatObjectMetadata = {
...flatObjectMetadata,
[property]: updatedPropertyValue,
};
const newUpdatedOtherObjectFlatFieldMetadatas =
property === 'nameSingular' || property === 'namePlural'
? renameRelatedMorphFieldOnObjectNamesUpdate({
flatFieldMetadataMaps,
fromFlatObjectMetadata: updatedFlatObjectMetadata,
toFlatObjectMetadata: updatedFlatObjectMetadata,
})
: [];
const newUpdatedFlatIndexMetadatas =
property === 'nameSingular'
? recomputeIndexAfterFlatObjectMetadataSingularNameUpdate({
flatFieldMetadataMaps,
existingFlatObjectMetadata: flatObjectMetadataToUpdate,
flatIndexMaps,
updatedSingularName: updatedFlatObjectMetadata.nameSingular,
})
: [];
return {
flatObjectMetadata: updatedFlatObjectMetadata,
otherObjectFlatFieldMetadataToUpdate: [
...otherObjectFlatFieldMetadatas,
...newUpdatedOtherObjectFlatFieldMetadatas,
],
flatIndexMetadataToUpdate: [
...flatIndexMetadataToUpdate,
...newUpdatedFlatIndexMetadatas,
],
};
},
initialAccumulator,
);
};
@@ -1,7 +1,7 @@
import { isDefined } from 'twenty-shared/utils';
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
import { findObjectFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/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';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { generateFlatIndexMetadataWithNameOrThrow } from 'src/engine/metadata-modules/index-metadata/utils/generate-flat-index.util';
@@ -31,10 +31,11 @@ export const recomputeIndexAfterFlatObjectMetadataSingularNameUpdate = ({
nameSingular: updatedSingularName,
};
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
flatFieldMetadataMaps,
flatObjectMetadata: optimisticFlatObjectMetadata,
});
const objectFlatFieldMetadatas =
findManyFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityMaps: flatFieldMetadataMaps,
flatEntityIds: optimisticFlatObjectMetadata.fieldMetadataIds,
});
return allRelatedFlatIndexMetadata.map<FlatIndexMetadata>((flatIndex) => {
const newIndex = generateFlatIndexMetadataWithNameOrThrow({
@@ -0,0 +1,85 @@
import { isDefined } from 'twenty-shared/utils';
import { v4 } from 'uuid';
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
import { type FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { DEFAULT_VIEW_FIELD_SIZE } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/constants/DEFAULT_VIEW_FIELD_SIZE';
type RecomputeViewFieldIdentifierAfterFlatObjectIdentifierUpdateArgs = {
existingFlatObjectMetadata: FlatObjectMetadata;
updatedLabelIdentifierFieldMetadataId: string;
} & Pick<AllFlatEntityMaps, 'flatViewFieldMaps' | 'flatViewMaps'>;
type FlatViewFieldToCreateAndUpdate = {
flatViewFieldToCreate: FlatViewField[];
flatViewFieldToUpdate: FlatViewField[];
};
export const recomputeViewFieldIdentifierAfterFlatObjectIdentifierUpdate = ({
existingFlatObjectMetadata,
flatViewFieldMaps,
flatViewMaps,
updatedLabelIdentifierFieldMetadataId,
}: RecomputeViewFieldIdentifierAfterFlatObjectIdentifierUpdateArgs): FlatViewFieldToCreateAndUpdate => {
const flatViews = findManyFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityMaps: flatViewMaps,
flatEntityIds: existingFlatObjectMetadata.viewIds,
});
const accumulator: FlatViewFieldToCreateAndUpdate = {
flatViewFieldToCreate: [],
flatViewFieldToUpdate: [],
};
for (const flatView of flatViews) {
const flatViewFields = findManyFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityIds: flatView.viewFieldIds,
flatEntityMaps: flatViewFieldMaps,
});
const labelMetadataIdentifierViewField = flatViewFields.find(
(viewField) =>
viewField.fieldMetadataId === updatedLabelIdentifierFieldMetadataId,
);
const ascSortedViewFieldPositions = flatViewFields
.map(({ position }) => position)
.sort((a, b) => a - b);
const lowestViewFieldPosition =
ascSortedViewFieldPositions.length > 0
? ascSortedViewFieldPositions[0]
: 0;
if (!isDefined(labelMetadataIdentifierViewField)) {
const viewFieldId = v4();
const createdAt = new Date();
const flatViewFieldToCreate: FlatViewField = {
fieldMetadataId: updatedLabelIdentifierFieldMetadataId,
position: lowestViewFieldPosition - 1,
isVisible: true,
size: DEFAULT_VIEW_FIELD_SIZE,
viewId: flatView.id,
workspaceId: flatView.workspaceId,
id: viewFieldId,
createdAt: createdAt,
updatedAt: createdAt,
deletedAt: null,
universalIdentifier: viewFieldId,
aggregateOperation: null,
};
accumulator.flatViewFieldToCreate.push(flatViewFieldToCreate);
} else if (
labelMetadataIdentifierViewField.position > lowestViewFieldPosition
) {
const updatedFlatViewField = {
...labelMetadataIdentifierViewField,
position: lowestViewFieldPosition - 1,
};
accumulator.flatViewFieldToUpdate.push(updatedFlatViewField);
}
}
return accumulator;
};
@@ -6,9 +6,9 @@ import {
import { computeMorphRelationFieldName } from 'twenty-shared/utils';
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { findObjectFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
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';
@@ -24,10 +24,11 @@ export const renameRelatedMorphFieldOnObjectNamesUpdate = ({
toFlatObjectMetadata,
flatFieldMetadataMaps,
}: RenameRelatedMorphFieldOnObjectNamesUpdateArgs): FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[] => {
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
flatFieldMetadataMaps,
flatObjectMetadata: fromFlatObjectMetadata,
});
const objectFlatFieldMetadatas =
findManyFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityMaps: flatFieldMetadataMaps,
flatEntityIds: fromFlatObjectMetadata.fieldMetadataIds,
});
const manyToOneMorphRelationFlatFieldMetadatas =
getFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrow({
flatFieldMetadataMaps,