[REQUIRES_FULL_CACHE_FLUSH_WHEN_RELEASED] Refactor FlatEntity to be UniversalFlatEntity superset (#17452)

# Introduction
In this PR we're refactoring the `FlatEntity` type to become a superset
of the `UniversalFlatEntity`.
Right now we're storing all the extra properties in `__universal`
property, at some point it might just be sibling to other entity and we
might rely on the `propertiesToCompare` constants and TypeScript
allowing passing a superset type into a smaller subset type

## FromTo utils
The entity to flat entity method now computes the universal information,
standardized a typing and pattern to do

## Example
Also strictly type
```ts
    "bbb019ea-6205-498c-aea5-67bc53bce8a9": {
      "workspaceId": "20202020-1c25-4d02-bf25-6aeccf7ea419",
      "universalIdentifier": "20202020-d111-4d11-8d11-da5ab0a11002",
      "applicationId": "d01b010d-b984-465b-b40b-370e954e5188",
      "id": "bbb019ea-6205-498c-aea5-67bc53bce8a9",
      "pageLayoutTabId": "791a512f-169f-4209-b731-aa86716668c6",
      "title": "Deals by Company",
      "type": "GRAPH",
      "objectMetadataId": "9e14efea-df5b-4c0e-aba9-cfe455f32397",
      "gridPosition": { "row": 0, "column": 6, "rowSpan": 6, "columnSpan": 6 },
      "configuration": {
        "color": "orange",
        "orderBy": "FIELD_ASC",
        "timezone": "UTC",
        "displayLegend": true,
        "displayDataLabel": false,
        "showCenterMetric": true,
        "configurationType": "PIE_CHART",
        "firstDayOfTheWeek": 0,
        "aggregateOperation": "COUNT",
        "groupBySubFieldName": "name",
        "groupByFieldMetadataId": "6673ff18-63d2-47a1-8f85-2b9b09ca27a5",
        "aggregateFieldMetadataId": "8d64ee41-5dd4-4de6-945a-7c0c18399715"
      },
      "createdAt": "2026-01-28T14:08:52.140Z",
      "updatedAt": "2026-01-28T14:08:52.140Z",
      "deletedAt": null,
      "__universal": {
        "universalIdentifier": "20202020-d111-4d11-8d11-da5ab0a11002",
        "applicationUniversalIdentifier": "20202020-64aa-4b6f-b003-9c74b97cee20",
        "pageLayoutTabUniversalIdentifier": "20202020-d011-4d11-8d11-da5ab0a01001",
        "objectMetadataUniversalIdentifier": "20202020-9549-49dd-b2b2-883999db8938",
        "gridPosition": {
          "row": 0,
          "column": 6,
          "rowSpan": 6,
          "columnSpan": 6
        },
        "configuration": {
          "color": "orange",
          "orderBy": "FIELD_ASC",
          "timezone": "UTC",
          "displayLegend": true,
          "displayDataLabel": false,
          "showCenterMetric": true,
          "configurationType": "PIE_CHART",
          "firstDayOfTheWeek": 0,
          "aggregateOperation": "COUNT",
          "groupBySubFieldName": "name",
          "aggregateFieldMetadataUniversalIdentifier": "20202020-d01a-4131-8a31-f123456789ab",
          "groupByFieldMetadataUniversalIdentifier": "20202020-cbac-457e-b565-adece5fc815f"
        }
      }
    },
```
This commit is contained in:
Paul Rastoin
2026-01-28 17:46:34 +01:00
committed by GitHub
parent 81eaee81a8
commit fe9d6f34ff
90 changed files with 3058 additions and 538 deletions
@@ -0,0 +1,166 @@
import { isDefined } from 'twenty-shared/utils';
import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type';
import { type PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
type PageLayoutWidgetConfiguration = PageLayoutWidgetEntity['configuration'];
type UniversalPageLayoutWidgetConfiguration = NonNullable<
FlatPageLayoutWidget['__universal']
>['configuration'];
// Field metadata IDs in widget configurations don't need to reference existing entities
const getFieldMetadataUniversalIdentifier = ({
fieldMetadataId,
fieldMetadataIdToUniversalIdentifierMap,
}: {
fieldMetadataId: string;
fieldMetadataIdToUniversalIdentifierMap: Map<string, string>;
}): string | null => {
return fieldMetadataIdToUniversalIdentifierMap.get(fieldMetadataId) ?? null;
};
export const fromPageLayoutWidgetConfigurationToUniversalConfiguration = ({
configuration,
fieldMetadataIdToUniversalIdentifierMap,
}: {
configuration: PageLayoutWidgetConfiguration;
fieldMetadataIdToUniversalIdentifierMap: Map<string, string>;
}): UniversalPageLayoutWidgetConfiguration => {
switch (configuration.configurationType) {
case WidgetConfigurationType.AGGREGATE_CHART: {
const { aggregateFieldMetadataId, ratioAggregateConfig, ...rest } =
configuration;
return {
...rest,
aggregateFieldMetadataUniversalIdentifier:
getFieldMetadataUniversalIdentifier({
fieldMetadataId: aggregateFieldMetadataId,
fieldMetadataIdToUniversalIdentifierMap,
}),
...(isDefined(ratioAggregateConfig) && {
ratioAggregateConfig: {
optionValue: ratioAggregateConfig.optionValue,
fieldMetadataUniversalIdentifier:
getFieldMetadataUniversalIdentifier({
fieldMetadataId: ratioAggregateConfig.fieldMetadataId,
fieldMetadataIdToUniversalIdentifierMap,
}),
},
}),
};
}
case WidgetConfigurationType.GAUGE_CHART: {
const { aggregateFieldMetadataId, ...rest } = configuration;
return {
...rest,
aggregateFieldMetadataUniversalIdentifier:
getFieldMetadataUniversalIdentifier({
fieldMetadataId: aggregateFieldMetadataId,
fieldMetadataIdToUniversalIdentifierMap,
}),
};
}
case WidgetConfigurationType.PIE_CHART: {
const { aggregateFieldMetadataId, groupByFieldMetadataId, ...rest } =
configuration;
return {
...rest,
aggregateFieldMetadataUniversalIdentifier:
getFieldMetadataUniversalIdentifier({
fieldMetadataId: aggregateFieldMetadataId,
fieldMetadataIdToUniversalIdentifierMap,
}),
groupByFieldMetadataUniversalIdentifier:
getFieldMetadataUniversalIdentifier({
fieldMetadataId: groupByFieldMetadataId,
fieldMetadataIdToUniversalIdentifierMap,
}),
};
}
case WidgetConfigurationType.BAR_CHART: {
const {
aggregateFieldMetadataId,
primaryAxisGroupByFieldMetadataId,
secondaryAxisGroupByFieldMetadataId,
...rest
} = configuration;
return {
...rest,
aggregateFieldMetadataUniversalIdentifier:
getFieldMetadataUniversalIdentifier({
fieldMetadataId: aggregateFieldMetadataId,
fieldMetadataIdToUniversalIdentifierMap,
}),
primaryAxisGroupByFieldMetadataUniversalIdentifier:
getFieldMetadataUniversalIdentifier({
fieldMetadataId: primaryAxisGroupByFieldMetadataId,
fieldMetadataIdToUniversalIdentifierMap,
}),
...(isDefined(secondaryAxisGroupByFieldMetadataId) && {
secondaryAxisGroupByFieldMetadataUniversalIdentifier:
getFieldMetadataUniversalIdentifier({
fieldMetadataId: secondaryAxisGroupByFieldMetadataId,
fieldMetadataIdToUniversalIdentifierMap,
}),
}),
};
}
case WidgetConfigurationType.LINE_CHART: {
const {
aggregateFieldMetadataId,
primaryAxisGroupByFieldMetadataId,
secondaryAxisGroupByFieldMetadataId,
...rest
} = configuration;
return {
...rest,
aggregateFieldMetadataUniversalIdentifier:
getFieldMetadataUniversalIdentifier({
fieldMetadataId: aggregateFieldMetadataId,
fieldMetadataIdToUniversalIdentifierMap,
}),
primaryAxisGroupByFieldMetadataUniversalIdentifier:
getFieldMetadataUniversalIdentifier({
fieldMetadataId: primaryAxisGroupByFieldMetadataId,
fieldMetadataIdToUniversalIdentifierMap,
}),
...(isDefined(secondaryAxisGroupByFieldMetadataId) && {
secondaryAxisGroupByFieldMetadataUniversalIdentifier:
getFieldMetadataUniversalIdentifier({
fieldMetadataId: secondaryAxisGroupByFieldMetadataId,
fieldMetadataIdToUniversalIdentifierMap,
}),
}),
};
}
case WidgetConfigurationType.VIEW:
case WidgetConfigurationType.FIELD:
case WidgetConfigurationType.FIELDS:
case WidgetConfigurationType.TIMELINE:
case WidgetConfigurationType.TASKS:
case WidgetConfigurationType.NOTES:
case WidgetConfigurationType.FILES:
case WidgetConfigurationType.EMAILS:
case WidgetConfigurationType.CALENDAR:
case WidgetConfigurationType.FIELD_RICH_TEXT:
case WidgetConfigurationType.WORKFLOW:
case WidgetConfigurationType.WORKFLOW_VERSION:
case WidgetConfigurationType.WORKFLOW_RUN:
case WidgetConfigurationType.FRONT_COMPONENT:
case WidgetConfigurationType.IFRAME:
case WidgetConfigurationType.STANDALONE_RICH_TEXT:
return configuration;
}
};
@@ -1,17 +1,77 @@
import { removePropertiesFromRecord } from 'twenty-shared/utils';
import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils';
import {
FlatEntityMapsException,
FlatEntityMapsExceptionCode,
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util';
import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type';
import { type PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
import { fromPageLayoutWidgetConfigurationToUniversalConfiguration } from 'src/engine/metadata-modules/flat-page-layout-widget/utils/from-page-layout-widget-configuration-to-universal-configuration.util';
import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type';
export const fromPageLayoutWidgetEntityToFlatPageLayoutWidget = (
pageLayoutWidgetEntity: PageLayoutWidgetEntity,
): FlatPageLayoutWidget => {
type FromPageLayoutWidgetEntityToFlatPageLayoutWidgetArgs =
FromEntityToFlatEntityArgs<'pageLayoutWidget'> & {
fieldMetadataIdToUniversalIdentifierMap: Map<string, string>;
};
export const fromPageLayoutWidgetEntityToFlatPageLayoutWidget = ({
entity: pageLayoutWidgetEntity,
applicationIdToUniversalIdentifierMap,
pageLayoutTabIdToUniversalIdentifierMap,
objectMetadataIdToUniversalIdentifierMap,
fieldMetadataIdToUniversalIdentifierMap,
}: FromPageLayoutWidgetEntityToFlatPageLayoutWidgetArgs): FlatPageLayoutWidget => {
const pageLayoutWidgetEntityWithoutRelations = removePropertiesFromRecord(
pageLayoutWidgetEntity,
getMetadataEntityRelationProperties('pageLayoutWidget'),
);
const applicationUniversalIdentifier =
applicationIdToUniversalIdentifierMap.get(
pageLayoutWidgetEntity.applicationId,
);
if (!isDefined(applicationUniversalIdentifier)) {
throw new FlatEntityMapsException(
`Application with id ${pageLayoutWidgetEntity.applicationId} not found for pageLayoutWidget ${pageLayoutWidgetEntity.id}`,
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
);
}
const pageLayoutTabUniversalIdentifier =
pageLayoutTabIdToUniversalIdentifierMap.get(
pageLayoutWidgetEntity.pageLayoutTabId,
);
if (!isDefined(pageLayoutTabUniversalIdentifier)) {
throw new FlatEntityMapsException(
`PageLayoutTab with id ${pageLayoutWidgetEntity.pageLayoutTabId} not found for pageLayoutWidget ${pageLayoutWidgetEntity.id}`,
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
);
}
let objectMetadataUniversalIdentifier: string | null = null;
if (isDefined(pageLayoutWidgetEntity.objectMetadataId)) {
objectMetadataUniversalIdentifier =
objectMetadataIdToUniversalIdentifierMap.get(
pageLayoutWidgetEntity.objectMetadataId,
) ?? null;
if (!isDefined(objectMetadataUniversalIdentifier)) {
throw new FlatEntityMapsException(
`ObjectMetadata with id ${pageLayoutWidgetEntity.objectMetadataId} not found for pageLayoutWidget ${pageLayoutWidgetEntity.id}`,
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
);
}
}
const configurationWithUniversalIdentifiers =
fromPageLayoutWidgetConfigurationToUniversalConfiguration({
configuration: pageLayoutWidgetEntityWithoutRelations.configuration,
fieldMetadataIdToUniversalIdentifierMap,
});
return {
...pageLayoutWidgetEntityWithoutRelations,
createdAt: pageLayoutWidgetEntity.createdAt.toISOString(),
@@ -20,5 +80,13 @@ export const fromPageLayoutWidgetEntityToFlatPageLayoutWidget = (
universalIdentifier:
pageLayoutWidgetEntityWithoutRelations.universalIdentifier,
applicationId: pageLayoutWidgetEntityWithoutRelations.applicationId,
__universal: {
universalIdentifier: pageLayoutWidgetEntity.universalIdentifier,
applicationUniversalIdentifier,
pageLayoutTabUniversalIdentifier,
objectMetadataUniversalIdentifier,
gridPosition: pageLayoutWidgetEntityWithoutRelations.gridPosition,
configuration: configurationWithUniversalIdentifiers,
},
};
};