From 28e5186f23e3bedec92de06943561325d1248c85 Mon Sep 17 00:00:00 2001 From: Paul Rastoin <45004772+prastoin@users.noreply.github.com> Date: Fri, 8 Aug 2025 15:11:47 +0200 Subject: [PATCH] `FieldMetadataServiceV2.deleteOneField` (#13744) # Introduction - Migrated the `deleteOneField` handler to new workspace migration v2 style - Refactored the build to expect `flatObjectMetadataMaps` that he instantly translate at the be beginning Commented `ObjectMetadataServiceV2` as it's still not implemented neither used --- .../field-metadata/field-metadata.resolver.ts | 28 +- .../services/field-metadata.service-v2.ts | 149 +- .../services/field-metadata.service.ts | 16 +- .../flat-field-metadata-validator.service.ts | 60 + ...to-flat-field-metadatas-to-create.util.ts} | 9 +- ...-to-flat-field-metadatas-to-delete.util.ts | 86 ++ ...field-input-to-flat-field-metadata.util.ts | 8 +- ...d-metadata-from-create-field-input.util.ts | 6 +- ...mpty-flat-object-metadata-maps.constant.ts | 6 + ...flat-object-metadata-maps-or-throw.util.ts | 8 +- ...t-metadata-maps-with-field-id-only.util.ts | 32 + ...ject-metadata-with-flat-field-maps.util.ts | 1 - ...datas-to-flat-object-metadata-maps.util.ts | 9 +- .../object-metadata-v2.service.ts | 75 +- .../workspace-migration-builder.spec.ts.snap | 1221 +++++++++++++++-- ...space-migration-builder-field-test-case.ts | 276 ++-- ...space-migration-builder-index-test-case.ts | 131 +- ...pace-migration-builder-object-test-case.ts | 136 +- ...kspace-migration-builder-test-case.type.ts | 10 +- .../workspace-migration-builder.spec.ts | 14 +- .../workspace-migration-builder-v2.service.ts | 28 +- .../workspace-migration-runner-v2.service.ts | 11 +- 22 files changed, 1751 insertions(+), 569 deletions(-) rename packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/{from-create-field-input-to-flat-field-and-its-flat-object-metadata.util.ts => from-create-field-input-to-flat-field-metadatas-to-create.util.ts} (95%) create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-delete-field-input-to-flat-field-metadatas-to-delete.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/constant/empty-flat-object-metadata-maps.constant.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-with-field-id-only.util.ts diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.resolver.ts index e15b4c4d17..ad5b3a3dfb 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.resolver.ts @@ -11,6 +11,8 @@ import { import { type FieldMetadataType } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; +import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; +import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service'; import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { @@ -28,8 +30,8 @@ import { DeleteOneFieldInput } from 'src/engine/metadata-modules/field-metadata/ import { FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto'; import { RelationDTO } from 'src/engine/metadata-modules/field-metadata/dtos/relation.dto'; import { - type UpdateFieldInput, UpdateOneFieldMetadataInput, + type UpdateFieldInput, } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input'; import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { @@ -38,6 +40,7 @@ import { } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception'; import { BeforeUpdateOneField } from 'src/engine/metadata-modules/field-metadata/hooks/before-update-one-field.hook'; import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service'; +import { FieldMetadataServiceV2 } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service-v2'; import { fieldMetadataGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/field-metadata/utils/field-metadata-graphql-api-exception-handler.util'; import { fromFieldMetadataEntityToFieldMetadataDto } from 'src/engine/metadata-modules/field-metadata/utils/from-field-metadata-entity-to-field-metadata-dto.util'; import { fromObjectMetadataEntityToObjectMetadataDto } from 'src/engine/metadata-modules/field-metadata/utils/from-object-metadata-entity-to-object-metadata-dto.util'; @@ -57,6 +60,8 @@ export class FieldMetadataResolver { constructor( private readonly fieldMetadataService: FieldMetadataService, private readonly beforeUpdateOneField: BeforeUpdateOneField, + private readonly featureFlagService: FeatureFlagService, + private readonly fieldMetadataServiceV2: FieldMetadataServiceV2, ) {} @UseGuards(SettingsPermissionsGuard(PermissionFlagType.DATA_MODEL)) @@ -103,10 +108,27 @@ export class FieldMetadataResolver { @Args('input') input: DeleteOneFieldInput, @AuthWorkspace() { id: workspaceId }: Workspace, ) { - if (!workspaceId) { + if (!isDefined(workspaceId)) { throw new ForbiddenError('Could not retrieve workspace ID'); } + try { + const isWorkspaceMigrationV2Enabled = + await this.featureFlagService.isFeatureEnabled( + FeatureFlagKey.IS_WORKSPACE_MIGRATION_V2_ENABLED, + workspaceId, + ); + + if (isWorkspaceMigrationV2Enabled) { + return await this.fieldMetadataServiceV2.deleteOneField({ + deleteOneFieldInput: input, + workspaceId, + }); + } + } catch (error) { + fieldMetadataGraphqlApiExceptionHandler(error); + } + const fieldMetadata = await this.fieldMetadataService.findOneWithinWorkspace(workspaceId, { where: { @@ -114,7 +136,7 @@ export class FieldMetadataResolver { }, }); - if (!fieldMetadata) { + if (!isDefined(fieldMetadata)) { throw new ValidationError('Field does not exist'); } diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service-v2.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service-v2.ts index 35126d29b5..9a00a41a02 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service-v2.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service-v2.ts @@ -1,13 +1,16 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm'; import { FieldMetadataType } from 'twenty-shared/types'; -import { isDefined } from 'twenty-shared/utils'; -import { Repository } from 'typeorm'; +import { + isDefined, + trimAndRemoveDuplicatedWhitespacesFromString, +} from 'twenty-shared/utils'; +import { In, Repository } from 'typeorm'; import { MultipleMetadataValidationErrors } from 'src/engine/core-modules/error/multiple-metadata-validation-errors'; import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input'; +import { type DeleteOneFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/delete-field.input'; import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { FieldMetadataException, @@ -16,20 +19,21 @@ import { import { FlatFieldMetadataValidatorService } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-validator.service'; import { type FailedFlatFieldMetadataValidationExceptions } from 'src/engine/metadata-modules/flat-field-metadata/types/failed-flat-field-metadata-validation.type'; import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; -import { fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-and-its-flat-object-metadata.util'; +import { fromCreateFieldInputToFlatFieldMetadatasToCreate } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-metadatas-to-create.util'; +import { fromDeleteFieldInputToFlatFieldMetadatasToDelete } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-delete-field-input-to-flat-field-metadatas-to-delete.util'; import { isFlatFieldMetadataEntityOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util'; import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type'; import { addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util'; import { addFlatFieldMetadataInFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-field-metadata-in-flat-object-metadata-maps.util'; +import { deleteFieldFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-field-from-flat-object-metadata-maps-or-throw.util'; import { extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.util'; import { extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps.util'; -import { fromFlatObjectMetadataMapsToFlatObjectMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-maps-to-flat-object-metadatas.util'; import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service'; import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service'; import { WorkspaceMigrationRunnerV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/workspace-migration-runner-v2.service'; @Injectable() -export class FieldMetadataServiceV2 extends TypeOrmQueryService { +export class FieldMetadataServiceV2 { constructor( @InjectRepository(FieldMetadataEntity, 'core') private readonly fieldMetadataRepository: Repository, @@ -37,14 +41,19 @@ export class FieldMetadataServiceV2 extends TypeOrmQueryService { - const [createdFieldMetadata] = await this.createMany([fieldMetadataInput]); + async createOne({ + fieldMetadataInput, + workspaceId, + }: { + fieldMetadataInput: Omit; + workspaceId: string; + }): Promise { + const [createdFieldMetadata] = await this.createMany({ + fieldMetadataInputs: [fieldMetadataInput], + workspaceId, + }); if (!isDefined(createdFieldMetadata)) { throw new FieldMetadataException( @@ -56,6 +65,76 @@ export class FieldMetadataServiceV2 extends TypeOrmQueryService { + const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } = + await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps( + { + workspaceId, + }, + ); + + const flatFieldMetadatasToDelete = + fromDeleteFieldInputToFlatFieldMetadatasToDelete({ + deleteOneFieldInput, + existingFlatObjectMetadataMaps, + }); + + const validationErrors = flatFieldMetadatasToDelete.flatMap( + (flatFieldMetadataToDelete) => + this.flatFieldMetadataValidatorService.validateFlatFieldMetadataDeletion( + { + existingFlatObjectMetadataMaps, + flatFieldMetadataToDelete, + }, + ), + ); + + if (validationErrors.length > 0) { + throw new MultipleMetadataValidationErrors( + validationErrors, + validationErrors.length > 1 + ? 'Multiple validation errors occurred while deleting field' + : 'A validation error occurred while deleting field', + ); + } + + const flatObjectMetadataMapsWithImpactedObject = + extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow({ + flatObjectMetadataMaps: existingFlatObjectMetadataMaps, + objectMetadataIds: flatFieldMetadatasToDelete.map( + (flatFieldMetadataToDelete) => + flatFieldMetadataToDelete.objectMetadataId, + ), + }); + + const toFlatObjectMetadataMaps = flatFieldMetadatasToDelete.reduce( + (flatObjectMetadataMaps, flatFieldMetadataToDelete) => + deleteFieldFromFlatObjectMetadataMapsOrThrow({ + fieldMetadataId: flatFieldMetadataToDelete.id, + flatObjectMetadataMaps, + objectMetadataId: flatFieldMetadataToDelete.objectMetadataId, + }), + flatObjectMetadataMapsWithImpactedObject, + ); + + const workspaceMigration = this.workspaceMigrationBuilderV2.build({ + fromFlatObjectMetadataMaps: flatObjectMetadataMapsWithImpactedObject, + toFlatObjectMetadataMaps, + inferDeletionFromMissingObjectFieldIndex: true, + workspaceId, + }); + + await this.workspaceMigrationRunnerV2Service.run(workspaceMigration); + + return; + } + private computeOtherFlatObjectMetadataMapsToValidate({ flatFieldMetadataToCreate, flatFieldMetadatasToCreate, @@ -107,15 +186,17 @@ export class FieldMetadataServiceV2 extends TypeOrmQueryService { + async createMany({ + fieldMetadataInputs, + workspaceId, + }: { + fieldMetadataInputs: Omit[]; + workspaceId: string; + }): Promise { if (!fieldMetadataInputs.length) { return []; } - const workspaceId = fieldMetadataInputs[0].workspaceId; - const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } = await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps( { workspaceId }, @@ -125,8 +206,9 @@ export class FieldMetadataServiceV2 extends TypeOrmQueryService - await fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata({ + await fromCreateFieldInputToFlatFieldMetadatasToCreate({ existingFlatObjectMetadataMaps, + workspaceId, rawCreateFieldInput: fieldMetadataInput, }), ), @@ -192,33 +274,38 @@ export class FieldMetadataServiceV2 extends TypeOrmQueryService + trimAndRemoveDuplicatedWhitespacesFromString( + flatFieldMetadata.name, + ), + ), + ), + workspaceId, + }, + }); } catch { // TODO prastoin We should pass the internal error here throw new FieldMetadataException( diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service.ts index 9bd425b7bc..ba0c9207ed 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service.ts @@ -7,10 +7,10 @@ import { FieldMetadataType } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { DataSource, - type FindOneOptions, In, - type QueryRunner, Repository, + type FindOneOptions, + type QueryRunner, } from 'typeorm'; import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface'; @@ -58,9 +58,9 @@ import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/wor import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util'; import { WorkspaceMigrationColumnActionType, + WorkspaceMigrationTableActionType, type WorkspaceMigrationColumnDrop, type WorkspaceMigrationTableAction, - WorkspaceMigrationTableActionType, } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity'; import { WorkspaceMigrationFactory } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.factory'; import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service'; @@ -111,7 +111,10 @@ export class FieldMetadataService extends TypeOrmQueryService({ diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-and-its-flat-object-metadata.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-metadatas-to-create.util.ts similarity index 95% rename from packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-and-its-flat-object-metadata.util.ts rename to packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-metadatas-to-create.util.ts index 43a95228be..ad043a5849 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-and-its-flat-object-metadata.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-metadatas-to-create.util.ts @@ -21,12 +21,14 @@ import { getDefaultFlatFieldMetadata } from 'src/engine/metadata-modules/flat-fi import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type'; type FromCreateFieldInputToFlatObjectMetadata = { - rawCreateFieldInput: CreateFieldInput; + rawCreateFieldInput: Omit; existingFlatObjectMetadataMaps: FlatObjectMetadataMaps; + workspaceId: string; }; -export const fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata = async ({ +export const fromCreateFieldInputToFlatFieldMetadatasToCreate = async ({ rawCreateFieldInput, + workspaceId, existingFlatObjectMetadataMaps, }: FromCreateFieldInputToFlatObjectMetadata): Promise => { if (rawCreateFieldInput.isRemoteCreation) { @@ -57,11 +59,13 @@ export const fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata = async ({ const fieldMetadataId = v4(); const commonFlatFieldMetadata = getDefaultFlatFieldMetadata({ createFieldInput, + workspaceId, fieldMetadataId, }); switch (createFieldInput.type) { case FieldMetadataType.MORPH_RELATION: { + // TODO prastoin throw new UserInputError( 'Morph relation feature is not migrated to workspace migration v2 yet', ); @@ -71,6 +75,7 @@ export const fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata = async ({ existingFlatObjectMetadataMaps, sourceParentFlatObjectMetadata: parentFlatObjectMetadata, createFieldInput, + workspaceId, }); } case FieldMetadataType.RATING: { diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-delete-field-input-to-flat-field-metadatas-to-delete.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-delete-field-input-to-flat-field-metadatas-to-delete.util.ts new file mode 100644 index 0000000000..98657c372f --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-delete-field-input-to-flat-field-metadatas-to-delete.util.ts @@ -0,0 +1,86 @@ +import { FieldMetadataType } from 'twenty-shared/types'; +import { + isDefined, + trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties, +} from 'twenty-shared/utils'; + +import { type DeleteOneFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/delete-field.input'; +import { + FieldMetadataException, + FieldMetadataExceptionCode, +} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception'; +import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; +import { isFlatFieldMetadataEntityOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util'; +import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type'; +import { findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-with-field-id-only.util'; + +type FromDeleteFieldInputToFlatFieldMetadatasToDeleteArgs = { + existingFlatObjectMetadataMaps: FlatObjectMetadataMaps; + deleteOneFieldInput: DeleteOneFieldInput; +}; +export const fromDeleteFieldInputToFlatFieldMetadatasToDelete = ({ + existingFlatObjectMetadataMaps, + deleteOneFieldInput: rawDeleteOneInput, +}: FromDeleteFieldInputToFlatFieldMetadatasToDeleteArgs): FlatFieldMetadata[] => { + const { id: fieldMetadataToDeleteId } = + trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties( + rawDeleteOneInput, + ['id'], + ); + + const flatFieldMetadataToDelete = + findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId({ + fieldMetadataId: fieldMetadataToDeleteId, + flatObjectMetadataMaps: existingFlatObjectMetadataMaps, + }); + + if (!isDefined(flatFieldMetadataToDelete)) { + throw new FieldMetadataException( + 'Field to delete not found', + FieldMetadataExceptionCode.FIELD_METADATA_NOT_FOUND, + ); + } + + if ( + isFlatFieldMetadataEntityOfType( + flatFieldMetadataToDelete, + FieldMetadataType.RELATION, + ) + ) { + const { relationTargetFieldMetadataId, relationTargetObjectMetadataId } = + flatFieldMetadataToDelete; + + const relatedFlatObjectMetadata = + existingFlatObjectMetadataMaps.byId[relationTargetObjectMetadataId]; + + if (!isDefined(relatedFlatObjectMetadata)) { + throw new FieldMetadataException( + `Deleted field metadata relation object metadata target not found`, + FieldMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND, + ); + } + const relatedFlatFieldMetadata = + relatedFlatObjectMetadata.fieldsById[relationTargetFieldMetadataId]; + + if (!isDefined(relatedFlatFieldMetadata)) { + throw new FieldMetadataException( + `Deleted field metadata relation field metadata target not found`, + FieldMetadataExceptionCode.FIELD_METADATA_NOT_FOUND, + ); + } + + return [flatFieldMetadataToDelete, relatedFlatFieldMetadata]; + } + + if ( + isFlatFieldMetadataEntityOfType( + flatFieldMetadataToDelete, + FieldMetadataType.MORPH_RELATION, + ) + ) { + // TODO prastoin + return [flatFieldMetadataToDelete]; + } + + return [flatFieldMetadataToDelete]; +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-relation-create-field-input-to-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-relation-create-field-input-to-flat-field-metadata.util.ts index 53279dd270..1d4638d6f0 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-relation-create-field-input-to-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-relation-create-field-input-to-flat-field-metadata.util.ts @@ -39,14 +39,16 @@ const computeFieldMetadataRelationSettingsForRelationType = ({ }; type FromRelationCreateFieldInputToFlatFieldMetadataArgs = { - createFieldInput: CreateFieldInput; + createFieldInput: Omit; existingFlatObjectMetadataMaps: FlatObjectMetadataMaps; sourceParentFlatObjectMetadata: FlatObjectMetadata; + workspaceId: string; }; export const fromRelationCreateFieldInputToFlatFieldMetadata = async ({ existingFlatObjectMetadataMaps, sourceParentFlatObjectMetadata, createFieldInput, + workspaceId, }: FromRelationCreateFieldInputToFlatFieldMetadataArgs): Promise< FlatFieldMetadata[] > => { @@ -85,6 +87,7 @@ export const fromRelationCreateFieldInputToFlatFieldMetadata = async ({ > = { ...getDefaultFlatFieldMetadata({ createFieldInput, + workspaceId, fieldMetadataId: sourceRelationTargetFieldMetadataId, }), icon: createFieldInput.icon ?? 'IconRelationOneToMany', @@ -105,7 +108,7 @@ export const fromRelationCreateFieldInputToFlatFieldMetadata = async ({ )}Id`, objectMetadataId: targetParentFlatObjectMetadata.id, type: FieldMetadataType.RELATION, - workspaceId: createFieldInput.workspaceId, + workspaceId, }; const targetFlatFieldMetadataSettings = computeFieldMetadataRelationSettingsForRelationType({ @@ -119,6 +122,7 @@ export const fromRelationCreateFieldInputToFlatFieldMetadata = async ({ { ...getDefaultFlatFieldMetadata({ createFieldInput: targetCreateFieldInput, + workspaceId, fieldMetadataId: targetRelationTargetFieldMetadataId, }), type: FieldMetadataType.RELATION, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/get-default-flat-field-metadata-from-create-field-input.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/get-default-flat-field-metadata-from-create-field-input.util.ts index 6a3359e793..4e7a1dd10d 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/get-default-flat-field-metadata-from-create-field-input.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/get-default-flat-field-metadata-from-create-field-input.util.ts @@ -6,11 +6,13 @@ import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-m type GetDefaultFlatFieldMetadataArgs = { fieldMetadataId: string; - createFieldInput: CreateFieldInput; + createFieldInput: Omit; + workspaceId: string; }; export const getDefaultFlatFieldMetadata = ({ createFieldInput, fieldMetadataId, + workspaceId, }: GetDefaultFlatFieldMetadataArgs) => { const { defaultValue, settings } = extractAndSanitizeObjectStringFields( createFieldInput, @@ -40,7 +42,7 @@ export const getDefaultFlatFieldMetadata = ({ standardOverrides: null, type: createFieldInput.type, uniqueIdentifier: fieldMetadataId, - workspaceId: createFieldInput.workspaceId, + workspaceId, flatRelationTargetFieldMetadata: null, flatRelationTargetObjectMetadata: null, options: null, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/constant/empty-flat-object-metadata-maps.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/constant/empty-flat-object-metadata-maps.constant.ts new file mode 100644 index 0000000000..57f24acb01 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/constant/empty-flat-object-metadata-maps.constant.ts @@ -0,0 +1,6 @@ +import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type'; + +export const EMPTY_FLAT_OBJECT_METADATA_MAPS = { + byId: {}, + idByNameSingular: {}, +} as const satisfies FlatObjectMetadataMaps; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.util.ts index 17366bba57..a2cc116ed4 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.util.ts @@ -1,5 +1,6 @@ import { isDefined } from 'twenty-shared/utils'; +import { EMPTY_FLAT_OBJECT_METADATA_MAPS } from 'src/engine/metadata-modules/flat-object-metadata-maps/constant/empty-flat-object-metadata-maps.constant'; import { FlatObjectMetadataMapsException, FlatObjectMetadataMapsExceptionCode, @@ -17,11 +18,6 @@ export const extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow = flatObjectMetadataMaps: sourceFlatObjectMetadataMaps, objectMetadataIds, }: ExtractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrowArgs): FlatObjectMetadataMaps => { - const emptyFlatObjectMetadataMaps: FlatObjectMetadataMaps = { - byId: {}, - idByNameSingular: {}, - }; - return objectMetadataIds.reduce( (flatObjectMetadataMaps, objectMetadataId) => { const flatObjectMetadataWithFlatFieldMaps = @@ -41,6 +37,6 @@ export const extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow = }, ); }, - emptyFlatObjectMetadataMaps, + EMPTY_FLAT_OBJECT_METADATA_MAPS, ); }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-with-field-id-only.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-with-field-id-only.util.ts new file mode 100644 index 0000000000..fad5371fc1 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-with-field-id-only.util.ts @@ -0,0 +1,32 @@ +import { isDefined } from 'twenty-shared/utils'; + +import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; +import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type'; + +export type FindFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldIdArgs = { + fieldMetadataId: string; + flatObjectMetadataMaps: FlatObjectMetadataMaps; +}; +// TODO add tests +export const findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId = ({ + flatObjectMetadataMaps, + fieldMetadataId, +}: FindFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldIdArgs): + | FlatFieldMetadata + | undefined => { + const flatObjectMetadataWithFlatFieldMapsArray = Object.values( + flatObjectMetadataMaps.byId, + ).filter(isDefined); + + const matchingFlatObjectMetadataWithFlatFieldMaps = + flatObjectMetadataWithFlatFieldMapsArray.find( + (flatObjectMetadataWithFlatFieldMaps) => + isDefined( + flatObjectMetadataWithFlatFieldMaps.fieldsById[fieldMetadataId], + ), + ); + + return matchingFlatObjectMetadataWithFlatFieldMaps?.fieldsById[ + fieldMetadataId + ]; +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-to-flat-object-metadata-with-flat-field-maps.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-to-flat-object-metadata-with-flat-field-maps.util.ts index d62a9ce5d5..c4159f4130 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-to-flat-object-metadata-with-flat-field-maps.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-to-flat-object-metadata-with-flat-field-maps.util.ts @@ -8,7 +8,6 @@ export const fromFlatObjectMetadataToFlatObjectMetadataWithFlatFieldMaps = ( const emptyFlatObjectMetadataWithFlatFieldMaps: FlatObjectMetadataWithFlatFieldMaps = { ...flatObjectMetadata, - flatIndexMetadatas: [], flatFieldMetadatas: [], fieldIdByJoinColumnName: {}, fieldIdByName: {}, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadatas-to-flat-object-metadata-maps.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadatas-to-flat-object-metadata-maps.util.ts index b3fa9be03b..6efac57740 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadatas-to-flat-object-metadata-maps.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadatas-to-flat-object-metadata-maps.util.ts @@ -1,21 +1,16 @@ -import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type'; +import { EMPTY_FLAT_OBJECT_METADATA_MAPS } from 'src/engine/metadata-modules/flat-object-metadata-maps/constant/empty-flat-object-metadata-maps.constant'; import { addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-object-metadata-to-flat-object-metadata-maps-or-throw.util'; import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; export const fromFlatObjectMetadatasToFlatObjectMetadataMaps = ( flatObjectMetadatas: FlatObjectMetadata[], ) => { - const emptyFlatObjectMetadataMaps: FlatObjectMetadataMaps = { - byId: {}, - idByNameSingular: {}, - }; - return flatObjectMetadatas.reduce( (flatObjectMetadataMaps, flatObjectMetadata) => addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow({ flatObjectMetadata, flatObjectMetadataMaps, }), - emptyFlatObjectMetadataMaps, + EMPTY_FLAT_OBJECT_METADATA_MAPS, ); }; diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata-v2.service.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata-v2.service.ts index 708b43696e..9300833001 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata-v2.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata-v2.service.ts @@ -4,17 +4,12 @@ import { InjectRepository } from '@nestjs/typeorm'; import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm'; import { Repository } from 'typeorm'; -import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type'; -import { fromCreateObjectInputToFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-create-object-input-to-flat-object-metadata.util'; -import { fromFlatObjectMetadataMapsToFlatObjectMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-maps-to-flat-object-metadatas.util'; import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service'; import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service'; import { WorkspaceMigrationRunnerV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/workspace-migration-runner-v2.service'; import { ObjectMetadataEntity } from './object-metadata.entity'; -import { type CreateObjectInput } from './dtos/create-object.input'; - @Injectable() export class ObjectMetadataServiceV2 extends TypeOrmQueryService { constructor( @@ -27,43 +22,43 @@ export class ObjectMetadataServiceV2 extends TypeOrmQueryService { - const { flatObjectMetadataMaps } = - await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps( - { - workspaceId: objectMetadataInput.workspaceId, - }, - ); + // override async createOne( + // objectMetadataInput: CreateObjectInput, + // ): Promise { + // const { flatObjectMetadataMaps } = + // await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps( + // { + // workspaceId: objectMetadataInput.workspaceId, + // }, + // ); - const createdRawFlatObjectMetadata = - fromCreateObjectInputToFlatObjectMetadata(objectMetadataInput); - const existingFlatObjectMetadatas: FlatObjectMetadataMaps = { - byId: {}, - idByNameSingular: {}, - }; - // @ts-expect-error TODO implement validateFlatObjectMetadata - const createdFlatObjectMetadata = validateFlatObjectMetadata({ - existing: - // Here we assume that EVERYTHING is in cache and up to date, this is very critical, also race condition prone :thinking: - fromFlatObjectMetadataMapsToFlatObjectMetadatas(flatObjectMetadataMaps), - toValidate: [createdRawFlatObjectMetadata], - }); + // const createdRawFlatObjectMetadata = + // fromCreateObjectInputToFlatObjectMetadata(objectMetadataInput); + // const existingFlatObjectMetadatas: FlatObjectMetadataMaps = { + // byId: {}, + // idByNameSingular: {}, + // }; + // // @ts-expect-error TODO implement validateFlatObjectMetadata + // const createdFlatObjectMetadata = validateFlatObjectMetadata({ + // existing: + // // Here we assume that EVERYTHING is in cache and up to date, this is very critical, also race condition prone :thinking: + // fromFlatObjectMetadataMapsToFlatObjectMetadatas(flatObjectMetadataMaps), + // toValidate: [createdRawFlatObjectMetadata], + // }); - const workspaceMigration = this.workspaceMigrationBuilderV2.build({ - objectMetadataFromToInputs: { - from: fromFlatObjectMetadataMapsToFlatObjectMetadatas( - existingFlatObjectMetadatas, - ), - to: [createdFlatObjectMetadata], - }, - inferDeletionFromMissingObjectFieldIndex: false, - workspaceId: objectMetadataInput.workspaceId, - }); + // const workspaceMigration = this.workspaceMigrationBuilderV2.build({ + // objectMetadataFromToInputs: { + // from: fromFlatObjectMetadataMapsToFlatObjectMetadatas( + // existingFlatObjectMetadatas, + // ), + // to: [createdFlatObjectMetadata], + // }, + // inferDeletionFromMissingObjectFieldIndex: false, + // workspaceId: objectMetadataInput.workspaceId, + // }); - await this.workspaceMigrationRunnerV2Service.run(workspaceMigration); + // await this.workspaceMigrationRunnerV2Service.run(workspaceMigration); - return createdFlatObjectMetadata; // TODO retrieve from cache - } + // return createdFlatObjectMetadata; // TODO retrieve from cache + // } } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/__snapshots__/workspace-migration-builder.spec.ts.snap b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/__snapshots__/workspace-migration-builder.spec.ts.snap index 012961b22e..188c64a1bf 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/__snapshots__/workspace-migration-builder.spec.ts.snap +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/__snapshots__/workspace-migration-builder.spec.ts.snap @@ -138,12 +138,12 @@ exports[`Workspace migration builder field actions test suite It should build an "type": "update_field", "updates": [ { - "from": "default flat field metadata description", + "from": "Opportunity stage", "property": "description", "to": "new description", }, { - "from": "icon", + "from": "IconProgressCheck", "property": "icon", "to": "new icon", }, @@ -153,7 +153,7 @@ exports[`Workspace migration builder field actions test suite It should build an "to": false, }, { - "from": "flatFieldMetadataName", + "from": "stage", "property": "name", "to": "new name", }, @@ -171,17 +171,12 @@ exports[`Workspace migration builder field actions test suite It should build an "type": "update_field", "updates": [ { - "from": "default flat field metadata description", + "from": null, "property": "description", "to": "new description", }, { - "from": true, - "property": "isActive", - "to": false, - }, - { - "from": "flat field metadata label", + "from": "Species", "property": "label", "to": "new label", }, @@ -255,29 +250,555 @@ exports[`Workspace migration builder index actions test suite It should not infe exports[`Workspace migration builder object actions test suite It should build a create_object action 1`] = ` [ { - "createFieldActions": [], + "createFieldActions": [ + { + "flatFieldMetadata": { + "defaultValue": "uuid", + "description": "Id", + "flatRelationTargetFieldMetadata": null, + "flatRelationTargetObjectMetadata": null, + "icon": "Icon123", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": true, + "isUnique": false, + "label": "Id", + "name": "id", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": null, + "relationTargetObjectMetadataId": null, + "settings": null, + "standardId": Any, + "standardOverrides": null, + "type": "UUID", + "uniqueIdentifier": "20202020-eda0-4cee-9577-3eb357e3c22b", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": "'Untitled'", + "description": "Name", + "flatRelationTargetFieldMetadata": null, + "flatRelationTargetObjectMetadata": null, + "icon": "IconAbc", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": false, + "isUnique": false, + "label": "Name", + "name": "name", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": null, + "relationTargetObjectMetadataId": null, + "settings": null, + "standardId": Any, + "standardOverrides": null, + "type": "TEXT", + "uniqueIdentifier": "20202020-ba07-4ffd-ba63-009491f5749c", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": "now", + "description": "Creation date", + "flatRelationTargetFieldMetadata": null, + "flatRelationTargetObjectMetadata": null, + "icon": "IconCalendar", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": false, + "isUnique": false, + "label": "Creation date", + "name": "createdAt", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": null, + "relationTargetObjectMetadataId": null, + "settings": null, + "standardId": Any, + "standardOverrides": null, + "type": "DATE_TIME", + "uniqueIdentifier": "20202020-66ac-4502-9975-e4d959c50311", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": "now", + "description": "Last time the record was changed", + "flatRelationTargetFieldMetadata": null, + "flatRelationTargetObjectMetadata": null, + "icon": "IconCalendarClock", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": false, + "isUnique": false, + "label": "Last update", + "name": "updatedAt", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": null, + "relationTargetObjectMetadataId": null, + "settings": null, + "standardId": Any, + "standardOverrides": null, + "type": "DATE_TIME", + "uniqueIdentifier": "20202020-d767-4622-bdcf-d8a084834d86", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": null, + "description": "Deletion date", + "flatRelationTargetFieldMetadata": null, + "flatRelationTargetObjectMetadata": null, + "icon": "IconCalendarClock", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": false, + "isUnique": false, + "label": "Deleted at", + "name": "deletedAt", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": null, + "relationTargetObjectMetadataId": null, + "settings": null, + "standardId": Any, + "standardOverrides": null, + "type": "DATE_TIME", + "uniqueIdentifier": "20202020-b9a7-48d8-8387-b9a3090a50ec", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": { + "name": "''", + "source": "'MANUAL'", + }, + "description": "The creator of the record", + "flatRelationTargetFieldMetadata": null, + "flatRelationTargetObjectMetadata": null, + "icon": "IconCreativeCommonsSa", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": false, + "isUnique": false, + "label": "Created by", + "name": "createdBy", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": null, + "relationTargetObjectMetadataId": null, + "settings": null, + "standardId": Any, + "standardOverrides": null, + "type": "ACTOR", + "uniqueIdentifier": "20202020-be0e-4971-865b-32ca87cbb315", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": 0, + "description": "Position", + "flatRelationTargetFieldMetadata": null, + "flatRelationTargetObjectMetadata": null, + "icon": "IconHierarchy2", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": true, + "isUnique": false, + "label": "Position", + "name": "position", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": null, + "relationTargetObjectMetadataId": null, + "settings": null, + "standardId": Any, + "standardOverrides": null, + "type": "POSITION", + "uniqueIdentifier": "20202020-c2bd-4e16-bb9a-c8b0411bf49d", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": null, + "description": "TimelineActivities tied to the Rocket", + "flatRelationTargetFieldMetadata": { + "defaultValue": null, + "description": "TimelineActivities Rocket", + "icon": "IconBuildingSkyscraper", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isUnique": false, + "label": "Rocket", + "name": "rocket", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "joinColumnName": "rocketId", + "onDelete": "CASCADE", + "relationType": "MANY_TO_ONE", + }, + "standardId": Any, + "standardOverrides": null, + "type": "RELATION", + "uniqueIdentifier": "20202020-0be6-444e-88f0-12e0c64ac27d", + "workspaceId": Any, + }, + "flatRelationTargetObjectMetadata": null, + "icon": "IconTimelineEvent", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isUnique": false, + "label": "TimelineActivities", + "name": "timelineActivities", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "relationType": "ONE_TO_MANY", + }, + "standardId": Any, + "standardOverrides": null, + "type": "RELATION", + "uniqueIdentifier": "20202020-f1ef-4ba4-8f33-1a4577afa477", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": null, + "description": "Favorites tied to the Rocket", + "flatRelationTargetFieldMetadata": { + "defaultValue": null, + "description": "Favorites Rocket", + "icon": "IconBuildingSkyscraper", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isUnique": false, + "label": "Rocket", + "name": "rocket", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "joinColumnName": "rocketId", + "onDelete": "CASCADE", + "relationType": "MANY_TO_ONE", + }, + "standardId": Any, + "standardOverrides": null, + "type": "RELATION", + "uniqueIdentifier": "20202020-78f7-4d7d-86e9-7562ced0b381", + "workspaceId": Any, + }, + "flatRelationTargetObjectMetadata": null, + "icon": "IconHeart", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isUnique": false, + "label": "Favorites", + "name": "favorites", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "relationType": "ONE_TO_MANY", + }, + "standardId": Any, + "standardOverrides": null, + "type": "RELATION", + "uniqueIdentifier": "20202020-a4a7-4686-b296-1c6c3482ee21", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": null, + "description": "Attachments tied to the Rocket", + "flatRelationTargetFieldMetadata": { + "defaultValue": null, + "description": "Attachments Rocket", + "icon": "IconBuildingSkyscraper", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isUnique": false, + "label": "Rocket", + "name": "rocket", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "joinColumnName": "rocketId", + "onDelete": "CASCADE", + "relationType": "MANY_TO_ONE", + }, + "standardId": Any, + "standardOverrides": null, + "type": "RELATION", + "uniqueIdentifier": "20202020-ca2d-47c0-8253-9d0b662fb01a", + "workspaceId": Any, + }, + "flatRelationTargetObjectMetadata": null, + "icon": "IconFileImport", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isUnique": false, + "label": "Attachments", + "name": "attachments", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "relationType": "ONE_TO_MANY", + }, + "standardId": Any, + "standardOverrides": null, + "type": "RELATION", + "uniqueIdentifier": "20202020-8d59-46ca-b7b2-73d167712134", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": null, + "description": "NoteTargets tied to the Rocket", + "flatRelationTargetFieldMetadata": { + "defaultValue": null, + "description": "NoteTargets Rocket", + "icon": "IconBuildingSkyscraper", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isUnique": false, + "label": "Rocket", + "name": "rocket", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "joinColumnName": "rocketId", + "onDelete": "CASCADE", + "relationType": "MANY_TO_ONE", + }, + "standardId": Any, + "standardOverrides": null, + "type": "RELATION", + "uniqueIdentifier": "20202020-52b6-4d5e-8f21-ed8b7c09c4a9", + "workspaceId": Any, + }, + "flatRelationTargetObjectMetadata": null, + "icon": "IconCheckbox", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isUnique": false, + "label": "NoteTargets", + "name": "noteTargets", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "relationType": "ONE_TO_MANY", + }, + "standardId": Any, + "standardOverrides": null, + "type": "RELATION", + "uniqueIdentifier": "20202020-01fd-4f37-99dc-9427a444018a", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": null, + "description": "TaskTargets tied to the Rocket", + "flatRelationTargetFieldMetadata": { + "defaultValue": null, + "description": "TaskTargets Rocket", + "icon": "IconBuildingSkyscraper", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isUnique": false, + "label": "Rocket", + "name": "rocket", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "joinColumnName": "rocketId", + "onDelete": "CASCADE", + "relationType": "MANY_TO_ONE", + }, + "standardId": Any, + "standardOverrides": null, + "type": "RELATION", + "uniqueIdentifier": "20202020-b98f-473d-85fb-8cebc40b9a50", + "workspaceId": Any, + }, + "flatRelationTargetObjectMetadata": null, + "icon": "IconCheckbox", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isUnique": false, + "label": "TaskTargets", + "name": "taskTargets", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "relationType": "ONE_TO_MANY", + }, + "standardId": Any, + "standardOverrides": null, + "type": "RELATION", + "uniqueIdentifier": "20202020-0860-4566-b865-bff3c626c303", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": null, + "description": "Field used for full-text search", + "flatRelationTargetFieldMetadata": null, + "flatRelationTargetObjectMetadata": null, + "icon": null, + "id": Any, + "isActive": false, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isUnique": false, + "label": "Search vector", + "name": "searchVector", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": null, + "relationTargetObjectMetadataId": null, + "settings": null, + "standardId": Any, + "standardOverrides": null, + "type": "TS_VECTOR", + "uniqueIdentifier": "70e56537-18ef-4811-b1c7-0a444006b815", + "workspaceId": Any, + }, + "type": "create_field", + }, + ], "flatObjectMetadataWithoutFields": { - "description": "default flat object metadata description", - "icon": "icon", + "description": "A rocket", + "icon": "IconRocket", "id": Any, - "imageIdentifierFieldMetadataId": Any, + "imageIdentifierFieldMetadataId": null, "isActive": true, "isAuditLogged": true, "isCustom": true, - "isLabelSyncedWithName": true, + "isLabelSyncedWithName": false, "isRemote": false, "isSearchable": true, "isSystem": false, "labelIdentifierFieldMetadataId": Any, - "labelPlural": "default flat object metadata label plural", - "labelSingular": "default flat object metadata label singular", - "namePlural": "totos", - "nameSingular": "toto", - "shortcut": "shortcut", + "labelPlural": "Rockets", + "labelSingular": "Rocket", + "namePlural": "rockets", + "nameSingular": "rocket", + "shortcut": null, "standardId": null, "standardOverrides": null, - "targetTableName": "", - "uniqueIdentifier": "pomme", + "targetTableName": "DEPRECATED", + "uniqueIdentifier": "d78ec657-74a4-4652-a350-1f44ff62970a", "workspaceId": Any, }, "type": "create_object", @@ -285,35 +806,122 @@ exports[`Workspace migration builder object actions test suite It should build a ] `; -exports[`Workspace migration builder object actions test suite It should build a create_object and create_field and create_index actions for each of this fieldMetadata 1`] = ` +exports[`Workspace migration builder object actions test suite It should build a create_object and create_index actions for each of this fieldMetadata 1`] = ` [ { "createFieldActions": [ { "flatFieldMetadata": { - "defaultValue": null, - "description": "default flat field metadata description", + "defaultValue": "uuid", + "description": "Id", "flatRelationTargetFieldMetadata": null, "flatRelationTargetObjectMetadata": null, - "icon": "icon", + "icon": "Icon123", "id": Any, "isActive": true, - "isCustom": true, + "isCustom": false, "isLabelSyncedWithName": false, - "isNullable": true, - "isSystem": false, + "isNullable": false, + "isSystem": true, "isUnique": false, - "label": "flat field metadata label", - "name": "flatFieldMetadataName", + "label": "Id", + "name": "id", "objectMetadataId": Any, "options": null, "relationTargetFieldMetadataId": null, "relationTargetObjectMetadataId": null, "settings": null, - "standardId": null, + "standardId": Any, + "standardOverrides": null, + "type": "UUID", + "uniqueIdentifier": "20202020-eda0-4cee-9577-3eb357e3c22b", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": "'Untitled'", + "description": "Name", + "flatRelationTargetFieldMetadata": null, + "flatRelationTargetObjectMetadata": null, + "icon": "IconAbc", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": false, + "isUnique": false, + "label": "Name", + "name": "name", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": null, + "relationTargetObjectMetadataId": null, + "settings": null, + "standardId": Any, "standardOverrides": null, "type": "TEXT", - "uniqueIdentifier": "field_0", + "uniqueIdentifier": "20202020-ba07-4ffd-ba63-009491f5749c", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": "now", + "description": "Creation date", + "flatRelationTargetFieldMetadata": null, + "flatRelationTargetObjectMetadata": null, + "icon": "IconCalendar", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": false, + "isUnique": false, + "label": "Creation date", + "name": "createdAt", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": null, + "relationTargetObjectMetadataId": null, + "settings": null, + "standardId": Any, + "standardOverrides": null, + "type": "DATE_TIME", + "uniqueIdentifier": "20202020-66ac-4502-9975-e4d959c50311", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": "now", + "description": "Last time the record was changed", + "flatRelationTargetFieldMetadata": null, + "flatRelationTargetObjectMetadata": null, + "icon": "IconCalendarClock", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": false, + "isUnique": false, + "label": "Last update", + "name": "updatedAt", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": null, + "relationTargetObjectMetadataId": null, + "settings": null, + "standardId": Any, + "standardOverrides": null, + "type": "DATE_TIME", + "uniqueIdentifier": "20202020-d767-4622-bdcf-d8a084834d86", "workspaceId": Any, }, "type": "create_field", @@ -321,28 +929,89 @@ exports[`Workspace migration builder object actions test suite It should build a { "flatFieldMetadata": { "defaultValue": null, - "description": "default flat field metadata description", + "description": "Deletion date", "flatRelationTargetFieldMetadata": null, "flatRelationTargetObjectMetadata": null, - "icon": "icon", + "icon": "IconCalendarClock", "id": Any, "isActive": true, - "isCustom": true, + "isCustom": false, "isLabelSyncedWithName": false, "isNullable": true, "isSystem": false, "isUnique": false, - "label": "flat field metadata label", - "name": "flatFieldMetadataName", + "label": "Deleted at", + "name": "deletedAt", "objectMetadataId": Any, "options": null, "relationTargetFieldMetadataId": null, "relationTargetObjectMetadataId": null, "settings": null, - "standardId": null, + "standardId": Any, "standardOverrides": null, - "type": "TEXT", - "uniqueIdentifier": "field_1", + "type": "DATE_TIME", + "uniqueIdentifier": "20202020-b9a7-48d8-8387-b9a3090a50ec", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": { + "name": "''", + "source": "'MANUAL'", + }, + "description": "The creator of the record", + "flatRelationTargetFieldMetadata": null, + "flatRelationTargetObjectMetadata": null, + "icon": "IconCreativeCommonsSa", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": false, + "isUnique": false, + "label": "Created by", + "name": "createdBy", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": null, + "relationTargetObjectMetadataId": null, + "settings": null, + "standardId": Any, + "standardOverrides": null, + "type": "ACTOR", + "uniqueIdentifier": "20202020-be0e-4971-865b-32ca87cbb315", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": 0, + "description": "Position", + "flatRelationTargetFieldMetadata": null, + "flatRelationTargetObjectMetadata": null, + "icon": "IconHierarchy2", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": true, + "isUnique": false, + "label": "Position", + "name": "position", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": null, + "relationTargetObjectMetadataId": null, + "settings": null, + "standardId": Any, + "standardOverrides": null, + "type": "POSITION", + "uniqueIdentifier": "20202020-c2bd-4e16-bb9a-c8b0411bf49d", "workspaceId": Any, }, "type": "create_field", @@ -350,28 +1019,57 @@ exports[`Workspace migration builder object actions test suite It should build a { "flatFieldMetadata": { "defaultValue": null, - "description": "default flat field metadata description", - "flatRelationTargetFieldMetadata": null, + "description": "TimelineActivities tied to the Rocket", + "flatRelationTargetFieldMetadata": { + "defaultValue": null, + "description": "TimelineActivities Rocket", + "icon": "IconBuildingSkyscraper", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isUnique": false, + "label": "Rocket", + "name": "rocket", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "joinColumnName": "rocketId", + "onDelete": "CASCADE", + "relationType": "MANY_TO_ONE", + }, + "standardId": Any, + "standardOverrides": null, + "type": "RELATION", + "uniqueIdentifier": "20202020-0be6-444e-88f0-12e0c64ac27d", + "workspaceId": Any, + }, "flatRelationTargetObjectMetadata": null, - "icon": "icon", + "icon": "IconTimelineEvent", "id": Any, "isActive": true, - "isCustom": true, + "isCustom": false, "isLabelSyncedWithName": false, "isNullable": true, - "isSystem": false, + "isSystem": true, "isUnique": false, - "label": "flat field metadata label", - "name": "flatFieldMetadataName", + "label": "TimelineActivities", + "name": "timelineActivities", "objectMetadataId": Any, "options": null, - "relationTargetFieldMetadataId": null, - "relationTargetObjectMetadataId": null, - "settings": null, - "standardId": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "relationType": "ONE_TO_MANY", + }, + "standardId": Any, "standardOverrides": null, - "type": "TEXT", - "uniqueIdentifier": "field_2", + "type": "RELATION", + "uniqueIdentifier": "20202020-f1ef-4ba4-8f33-1a4577afa477", "workspaceId": Any, }, "type": "create_field", @@ -379,28 +1077,57 @@ exports[`Workspace migration builder object actions test suite It should build a { "flatFieldMetadata": { "defaultValue": null, - "description": "default flat field metadata description", - "flatRelationTargetFieldMetadata": null, + "description": "Favorites tied to the Rocket", + "flatRelationTargetFieldMetadata": { + "defaultValue": null, + "description": "Favorites Rocket", + "icon": "IconBuildingSkyscraper", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isUnique": false, + "label": "Rocket", + "name": "rocket", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "joinColumnName": "rocketId", + "onDelete": "CASCADE", + "relationType": "MANY_TO_ONE", + }, + "standardId": Any, + "standardOverrides": null, + "type": "RELATION", + "uniqueIdentifier": "20202020-78f7-4d7d-86e9-7562ced0b381", + "workspaceId": Any, + }, "flatRelationTargetObjectMetadata": null, - "icon": "icon", + "icon": "IconHeart", "id": Any, "isActive": true, - "isCustom": true, + "isCustom": false, "isLabelSyncedWithName": false, "isNullable": true, - "isSystem": false, + "isSystem": true, "isUnique": false, - "label": "flat field metadata label", - "name": "flatFieldMetadataName", + "label": "Favorites", + "name": "favorites", "objectMetadataId": Any, "options": null, - "relationTargetFieldMetadataId": null, - "relationTargetObjectMetadataId": null, - "settings": null, - "standardId": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "relationType": "ONE_TO_MANY", + }, + "standardId": Any, "standardOverrides": null, - "type": "TEXT", - "uniqueIdentifier": "field_3", + "type": "RELATION", + "uniqueIdentifier": "20202020-a4a7-4686-b296-1c6c3482ee21", "workspaceId": Any, }, "type": "create_field", @@ -408,55 +1135,229 @@ exports[`Workspace migration builder object actions test suite It should build a { "flatFieldMetadata": { "defaultValue": null, - "description": "default flat field metadata description", - "flatRelationTargetFieldMetadata": null, + "description": "Attachments tied to the Rocket", + "flatRelationTargetFieldMetadata": { + "defaultValue": null, + "description": "Attachments Rocket", + "icon": "IconBuildingSkyscraper", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isUnique": false, + "label": "Rocket", + "name": "rocket", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "joinColumnName": "rocketId", + "onDelete": "CASCADE", + "relationType": "MANY_TO_ONE", + }, + "standardId": Any, + "standardOverrides": null, + "type": "RELATION", + "uniqueIdentifier": "20202020-ca2d-47c0-8253-9d0b662fb01a", + "workspaceId": Any, + }, "flatRelationTargetObjectMetadata": null, - "icon": "icon", + "icon": "IconFileImport", "id": Any, "isActive": true, - "isCustom": true, + "isCustom": false, "isLabelSyncedWithName": false, "isNullable": true, - "isSystem": false, + "isSystem": true, "isUnique": false, - "label": "flat field metadata label", - "name": "flatFieldMetadataName", + "label": "Attachments", + "name": "attachments", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "relationType": "ONE_TO_MANY", + }, + "standardId": Any, + "standardOverrides": null, + "type": "RELATION", + "uniqueIdentifier": "20202020-8d59-46ca-b7b2-73d167712134", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": null, + "description": "NoteTargets tied to the Rocket", + "flatRelationTargetFieldMetadata": { + "defaultValue": null, + "description": "NoteTargets Rocket", + "icon": "IconBuildingSkyscraper", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isUnique": false, + "label": "Rocket", + "name": "rocket", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "joinColumnName": "rocketId", + "onDelete": "CASCADE", + "relationType": "MANY_TO_ONE", + }, + "standardId": Any, + "standardOverrides": null, + "type": "RELATION", + "uniqueIdentifier": "20202020-52b6-4d5e-8f21-ed8b7c09c4a9", + "workspaceId": Any, + }, + "flatRelationTargetObjectMetadata": null, + "icon": "IconCheckbox", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isUnique": false, + "label": "NoteTargets", + "name": "noteTargets", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "relationType": "ONE_TO_MANY", + }, + "standardId": Any, + "standardOverrides": null, + "type": "RELATION", + "uniqueIdentifier": "20202020-01fd-4f37-99dc-9427a444018a", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": null, + "description": "TaskTargets tied to the Rocket", + "flatRelationTargetFieldMetadata": { + "defaultValue": null, + "description": "TaskTargets Rocket", + "icon": "IconBuildingSkyscraper", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isUnique": false, + "label": "Rocket", + "name": "rocket", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "joinColumnName": "rocketId", + "onDelete": "CASCADE", + "relationType": "MANY_TO_ONE", + }, + "standardId": Any, + "standardOverrides": null, + "type": "RELATION", + "uniqueIdentifier": "20202020-b98f-473d-85fb-8cebc40b9a50", + "workspaceId": Any, + }, + "flatRelationTargetObjectMetadata": null, + "icon": "IconCheckbox", + "id": Any, + "isActive": true, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isUnique": false, + "label": "TaskTargets", + "name": "taskTargets", + "objectMetadataId": Any, + "options": null, + "relationTargetFieldMetadataId": Any, + "relationTargetObjectMetadataId": Any, + "settings": { + "relationType": "ONE_TO_MANY", + }, + "standardId": Any, + "standardOverrides": null, + "type": "RELATION", + "uniqueIdentifier": "20202020-0860-4566-b865-bff3c626c303", + "workspaceId": Any, + }, + "type": "create_field", + }, + { + "flatFieldMetadata": { + "defaultValue": null, + "description": "Field used for full-text search", + "flatRelationTargetFieldMetadata": null, + "flatRelationTargetObjectMetadata": null, + "icon": null, + "id": Any, + "isActive": false, + "isCustom": false, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isUnique": false, + "label": "Search vector", + "name": "searchVector", "objectMetadataId": Any, "options": null, "relationTargetFieldMetadataId": null, "relationTargetObjectMetadataId": null, "settings": null, - "standardId": null, + "standardId": Any, "standardOverrides": null, - "type": "TEXT", - "uniqueIdentifier": "field_4", + "type": "TS_VECTOR", + "uniqueIdentifier": "70e56537-18ef-4811-b1c7-0a444006b815", "workspaceId": Any, }, "type": "create_field", }, ], "flatObjectMetadataWithoutFields": { - "description": "default flat object metadata description", - "icon": "icon", + "description": "A rocket", + "icon": "IconRocket", "id": Any, - "imageIdentifierFieldMetadataId": Any, + "imageIdentifierFieldMetadataId": null, "isActive": true, "isAuditLogged": true, "isCustom": true, - "isLabelSyncedWithName": true, + "isLabelSyncedWithName": false, "isRemote": false, "isSearchable": true, "isSystem": false, "labelIdentifierFieldMetadataId": Any, - "labelPlural": "default flat object metadata label plural", - "labelSingular": "default flat object metadata label singular", - "namePlural": "totos", - "nameSingular": "toto", - "shortcut": "shortcut", + "labelPlural": "Rockets", + "labelSingular": "Rocket", + "namePlural": "rockets", + "nameSingular": "rocket", + "shortcut": null, "standardId": null, "standardOverrides": null, - "targetTableName": "", - "uniqueIdentifier": "pomme", + "targetTableName": "DEPRECATED", + "uniqueIdentifier": "d78ec657-74a4-4652-a350-1f44ff62970a", "workspaceId": Any, }, "type": "create_object", @@ -487,6 +1388,141 @@ exports[`Workspace migration builder object actions test suite It should build a "objectMetadataId": Any, "type": "delete_object", }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, + { + "fieldMetadataId": Any, + "objectMetadataId": Any, + "type": "delete_field", + }, ] `; @@ -497,17 +1533,12 @@ exports[`Workspace migration builder object actions test suite It should build a "type": "update_object", "updates": [ { - "from": true, - "property": "isLabelSyncedWithName", - "to": false, - }, - { - "from": "totos", + "from": "pets", "property": "namePlural", "to": "prastoins", }, { - "from": "toto", + "from": "pet", "property": "nameSingular", "to": "prastouin", }, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-field-test-case.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-field-test-case.ts index a0592f2a4c..9e70e55af3 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-field-test-case.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-field-test-case.ts @@ -3,19 +3,20 @@ import { FieldMetadataType } from 'twenty-shared/types'; import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface'; +import { COMPANY_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/company-flat-fields.mock'; import { getFlatFieldMetadataMock } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/get-flat-field-metadata.mock'; +import { OPPORTUNITY_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/opportunity-flat-fields.mock'; +import { PET_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/pet-flat-fields.mock'; +import { FLAT_OBJECT_METADATA_MAPS_MOCKS } from 'src/engine/metadata-modules/flat-object-metadata-maps/mocks/flat-object-metadata-maps.mock'; +import { addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util'; +import { deleteFieldFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-field-from-flat-object-metadata-maps-or-throw.util'; +import { replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/replace-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util'; +import { COMPANY_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/company-flat-object.mock'; import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock'; +import { PET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/pet-flat-object.mock'; +import { ROCKET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/rocket-flat-object.mock'; import { type WorkspaceMigrationBuilderTestCase } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type'; -const basicObjectMetadataId = faker.string.uuid(); -const basicFlatFieldMetadatas = Array.from({ length: 5 }, (_value, index) => - getFlatFieldMetadataMock({ - objectMetadataId: basicObjectMetadataId, - uniqueIdentifier: `field_${index}`, - type: FieldMetadataType.TEXT, - }), -); - // TODO prastoin test defaultValue and settings updates // TODO prastoin test standard abstraction in TDD style @@ -24,16 +25,14 @@ const relationTestCases: WorkspaceMigrationBuilderTestCase[] = [ title: 'It should build an create_field action for a RELATION field', context: { input: () => { - const objectMetadataId = faker.string.uuid(); - const targetObjectMetadataId = faker.string.uuid(); const createdFlatRelationFieldMetadata = getFlatFieldMetadataMock({ uniqueIdentifier: 'field-metadata-unique-identifier-1', - objectMetadataId, + objectMetadataId: PET_FLAT_OBJECT_MOCK.id, type: FieldMetadataType.RELATION, relationTargetFieldMetadataId: faker.string.uuid(), relationTargetObjectMetadataId: faker.string.uuid(), flatRelationTargetFieldMetadata: getFlatFieldMetadataMock({ - objectMetadataId: targetObjectMetadataId, + objectMetadataId: COMPANY_FLAT_OBJECT_MOCK.id, type: FieldMetadataType.RELATION, uniqueIdentifier: 'field-metadata-unique-identifier-2', }), @@ -41,20 +40,14 @@ const relationTestCases: WorkspaceMigrationBuilderTestCase[] = [ uniqueIdentifier: 'object-metadata-unique-identifier-2', }), }); - const flatObjectMetadata = getFlatObjectMetadataMock({ - uniqueIdentifier: 'object-metadata-unique-identifier-1', - isLabelSyncedWithName: true, - flatFieldMetadatas: [], - }); return { - from: [flatObjectMetadata], - to: [ - { - ...flatObjectMetadata, - flatFieldMetadatas: [createdFlatRelationFieldMetadata], - }, - ], + fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + toFlatObjectMetadataMaps: + addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({ + flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + flatFieldMetadata: createdFlatRelationFieldMetadata, + }), }; }, expectedActionsTypeCounter: { @@ -65,42 +58,17 @@ const relationTestCases: WorkspaceMigrationBuilderTestCase[] = [ { title: 'It should build an update_field action for a RELATION field', context: { - input: () => { - const objectMetadataId = faker.string.uuid(); - - const updatedFieldMetadata = getFlatFieldMetadataMock({ - uniqueIdentifier: 'field-metadata-unique-identifier-1', - objectMetadataId, - type: FieldMetadataType.RELATION, - relationTargetFieldMetadataId: faker.string.uuid(), - relationTargetObjectMetadataId: faker.string.uuid(), - }); - const flatObjectMetadata = getFlatObjectMetadataMock({ - uniqueIdentifier: 'object-metadata-unique-identifier-1', - isLabelSyncedWithName: true, - flatFieldMetadatas: [ - ...basicFlatFieldMetadatas, - updatedFieldMetadata, - ], - }); - - return { - from: [flatObjectMetadata], - to: [ - { - ...flatObjectMetadata, - flatFieldMetadatas: [ - ...basicFlatFieldMetadatas, - { - ...updatedFieldMetadata, - isActive: false, - description: 'new description', - label: 'new label', - }, - ], - }, - ], - }; + input: { + fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + toFlatObjectMetadataMaps: + replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({ + flatFieldMetadata: getFlatFieldMetadataMock({ + ...PET_FLAT_FIELDS_MOCK.species, + description: 'new description', + label: 'new label', + }), + flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + }), }, expectedActionsTypeCounter: { updateField: 1, @@ -111,47 +79,23 @@ const relationTestCases: WorkspaceMigrationBuilderTestCase[] = [ title: 'It should NOT build an update_field action for a field RELATION uncovered fields mutation', context: { - input: () => { - const objectMetadataId = faker.string.uuid(); - const updatedFieldMetadata = getFlatFieldMetadataMock({ - uniqueIdentifier: 'field-metadata-unique-identifier-1', - objectMetadataId, - type: FieldMetadataType.RELATION, - settings: { - relationType: RelationType.MANY_TO_ONE, - joinColumnName: 'column-name', - onDelete: undefined, - }, - relationTargetFieldMetadataId: faker.string.uuid(), - relationTargetObjectMetadataId: faker.string.uuid(), - }); - const flatObjectMetadata = getFlatObjectMetadataMock({ - uniqueIdentifier: 'object-metadata-unique-identifier-1', - isLabelSyncedWithName: true, - flatFieldMetadatas: [updatedFieldMetadata], - }); - - return { - from: [flatObjectMetadata], - to: [ - { - ...flatObjectMetadata, - flatFieldMetadatas: [ - { - ...updatedFieldMetadata, - settings: { - relationType: RelationType.ONE_TO_MANY, - isForeignKey: false, - joinColumnName: 'new-column-name', - onDelete: undefined, - }, - relationTargetFieldMetadataId: faker.string.uuid(), - relationTargetObjectMetadataId: faker.string.uuid(), - }, - ], - }, - ], - }; + input: { + fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + toFlatObjectMetadataMaps: + replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({ + flatFieldMetadata: getFlatFieldMetadataMock({ + ...COMPANY_FLAT_FIELDS_MOCK.opportunities, + settings: { + relationType: RelationType.MANY_TO_ONE, + joinColumnName: 'new-column-name', + onDelete: undefined, + }, + name: 'newName', + relationTargetFieldMetadataId: faker.string.uuid(), + relationTargetObjectMetadataId: faker.string.uuid(), + }), + flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + }), }, }, }, @@ -161,29 +105,17 @@ const basicCrudTestCases: WorkspaceMigrationBuilderTestCase[] = [ { title: 'It should build an create_field action', context: { - input: () => { - const objectMetadataId = faker.string.uuid(); - - const flatFieldMetadata = getFlatFieldMetadataMock({ - uniqueIdentifier: 'field-metadata-unique-identifier-1', - type: FieldMetadataType.TEXT, - objectMetadataId, - }); - const flatObjectMetadata = getFlatObjectMetadataMock({ - uniqueIdentifier: 'object-metadata-unique-identifier-1', - isLabelSyncedWithName: true, - flatFieldMetadatas: [], - }); - - return { - from: [flatObjectMetadata], - to: [ - { - ...flatObjectMetadata, - flatFieldMetadatas: [flatFieldMetadata], - }, - ], - }; + input: { + fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + toFlatObjectMetadataMaps: + addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({ + flatFieldMetadata: getFlatFieldMetadataMock({ + uniqueIdentifier: 'field-metadata-unique-identifier-1', + type: FieldMetadataType.TEXT, + objectMetadataId: ROCKET_FLAT_OBJECT_MOCK.id, + }), + flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + }), }, expectedActionsTypeCounter: { createField: 1, @@ -193,37 +125,19 @@ const basicCrudTestCases: WorkspaceMigrationBuilderTestCase[] = [ { title: 'It should build an update_field action', context: { - input: () => { - const objectMetadataId = faker.string.uuid(); - - const flatFieldMetadata = getFlatFieldMetadataMock({ - uniqueIdentifier: 'field-metadata-unique-identifier-1', - type: FieldMetadataType.TEXT, - objectMetadataId, - }); - const flatObjectMetadata = getFlatObjectMetadataMock({ - uniqueIdentifier: 'object-metadata-unique-identifier-1', - flatFieldMetadatas: [...basicFlatFieldMetadatas, flatFieldMetadata], - }); - - return { - from: [flatObjectMetadata], - to: [ - { - ...flatObjectMetadata, - flatFieldMetadatas: [ - ...basicFlatFieldMetadatas, - { - ...flatFieldMetadata, - description: 'new description', - name: 'new name', - isActive: false, - icon: 'new icon', - }, - ], - }, - ], - }; + input: { + fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + toFlatObjectMetadataMaps: + replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({ + flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + flatFieldMetadata: getFlatFieldMetadataMock({ + ...OPPORTUNITY_FLAT_FIELDS_MOCK.stage, + description: 'new description', + name: 'new name', + isActive: false, + icon: 'new icon', + }), + }), }, expectedActionsTypeCounter: { updateField: 1, @@ -233,28 +147,13 @@ const basicCrudTestCases: WorkspaceMigrationBuilderTestCase[] = [ { title: 'It should build a delete_field action', context: { - input: () => { - const objectMetadataId = faker.string.uuid(); - - const flatFieldMetadata = getFlatFieldMetadataMock({ - uniqueIdentifier: 'field-metadata-unique-identifier-1', - type: FieldMetadataType.TEXT, - objectMetadataId, - }); - const flatObjectMetadata = getFlatObjectMetadataMock({ - uniqueIdentifier: 'object-metadata-unique-identifier-1', - flatFieldMetadatas: [...basicFlatFieldMetadatas, flatFieldMetadata], - }); - - return { - from: [flatObjectMetadata], - to: [ - { - ...flatObjectMetadata, - flatFieldMetadatas: basicFlatFieldMetadatas, - }, - ], - }; + input: { + fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + toFlatObjectMetadataMaps: deleteFieldFromFlatObjectMetadataMapsOrThrow({ + fieldMetadataId: PET_FLAT_FIELDS_MOCK.species.id, + objectMetadataId: PET_FLAT_FIELDS_MOCK.species.objectMetadataId, + flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + }), }, expectedActionsTypeCounter: { deleteField: 1, @@ -271,24 +170,9 @@ export const WORKSPACE_MIGRATION_FIELD_BUILDER_TEST_CASES: WorkspaceMigrationBui title: 'It should not infer any actions as from and to fields are identical', context: { - input: () => { - const objectMetadataId = faker.string.uuid(); - const flatFieldMetadata = getFlatFieldMetadataMock({ - uniqueIdentifier: 'field-metadata-unique-identifier-1', - type: FieldMetadataType.TEXT, - objectMetadataId, - }); - const from = [ - getFlatObjectMetadataMock({ - uniqueIdentifier: 'object-metadata-unique-identifier-1', - flatFieldMetadatas: [flatFieldMetadata], - }), - ]; - - return { - from, - to: from, - }; + input: { + fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + toFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, }, }, }, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-index-test-case.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-index-test-case.ts index 9f3fb6716e..480c3c6dc2 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-index-test-case.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-index-test-case.ts @@ -1,37 +1,30 @@ -import { faker } from '@faker-js/faker'; - import { getFlatIndexMetadataMock } from 'src/engine/metadata-modules/flat-index-metadata/__mocks__/get-flat-index-metadata.mock'; +import { FLAT_OBJECT_METADATA_MAPS_MOCKS } from 'src/engine/metadata-modules/flat-object-metadata-maps/mocks/flat-object-metadata-maps.mock'; +import { replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/replace-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util'; import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock'; +import { NOTE_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/note-flat-object.mock'; import { type WorkspaceMigrationBuilderTestCase } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type'; -// Should test more things such as flatFieldIndex diffing -const objectMetadataId = faker.string.uuid(); - export const WORKSPACE_MIGRATION_INDEX_BUILDER_TEST_CASES: WorkspaceMigrationBuilderTestCase[] = [ { title: 'It should build an create_index action', context: { - input: () => { - const flatIndexMetadata = getFlatIndexMetadataMock({ - uniqueIdentifier: 'field-metadata-unique-identifier-1', - objectMetadataId, - }); - const flatObjectMetadata = getFlatObjectMetadataMock({ - uniqueIdentifier: 'object-metadata-unique-identifier-1', - isLabelSyncedWithName: true, - flatIndexMetadatas: [], - }); - - return { - from: [flatObjectMetadata], - to: [ - { - ...flatObjectMetadata, - flatIndexMetadatas: [flatIndexMetadata], - }, - ], - }; + input: { + fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + toFlatObjectMetadataMaps: + replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({ + flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + flatObjectMetadata: getFlatObjectMetadataMock({ + ...NOTE_FLAT_OBJECT_MOCK, + flatIndexMetadatas: [ + getFlatIndexMetadataMock({ + objectMetadataId: NOTE_FLAT_OBJECT_MOCK.id, + uniqueIdentifier: 'field-metadata-unique-identifier-1', + }), + ], + }), + }), }, expectedActionsTypeCounter: { createIndex: 1, @@ -44,30 +37,37 @@ export const WORKSPACE_MIGRATION_INDEX_BUILDER_TEST_CASES: WorkspaceMigrationBui context: { input: () => { const flatIndexMetadata = getFlatIndexMetadataMock({ + objectMetadataId: NOTE_FLAT_OBJECT_MOCK.id, uniqueIdentifier: 'field-metadata-unique-identifier-1', - objectMetadataId, }); - const flatObjectMetadata = getFlatObjectMetadataMock({ - uniqueIdentifier: 'object-metadata-unique-identifier-1', - isLabelSyncedWithName: true, - flatIndexMetadatas: [flatIndexMetadata], - }); - - return { - from: [flatObjectMetadata], - to: [ - { - ...flatObjectMetadata, + const fromFlatObjectMetadataMaps = + replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({ + flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + flatObjectMetadata: getFlatObjectMetadataMock({ + ...NOTE_FLAT_OBJECT_MOCK, + flatIndexMetadatas: [flatIndexMetadata], + }), + }); + const toFlatObjectMetadataMaps = + replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({ + flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + flatObjectMetadata: getFlatObjectMetadataMock({ + ...NOTE_FLAT_OBJECT_MOCK, flatIndexMetadatas: [ { ...flatIndexMetadata, name: 'new index name', isUnique: false, indexWhereClause: 'new index where clause', + uniqueIdentifier: 'field-metadata-unique-identifier-1', }, ], - }, - ], + }), + }); + + return { + fromFlatObjectMetadataMaps, + toFlatObjectMetadataMaps, }; }, expectedActionsTypeCounter: { @@ -79,26 +79,21 @@ export const WORKSPACE_MIGRATION_INDEX_BUILDER_TEST_CASES: WorkspaceMigrationBui { title: 'It should build a delete_index action', context: { - input: () => { - const flatIndexMetadata = getFlatIndexMetadataMock({ - uniqueIdentifier: 'field-metadata-unique-identifier-1', - objectMetadataId, - }); - const flatObjectMetadata = getFlatObjectMetadataMock({ - uniqueIdentifier: 'object-metadata-unique-identifier-1', - isLabelSyncedWithName: true, - flatIndexMetadatas: [flatIndexMetadata], - }); - - return { - from: [flatObjectMetadata], - to: [ - { - ...flatObjectMetadata, - flatIndexMetadatas: [], - }, - ], - }; + input: { + fromFlatObjectMetadataMaps: + replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({ + flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + flatObjectMetadata: getFlatObjectMetadataMock({ + ...NOTE_FLAT_OBJECT_MOCK, + flatIndexMetadatas: [ + getFlatIndexMetadataMock({ + objectMetadataId: NOTE_FLAT_OBJECT_MOCK.id, + uniqueIdentifier: 'field-metadata-unique-identifier-1', + }), + ], + }), + }), + toFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, }, expectedActionsTypeCounter: { deleteIndex: 1, @@ -109,21 +104,9 @@ export const WORKSPACE_MIGRATION_INDEX_BUILDER_TEST_CASES: WorkspaceMigrationBui title: 'It should not infer any actions as from and to indexes are identical', context: { - input: () => { - const flatIndexMetadata = getFlatIndexMetadataMock({ - uniqueIdentifier: 'field-metadata-unique-identifier-1', - objectMetadataId, - }); - const flatObjectMetadata = getFlatObjectMetadataMock({ - uniqueIdentifier: 'object-metadata-unique-identifier-1', - isLabelSyncedWithName: true, - flatIndexMetadatas: [flatIndexMetadata], - }); - - return { - from: [flatObjectMetadata], - to: [flatObjectMetadata], - }; + input: { + fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + toFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, }, }, }, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-object-test-case.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-object-test-case.ts index 45db4a75a2..e0f6b661ea 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-object-test-case.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-object-test-case.ts @@ -1,9 +1,13 @@ -import { faker } from '@faker-js/faker'; -import { FieldMetadataType } from 'twenty-shared/types'; - -import { getFlatFieldMetadataMock } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/get-flat-field-metadata.mock'; import { getFlatIndexMetadataMock } from 'src/engine/metadata-modules/flat-index-metadata/__mocks__/get-flat-index-metadata.mock'; +import { EMPTY_FLAT_OBJECT_METADATA_MAPS } from 'src/engine/metadata-modules/flat-object-metadata-maps/constant/empty-flat-object-metadata-maps.constant'; +import { FLAT_OBJECT_METADATA_MAPS_MOCKS } from 'src/engine/metadata-modules/flat-object-metadata-maps/mocks/flat-object-metadata-maps.mock'; +import { deleteObjectFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-object-from-flat-object-metadata-maps-or-throw.util'; +import { replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/replace-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util'; +import { COMPANY_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/company-flat-object.mock'; import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock'; +import { PET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/pet-flat-object.mock'; +import { ROCKET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/rocket-flat-object.mock'; +import { fromFlatObjectMetadatasToFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadatas-to-flat-object-metadata-maps.util'; import { type WorkspaceMigrationBuilderTestCase } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type'; export const WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES: WorkspaceMigrationBuilderTestCase[] = @@ -12,25 +16,18 @@ export const WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES: WorkspaceMigrationBu title: 'It should build an update_object action with all object updated fields', context: { - input: () => { - const flatObjectMetadata = getFlatObjectMetadataMock({ - uniqueIdentifier: 'pomme', - nameSingular: 'toto', - namePlural: 'totos', - isLabelSyncedWithName: true, - }); - - return { - from: [flatObjectMetadata], - to: [ - { - ...flatObjectMetadata, + input: { + fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + toFlatObjectMetadataMaps: + replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({ + flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + flatObjectMetadata: getFlatObjectMetadataMock({ + ...PET_FLAT_OBJECT_MOCK, nameSingular: 'prastouin', namePlural: 'prastoins', isLabelSyncedWithName: false, - }, - ], - }; + }), + }), }, expectedActionsTypeCounter: { updateObject: 1, @@ -40,18 +37,16 @@ export const WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES: WorkspaceMigrationBu { title: 'It should build a create_object action', context: { - input: () => { - const flatObjectMetadata = getFlatObjectMetadataMock({ - uniqueIdentifier: 'pomme', - nameSingular: 'toto', - namePlural: 'totos', - isLabelSyncedWithName: true, - }); - - return { - from: [], - to: [flatObjectMetadata], - }; + input: { + fromFlatObjectMetadataMaps: + fromFlatObjectMetadatasToFlatObjectMetadataMaps([ + PET_FLAT_OBJECT_MOCK, + ]), + toFlatObjectMetadataMaps: + fromFlatObjectMetadatasToFlatObjectMetadataMaps([ + PET_FLAT_OBJECT_MOCK, + ROCKET_FLAT_OBJECT_MOCK, + ]), }, expectedActionsTypeCounter: { createObject: 1, @@ -60,37 +55,22 @@ export const WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES: WorkspaceMigrationBu }, { title: - 'It should build a create_object and create_field and create_index actions for each of this fieldMetadata', + 'It should build a create_object and create_index actions for each of this fieldMetadata', context: { - input: () => { - const objectMetadataId = faker.string.uuid(); - const flatFieldMetadatas = Array.from( - { length: 5 }, - (_value, index) => - getFlatFieldMetadataMock({ - type: FieldMetadataType.TEXT, - objectMetadataId, - uniqueIdentifier: `field_${index}`, - }), - ); - const flatIndexMetadata = getFlatIndexMetadataMock({ - uniqueIdentifier: 'field-metadata-unique-identifier-1', - objectMetadataId, - }); - const flatObjectMetadata = getFlatObjectMetadataMock({ - uniqueIdentifier: 'pomme', - nameSingular: 'toto', - namePlural: 'totos', - isLabelSyncedWithName: true, - id: objectMetadataId, - flatFieldMetadatas, - flatIndexMetadatas: [flatIndexMetadata], - }); - - return { - from: [], - to: [flatObjectMetadata], - }; + input: { + fromFlatObjectMetadataMaps: EMPTY_FLAT_OBJECT_METADATA_MAPS, + toFlatObjectMetadataMaps: + fromFlatObjectMetadatasToFlatObjectMetadataMaps([ + { + ...ROCKET_FLAT_OBJECT_MOCK, + flatIndexMetadatas: [ + getFlatIndexMetadataMock({ + objectMetadataId: ROCKET_FLAT_OBJECT_MOCK.id, + uniqueIdentifier: 'field-metadata-unique-identifier-1', + }), + ], + }, + ]), }, expectedActionsTypeCounter: { @@ -103,36 +83,26 @@ export const WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES: WorkspaceMigrationBu { title: 'It should build a delete_object action', context: { - input: () => { - const flatObjectMetadata = getFlatObjectMetadataMock({ - uniqueIdentifier: 'pomme', - nameSingular: 'toto', - namePlural: 'totos', - isLabelSyncedWithName: true, - }); - - return { - from: [flatObjectMetadata], - to: [], - }; + input: { + fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + toFlatObjectMetadataMaps: + deleteObjectFromFlatObjectMetadataMapsOrThrow({ + flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + objectMetadataId: COMPANY_FLAT_OBJECT_MOCK.id, + }), }, expectedActionsTypeCounter: { deleteObject: 1, + deleteField: COMPANY_FLAT_OBJECT_MOCK.flatFieldMetadatas.length, }, }, }, { title: 'It should not infer any actions as from and to are identical', context: { - input: () => { - const from = [ - getFlatObjectMetadataMock({ uniqueIdentifier: 'pomme' }), - ]; - - return { - from, - to: from, - }; + input: { + fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, + toFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS, }, }, }, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type.ts index ec371e1e8d..517e8d6182 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type.ts @@ -2,11 +2,7 @@ import { type EachTestingContext } from 'twenty-shared/testing'; import { type ConvertActionTypeToCamelCase } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/convert-action-type-to-camel-case.type'; import { type WorkspaceMigrationActionTypeV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2'; -import { type WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service'; - -type WorkspaceBuilderArgs = Parameters< - typeof WorkspaceMigrationBuilderV2Service.prototype.build ->[0]['objectMetadataFromToInputs']; +import { type WorkspaceMigrationBuildArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service'; export type CamelCasedWorkspaceMigrationActionsType = ConvertActionTypeToCamelCase; @@ -16,6 +12,8 @@ export type ExpectedActionCounters = Partial< >; export type WorkspaceMigrationBuilderTestCase = EachTestingContext<{ - input: WorkspaceBuilderArgs | (() => WorkspaceBuilderArgs); + input: + | Omit + | (() => Omit); expectedActionsTypeCounter?: ExpectedActionCounters; }>; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/workspace-migration-builder.spec.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/workspace-migration-builder.spec.ts index d2b8ab0e4f..020b7998f2 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/workspace-migration-builder.spec.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/workspace-migration-builder.spec.ts @@ -31,6 +31,7 @@ const allWorkspaceBuilderTestCases: { }, ]; +// TODO prastoin add coverage to infer deletion from missing entities const expectedActionsTypeCounterChecker = ({ expectedActionsTypeCounter, workspaceMigration, @@ -81,12 +82,15 @@ describe.each(allWorkspaceBuilderTestCases)( it.each(eachTestingContextFilter(testCases))( '$title', ({ context: { input, expectedActionsTypeCounter } }) => { - const { from, to } = typeof input === 'function' ? input() : input; + const { + fromFlatObjectMetadataMaps, + toFlatObjectMetadataMaps, + inferDeletionFromMissingObjectFieldIndex, + } = typeof input === 'function' ? input() : input; const workspaceMigration = service.build({ - objectMetadataFromToInputs: { - from, - to, - }, + fromFlatObjectMetadataMaps, + toFlatObjectMetadataMaps, + inferDeletionFromMissingObjectFieldIndex, workspaceId: '20202020-52cc-4c64-ad63-76c26fc3a1e1', }); diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service.ts index beebc09f36..9634dbb5bb 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service.ts @@ -2,7 +2,8 @@ import { Injectable } from '@nestjs/common'; import { type FromTo } from 'twenty-shared/types'; -import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; +import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type'; +import { fromFlatObjectMetadataMapsToFlatObjectMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-maps-to-flat-object-metadatas.util'; import { deletedCreatedUpdatedMatrixDispatcher } from 'src/engine/workspace-manager/workspace-migration-v2/utils/deleted-created-updated-matrix-dispatcher.util'; import { type WorkspaceMigrationV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-v2'; import { computeUpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/compute-updated-object-metadata-deleted-created-updated-field-matrix.util'; @@ -12,24 +13,35 @@ import { getWorkspaceMigrationV2CreateIndexAction } from 'src/engine/workspace-m import { buildWorkspaceMigrationV2FieldActions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-v2-field-actions-builder'; import { buildWorkspaceMigrationIndexActions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-v2-index-actions-builder'; import { buildWorkspaceMigrationV2ObjectActions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-v2-object-actions-builder'; +export type WorkspaceMigrationBuildArgs = { + workspaceId: string; + inferDeletionFromMissingObjectFieldIndex?: boolean; +} & FromTo; @Injectable() export class WorkspaceMigrationBuilderV2Service { constructor() {} build({ - objectMetadataFromToInputs, + fromFlatObjectMetadataMaps, + toFlatObjectMetadataMaps, workspaceId, inferDeletionFromMissingObjectFieldIndex = true, - }: { - objectMetadataFromToInputs: FromTo; - workspaceId: string; - inferDeletionFromMissingObjectFieldIndex?: boolean; - }): WorkspaceMigrationV2 { + }: WorkspaceMigrationBuildArgs): WorkspaceMigrationV2 { + const fromFlatObjectMetadatas = + fromFlatObjectMetadataMapsToFlatObjectMetadatas( + fromFlatObjectMetadataMaps, + ); + const toFlatObjectMetadatas = + fromFlatObjectMetadataMapsToFlatObjectMetadatas(toFlatObjectMetadataMaps); + const { created: createdObjectMetadata, deleted: deletedObjectMetadata, updated: updatedObjectMetadata, - } = deletedCreatedUpdatedMatrixDispatcher(objectMetadataFromToInputs); + } = deletedCreatedUpdatedMatrixDispatcher({ + from: fromFlatObjectMetadatas, + to: toFlatObjectMetadatas, + }); const objectWorkspaceMigrationActions = buildWorkspaceMigrationV2ObjectActions({ diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/workspace-migration-runner-v2.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/workspace-migration-runner-v2.service.ts index 07ef99d519..aaeaa69ce8 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/workspace-migration-runner-v2.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/workspace-migration-runner-v2.service.ts @@ -3,6 +3,7 @@ import { InjectDataSource } from '@nestjs/typeorm'; import { DataSource } from 'typeorm'; +import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type'; import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service'; import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service'; import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service'; @@ -23,10 +24,12 @@ export class WorkspaceMigrationRunnerV2Service { private readonly coreDataSource: DataSource, ) {} - run = async (workspaceMigration: WorkspaceMigrationV2) => { + run = async ( + workspaceMigration: WorkspaceMigrationV2, + ): Promise => { const queryRunner = this.coreDataSource.createQueryRunner(); - const { flatObjectMetadataMaps } = + const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } = await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps( { workspaceId: workspaceMigration.workspaceId, @@ -36,7 +39,7 @@ export class WorkspaceMigrationRunnerV2Service { await queryRunner.connect(); await queryRunner.startTransaction(); let optimisticFlatObjectMetadataMaps = structuredClone( - flatObjectMetadataMaps, + existingFlatObjectMetadataMaps, ); try { @@ -73,6 +76,8 @@ export class WorkspaceMigrationRunnerV2Service { workspaceId, }, ); + + return optimisticFlatObjectMetadataMaps; } catch (error) { if (queryRunner.isTransactionActive) { await queryRunner.rollbackTransaction();