From 381bf0fc8db3ae2fd58db4c158586d2410047527 Mon Sep 17 00:00:00 2001 From: Weiko Date: Fri, 3 May 2024 19:05:56 +0200 Subject: [PATCH] Create convert record positions to integers command (#5287) ## Context Positions are used within a view to display and sort the different records of standard/custom object. When we add a new record and want to put it before the existing first record, we have to use float values to insert them in the DB and respect the desired order. We are adding a new command that can be executed to flatten those positions. --------- Co-authored-by: bosiraphael --- ...rt-record-positions-to-integers.command.ts | 128 ++++++++++++++++++ ...workspace-sync-metadata-commands.module.ts | 4 + 2 files changed, 132 insertions(+) create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/convert-record-positions-to-integers.command.ts diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/convert-record-positions-to-integers.command.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/convert-record-positions-to-integers.command.ts new file mode 100644 index 000000000..0c5e0b9a5 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/convert-record-positions-to-integers.command.ts @@ -0,0 +1,128 @@ +import { Logger } from '@nestjs/common'; +import { InjectDataSource } from '@nestjs/typeorm'; + +import { Command, CommandRunner, Option } from 'nest-commander'; +import { DataSource } from 'typeorm'; + +import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; + +interface RunCommandOptions { + workspaceId?: string; +} + +@Command({ + name: 'workspace:convert-record-positions-to-integers', + description: 'Convert record positions to integers', +}) +export class ConvertRecordPositionsToIntegers extends CommandRunner { + private readonly logger = new Logger(ConvertRecordPositionsToIntegers.name); + + constructor( + @InjectDataSource('metadata') + private readonly metadataDataSource: DataSource, + private readonly workspaceDataSourceService: WorkspaceDataSourceService, + ) { + super(); + } + + async run(_passedParam: string[], options: RunCommandOptions): Promise { + const queryRunner = this.metadataDataSource.createQueryRunner(); + const workspaceId = options.workspaceId; + + if (!workspaceId || typeof workspaceId !== 'string') { + this.logger.error('Workspace id is required'); + + return; + } + + const customObjectMetadataCollection = await this.metadataDataSource + .getRepository(ObjectMetadataEntity) + .findBy({ + workspaceId, + isCustom: true, + }); + + const customObjectTableNames = customObjectMetadataCollection.map( + (metadata) => metadata.nameSingular, + ); + + await queryRunner.connect(); + await queryRunner.startTransaction(); + + const transactionManager = queryRunner.manager; + + this.logger.log('Converting record positions to integers'); + + try { + await this.convertRecordPositionsToIntegers( + customObjectTableNames, + workspaceId, + transactionManager, + ); + + await queryRunner.commitTransaction(); + } catch (error) { + await queryRunner.rollbackTransaction(); + this.logger.error('Error converting record positions to integers', error); + } finally { + await queryRunner.release(); + this.logger.log('Record positions converted to integers'); + } + } + + private async convertRecordPositionsToIntegers( + customObjectTableNames: string[], + workspaceId: string, + transactionManager: any, + ): Promise { + const dataSourceSchema = + this.workspaceDataSourceService.getSchemaName(workspaceId); + + for (const tableName of ['company', 'person', 'opportunity']) { + await this.convertRecordPositionsToIntegersForTable( + tableName, + dataSourceSchema, + workspaceId, + transactionManager, + ); + } + + for (const tableName of customObjectTableNames) { + await this.convertRecordPositionsToIntegersForTable( + `_${tableName}`, + dataSourceSchema, + workspaceId, + transactionManager, + ); + } + } + + private async convertRecordPositionsToIntegersForTable( + tableName: string, + dataSourceSchema: string, + workspaceId: string, + transactionManager: any, + ): Promise { + await this.workspaceDataSourceService.executeRawQuery( + `UPDATE ${dataSourceSchema}.${tableName} SET position = subquery.position + FROM ( + SELECT id, ROW_NUMBER() OVER (ORDER BY position) as position + FROM ${dataSourceSchema}.${tableName} + ) as subquery + WHERE ${dataSourceSchema}.${tableName}.id = subquery.id`, + [], + workspaceId, + transactionManager, + ); + } + + @Option({ + flags: '-w, --workspace-id [workspace_id]', + description: 'workspace id', + required: true, + }) + parseWorkspaceId(value: string): string { + return value; + } +} diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module.ts index c9fc90423..8e30f0c41 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/workspace-sync-metadata-commands.module.ts @@ -5,6 +5,8 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp import { WorkspaceHealthModule } from 'src/engine/workspace-manager/workspace-health/workspace-health.module'; import { WorkspaceModule } from 'src/engine/core-modules/workspace/workspace.module'; import { AddStandardIdCommand } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/add-standard-id.command'; +import { ConvertRecordPositionsToIntegers } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/convert-record-positions-to-integers.command'; +import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; import { SyncWorkspaceMetadataCommand } from './sync-workspace-metadata.command'; @@ -16,10 +18,12 @@ import { SyncWorkspaceLoggerService } from './services/sync-workspace-logger.ser WorkspaceHealthModule, WorkspaceModule, DataSourceModule, + WorkspaceDataSourceModule, ], providers: [ SyncWorkspaceMetadataCommand, AddStandardIdCommand, + ConvertRecordPositionsToIntegers, SyncWorkspaceLoggerService, ], })