Browse Source

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

fix: Validate select options before insert
nc-oss/44517c26
Raju Udava 1 year 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 // validate data
if (col?.meta?.validate && col?.validate) { if (col?.meta?.validate && col?.validate) {
const validate = col.getValidators(); const validate = col.getValidators();
@ -3208,7 +3210,12 @@ class BaseModelSqlv2 {
} }
async bulkUpdateAll( async bulkUpdateAll(
args: { where?: string; filterArr?: Filter[]; viewId?: string } = {}, args: {
where?: string;
filterArr?: Filter[];
viewId?: string;
skipValidationAndHooks?: boolean;
} = {},
data, data,
{ cookie }: { cookie?: any } = {}, { cookie }: { cookie?: any } = {},
) { ) {
@ -3219,7 +3226,7 @@ class BaseModelSqlv2 {
this.clientMeta, this.clientMeta,
this.dbDriver, this.dbDriver,
); );
await this.validate(updateData); if (!args.skipValidationAndHooks) await this.validate(updateData);
const pkValues = await this._extractPksValues(updateData); const pkValues = await this._extractPksValues(updateData);
if (pkValues) { if (pkValues) {
// pk is specified - by pass // pk is specified - by pass
@ -3253,7 +3260,7 @@ class BaseModelSqlv2 {
); );
} }
await conditionV2(this, conditionObj, qb); await conditionV2(this, conditionObj, qb, undefined, true);
count = ( count = (
await this.execAndParse( await this.execAndParse(
@ -3271,7 +3278,8 @@ class BaseModelSqlv2 {
await this.execAndParse(qb, null, { raw: true }); 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; return count;
} catch (e) { } catch (e) {
@ -3763,6 +3771,8 @@ class BaseModelSqlv2 {
// let cols = Object.keys(this.columns); // let cols = Object.keys(this.columns);
for (let i = 0; i < this.model.columns.length; ++i) { for (let i = 0; i < this.model.columns.length; ++i) {
const column = this.model.columns[i]; const column = this.model.columns[i];
await this.validateOptions(column, columns);
// skip validation if `validate` is undefined or false // skip validation if `validate` is undefined or false
if (!column?.meta?.validate || !column?.validate) continue; if (!column?.meta?.validate || !column?.validate) continue;
@ -3797,6 +3807,49 @@ class BaseModelSqlv2 {
return true; 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({ async addChild({
colId, colId,
rowId, rowId,

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

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

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

@ -148,13 +148,13 @@ const customColumns = function (type: string, options: any = {}) {
column_name: 'SingleSelect', column_name: 'SingleSelect',
title: 'SingleSelect', title: 'SingleSelect',
uidt: UITypes.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', column_name: 'MultiSelect',
title: 'MultiSelect', title: 'MultiSelect',
uidt: UITypes.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': case 'custom':

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

@ -519,13 +519,13 @@ function filterSelectBased() {
column_name: 'SingleSelect', column_name: 'SingleSelect',
title: 'SingleSelect', title: 'SingleSelect',
uidt: UITypes.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', column_name: 'MultiSelect',
title: 'MultiSelect', title: 'MultiSelect',
uidt: UITypes.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