diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-20/1-20-backfill-select-field-option-ids.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-20/1-20-backfill-select-field-option-ids.command.ts new file mode 100644 index 0000000000..ac47652464 --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-20/1-20-backfill-select-field-option-ids.command.ts @@ -0,0 +1,99 @@ +import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; + +import { Command } from 'nest-commander'; +import { isDefined } from 'twenty-shared/utils'; +import { v4 } from 'uuid'; +import { DataSource, Repository } from 'typeorm'; + +import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner'; +import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner'; +import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; +import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; +import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; + +@Command({ + name: 'upgrade:1-20:backfill-select-field-option-ids', + description: + 'Backfill missing ids on SELECT and MULTI_SELECT field metadata options', +}) +export class BackfillSelectFieldOptionIdsCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { + constructor( + @InjectRepository(WorkspaceEntity) + protected readonly workspaceRepository: Repository, + @InjectDataSource() + private readonly coreDataSource: DataSource, + protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, + protected readonly dataSourceService: DataSourceService, + ) { + super(workspaceRepository, twentyORMGlobalManager, dataSourceService); + } + + override async runOnWorkspace({ + workspaceId, + options, + }: RunOnWorkspaceArgs): Promise { + const dryRun = options?.dryRun ?? false; + + const selectFields: { id: string; options: Record[] }[] = + await this.coreDataSource.query( + `SELECT "id", "options" + FROM core."fieldMetadata" + WHERE "workspaceId" = $1 + AND "type" IN ('SELECT', 'MULTI_SELECT') + AND "options" IS NOT NULL`, + [workspaceId], + ); + + const fieldsToUpdate = selectFields.filter((field) => + field.options.some((option) => !isDefined(option.id)), + ); + + if (fieldsToUpdate.length === 0) { + this.logger.log( + `No SELECT/MULTI_SELECT options missing ids in workspace ${workspaceId}`, + ); + + return; + } + + this.logger.log( + `${dryRun ? '[DRY RUN] ' : ''}Found ${fieldsToUpdate.length} field(s) with options missing ids in workspace ${workspaceId}`, + ); + + if (dryRun) { + return; + } + + const queryRunner = this.coreDataSource.createQueryRunner(); + + await queryRunner.connect(); + await queryRunner.startTransaction(); + + try { + for (const field of fieldsToUpdate) { + const patchedOptions = field.options.map((option) => ({ + ...option, + id: isDefined(option.id) ? option.id : v4(), + })); + + await queryRunner.query( + `UPDATE core."fieldMetadata" + SET "options" = $1::jsonb + WHERE "id" = $2`, + [JSON.stringify(patchedOptions), field.id], + ); + } + + await queryRunner.commitTransaction(); + + this.logger.log( + `Backfilled option ids for ${fieldsToUpdate.length} field(s) in workspace ${workspaceId}`, + ); + } catch (error) { + await queryRunner.rollbackTransaction(); + throw error; + } finally { + await queryRunner.release(); + } + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-20/1-20-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-20/1-20-upgrade-version-command.module.ts index f605a10cf6..6cacd6d11c 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-20/1-20-upgrade-version-command.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-20/1-20-upgrade-version-command.module.ts @@ -4,6 +4,7 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { BackfillCommandMenuItemsCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-backfill-command-menu-items.command'; import { BackfillNavigationMenuItemTypeCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-backfill-navigation-menu-item-type.command'; import { BackfillPageLayoutsCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-backfill-page-layouts.command'; +import { BackfillSelectFieldOptionIdsCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-backfill-select-field-option-ids.command'; import { IdentifyObjectPermissionMetadataCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-identify-object-permission-metadata.command'; import { IdentifyPermissionFlagMetadataCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-identify-permission-flag-metadata.command'; import { MakeObjectPermissionUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-make-object-permission-universal-identifier-and-application-id-not-nullable-migration.command'; @@ -55,6 +56,7 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace BackfillCommandMenuItemsCommand, BackfillNavigationMenuItemTypeCommand, BackfillPageLayoutsCommand, + BackfillSelectFieldOptionIdsCommand, SeedCliApplicationRegistrationCommand, MigrateRichTextToTextCommand, MigrateMessagingInfrastructureToMetadataCommand, @@ -67,6 +69,7 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace BackfillCommandMenuItemsCommand, BackfillNavigationMenuItemTypeCommand, BackfillPageLayoutsCommand, + BackfillSelectFieldOptionIdsCommand, SeedCliApplicationRegistrationCommand, MigrateRichTextToTextCommand, MigrateMessagingInfrastructureToMetadataCommand, 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 0c9202120f..65afacbc20 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 @@ -36,6 +36,7 @@ import { SeedServerIdCommand } from 'src/database/commands/upgrade-version-comma import { BackfillCommandMenuItemsCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-backfill-command-menu-items.command'; import { BackfillNavigationMenuItemTypeCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-backfill-navigation-menu-item-type.command'; import { BackfillPageLayoutsCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-backfill-page-layouts.command'; +import { BackfillSelectFieldOptionIdsCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-backfill-select-field-option-ids.command'; import { IdentifyObjectPermissionMetadataCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-identify-object-permission-metadata.command'; import { IdentifyPermissionFlagMetadataCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-identify-permission-flag-metadata.command'; import { MakeObjectPermissionUniversalIdentifierAndApplicationIdNotNullableMigrationCommand } from 'src/database/commands/upgrade-version-command/1-20/1-20-make-object-permission-universal-identifier-and-application-id-not-nullable-migration.command'; @@ -103,6 +104,7 @@ export class UpgradeCommand extends UpgradeCommandRunner { protected readonly seedCliApplicationRegistrationCommand: SeedCliApplicationRegistrationCommand, protected readonly migrateRichTextToTextCommand: MigrateRichTextToTextCommand, protected readonly migrateMessagingInfrastructureToMetadataCommand: MigrateMessagingInfrastructureToMetadataCommand, + protected readonly backfillSelectFieldOptionIdsCommand: BackfillSelectFieldOptionIdsCommand, ) { super( workspaceRepository, @@ -162,6 +164,7 @@ export class UpgradeCommand extends UpgradeCommandRunner { this.backfillPageLayoutsCommand, this.seedCliApplicationRegistrationCommand, this.migrateMessagingInfrastructureToMetadataCommand, + this.backfillSelectFieldOptionIdsCommand, ]; this.allCommands = {