Spread in parent and requires FlatEntity.__universal (#17753)
# Introduction Requiring the spreaded `__universal` record that aggregates all the universal identifier ( relations fk and aggregators ) of an entity to its root It's blockin for https://github.com/twentyhq/twenty/pull/17687 to be finalized because if we don't we would have to migrated all related entities at once in order for them to always have the universal properties ## `resolveEntityRelationUniversalIdentifiers` Introduced `resolveEntityRelationUniversalIdentifiers` a centralized utility that resolves foreign key IDs to universal identifiers using ALL_METADATA_RELATIONS metadata. It provides strict typing for both input (foreign keys) and output (universal identifiers), with nullability dynamically inferred from entity relation types. Strictly and dynamically typed for both output and input To do so added a new type and const/runtime grain to ALL_METADATA_RELATIONS `isNullable`to many-to-one entries, derived from the entity relation property types. And fixed incorrectly typed typeorm entities ### Usage ```ts const { availabilityObjectMetadataUniversalIdentifier, frontComponentUniversalIdentifier, } = resolveEntityRelationUniversalIdentifiers({ metadataName: 'commandMenuItem', foreignKeyValues: { availabilityObjectMetadataId: createCommandMenuItemInput.availabilityObjectMetadataId, frontComponentId: createCommandMenuItemInput.frontComponentId, }, flatEntityMaps: { flatObjectMetadataMaps, flatFrontComponentMaps }, }); ```
This commit is contained in:
+4
@@ -41,10 +41,14 @@ export const generateIndexForFlatFieldMetadata = ({
|
||||
isCustom: true,
|
||||
isUnique: flatFieldMetadata.isUnique ?? false,
|
||||
objectMetadataId: flatObjectMetadata.id,
|
||||
objectMetadataUniversalIdentifier:
|
||||
flatObjectMetadata.universalIdentifier,
|
||||
universalIdentifier: indexId,
|
||||
updatedAt: createdAt,
|
||||
workspaceId,
|
||||
applicationId: flatFieldMetadata.applicationId,
|
||||
applicationUniversalIdentifier:
|
||||
flatFieldMetadata.applicationUniversalIdentifier,
|
||||
},
|
||||
flatObjectMetadata,
|
||||
},
|
||||
|
||||
+13
-5
@@ -2,10 +2,10 @@ import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findManyFlatEntityByUniversalIdentifierInUniversalFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-universal-identifier-in-universal-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type';
|
||||
import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type';
|
||||
import { reduceFlatViewGroupsByViewId } from 'src/engine/metadata-modules/flat-view-group/utils/reduce-flat-view-groups-by-view-id.util';
|
||||
import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type';
|
||||
|
||||
type HandleFlatFieldMetadataDeactivationSideEffectsArgs = FromTo<
|
||||
@@ -59,14 +59,22 @@ export const handleFieldMetadataDeactivationSideEffects = ({
|
||||
flatEntityMaps: flatViewGroupMaps,
|
||||
});
|
||||
|
||||
const { flatViewGroupRecordByViewId } = reduceFlatViewGroupsByViewId({
|
||||
flatViewGroups,
|
||||
});
|
||||
const uniqueViewUniversalIdentifiers = [
|
||||
...new Set(flatViewGroups.map((vg) => vg.viewUniversalIdentifier)),
|
||||
];
|
||||
|
||||
const flatViewsWithViewGroups =
|
||||
findManyFlatEntityByUniversalIdentifierInUniversalFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatViewMaps,
|
||||
universalIdentifiers: uniqueViewUniversalIdentifiers,
|
||||
});
|
||||
|
||||
const viewIdsFromViewGroups = flatViewsWithViewGroups.map((view) => view.id);
|
||||
|
||||
// Note: We assume a view only has view groups related to one field
|
||||
const viewIdsToDelete = [
|
||||
...new Set([
|
||||
...Object.keys(flatViewGroupRecordByViewId),
|
||||
...viewIdsFromViewGroups,
|
||||
...fromFlatFieldMetadata.calendarViewIds,
|
||||
...fromFlatFieldMetadata.mainGroupByFieldMetadataViewIds,
|
||||
]),
|
||||
|
||||
+26
-10
@@ -3,10 +3,11 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group.type';
|
||||
import { reduceFlatViewGroupsByViewId } from 'src/engine/metadata-modules/flat-view-group/utils/reduce-flat-view-groups-by-view-id.util';
|
||||
import { reduceFlatViewGroupsByViewUniversalIdentifier } from 'src/engine/metadata-modules/flat-view-group/utils/reduce-flat-view-groups-by-view-universal-identifier.util';
|
||||
|
||||
type RecomputeViewGroupsOnEnumFlatFieldMetadataIsNullableUpdateArgs = FromTo<
|
||||
FlatFieldMetadata,
|
||||
@@ -47,15 +48,19 @@ export const recomputeViewGroupsOnEnumFlatFieldMetadataIsNullableUpdate = ({
|
||||
),
|
||||
flatEntityMaps: allFlatViewGroups,
|
||||
});
|
||||
const { flatViewGroupRecordByViewId, highestViewGroupPositionByViewId } =
|
||||
reduceFlatViewGroupsByViewId({
|
||||
flatViewGroups,
|
||||
});
|
||||
const {
|
||||
flatViewGroupRecordByViewUniversalIdentifier,
|
||||
highestViewGroupPositionByViewUniversalIdentifier,
|
||||
} = reduceFlatViewGroupsByViewUniversalIdentifier({
|
||||
flatViewGroups,
|
||||
});
|
||||
|
||||
for (const viewId in flatViewGroupRecordByViewId) {
|
||||
const flatViewGroups = Object.values(flatViewGroupRecordByViewId[viewId]);
|
||||
for (const viewUniversalIdentifier in flatViewGroupRecordByViewUniversalIdentifier) {
|
||||
const flatViewGroupsForView = Object.values(
|
||||
flatViewGroupRecordByViewUniversalIdentifier[viewUniversalIdentifier],
|
||||
);
|
||||
|
||||
const emptyValueFlatViewGroup = flatViewGroups.find(
|
||||
const emptyValueFlatViewGroup = flatViewGroupsForView.find(
|
||||
(flatViewGroup) => flatViewGroup.fieldValue === '',
|
||||
);
|
||||
|
||||
@@ -63,21 +68,32 @@ export const recomputeViewGroupsOnEnumFlatFieldMetadataIsNullableUpdate = ({
|
||||
toFlatFieldMetadata.isNullable === true &&
|
||||
!isDefined(emptyValueFlatViewGroup)
|
||||
) {
|
||||
const highestViewGroupPosition = highestViewGroupPositionByViewId[viewId];
|
||||
const highestViewGroupPosition =
|
||||
highestViewGroupPositionByViewUniversalIdentifier[
|
||||
viewUniversalIdentifier
|
||||
];
|
||||
const viewGroupId = v4();
|
||||
const createdAt = new Date().toISOString();
|
||||
|
||||
const flatView = findFlatEntityByUniversalIdentifierOrThrow({
|
||||
flatEntityMaps: flatViewMaps,
|
||||
universalIdentifier: viewUniversalIdentifier,
|
||||
});
|
||||
|
||||
sideEffectResult.flatViewGroupsToCreate.push({
|
||||
id: viewGroupId,
|
||||
universalIdentifier: viewGroupId,
|
||||
fieldValue: '',
|
||||
position: highestViewGroupPosition + 1,
|
||||
applicationUniversalIdentifier:
|
||||
toFlatFieldMetadata.applicationUniversalIdentifier,
|
||||
viewUniversalIdentifier,
|
||||
isVisible: true,
|
||||
workspaceId: toFlatFieldMetadata.workspaceId,
|
||||
createdAt,
|
||||
updatedAt: createdAt,
|
||||
deletedAt: null,
|
||||
viewId,
|
||||
viewId: flatView.id,
|
||||
applicationId: toFlatFieldMetadata.applicationId,
|
||||
});
|
||||
} else if (isDefined(emptyValueFlatViewGroup)) {
|
||||
|
||||
+42
-19
@@ -8,11 +8,12 @@ import {
|
||||
FlatEntityMapsExceptionCode,
|
||||
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util';
|
||||
import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { compareTwoFlatFieldMetadataEnumOptions } from 'src/engine/metadata-modules/flat-field-metadata/utils/compare-two-flat-field-metadata-enum-options.util';
|
||||
import { type FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group.type';
|
||||
import { reduceFlatViewGroupsByViewId } from 'src/engine/metadata-modules/flat-view-group/utils/reduce-flat-view-groups-by-view-id.util';
|
||||
import { reduceFlatViewGroupsByViewUniversalIdentifier } from 'src/engine/metadata-modules/flat-view-group/utils/reduce-flat-view-groups-by-view-universal-identifier.util';
|
||||
|
||||
type RecomputeViewGroupsOnFlatFieldMetadataOptionsUpdateArgs = {
|
||||
fromFlatFieldMetadata: FlatFieldMetadata<EnumFieldMetadataType>;
|
||||
@@ -85,51 +86,71 @@ export const recomputeViewGroupsOnFlatFieldMetadataOptionsUpdate = ({
|
||||
),
|
||||
);
|
||||
|
||||
const viewGroupsByViewId = reduceFlatViewGroupsByViewId({
|
||||
flatViewGroups: remainingFlatViewGroups,
|
||||
});
|
||||
const viewGroupsByViewUniversalIdentifier =
|
||||
reduceFlatViewGroupsByViewUniversalIdentifier({
|
||||
flatViewGroups: remainingFlatViewGroups,
|
||||
});
|
||||
|
||||
// Count visible view groups per view to enforce the limit
|
||||
const visibleViewGroupCountByViewId = remainingFlatViewGroups.reduce<
|
||||
Record<string, number>
|
||||
>((acc, flatViewGroup) => {
|
||||
if (flatViewGroup.isVisible) {
|
||||
acc[flatViewGroup.viewId] = (acc[flatViewGroup.viewId] ?? 0) + 1;
|
||||
}
|
||||
const visibleViewGroupCountByViewUniversalIdentifier =
|
||||
remainingFlatViewGroups.reduce<Record<string, number>>(
|
||||
(acc, flatViewGroup) => {
|
||||
if (flatViewGroup.isVisible) {
|
||||
acc[flatViewGroup.viewUniversalIdentifier] =
|
||||
(acc[flatViewGroup.viewUniversalIdentifier] ?? 0) + 1;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
const viewIds = Object.keys(viewGroupsByViewId.flatViewGroupRecordByViewId);
|
||||
const viewUniversalIdentifiers = Object.keys(
|
||||
viewGroupsByViewUniversalIdentifier.flatViewGroupRecordByViewUniversalIdentifier,
|
||||
);
|
||||
|
||||
const createdAt = new Date().toISOString();
|
||||
const flatViewGroupsToCreate = createdFieldMetadataOptions.flatMap(
|
||||
(option, createdOptionIndex) =>
|
||||
viewIds.map<FlatViewGroup>((viewId) => {
|
||||
viewUniversalIdentifiers.map<FlatViewGroup>((viewUniversalIdentifier) => {
|
||||
const viewGroupHighestPosition =
|
||||
viewGroupsByViewId.highestViewGroupPositionByViewId[viewId];
|
||||
viewGroupsByViewUniversalIdentifier
|
||||
.highestViewGroupPositionByViewUniversalIdentifier[
|
||||
viewUniversalIdentifier
|
||||
];
|
||||
|
||||
if (!isDefined(viewGroupHighestPosition)) {
|
||||
throw new FlatEntityMapsException(
|
||||
'View id highest position not found, should never occur',
|
||||
'View universal identifier highest position not found, should never occur',
|
||||
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const currentVisibleCount = visibleViewGroupCountByViewId[viewId] ?? 0;
|
||||
const currentVisibleCount =
|
||||
visibleViewGroupCountByViewUniversalIdentifier[
|
||||
viewUniversalIdentifier
|
||||
] ?? 0;
|
||||
const isVisible = currentVisibleCount < VIEW_GROUP_VISIBLE_OPTIONS_MAX;
|
||||
|
||||
// Increment the count for future iterations if this group will be visible
|
||||
if (isVisible) {
|
||||
visibleViewGroupCountByViewId[viewId] = currentVisibleCount + 1;
|
||||
visibleViewGroupCountByViewUniversalIdentifier[
|
||||
viewUniversalIdentifier
|
||||
] = currentVisibleCount + 1;
|
||||
}
|
||||
|
||||
const viewGroupId = v4();
|
||||
|
||||
const flatView = findFlatEntityByUniversalIdentifierOrThrow({
|
||||
flatEntityMaps: flatViewMaps,
|
||||
universalIdentifier: viewUniversalIdentifier,
|
||||
});
|
||||
|
||||
return {
|
||||
id: viewGroupId,
|
||||
fieldMetadataId: fromFlatFieldMetadata.id,
|
||||
viewId,
|
||||
viewId: flatView.id,
|
||||
viewUniversalIdentifier,
|
||||
workspaceId,
|
||||
createdAt: createdAt,
|
||||
updatedAt: createdAt,
|
||||
@@ -139,6 +160,8 @@ export const recomputeViewGroupsOnFlatFieldMetadataOptionsUpdate = ({
|
||||
fieldValue: option.value,
|
||||
position: viewGroupHighestPosition + createdOptionIndex + 1,
|
||||
applicationId: fromFlatFieldMetadata.applicationId,
|
||||
applicationUniversalIdentifier:
|
||||
fromFlatFieldMetadata.applicationUniversalIdentifier,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user