From f6b7ab2251e5574c081f1babeaeca5c530011409 Mon Sep 17 00:00:00 2001 From: Paul Rastoin <45004772+prastoin@users.noreply.github.com> Date: Thu, 12 Feb 2026 18:06:55 +0100 Subject: [PATCH] Scalar and universal flat entity transpilers (#17891) # Introduction ## Use `flatEntityTranspilers.toScalarFlatEntity` in create action handler **Changes:** - Modified `BaseWorkspaceMigrationRunnerActionHandlerService.insertFlatEntitiesInRepository()` to transform flat entities using `toScalarFlatEntity()` before database insertion **What it does:** Strips out TypeORM relation objects and metadata-only properties, ensuring only scalar values (primitives, IDs, dates) are inserted into the database. **Benefits:** - **Type Safety:** Prevents accidental insertion of nested objects that TypeORM can't persist - **Consistency:** All 17+ create action handlers automatically benefit from proper data transformation - **Single Source of Truth:** Centralized logic for what constitutes a database-insertable entity - **Prevents Errors:** Uses entity configuration schema to ensure only valid properties are included ## Usage ```ts protected async insertFlatEntitiesInRepository({ flatEntities, queryRunner, }: { queryRunner: QueryRunner; flatEntities: MetadataFlatEntity[]; }) { const metadataEntity = ALL_METADATA_ENTITY_BY_METADATA_NAME[this.metadataName]; const repository = queryRunner.manager.getRepository(metadataEntity); const scalarFlatEntities = flatEntities.map((flatEntity) => flatEntityTranspilers.toScalarFlatEntity({ flatEntity, metadataName: this.metadataName, }), ); await repository.insert(scalarFlatEntities); } ``` ## Upcoming refactor About to completely split the `packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface.ts` into three dedicated boilerplate one for each action type `create` `delete` `update` will provide a better interfacing and typing + will allow not requiring the user to provide the metadata execute handler as required --- ...ompare-and-stringify.constant.spec.ts.snap | 2 +- ...configuration-by-metadata-name.constant.ts | 1217 +++++++++++++++-- ...rties-to-compare-and-stringify.constant.ts | 39 +- .../types/flat-entity-from.type.ts | 12 +- .../types/flat-entity-update.type.ts | 17 +- .../types/scalar-flat-entity.type.ts | 9 + ...-flat-entity-properties-to-compare.type.ts | 12 +- ...lat-entity-properties-to-stringify.type.ts | 8 +- .../universal-flat-index-metadata.type.ts | 2 +- ...iversal-flat-entity-for-comparison.util.ts | 2 +- .../create-agent-action-handler.service.ts | 12 +- ...ommand-menu-item-action-handler.service.ts | 14 +- .../create-field-action-handler.service.ts | 11 +- ...-front-component-action-handler.service.ts | 12 +- .../create-index-action-handler.service.ts | 19 +- .../update-index-action-handler.service.ts | 23 +- .../index/utils/index-action-handler.utils.ts | 46 +- ...e-logic-function-action-handler.service.ts | 14 +- ...gation-menu-item-action-handler.service.ts | 14 +- .../create-object-action-handler.service.ts | 28 +- ...-page-layout-tab-action-handler.service.ts | 14 +- ...ge-layout-widget-action-handler.service.ts | 14 +- ...eate-page-layout-action-handler.service.ts | 12 +- ...eate-role-target-action-handler.service.ts | 14 +- .../create-role-action-handler.service.ts | 14 +- ...-predicate-group-action-handler.service.ts | 14 +- ...ission-predicate-action-handler.service.ts | 14 +- .../create-skill-action-handler.service.ts | 12 +- ...reate-view-field-action-handler.service.ts | 12 +- ...iew-filter-group-action-handler.service.ts | 14 +- ...eate-view-filter-action-handler.service.ts | 12 +- ...reate-view-group-action-handler.service.ts | 12 +- .../create-view-action-handler.service.ts | 12 +- .../create-webhook-action-handler.service.ts | 12 +- ...runner-action-handler-service.interface.ts | 23 + ...-entity-to-scalar-flat-entity.util.spec.ts | 80 ++ .../flat-entity-to-scalar-flat-entity.util.ts | 30 + 37 files changed, 1400 insertions(+), 428 deletions(-) create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-entity/types/scalar-flat-entity.type.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/__tests__/flat-entity-to-scalar-flat-entity.util.spec.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/flat-entity-to-scalar-flat-entity.util.ts diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/__tests__/__snapshots__/all-universal-flat-entity-properties-to-compare-and-stringify.constant.spec.ts.snap b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/__tests__/__snapshots__/all-universal-flat-entity-properties-to-compare-and-stringify.constant.spec.ts.snap index c980e46811..ad3c9a0b50 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/__tests__/__snapshots__/all-universal-flat-entity-properties-to-compare-and-stringify.constant.spec.ts.snap +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/__tests__/__snapshots__/all-universal-flat-entity-properties-to-compare-and-stringify.constant.spec.ts.snap @@ -66,9 +66,9 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma "propertiesToCompare": [ "indexType", "indexWhereClause", - "universalFlatIndexFieldMetadatas", "isUnique", "name", + "universalFlatIndexFieldMetadatas", ], "propertiesToStringify": [ "universalFlatIndexFieldMetadatas", diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant.ts index fa7b36a022..a49157dd29 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant.ts @@ -1,12 +1,11 @@ import { type AllMetadataName } from 'twenty-shared/metadata'; import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type'; -import { type MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type'; import { type MetadataManyToOneJoinColumn } from 'src/engine/metadata-modules/flat-entity/types/metadata-many-to-one-join-column.type'; +import { type ScalarFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/scalar-flat-entity.type'; import { type AllJsonbPropertiesWithSerializedPropertiesForMetadataName } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-jsonb-properties-with-serialized-relation-by-metadata-name.constant'; import { type ToUniversalForeignKey } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-universal-metadata-relations.constant'; import { type ExtractJsonbProperties } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/extract-jsonb-properties.type'; -import { type UniversalFlatEntityExtraProperties } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type'; type HasObjectInUnion = T extends unknown ? T extends object @@ -18,351 +17,1256 @@ type MetadataEntityPropertyConfiguration< TMetadataName extends AllMetadataName, > = { [K in keyof Omit< - MetadataFlatEntity, - | Extract< - keyof MetadataFlatEntity, - keyof UniversalFlatEntityExtraProperties< - MetadataEntity, - TMetadataName - > - > - | '__universal' - >]?: { + ScalarFlatEntity>, + 'id' | 'workspaceId' | 'applicationId' | 'universalIdentifier' + >]: { universalProperty: K extends AllJsonbPropertiesWithSerializedPropertiesForMetadataName & string ? `universal${Capitalize}` : K extends MetadataManyToOneJoinColumn & string ? ToUniversalForeignKey - : undefined | string; + : undefined; toStringify: K extends ExtractJsonbProperties> ? true : K extends keyof MetadataEntity - ? HasObjectInUnion[K]> + ? NonNullable[K]> extends Date + ? false + : HasObjectInUnion[K]> : boolean; + toCompare: boolean; }; }; export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = { fieldMetadata: { - defaultValue: { toStringify: true, universalProperty: undefined }, - description: { toStringify: false, universalProperty: undefined }, - icon: { toStringify: false, universalProperty: undefined }, - isActive: { toStringify: false, universalProperty: undefined }, - isLabelSyncedWithName: { toStringify: false, universalProperty: undefined }, - isUnique: { toStringify: false, universalProperty: undefined }, - label: { toStringify: false, universalProperty: undefined }, - name: { toStringify: false, universalProperty: undefined }, - options: { toStringify: true, universalProperty: undefined }, - standardOverrides: { toStringify: true, universalProperty: undefined }, - settings: { toStringify: true, universalProperty: 'universalSettings' }, - }, - objectMetadata: { - description: { toStringify: false, universalProperty: undefined }, - icon: { toStringify: false, universalProperty: undefined }, - isActive: { toStringify: false, universalProperty: undefined }, - isLabelSyncedWithName: { toStringify: false, universalProperty: undefined }, - labelPlural: { toStringify: false, universalProperty: undefined }, - labelSingular: { toStringify: false, universalProperty: undefined }, - namePlural: { toStringify: false, universalProperty: undefined }, - nameSingular: { toStringify: false, universalProperty: undefined }, - labelIdentifierFieldMetadataId: { - toStringify: false, - universalProperty: 'labelIdentifierFieldMetadataUniversalIdentifier', + defaultValue: { + toCompare: true, + toStringify: true, + universalProperty: undefined, }, - standardOverrides: { toStringify: true, universalProperty: undefined }, - }, - view: { - key: { toStringify: false, universalProperty: undefined }, - deletedAt: { toStringify: false, universalProperty: undefined }, - createdByUserWorkspaceId: { + description: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + icon: { toCompare: true, toStringify: false, universalProperty: undefined }, + isActive: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + isLabelSyncedWithName: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + isUnique: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + label: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + name: { toCompare: true, toStringify: false, universalProperty: undefined }, + options: { + toCompare: true, + toStringify: true, + universalProperty: undefined, + }, + standardOverrides: { + toCompare: true, + toStringify: true, + universalProperty: undefined, + }, + settings: { + toCompare: true, + toStringify: true, + universalProperty: 'universalSettings', + }, + objectMetadataId: { + toCompare: false, + toStringify: false, + universalProperty: 'objectMetadataUniversalIdentifier', + }, + type: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + isCustom: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + isSystem: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + isUIReadOnly: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + isNullable: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + relationTargetFieldMetadataId: { + toCompare: false, + toStringify: false, + universalProperty: 'relationTargetFieldMetadataUniversalIdentifier', + }, + relationTargetObjectMetadataId: { + toCompare: false, + toStringify: false, + universalProperty: 'relationTargetObjectMetadataUniversalIdentifier', + }, + morphId: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + }, + objectMetadata: { + dataSourceId: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + description: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + icon: { toCompare: true, toStringify: false, universalProperty: undefined }, + isActive: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + isLabelSyncedWithName: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + labelPlural: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + labelSingular: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + namePlural: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + nameSingular: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + labelIdentifierFieldMetadataId: { + toCompare: true, + toStringify: false, + // @ts-expect-error remove once https://github.com/twentyhq/core-team-issues/issues/2172 has been resolved + universalProperty: 'labelIdentifierFieldMetadataUniversalIdentifier', + }, + standardOverrides: { + toCompare: true, + toStringify: true, + universalProperty: undefined, + }, + isCustom: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + isRemote: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + isSystem: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + isUIReadOnly: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + isAuditLogged: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + isSearchable: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + duplicateCriteria: { + toCompare: false, + toStringify: true, + universalProperty: undefined, + }, + shortcut: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + imageIdentifierFieldMetadataId: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + targetTableName: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + }, + view: { + key: { toCompare: true, toStringify: false, universalProperty: undefined }, + deletedAt: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + createdByUserWorkspaceId: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + name: { toCompare: true, toStringify: false, universalProperty: undefined }, + type: { toCompare: true, toStringify: false, universalProperty: undefined }, + icon: { toCompare: true, toStringify: false, universalProperty: undefined }, + position: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + isCompact: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + openRecordIn: { + toCompare: true, toStringify: false, universalProperty: undefined, }, - name: { toStringify: false, universalProperty: undefined }, - type: { toStringify: false, universalProperty: undefined }, - icon: { toStringify: false, universalProperty: undefined }, - position: { toStringify: false, universalProperty: undefined }, - isCompact: { toStringify: false, universalProperty: undefined }, - openRecordIn: { toStringify: false, universalProperty: undefined }, kanbanAggregateOperation: { + toCompare: true, toStringify: false, universalProperty: undefined, }, kanbanAggregateOperationFieldMetadataId: { + toCompare: true, toStringify: false, universalProperty: 'kanbanAggregateOperationFieldMetadataUniversalIdentifier', }, - anyFieldFilterValue: { toStringify: false, universalProperty: undefined }, - calendarLayout: { toStringify: false, universalProperty: undefined }, + anyFieldFilterValue: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + calendarLayout: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, calendarFieldMetadataId: { + toCompare: true, toStringify: false, universalProperty: 'calendarFieldMetadataUniversalIdentifier', }, - visibility: { toStringify: false, universalProperty: undefined }, + visibility: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, mainGroupByFieldMetadataId: { + toCompare: true, toStringify: false, universalProperty: 'mainGroupByFieldMetadataUniversalIdentifier', }, - shouldHideEmptyGroups: { toStringify: false, universalProperty: undefined }, + shouldHideEmptyGroups: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + objectMetadataId: { + toCompare: false, + toStringify: false, + universalProperty: 'objectMetadataUniversalIdentifier', + }, + isCustom: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + }, + viewFieldGroup: { + name: { toStringify: false, universalProperty: undefined, toCompare: true }, + position: { + toStringify: false, + universalProperty: undefined, + toCompare: true, + }, + isVisible: { + toStringify: false, + universalProperty: undefined, + toCompare: true, + }, + deletedAt: { + toStringify: false, + universalProperty: undefined, + toCompare: true, + }, + createdAt: { + toCompare: false, + universalProperty: undefined, + toStringify: false, + }, + updatedAt: { + toCompare: false, + universalProperty: undefined, + toStringify: false, + }, + viewId: { + toCompare: false, + universalProperty: 'viewUniversalIdentifier', + toStringify: false, + }, }, viewField: { - isVisible: { toStringify: false, universalProperty: undefined }, - size: { toStringify: false, universalProperty: undefined }, - position: { toStringify: false, universalProperty: undefined }, - aggregateOperation: { toStringify: false, universalProperty: undefined }, + isVisible: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + size: { toCompare: true, toStringify: false, universalProperty: undefined }, + position: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + aggregateOperation: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, viewFieldGroupId: { toStringify: false, universalProperty: 'viewFieldGroupUniversalIdentifier', + toCompare: true, + }, + deletedAt: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + fieldMetadataId: { + toCompare: false, + toStringify: false, + universalProperty: 'fieldMetadataUniversalIdentifier', + }, + viewId: { + toCompare: false, + toStringify: false, + universalProperty: 'viewUniversalIdentifier', }, - deletedAt: { toStringify: false, universalProperty: undefined }, - }, - viewFieldGroup: { - name: { toStringify: false, universalProperty: undefined }, - position: { toStringify: false, universalProperty: undefined }, - isVisible: { toStringify: false, universalProperty: undefined }, - deletedAt: { toStringify: false, universalProperty: undefined }, }, viewGroup: { - isVisible: { toStringify: false, universalProperty: undefined }, - fieldValue: { toStringify: false, universalProperty: undefined }, - position: { toStringify: false, universalProperty: undefined }, - deletedAt: { toStringify: false, universalProperty: undefined }, + isVisible: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + fieldValue: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + position: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + deletedAt: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + viewId: { + toCompare: false, + toStringify: false, + universalProperty: 'viewUniversalIdentifier', + }, }, index: { - indexType: { toStringify: false, universalProperty: undefined }, - indexWhereClause: { toStringify: false, universalProperty: undefined }, - flatIndexFieldMetadatas: { - toStringify: true, - universalProperty: 'universalFlatIndexFieldMetadatas', + indexType: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + indexWhereClause: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + isUnique: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + name: { toCompare: true, toStringify: false, universalProperty: undefined }, + objectMetadataId: { + toCompare: false, + toStringify: false, + universalProperty: 'objectMetadataUniversalIdentifier', + }, + isCustom: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, }, - isUnique: { toStringify: false, universalProperty: undefined }, - name: { toStringify: false, universalProperty: undefined }, }, logicFunction: { - name: { toStringify: false, universalProperty: undefined }, - description: { toStringify: false, universalProperty: undefined }, - timeoutSeconds: { toStringify: false, universalProperty: undefined }, - checksum: { toStringify: false, universalProperty: undefined }, - sourceHandlerPath: { toStringify: false, universalProperty: undefined }, - handlerName: { toStringify: false, universalProperty: undefined }, - toolInputSchema: { toStringify: true, universalProperty: undefined }, - isTool: { toStringify: false, universalProperty: undefined }, - isBuildUpToDate: { toStringify: false, universalProperty: undefined }, - deletedAt: { toStringify: false, universalProperty: undefined }, - cronTriggerSettings: { toStringify: true, universalProperty: undefined }, + name: { toCompare: true, toStringify: false, universalProperty: undefined }, + description: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + timeoutSeconds: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + checksum: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + sourceHandlerPath: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + handlerName: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + toolInputSchema: { + toCompare: true, + toStringify: true, + universalProperty: undefined, + }, + isTool: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + isBuildUpToDate: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + deletedAt: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + cronTriggerSettings: { + toCompare: true, + toStringify: true, + universalProperty: undefined, + }, databaseEventTriggerSettings: { + toCompare: true, toStringify: true, universalProperty: undefined, }, httpRouteTriggerSettings: { + toCompare: true, toStringify: true, universalProperty: undefined, }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + builtHandlerPath: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + runtime: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, }, viewFilter: { viewId: { + toCompare: true, toStringify: false, universalProperty: 'viewUniversalIdentifier', }, - deletedAt: { toStringify: false, universalProperty: undefined }, + deletedAt: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, fieldMetadataId: { + toCompare: true, toStringify: false, universalProperty: 'fieldMetadataUniversalIdentifier', }, - operand: { toStringify: false, universalProperty: undefined }, - value: { toStringify: true, universalProperty: undefined }, + operand: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + value: { toCompare: true, toStringify: true, universalProperty: undefined }, viewFilterGroupId: { + toCompare: true, toStringify: false, universalProperty: 'viewFilterGroupUniversalIdentifier', }, positionInViewFilterGroup: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + subFieldName: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, toStringify: false, universalProperty: undefined, }, - subFieldName: { toStringify: false, universalProperty: undefined }, }, role: { - label: { toStringify: false, universalProperty: undefined }, - description: { toStringify: false, universalProperty: undefined }, - icon: { toStringify: false, universalProperty: undefined }, - canUpdateAllSettings: { toStringify: false, universalProperty: undefined }, - canAccessAllTools: { toStringify: false, universalProperty: undefined }, + label: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + description: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + icon: { toCompare: true, toStringify: false, universalProperty: undefined }, + canUpdateAllSettings: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + canAccessAllTools: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, canReadAllObjectRecords: { + toCompare: true, toStringify: false, universalProperty: undefined, }, canUpdateAllObjectRecords: { + toCompare: true, toStringify: false, universalProperty: undefined, }, canSoftDeleteAllObjectRecords: { + toCompare: true, toStringify: false, universalProperty: undefined, }, canDestroyAllObjectRecords: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + canBeAssignedToUsers: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + canBeAssignedToAgents: { + toCompare: true, toStringify: false, universalProperty: undefined, }, - canBeAssignedToUsers: { toStringify: false, universalProperty: undefined }, - canBeAssignedToAgents: { toStringify: false, universalProperty: undefined }, canBeAssignedToApiKeys: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + isEditable: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, toStringify: false, universalProperty: undefined, }, }, roleTarget: { roleId: { + toCompare: true, toStringify: false, universalProperty: 'roleUniversalIdentifier', }, - userWorkspaceId: { toStringify: false, universalProperty: undefined }, - apiKeyId: { toStringify: false, universalProperty: undefined }, - agentId: { toStringify: false, universalProperty: undefined }, + userWorkspaceId: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + apiKeyId: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + agentId: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, }, agent: { - name: { toStringify: false, universalProperty: undefined }, - label: { toStringify: false, universalProperty: undefined }, - icon: { toStringify: false, universalProperty: undefined }, - description: { toStringify: false, universalProperty: undefined }, - prompt: { toStringify: false, universalProperty: undefined }, - modelId: { toStringify: false, universalProperty: undefined }, - responseFormat: { toStringify: true, universalProperty: undefined }, - modelConfiguration: { toStringify: true, universalProperty: undefined }, - evaluationInputs: { toStringify: true, universalProperty: undefined }, + name: { toCompare: true, toStringify: false, universalProperty: undefined }, + label: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + icon: { toCompare: true, toStringify: false, universalProperty: undefined }, + description: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + prompt: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + modelId: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + responseFormat: { + toCompare: true, + toStringify: true, + universalProperty: undefined, + }, + modelConfiguration: { + toCompare: true, + toStringify: true, + universalProperty: undefined, + }, + evaluationInputs: { + toCompare: true, + toStringify: true, + universalProperty: undefined, + }, + isCustom: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + deletedAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, }, pageLayout: { - name: { toStringify: false, universalProperty: undefined }, - type: { toStringify: false, universalProperty: undefined }, + name: { toCompare: true, toStringify: false, universalProperty: undefined }, + type: { toCompare: true, toStringify: false, universalProperty: undefined }, objectMetadataId: { + toCompare: true, toStringify: false, universalProperty: 'objectMetadataUniversalIdentifier', }, defaultTabToFocusOnMobileAndSidePanelId: { + toCompare: true, toStringify: false, universalProperty: 'defaultTabToFocusOnMobileAndSidePanelUniversalIdentifier', }, - deletedAt: { toStringify: false, universalProperty: undefined }, + deletedAt: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, }, pageLayoutWidget: { - title: { toStringify: false, universalProperty: undefined }, - type: { toStringify: false, universalProperty: undefined }, + title: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + type: { toCompare: true, toStringify: false, universalProperty: undefined }, objectMetadataId: { + toCompare: true, toStringify: false, universalProperty: 'objectMetadataUniversalIdentifier', }, - gridPosition: { toStringify: true, universalProperty: undefined }, - position: { toStringify: true, universalProperty: undefined }, + gridPosition: { + toCompare: true, + toStringify: true, + universalProperty: undefined, + }, + position: { + toCompare: true, + toStringify: true, + universalProperty: undefined, + }, configuration: { + toCompare: true, toStringify: true, universalProperty: 'universalConfiguration', }, - deletedAt: { toStringify: false, universalProperty: undefined }, + deletedAt: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + pageLayoutTabId: { + toCompare: false, + toStringify: false, + universalProperty: 'pageLayoutTabUniversalIdentifier', + }, + conditionalDisplay: { + toCompare: false, + toStringify: true, + universalProperty: undefined, + }, }, pageLayoutTab: { - title: { toStringify: false, universalProperty: undefined }, - position: { toStringify: false, universalProperty: undefined }, - deletedAt: { toStringify: false, universalProperty: undefined }, + title: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + position: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + deletedAt: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + icon: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + pageLayoutId: { + toCompare: false, + toStringify: false, + universalProperty: 'pageLayoutUniversalIdentifier', + }, + layoutMode: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, }, skill: { - name: { toStringify: false, universalProperty: undefined }, - label: { toStringify: false, universalProperty: undefined }, - icon: { toStringify: false, universalProperty: undefined }, - description: { toStringify: false, universalProperty: undefined }, - content: { toStringify: false, universalProperty: undefined }, - isActive: { toStringify: false, universalProperty: undefined }, + name: { toCompare: true, toStringify: false, universalProperty: undefined }, + label: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + icon: { toCompare: true, toStringify: false, universalProperty: undefined }, + description: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + content: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + isActive: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + isCustom: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, }, commandMenuItem: { - label: { toStringify: false, universalProperty: undefined }, - icon: { toStringify: false, universalProperty: undefined }, - isPinned: { toStringify: false, universalProperty: undefined }, - availabilityType: { toStringify: false, universalProperty: undefined }, + label: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + icon: { toCompare: true, toStringify: false, universalProperty: undefined }, + isPinned: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + availabilityType: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, availabilityObjectMetadataId: { + toCompare: true, toStringify: false, universalProperty: 'availabilityObjectMetadataUniversalIdentifier', }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + frontComponentId: { + toCompare: false, + toStringify: false, + universalProperty: 'frontComponentUniversalIdentifier', + }, + workflowVersionId: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, }, navigationMenuItem: { - position: { toStringify: false, universalProperty: undefined }, + position: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, folderId: { + toCompare: true, toStringify: false, universalProperty: 'folderUniversalIdentifier', }, - name: { toStringify: false, universalProperty: undefined }, - link: { toStringify: false, universalProperty: undefined }, + name: { toCompare: true, toStringify: false, universalProperty: undefined }, + link: { toCompare: true, toStringify: false, universalProperty: undefined }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + viewId: { + toCompare: false, + toStringify: false, + universalProperty: 'viewUniversalIdentifier', + }, + userWorkspaceId: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + targetRecordId: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + targetObjectMetadataId: { + toCompare: false, + toStringify: false, + universalProperty: 'targetObjectMetadataUniversalIdentifier', + }, }, rowLevelPermissionPredicate: { fieldMetadataId: { + toCompare: true, toStringify: false, universalProperty: 'fieldMetadataUniversalIdentifier', }, - operand: { toStringify: false, universalProperty: undefined }, - value: { toStringify: true, universalProperty: undefined }, + operand: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + value: { toCompare: true, toStringify: true, universalProperty: undefined }, rowLevelPermissionPredicateGroupId: { + toCompare: true, toStringify: false, universalProperty: 'rowLevelPermissionPredicateGroupUniversalIdentifier', }, positionInRowLevelPermissionPredicateGroup: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + subFieldName: { + toCompare: true, toStringify: false, universalProperty: undefined, }, - subFieldName: { toStringify: false, universalProperty: undefined }, workspaceMemberFieldMetadataId: { + toCompare: true, toStringify: false, universalProperty: 'workspaceMemberFieldMetadataUniversalIdentifier', }, workspaceMemberSubFieldName: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + deletedAt: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + objectMetadataId: { + toCompare: false, + toStringify: false, + universalProperty: 'objectMetadataUniversalIdentifier', + }, + roleId: { + toCompare: false, + toStringify: false, + universalProperty: 'roleUniversalIdentifier', + }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, toStringify: false, universalProperty: undefined, }, - deletedAt: { toStringify: false, universalProperty: undefined }, }, rowLevelPermissionPredicateGroup: { - logicalOperator: { toStringify: false, universalProperty: undefined }, + logicalOperator: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, positionInRowLevelPermissionPredicateGroup: { + toCompare: true, toStringify: false, universalProperty: undefined, }, parentRowLevelPermissionPredicateGroupId: { + toCompare: true, toStringify: false, universalProperty: 'parentRowLevelPermissionPredicateGroupUniversalIdentifier', }, - deletedAt: { toStringify: false, universalProperty: undefined }, + deletedAt: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + objectMetadataId: { + toCompare: false, + toStringify: false, + universalProperty: 'objectMetadataUniversalIdentifier', + }, + roleId: { + toCompare: false, + toStringify: false, + universalProperty: 'roleUniversalIdentifier', + }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, }, viewFilterGroup: { viewId: { + toCompare: true, toStringify: false, universalProperty: 'viewUniversalIdentifier', }, - deletedAt: { toStringify: false, universalProperty: undefined }, + deletedAt: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, parentViewFilterGroupId: { + toCompare: true, toStringify: false, universalProperty: 'parentViewFilterGroupUniversalIdentifier', }, - logicalOperator: { toStringify: false, universalProperty: undefined }, + logicalOperator: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, positionInViewFilterGroup: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, toStringify: false, universalProperty: undefined, }, }, frontComponent: { - name: { toStringify: false, universalProperty: undefined }, - description: { toStringify: false, universalProperty: undefined }, - builtComponentChecksum: { + name: { toCompare: true, toStringify: false, universalProperty: undefined }, + description: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + builtComponentChecksum: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + sourceComponentPath: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + builtComponentPath: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + componentName: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, toStringify: false, universalProperty: undefined, }, - sourceComponentPath: { toStringify: false, universalProperty: undefined }, - builtComponentPath: { toStringify: false, universalProperty: undefined }, - componentName: { toStringify: false, universalProperty: undefined }, }, webhook: { - targetUrl: { toStringify: false, universalProperty: undefined }, - operations: { toStringify: true, universalProperty: undefined }, - description: { toStringify: false, universalProperty: undefined }, - secret: { toStringify: false, universalProperty: undefined }, + targetUrl: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + operations: { + toCompare: true, + toStringify: true, + universalProperty: undefined, + }, + description: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + secret: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, + createdAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + updatedAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, + deletedAt: { + toCompare: false, + toStringify: false, + universalProperty: undefined, + }, }, } as const satisfies { [P in AllMetadataName]: MetadataEntityPropertyConfiguration

; @@ -370,3 +1274,12 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = { export type MetadataEntityPropertyName = keyof (typeof ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME)[T]; + +type FilterComparableKeys = { + [P in keyof TConfig]: TConfig[P] extends { toCompare: true } ? P : never; +}[keyof TConfig]; + +export type MetadataEntityComparablePropertyName = + FilterComparableKeys< + (typeof ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME)[T] + >; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-universal-flat-entity-properties-to-compare-and-stringify.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-universal-flat-entity-properties-to-compare-and-stringify.constant.ts index cbfad94a73..635c9775e4 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-universal-flat-entity-properties-to-compare-and-stringify.constant.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-universal-flat-entity-properties-to-compare-and-stringify.constant.ts @@ -4,9 +4,31 @@ import { } from 'twenty-shared/metadata'; import { ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant'; +import { type MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type'; import { type MetadataUniversalFlatEntityPropertiesToCompare } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/metadata-universal-flat-entity-properties-to-compare.type'; import { type MetadataUniversalFlatEntityPropertiesToStringify } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/metadata-universal-flat-entity-properties-to-stringify.type'; +type PropertyConfiguration = { + universalProperty: string | undefined; + toStringify: boolean; + toCompare: boolean; +}; + +// TODO remove once https://github.com/twentyhq/core-team-issues/issues/2227 has been resolved +const EXTRA_PROPERTIES_TO_COMPARE = { + index: { + flatIndexFieldMetadatas: { + toStringify: true, + universalProperty: 'universalFlatIndexFieldMetadatas', + toCompare: true, + }, + }, +} as const satisfies { + [P in AllMetadataName]?: Partial< + Record, PropertyConfiguration> + >; +}; + type UniversalFlatEntityPropertiesToCompareAndStringify< T extends AllMetadataName, > = { @@ -18,18 +40,23 @@ const computeUniversalFlatEntityPropertiesToCompareAndStringify = < >( metadataName: T, ): UniversalFlatEntityPropertiesToCompareAndStringify => { - const entries = Object.entries( - ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME[metadataName], - ) as [ - string, - { universalProperty: string | undefined; toStringify: boolean }, - ][]; + const entries = Object.entries({ + ...ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME[metadataName], + ...EXTRA_PROPERTIES_TO_COMPARE[ + metadataName as keyof typeof EXTRA_PROPERTIES_TO_COMPARE + ], + }) as [string, PropertyConfiguration][]; + const accumulator: UniversalFlatEntityPropertiesToCompareAndStringify = { propertiesToCompare: [], propertiesToStringify: [], }; for (const [property, configuration] of entries) { + if (!configuration.toCompare) { + continue; + } + const comparedProperty = configuration.universalProperty ?? property; accumulator.propertiesToCompare.push( diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/flat-entity-from.type.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/flat-entity-from.type.ts index e82c53ded6..f095014518 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/flat-entity-from.type.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/flat-entity-from.type.ts @@ -1,9 +1,8 @@ import { type AllMetadataName } from 'twenty-shared/metadata'; import { type AddSuffixToEntityOneToManyProperties } from 'src/engine/metadata-modules/flat-entity/types/add-suffix-to-entity-one-to-many-properties.type'; -import { type CastRecordTypeOrmDatePropertiesToString } from 'src/engine/metadata-modules/flat-entity/types/cast-record-typeorm-date-properties-to-string.type'; -import { type ExtractEntityRelatedEntityProperties } from 'src/engine/metadata-modules/flat-entity/types/extract-entity-related-entity-properties.type'; import { type FromMetadataEntityToMetadataName } from 'src/engine/metadata-modules/flat-entity/types/from-metadata-entity-to-metadata-name.type'; +import { type ScalarFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/scalar-flat-entity.type'; import { type SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface'; import { type UniversalFlatEntityExtraProperties } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type'; @@ -14,18 +13,11 @@ export type SyncableFlatEntity = Omit< id: string; }; -type AtomicFlatEntity = Omit< - TEntity, - | ExtractEntityRelatedEntityProperties - | keyof CastRecordTypeOrmDatePropertiesToString ->; - export type FlatEntityFrom< TEntity, // Required to be passed for narrowed type TMetadataName extends AllMetadataName | undefined = undefined, -> = AtomicFlatEntity & - CastRecordTypeOrmDatePropertiesToString & +> = ScalarFlatEntity & AddSuffixToEntityOneToManyProperties & (TEntity extends SyncableEntity ? UniversalFlatEntityExtraProperties< diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/flat-entity-update.type.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/flat-entity-update.type.ts index d9adacc740..f5b8da5c5b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/flat-entity-update.type.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/flat-entity-update.type.ts @@ -1,6 +1,9 @@ import { type AllMetadataName } from 'twenty-shared/metadata'; -import { type ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant'; +import { + type ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME, + type MetadataEntityComparablePropertyName, +} from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant'; import { type MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type'; type IsUniversalMappedProperty< @@ -13,15 +16,19 @@ type IsUniversalMappedProperty< export type FlatEntityUpdate< T extends AllMetadataName, MetadataPropertyConfig = (typeof ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME)[T], + TComparedKeys extends + keyof MetadataPropertyConfig = MetadataEntityComparablePropertyName & + keyof MetadataPropertyConfig, > = Partial< Pick< MetadataFlatEntity, - Extract, keyof MetadataPropertyConfig> + Extract, TComparedKeys> > > & { - [K in keyof MetadataPropertyConfig as [ - never, - ] extends IsUniversalMappedProperty + [K in TComparedKeys as [never] extends IsUniversalMappedProperty< + MetadataPropertyConfig, + K + > ? never : IsUniversalMappedProperty]?: never; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/scalar-flat-entity.type.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/scalar-flat-entity.type.ts new file mode 100644 index 0000000000..834f8b2569 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/scalar-flat-entity.type.ts @@ -0,0 +1,9 @@ +import { type CastRecordTypeOrmDatePropertiesToString } from 'src/engine/metadata-modules/flat-entity/types/cast-record-typeorm-date-properties-to-string.type'; +import { type ExtractEntityRelatedEntityProperties } from 'src/engine/metadata-modules/flat-entity/types/extract-entity-related-entity-properties.type'; + +export type ScalarFlatEntity = Omit< + TEntity, + | ExtractEntityRelatedEntityProperties + | keyof CastRecordTypeOrmDatePropertiesToString +> & + CastRecordTypeOrmDatePropertiesToString; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/metadata-universal-flat-entity-properties-to-compare.type.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/metadata-universal-flat-entity-properties-to-compare.type.ts index 2fa41343c8..f8024ed4f7 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/metadata-universal-flat-entity-properties-to-compare.type.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/metadata-universal-flat-entity-properties-to-compare.type.ts @@ -1,7 +1,10 @@ import { type AllMetadataName } from 'twenty-shared/metadata'; import { type Equal, type Expect } from 'twenty-shared/testing'; -import { type ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant'; +import { + type ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME, + type MetadataEntityComparablePropertyName, +} from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant'; import { type MetadataUniversalFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-universal-flat-entity.type'; type ExtractPropertyToCompare< @@ -14,9 +17,12 @@ type ExtractPropertyToCompare< export type MetadataUniversalFlatEntityPropertiesToCompare< T extends AllMetadataName, MetadataConfig = (typeof ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME)[T], + TComparedKeys extends + keyof MetadataConfig = MetadataEntityComparablePropertyName & + keyof MetadataConfig, > = { - [P in keyof MetadataConfig]: ExtractPropertyToCompare; -}[keyof MetadataConfig] & + [P in TComparedKeys]: ExtractPropertyToCompare; +}[TComparedKeys] & keyof MetadataUniversalFlatEntity; // eslint-disable-next-line unused-imports/no-unused-vars diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/metadata-universal-flat-entity-properties-to-stringify.type.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/metadata-universal-flat-entity-properties-to-stringify.type.ts index e48c348bb6..7edb04a688 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/metadata-universal-flat-entity-properties-to-stringify.type.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/metadata-universal-flat-entity-properties-to-stringify.type.ts @@ -2,12 +2,14 @@ import { type AllMetadataName } from 'twenty-shared/metadata'; import { type ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant'; -type ExtractKeysWithToStringify = { - [K in keyof T]: T[K] extends { toStringify: true } ? K : never; +type ExtractComparableKeysWithToStringify = { + [K in keyof T]: T[K] extends { toCompare: true; toStringify: true } + ? K + : never; }[keyof T]; export type MetadataUniversalFlatEntityPropertiesToStringify< T extends AllMetadataName, -> = ExtractKeysWithToStringify< +> = ExtractComparableKeysWithToStringify< (typeof ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME)[T] >; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-index-metadata.type.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-index-metadata.type.ts index 6df59516d2..17aaefe28c 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-index-metadata.type.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-index-metadata.type.ts @@ -3,7 +3,7 @@ import { type IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index import { type IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity'; import { type UniversalFlatEntityFrom } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type'; -// Note: IndexFieldMetadataEntity is just in between a SyncableEntity and a jsonb we should decide one of both +// Note: IndexFieldMetadataEntity is just in between a SyncableEntity and a jsonb we should decide one of both https://github.com/twentyhq/core-team-issues/issues/2227 export type UniversalFlatIndexFieldMetadata = Omit< IndexFieldMetadataEntity, | 'indexMetadataId' diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/transform-universal-flat-entity-for-comparison.util.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/transform-universal-flat-entity-for-comparison.util.ts index b91ad9e75b..1f4aa23b15 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/transform-universal-flat-entity-for-comparison.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/transform-universal-flat-entity-for-comparison.util.ts @@ -22,7 +22,7 @@ export function transformUniversalFlatEntityForComparison< if ( propertiesToStringify.includes( - propertyToCompare as MetadataUniversalFlatEntityPropertiesToStringify, + propertyToCompare as string as MetadataUniversalFlatEntityPropertiesToStringify, ) ) { const orderedValue = orderObjectProperties(currentValue); diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/agent/services/create-agent-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/agent/services/create-agent-action-handler.service.ts index fda5037f5f..0bceff7913 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/agent/services/create-agent-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/agent/services/create-agent-action-handler.service.ts @@ -4,7 +4,6 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; -import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity'; import { FlatCreateAgentAction, UniversalCreateAgentAction, @@ -42,15 +41,12 @@ export class CreateAgentActionHandlerService extends WorkspaceMigrationRunnerAct async executeForMetadata( context: WorkspaceMigrationActionRunnerContext, ): Promise { - const { flatAction, queryRunner, workspaceId } = context; + const { flatAction, queryRunner } = context; const { flatEntity } = flatAction; - const agentRepository = - queryRunner.manager.getRepository(AgentEntity); - - await agentRepository.save({ - ...flatEntity, - workspaceId, + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [flatEntity], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/command-menu-item/services/create-command-menu-item-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/command-menu-item/services/create-command-menu-item-action-handler.service.ts index 31c2ca138a..04ce06508d 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/command-menu-item/services/create-command-menu-item-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/command-menu-item/services/create-command-menu-item-action-handler.service.ts @@ -4,7 +4,6 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; -import { CommandMenuItemEntity } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity'; import { resolveUniversalRelationIdentifiersToIds } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util'; import { FlatCreateCommandMenuItemAction, @@ -53,17 +52,12 @@ export class CreateCommandMenuItemActionHandlerService extends WorkspaceMigratio async executeForMetadata( context: WorkspaceMigrationActionRunnerContext, ): Promise { - const { flatAction, queryRunner, workspaceId } = context; + const { flatAction, queryRunner } = context; const { flatEntity } = flatAction; - const commandMenuItemRepository = - queryRunner.manager.getRepository( - CommandMenuItemEntity, - ); - - await commandMenuItemRepository.insert({ - ...flatEntity, - workspaceId, + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [flatEntity], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/field/services/create-field-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/field/services/create-field-action-handler.service.ts index fc470c7f36..bf0a4c992c 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/field/services/create-field-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/field/services/create-field-action-handler.service.ts @@ -7,7 +7,6 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; -import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { type MetadataFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity-maps.type'; import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/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'; @@ -91,12 +90,10 @@ export class CreateFieldActionHandlerService extends WorkspaceMigrationRunnerAct const { queryRunner, flatAction } = context; const { flatFieldMetadatas } = flatAction; - const fieldMetadataRepository = - queryRunner.manager.getRepository( - FieldMetadataEntity, - ); - - await fieldMetadataRepository.insert(flatFieldMetadatas); + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: flatFieldMetadatas, + }); } async executeForWorkspaceSchema( diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/front-component/services/create-front-component-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/front-component/services/create-front-component-action-handler.service.ts index b17c3b8ad8..1edf083b82 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/front-component/services/create-front-component-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/front-component/services/create-front-component-action-handler.service.ts @@ -7,7 +7,6 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; import { FileStorageService } from 'src/engine/core-modules/file-storage/file-storage.service'; -import { FrontComponentEntity } from 'src/engine/metadata-modules/front-component/entities/front-component.entity'; import { FrontComponentException, FrontComponentExceptionCode, @@ -62,14 +61,9 @@ export class CreateFrontComponentActionHandlerService extends WorkspaceMigration }); } - const frontComponentRepository = - queryRunner.manager.getRepository( - FrontComponentEntity, - ); - - await frontComponentRepository.insert({ - ...frontComponent, - workspaceId, + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [frontComponent], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/index/services/create-index-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/index/services/create-index-action-handler.service.ts index 33ebe04662..121985b6a4 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/index/services/create-index-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/index/services/create-index-action-handler.service.ts @@ -5,16 +5,14 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util'; +import { IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity'; import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service'; import { type FlatCreateIndexAction, type UniversalCreateIndexAction, } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/index/types/workspace-migration-index-action'; import { fromUniversalFlatIndexToFlatIndex } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/index/utils/from-universal-flat-index-to-flat-index.util'; -import { - createIndexInWorkspaceSchema, - insertIndexMetadata, -} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/index/utils/index-action-handler.utils'; +import { createIndexInWorkspaceSchema } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/index/utils/index-action-handler.utils'; import { type WorkspaceMigrationActionRunnerArgs, type WorkspaceMigrationActionRunnerContext, @@ -55,11 +53,20 @@ export class CreateIndexActionHandlerService extends WorkspaceMigrationRunnerAct context: WorkspaceMigrationActionRunnerContext, ): Promise { const { flatAction, queryRunner } = context; + const { flatEntity: flatIndexMetadata } = flatAction; - await insertIndexMetadata({ - flatIndexMetadata: flatAction.flatEntity, + await this.insertFlatEntitiesInRepository({ queryRunner, + flatEntities: [flatIndexMetadata], }); + + const indexFieldMetadataRepository = queryRunner.manager.getRepository( + IndexFieldMetadataEntity, + ); + + await indexFieldMetadataRepository.insert( + flatIndexMetadata.flatIndexFieldMetadatas, + ); } async executeForWorkspaceSchema( diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/index/services/update-index-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/index/services/update-index-action-handler.service.ts index 45c864a3ce..00305cd9e7 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/index/services/update-index-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/index/services/update-index-action-handler.service.ts @@ -6,6 +6,7 @@ import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-mana import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util'; import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util'; +import { IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity'; import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service'; import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util'; import { @@ -17,7 +18,6 @@ import { createIndexInWorkspaceSchema, deleteIndexMetadata, dropIndexFromWorkspaceSchema, - insertIndexMetadata, } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/index/utils/index-action-handler.utils'; import { type WorkspaceMigrationActionRunnerArgs, @@ -66,19 +66,32 @@ export class UpdateIndexActionHandlerService extends WorkspaceMigrationRunnerAct context: WorkspaceMigrationActionRunnerContext, ): Promise { const { flatAction, queryRunner, workspaceId } = context; + const { updatedFlatIndex } = flatAction; - // Delete old index metadata await deleteIndexMetadata({ entityId: flatAction.entityId, queryRunner, workspaceId, }); - // Create new index metadata - await insertIndexMetadata({ - flatIndexMetadata: flatAction.updatedFlatIndex, + await this.insertFlatEntitiesInRepository({ queryRunner, + flatEntities: [updatedFlatIndex], }); + + const indexFieldMetadataRepository = queryRunner.manager.getRepository( + IndexFieldMetadataEntity, + ); + + const indexFieldMetadataToInsert = + updatedFlatIndex.flatIndexFieldMetadatas.map( + (flatIndexFieldMetadata) => ({ + ...flatIndexFieldMetadata, + indexMetadataId: updatedFlatIndex.id, + }), + ); + + await indexFieldMetadataRepository.insert(indexFieldMetadataToInsert); } async executeForWorkspaceSchema( diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/index/utils/index-action-handler.utils.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/index/utils/index-action-handler.utils.ts index 2e1ba53041..5ce7589553 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/index/utils/index-action-handler.utils.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/index/utils/index-action-handler.utils.ts @@ -15,14 +15,9 @@ import { type FlatIndexFieldMetadata, type FlatIndexMetadata, } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type'; -import { IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity'; -import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity'; import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; +import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity'; import { type WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service'; -import { - WorkspaceMigrationActionExecutionException, - WorkspaceMigrationActionExecutionExceptionCode, -} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/exceptions/workspace-migration-action-execution.exception'; import { getWorkspaceSchemaContextForMigration } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/get-workspace-schema-context-for-migration.util'; export const computeFlatIndexFieldColumnNames = ({ @@ -81,45 +76,6 @@ export const computeFlatIndexFieldColumnNames = ({ }); }; -export const insertIndexMetadata = async ({ - flatIndexMetadata, - queryRunner, -}: { - flatIndexMetadata: FlatIndexMetadata; - queryRunner: QueryRunner; -}): Promise => { - const indexMetadataRepository = - queryRunner.manager.getRepository(IndexMetadataEntity); - const indexFieldMetadataRepository = - queryRunner.manager.getRepository( - IndexFieldMetadataEntity, - ); - - const { flatIndexFieldMetadatas, ...indexMetadataToInsert } = - flatIndexMetadata; - - const indexInsertResult = await indexMetadataRepository.insert( - indexMetadataToInsert, - ); - - if (indexInsertResult.identifiers.length !== 1) { - throw new WorkspaceMigrationActionExecutionException({ - message: 'Failed to create index metadata', - code: WorkspaceMigrationActionExecutionExceptionCode.INTERNAL_SERVER_ERROR, - }); - } - const indexMetadataId = indexInsertResult.identifiers[0].id; - - const indexFieldMetadataToInsert = flatIndexFieldMetadatas.map( - (flatIndexFieldMetadata) => ({ - ...flatIndexFieldMetadata, - indexMetadataId, - }), - ); - - await indexFieldMetadataRepository.insert(indexFieldMetadataToInsert); -}; - export const deleteIndexMetadata = async ({ entityId, queryRunner, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/logic-function/services/create-logic-function-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/logic-function/services/create-logic-function-action-handler.service.ts index 052249b08d..e4899fddfb 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/logic-function/services/create-logic-function-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/logic-function/services/create-logic-function-action-handler.service.ts @@ -4,7 +4,6 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; -import { LogicFunctionEntity } from 'src/engine/metadata-modules/logic-function/logic-function.entity'; import { FlatCreateLogicFunctionAction, UniversalCreateLogicFunctionAction, @@ -38,17 +37,12 @@ export class CreateLogicFunctionActionHandlerService extends WorkspaceMigrationR async executeForMetadata( context: WorkspaceMigrationActionRunnerContext, ): Promise { - const { flatAction, queryRunner, workspaceId } = context; + const { flatAction, queryRunner } = context; const { flatEntity: logicFunction } = flatAction; - const logicFunctionRepository = - queryRunner.manager.getRepository( - LogicFunctionEntity, - ); - - await logicFunctionRepository.insert({ - ...logicFunction, - workspaceId, + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [logicFunction], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/navigation-menu-item/services/create-navigation-menu-item-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/navigation-menu-item/services/create-navigation-menu-item-action-handler.service.ts index f5c793ed21..fc62659f12 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/navigation-menu-item/services/create-navigation-menu-item-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/navigation-menu-item/services/create-navigation-menu-item-action-handler.service.ts @@ -4,7 +4,6 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; -import { NavigationMenuItemEntity } from 'src/engine/metadata-modules/navigation-menu-item/entities/navigation-menu-item.entity'; import { resolveUniversalRelationIdentifiersToIds } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util'; import { FlatCreateNavigationMenuItemAction, @@ -54,17 +53,12 @@ export class CreateNavigationMenuItemActionHandlerService extends WorkspaceMigra async executeForMetadata( context: WorkspaceMigrationActionRunnerContext, ): Promise { - const { flatAction, queryRunner, workspaceId } = context; + const { flatAction, queryRunner } = context; const { flatEntity } = flatAction; - const navigationMenuItemRepository = - queryRunner.manager.getRepository( - NavigationMenuItemEntity, - ); - - await navigationMenuItemRepository.insert({ - ...flatEntity, - workspaceId, + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [flatEntity], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/object/services/create-object-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/object/services/create-object-action-handler.service.ts index cde9959b78..421a1224fb 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/object/services/create-object-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/object/services/create-object-action-handler.service.ts @@ -5,10 +5,9 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity'; -import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { ALL_METADATA_ENTITY_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-entity-by-metadata-name.constant'; import { isCompositeFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-composite-flat-field-metadata.util'; import { isEnumFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-enum-flat-field-metadata.util'; -import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service'; import { FlatCreateObjectAction, @@ -20,6 +19,7 @@ import { WorkspaceMigrationActionRunnerContext, type WorkspaceMigrationActionRunnerArgs, } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/types/workspace-migration-action-runner-args.type'; +import { flatEntityToScalarFlatEntity } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/flat-entity-to-scalar-flat-entity.util'; import { generateColumnDefinitions } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/generate-column-definitions.util'; import { getWorkspaceSchemaContextForMigration } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/get-workspace-schema-context-for-migration.util'; import { @@ -107,19 +107,23 @@ export class CreateObjectActionHandlerService extends WorkspaceMigrationRunnerAc const { queryRunner, flatAction } = context; const { flatEntity: flatObjectMetadata, flatFieldMetadatas } = flatAction; - const objectMetadataRepository = - queryRunner.manager.getRepository( - ObjectMetadataEntity, - ); + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [flatObjectMetadata], + }); - await objectMetadataRepository.insert(flatObjectMetadata); + const scalarFieldMetadatas = flatFieldMetadatas.map((flatFieldMetadata) => + flatEntityToScalarFlatEntity({ + metadataName: 'fieldMetadata', + flatEntity: flatFieldMetadata, + }), + ); - const fieldMetadataRepository = - queryRunner.manager.getRepository( - FieldMetadataEntity, - ); + const fieldMetadataRepository = queryRunner.manager.getRepository( + ALL_METADATA_ENTITY_BY_METADATA_NAME['fieldMetadata'], + ); - await fieldMetadataRepository.insert(flatFieldMetadatas); + await fieldMetadataRepository.insert(scalarFieldMetadatas); } async executeForWorkspaceSchema( diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/page-layout-tab/services/create-page-layout-tab-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/page-layout-tab/services/create-page-layout-tab-action-handler.service.ts index b87b895982..eef30805ae 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/page-layout-tab/services/create-page-layout-tab-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/page-layout-tab/services/create-page-layout-tab-action-handler.service.ts @@ -4,7 +4,6 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; -import { PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab/entities/page-layout-tab.entity'; import { resolveUniversalRelationIdentifiersToIds } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util'; import { FlatCreatePageLayoutTabAction, @@ -52,17 +51,12 @@ export class CreatePageLayoutTabActionHandlerService extends WorkspaceMigrationR async executeForMetadata( context: WorkspaceMigrationActionRunnerContext, ): Promise { - const { flatAction, queryRunner, workspaceId } = context; + const { flatAction, queryRunner } = context; const { flatEntity } = flatAction; - const pageLayoutTabRepository = - queryRunner.manager.getRepository( - PageLayoutTabEntity, - ); - - await pageLayoutTabRepository.insert({ - ...flatEntity, - workspaceId, + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [flatEntity], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/page-layout-widget/services/create-page-layout-widget-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/page-layout-widget/services/create-page-layout-widget-action-handler.service.ts index 836d8d0fba..f5ccde47d8 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/page-layout-widget/services/create-page-layout-widget-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/page-layout-widget/services/create-page-layout-widget-action-handler.service.ts @@ -4,7 +4,6 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; -import { PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity'; import { resolveUniversalRelationIdentifiersToIds } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util'; import { FlatCreatePageLayoutWidgetAction, @@ -61,17 +60,12 @@ export class CreatePageLayoutWidgetActionHandlerService extends WorkspaceMigrati async executeForMetadata( context: WorkspaceMigrationActionRunnerContext, ): Promise { - const { flatAction, queryRunner, workspaceId } = context; + const { flatAction, queryRunner } = context; const { flatEntity } = flatAction; - const pageLayoutWidgetRepository = - queryRunner.manager.getRepository( - PageLayoutWidgetEntity, - ); - - await pageLayoutWidgetRepository.insert({ - ...flatEntity, - workspaceId, + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [flatEntity], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/page-layout/services/create-page-layout-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/page-layout/services/create-page-layout-action-handler.service.ts index d403c03c59..c000d61b5b 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/page-layout/services/create-page-layout-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/page-layout/services/create-page-layout-action-handler.service.ts @@ -9,7 +9,6 @@ import { FlatEntityMapsException, FlatEntityMapsExceptionCode, } from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; -import { PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout.entity'; import { resolveUniversalRelationIdentifiersToIds } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util'; import { FlatCreatePageLayoutAction, @@ -88,15 +87,12 @@ export class CreatePageLayoutActionHandlerService extends WorkspaceMigrationRunn async executeForMetadata( context: WorkspaceMigrationActionRunnerContext, ): Promise { - const { flatAction, queryRunner, workspaceId } = context; + const { flatAction, queryRunner } = context; const { flatEntity } = flatAction; - const pageLayoutRepository = - queryRunner.manager.getRepository(PageLayoutEntity); - - await pageLayoutRepository.insert({ - ...flatEntity, - workspaceId, + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [flatEntity], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/role-target/services/create-role-target-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/role-target/services/create-role-target-action-handler.service.ts index 15cad30773..0d876da3c7 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/role-target/services/create-role-target-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/role-target/services/create-role-target-action-handler.service.ts @@ -4,7 +4,6 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; -import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity'; import { resolveUniversalRelationIdentifiersToIds } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util'; import { FlatCreateRoleTargetAction, @@ -47,15 +46,12 @@ export class CreateRoleTargetActionHandlerService extends WorkspaceMigrationRunn async executeForMetadata( context: WorkspaceMigrationActionRunnerContext, ): Promise { - const { flatAction, queryRunner, workspaceId } = context; - const { flatEntity: roleTarget } = flatAction; + const { flatAction, queryRunner } = context; + const { flatEntity } = flatAction; - const roleTargetRepository = - queryRunner.manager.getRepository(RoleTargetEntity); - - await roleTargetRepository.insert({ - ...roleTarget, - workspaceId, + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [flatEntity], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/role/services/create-role-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/role/services/create-role-action-handler.service.ts index 375bb4de04..78fbe8ac9c 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/role/services/create-role-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/role/services/create-role-action-handler.service.ts @@ -4,7 +4,6 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; -import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; import { FlatCreateRoleAction, UniversalCreateRoleAction, @@ -48,15 +47,12 @@ export class CreateRoleActionHandlerService extends WorkspaceMigrationRunnerActi async executeForMetadata( context: WorkspaceMigrationActionRunnerContext, ): Promise { - const { flatAction, queryRunner, workspaceId } = context; - const { flatEntity: role } = flatAction; + const { flatAction, queryRunner } = context; + const { flatEntity } = flatAction; - const roleRepository = - queryRunner.manager.getRepository(RoleEntity); - - await roleRepository.insert({ - ...role, - workspaceId, + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [flatEntity], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/row-level-permission-predicate-group/services/create-row-level-permission-predicate-group-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/row-level-permission-predicate-group/services/create-row-level-permission-predicate-group-action-handler.service.ts index 9e083d39f8..cff6391fc8 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/row-level-permission-predicate-group/services/create-row-level-permission-predicate-group-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/row-level-permission-predicate-group/services/create-row-level-permission-predicate-group-action-handler.service.ts @@ -6,7 +6,6 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; -import { RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity'; import { resolveUniversalRelationIdentifiersToIds } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util'; import { FlatCreateRowLevelPermissionPredicateGroupAction, @@ -57,17 +56,12 @@ export class CreateRowLevelPermissionPredicateGroupActionHandlerService extends async executeForMetadata( context: WorkspaceMigrationActionRunnerContext, ): Promise { - const { flatAction, queryRunner, workspaceId } = context; + const { flatAction, queryRunner } = context; const { flatEntity } = flatAction; - const repository = - queryRunner.manager.getRepository( - RowLevelPermissionPredicateGroupEntity, - ); - - await repository.insert({ - ...flatEntity, - workspaceId, + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [flatEntity], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/row-level-permission-predicate/services/create-row-level-permission-predicate-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/row-level-permission-predicate/services/create-row-level-permission-predicate-action-handler.service.ts index 1d62f9a2fe..a6e09a94a5 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/row-level-permission-predicate/services/create-row-level-permission-predicate-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/row-level-permission-predicate/services/create-row-level-permission-predicate-action-handler.service.ts @@ -6,7 +6,6 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; -import { RowLevelPermissionPredicateEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity'; import { resolveUniversalRelationIdentifiersToIds } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util'; import { FlatCreateRowLevelPermissionPredicateAction, @@ -59,17 +58,12 @@ export class CreateRowLevelPermissionPredicateActionHandlerService extends Works async executeForMetadata( context: WorkspaceMigrationActionRunnerContext, ): Promise { - const { flatAction, queryRunner, workspaceId } = context; + const { flatAction, queryRunner } = context; const { flatEntity } = flatAction; - const repository = - queryRunner.manager.getRepository( - RowLevelPermissionPredicateEntity, - ); - - await repository.insert({ - ...flatEntity, - workspaceId, + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [flatEntity], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/skill/services/create-skill-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/skill/services/create-skill-action-handler.service.ts index 344956c3e5..656d530ecc 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/skill/services/create-skill-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/skill/services/create-skill-action-handler.service.ts @@ -4,7 +4,6 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; -import { SkillEntity } from 'src/engine/metadata-modules/skill/entities/skill.entity'; import { FlatCreateSkillAction, UniversalCreateSkillAction, @@ -42,15 +41,12 @@ export class CreateSkillActionHandlerService extends WorkspaceMigrationRunnerAct async executeForMetadata( context: WorkspaceMigrationActionRunnerContext, ): Promise { - const { flatAction, queryRunner, workspaceId } = context; + const { flatAction, queryRunner } = context; const { flatEntity } = flatAction; - const skillRepository = - queryRunner.manager.getRepository(SkillEntity); - - await skillRepository.save({ - ...flatEntity, - workspaceId, + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [flatEntity], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-field/services/create-view-field-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-field/services/create-view-field-action-handler.service.ts index 1952f4321b..8e9f13c6b3 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-field/services/create-view-field-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-field/services/create-view-field-action-handler.service.ts @@ -4,7 +4,6 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; -import { ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity'; import { resolveUniversalRelationIdentifiersToIds } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util'; import { FlatCreateViewFieldAction, @@ -54,15 +53,12 @@ export class CreateViewFieldActionHandlerService extends WorkspaceMigrationRunne async executeForMetadata( context: WorkspaceMigrationActionRunnerContext, ): Promise { - const { flatAction, queryRunner, workspaceId } = context; + const { flatAction, queryRunner } = context; const { flatEntity } = flatAction; - const viewFieldRepository = - queryRunner.manager.getRepository(ViewFieldEntity); - - await viewFieldRepository.insert({ - ...flatEntity, - workspaceId, + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [flatEntity], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-filter-group/services/create-view-filter-group-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-filter-group/services/create-view-filter-group-action-handler.service.ts index 8b1126ac2d..9ea3d9be31 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-filter-group/services/create-view-filter-group-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-filter-group/services/create-view-filter-group-action-handler.service.ts @@ -4,7 +4,6 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; -import { ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity'; import { resolveUniversalRelationIdentifiersToIds } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util'; import { type FlatCreateViewFilterGroupAction, @@ -51,17 +50,12 @@ export class CreateViewFilterGroupActionHandlerService extends WorkspaceMigratio async executeForMetadata( context: WorkspaceMigrationActionRunnerContext, ): Promise { - const { flatAction, queryRunner, workspaceId } = context; + const { flatAction, queryRunner } = context; const { flatEntity } = flatAction; - const viewFilterGroupRepository = - queryRunner.manager.getRepository( - ViewFilterGroupEntity, - ); - - await viewFilterGroupRepository.insert({ - ...flatEntity, - workspaceId, + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [flatEntity], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-filter/services/create-view-filter-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-filter/services/create-view-filter-action-handler.service.ts index 8666631e5e..4fd5f2ded0 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-filter/services/create-view-filter-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-filter/services/create-view-filter-action-handler.service.ts @@ -4,7 +4,6 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; -import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity'; import { resolveUniversalRelationIdentifiersToIds } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util'; import { FlatCreateViewFilterAction, @@ -50,15 +49,12 @@ export class CreateViewFilterActionHandlerService extends WorkspaceMigrationRunn async executeForMetadata( context: WorkspaceMigrationActionRunnerContext, ): Promise { - const { flatAction, queryRunner, workspaceId } = context; + const { flatAction, queryRunner } = context; const { flatEntity } = flatAction; - const viewFilterRepository = - queryRunner.manager.getRepository(ViewFilterEntity); - - await viewFilterRepository.insert({ - ...flatEntity, - workspaceId, + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [flatEntity], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-group/services/create-view-group-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-group/services/create-view-group-action-handler.service.ts index ea0cf64215..f48518e713 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-group/services/create-view-group-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-group/services/create-view-group-action-handler.service.ts @@ -4,7 +4,6 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; -import { ViewGroupEntity } from 'src/engine/metadata-modules/view-group/entities/view-group.entity'; import { resolveUniversalRelationIdentifiersToIds } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util'; import { FlatCreateViewGroupAction, @@ -47,15 +46,12 @@ export class CreateViewGroupActionHandlerService extends WorkspaceMigrationRunne async executeForMetadata( context: WorkspaceMigrationActionRunnerContext, ): Promise { - const { flatAction, queryRunner, workspaceId } = context; + const { flatAction, queryRunner } = context; const { flatEntity } = flatAction; - const viewGroupRepository = - queryRunner.manager.getRepository(ViewGroupEntity); - - await viewGroupRepository.insert({ - ...flatEntity, - workspaceId, + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [flatEntity], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view/services/create-view-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view/services/create-view-action-handler.service.ts index 7369d9ba8f..048b2572e6 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view/services/create-view-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view/services/create-view-action-handler.service.ts @@ -4,7 +4,6 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; -import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity'; import { resolveUniversalRelationIdentifiersToIds } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util'; import { FlatCreateViewAction, @@ -64,15 +63,12 @@ export class CreateViewActionHandlerService extends WorkspaceMigrationRunnerActi async executeForMetadata( context: WorkspaceMigrationActionRunnerContext, ): Promise { - const { flatAction, queryRunner, workspaceId } = context; + const { flatAction, queryRunner } = context; const { flatEntity } = flatAction; - const viewRepository = - queryRunner.manager.getRepository(ViewEntity); - - await viewRepository.insert({ - ...flatEntity, - workspaceId, + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [flatEntity], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/webhook/services/create-webhook-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/webhook/services/create-webhook-action-handler.service.ts index 31f47c05ea..28003716cc 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/webhook/services/create-webhook-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/webhook/services/create-webhook-action-handler.service.ts @@ -4,7 +4,6 @@ import { v4 } from 'uuid'; import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface'; -import { WebhookEntity } from 'src/engine/metadata-modules/webhook/entities/webhook.entity'; import { FlatCreateWebhookAction, UniversalCreateWebhookAction, @@ -42,15 +41,12 @@ export class CreateWebhookActionHandlerService extends WorkspaceMigrationRunnerA async executeForMetadata( context: WorkspaceMigrationActionRunnerContext, ): Promise { - const { flatAction, queryRunner, workspaceId } = context; + const { flatAction, queryRunner } = context; const { flatEntity } = flatAction; - const webhookRepository = - queryRunner.manager.getRepository(WebhookEntity); - - await webhookRepository.insert({ - ...flatEntity, - workspaceId, + await this.insertFlatEntitiesInRepository({ + queryRunner, + flatEntities: [flatEntity], }); } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface.ts index f535e4c0e4..078b44ad52 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface.ts @@ -1,8 +1,10 @@ import { Inject, SetMetadata } from '@nestjs/common'; import { AllMetadataName } from 'twenty-shared/metadata'; +import { QueryRunner } from 'typeorm'; import { LoggerService } from 'src/engine/core-modules/logger/logger.service'; +import { ALL_METADATA_ENTITY_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-entity-by-metadata-name.constant'; import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type'; import { AllFlatEntityTypesByMetadataName } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-types-by-metadata-name'; import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; @@ -33,6 +35,7 @@ import { import { deriveMetadataEventsFromCreateAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/derive-metadata-events-from-create-action.util'; import { deriveMetadataEventsFromDeleteAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/derive-metadata-events-from-delete-action.util'; import { deriveMetadataEventsFromUpdateAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/derive-metadata-events-from-update-action.util'; +import { flatEntityToScalarFlatEntity } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/flat-entity-to-scalar-flat-entity.util'; import { optimisticallyApplyCreateActionOnAllFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/optimistically-apply-create-action-on-all-flat-entity-maps.util'; import { optimisticallyApplyDeleteActionOnAllFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/optimistically-apply-delete-action-on-all-flat-entity-maps.util'; import { optimisticallyApplyUpdateActionOnAllFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/optimistically-apply-update-action-on-all-flat-entity-maps.util'; @@ -72,6 +75,26 @@ export abstract class BaseWorkspaceMigrationRunnerActionHandlerService< context: WorkspaceMigrationActionRunnerArgs, ): Promise; + protected async insertFlatEntitiesInRepository({ + flatEntities, + queryRunner, + }: { + queryRunner: QueryRunner; + flatEntities: MetadataFlatEntity[]; + }) { + const metadataEntity = + ALL_METADATA_ENTITY_BY_METADATA_NAME[this.metadataName]; + const repository = queryRunner.manager.getRepository(metadataEntity); + const scalarFlatEntities = flatEntities.map((flatEntity) => + flatEntityToScalarFlatEntity({ + flatEntity, + metadataName: this.metadataName, + }), + ); + + await repository.insert(scalarFlatEntities); + } + protected transpileUniversalDeleteActionToFlatDeleteAction( context: 'delete' extends TActionType ? WorkspaceMigrationActionRunnerArgs< diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/__tests__/flat-entity-to-scalar-flat-entity.util.spec.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/__tests__/flat-entity-to-scalar-flat-entity.util.spec.ts new file mode 100644 index 0000000000..0d543c0296 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/__tests__/flat-entity-to-scalar-flat-entity.util.spec.ts @@ -0,0 +1,80 @@ +import { FieldMetadataType } from 'twenty-shared/types'; + +import { getFlatFieldMetadataMock } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/get-flat-field-metadata.mock'; +import { flatEntityToScalarFlatEntity } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/flat-entity-to-scalar-flat-entity.util'; + +describe('flatEntityToScalarFlatEntity', () => { + it('should return only scalar and structural properties, excluding universal extras', () => { + const flatEntity = getFlatFieldMetadataMock({ + id: 'field-metadata-id', + workspaceId: 'workspace-id', + applicationId: 'application-id', + universalIdentifier: 'universal-identifier', + applicationUniversalIdentifier: 'app-universal-id', + objectMetadataId: 'object-metadata-id', + objectMetadataUniversalIdentifier: 'object-universal-id', + type: FieldMetadataType.TEXT, + defaultValue: "'default-value'", + description: 'test description', + icon: 'IconTest', + isActive: true, + isLabelSyncedWithName: false, + isUnique: false, + label: 'Test Label', + name: 'testField', + options: null, + standardOverrides: null, + settings: null, + universalSettings: null, + isCustom: true, + isSystem: false, + isUIReadOnly: false, + isNullable: true, + relationTargetFieldMetadataId: 'relation-target-field-id', + relationTargetFieldMetadataUniversalIdentifier: + 'relation-target-field-universal-id', + relationTargetObjectMetadataId: 'relation-target-object-id', + relationTargetObjectMetadataUniversalIdentifier: + 'relation-target-object-universal-id', + morphId: null, + createdAt: '2025-01-01T00:00:00.000Z', + updatedAt: '2025-01-01T00:00:00.000Z', + }); + + const result = flatEntityToScalarFlatEntity({ + metadataName: 'fieldMetadata', + flatEntity, + }); + + expect(result).toMatchInlineSnapshot(` +{ + "applicationId": "application-id", + "createdAt": "2025-01-01T00:00:00.000Z", + "defaultValue": "'default-value'", + "description": "test description", + "icon": "IconTest", + "id": "field-metadata-id", + "isActive": true, + "isCustom": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": false, + "isUIReadOnly": false, + "isUnique": false, + "label": "Test Label", + "morphId": null, + "name": "testField", + "objectMetadataId": "object-metadata-id", + "options": null, + "relationTargetFieldMetadataId": "relation-target-field-id", + "relationTargetObjectMetadataId": "relation-target-object-id", + "settings": null, + "standardOverrides": null, + "type": "TEXT", + "universalIdentifier": "universal-identifier", + "updatedAt": "2025-01-01T00:00:00.000Z", + "workspaceId": "workspace-id", +} +`); + }); +}); diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/flat-entity-to-scalar-flat-entity.util.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/flat-entity-to-scalar-flat-entity.util.ts new file mode 100644 index 0000000000..a5429f779c --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-runner/utils/flat-entity-to-scalar-flat-entity.util.ts @@ -0,0 +1,30 @@ +import { type AllMetadataName } from 'twenty-shared/metadata'; + +import { ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant'; +import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type'; +import { type MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type'; +import { type ScalarFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/scalar-flat-entity.type'; + +export const flatEntityToScalarFlatEntity = ({ + metadataName, + flatEntity, +}: { + metadataName: T; + flatEntity: MetadataFlatEntity; +}): ScalarFlatEntity> => { + const config = + ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME[metadataName]; + const result: Record = {}; + const flatEntityRecord = flatEntity as Record; + + for (const key of Object.keys(config)) { + result[key] = flatEntityRecord[key]; + } + + result.id = flatEntityRecord.id; + result.workspaceId = flatEntityRecord.workspaceId; + result.applicationId = flatEntityRecord.applicationId; + result.universalIdentifier = flatEntityRecord.universalIdentifier; + + return result as ScalarFlatEntity>; +};