diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-5/1-5-remove-favorite-view-relation.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-5/1-5-remove-favorite-view-relation.command.ts new file mode 100644 index 0000000000..5fc1adf1f7 --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-5/1-5-remove-favorite-view-relation.command.ts @@ -0,0 +1,151 @@ +import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; + +import { Command } from 'nest-commander'; +import { FieldMetadataType } from 'twenty-shared/types'; +import { DataSource, Repository } from 'typeorm'; + +import { + ActiveOrSuspendedWorkspacesMigrationCommandRunner, + type RunOnWorkspaceArgs, +} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; +import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service'; +import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util'; +import { + FAVORITE_STANDARD_FIELD_IDS, + VIEW_STANDARD_FIELD_IDS, +} from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; +import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; + +@Command({ + name: 'migrate:1-5:remove-favorite-view-relation', + description: 'Remove favorite view relation.', +}) +export class RemoveFavoriteViewRelation extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { + constructor( + @InjectRepository(Workspace, 'core') + protected readonly workspaceRepository: Repository, + protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, + protected readonly workspaceSchemaManager: WorkspaceSchemaManagerService, + @InjectDataSource('core') + protected readonly coreDataSource: DataSource, + private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService, + ) { + super(workspaceRepository, twentyORMGlobalManager); + } + + override async runOnWorkspace({ + workspaceId, + options, + }: RunOnWorkspaceArgs): Promise { + const queryRunner = this.coreDataSource.createQueryRunner(); + + await queryRunner.connect(); + + await queryRunner.startTransaction(); + + try { + const objectMetadataRepository = + queryRunner.manager.getRepository(ObjectMetadataEntity); + + const fieldMetadataRepository = + queryRunner.manager.getRepository(FieldMetadataEntity); + + const [favoriteObjectMetadata] = await objectMetadataRepository.find({ + where: { + standardId: STANDARD_OBJECT_IDS.favorite, + workspaceId, + }, + }); + + if (!favoriteObjectMetadata) { + throw new Error('Favorite object metadata not found'); + } + + const [viewObjectMetadata] = await objectMetadataRepository.find({ + where: { + standardId: STANDARD_OBJECT_IDS.view, + workspaceId, + }, + }); + + if (!viewObjectMetadata) { + throw new Error('View object metadata not found'); + } + + const [favoriteViewFieldMetadata] = await fieldMetadataRepository.find({ + where: { + objectMetadataId: favoriteObjectMetadata.id, + standardId: FAVORITE_STANDARD_FIELD_IDS.view, + workspaceId, + }, + }); + + const [viewFavoriteFieldMetadata] = await fieldMetadataRepository.find({ + where: { + objectMetadataId: viewObjectMetadata.id, + standardId: VIEW_STANDARD_FIELD_IDS.favorites, + workspaceId, + }, + }); + + if (!viewFavoriteFieldMetadata || !favoriteViewFieldMetadata) { + this.logger.warn( + 'View or favorite view field metadata not found or already migrated, skipping...', + ); + + return; + } + + if (!options.dryRun) { + await fieldMetadataRepository.delete(viewFavoriteFieldMetadata.id); + await fieldMetadataRepository.update(favoriteViewFieldMetadata.id, { + name: 'viewId', + type: FieldMetadataType.UUID, + label: 'ViewId', + description: 'ViewId', + icon: 'IconView', + isSystem: true, + isNullable: true, + relationTargetFieldMetadataId: null, + relationTargetObjectMetadataId: null, + settings: null, + }); + } + + const workspaceSchemaName = getWorkspaceSchemaName(workspaceId); + + const foreignKeyName = + queryRunner.connection.namingStrategy.foreignKeyName( + favoriteObjectMetadata.nameSingular, + [`${favoriteViewFieldMetadata.name}Id`], + `${workspaceSchemaName}.${viewObjectMetadata.nameSingular}`, + [`${viewFavoriteFieldMetadata.name}`], + ); + + if (!options.dryRun) { + await this.workspaceSchemaManager.foreignKeyManager.dropForeignKey({ + queryRunner, + schemaName: workspaceSchemaName, + tableName: favoriteObjectMetadata.nameSingular, + foreignKeyName, + }); + } + + await queryRunner.commitTransaction(); + } catch (error) { + await queryRunner.rollbackTransaction(); + throw error; + } finally { + await queryRunner.release(); + } + + await this.workspaceMetadataVersionService.incrementMetadataVersion( + workspaceId, + ); + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-5/1-5-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-5/1-5-upgrade-version-command.module.ts new file mode 100644 index 0000000000..0cf2fe865c --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-5/1-5-upgrade-version-command.module.ts @@ -0,0 +1,25 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { RemoveFavoriteViewRelation } from 'src/database/commands/upgrade-version-command/1-5/1-5-remove-favorite-view-relation.command'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module'; +import { WorkspaceSchemaManagerModule } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.module'; +import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; + +@Module({ + imports: [ + TypeOrmModule.forFeature( + [Workspace, FieldMetadataEntity, ObjectMetadataEntity], + 'core', + ), + WorkspaceDataSourceModule, + WorkspaceSchemaManagerModule, + WorkspaceMetadataVersionModule, + ], + providers: [RemoveFavoriteViewRelation], + exports: [RemoveFavoriteViewRelation], +}) +export class V1_5_UpgradeVersionCommandModule {} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade-version-command.module.ts index e6afb3ee54..69abcdee3a 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade-version-command.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade-version-command.module.ts @@ -6,6 +6,7 @@ import { V0_55_UpgradeVersionCommandModule } from 'src/database/commands/upgrade import { V1_1_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-1/1-1-upgrade-version-command.module'; import { V1_2_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-2/1-2-upgrade-version-command.module'; import { V1_3_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-3/1-3-upgrade-version-command.module'; +import { V1_5_UpgradeVersionCommandModule } from 'src/database/commands/upgrade-version-command/1-5/1-5-upgrade-version-command.module'; import { DatabaseMigrationService, UpgradeCommand, @@ -21,6 +22,7 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp V1_1_UpgradeVersionCommandModule, V1_2_UpgradeVersionCommandModule, V1_3_UpgradeVersionCommandModule, + V1_5_UpgradeVersionCommandModule, WorkspaceSyncMetadataModule, ], providers: [DatabaseMigrationService, UpgradeCommand], diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts index ff338d3978..cdaec72b56 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts @@ -31,6 +31,7 @@ import { RemoveWorkflowRunsWithoutState } from 'src/database/commands/upgrade-ve import { AddNextStepIdsToWorkflowRunsTrigger } from 'src/database/commands/upgrade-version-command/1-3/1-3-add-next-step-ids-to-workflow-runs-trigger.command'; import { AssignRolesToExistingApiKeysCommand } from 'src/database/commands/upgrade-version-command/1-3/1-3-assign-roles-to-existing-api-keys.command'; import { UpdateTimestampColumnTypeInWorkspaceSchemaCommand } from 'src/database/commands/upgrade-version-command/1-3/1-3-update-timestamp-column-type-in-workspace-schema.command'; +import { RemoveFavoriteViewRelation } from 'src/database/commands/upgrade-version-command/1-5/1-5-remove-favorite-view-relation.command'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; @@ -161,6 +162,9 @@ export class UpgradeCommand extends UpgradeCommandRunner { protected readonly assignRolesToExistingApiKeysCommand: AssignRolesToExistingApiKeysCommand, protected readonly addNextStepIdsToWorkflowRunsTrigger: AddNextStepIdsToWorkflowRunsTrigger, protected readonly updateTimestampColumnTypeInWorkspaceSchemaCommand: UpdateTimestampColumnTypeInWorkspaceSchemaCommand, + + // 1.5 Commands + protected readonly removeFavoriteViewRelation: RemoveFavoriteViewRelation, ) { super( workspaceRepository, @@ -234,6 +238,11 @@ export class UpgradeCommand extends UpgradeCommandRunner { afterSyncMetadata: [], }; + const commands_150: VersionCommands = { + beforeSyncMetadata: [this.removeFavoriteViewRelation], + afterSyncMetadata: [], + }; + this.allCommands = { '0.53.0': commands_053, '0.54.0': commands_054, @@ -244,6 +253,7 @@ export class UpgradeCommand extends UpgradeCommandRunner { '1.2.0': commands_120, '1.3.0': commands_130, '1.4.0': commands_140, + '1.5.0': commands_150, }; } diff --git a/packages/twenty-server/src/modules/favorite/standard-objects/favorite.workspace-entity.ts b/packages/twenty-server/src/modules/favorite/standard-objects/favorite.workspace-entity.ts index ebbd0df837..36892e519e 100644 --- a/packages/twenty-server/src/modules/favorite/standard-objects/favorite.workspace-entity.ts +++ b/packages/twenty-server/src/modules/favorite/standard-objects/favorite.workspace-entity.ts @@ -23,7 +23,6 @@ import { NoteWorkspaceEntity } from 'src/modules/note/standard-objects/note.work import { OpportunityWorkspaceEntity } from 'src/modules/opportunity/standard-objects/opportunity.workspace-entity'; import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity'; import { TaskWorkspaceEntity } from 'src/modules/task/standard-objects/task.workspace-entity'; -import { ViewWorkspaceEntity } from 'src/modules/view/standard-objects/view.workspace-entity'; import { WorkflowRunWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity'; import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity'; import { WorkflowWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity'; @@ -211,20 +210,14 @@ export class FavoriteWorkspaceEntity extends BaseWorkspaceEntity { @WorkspaceJoinColumn('note') noteId: string; - @WorkspaceRelation({ + @WorkspaceField({ standardId: FAVORITE_STANDARD_FIELD_IDS.view, - type: RelationType.MANY_TO_ONE, - label: msg`View`, - description: msg`Favorite view`, - icon: 'IconLayoutCollage', - inverseSideTarget: () => ViewWorkspaceEntity, - inverseSideFieldKey: 'favorites', - onDelete: RelationOnDeleteAction.CASCADE, + type: FieldMetadataType.UUID, + label: msg`ViewId`, + description: msg`ViewId`, + icon: 'IconView', }) @WorkspaceIsNullable() - view: Relation | null; - - @WorkspaceJoinColumn('view') viewId: string; @WorkspaceDynamicRelation({ diff --git a/packages/twenty-server/src/modules/view/standard-objects/view.workspace-entity.ts b/packages/twenty-server/src/modules/view/standard-objects/view.workspace-entity.ts index cadd69d03f..477a5c717c 100644 --- a/packages/twenty-server/src/modules/view/standard-objects/view.workspace-entity.ts +++ b/packages/twenty-server/src/modules/view/standard-objects/view.workspace-entity.ts @@ -17,7 +17,6 @@ import { WorkspaceRelation } from 'src/engine/twenty-orm/decorators/workspace-re import { VIEW_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-icons'; import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; -import { FavoriteWorkspaceEntity } from 'src/modules/favorite/standard-objects/favorite.workspace-entity'; import { ViewFieldWorkspaceEntity } from 'src/modules/view/standard-objects/view-field.workspace-entity'; import { ViewFilterGroupWorkspaceEntity } from 'src/modules/view/standard-objects/view-filter-group.workspace-entity'; import { ViewFilterWorkspaceEntity } from 'src/modules/view/standard-objects/view-filter.workspace-entity'; @@ -201,18 +200,6 @@ export class ViewWorkspaceEntity extends BaseWorkspaceEntity { @WorkspaceIsNullable() viewSorts: Relation; - @WorkspaceRelation({ - standardId: VIEW_STANDARD_FIELD_IDS.favorites, - type: RelationType.ONE_TO_MANY, - label: msg`Favorites`, - description: msg`Favorites linked to the view`, - icon: 'IconHeart', - inverseSideTarget: () => FavoriteWorkspaceEntity, - onDelete: RelationOnDeleteAction.CASCADE, - }) - @WorkspaceIsSystem() - favorites: Relation; - @WorkspaceField({ standardId: VIEW_STANDARD_FIELD_IDS.kanbanAggregateOperation, type: FieldMetadataType.SELECT,