Fix/enum validation (#2863)
* fix: SELECT enum can have a color key * fix: "findOneOrFail" of undefined * feat: alter column migration store previous metadata informations * fix: enum validation extra keys
This commit is contained in:
@ -26,7 +26,6 @@ const bootstrap = async () => {
|
|||||||
// Apply validation pipes globally
|
// Apply validation pipes globally
|
||||||
app.useGlobalPipes(
|
app.useGlobalPipes(
|
||||||
new ValidationPipe({
|
new ValidationPipe({
|
||||||
// whitelist: true,
|
|
||||||
transform: true,
|
transform: true,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
|||||||
import { IsFieldMetadataDefaultValue } from 'src/metadata/field-metadata/validators/is-field-metadata-default-value.validator';
|
import { IsFieldMetadataDefaultValue } from 'src/metadata/field-metadata/validators/is-field-metadata-default-value.validator';
|
||||||
import { FieldMetadataResolver } from 'src/metadata/field-metadata/field-metadata.resolver';
|
import { FieldMetadataResolver } from 'src/metadata/field-metadata/field-metadata.resolver';
|
||||||
import { FieldMetadataDTO } from 'src/metadata/field-metadata/dtos/field-metadata.dto';
|
import { FieldMetadataDTO } from 'src/metadata/field-metadata/dtos/field-metadata.dto';
|
||||||
|
import { IsFieldMetadataOptions } from 'src/metadata/field-metadata/validators/is-field-metadata-options.validator';
|
||||||
|
|
||||||
import { FieldMetadataService } from './field-metadata.service';
|
import { FieldMetadataService } from './field-metadata.service';
|
||||||
import { FieldMetadataEntity } from './field-metadata.entity';
|
import { FieldMetadataEntity } from './field-metadata.entity';
|
||||||
@ -65,6 +66,7 @@ import { UpdateFieldInput } from './dtos/update-field.input';
|
|||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
IsFieldMetadataDefaultValue,
|
IsFieldMetadataDefaultValue,
|
||||||
|
IsFieldMetadataOptions,
|
||||||
FieldMetadataService,
|
FieldMetadataService,
|
||||||
FieldMetadataResolver,
|
FieldMetadataResolver,
|
||||||
],
|
],
|
||||||
|
|||||||
@ -57,7 +57,13 @@ export const validateDefaultValueForType = (
|
|||||||
FieldMetadataDefaultValue
|
FieldMetadataDefaultValue
|
||||||
>(validator, defaultValue);
|
>(validator, defaultValue);
|
||||||
|
|
||||||
return validateSync(defaultValueInstance).length === 0;
|
return (
|
||||||
|
validateSync(defaultValueInstance, {
|
||||||
|
whitelist: true,
|
||||||
|
forbidNonWhitelisted: true,
|
||||||
|
forbidUnknownValues: true,
|
||||||
|
}).length === 0
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return isValid;
|
return isValid;
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import {
|
|||||||
|
|
||||||
export const optionsValidatorsMap = {
|
export const optionsValidatorsMap = {
|
||||||
[FieldMetadataType.RATING]: [FieldMetadataDefaultOptions],
|
[FieldMetadataType.RATING]: [FieldMetadataDefaultOptions],
|
||||||
[FieldMetadataType.SELECT]: [FieldMetadataDefaultOptions],
|
[FieldMetadataType.SELECT]: [FieldMetadataComplexOptions],
|
||||||
[FieldMetadataType.MULTI_SELECT]: [FieldMetadataComplexOptions],
|
[FieldMetadataType.MULTI_SELECT]: [FieldMetadataComplexOptions],
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -31,12 +31,18 @@ export const validateOptionsForType = (
|
|||||||
|
|
||||||
const isValid = options.every((option) => {
|
const isValid = options.every((option) => {
|
||||||
return validators.some((validator) => {
|
return validators.some((validator) => {
|
||||||
const optionsInstance = plainToInstance<any, FieldMetadataDefaultOptions>(
|
const optionsInstance = plainToInstance<
|
||||||
validator,
|
any,
|
||||||
option,
|
FieldMetadataDefaultOptions | FieldMetadataComplexOptions
|
||||||
);
|
>(validator, option);
|
||||||
|
|
||||||
return validateSync(optionsInstance).length === 0;
|
return (
|
||||||
|
validateSync(optionsInstance, {
|
||||||
|
whitelist: true,
|
||||||
|
forbidNonWhitelisted: true,
|
||||||
|
forbidUnknownValues: true,
|
||||||
|
}).length === 0
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,10 @@ import { ValidationArguments, ValidatorConstraint } from 'class-validator';
|
|||||||
import { FieldMetadataOptions } from 'src/metadata/field-metadata/interfaces/field-metadata-options.interface';
|
import { FieldMetadataOptions } from 'src/metadata/field-metadata/interfaces/field-metadata-options.interface';
|
||||||
|
|
||||||
import { FieldMetadataService } from 'src/metadata/field-metadata/field-metadata.service';
|
import { FieldMetadataService } from 'src/metadata/field-metadata/field-metadata.service';
|
||||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
import {
|
||||||
|
FieldMetadataEntity,
|
||||||
|
FieldMetadataType,
|
||||||
|
} from 'src/metadata/field-metadata/field-metadata.entity';
|
||||||
import { validateOptionsForType } from 'src/metadata/field-metadata/utils/validate-options-for-type.util';
|
import { validateOptionsForType } from 'src/metadata/field-metadata/utils/validate-options-for-type.util';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -28,7 +31,13 @@ export class IsFieldMetadataOptions {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fieldMetadata = await this.fieldMetadataService.findOneOrFail(id);
|
let fieldMetadata: FieldMetadataEntity;
|
||||||
|
|
||||||
|
try {
|
||||||
|
fieldMetadata = await this.fieldMetadataService.findOneOrFail(id);
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
type = fieldMetadata.type;
|
type = fieldMetadata.type;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,21 +47,31 @@ export class BasicColumnActionFactory extends ColumnActionAbstractFactory<BasicF
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected handleAlterAction(
|
protected handleAlterAction(
|
||||||
previousFieldMetadata: FieldMetadataInterface<BasicFieldMetadataType>,
|
currentFieldMetadata: FieldMetadataInterface<BasicFieldMetadataType>,
|
||||||
nextFieldMetadata: FieldMetadataInterface<BasicFieldMetadataType>,
|
alteredFieldMetadata: FieldMetadataInterface<BasicFieldMetadataType>,
|
||||||
options?: WorkspaceColumnActionOptions,
|
options?: WorkspaceColumnActionOptions,
|
||||||
): WorkspaceMigrationColumnAlter {
|
): WorkspaceMigrationColumnAlter {
|
||||||
const defaultValue =
|
const defaultValue =
|
||||||
this.getDefaultValue(nextFieldMetadata.defaultValue) ??
|
this.getDefaultValue(alteredFieldMetadata.defaultValue) ??
|
||||||
options?.defaultValue;
|
options?.defaultValue;
|
||||||
const serializedDefaultValue = serializeDefaultValue(defaultValue);
|
const serializedDefaultValue = serializeDefaultValue(defaultValue);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
action: WorkspaceMigrationColumnActionType.ALTER,
|
action: WorkspaceMigrationColumnActionType.ALTER,
|
||||||
columnName: nextFieldMetadata.targetColumnMap.value,
|
currentColumnDefinition: {
|
||||||
columnType: fieldMetadataTypeToColumnType(nextFieldMetadata.type),
|
columnName: currentFieldMetadata.targetColumnMap.value,
|
||||||
isNullable: nextFieldMetadata.isNullable,
|
columnType: fieldMetadataTypeToColumnType(currentFieldMetadata.type),
|
||||||
defaultValue: serializedDefaultValue,
|
isNullable: currentFieldMetadata.isNullable,
|
||||||
|
defaultValue: serializeDefaultValue(
|
||||||
|
this.getDefaultValue(currentFieldMetadata.defaultValue),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
alteredColumnDefinition: {
|
||||||
|
columnName: alteredFieldMetadata.targetColumnMap.value,
|
||||||
|
columnType: fieldMetadataTypeToColumnType(alteredFieldMetadata.type),
|
||||||
|
isNullable: alteredFieldMetadata.isNullable,
|
||||||
|
defaultValue: serializedDefaultValue,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,21 +23,21 @@ export class ColumnActionAbstractFactory<
|
|||||||
action:
|
action:
|
||||||
| WorkspaceMigrationColumnActionType.CREATE
|
| WorkspaceMigrationColumnActionType.CREATE
|
||||||
| WorkspaceMigrationColumnActionType.ALTER,
|
| WorkspaceMigrationColumnActionType.ALTER,
|
||||||
previousFieldMetadata: FieldMetadataInterface<T> | undefined,
|
currentFieldMetadata: FieldMetadataInterface<T> | undefined,
|
||||||
nextFieldMetadata: FieldMetadataInterface<T>,
|
alteredFieldMetadata: FieldMetadataInterface<T>,
|
||||||
options?: WorkspaceColumnActionOptions,
|
options?: WorkspaceColumnActionOptions,
|
||||||
): WorkspaceMigrationColumnAction {
|
): WorkspaceMigrationColumnAction {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case WorkspaceMigrationColumnActionType.CREATE:
|
case WorkspaceMigrationColumnActionType.CREATE:
|
||||||
return this.handleCreateAction(nextFieldMetadata, options);
|
return this.handleCreateAction(alteredFieldMetadata, options);
|
||||||
case WorkspaceMigrationColumnActionType.ALTER: {
|
case WorkspaceMigrationColumnActionType.ALTER: {
|
||||||
if (!previousFieldMetadata) {
|
if (!currentFieldMetadata) {
|
||||||
throw new Error('Previous field metadata is required for alter');
|
throw new Error('current field metadata is required for alter');
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.handleAlterAction(
|
return this.handleAlterAction(
|
||||||
previousFieldMetadata,
|
currentFieldMetadata,
|
||||||
nextFieldMetadata,
|
alteredFieldMetadata,
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -57,8 +57,8 @@ export class ColumnActionAbstractFactory<
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected handleAlterAction(
|
protected handleAlterAction(
|
||||||
_previousFieldMetadata: FieldMetadataInterface<T>,
|
_currentFieldMetadata: FieldMetadataInterface<T>,
|
||||||
_nextFieldMetadata: FieldMetadataInterface<T>,
|
_alteredFieldMetadata: FieldMetadataInterface<T>,
|
||||||
_options?: WorkspaceColumnActionOptions,
|
_options?: WorkspaceColumnActionOptions,
|
||||||
): WorkspaceMigrationColumnAlter {
|
): WorkspaceMigrationColumnAlter {
|
||||||
throw new Error('handleAlterAction method not implemented.');
|
throw new Error('handleAlterAction method not implemented.');
|
||||||
|
|||||||
@ -45,24 +45,24 @@ export class EnumColumnActionFactory extends ColumnActionAbstractFactory<EnumFie
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected handleAlterAction(
|
protected handleAlterAction(
|
||||||
previousFieldMetadata: FieldMetadataInterface<EnumFieldMetadataType>,
|
currentFieldMetadata: FieldMetadataInterface<EnumFieldMetadataType>,
|
||||||
nextFieldMetadata: FieldMetadataInterface<EnumFieldMetadataType>,
|
alteredFieldMetadata: FieldMetadataInterface<EnumFieldMetadataType>,
|
||||||
options: WorkspaceColumnActionOptions,
|
options: WorkspaceColumnActionOptions,
|
||||||
): WorkspaceMigrationColumnAlter {
|
): WorkspaceMigrationColumnAlter {
|
||||||
const defaultValue =
|
const defaultValue =
|
||||||
nextFieldMetadata.defaultValue?.value ?? options?.defaultValue;
|
alteredFieldMetadata.defaultValue?.value ?? options?.defaultValue;
|
||||||
const serializedDefaultValue = serializeDefaultValue(defaultValue);
|
const serializedDefaultValue = serializeDefaultValue(defaultValue);
|
||||||
const enumOptions = nextFieldMetadata.options
|
const enumOptions = alteredFieldMetadata.options
|
||||||
? [
|
? [
|
||||||
...nextFieldMetadata.options.map((option) => {
|
...alteredFieldMetadata.options.map((option) => {
|
||||||
const previousOption = previousFieldMetadata.options?.find(
|
const currentOption = currentFieldMetadata.options?.find(
|
||||||
(previousOption) => previousOption.id === option.id,
|
(currentOption) => currentOption.id === option.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
// The id is the same, but the value is different, so we need to alter the enum
|
// The id is the same, but the value is different, so we need to alter the enum
|
||||||
if (previousOption && previousOption.value !== option.value) {
|
if (currentOption && currentOption.value !== option.value) {
|
||||||
return {
|
return {
|
||||||
from: previousOption.value,
|
from: currentOption.value,
|
||||||
to: option.value,
|
to: option.value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -74,12 +74,26 @@ export class EnumColumnActionFactory extends ColumnActionAbstractFactory<EnumFie
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
action: WorkspaceMigrationColumnActionType.ALTER,
|
action: WorkspaceMigrationColumnActionType.ALTER,
|
||||||
columnName: nextFieldMetadata.targetColumnMap.value,
|
currentColumnDefinition: {
|
||||||
columnType: fieldMetadataTypeToColumnType(nextFieldMetadata.type),
|
columnName: currentFieldMetadata.targetColumnMap.value,
|
||||||
enum: enumOptions,
|
columnType: fieldMetadataTypeToColumnType(currentFieldMetadata.type),
|
||||||
isArray: nextFieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
enum: currentFieldMetadata.options
|
||||||
isNullable: nextFieldMetadata.isNullable,
|
? [...currentFieldMetadata.options.map((option) => option.value)]
|
||||||
defaultValue: serializedDefaultValue,
|
: undefined,
|
||||||
|
isArray: currentFieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||||
|
isNullable: currentFieldMetadata.isNullable,
|
||||||
|
defaultValue: serializeDefaultValue(
|
||||||
|
currentFieldMetadata.defaultValue?.value,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
alteredColumnDefinition: {
|
||||||
|
columnName: alteredFieldMetadata.targetColumnMap.value,
|
||||||
|
columnType: fieldMetadataTypeToColumnType(alteredFieldMetadata.type),
|
||||||
|
enum: enumOptions,
|
||||||
|
isArray: alteredFieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||||
|
isNullable: alteredFieldMetadata.isNullable,
|
||||||
|
defaultValue: serializedDefaultValue,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,8 +14,8 @@ export interface WorkspaceColumnActionFactory<
|
|||||||
action:
|
action:
|
||||||
| WorkspaceMigrationColumnActionType.CREATE
|
| WorkspaceMigrationColumnActionType.CREATE
|
||||||
| WorkspaceMigrationColumnActionType.ALTER,
|
| WorkspaceMigrationColumnActionType.ALTER,
|
||||||
previousFieldMetadata: FieldMetadataInterface<T> | undefined,
|
currentFieldMetadata: FieldMetadataInterface<T> | undefined,
|
||||||
nextFieldMetadata: FieldMetadataInterface<T>,
|
alteredFieldMetadata: FieldMetadataInterface<T>,
|
||||||
options?: WorkspaceColumnActionOptions,
|
options?: WorkspaceColumnActionOptions,
|
||||||
): WorkspaceMigrationColumnAction;
|
): WorkspaceMigrationColumnAction;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,24 +13,24 @@ export enum WorkspaceMigrationColumnActionType {
|
|||||||
|
|
||||||
export type WorkspaceMigrationEnum = string | { from: string; to: string };
|
export type WorkspaceMigrationEnum = string | { from: string; to: string };
|
||||||
|
|
||||||
export type WorkspaceMigrationColumnCreate = {
|
export interface WorkspaceMigrationColumnDefinition {
|
||||||
action: WorkspaceMigrationColumnActionType.CREATE;
|
|
||||||
columnName: string;
|
columnName: string;
|
||||||
columnType: string;
|
columnType: string;
|
||||||
enum?: WorkspaceMigrationEnum[];
|
enum?: WorkspaceMigrationEnum[];
|
||||||
isArray?: boolean;
|
isArray?: boolean;
|
||||||
isNullable?: boolean;
|
isNullable?: boolean;
|
||||||
defaultValue?: any;
|
defaultValue?: any;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export interface WorkspaceMigrationColumnCreate
|
||||||
|
extends WorkspaceMigrationColumnDefinition {
|
||||||
|
action: WorkspaceMigrationColumnActionType.CREATE;
|
||||||
|
}
|
||||||
|
|
||||||
export type WorkspaceMigrationColumnAlter = {
|
export type WorkspaceMigrationColumnAlter = {
|
||||||
action: WorkspaceMigrationColumnActionType.ALTER;
|
action: WorkspaceMigrationColumnActionType.ALTER;
|
||||||
columnName: string;
|
currentColumnDefinition: WorkspaceMigrationColumnDefinition;
|
||||||
columnType: string;
|
alteredColumnDefinition: WorkspaceMigrationColumnDefinition;
|
||||||
enum?: WorkspaceMigrationEnum[];
|
|
||||||
isArray?: boolean;
|
|
||||||
isNullable?: boolean;
|
|
||||||
defaultValue?: any;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WorkspaceMigrationColumnRelation = {
|
export type WorkspaceMigrationColumnRelation = {
|
||||||
|
|||||||
@ -97,51 +97,51 @@ export class WorkspaceMigrationFactory {
|
|||||||
|
|
||||||
createColumnActions(
|
createColumnActions(
|
||||||
action: WorkspaceMigrationColumnActionType.ALTER,
|
action: WorkspaceMigrationColumnActionType.ALTER,
|
||||||
previousFieldMetadata: FieldMetadataInterface,
|
currentFieldMetadata: FieldMetadataInterface,
|
||||||
nextFieldMetadata: FieldMetadataInterface,
|
alteredFieldMetadata: FieldMetadataInterface,
|
||||||
): WorkspaceMigrationColumnAction[];
|
): WorkspaceMigrationColumnAction[];
|
||||||
|
|
||||||
createColumnActions(
|
createColumnActions(
|
||||||
action:
|
action:
|
||||||
| WorkspaceMigrationColumnActionType.CREATE
|
| WorkspaceMigrationColumnActionType.CREATE
|
||||||
| WorkspaceMigrationColumnActionType.ALTER,
|
| WorkspaceMigrationColumnActionType.ALTER,
|
||||||
fieldMetadataOrPreviousFieldMetadata: FieldMetadataInterface,
|
fieldMetadataOrCurrentFieldMetadata: FieldMetadataInterface,
|
||||||
undefinedOrnextFieldMetadata?: FieldMetadataInterface,
|
undefinedOrAlteredFieldMetadata?: FieldMetadataInterface,
|
||||||
): WorkspaceMigrationColumnAction[] {
|
): WorkspaceMigrationColumnAction[] {
|
||||||
const previousFieldMetadata =
|
const currentFieldMetadata =
|
||||||
action === WorkspaceMigrationColumnActionType.ALTER
|
action === WorkspaceMigrationColumnActionType.ALTER
|
||||||
? fieldMetadataOrPreviousFieldMetadata
|
? fieldMetadataOrCurrentFieldMetadata
|
||||||
: undefined;
|
: undefined;
|
||||||
const nextFieldMetadata =
|
const alteredFieldMetadata =
|
||||||
action === WorkspaceMigrationColumnActionType.CREATE
|
action === WorkspaceMigrationColumnActionType.CREATE
|
||||||
? fieldMetadataOrPreviousFieldMetadata
|
? fieldMetadataOrCurrentFieldMetadata
|
||||||
: undefinedOrnextFieldMetadata;
|
: undefinedOrAlteredFieldMetadata;
|
||||||
|
|
||||||
if (!nextFieldMetadata) {
|
if (!alteredFieldMetadata) {
|
||||||
this.logger.error(
|
this.logger.error(
|
||||||
`No field metadata provided for action ${action}`,
|
`No field metadata provided for action ${action}`,
|
||||||
fieldMetadataOrPreviousFieldMetadata,
|
undefinedOrAlteredFieldMetadata,
|
||||||
);
|
);
|
||||||
|
|
||||||
throw new Error(`No field metadata provided for action ${action}`);
|
throw new Error(`No field metadata provided for action ${action}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's a composite field type, we need to create a column action for each of the fields
|
// If it's a composite field type, we need to create a column action for each of the fields
|
||||||
if (isCompositeFieldMetadataType(nextFieldMetadata.type)) {
|
if (isCompositeFieldMetadataType(alteredFieldMetadata.type)) {
|
||||||
const fieldMetadataCollection = this.compositeDefinitions.get(
|
const fieldMetadataCollection = this.compositeDefinitions.get(
|
||||||
nextFieldMetadata.type,
|
alteredFieldMetadata.type,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!fieldMetadataCollection) {
|
if (!fieldMetadataCollection) {
|
||||||
this.logger.error(
|
this.logger.error(
|
||||||
`No composite definition found for type ${nextFieldMetadata.type}`,
|
`No composite definition found for type ${alteredFieldMetadata.type}`,
|
||||||
{
|
{
|
||||||
nextFieldMetadata,
|
alteredFieldMetadata,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`No composite definition found for type ${nextFieldMetadata.type}`,
|
`No composite definition found for type ${alteredFieldMetadata.type}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,8 +153,8 @@ export class WorkspaceMigrationFactory {
|
|||||||
// Otherwise, we create a single column action
|
// Otherwise, we create a single column action
|
||||||
const columnAction = this.createColumnAction(
|
const columnAction = this.createColumnAction(
|
||||||
action,
|
action,
|
||||||
previousFieldMetadata,
|
currentFieldMetadata,
|
||||||
nextFieldMetadata,
|
alteredFieldMetadata,
|
||||||
);
|
);
|
||||||
|
|
||||||
return [columnAction];
|
return [columnAction];
|
||||||
@ -164,24 +164,27 @@ export class WorkspaceMigrationFactory {
|
|||||||
action:
|
action:
|
||||||
| WorkspaceMigrationColumnActionType.CREATE
|
| WorkspaceMigrationColumnActionType.CREATE
|
||||||
| WorkspaceMigrationColumnActionType.ALTER,
|
| WorkspaceMigrationColumnActionType.ALTER,
|
||||||
previousFieldMetadata: FieldMetadataInterface | undefined,
|
currentFieldMetadata: FieldMetadataInterface | undefined,
|
||||||
nextFieldMetadata: FieldMetadataInterface,
|
alteredFieldMetadata: FieldMetadataInterface,
|
||||||
): WorkspaceMigrationColumnAction {
|
): WorkspaceMigrationColumnAction {
|
||||||
const { factory, options } =
|
const { factory, options } =
|
||||||
this.factoriesMap.get(nextFieldMetadata.type) ?? {};
|
this.factoriesMap.get(alteredFieldMetadata.type) ?? {};
|
||||||
|
|
||||||
if (!factory) {
|
if (!factory) {
|
||||||
this.logger.error(`No factory found for type ${nextFieldMetadata.type}`, {
|
this.logger.error(
|
||||||
nextFieldMetadata,
|
`No factory found for type ${alteredFieldMetadata.type}`,
|
||||||
});
|
{
|
||||||
|
alteredFieldMetadata,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
throw new Error(`No factory found for type ${nextFieldMetadata.type}`);
|
throw new Error(`No factory found for type ${alteredFieldMetadata.type}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return factory.create(
|
return factory.create(
|
||||||
action,
|
action,
|
||||||
previousFieldMetadata,
|
currentFieldMetadata,
|
||||||
nextFieldMetadata,
|
alteredFieldMetadata,
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,10 +12,11 @@ export class WorkspaceMigrationEnumService {
|
|||||||
tableName: string,
|
tableName: string,
|
||||||
migrationColumn: WorkspaceMigrationColumnAlter,
|
migrationColumn: WorkspaceMigrationColumnAlter,
|
||||||
) {
|
) {
|
||||||
const oldEnumTypeName = `${tableName}_${migrationColumn.columnName}_enum`;
|
const columnDefinition = migrationColumn.alteredColumnDefinition;
|
||||||
const newEnumTypeName = `${tableName}_${migrationColumn.columnName}_enum_new`;
|
const oldEnumTypeName = `${tableName}_${columnDefinition.columnName}_enum`;
|
||||||
|
const newEnumTypeName = `${tableName}_${columnDefinition.columnName}_enum_new`;
|
||||||
const enumValues =
|
const enumValues =
|
||||||
migrationColumn.enum?.map((enumValue) => {
|
columnDefinition.enum?.map((enumValue) => {
|
||||||
if (typeof enumValue === 'string') {
|
if (typeof enumValue === 'string') {
|
||||||
return enumValue;
|
return enumValue;
|
||||||
}
|
}
|
||||||
@ -23,8 +24,8 @@ export class WorkspaceMigrationEnumService {
|
|||||||
return enumValue.to;
|
return enumValue.to;
|
||||||
}) ?? [];
|
}) ?? [];
|
||||||
|
|
||||||
if (!migrationColumn.isNullable && !migrationColumn.defaultValue) {
|
if (!columnDefinition.isNullable && !columnDefinition.defaultValue) {
|
||||||
migrationColumn.defaultValue = migrationColumn.enum?.[0];
|
columnDefinition.defaultValue = columnDefinition.enum?.[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new enum type with new values
|
// Create new enum type with new values
|
||||||
@ -38,7 +39,7 @@ export class WorkspaceMigrationEnumService {
|
|||||||
// Temporarily change column type to text
|
// Temporarily change column type to text
|
||||||
await queryRunner.query(`
|
await queryRunner.query(`
|
||||||
ALTER TABLE "${schemaName}"."${tableName}"
|
ALTER TABLE "${schemaName}"."${tableName}"
|
||||||
ALTER COLUMN "${migrationColumn.columnName}" TYPE TEXT
|
ALTER COLUMN "${columnDefinition.columnName}" TYPE TEXT
|
||||||
`);
|
`);
|
||||||
|
|
||||||
// Migrate existing values to new values
|
// Migrate existing values to new values
|
||||||
@ -63,7 +64,7 @@ export class WorkspaceMigrationEnumService {
|
|||||||
queryRunner,
|
queryRunner,
|
||||||
schemaName,
|
schemaName,
|
||||||
tableName,
|
tableName,
|
||||||
migrationColumn.columnName,
|
columnDefinition.columnName,
|
||||||
newEnumTypeName,
|
newEnumTypeName,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -100,20 +101,21 @@ export class WorkspaceMigrationEnumService {
|
|||||||
tableName: string,
|
tableName: string,
|
||||||
migrationColumn: WorkspaceMigrationColumnAlter,
|
migrationColumn: WorkspaceMigrationColumnAlter,
|
||||||
) {
|
) {
|
||||||
if (!migrationColumn.enum) {
|
const columnDefinition = migrationColumn.alteredColumnDefinition;
|
||||||
|
|
||||||
|
if (!columnDefinition.enum) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const enumValue of migrationColumn.enum) {
|
for (const enumValue of columnDefinition.enum) {
|
||||||
// Skip string values
|
// Skip string values
|
||||||
if (typeof enumValue === 'string') {
|
if (typeof enumValue === 'string') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
await queryRunner.query(`
|
await queryRunner.query(`
|
||||||
UPDATE "${schemaName}"."${tableName}"
|
UPDATE "${schemaName}"."${tableName}"
|
||||||
SET "${migrationColumn.columnName}" = '${enumValue.to}'
|
SET "${columnDefinition.columnName}" = '${enumValue.to}'
|
||||||
WHERE "${migrationColumn.columnName}" = '${enumValue.from}'
|
WHERE "${columnDefinition.columnName}" = '${enumValue.from}'
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,23 +127,25 @@ export class WorkspaceMigrationEnumService {
|
|||||||
migrationColumn: WorkspaceMigrationColumnAlter,
|
migrationColumn: WorkspaceMigrationColumnAlter,
|
||||||
enumValues: string[],
|
enumValues: string[],
|
||||||
) {
|
) {
|
||||||
|
const columnDefinition = migrationColumn.alteredColumnDefinition;
|
||||||
|
|
||||||
// Set missing values to null or default value
|
// Set missing values to null or default value
|
||||||
let defaultValue = 'NULL';
|
let defaultValue = 'NULL';
|
||||||
|
|
||||||
if (migrationColumn.defaultValue) {
|
if (columnDefinition.defaultValue) {
|
||||||
if (Array.isArray(migrationColumn.defaultValue)) {
|
if (Array.isArray(columnDefinition.defaultValue)) {
|
||||||
defaultValue = `ARRAY[${migrationColumn.defaultValue
|
defaultValue = `ARRAY[${columnDefinition.defaultValue
|
||||||
.map((e) => `'${e}'`)
|
.map((e) => `'${e}'`)
|
||||||
.join(', ')}]`;
|
.join(', ')}]`;
|
||||||
} else {
|
} else {
|
||||||
defaultValue = `'${migrationColumn.defaultValue}'`;
|
defaultValue = `'${columnDefinition.defaultValue}'`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await queryRunner.query(`
|
await queryRunner.query(`
|
||||||
UPDATE "${schemaName}"."${tableName}"
|
UPDATE "${schemaName}"."${tableName}"
|
||||||
SET "${migrationColumn.columnName}" = ${defaultValue}
|
SET "${columnDefinition.columnName}" = ${defaultValue}
|
||||||
WHERE "${migrationColumn.columnName}" NOT IN (${enumValues
|
WHERE "${columnDefinition.columnName}" NOT IN (${enumValues
|
||||||
.map((e) => `'${e}'`)
|
.map((e) => `'${e}'`)
|
||||||
.join(', ')})
|
.join(', ')})
|
||||||
`);
|
`);
|
||||||
|
|||||||
@ -237,7 +237,7 @@ export class WorkspaceMigrationRunnerService {
|
|||||||
tableName: string,
|
tableName: string,
|
||||||
migrationColumn: WorkspaceMigrationColumnAlter,
|
migrationColumn: WorkspaceMigrationColumnAlter,
|
||||||
) {
|
) {
|
||||||
const enumValues = migrationColumn.enum;
|
const enumValues = migrationColumn.alteredColumnDefinition.enum;
|
||||||
|
|
||||||
// TODO: Maybe we can do something better if we can recreate the old `TableColumn` object
|
// TODO: Maybe we can do something better if we can recreate the old `TableColumn` object
|
||||||
if (enumValues) {
|
if (enumValues) {
|
||||||
@ -248,18 +248,32 @@ export class WorkspaceMigrationRunnerService {
|
|||||||
tableName,
|
tableName,
|
||||||
migrationColumn,
|
migrationColumn,
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
await queryRunner.changeColumn(
|
|
||||||
`${schemaName}.${tableName}`,
|
|
||||||
migrationColumn.columnName,
|
|
||||||
new TableColumn({
|
|
||||||
name: migrationColumn.columnName,
|
|
||||||
type: migrationColumn.columnType,
|
|
||||||
default: migrationColumn.defaultValue,
|
|
||||||
isNullable: migrationColumn.isNullable,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await queryRunner.changeColumn(
|
||||||
|
`${schemaName}.${tableName}`,
|
||||||
|
new TableColumn({
|
||||||
|
name: migrationColumn.currentColumnDefinition.columnName,
|
||||||
|
type: migrationColumn.currentColumnDefinition.columnType,
|
||||||
|
default: migrationColumn.currentColumnDefinition.defaultValue,
|
||||||
|
enum: migrationColumn.currentColumnDefinition.enum?.filter(
|
||||||
|
(value): value is string => typeof value === 'string',
|
||||||
|
),
|
||||||
|
isArray: migrationColumn.currentColumnDefinition.isArray,
|
||||||
|
isNullable: migrationColumn.currentColumnDefinition.isNullable,
|
||||||
|
}),
|
||||||
|
new TableColumn({
|
||||||
|
name: migrationColumn.alteredColumnDefinition.columnName,
|
||||||
|
type: migrationColumn.alteredColumnDefinition.columnType,
|
||||||
|
default: migrationColumn.alteredColumnDefinition.defaultValue,
|
||||||
|
enum: migrationColumn.currentColumnDefinition.enum?.filter(
|
||||||
|
(value): value is string => typeof value === 'string',
|
||||||
|
),
|
||||||
|
isArray: migrationColumn.alteredColumnDefinition.isArray,
|
||||||
|
isNullable: migrationColumn.alteredColumnDefinition.isNullable,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createForeignKey(
|
private async createForeignKey(
|
||||||
|
|||||||
Reference in New Issue
Block a user