[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:
+1
@@ -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',
|
||||
|
||||
+12
-1
@@ -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<
|
||||
|
||||
+2
@@ -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[];
|
||||
};
|
||||
|
||||
+1
@@ -54,6 +54,7 @@ export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCre
|
||||
const createdAt = new Date();
|
||||
const flatObjectMetadataToCreate: FlatObjectMetadata = {
|
||||
fieldMetadataIds: [],
|
||||
viewIds: [],
|
||||
indexMetadataIds: [],
|
||||
createdAt,
|
||||
updatedAt: createdAt,
|
||||
|
||||
+6
-5
@@ -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)) {
|
||||
|
||||
+1
@@ -16,6 +16,7 @@ export const fromObjectMetadataEntityToFlatObjectMetadata = (
|
||||
|
||||
return {
|
||||
...objectMetadataEntityWithoutRelations,
|
||||
viewIds: objectMetadataEntity.views.map((viewEntity) => viewEntity.id),
|
||||
indexMetadataIds: objectMetadataEntity.indexMetadatas.map(
|
||||
(indexEntity) => indexEntity.id,
|
||||
),
|
||||
|
||||
+231
@@ -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,
|
||||
);
|
||||
};
|
||||
-182
@@ -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,
|
||||
);
|
||||
};
|
||||
+6
-5
@@ -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({
|
||||
|
||||
+85
@@ -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
-5
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user