Refactor flat entity maps to be universal oriented (#17665)

# Introduction
In preparation of the workspace agnostic builder, we're migrating
`FlatEntityMaps` to be universal identifier oriented and based
As in the builder context there're won't be any ids at all
Please also note that the FlatEntity is a UniversalFlatEntity superset

From
```ts
import { type SyncableFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';

export type FlatEntityMaps<T extends SyncableFlatEntity> = {
  byId: Partial<Record<string, T>>;
  idByUniversalIdentifier: Partial<Record<string, string>>;
  universalIdentifiersByApplicationId: Partial<Record<string, string[]>>;
};
```

To
```ts
export type FlatEntityMaps<
  T extends SyncableFlatEntity | UniversalSyncableFlatEntity,
> = {
  byUniversalIdentifier: Partial<Record<string, T>>;
  universalIdentifierById: Partial<Record<string, string>>;
  universalIdentifiersByApplicationId: Partial<Record<string, string[]>>; // this might make more sense to be migrated to universalIdentifiersByApplicationUniversalIdentifier but it's the main topic of this PR
};
```

## Low level maps tools
Had to refactor find | create | delete | replace | find-many | get-sub
tools ( through mutations and or throw equivalent )
This commit is contained in:
Paul Rastoin
2026-02-03 17:16:02 +01:00
committed by GitHub
parent 43042d55b2
commit 476bdf764c
202 changed files with 1853 additions and 909 deletions
@@ -4,6 +4,7 @@ import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-enti
import { type MetadataFlatEntityAndRelatedFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-types.type';
import { addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/add-flat-entity-to-flat-entity-and-related-entity-maps-through-mutation-or-throw.util';
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
import { getFlatFieldMetadataMock } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/get-flat-field-metadata.mock';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
@@ -72,21 +73,26 @@ describe('addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow',
});
expect(
flatEntityAndRelatedMapsToMutate.flatViewMaps.byId[mockView.id],
findFlatEntityByIdInFlatEntityMaps({
flatEntityId: mockView.id,
flatEntityMaps: flatEntityAndRelatedMapsToMutate.flatViewMaps,
}),
).toMatchObject(mockView);
expect(
flatEntityAndRelatedMapsToMutate.flatObjectMetadataMaps.byId[
objectMetadataId
],
findFlatEntityByIdInFlatEntityMaps({
flatEntityId: objectMetadataId,
flatEntityMaps: flatEntityAndRelatedMapsToMutate.flatObjectMetadataMaps,
}),
).toMatchObject<Partial<FlatObjectMetadata>>({
viewIds: [mockView.id],
});
expect(
flatEntityAndRelatedMapsToMutate.flatFieldMetadataMaps.byId[
mockFieldMetadata.id
],
findFlatEntityByIdInFlatEntityMaps({
flatEntityId: mockFieldMetadata.id,
flatEntityMaps: flatEntityAndRelatedMapsToMutate.flatFieldMetadataMaps,
}),
).toMatchObject<Partial<FlatFieldMetadata>>({
calendarViewIds: [mockView.id],
});
@@ -4,6 +4,7 @@ import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-enti
import { type MetadataFlatEntityAndRelatedFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-types.type';
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
import { deleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/delete-flat-entity-from-flat-entity-and-related-entity-maps-through-mutation-or-throw.util';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
import { getFlatFieldMetadataMock } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/get-flat-field-metadata.mock';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
@@ -82,21 +83,26 @@ describe('deleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOrThr
});
expect(
flatEntityAndRelatedMapsToMutate.flatViewMaps.byId[viewId],
findFlatEntityByIdInFlatEntityMaps({
flatEntityId: viewId,
flatEntityMaps: flatEntityAndRelatedMapsToMutate.flatViewMaps,
}),
).toBeUndefined();
expect(
flatEntityAndRelatedMapsToMutate.flatObjectMetadataMaps.byId[
objectMetadataId
],
findFlatEntityByIdInFlatEntityMaps({
flatEntityId: objectMetadataId,
flatEntityMaps: flatEntityAndRelatedMapsToMutate.flatObjectMetadataMaps,
}),
).toMatchObject<Partial<FlatObjectMetadata>>({
viewIds: ['something-else'],
});
expect(
flatEntityAndRelatedMapsToMutate.flatFieldMetadataMaps.byId[
mockFieldMetadata.id
],
findFlatEntityByIdInFlatEntityMaps({
flatEntityId: mockFieldMetadata.id,
flatEntityMaps: flatEntityAndRelatedMapsToMutate.flatFieldMetadataMaps,
}),
).toMatchObject<Partial<FlatFieldMetadata>>({
calendarViewIds: [],
});
@@ -18,7 +18,11 @@ export const addFlatEntityToFlatEntityMapsOrThrow = <
flatEntity,
flatEntityMaps,
}: AddFlatEntityToFlatEntityMapsOrThrowArgs<T>): FlatEntityMaps<T> => {
if (isDefined(flatEntityMaps.byId[flatEntity.id])) {
if (
isDefined(
flatEntityMaps.byUniversalIdentifier[flatEntity.universalIdentifier],
)
) {
throw new FlatEntityMapsException(
'addFlatEntityToFlatEntityMapsOrThrow: flat entity to add already exists',
FlatEntityMapsExceptionCode.ENTITY_ALREADY_EXISTS,
@@ -26,13 +30,13 @@ export const addFlatEntityToFlatEntityMapsOrThrow = <
}
return {
byId: {
...flatEntityMaps.byId,
[flatEntity.id]: flatEntity,
byUniversalIdentifier: {
...flatEntityMaps.byUniversalIdentifier,
[flatEntity.universalIdentifier]: flatEntity,
},
idByUniversalIdentifier: {
...flatEntityMaps.idByUniversalIdentifier,
[flatEntity.universalIdentifier]: flatEntity.id,
universalIdentifierById: {
...flatEntityMaps.universalIdentifierById,
[flatEntity.id]: flatEntity.universalIdentifier,
},
universalIdentifiersByApplicationId: {
...flatEntityMaps.universalIdentifiersByApplicationId,
@@ -4,7 +4,7 @@ import { type FlatEntityToCreateDeleteUpdate } from 'src/engine/metadata-modules
import { type MetadataFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity-maps.type';
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util';
import { getSubFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/get-sub-flat-entity-maps-or-throw.util';
import { getSubFlatEntityByIdsMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/get-sub-flat-entity-by-ids-maps-or-throw.util';
import { replaceFlatEntityInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/replace-flat-entity-in-flat-entity-maps-or-throw.util';
export type ComputeFlatEntityMapsFromToArgs<T extends AllMetadataName> = {
@@ -21,7 +21,7 @@ export const computeFlatEntityMapsFromTo = <T extends AllMetadataName>({
} => {
const fromFlatEntityMaps =
flatEntityToDelete.length > 0
? getSubFlatEntityMapsOrThrow({
? getSubFlatEntityByIdsMapsOrThrow({
flatEntityIds: [...flatEntityToDelete, ...flatEntityToUpdate].map(
({ id }) => id,
),
@@ -21,16 +21,19 @@ export const deleteFlatEntityFromFlatEntityMapsOrThrow = <
flatEntityMaps,
entityToDeleteId,
}: DeleteFlatEntityFromFlatEntityMapsOrThrowArgs<T>): FlatEntityMaps<T> => {
if (!isDefined(flatEntityMaps.byId[entityToDeleteId])) {
const universalIdentifierToDelete =
flatEntityMaps.universalIdentifierById[entityToDeleteId];
if (!isDefined(universalIdentifierToDelete)) {
throw new FlatEntityMapsException(
'deleteFlatEntityFromFlatEntityMapsOrThrow: entity to delete not found',
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
);
}
const updatedIdByUniversalIdentifierEntries = Object.entries(
flatEntityMaps.idByUniversalIdentifier,
).filter(([_universalIdentifier, id]) => id !== entityToDeleteId);
const updatedUniversalIdentifierByIdEntries = Object.entries(
flatEntityMaps.universalIdentifierById,
).filter(([id]) => id !== entityToDeleteId);
const updatedUniversalIdentifiersByApplicationIdEntries = Object.entries(
flatEntityMaps.universalIdentifiersByApplicationId,
@@ -38,10 +41,7 @@ export const deleteFlatEntityFromFlatEntityMapsOrThrow = <
.map(([applicationId, universalIdentifiers]) => {
const stillPresentUniversalIdentifiers = universalIdentifiers?.filter(
(universalIdentifier) =>
updatedIdByUniversalIdentifierEntries.some(
([existingUniversalIdentifier]) =>
existingUniversalIdentifier === universalIdentifier,
),
universalIdentifier !== universalIdentifierToDelete,
);
if (
@@ -56,9 +56,12 @@ export const deleteFlatEntityFromFlatEntityMapsOrThrow = <
.filter(isDefined);
return {
byId: removePropertiesFromRecord(flatEntityMaps.byId, [entityToDeleteId]),
idByUniversalIdentifier: Object.fromEntries(
updatedIdByUniversalIdentifierEntries,
byUniversalIdentifier: removePropertiesFromRecord(
flatEntityMaps.byUniversalIdentifier,
[universalIdentifierToDelete],
),
universalIdentifierById: Object.fromEntries(
updatedUniversalIdentifierByIdEntries,
),
universalIdentifiersByApplicationId: Object.fromEntries(
updatedUniversalIdentifiersByApplicationIdEntries,
@@ -19,13 +19,7 @@ export const findFlatEntitiesByApplicationId = <T extends SyncableFlatEntity>({
return universalIdentifiers
.map((universalId) => {
const id = flatEntityMaps.idByUniversalIdentifier[universalId];
if (!isDefined(id)) {
return undefined;
}
const entity = flatEntityMaps.byId[id];
const entity = flatEntityMaps.byUniversalIdentifier[universalId];
if (!isDefined(entity)) {
return undefined;
@@ -20,7 +20,17 @@ export const findFlatEntityByIdInFlatEntityMapsOrThrow = <
flatEntityMaps,
flatEntityId,
}: FindFlatEntityByIdInFlatEntityMapsOrThrowArgs<T>): T => {
const flatEntity = flatEntityMaps.byId[flatEntityId];
const universalIdentifier =
flatEntityMaps.universalIdentifierById[flatEntityId];
if (!isDefined(universalIdentifier)) {
throw new FlatEntityMapsException(
t`Could not find flat entity in maps`,
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
);
}
const flatEntity = flatEntityMaps.byUniversalIdentifier[universalIdentifier];
if (!isDefined(flatEntity)) {
throw new FlatEntityMapsException(
@@ -1,5 +1,3 @@
import { isDefined } from 'twenty-shared/utils';
import { type SyncableFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
@@ -11,20 +9,6 @@ export const findFlatEntityByUniversalIdentifier = <
}: {
flatEntityMaps: FlatEntityMaps<T>;
universalIdentifier: string;
}): (T & { id: string }) | undefined => {
const flatEntityId =
flatEntityMaps.idByUniversalIdentifier[universalIdentifier];
if (!isDefined(flatEntityId)) {
return;
}
const result = flatEntityMaps.byId[flatEntityId];
return isDefined(result)
? {
...result,
id: flatEntityId,
}
: undefined;
}): T | undefined => {
return flatEntityMaps.byUniversalIdentifier[universalIdentifier];
};
@@ -2,7 +2,7 @@ import { isDefined } from 'twenty-shared/utils';
import { type SyncableFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { getSubFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/get-sub-flat-entity-maps-or-throw.util';
import { getSubFlatEntityByIdsMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/get-sub-flat-entity-by-ids-maps-or-throw.util';
export type FindManyFlatEntityByIdInFlatEntityMapsOrThrowArgs<
T extends SyncableFlatEntity,
@@ -16,10 +16,12 @@ export const findManyFlatEntityByIdInFlatEntityMapsOrThrow = <
flatEntityMaps,
flatEntityIds,
}: FindManyFlatEntityByIdInFlatEntityMapsOrThrowArgs<T>): T[] => {
const subFlatEntityMaps = getSubFlatEntityMapsOrThrow<T>({
const subFlatEntityMaps = getSubFlatEntityByIdsMapsOrThrow<T>({
flatEntityIds,
flatEntityMaps,
});
return Object.values(subFlatEntityMaps.byId).filter(isDefined);
return Object.values(subFlatEntityMaps.byUniversalIdentifier).filter(
isDefined,
);
};
@@ -0,0 +1,28 @@
import { isDefined } from 'twenty-shared/utils';
import { type SyncableFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
export type FindManyFlatEntityByIdInFlatEntityMapsArgs<
T extends SyncableFlatEntity,
> = {
flatEntityMaps: FlatEntityMaps<T>;
flatEntityIds: string[];
};
export const findManyFlatEntityByIdInFlatEntityMaps = <
T extends SyncableFlatEntity,
>({
flatEntityMaps,
flatEntityIds,
}: FindManyFlatEntityByIdInFlatEntityMapsArgs<T>): T[] => {
return flatEntityIds
.map((flatEntityId) =>
findFlatEntityByIdInFlatEntityMaps({
flatEntityId,
flatEntityMaps,
}),
)
.filter(isDefined);
};
@@ -4,7 +4,7 @@ import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/typ
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
export const getSubFlatEntityMapsOrThrow = <T extends SyncableFlatEntity>({
export const getSubFlatEntityByIdsMapsOrThrow = <T extends SyncableFlatEntity>({
flatEntityIds,
flatEntityMaps,
}: {
@@ -1,7 +1,7 @@
import { type SyncableFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { findFlatEntitiesByApplicationId } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entities-by-application-id.util';
import { getSubFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/get-sub-flat-entity-maps-or-throw.util';
import { getSubFlatEntityByIdsMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/get-sub-flat-entity-by-ids-maps-or-throw.util';
export const getSubFlatEntityMapsByApplicationIdOrThrow = <
T extends SyncableFlatEntity,
@@ -17,7 +17,7 @@ export const getSubFlatEntityMapsByApplicationIdOrThrow = <
flatEntityMaps,
});
return getSubFlatEntityMapsOrThrow({
return getSubFlatEntityByIdsMapsOrThrow({
flatEntityIds: allApplicationFlatEntity.map((flatEntity) => flatEntity.id),
flatEntityMaps,
});