mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-23 03:51:36 +03:00
Rename enum types when tables are renamed (#8794)
## Context Enum are named after this pattern `${schema}_${tableName}_${columnName}_enum` however now that we allowed table name update, we need to make sure their enums are renamed as well.
This commit is contained in:
parent
fbe042db3b
commit
75f5afb968
@ -443,14 +443,17 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
objectMetadataForUpdate.workspaceId,
|
||||
);
|
||||
|
||||
await this.objectMetadataMigrationService.recomputeEnumNames(
|
||||
objectMetadataForUpdate,
|
||||
objectMetadataForUpdate.workspaceId,
|
||||
);
|
||||
|
||||
const recomputedIndexes =
|
||||
await this.indexMetadataService.recomputeIndexMetadataForObject(
|
||||
objectMetadataForUpdate.workspaceId,
|
||||
objectMetadataForUpdate,
|
||||
);
|
||||
|
||||
// TODO: recompute foreign keys indexes as well (in the related object and not objectMetadataForUpdate)
|
||||
|
||||
await this.indexMetadataService.createIndexRecomputeMigrations(
|
||||
objectMetadataForUpdate.workspaceId,
|
||||
objectMetadataForUpdate,
|
||||
|
@ -1,9 +1,12 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
import { In, Repository } from 'typeorm';
|
||||
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import {
|
||||
FieldMetadataEntity,
|
||||
FieldMetadataType,
|
||||
} 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 { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { buildMigrationsForCustomObjectRelations } from 'src/engine/metadata-modules/object-metadata/utils/build-migrations-for-custom-object-relations.util';
|
||||
@ -264,4 +267,43 @@ export class ObjectMetadataMigrationService {
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
public async recomputeEnumNames(
|
||||
updatedObjectMetadata: ObjectMetadataEntity,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const fieldMetadataToUpdate = await this.fieldMetadataRepository.find({
|
||||
where: {
|
||||
objectMetadataId: updatedObjectMetadata.id,
|
||||
workspaceId,
|
||||
type: In([
|
||||
FieldMetadataType.SELECT,
|
||||
FieldMetadataType.MULTI_SELECT,
|
||||
FieldMetadataType.RATING,
|
||||
FieldMetadataType.ACTOR,
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
for (const fieldMetadata of fieldMetadataToUpdate) {
|
||||
await this.workspaceMigrationService.createCustomMigration(
|
||||
generateMigrationName(`update-${fieldMetadata.name}-enum-name`),
|
||||
workspaceId,
|
||||
[
|
||||
{
|
||||
name: computeTableName(
|
||||
updatedObjectMetadata.nameSingular,
|
||||
updatedObjectMetadata.isCustom,
|
||||
),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: this.workspaceMigrationFactory.createColumnActions(
|
||||
WorkspaceMigrationColumnActionType.ALTER,
|
||||
fieldMetadata,
|
||||
fieldMetadata,
|
||||
),
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,4 +12,5 @@ export enum WorkspaceMigrationExceptionCode {
|
||||
INVALID_ACTION = 'INVALID_ACTION',
|
||||
INVALID_FIELD_METADATA = 'INVALID_FIELD_METADATA',
|
||||
INVALID_COMPOSITE_TYPE = 'INVALID_COMPOSITE_TYPE',
|
||||
ENUM_TYPE_NAME_NOT_FOUND = 'ENUM_TYPE_NAME_NOT_FOUND',
|
||||
}
|
||||
|
@ -0,0 +1,101 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { DataSource, QueryRunner, TableColumn } from 'typeorm';
|
||||
|
||||
import {
|
||||
WorkspaceMigrationColumnActionType,
|
||||
WorkspaceMigrationColumnAlter,
|
||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||
import { WorkspaceMigrationEnumService } from 'src/engine/workspace-manager/workspace-migration-runner/services/workspace-migration-enum.service';
|
||||
|
||||
describe('WorkspaceMigrationEnumService', () => {
|
||||
let service: WorkspaceMigrationEnumService;
|
||||
let queryRunner: QueryRunner;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
WorkspaceMigrationEnumService,
|
||||
{
|
||||
provide: DataSource,
|
||||
useValue: {
|
||||
createQueryRunner: () => ({
|
||||
query: jest.fn(),
|
||||
addColumn: jest.fn(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<WorkspaceMigrationEnumService>(
|
||||
WorkspaceMigrationEnumService,
|
||||
);
|
||||
queryRunner = module.get(DataSource).createQueryRunner();
|
||||
});
|
||||
|
||||
describe('alterEnum', () => {
|
||||
it('should handle enum column alteration with renamed values', async () => {
|
||||
const mockMigrationColumn: WorkspaceMigrationColumnAlter = {
|
||||
action: WorkspaceMigrationColumnActionType.ALTER,
|
||||
currentColumnDefinition: {
|
||||
columnName: 'status',
|
||||
columnType: 'enum',
|
||||
enum: ['ACTIVE', 'INACTIVE'],
|
||||
isNullable: false,
|
||||
defaultValue: 'ACTIVE',
|
||||
},
|
||||
alteredColumnDefinition: {
|
||||
columnName: 'status',
|
||||
columnType: 'enum',
|
||||
enum: [
|
||||
{ from: 'ACTIVE', to: 'ENABLED' },
|
||||
{ from: 'INACTIVE', to: 'DISABLED' },
|
||||
],
|
||||
isNullable: false,
|
||||
defaultValue: 'ENABLED',
|
||||
},
|
||||
};
|
||||
|
||||
jest.spyOn(queryRunner, 'query').mockImplementation((query: string) => {
|
||||
if (query.includes('information_schema.columns')) {
|
||||
return Promise.resolve([{ udt_name: 'test_status_enum' }]);
|
||||
}
|
||||
if (query.includes('SELECT id')) {
|
||||
return Promise.resolve([
|
||||
{ id: '1', status: 'ACTIVE' },
|
||||
{ id: '2', status: 'INACTIVE' },
|
||||
]);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
await service.alterEnum(
|
||||
queryRunner,
|
||||
'test_schema',
|
||||
'test_table',
|
||||
mockMigrationColumn,
|
||||
);
|
||||
|
||||
expect(queryRunner.query).toHaveBeenCalledWith(
|
||||
expect.stringContaining('information_schema.columns'),
|
||||
['test_schema', 'test_table', 'status'],
|
||||
);
|
||||
expect(queryRunner.query).toHaveBeenCalledWith(
|
||||
expect.stringContaining('ALTER TYPE'),
|
||||
);
|
||||
expect(queryRunner.addColumn).toHaveBeenCalledWith(
|
||||
'test_schema.test_table',
|
||||
new TableColumn({
|
||||
name: 'status',
|
||||
type: 'enum',
|
||||
enum: ['ENABLED', 'DISABLED'],
|
||||
isNullable: false,
|
||||
default: 'ENABLED',
|
||||
enumName: 'test_table_status_enum',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
@ -10,6 +10,10 @@ import {
|
||||
WorkspaceMigrationColumnAlter,
|
||||
WorkspaceMigrationRenamedEnum,
|
||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||
import {
|
||||
WorkspaceMigrationException,
|
||||
WorkspaceMigrationExceptionCode,
|
||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.exception';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationEnumService {
|
||||
@ -33,8 +37,14 @@ export class WorkspaceMigrationEnumService {
|
||||
);
|
||||
}
|
||||
|
||||
const oldEnumTypeName = await this.getEnumTypeName(
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableName,
|
||||
migrationColumn.currentColumnDefinition.columnName,
|
||||
);
|
||||
|
||||
const columnDefinition = migrationColumn.alteredColumnDefinition;
|
||||
const oldEnumTypeName = `${tableName}_${migrationColumn.currentColumnDefinition.columnName}_enum`;
|
||||
const tempEnumTypeName = `${oldEnumTypeName}_temp`;
|
||||
const newEnumTypeName = `${tableName}_${columnDefinition.columnName}_enum`;
|
||||
const enumValues =
|
||||
@ -214,4 +224,27 @@ export class WorkspaceMigrationEnumService {
|
||||
RENAME TO "${newEnumTypeName}"
|
||||
`);
|
||||
}
|
||||
|
||||
private async getEnumTypeName(
|
||||
queryRunner: QueryRunner,
|
||||
schemaName: string,
|
||||
tableName: string,
|
||||
columnName: string,
|
||||
): Promise<string> {
|
||||
const result = await queryRunner.query(
|
||||
`SELECT udt_name FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 AND column_name = $3`,
|
||||
[schemaName, tableName, columnName],
|
||||
);
|
||||
|
||||
const enumTypeName = result[0].udt_name;
|
||||
|
||||
if (!enumTypeName) {
|
||||
throw new WorkspaceMigrationException(
|
||||
`Enum type name not found for column ${columnName} in table ${tableName} while trying to alter enum`,
|
||||
WorkspaceMigrationExceptionCode.ENUM_TYPE_NAME_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return enumTypeName;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user