Fix enum defaultValue issues (#5307)
This PR fixes several issues:
- enum naming should be: {tableName}_{fieldName}_enum and respecting the
case
- defaultValue format handled in the FE should respect the one in the BE
In my opinion we should refactor the defaultValue:
- we should respect backend format: "'myDefault'" for constant default
and "0" for float, "now" for expressions, "true" for booleans. we can
rename it to defaultValueExpression if it is more clear but we should
not maintain a parallel system
- we should deprecate option: isDefaultValue which is confusing
- we should re-work backend to have a more unified approach between
fields and avoid having if everywhere about select, multiselect, and
currency cases. one unified "computeDefaultValue" function should do the
job
What is still broken:
- currency default Value on creation. I think we should do the refactor
first
- select default value edition.
These cases do not break the schema but are ignored currently
This commit is contained in:
@ -26,6 +26,7 @@ export const useFieldMetadataItem = () => {
|
|||||||
) => {
|
) => {
|
||||||
const formattedInput = formatFieldMetadataItemInput(input);
|
const formattedInput = formatFieldMetadataItemInput(input);
|
||||||
|
|
||||||
|
debugger;
|
||||||
const defaultValue = getDefaultValueForBackend(
|
const defaultValue = getDefaultValueForBackend(
|
||||||
input.defaultValue ?? formattedInput.defaultValue,
|
input.defaultValue ?? formattedInput.defaultValue,
|
||||||
input.type,
|
input.type,
|
||||||
@ -51,24 +52,24 @@ export const useFieldMetadataItem = () => {
|
|||||||
| 'options'
|
| 'options'
|
||||||
>,
|
>,
|
||||||
) => {
|
) => {
|
||||||
const formattedInput = formatFieldMetadataItemInput(input);
|
// In Edit mode, all options need an id,
|
||||||
const defaultValue = input.defaultValue
|
// so we generate an id for newly created options.
|
||||||
? typeof input.defaultValue == 'string'
|
const inputOptions = input.options?.map((option: FieldMetadataOption) =>
|
||||||
? `'${input.defaultValue}'`
|
option.id ? option : { ...option, id: v4() },
|
||||||
: input.defaultValue
|
);
|
||||||
: formattedInput.defaultValue ?? undefined;
|
const formattedInput = formatFieldMetadataItemInput({
|
||||||
|
...input,
|
||||||
|
options: inputOptions,
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultValue = input.defaultValue ?? formattedInput.defaultValue;
|
||||||
|
|
||||||
return updateOneFieldMetadataItem({
|
return updateOneFieldMetadataItem({
|
||||||
fieldMetadataIdToUpdate: input.id,
|
fieldMetadataIdToUpdate: input.id,
|
||||||
updatePayload: formatFieldMetadataItemInput({
|
updatePayload: {
|
||||||
...input,
|
...formattedInput,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
// In Edit mode, all options need an id,
|
},
|
||||||
// so we generate an id for newly created options.
|
|
||||||
options: input.options?.map((option: FieldMetadataOption) =>
|
|
||||||
option.id ? option : { ...option, id: v4() },
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -28,7 +28,13 @@ export const useUpdateOneFieldMetadataItem = () => {
|
|||||||
fieldMetadataIdToUpdate: UpdateOneFieldMetadataItemMutationVariables['idToUpdate'];
|
fieldMetadataIdToUpdate: UpdateOneFieldMetadataItemMutationVariables['idToUpdate'];
|
||||||
updatePayload: Pick<
|
updatePayload: Pick<
|
||||||
UpdateOneFieldMetadataItemMutationVariables['updatePayload'],
|
UpdateOneFieldMetadataItemMutationVariables['updatePayload'],
|
||||||
'description' | 'icon' | 'isActive' | 'label' | 'name'
|
| 'description'
|
||||||
|
| 'icon'
|
||||||
|
| 'isActive'
|
||||||
|
| 'label'
|
||||||
|
| 'name'
|
||||||
|
| 'defaultValue'
|
||||||
|
| 'options'
|
||||||
>;
|
>;
|
||||||
}) => {
|
}) => {
|
||||||
return await mutate({
|
return await mutate({
|
||||||
|
|||||||
@ -77,7 +77,7 @@ describe('formatFieldMetadataItemInput', () => {
|
|||||||
value: 'OPTION_2',
|
value: 'OPTION_2',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
defaultValue: "'OPTION_1'",
|
defaultValue: 'OPTION_1',
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = formatFieldMetadataItemInput(input);
|
const result = formatFieldMetadataItemInput(input);
|
||||||
@ -140,7 +140,7 @@ describe('formatFieldMetadataItemInput', () => {
|
|||||||
value: 'OPTION_2',
|
value: 'OPTION_2',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
defaultValue: ["'OPTION_1'", "'OPTION_2'"],
|
defaultValue: ['OPTION_1', 'OPTION_2'],
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = formatFieldMetadataItemInput(input);
|
const result = formatFieldMetadataItemInput(input);
|
||||||
|
|||||||
@ -32,14 +32,14 @@ export const formatFieldMetadataItemInput = (
|
|||||||
const defaultOptions = options?.filter((option) => option.isDefault);
|
const defaultOptions = options?.filter((option) => option.isDefault);
|
||||||
if (isDefined(defaultOptions)) {
|
if (isDefined(defaultOptions)) {
|
||||||
defaultValue = defaultOptions.map(
|
defaultValue = defaultOptions.map(
|
||||||
(defaultOption) => `'${getOptionValueFromLabel(defaultOption.label)}'`,
|
(defaultOption) => `${getOptionValueFromLabel(defaultOption.label)}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (input.type === FieldMetadataType.Select) {
|
if (input.type === FieldMetadataType.Select) {
|
||||||
const defaultOption = options?.find((option) => option.isDefault);
|
const defaultOption = options?.find((option) => option.isDefault);
|
||||||
defaultValue = isDefined(defaultOption)
|
defaultValue = isDefined(defaultOption)
|
||||||
? `'${getOptionValueFromLabel(defaultOption.label)}'`
|
? `${getOptionValueFromLabel(defaultOption.label)}`
|
||||||
: undefined;
|
: undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { CurrencyCode } from '@/object-record/record-field/types/CurrencyCode';
|
||||||
import { FieldCurrencyValue } from '@/object-record/record-field/types/FieldMetadata';
|
import { FieldCurrencyValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
@ -9,10 +10,12 @@ export const getDefaultValueForBackend = (
|
|||||||
const currencyDefaultValue = defaultValue as FieldCurrencyValue;
|
const currencyDefaultValue = defaultValue as FieldCurrencyValue;
|
||||||
return {
|
return {
|
||||||
amountMicros: currencyDefaultValue.amountMicros,
|
amountMicros: currencyDefaultValue.amountMicros,
|
||||||
currencyCode: `'${currencyDefaultValue.currencyCode}'` as any,
|
currencyCode: `'${currencyDefaultValue.currencyCode}'` as CurrencyCode,
|
||||||
} satisfies FieldCurrencyValue;
|
} satisfies FieldCurrencyValue;
|
||||||
} else if (typeof defaultValue === 'string') {
|
} else if (fieldMetadataType === FieldMetadataType.Select) {
|
||||||
return `'${defaultValue}'`;
|
return `'${defaultValue}'`;
|
||||||
|
} else if (fieldMetadataType === FieldMetadataType.MultiSelect) {
|
||||||
|
return defaultValue.map((value: string) => `'${value}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
|
|||||||
@ -13,7 +13,7 @@ export function IsQuotedString(validationOptions?: ValidationOptions) {
|
|||||||
options: validationOptions,
|
options: validationOptions,
|
||||||
validator: {
|
validator: {
|
||||||
validate(value: any) {
|
validate(value: any) {
|
||||||
return typeof value === 'string' && /^'.*'$/.test(value);
|
return typeof value === 'string' && /^'{1}.*'{1}$/.test(value);
|
||||||
},
|
},
|
||||||
defaultMessage(args: ValidationArguments) {
|
defaultMessage(args: ValidationArguments) {
|
||||||
return `${args.property} must be a quoted string`;
|
return `${args.property} must be a quoted string`;
|
||||||
|
|||||||
@ -166,9 +166,8 @@ export class DatabaseStructureService {
|
|||||||
// Compute enum name to compare data type properly
|
// Compute enum name to compare data type properly
|
||||||
if (typeORMType === 'enum') {
|
if (typeORMType === 'enum') {
|
||||||
const objectName = fieldMetadata.object?.nameSingular;
|
const objectName = fieldMetadata.object?.nameSingular;
|
||||||
const prefix = fieldMetadata.isCustom ? '_' : '';
|
|
||||||
|
|
||||||
return `${objectName}_${prefix}${columnName}_enum`;
|
return `${objectName}_${columnName}_enum`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mainDataSource.driver.normalizeType({
|
return mainDataSource.driver.normalizeType({
|
||||||
|
|||||||
@ -32,9 +32,9 @@ export class WorkspaceMigrationEnumService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const columnDefinition = migrationColumn.alteredColumnDefinition;
|
const columnDefinition = migrationColumn.alteredColumnDefinition;
|
||||||
const oldEnumTypeName =
|
const oldEnumTypeName = `${tableName}_${migrationColumn.currentColumnDefinition.columnName}_enum`;
|
||||||
`${tableName}_${migrationColumn.currentColumnDefinition.columnName}_enum`.toLowerCase();
|
|
||||||
const tempEnumTypeName = `${oldEnumTypeName}_temp`;
|
const tempEnumTypeName = `${oldEnumTypeName}_temp`;
|
||||||
|
const newEnumTypeName = `${tableName}_${columnDefinition.columnName}_enum`;
|
||||||
const enumValues =
|
const enumValues =
|
||||||
columnDefinition.enum?.map((enumValue) => {
|
columnDefinition.enum?.map((enumValue) => {
|
||||||
if (typeof enumValue === 'string') {
|
if (typeof enumValue === 'string') {
|
||||||
@ -76,6 +76,7 @@ export class WorkspaceMigrationEnumService {
|
|||||||
type: columnDefinition.columnType,
|
type: columnDefinition.columnType,
|
||||||
default: columnDefinition.defaultValue,
|
default: columnDefinition.defaultValue,
|
||||||
enum: enumValues,
|
enum: enumValues,
|
||||||
|
enumName: newEnumTypeName,
|
||||||
isArray: columnDefinition.isArray,
|
isArray: columnDefinition.isArray,
|
||||||
isNullable: columnDefinition.isNullable,
|
isNullable: columnDefinition.isNullable,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -307,6 +307,8 @@ export class WorkspaceMigrationRunnerService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const enumName = `${tableName}_${migrationColumn.columnName}_enum`;
|
||||||
|
|
||||||
await queryRunner.addColumn(
|
await queryRunner.addColumn(
|
||||||
`${schemaName}.${tableName}`,
|
`${schemaName}.${tableName}`,
|
||||||
new TableColumn({
|
new TableColumn({
|
||||||
@ -316,6 +318,7 @@ export class WorkspaceMigrationRunnerService {
|
|||||||
enum: migrationColumn.enum?.filter(
|
enum: migrationColumn.enum?.filter(
|
||||||
(value): value is string => typeof value === 'string',
|
(value): value is string => typeof value === 'string',
|
||||||
),
|
),
|
||||||
|
enumName: enumName,
|
||||||
isArray: migrationColumn.isArray,
|
isArray: migrationColumn.isArray,
|
||||||
isNullable: migrationColumn.isNullable,
|
isNullable: migrationColumn.isNullable,
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user