Extract field metadata build out of object metadata build (#14763)

# Introduction

Extracting the legacy fields build and dispatch out of the object one to
follow the generic flat entity build, also update caches entries for
object

## Main tasks
- flat field map cache
- flat field builder
- refactored the dispatch matrix to return flat entity maps instead of
flat entity arrays
- orchestrator aggregator
- removing legacy code
- making universal identifier aka standardId of standard field for
custom object deterministically dynamic
- perfs debug logs for v2

## TODO
- [x] Refactor the generic entity builder to be dependency flat maps in
order to main foreign keys list in flat parent
- [x] Refactor the flat object metadata to contain the array of related
fields and avoid costy find object fields
- [ ] Improve the create field handler to handle multiple field at once
- [ ] Refactor the dispatch to embbed the comparison
- [ ] Improve perf by extracting from elements out of existing
- [ ] Fix the labelIdentifierId validators on object before field
creation ( integ tests are in failing mode )

## Debug logs snippet

```ts
[EntityBuilder fieldMetadata] matrix computation: 0.027ms
[EntityBuilder fieldMetadata] creation validation: 0.001ms
[EntityBuilder fieldMetadata] deletion validation: 0.293ms
[EntityBuilder fieldMetadata] update validation: 0.006ms
[EntityBuilder fieldMetadata] entity processing: 0.363ms
[EntityBuilder fieldMetadata] validateAndBuild: 0.455ms
[EntityBuilder index] matrix computation: 0.005ms
[EntityBuilder index] creation validation: 0.001ms
[EntityBuilder index] deletion validation: 0.146ms
[EntityBuilder index] update validation: 0.004ms
[EntityBuilder index] entity processing: 0.199ms
[EntityBuilder index] validateAndBuild: 0.228ms
[Runner] Initial cache retrieval: 0.549ms
[BaseWorkspaceMigrationRunnerActionHandlerService] delete_index executeForWorkspaceSchema: 11.665ms
[BaseWorkspaceMigrationRunnerActionHandlerService] delete_index executeForMetadata: 12.864ms
[BaseWorkspaceMigrationRunnerActionHandlerService] delete_field executeForWorkspaceSchema: 1.476ms
[BaseWorkspaceMigrationRunnerActionHandlerService] delete_field executeForMetadata: 6.816ms
[BaseWorkspaceMigrationRunnerActionHandlerService] delete_field executeForWorkspaceSchema: 0.062ms
[BaseWorkspaceMigrationRunnerActionHandlerService] delete_field executeForMetadata: 0.889ms
[Runner] Transaction execution: 23.434ms
[Runner] Cache invalidation: 316.662ms
[Runner] Total execution: 340.767ms
```
As you can see cache invalidation is way to long, we could replace the
cache by the optimistic in the end
This commit is contained in:
Paul Rastoin
2025-10-02 18:07:34 +02:00
committed by GitHub
parent d4b83df3f5
commit 170f6d27c5
240 changed files with 11385 additions and 17744 deletions
@@ -13,9 +13,9 @@ import { type UpdateObjectAction } from 'src/engine/workspace-manager/workspace-
import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
export const compareTwoFlatObjectMetadata = ({
from,
to,
}: FromTo<FlatObjectMetadata>) => {
fromFlatObjectMetadata,
toFlatObjectMetadata,
}: FromTo<FlatObjectMetadata, 'flatObjectMetadata'>) => {
const transformMetadataForComparisonParameters = {
propertiesToStringify: FLAT_OBJECT_METADATA_JSONB_PROPERTIES,
shouldIgnoreProperty: (
@@ -41,11 +41,11 @@ export const compareTwoFlatObjectMetadata = ({
},
};
const fromCompare = transformMetadataForComparison(
from,
fromFlatObjectMetadata,
transformMetadataForComparisonParameters,
);
const toCompare = transformMetadataForComparison(
to,
toFlatObjectMetadata,
transformMetadataForComparisonParameters,
);
const objectMetadataDifference = diff(fromCompare, omit(toCompare, 'fields'));
@@ -1,30 +0,0 @@
import { isDefined } from 'twenty-shared/utils';
import { type MorphOrRelationFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/morph-or-relation-field-metadata-type.type';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { findRelationFlatFieldMetadataTargetFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-relation-flat-field-metadatas-target-flat-field-metadata.util';
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
import { getSubFlatObjectMetadataMapsOutOfFlatFieldMetadatas } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps-out-of-flat-field-metadatas.util';
export const computeMorphOrRelationTargetFlatObjectMetadataMaps = ({
flatFieldMetadata,
flatObjectMetadataMaps,
}: {
flatFieldMetadata: FlatFieldMetadata<MorphOrRelationFieldMetadataType>;
flatObjectMetadataMaps: FlatObjectMetadataMaps;
}): FlatObjectMetadataMaps | undefined => {
const relationTargetFlatFieldMetadata =
findRelationFlatFieldMetadataTargetFlatFieldMetadata({
flatFieldMetadata,
flatObjectMetadataMaps,
});
if (!isDefined(relationTargetFlatFieldMetadata)) {
return undefined;
}
return getSubFlatObjectMetadataMapsOutOfFlatFieldMetadatas({
flatObjectMetadataMaps,
flatFieldMetadatas: [relationTargetFlatFieldMetadata],
});
};
@@ -1,26 +0,0 @@
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
export const deleteFlatFieldMetadataFromFlatObjectMetadatas = ({
flatFieldMetadata: flatFieldMetadataToRemove,
flatObjectMetadatas,
}: {
flatObjectMetadatas: FlatObjectMetadata[];
flatFieldMetadata: FlatFieldMetadata;
}) => {
return flatObjectMetadatas.map((flatObjectMetadata) => {
if (flatObjectMetadata.id !== flatFieldMetadataToRemove.objectMetadataId) {
return flatObjectMetadata;
}
const flatFieldMetadatas = flatObjectMetadata.flatFieldMetadatas.filter(
({ id: flatFieldMetadataId }) =>
flatFieldMetadataId !== flatFieldMetadataToRemove.id,
);
return {
...flatObjectMetadata,
flatFieldMetadatas,
};
});
};
@@ -1,121 +0,0 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { type MorphOrRelationFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/morph-or-relation-field-metadata-type.type';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { type FlatRelationTargetFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-relation-target-field-metadata.type';
import { fromObjectMetadataItemWithFieldMapsToFlatObjectMetadataWithoutFields } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-object-metadata-item-with-field-maps-to-flat-object-metadata-without-fields.util';
import { type CachedFieldMetadataEntity } from 'src/engine/metadata-modules/types/cached-field-metadata-entity';
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
import {
WorkspaceMetadataCacheException,
WorkspaceMetadataCacheExceptionCode,
} from 'src/engine/metadata-modules/workspace-metadata-cache/exceptions/workspace-metadata-cache.exception';
import { isCachedFieldMetadataEntityOfType } from 'src/engine/utils/is-cached-field-metadata-of-type.util';
const fromCachedRelationFieldMetadataEntityToFlatRelationTargetFieldMetadata = (
cachedFieldMetadataEntity: CachedFieldMetadataEntity<MorphOrRelationFieldMetadataType>,
): FlatRelationTargetFieldMetadata => {
return {
...cachedFieldMetadataEntity,
universalIdentifier:
cachedFieldMetadataEntity.standardId ?? cachedFieldMetadataEntity.id,
type: cachedFieldMetadataEntity.type,
};
};
type CachedFieldMetadataEntityAndObjectMetadataMap<
T extends FieldMetadataType,
> = {
objectMetadataMaps: ObjectMetadataMaps;
cachedFieldMetadataEntity: CachedFieldMetadataEntity<T>;
};
export const fromCachedFieldMetadataEntityToFlatFieldMetadata = <
T extends FieldMetadataType,
>(
{
cachedFieldMetadataEntity,
objectMetadataMaps,
}: CachedFieldMetadataEntityAndObjectMetadataMap<T>,
// This is intended to be abstract return type
): FlatFieldMetadata => {
if (
isCachedFieldMetadataEntityOfType(
cachedFieldMetadataEntity,
FieldMetadataType.RELATION,
) ||
isCachedFieldMetadataEntityOfType(
cachedFieldMetadataEntity,
FieldMetadataType.MORPH_RELATION,
)
) {
const relationTargetObjectMetadataItemWithFieldsMaps =
objectMetadataMaps.byId[
cachedFieldMetadataEntity.relationTargetObjectMetadataId
];
if (!isDefined(relationTargetObjectMetadataItemWithFieldsMaps)) {
throw new WorkspaceMetadataCacheException(
'Cache object is not up to date',
WorkspaceMetadataCacheExceptionCode.OBJECT_METADATA_MAP_NOT_FOUND,
);
}
const flatRelationTargetObjectMetadata =
fromObjectMetadataItemWithFieldMapsToFlatObjectMetadataWithoutFields(
relationTargetObjectMetadataItemWithFieldsMaps,
);
const relationTargetCachedFieldMetadata =
relationTargetObjectMetadataItemWithFieldsMaps.fieldsById[
cachedFieldMetadataEntity.relationTargetFieldMetadataId
];
if (!isDefined(relationTargetCachedFieldMetadata)) {
throw new WorkspaceMetadataCacheException(
'Cache field is not up to date',
WorkspaceMetadataCacheExceptionCode.FIELD_METADATA_NOT_FOUND,
);
}
if (
!isCachedFieldMetadataEntityOfType(
relationTargetCachedFieldMetadata,
FieldMetadataType.RELATION,
) &&
!isCachedFieldMetadataEntityOfType(
relationTargetCachedFieldMetadata,
FieldMetadataType.MORPH_RELATION,
)
) {
throw new WorkspaceMetadataCacheException(
'Cache field is invalid',
WorkspaceMetadataCacheExceptionCode.FIELD_METADATA_INVALID,
);
}
const flatRelationTargetFieldMetadata =
fromCachedRelationFieldMetadataEntityToFlatRelationTargetFieldMetadata(
relationTargetCachedFieldMetadata,
);
return {
...cachedFieldMetadataEntity,
universalIdentifier:
cachedFieldMetadataEntity.standardId ?? cachedFieldMetadataEntity.id,
flatRelationTargetFieldMetadata,
flatRelationTargetObjectMetadata,
type: cachedFieldMetadataEntity.type,
} satisfies FlatFieldMetadata<
FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION
>;
}
return {
...cachedFieldMetadataEntity,
universalIdentifier:
cachedFieldMetadataEntity.standardId ?? cachedFieldMetadataEntity.id,
flatRelationTargetFieldMetadata: null,
flatRelationTargetObjectMetadata: null,
};
};
@@ -4,9 +4,9 @@ import {
} from 'twenty-shared/utils';
import { v4 } from 'uuid';
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.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 { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { type CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input';
import { buildDefaultFlatFieldMetadatasForCustomObject } from 'src/engine/metadata-modules/object-metadata/utils/build-default-flat-field-metadatas-for-custom-object.util';
@@ -17,16 +17,16 @@ type FromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCreateArgs
{
createObjectInput: Omit<CreateObjectInput, 'workspaceId'>;
workspaceId: string;
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
};
} & Pick<AllFlatEntityMaps, 'flatObjectMetadataMaps'>;
export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCreate =
({
createObjectInput: rawCreateObjectInput,
workspaceId,
existingFlatObjectMetadataMaps,
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
}: FromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCreateArgs): {
flatObjectMetadataToCreate: FlatObjectMetadata;
relationTargetFlatFieldMetadataToCreate: FlatFieldMetadata[];
flatFieldMetadataToCreateOnObject: FlatFieldMetadata[];
flatIndexMetadataToCreate: FlatIndexMetadata[];
} => {
const createObjectInput =
@@ -53,8 +53,9 @@ export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCre
});
const createdAt = new Date();
const flatObjectMetadataToCreate: FlatObjectMetadata = {
fieldMetadataIds: [],
indexMetadataIds: [],
createdAt,
flatFieldMetadatas: [],
updatedAt: createdAt,
duplicateCriteria: null,
description: createObjectInput.description ?? null,
@@ -93,12 +94,13 @@ export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCre
workspaceId,
});
flatObjectMetadataToCreate.flatFieldMetadatas = [
const objectFlatFieldMetadatas: FlatFieldMetadata[] = [
...Object.values(defaultFlatFieldForCustomObjectMaps.fields),
...standardSourceFlatFieldMetadatas,
];
const defaultIndexesForCustomObject = buildDefaultIndexesForCustomObject({
objectFlatFieldMetadatas,
defaultFlatFieldForCustomObjectMaps,
flatObjectMetadata: flatObjectMetadataToCreate,
workspaceId,
@@ -106,9 +108,10 @@ export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCre
return {
flatObjectMetadataToCreate,
relationTargetFlatFieldMetadataToCreate: standardTargetFlatFieldMetadatas,
flatIndexMetadataToCreate: Object.values(
defaultIndexesForCustomObject.indexes,
),
relationTargetFlatFieldMetadataToCreate: standardTargetFlatFieldMetadatas,
flatFieldMetadataToCreateOnObject: objectFlatFieldMetadatas,
};
};
@@ -3,13 +3,13 @@ import {
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties,
} from 'twenty-shared/utils';
import { type FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
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 { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { findObjectFieldsInFlatFieldMetadataMapsOrThrow } 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';
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
import { findFlatObjectMetadataInFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps.util';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { type DeleteOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/delete-object.input';
import {
@@ -19,13 +19,15 @@ import {
type FromDeleteObjectInputToFlatFieldMetadatasToDeleteArgs = {
deleteObjectInput: DeleteOneObjectInput;
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
existingFlatIndexMaps: FlatEntityMaps<FlatIndexMetadata>;
};
} & Pick<
AllFlatEntityMaps,
'flatFieldMetadataMaps' | 'flatObjectMetadataMaps' | 'flatIndexMaps'
>;
export const fromDeleteObjectInputToFlatFieldMetadatasToDelete = ({
deleteObjectInput: rawDeleteObjectInput,
existingFlatObjectMetadataMaps,
existingFlatIndexMaps,
flatFieldMetadataMaps,
flatObjectMetadataMaps,
flatIndexMaps,
// This should return an AllFlatEntityMaps
}: FromDeleteObjectInputToFlatFieldMetadatasToDeleteArgs): {
flatFieldMetadatasToDelete: FlatFieldMetadata[];
@@ -38,11 +40,10 @@ export const fromDeleteObjectInputToFlatFieldMetadatasToDelete = ({
['id'],
);
const flatObjectMetadataToDelete =
findFlatObjectMetadataInFlatObjectMetadataMaps({
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
objectMetadataId: objectMetadataToDeleteId,
});
const flatObjectMetadataToDelete = findFlatEntityByIdInFlatEntityMapsOrThrow({
flatEntityMaps: flatObjectMetadataMaps,
flatEntityId: objectMetadataToDeleteId,
});
if (!isDefined(flatObjectMetadataToDelete)) {
throw new ObjectMetadataException(
@@ -51,27 +52,29 @@ export const fromDeleteObjectInputToFlatFieldMetadatasToDelete = ({
);
}
const flatFieldMetadatasToDelete =
flatObjectMetadataToDelete.flatFieldMetadatas.flatMap(
(flatFieldMetadata) => {
if (isMorphOrRelationFlatFieldMetadata(flatFieldMetadata)) {
const relationTargetFlatFieldMetadata =
findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow({
flatFieldMetadata,
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
});
const { objectFlatFieldMetadatas } =
findObjectFieldsInFlatFieldMetadataMapsOrThrow({
flatFieldMetadataMaps,
flatObjectMetadata: flatObjectMetadataToDelete,
});
const flatFieldMetadatasToDelete = objectFlatFieldMetadatas.flatMap(
(flatFieldMetadata) => {
if (isMorphOrRelationFlatFieldMetadata(flatFieldMetadata)) {
const relationTargetFlatFieldMetadata =
findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow({
flatFieldMetadata,
flatFieldMetadataMaps,
});
return [flatFieldMetadata, relationTargetFlatFieldMetadata];
}
return [flatFieldMetadata, relationTargetFlatFieldMetadata];
}
return [flatFieldMetadata];
},
);
return [flatFieldMetadata];
},
);
// TODO We should maintain a idsByObjectMetadataId in the flatIndexMaps
const flatIndexMetadataToDelete = Object.values(
existingFlatIndexMaps.byId,
).filter(
const flatIndexMetadataToDelete = Object.values(flatIndexMaps.byId).filter(
(flatIndex): flatIndex is FlatIndexMetadata =>
isDefined(flatIndex) &&
flatIndex.objectMetadataId === flatObjectMetadataToDelete.id,
@@ -1,13 +0,0 @@
import { isDefined } from 'twenty-shared/utils';
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { fromFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-with-flat-field-maps-to-flat-object-metadatas.util';
export const fromFlatObjectMetadataMapsToFlatObjectMetadatas = (
flatObjectMetadataMaps: FlatObjectMetadataMaps,
): FlatObjectMetadata[] => {
return Object.values(flatObjectMetadataMaps.byId)
.filter(isDefined)
.map(fromFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadata);
};
@@ -1,25 +0,0 @@
import { type FlatObjectMetadataWithFlatFieldMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-with-flat-field-metadata-maps.type';
import { addFlatFieldMetadataToFlatObjectMetadataWithFlatFieldMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-field-metadata-to-flat-object-metadata-with-flat-field-maps-or-throw.util';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
export const fromFlatObjectMetadataToFlatObjectMetadataWithFlatFieldMaps = (
flatObjectMetadata: FlatObjectMetadata,
): FlatObjectMetadataWithFlatFieldMaps => {
const emptyFlatObjectMetadataWithFlatFieldMaps: FlatObjectMetadataWithFlatFieldMaps =
{
...flatObjectMetadata,
flatFieldMetadatas: [],
fieldIdByJoinColumnName: {},
fieldIdByName: {},
fieldsById: {},
};
return flatObjectMetadata.flatFieldMetadatas.reduce(
(flatObjectMetadataWithFlatFieldMaps, flatFieldMetadata) =>
addFlatFieldMetadataToFlatObjectMetadataWithFlatFieldMapsOrThrow({
flatFieldMetadata,
flatObjectMetadataWithFlatFieldMaps,
}),
emptyFlatObjectMetadataWithFlatFieldMaps,
);
};
@@ -1,11 +1,8 @@
import {
type FlatObjectMetadataWithoutFields,
type FlatObjectMetadata,
} from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { type ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto';
export const fromFlatObjectMetadataToObjectMetadataDto = (
flatObjectMetadata: FlatObjectMetadata | FlatObjectMetadataWithoutFields,
flatObjectMetadata: FlatObjectMetadata,
): ObjectMetadataDTO => {
const {
createdAt,
@@ -1,14 +0,0 @@
import { removePropertiesFromRecord } from 'twenty-shared/utils';
import { type FlatObjectMetadataWithFlatFieldMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-with-flat-field-metadata-maps.type';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
export const fromFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadata = (
flatObjectMetadataWithFlatFieldMaps: FlatObjectMetadataWithFlatFieldMaps,
): FlatObjectMetadata => {
return removePropertiesFromRecord(flatObjectMetadataWithFlatFieldMaps, [
'fieldsById',
'fieldIdByName',
'fieldIdByJoinColumnName',
]);
};
@@ -1,16 +0,0 @@
import { EMPTY_FLAT_OBJECT_METADATA_MAPS } from 'src/engine/metadata-modules/flat-object-metadata-maps/constant/empty-flat-object-metadata-maps.constant';
import { addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-object-metadata-to-flat-object-metadata-maps-or-throw.util';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
export const fromFlatObjectMetadatasToFlatObjectMetadataMaps = (
flatObjectMetadatas: FlatObjectMetadata[],
) => {
return flatObjectMetadatas.reduce(
(flatObjectMetadataMaps, flatObjectMetadata) =>
addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow({
flatObjectMetadata,
flatObjectMetadataMaps,
}),
EMPTY_FLAT_OBJECT_METADATA_MAPS,
);
};
@@ -1,6 +1,5 @@
import { removePropertiesFromRecord } from 'twenty-shared/utils';
import { fromFieldMetadataEntityToFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-field-metadata-entity-to-flat-field-metadata.util';
import {
type FlatObjectMetadata,
objectMetadataEntityRelationProperties,
@@ -17,11 +16,14 @@ export const fromObjectMetadataEntityToFlatObjectMetadata = (
return {
...objectMetadataEntityWithoutRelations,
indexMetadataIds: objectMetadataEntity.indexMetadatas.map(
(indexEntity) => indexEntity.id,
),
fieldMetadataIds: objectMetadataEntity.fields.map(
(fieldEntity) => fieldEntity.id,
),
universalIdentifier:
objectMetadataEntityWithoutRelations.standardId ??
objectMetadataEntityWithoutRelations.id,
flatFieldMetadatas: objectMetadataEntity.fields.map(
fromFieldMetadataEntityToFlatFieldMetadata,
),
};
};
@@ -1,22 +0,0 @@
import { type FlatObjectMetadataWithoutFields } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
import { fromObjectMetadataEntityToFlatObjectMetadataWithoutFields } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/from-object-metadata-entity-to-flat-object-metadata-without-fields.util';
export const fromObjectMetadataItemWithFieldMapsToFlatObjectMetadataWithoutFields =
(
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
): FlatObjectMetadataWithoutFields => {
const {
fieldsById: _fieldsById,
fieldIdByJoinColumnName: _fieldIdByJoinColumnName,
fieldIdByName: _fieldIdByName,
indexMetadatas,
...rest
} = objectMetadataItemWithFieldMaps;
return fromObjectMetadataEntityToFlatObjectMetadataWithoutFields({
...rest,
fields: [],
indexMetadatas,
});
};
@@ -1,39 +0,0 @@
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { fromCachedFieldMetadataEntityToFlatFieldMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-cached-field-metadata-entity-to-flat-field-metadata.util';
import { type CachedFieldMetadataEntity } from 'src/engine/metadata-modules/types/cached-field-metadata-entity';
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
type FromObjectMetadataItemWithFieldMapsToFlatObjectMetadataArgs = {
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps;
objectMetadataMaps: ObjectMetadataMaps;
};
export const fromObjectMetadataItemWithFieldMapsToFlatObjectMetadata = ({
objectMetadataItemWithFieldMaps,
objectMetadataMaps,
}: FromObjectMetadataItemWithFieldMapsToFlatObjectMetadataArgs): FlatObjectMetadata => {
const {
fieldsById,
fieldIdByJoinColumnName: _fieldIdByJoinColumnName,
fieldIdByName: _fieldIdByName,
indexMetadatas: _indexMetadatas,
...rest
} = objectMetadataItemWithFieldMaps;
const cachedFieldMetadataEntities =
Object.values<CachedFieldMetadataEntity>(fieldsById);
const flatFieldMetadatas = cachedFieldMetadataEntities.map(
(cachedFieldMetadataEntity) =>
fromCachedFieldMetadataEntityToFlatFieldMetadata({
cachedFieldMetadataEntity,
objectMetadataMaps,
}),
);
return {
...rest,
flatFieldMetadatas,
universalIdentifier: rest.standardId ?? rest.id,
};
};
@@ -5,10 +5,9 @@ import {
} 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 { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
import { findFlatObjectMetadataInFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps.util';
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';
@@ -24,9 +23,11 @@ import { type ObjectMetadataStandardOverridesProperties } from 'src/engine/metad
import { isStandardMetadata } from 'src/engine/metadata-modules/utils/is-standard-metadata.util';
type FromUpdateObjectInputToFlatObjectMetadataArgs = {
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
updateObjectInput: UpdateOneObjectInput;
} & Pick<AllFlatEntityMaps, 'flatIndexMaps'>;
} & Pick<
AllFlatEntityMaps,
'flatIndexMaps' | 'flatObjectMetadataMaps' | 'flatFieldMetadataMaps'
>;
const objectMetadataEditableProperties =
FLAT_OBJECT_METADATA_PROPERTIES_TO_COMPARE.filter(
@@ -45,9 +46,10 @@ type UpdatedFlatObjectAndOtherObjectFieldMetadatas = {
};
export const fromUpdateObjectInputToFlatObjectMetadata = ({
existingFlatObjectMetadataMaps,
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
updateObjectInput: rawUpdateObjectInput,
flatIndexMaps,
flatFieldMetadataMaps,
}: FromUpdateObjectInputToFlatObjectMetadataArgs): UpdatedFlatObjectAndOtherObjectFieldMetadatas => {
const { id: objectMetadataIdToUpdate } =
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
@@ -59,11 +61,10 @@ export const fromUpdateObjectInputToFlatObjectMetadata = ({
objectMetadataEditableProperties,
);
const flatObjectMetadataToUpdate =
findFlatObjectMetadataInFlatObjectMetadataMaps({
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
objectMetadataId: objectMetadataIdToUpdate,
});
const flatObjectMetadataToUpdate = findFlatEntityByIdInFlatEntityMaps({
flatEntityMaps: existingFlatObjectMetadataMaps,
flatEntityId: objectMetadataIdToUpdate,
});
if (!isDefined(flatObjectMetadataToUpdate)) {
throw new ObjectMetadataException(
@@ -148,7 +149,7 @@ export const fromUpdateObjectInputToFlatObjectMetadata = ({
const newUpdatedOtherObjectFlatFieldMetadatas =
property === 'nameSingular' || property === 'namePlural'
? renameRelatedMorphFieldOnObjectNamesUpdate({
existingFlatObjectMetadataMaps,
flatFieldMetadataMaps,
fromFlatObjectMetadata: updatedFlatObjectMetadata,
toFlatObjectMetadata: updatedFlatObjectMetadata,
})
@@ -157,6 +158,7 @@ export const fromUpdateObjectInputToFlatObjectMetadata = ({
const newUpdatedFlatIndexMetadatas =
property === 'nameSingular'
? recomputeIndexAfterFlatObjectMetadataSingularNameUpdate({
flatFieldMetadataMaps,
existingFlatObjectMetadata: flatObjectMetadataToUpdate,
flatIndexMaps,
updatedSingularName: updatedFlatObjectMetadata.nameSingular,
@@ -1,46 +1,40 @@
import { FieldMetadataType } from 'twenty-shared/types';
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 { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
import { findFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
type GetFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrowArgs = {
flatObjectMetadata: FlatObjectMetadata;
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
};
objectFlatFieldMetadatas: FlatFieldMetadata[];
} & Pick<AllFlatEntityMaps, 'flatFieldMetadataMaps'>;
export const getFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrow =
({
flatObjectMetadata,
existingFlatObjectMetadataMaps,
objectFlatFieldMetadatas,
flatFieldMetadataMaps,
}: GetFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrowArgs): FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[] => {
const manyToOneMorphRelationTargetFlatFieldMetadataIds =
flatObjectMetadata.flatFieldMetadatas
.filter(
(
flatFieldMetadata,
): flatFieldMetadata is FlatFieldMetadata<FieldMetadataType.RELATION> =>
isFlatFieldMetadataOfType(
flatFieldMetadata,
FieldMetadataType.RELATION,
) &&
isFlatFieldMetadataOfType(
flatFieldMetadata.flatRelationTargetFieldMetadata,
FieldMetadataType.MORPH_RELATION,
),
return objectFlatFieldMetadatas.flatMap((flatFieldMetadata) => {
if (
!isFlatFieldMetadataOfType(
flatFieldMetadata,
FieldMetadataType.RELATION,
)
.map(({ id: fieldMetadataId, objectMetadataId }) => ({
fieldMetadataId,
objectMetadataId,
}));
) {
return [];
}
return manyToOneMorphRelationTargetFlatFieldMetadataIds.map(
({ fieldMetadataId, objectMetadataId }) =>
findFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
fieldMetadataId,
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
objectMetadataId,
}),
) as FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[];
const targetFlatFieldMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow(
{
flatEntityId: flatFieldMetadata.relationTargetFieldMetadataId,
flatEntityMaps: flatFieldMetadataMaps,
},
);
return isFlatFieldMetadataOfType(
targetFlatFieldMetadata,
FieldMetadataType.MORPH_RELATION,
)
? [targetFlatFieldMetadata]
: [];
});
};
@@ -1,6 +1,7 @@
import { isDefined } from 'twenty-shared/utils';
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
import { findObjectFieldsInFlatFieldMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-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';
@@ -8,11 +9,12 @@ import { generateFlatIndexMetadataWithNameOrThrow } from 'src/engine/metadata-mo
type RecomputeIndexAfterFlatObjectMetadataSingularNameUpdateArgs = {
existingFlatObjectMetadata: FlatObjectMetadata;
updatedSingularName: string;
} & Pick<AllFlatEntityMaps, 'flatIndexMaps'>;
} & Pick<AllFlatEntityMaps, 'flatIndexMaps' | 'flatFieldMetadataMaps'>;
export const recomputeIndexAfterFlatObjectMetadataSingularNameUpdate = ({
existingFlatObjectMetadata,
flatIndexMaps,
updatedSingularName,
flatFieldMetadataMaps,
}: RecomputeIndexAfterFlatObjectMetadataSingularNameUpdateArgs): FlatIndexMetadata[] => {
const allRelatedFlatIndexMetadata = Object.values(flatIndexMaps.byId).filter(
(flatIndexMetadata): flatIndexMetadata is FlatIndexMetadata =>
@@ -29,10 +31,17 @@ export const recomputeIndexAfterFlatObjectMetadataSingularNameUpdate = ({
nameSingular: updatedSingularName,
};
const { objectFlatFieldMetadatas } =
findObjectFieldsInFlatFieldMetadataMapsOrThrow({
flatFieldMetadataMaps,
flatObjectMetadata: optimisticFlatObjectMetadata,
});
return allRelatedFlatIndexMetadata.map<FlatIndexMetadata>((flatIndex) => {
const newIndex = generateFlatIndexMetadataWithNameOrThrow({
flatIndex,
flatObjectMetadata: optimisticFlatObjectMetadata,
objectFlatFieldMetadatas,
});
return newIndex;
@@ -4,10 +4,11 @@ import {
type FromTo,
} from 'twenty-shared/types';
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
import { computeMorphRelationFieldName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-relation-field-name.util';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
import { findObjectFieldsInFlatFieldMetadataMapsOrThrow } 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';
@@ -34,19 +35,23 @@ const searchAndReplaceLast = ({
type RenameRelatedMorphFieldOnObjectNamesUpdateArgs = FromTo<
FlatObjectMetadata,
'flatObjectMetadata'
> & {
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
};
> &
Pick<AllFlatEntityMaps, 'flatFieldMetadataMaps'>;
// TODO We should recompute each index here too
export const renameRelatedMorphFieldOnObjectNamesUpdate = ({
fromFlatObjectMetadata,
existingFlatObjectMetadataMaps,
toFlatObjectMetadata,
flatFieldMetadataMaps,
}: RenameRelatedMorphFieldOnObjectNamesUpdateArgs): FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[] => {
const { objectFlatFieldMetadatas } =
findObjectFieldsInFlatFieldMetadataMapsOrThrow({
flatFieldMetadataMaps,
flatObjectMetadata: fromFlatObjectMetadata,
});
const manyToOneMorphRelationFlatFieldMetadatas =
getFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrow({
flatObjectMetadata: fromFlatObjectMetadata,
existingFlatObjectMetadataMaps,
flatFieldMetadataMaps,
objectFlatFieldMetadatas,
});
const updatedFlatFieldMetadatas =