Browse Source

Merge pull request #7070 from nocodb/fix/5492-validate-select-options

fix: Validate select options before insert
nc-oss/44517c26
Raju Udava 10 months ago committed by GitHub
parent
commit
42be112f5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 61
      packages/nocodb/src/db/BaseModelSqlv2.ts
  2. 15
      packages/nocodb/src/services/columns.service.ts
  3. 4
      packages/nocodb/tests/unit/factory/column.ts
  4. 4
      packages/nocodb/tests/unit/rest/tests/filter.test.ts

61
packages/nocodb/src/db/BaseModelSqlv2.ts

@ -2977,6 +2977,8 @@ class BaseModelSqlv2 {
}
}
await this.validateOptions(col, insertObj);
// validate data
if (col?.meta?.validate && col?.validate) {
const validate = col.getValidators();
@ -3208,7 +3210,12 @@ class BaseModelSqlv2 {
}
async bulkUpdateAll(
args: { where?: string; filterArr?: Filter[]; viewId?: string } = {},
args: {
where?: string;
filterArr?: Filter[];
viewId?: string;
skipValidationAndHooks?: boolean;
} = {},
data,
{ cookie }: { cookie?: any } = {},
) {
@ -3219,7 +3226,7 @@ class BaseModelSqlv2 {
this.clientMeta,
this.dbDriver,
);
await this.validate(updateData);
if (!args.skipValidationAndHooks) await this.validate(updateData);
const pkValues = await this._extractPksValues(updateData);
if (pkValues) {
// pk is specified - by pass
@ -3253,7 +3260,7 @@ class BaseModelSqlv2 {
);
}
await conditionV2(this, conditionObj, qb);
await conditionV2(this, conditionObj, qb, undefined, true);
count = (
await this.execAndParse(
@ -3271,7 +3278,8 @@ class BaseModelSqlv2 {
await this.execAndParse(qb, null, { raw: true });
}
await this.afterBulkUpdate(null, count, this.dbDriver, cookie, true);
if (!args.skipValidationAndHooks)
await this.afterBulkUpdate(null, count, this.dbDriver, cookie, true);
return count;
} catch (e) {
@ -3763,6 +3771,8 @@ class BaseModelSqlv2 {
// let cols = Object.keys(this.columns);
for (let i = 0; i < this.model.columns.length; ++i) {
const column = this.model.columns[i];
await this.validateOptions(column, columns);
// skip validation if `validate` is undefined or false
if (!column?.meta?.validate || !column?.validate) continue;
@ -3797,6 +3807,49 @@ class BaseModelSqlv2 {
return true;
}
// method for validating otpions if column is single/multi select
private async validateOptions(
column: Column<any>,
insertOrUpdateObject: Record<string, any>,
) {
// if SingleSelect or MultiSelect, then validate the options
if (
!(
column.uidt === UITypes.SingleSelect ||
column.uidt === UITypes.MultiSelect
)
) {
return;
}
const options = await column
.getColOptions<{ options: SelectOption[] }>()
.then(({ options }) => options.map((opt) => opt.title));
const columnTitle = column.title;
const columnName = column.column_name;
const columnValue =
insertOrUpdateObject?.[columnTitle] ?? insertOrUpdateObject?.[columnName];
if (!columnValue) {
return;
}
// if multi select, then split the values
const columnValueArr =
column.uidt === UITypes.MultiSelect
? columnValue.split(',')
: [columnValue];
for (let j = 0; j < columnValueArr.length; ++j) {
const val = columnValueArr[j];
if (!options.includes(val)) {
NcError.badRequest(
`Invalid option "${val}" provided for column "${columnTitle}". Valid options are "${options.join(
', ',
)}"`,
);
}
}
}
async addChild({
colId,
rowId,

15
packages/nocodb/src/services/columns.service.ts

@ -546,7 +546,10 @@ export class ColumnsService {
]);
} else {
await baseModel.bulkUpdateAll(
{ where: `(${column.title},eq,${option.title})` },
{
where: `(${column.title},eq,${option.title})`,
skipValidationAndHooks: true,
},
{ [column.column_name]: null },
{ cookie },
);
@ -728,7 +731,10 @@ export class ColumnsService {
]);
} else {
await baseModel.bulkUpdateAll(
{ where: `(${column.title},eq,${option.title})` },
{
where: `(${column.title},eq,${option.title})`,
skipValidationAndHooks: true,
},
{ [column.column_name]: newOp.title },
{ cookie },
);
@ -814,7 +820,10 @@ export class ColumnsService {
]);
} else {
await baseModel.bulkUpdateAll(
{ where: `(${column.title},eq,${ch.temp_title})` },
{
where: `(${column.title},eq,${ch.temp_title})`,
skipValidationAndHooks: true,
},
{ [column.column_name]: newOp.title },
{ cookie },
);

4
packages/nocodb/tests/unit/factory/column.ts

@ -148,13 +148,13 @@ const customColumns = function (type: string, options: any = {}) {
column_name: 'SingleSelect',
title: 'SingleSelect',
uidt: UITypes.SingleSelect,
dtxp: "'jan','feb','mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'",
dtxp: "'jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'",
},
{
column_name: 'MultiSelect',
title: 'MultiSelect',
uidt: UITypes.MultiSelect,
dtxp: "'jan','feb','mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'",
dtxp: "'jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'",
},
];
case 'custom':

4
packages/nocodb/tests/unit/rest/tests/filter.test.ts

@ -519,13 +519,13 @@ function filterSelectBased() {
column_name: 'SingleSelect',
title: 'SingleSelect',
uidt: UITypes.SingleSelect,
dtxp: "'jan','feb','mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'",
dtxp: "'jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'",
},
{
column_name: 'MultiSelect',
title: 'MultiSelect',
uidt: UITypes.MultiSelect,
dtxp: "'jan','feb','mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'",
dtxp: "'jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'",
},
],
});

Loading…
Cancel
Save