Files
twenty/packages/twenty-server/test/integration/constants/widget-configuration-test-data.constants.ts
T
Paul Rastoin 8c951d3623 Migrate Views-xxx Index Field Object Skill to be fully universal ( all actions and metadata runner and builder ) + all metadata update actions runner (#17687)
# What this PR does
Overall naming `universal` versus `flat` is not always the most updated
and so on
Will make a big cleaning tour after I've finished the whole migration
Migrating all `view` and ( filter fields etc ) `field` `object` `index`
to the universal pattern on all `services`, `builder` and `runner`
levels

## Universal and flat optimistic tooling

`addUniversalFlatEntityToUniversalFlatEntityAndRelatedEntityMapsThroughMutationOrThrow`
and its delete counterpart maintain the consistency of
`UniversalFlatEntityMaps` when an entity is created or removed. Beyond
inserting/removing the entity from its own maps, they walk through
`ALL_UNIVERSAL_METADATA_RELATIONS` to update the **aggregator arrays**
on related parent entities — the add appends the new entity's
`universalIdentifier` to the parent's aggregator (e.g. a new viewField's
identifier gets appended to its parent view's
`viewFieldUniversalIdentifiers`), and the delete filters it out. This
keeps the maps in sync so that diff computations and relation lookups
remain accurate throughout the migration building process.

## ALL_UNIVERSAL_METADATA_RELATIONS
`ALL_UNIVERSAL_METADATA_RELATIONS` is the universal counterpart of
`ALL_METADATA_RELATIONS`. It maps each metadata entity to its
many-to-one and one-to-many relations using universal foreign keys
(`*UniversalIdentifier`) instead of database IDs (`*Id`). This allows
migration actions to reference related entities in a workspace-agnostic
way. Relations that are workspace-specific (e.g. `workspace`,
`dataSource`, `userWorkspace`) are set to `null` and skipped during
resolution.

## `workspaceMigrationCreateIdEnrichment`
Reserved to API metadata ( will be able to validate at app installation
lvl )
- Workspace migration `create` actions now carry an optional `id` (and
`fieldIdByUniversalIdentifier` for object/field actions) so that
caller-provided IDs flow through the entire build-validate-run pipeline.
- New `enrichCreateWorkspaceMigrationActionsWithIds` utility resolves
`universalIdentifier → id` mappings after the builder runs and injects
them into the migration actions before the runner persists entities.
- Runner action handlers use the provided IDs instead of generating new
UUIDs, enabling deterministic entity creation for synchronization
workflows.


## `resolveUniversalUpdateRelationIdentifiersToIds`
`resolveUniversalUpdateRelationIdentifiersToIds` converts universal
identifiers (workspace-agnostic, stable keys) in a migration update
payload into concrete database UUIDs, so the update can be applied to a
specific workspace.

It iterates over the many-to-one relations defined in
`ALL_UNIVERSAL_METADATA_RELATIONS` for the given entity type, replaces
each `*UniversalIdentifier` property with its corresponding `*Id` by
looking up the target entity in `allFlatEntityMaps`, and throws if a
non-null identifier can't be resolved.

Used by all `update` action handlers in
`transpileUniversalActionToFlatAction`, avoiding duplicated resolution
logic across handlers.

## What this PR does not
- Migrating twenty-standard declaration to universal
- Migrating all the inputs transpilers to universal
- Migrating all metadata to be fully universal ( we still need to
de-scope the type of all of them and refactor their validator very close
)

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2026-02-09 19:02:38 +01:00

226 lines
9.0 KiB
TypeScript

