Add fix schema array type command (#12887)
Following this fix https://github.com/twentyhq/twenty/pull/12874 we now need to add a command to retroactively fix existing columns --------- Co-authored-by: Paul Rastoin <45004772+prastoin@users.noreply.github.com> Co-authored-by: prastoin <paul@twenty.com>
This commit is contained in:
@ -0,0 +1,102 @@
|
|||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { Command } from 'nest-commander';
|
||||||
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
|
||||||
|
RunOnWorkspaceArgs,
|
||||||
|
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
|
||||||
|
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||||
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
|
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
|
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||||
|
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||||
|
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||||
|
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||||
|
import { DatabaseStructureService } from 'src/engine/workspace-manager/workspace-health/services/database-structure.service';
|
||||||
|
|
||||||
|
@Command({
|
||||||
|
name: 'upgrade:1-1:fix-schema-array-type',
|
||||||
|
description: 'Fix columns for ARRAY fields to be text[] in the DB schema',
|
||||||
|
})
|
||||||
|
export class FixSchemaArrayTypeCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(Workspace, 'core')
|
||||||
|
protected readonly workspaceRepository: Repository<Workspace>,
|
||||||
|
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||||
|
private readonly databaseStructureService: DatabaseStructureService,
|
||||||
|
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||||
|
private readonly typeORMService: TypeORMService,
|
||||||
|
@InjectRepository(FieldMetadataEntity, 'core')
|
||||||
|
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||||
|
) {
|
||||||
|
super(workspaceRepository, twentyORMGlobalManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
override async runOnWorkspace({
|
||||||
|
workspaceId,
|
||||||
|
options,
|
||||||
|
}: RunOnWorkspaceArgs): Promise<void> {
|
||||||
|
this.logger.log(`Fixing ARRAY field columns for workspace ${workspaceId}`);
|
||||||
|
|
||||||
|
const arrayFields: FieldMetadataEntity[] =
|
||||||
|
await this.fieldMetadataRepository.find({
|
||||||
|
where: {
|
||||||
|
workspaceId,
|
||||||
|
type: FieldMetadataType.ARRAY,
|
||||||
|
isCustom: true,
|
||||||
|
},
|
||||||
|
relations: ['object'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (arrayFields.length === 0) {
|
||||||
|
this.logger.log('No ARRAY fields found for this workspace.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const field of arrayFields) {
|
||||||
|
const object = field.object;
|
||||||
|
|
||||||
|
const tableName = computeObjectTargetTable(object);
|
||||||
|
const schemaName =
|
||||||
|
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||||
|
const columns =
|
||||||
|
await this.databaseStructureService.getWorkspaceTableColumns(
|
||||||
|
schemaName,
|
||||||
|
tableName,
|
||||||
|
);
|
||||||
|
const columnName = computeColumnName(field);
|
||||||
|
const dbColumn = columns.find((col) => col.columnName === columnName);
|
||||||
|
|
||||||
|
if (!dbColumn) {
|
||||||
|
this.logger.warn(
|
||||||
|
`Column ${columnName} not found in table ${schemaName}.${tableName}`,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (dbColumn.dataType === 'text[]' && dbColumn.isArray) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this.logger.log(
|
||||||
|
`Altering column ${schemaName}.${tableName}.${columnName} to type text[] (was ${dbColumn.dataType})`,
|
||||||
|
);
|
||||||
|
if (!options.dryRun) {
|
||||||
|
const queryRunner = this.typeORMService
|
||||||
|
.getMainDataSource()
|
||||||
|
.createQueryRunner();
|
||||||
|
|
||||||
|
await queryRunner.connect();
|
||||||
|
try {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "${schemaName}"."${tableName}" ALTER COLUMN "${columnName}" TYPE text[] USING "${columnName}"::text[];`,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
await queryRunner.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,9 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { FixSchemaArrayTypeCommand } from 'src/database/commands/upgrade-version-command/1-1/1-1-fix-schema-array-type.command';
|
||||||
import { FixUpdateStandardFieldsIsLabelSyncedWithName } from 'src/database/commands/upgrade-version-command/1-1/1-1-fix-update-standard-field-is-label-synced-with-name.command';
|
import { FixUpdateStandardFieldsIsLabelSyncedWithName } from 'src/database/commands/upgrade-version-command/1-1/1-1-fix-update-standard-field-is-label-synced-with-name.command';
|
||||||
|
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
||||||
import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity';
|
import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity';
|
||||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||||
@ -10,6 +12,7 @@ import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/
|
|||||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-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 { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
|
||||||
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||||
|
import { WorkspaceHealthModule } from 'src/engine/workspace-manager/workspace-health/workspace-health.module';
|
||||||
import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module';
|
import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
@ -28,8 +31,16 @@ import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/wor
|
|||||||
WorkspaceDataSourceModule,
|
WorkspaceDataSourceModule,
|
||||||
WorkspaceMigrationRunnerModule,
|
WorkspaceMigrationRunnerModule,
|
||||||
WorkspaceMetadataVersionModule,
|
WorkspaceMetadataVersionModule,
|
||||||
|
WorkspaceHealthModule,
|
||||||
|
TypeORMModule,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
FixUpdateStandardFieldsIsLabelSyncedWithName,
|
||||||
|
FixSchemaArrayTypeCommand,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
FixUpdateStandardFieldsIsLabelSyncedWithName,
|
||||||
|
FixSchemaArrayTypeCommand,
|
||||||
],
|
],
|
||||||
providers: [FixUpdateStandardFieldsIsLabelSyncedWithName],
|
|
||||||
exports: [FixUpdateStandardFieldsIsLabelSyncedWithName],
|
|
||||||
})
|
})
|
||||||
export class V1_1_UpgradeVersionCommandModule {}
|
export class V1_1_UpgradeVersionCommandModule {}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import { FixStandardSelectFieldsPositionCommand } from 'src/database/commands/up
|
|||||||
import { LowercaseUserAndInvitationEmailsCommand } from 'src/database/commands/upgrade-version-command/0-54/0-54-lowercase-user-and-invitation-emails.command';
|
import { LowercaseUserAndInvitationEmailsCommand } from 'src/database/commands/upgrade-version-command/0-54/0-54-lowercase-user-and-invitation-emails.command';
|
||||||
import { MigrateDefaultAvatarUrlToUserWorkspaceCommand } from 'src/database/commands/upgrade-version-command/0-54/0-54-migrate-default-avatar-url-to-user-workspace.command';
|
import { MigrateDefaultAvatarUrlToUserWorkspaceCommand } from 'src/database/commands/upgrade-version-command/0-54/0-54-migrate-default-avatar-url-to-user-workspace.command';
|
||||||
import { DeduplicateIndexedFieldsCommand } from 'src/database/commands/upgrade-version-command/0-55/0-55-deduplicate-indexed-fields.command';
|
import { DeduplicateIndexedFieldsCommand } from 'src/database/commands/upgrade-version-command/0-55/0-55-deduplicate-indexed-fields.command';
|
||||||
|
import { FixSchemaArrayTypeCommand } from 'src/database/commands/upgrade-version-command/1-1/1-1-fix-schema-array-type.command';
|
||||||
import { FixUpdateStandardFieldsIsLabelSyncedWithName } from 'src/database/commands/upgrade-version-command/1-1/1-1-fix-update-standard-field-is-label-synced-with-name.command';
|
import { FixUpdateStandardFieldsIsLabelSyncedWithName } from 'src/database/commands/upgrade-version-command/1-1/1-1-fix-update-standard-field-is-label-synced-with-name.command';
|
||||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
@ -137,7 +138,8 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
|||||||
// 0.55 Commands
|
// 0.55 Commands
|
||||||
protected readonly deduplicateIndexedFieldsCommand: DeduplicateIndexedFieldsCommand,
|
protected readonly deduplicateIndexedFieldsCommand: DeduplicateIndexedFieldsCommand,
|
||||||
|
|
||||||
// 1.1 Commands
|
// 1.1 Commands
|
||||||
|
protected readonly fixSchemaArrayTypeCommand: FixSchemaArrayTypeCommand,
|
||||||
protected readonly fixUpdateStandardFieldsIsLabelSyncedWithNameCommand: FixUpdateStandardFieldsIsLabelSyncedWithName,
|
protected readonly fixUpdateStandardFieldsIsLabelSyncedWithNameCommand: FixUpdateStandardFieldsIsLabelSyncedWithName,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
@ -182,6 +184,7 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
|||||||
const commands_110: VersionCommands = {
|
const commands_110: VersionCommands = {
|
||||||
beforeSyncMetadata: [
|
beforeSyncMetadata: [
|
||||||
this.fixUpdateStandardFieldsIsLabelSyncedWithNameCommand,
|
this.fixUpdateStandardFieldsIsLabelSyncedWithNameCommand,
|
||||||
|
this.fixSchemaArrayTypeCommand
|
||||||
],
|
],
|
||||||
afterSyncMetadata: [],
|
afterSyncMetadata: [],
|
||||||
};
|
};
|
||||||
|
|||||||
@ -32,6 +32,6 @@ import { WorkspaceFixService } from './services/workspace-fix.service';
|
|||||||
FieldMetadataHealthService,
|
FieldMetadataHealthService,
|
||||||
WorkspaceFixService,
|
WorkspaceFixService,
|
||||||
],
|
],
|
||||||
exports: [WorkspaceHealthService],
|
exports: [WorkspaceHealthService, DatabaseStructureService],
|
||||||
})
|
})
|
||||||
export class WorkspaceHealthModule {}
|
export class WorkspaceHealthModule {}
|
||||||
|
|||||||
Reference in New Issue
Block a user