import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant';
import { type AggregateChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/aggregate-chart-configuration.dto';
import { type BarChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/bar-chart-configuration.dto';
import { type GaugeChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/gauge-chart-configuration.dto';
import { type IframeConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/iframe-configuration.dto';
import { type LineChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/line-chart-configuration.dto';
import { type PieChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/pie-chart-configuration.dto';
import { type StandaloneRichTextConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/standalone-rich-text-configuration.dto';
import { AxisNameDisplay } from 'src/engine/metadata-modules/page-layout-widget/enums/axis-name-display.enum';
import { BarChartLayout } from 'src/engine/metadata-modules/page-layout-widget/enums/bar-chart-layout.enum';
import { GraphOrderBy } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-order-by.enum';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
// Hardcoded UUIDs for unit tests only (no DB lookup needed)
const UNIT_TEST_FIELD_METADATA_ID_1 = '20202020-1111-4111-a111-111111111111';
const UNIT_TEST_FIELD_METADATA_ID_2 = '20202020-2222-4222-a222-222222222222';
const UNIT_TEST_FIELD_METADATA_ID_3 = '20202020-3333-4333-a333-333333333333';
// --- Static configs (no field metadata IDs) ---
export const TEST_IFRAME_CONFIG: IframeConfigurationDTO = {
configurationType: WidgetConfigurationType.IFRAME,
url: 'https://example.com/dashboard',
};
export const TEST_IFRAME_CONFIG_ALTERNATIVE: IframeConfigurationDTO = {
configurationType: WidgetConfigurationType.IFRAME,
url: 'https://app.twenty.com/analytics',
};
export const TEST_STANDALONE_RICH_TEXT_CONFIG: StandaloneRichTextConfigurationDTO =
{
configurationType: WidgetConfigurationType.STANDALONE_RICH_TEXT,
body: {
blocknote:
'{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"Hello world"}]}]}',
markdown: '# Hello world',
},
};
export const TEST_STANDALONE_RICH_TEXT_CONFIG_MINIMAL: StandaloneRichTextConfigurationDTO =
{
configurationType: WidgetConfigurationType.STANDALONE_RICH_TEXT,
body: {
markdown: 'Simple text',
},
};
// --- Invalid configs (for failure test cases) ---
export const INVALID_STANDALONE_RICH_TEXT_CONFIG_MISSING_BODY = {};
export const INVALID_STANDALONE_RICH_TEXT_CONFIG_BODY_WRONG_TYPE = {
body: 'not an object',
};
export const INVALID_IFRAME_CONFIG_BAD_URL = {
url: 'not-a-valid-url',
};
export const INVALID_IFRAME_CONFIG_EMPTY_URL = {
url: '',
};
export const INVALID_NUMBER_CHART_CONFIG_MISSING_FIELDS = {
configurationType: WidgetConfigurationType.AGGREGATE_CHART,
};
export const INVALID_NUMBER_CHART_CONFIG_BAD_UUID = {
configurationType: WidgetConfigurationType.AGGREGATE_CHART,
aggregateFieldMetadataId: 'not-a-uuid',
aggregateOperation: AggregateOperations.COUNT,
};
export const INVALID_VERTICAL_BAR_CHART_CONFIG_MISSING_GROUP_BY = {
configurationType: WidgetConfigurationType.BAR_CHART,
aggregateFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_1,
aggregateOperation: AggregateOperations.SUM,
};
export const INVALID_HORIZONTAL_BAR_CHART_CONFIG_MISSING_GROUP_BY = {
configurationType: WidgetConfigurationType.BAR_CHART,
aggregateFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_1,
aggregateOperation: AggregateOperations.SUM,
};
// --- Unit-test-only graph configs (with hardcoded UUIDs, for validation tests) ---
export const TEST_NUMBER_CHART_CONFIG: AggregateChartConfigurationDTO = {
configurationType: WidgetConfigurationType.AGGREGATE_CHART,
aggregateFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_1,
aggregateOperation: AggregateOperations.COUNT,
label: 'Total Records',
description: 'Count of all records',
format: '0,0',
displayDataLabel: true,
};
export const TEST_NUMBER_CHART_CONFIG_MINIMAL: AggregateChartConfigurationDTO =
{
configurationType: WidgetConfigurationType.AGGREGATE_CHART,
aggregateFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_1,
aggregateOperation: AggregateOperations.SUM,
displayDataLabel: false,
};
export const TEST_VERTICAL_BAR_CHART_CONFIG: BarChartConfigurationDTO = {
configurationType: WidgetConfigurationType.BAR_CHART,
layout: BarChartLayout.VERTICAL,
aggregateFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_1,
aggregateOperation: AggregateOperations.SUM,
primaryAxisGroupByFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_2,
primaryAxisOrderBy: GraphOrderBy.FIELD_ASC,
displayDataLabel: true,
axisNameDisplay: AxisNameDisplay.NONE,
color: 'red',
description: 'Monthly revenue breakdown',
omitNullValues: true,
rangeMin: 0,
rangeMax: 100000,
};
export const TEST_VERTICAL_BAR_CHART_CONFIG_MINIMAL: BarChartConfigurationDTO =
{
configurationType: WidgetConfigurationType.BAR_CHART,
layout: BarChartLayout.VERTICAL,
aggregateFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_1,
aggregateOperation: AggregateOperations.COUNT,
primaryAxisGroupByFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_2,
primaryAxisOrderBy: GraphOrderBy.VALUE_DESC,
displayDataLabel: false,
axisNameDisplay: AxisNameDisplay.NONE,
};
export const TEST_HORIZONTAL_BAR_CHART_CONFIG: BarChartConfigurationDTO = {
configurationType: WidgetConfigurationType.BAR_CHART,
layout: BarChartLayout.HORIZONTAL,
aggregateFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_1,
aggregateOperation: AggregateOperations.SUM,
primaryAxisGroupByFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_2,
primaryAxisOrderBy: GraphOrderBy.FIELD_ASC,
displayDataLabel: true,
axisNameDisplay: AxisNameDisplay.NONE,
color: 'blue',
description: 'Horizontal revenue breakdown',
omitNullValues: true,
rangeMin: 0,
rangeMax: 100000,
};
export const TEST_HORIZONTAL_BAR_CHART_CONFIG_MINIMAL: BarChartConfigurationDTO =
{
configurationType: WidgetConfigurationType.BAR_CHART,
layout: BarChartLayout.HORIZONTAL,
aggregateFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_1,
aggregateOperation: AggregateOperations.COUNT,
primaryAxisGroupByFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_2,
primaryAxisOrderBy: GraphOrderBy.VALUE_DESC,
displayDataLabel: false,
axisNameDisplay: AxisNameDisplay.NONE,
};
export const TEST_LINE_CHART_CONFIG: LineChartConfigurationDTO = {
configurationType: WidgetConfigurationType.LINE_CHART,
aggregateFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_1,
aggregateOperation: AggregateOperations.AVG,
primaryAxisGroupByFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_2,
primaryAxisOrderBy: GraphOrderBy.FIELD_ASC,
secondaryAxisGroupByFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_3,
secondaryAxisOrderBy: GraphOrderBy.FIELD_DESC,
displayDataLabel: true,
axisNameDisplay: AxisNameDisplay.NONE,
color: 'cyan',
description: 'Trend over time',
omitNullValues: false,
rangeMin: -100,
rangeMax: 100,
};
export const TEST_LINE_CHART_CONFIG_MINIMAL: LineChartConfigurationDTO = {
configurationType: WidgetConfigurationType.LINE_CHART,
aggregateFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_1,
aggregateOperation: AggregateOperations.MAX,
primaryAxisGroupByFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_2,
primaryAxisOrderBy: GraphOrderBy.VALUE_ASC,
displayDataLabel: false,
axisNameDisplay: AxisNameDisplay.NONE,
};
export const TEST_PIE_CHART_CONFIG: PieChartConfigurationDTO = {
configurationType: WidgetConfigurationType.PIE_CHART,
groupByFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_1,
aggregateFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_2,
aggregateOperation: AggregateOperations.SUM,
orderBy: GraphOrderBy.VALUE_DESC,
displayDataLabel: true,
displayLegend: true,
showCenterMetric: true,
color: 'yellow',
description: 'Distribution by category',
};
export const TEST_PIE_CHART_CONFIG_MINIMAL: PieChartConfigurationDTO = {
configurationType: WidgetConfigurationType.PIE_CHART,
groupByFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_1,
aggregateFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_2,
aggregateOperation: AggregateOperations.COUNT,
orderBy: GraphOrderBy.FIELD_ASC,
displayDataLabel: false,
};
export const TEST_GAUGE_CHART_CONFIG: GaugeChartConfigurationDTO = {
configurationType: WidgetConfigurationType.GAUGE_CHART,
aggregateFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_1,
aggregateOperation: AggregateOperations.SUM,
description: 'Completion percentage',
displayDataLabel: true,
};
export const TEST_GAUGE_CHART_CONFIG_MINIMAL: GaugeChartConfigurationDTO = {
configurationType: WidgetConfigurationType.GAUGE_CHART,
aggregateFieldMetadataId: UNIT_TEST_FIELD_METADATA_ID_1,
aggregateOperation: AggregateOperations.COUNT_TRUE,
displayDataLabel: false,
};