Browse Source

Merge pull request #6770 from nocodb/fix/6342-incorrect-filter

fix: Validate filter before bulk delete/update operations
pull/6271/merge
Raju Udava 1 year ago committed by GitHub
parent
commit
5d60aeb41d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 74
      packages/nocodb/src/db/BaseModelSqlv2.ts
  2. 67
      packages/nocodb/src/db/conditionV2.ts
  3. 11
      packages/nocodb/src/db/sortV2.ts
  4. 24
      packages/nocodb/src/helpers/catchError.ts
  5. 14
      packages/nocodb/src/helpers/getAst.ts
  6. 7
      packages/nocodb/src/services/data-table.service.ts
  7. 18
      packages/nocodb/src/services/datas.service.ts
  8. 21
      packages/nocodb/tests/unit/rest/tests/newDataApis.test.ts

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

@ -138,9 +138,11 @@ class BaseModelSqlv2 {
{ {
ignoreView = false, ignoreView = false,
getHiddenColumn = false, getHiddenColumn = false,
throwErrorIfInvalidParams = false,
}: { }: {
ignoreView?: boolean; ignoreView?: boolean;
getHiddenColumn?: boolean; getHiddenColumn?: boolean;
throwErrorIfInvalidParams?: boolean;
} = {}, } = {},
): Promise<any> { ): Promise<any> {
const qb = this.dbDriver(this.tnPath); const qb = this.dbDriver(this.tnPath);
@ -150,6 +152,7 @@ class BaseModelSqlv2 {
model: this.model, model: this.model,
view: ignoreView ? null : this.viewId && (await View.get(this.viewId)), view: ignoreView ? null : this.viewId && (await View.get(this.viewId)),
getHiddenColumn, getHiddenColumn,
throwErrorIfInvalidParams,
}); });
await this.selectObject({ await this.selectObject({
@ -265,6 +268,7 @@ class BaseModelSqlv2 {
} = {}, } = {},
ignoreViewFilterAndSort = false, ignoreViewFilterAndSort = false,
validateFormula = false, validateFormula = false,
throwErrorIfInvalidParams = false,
): Promise<any> { ): Promise<any> {
const { where, fields, ...rest } = this._getListArgs(args as any); const { where, fields, ...rest } = this._getListArgs(args as any);
@ -281,8 +285,16 @@ class BaseModelSqlv2 {
} }
const aliasColObjMap = await this.model.getAliasColObjMap(); const aliasColObjMap = await this.model.getAliasColObjMap();
let sorts = extractSortsObject(rest?.sort, aliasColObjMap); let sorts = extractSortsObject(
const filterObj = extractFilterFromXwhere(where, aliasColObjMap); rest?.sort,
aliasColObjMap,
throwErrorIfInvalidParams,
);
const filterObj = extractFilterFromXwhere(
where,
aliasColObjMap,
throwErrorIfInvalidParams,
);
// todo: replace with view id // todo: replace with view id
if (!ignoreViewFilterAndSort && this.viewId) { if (!ignoreViewFilterAndSort && this.viewId) {
await conditionV2( await conditionV2(
@ -305,6 +317,8 @@ class BaseModelSqlv2 {
}), }),
], ],
qb, qb,
undefined,
throwErrorIfInvalidParams,
); );
if (!sorts) if (!sorts)
@ -312,7 +326,7 @@ class BaseModelSqlv2 {
? args.sortArr ? args.sortArr
: await Sort.list({ viewId: this.viewId }); : await Sort.list({ viewId: this.viewId });
await sortV2(this, sorts, qb); await sortV2(this, sorts, qb, undefined, throwErrorIfInvalidParams);
} else { } else {
await conditionV2( await conditionV2(
this, this,
@ -329,11 +343,13 @@ class BaseModelSqlv2 {
}), }),
], ],
qb, qb,
undefined,
throwErrorIfInvalidParams,
); );
if (!sorts) sorts = args.sortArr; if (!sorts) sorts = args.sortArr;
await sortV2(this, sorts, qb); await sortV2(this, sorts, qb, undefined, throwErrorIfInvalidParams);
} }
// sort by primary key if not autogenerated string // sort by primary key if not autogenerated string
@ -366,6 +382,7 @@ class BaseModelSqlv2 {
public async count( public async count(
args: { where?: string; limit?; filterArr?: Filter[] } = {}, args: { where?: string; limit?; filterArr?: Filter[] } = {},
ignoreViewFilterAndSort = false, ignoreViewFilterAndSort = false,
throwErrorIfInvalidParams = false,
): Promise<any> { ): Promise<any> {
await this.model.getColumns(); await this.model.getColumns();
const { where } = this._getListArgs(args); const { where } = this._getListArgs(args);
@ -374,7 +391,11 @@ class BaseModelSqlv2 {
// qb.xwhere(where, await this.model.getAliasColMapping()); // qb.xwhere(where, await this.model.getAliasColMapping());
const aliasColObjMap = await this.model.getAliasColObjMap(); const aliasColObjMap = await this.model.getAliasColObjMap();
const filterObj = extractFilterFromXwhere(where, aliasColObjMap); const filterObj = extractFilterFromXwhere(
where,
aliasColObjMap,
throwErrorIfInvalidParams,
);
if (!ignoreViewFilterAndSort && this.viewId) { if (!ignoreViewFilterAndSort && this.viewId) {
await conditionV2( await conditionV2(
@ -398,6 +419,8 @@ class BaseModelSqlv2 {
...(args.filterArr || []), ...(args.filterArr || []),
], ],
qb, qb,
undefined,
throwErrorIfInvalidParams,
); );
} else { } else {
await conditionV2( await conditionV2(
@ -416,6 +439,8 @@ class BaseModelSqlv2 {
...(args.filterArr || []), ...(args.filterArr || []),
], ],
qb, qb,
undefined,
throwErrorIfInvalidParams,
); );
} }
@ -2991,7 +3016,7 @@ class BaseModelSqlv2 {
const { where } = this._getListArgs(args); const { where } = this._getListArgs(args);
const qb = this.dbDriver(this.tnPath); const qb = this.dbDriver(this.tnPath);
const aliasColObjMap = await this.model.getAliasColObjMap(); const aliasColObjMap = await this.model.getAliasColObjMap();
const filterObj = extractFilterFromXwhere(where, aliasColObjMap); const filterObj = extractFilterFromXwhere(where, aliasColObjMap, true);
const conditionObj = [ const conditionObj = [
new Filter({ new Filter({
@ -3177,7 +3202,7 @@ class BaseModelSqlv2 {
const { where } = this._getListArgs(args); const { where } = this._getListArgs(args);
const qb = this.dbDriver(this.tnPath); const qb = this.dbDriver(this.tnPath);
const aliasColObjMap = await this.model.getAliasColObjMap(); const aliasColObjMap = await this.model.getAliasColObjMap();
const filterObj = extractFilterFromXwhere(where, aliasColObjMap); const filterObj = extractFilterFromXwhere(where, aliasColObjMap, true);
await conditionV2( await conditionV2(
this, this,
@ -3194,6 +3219,8 @@ class BaseModelSqlv2 {
}), }),
], ],
qb, qb,
undefined,
true,
); );
const execQueries: ((trx: Knex.Transaction, qb: any) => Promise<any>)[] = const execQueries: ((trx: Knex.Transaction, qb: any) => Promise<any>)[] =
[]; [];
@ -4846,6 +4873,7 @@ class BaseModelSqlv2 {
export function extractSortsObject( export function extractSortsObject(
_sorts: string | string[], _sorts: string | string[],
aliasColObjMap: { [columnAlias: string]: Column }, aliasColObjMap: { [columnAlias: string]: Column },
throwErrorIfInvalid = false,
): Sort[] { ): Sort[] {
if (!_sorts?.length) return; if (!_sorts?.length) return;
@ -4862,6 +4890,11 @@ export function extractSortsObject(
// replace + at the beginning if present // replace + at the beginning if present
else sort.fk_column_id = aliasColObjMap[s.replace(/^\+/, '')]?.id; else sort.fk_column_id = aliasColObjMap[s.replace(/^\+/, '')]?.id;
if (throwErrorIfInvalid && !sort.fk_column_id)
NcError.unprocessableEntity(
`Invalid column '${s.replace(/^[+-]/, '')}' in sort`,
);
return new Sort(sort); return new Sort(sort);
}); });
} }
@ -4869,6 +4902,7 @@ export function extractSortsObject(
export function extractFilterFromXwhere( export function extractFilterFromXwhere(
str, str,
aliasColObjMap: { [columnAlias: string]: Column }, aliasColObjMap: { [columnAlias: string]: Column },
throwErrorIfInvalid = false,
) { ) {
if (!str) { if (!str) {
return []; return [];
@ -4890,7 +4924,11 @@ export function extractFilterFromXwhere(
nestedArrayConditions = str.split( nestedArrayConditions = str.split(
/(?=~(?:or(?:not)?|and(?:not)?|not)\()/, /(?=~(?:or(?:not)?|and(?:not)?|not)\()/,
); );
return extractCondition(nestedArrayConditions || [], aliasColObjMap); return extractCondition(
nestedArrayConditions || [],
aliasColObjMap,
throwErrorIfInvalid,
);
} }
// iterate until finding right closing // iterate until finding right closing
@ -4918,7 +4956,11 @@ export function extractFilterFromXwhere(
const lhsOfNestedQuery = str.substring(0, openIndex); const lhsOfNestedQuery = str.substring(0, openIndex);
nestedArrayConditions.push( nestedArrayConditions.push(
...extractFilterFromXwhere(lhsOfNestedQuery, aliasColObjMap), ...extractFilterFromXwhere(
lhsOfNestedQuery,
aliasColObjMap,
throwErrorIfInvalid,
),
// calling recursively for nested query // calling recursively for nested query
new Filter({ new Filter({
is_group: true, is_group: true,
@ -4929,7 +4971,11 @@ export function extractFilterFromXwhere(
), ),
}), }),
// RHS of nested query(recursion) // RHS of nested query(recursion)
...extractFilterFromXwhere(str.substring(closingIndex + 2), aliasColObjMap), ...extractFilterFromXwhere(
str.substring(closingIndex + 2),
aliasColObjMap,
throwErrorIfInvalid,
),
); );
return nestedArrayConditions; return nestedArrayConditions;
} }
@ -4956,7 +5002,11 @@ function validateFilterComparison(uidt: UITypes, op: any, sub_op?: any) {
} }
} }
export function extractCondition(nestedArrayConditions, aliasColObjMap) { export function extractCondition(
nestedArrayConditions,
aliasColObjMap,
throwErrorIfInvalid,
) {
return nestedArrayConditions?.map((str) => { return nestedArrayConditions?.map((str) => {
let [logicOp, alias, op, value] = let [logicOp, alias, op, value] =
str.match(/(?:~(and|or|not))?\((.*?),(\w+),(.*)\)/)?.slice(1) || []; str.match(/(?:~(and|or|not))?\((.*?),(\w+),(.*)\)/)?.slice(1) || [];
@ -4983,6 +5033,8 @@ export function extractCondition(nestedArrayConditions, aliasColObjMap) {
} }
validateFilterComparison(aliasColObjMap[alias].uidt, op, sub_op); validateFilterComparison(aliasColObjMap[alias].uidt, op, sub_op);
} else if (throwErrorIfInvalid) {
NcError.unprocessableEntity(`Column '${alias}' not found.`);
} }
return new Filter({ return new Filter({

67
packages/nocodb/src/db/conditionV2.ts

@ -8,6 +8,7 @@ import type Column from '~/models/Column';
import type LookupColumn from '~/models/LookupColumn'; import type LookupColumn from '~/models/LookupColumn';
import type RollupColumn from '~/models/RollupColumn'; import type RollupColumn from '~/models/RollupColumn';
import type FormulaColumn from '~/models/FormulaColumn'; import type FormulaColumn from '~/models/FormulaColumn';
import { NcError } from '~/helpers/catchError';
import formulaQueryBuilderv2 from '~/db/formulav2/formulaQueryBuilderv2'; import formulaQueryBuilderv2 from '~/db/formulav2/formulaQueryBuilderv2';
import genRollupSelectv2 from '~/db/genRollupSelectv2'; import genRollupSelectv2 from '~/db/genRollupSelectv2';
import { sanitize } from '~/helpers/sqlSanitize'; import { sanitize } from '~/helpers/sqlSanitize';
@ -21,13 +22,21 @@ export default async function conditionV2(
conditionObj: Filter | Filter[], conditionObj: Filter | Filter[],
qb: Knex.QueryBuilder, qb: Knex.QueryBuilder,
alias?: string, alias?: string,
throwErrorIfInvalid = false,
) { ) {
if (!conditionObj || typeof conditionObj !== 'object') { if (!conditionObj || typeof conditionObj !== 'object') {
return; return;
} }
(await parseConditionV2(baseModelSqlv2, conditionObj, { count: 0 }, alias))( (
qb, await parseConditionV2(
); baseModelSqlv2,
conditionObj,
{ count: 0 },
alias,
undefined,
throwErrorIfInvalid,
)
)(qb);
} }
function getLogicalOpMethod(filter: Filter) { function getLogicalOpMethod(filter: Filter) {
@ -49,6 +58,7 @@ const parseConditionV2 = async (
aliasCount = { count: 0 }, aliasCount = { count: 0 },
alias?, alias?,
customWhereClause?, customWhereClause?,
throwErrorIfInvalid = false,
) => { ) => {
const knex = baseModelSqlv2.dbDriver; const knex = baseModelSqlv2.dbDriver;
@ -60,7 +70,14 @@ const parseConditionV2 = async (
if (Array.isArray(_filter)) { if (Array.isArray(_filter)) {
const qbs = await Promise.all( const qbs = await Promise.all(
_filter.map((child) => _filter.map((child) =>
parseConditionV2(baseModelSqlv2, child, aliasCount, alias), parseConditionV2(
baseModelSqlv2,
child,
aliasCount,
alias,
undefined,
throwErrorIfInvalid,
),
), ),
); );
@ -76,7 +93,14 @@ const parseConditionV2 = async (
const qbs = await Promise.all( const qbs = await Promise.all(
(children || []).map((child) => (children || []).map((child) =>
parseConditionV2(baseModelSqlv2, child, aliasCount), parseConditionV2(
baseModelSqlv2,
child,
aliasCount,
undefined,
undefined,
throwErrorIfInvalid,
),
), ),
); );
@ -89,7 +113,14 @@ const parseConditionV2 = async (
}; };
} else { } else {
const column = await filter.getColumn(); const column = await filter.getColumn();
if (!column) return () => {}; if (!column) {
if (throwErrorIfInvalid) {
NcError.unprocessableEntity(
`Invalid column id '${filter.fk_column_id}' in filter`,
);
}
return;
}
if (column.uidt === UITypes.LinkToAnotherRecord) { if (column.uidt === UITypes.LinkToAnotherRecord) {
const colOptions = const colOptions =
(await column.getColOptions()) as LinkToAnotherRecordColumn; (await column.getColOptions()) as LinkToAnotherRecordColumn;
@ -153,6 +184,9 @@ const parseConditionV2 = async (
fk_column_id: childModel?.displayValue?.id, fk_column_id: childModel?.displayValue?.id,
}), }),
aliasCount, aliasCount,
undefined,
undefined,
throwErrorIfInvalid,
) )
)(selectQb); )(selectQb);
@ -216,6 +250,9 @@ const parseConditionV2 = async (
fk_column_id: parentModel?.displayValue?.id, fk_column_id: parentModel?.displayValue?.id,
}), }),
aliasCount, aliasCount,
undefined,
undefined,
throwErrorIfInvalid,
) )
)(selectQb); )(selectQb);
@ -292,6 +329,9 @@ const parseConditionV2 = async (
fk_column_id: parentModel?.displayValue?.id, fk_column_id: parentModel?.displayValue?.id,
}), }),
aliasCount, aliasCount,
undefined,
undefined,
throwErrorIfInvalid,
) )
)(selectQb); )(selectQb);
@ -310,6 +350,7 @@ const parseConditionV2 = async (
filter, filter,
knex, knex,
aliasCount, aliasCount,
throwErrorIfInvalid,
); );
} else if ( } else if (
[UITypes.Rollup, UITypes.Links].includes(column.uidt) && [UITypes.Rollup, UITypes.Links].includes(column.uidt) &&
@ -843,6 +884,7 @@ async function generateLookupCondition(
filter: Filter, filter: Filter,
knex, knex,
aliasCount = { count: 0 }, aliasCount = { count: 0 },
throwErrorIfInvalid = false,
): Promise<any> { ): Promise<any> {
const colOptions = await col.getColOptions<LookupColumn>(); const colOptions = await col.getColOptions<LookupColumn>();
const relationColumn = await colOptions.getRelationColumn(); const relationColumn = await colOptions.getRelationColumn();
@ -880,6 +922,7 @@ async function generateLookupCondition(
knex, knex,
alias, alias,
aliasCount, aliasCount,
throwErrorIfInvalid,
); );
return (qbP: Knex.QueryBuilder) => { return (qbP: Knex.QueryBuilder) => {
@ -906,6 +949,7 @@ async function generateLookupCondition(
knex, knex,
alias, alias,
aliasCount, aliasCount,
throwErrorIfInvalid,
); );
return (qbP: Knex.QueryBuilder) => { return (qbP: Knex.QueryBuilder) => {
@ -943,6 +987,7 @@ async function generateLookupCondition(
knex, knex,
childAlias, childAlias,
aliasCount, aliasCount,
throwErrorIfInvalid,
); );
return (qbP: Knex.QueryBuilder) => { return (qbP: Knex.QueryBuilder) => {
@ -962,6 +1007,7 @@ async function nestedConditionJoin(
knex, knex,
alias: string, alias: string,
aliasCount: { count: number }, aliasCount: { count: number },
throwErrorIfInvalid = false,
) { ) {
if ( if (
lookupColumn.uidt === UITypes.Lookup || lookupColumn.uidt === UITypes.Lookup ||
@ -1044,6 +1090,7 @@ async function nestedConditionJoin(
knex, knex,
relAlias, relAlias,
aliasCount, aliasCount,
throwErrorIfInvalid,
); );
} else { } else {
switch (relationColOptions.type) { switch (relationColOptions.type) {
@ -1059,6 +1106,8 @@ async function nestedConditionJoin(
}), }),
aliasCount, aliasCount,
relAlias, relAlias,
undefined,
throwErrorIfInvalid,
) )
)(qb); )(qb);
} }
@ -1075,6 +1124,8 @@ async function nestedConditionJoin(
}), }),
aliasCount, aliasCount,
relAlias, relAlias,
undefined,
throwErrorIfInvalid,
) )
)(qb); )(qb);
} }
@ -1091,6 +1142,8 @@ async function nestedConditionJoin(
}), }),
aliasCount, aliasCount,
relAlias, relAlias,
undefined,
throwErrorIfInvalid,
) )
)(qb); )(qb);
} }
@ -1108,6 +1161,8 @@ async function nestedConditionJoin(
}), }),
aliasCount, aliasCount,
alias, alias,
undefined,
throwErrorIfInvalid,
) )
)(qb); )(qb);
} }

11
packages/nocodb/src/db/sortV2.ts

@ -7,6 +7,7 @@ import type {
LookupColumn, LookupColumn,
RollupColumn, RollupColumn,
} from '~/models'; } from '~/models';
import { NcError } from '~/helpers/catchError';
import formulaQueryBuilderv2 from '~/db/formulav2/formulaQueryBuilderv2'; import formulaQueryBuilderv2 from '~/db/formulav2/formulaQueryBuilderv2';
import genRollupSelectv2 from '~/db/genRollupSelectv2'; import genRollupSelectv2 from '~/db/genRollupSelectv2';
import { sanitize } from '~/helpers/sqlSanitize'; import { sanitize } from '~/helpers/sqlSanitize';
@ -17,6 +18,7 @@ export default async function sortV2(
sortList: Sort[], sortList: Sort[],
qb: Knex.QueryBuilder, qb: Knex.QueryBuilder,
alias?: string, alias?: string,
throwErrorIfInvalid = false,
) { ) {
const knex = baseModelSqlv2.dbDriver; const knex = baseModelSqlv2.dbDriver;
@ -32,7 +34,14 @@ export default async function sortV2(
sort = new Sort(_sort); sort = new Sort(_sort);
} }
const column = await sort.getColumn(); const column = await sort.getColumn();
if (!column) continue; if (!column) {
if (throwErrorIfInvalid) {
NcError.unprocessableEntity(
`Invalid column id '${sort.fk_column_id}' in sort`,
);
}
continue;
}
const model = await column.getModel(); const model = await column.getModel();
const nulls = sort.direction === 'desc' ? 'LAST' : 'FIRST'; const nulls = sort.direction === 'desc' ? 'LAST' : 'FIRST';

24
packages/nocodb/src/helpers/catchError.ts

@ -436,23 +436,29 @@ export default function (
}; };
} }
export class BadRequest extends Error {} export class NcBaseError extends Error {
constructor(message: string) {
super(message);
}
}
export class BadRequest extends NcBaseError {}
export class NotAllowed extends Error {} export class NotAllowed extends NcBaseError {}
export class Unauthorized extends Error {} export class Unauthorized extends NcBaseError {}
export class Forbidden extends Error {} export class Forbidden extends NcBaseError {}
export class NotFound extends Error {} export class NotFound extends NcBaseError {}
export class InternalServerError extends Error {} export class InternalServerError extends NcBaseError {}
export class NotImplemented extends Error {} export class NotImplemented extends NcBaseError {}
export class UnprocessableEntity extends Error {} export class UnprocessableEntity extends NcBaseError {}
export class AjvError extends Error { export class AjvError extends NcBaseError {
constructor(param: { message: string; errors: ErrorObject[] }) { constructor(param: { message: string; errors: ErrorObject[] }) {
super(param.message); super(param.message);
this.errors = param.errors; this.errors = param.errors;

14
packages/nocodb/src/helpers/getAst.ts

@ -5,6 +5,7 @@ import type {
LookupColumn, LookupColumn,
Model, Model,
} from '~/models'; } from '~/models';
import { NcError } from '~/helpers/catchError';
import { GalleryView, KanbanView, View } from '~/models'; import { GalleryView, KanbanView, View } from '~/models';
const getAst = async ({ const getAst = async ({
@ -19,6 +20,7 @@ const getAst = async ({
fieldsSet: new Set(), fieldsSet: new Set(),
}, },
getHiddenColumn = query?.['getHiddenColumn'], getHiddenColumn = query?.['getHiddenColumn'],
throwErrorIfInvalidParams = false,
}: { }: {
query?: RequestQuery; query?: RequestQuery;
extractOnlyPrimaries?: boolean; extractOnlyPrimaries?: boolean;
@ -27,6 +29,7 @@ const getAst = async ({
view?: View; view?: View;
dependencyFields?: DependantFields; dependencyFields?: DependantFields;
getHiddenColumn?: boolean; getHiddenColumn?: boolean;
throwErrorIfInvalidParams?: boolean;
}) => { }) => {
// set default values of dependencyFields and nested // set default values of dependencyFields and nested
dependencyFields.nested = dependencyFields.nested || {}; dependencyFields.nested = dependencyFields.nested || {};
@ -63,6 +66,15 @@ const getAst = async ({
let fields = query?.fields || query?.f; let fields = query?.fields || query?.f;
if (fields && fields !== '*') { if (fields && fields !== '*') {
fields = Array.isArray(fields) ? fields : fields.split(','); fields = Array.isArray(fields) ? fields : fields.split(',');
if (throwErrorIfInvalidParams) {
const colAliasMap = await model.getColAliasMapping();
const invalidFields = fields.filter((f) => !colAliasMap[f]);
if (invalidFields.length) {
NcError.unprocessableEntity(
`Following fields are invalid: ${invalidFields.join(', ')}`,
);
}
}
} else { } else {
fields = null; fields = null;
} }
@ -99,6 +111,7 @@ const getAst = async ({
nested: {}, nested: {},
fieldsSet: new Set(), fieldsSet: new Set(),
}), }),
throwErrorIfInvalidParams,
}); });
value = ast; value = ast;
@ -126,6 +139,7 @@ const getAst = async ({
nested: {}, nested: {},
fieldsSet: new Set(), fieldsSet: new Set(),
}), }),
throwErrorIfInvalidParams,
}) })
).ast; ).ast;
} }

7
packages/nocodb/src/services/data-table.service.ts

@ -25,6 +25,7 @@ export class DataTableService {
model, model,
view, view,
query: param.query, query: param.query,
throwErrorIfInvalidParams: true,
}); });
} }
@ -45,7 +46,9 @@ export class DataTableService {
dbDriver: await NcConnectionMgrv2.get(source), dbDriver: await NcConnectionMgrv2.get(source),
}); });
const row = await baseModel.readByPk(param.rowId, false, param.query); const row = await baseModel.readByPk(param.rowId, false, param.query, {
throwErrorIfInvalidParams: true,
});
if (!row) { if (!row) {
NcError.notFound('Row not found'); NcError.notFound('Row not found');
@ -167,7 +170,7 @@ export class DataTableService {
countArgs.filterArr = JSON.parse(countArgs.filterArrJson); countArgs.filterArr = JSON.parse(countArgs.filterArrJson);
} catch (e) {} } catch (e) {}
const count: number = await baseModel.count(countArgs); const count: number = await baseModel.count(countArgs, false, true);
return { count }; return { count };
} }

18
packages/nocodb/src/services/datas.service.ts

@ -7,7 +7,7 @@ import type { BaseModelSqlv2 } from '~/db/BaseModelSqlv2';
import type { PathParams } from '~/modules/datas/helpers'; import type { PathParams } from '~/modules/datas/helpers';
import { getDbRows, getViewAndModelByAliasOrId } from '~/modules/datas/helpers'; import { getDbRows, getViewAndModelByAliasOrId } from '~/modules/datas/helpers';
import { Base, Column, Model, Source, View } from '~/models'; import { Base, Column, Model, Source, View } from '~/models';
import { NcError } from '~/helpers/catchError'; import { NcBaseError, NcError } from '~/helpers/catchError';
import getAst from '~/helpers/getAst'; import getAst from '~/helpers/getAst';
import { PagedResponseImpl } from '~/helpers/PagedResponse'; import { PagedResponseImpl } from '~/helpers/PagedResponse';
import NcConnectionMgrv2 from '~/utils/common/NcConnectionMgrv2'; import NcConnectionMgrv2 from '~/utils/common/NcConnectionMgrv2';
@ -133,6 +133,7 @@ export class DatasService {
view?: View; view?: View;
query: any; query: any;
baseModel?: BaseModelSqlv2; baseModel?: BaseModelSqlv2;
throwErrorIfInvalidParams?: boolean;
}) { }) {
const { model, view, query = {} } = param; const { model, view, query = {} } = param;
@ -146,7 +147,12 @@ export class DatasService {
dbDriver: await NcConnectionMgrv2.get(source), dbDriver: await NcConnectionMgrv2.get(source),
})); }));
const { ast, dependencyFields } = await getAst({ model, query, view }); const { ast, dependencyFields } = await getAst({
model,
query,
view,
throwErrorIfInvalidParams: param.throwErrorIfInvalidParams,
});
const listArgs: any = dependencyFields; const listArgs: any = dependencyFields;
try { try {
@ -163,11 +169,17 @@ export class DatasService {
try { try {
data = await nocoExecute( data = await nocoExecute(
ast, ast,
await baseModel.list(listArgs), await baseModel.list(
listArgs,
false,
false,
param.throwErrorIfInvalidParams,
),
{}, {},
listArgs, listArgs,
); );
} catch (e) { } catch (e) {
if (e instanceof NcBaseError) throw e;
this.logger.log(e); this.logger.log(e);
NcError.internalServerError( NcError.internalServerError(
'Please check server log for more details', 'Please check server log for more details',

21
packages/nocodb/tests/unit/rest/tests/newDataApis.test.ts

@ -865,16 +865,19 @@ function textBased() {
query: { query: {
sort: 'abc', sort: 'abc',
}, },
status: 422,
}); });
await ncAxiosGet({ await ncAxiosGet({
query: { query: {
where: 'abc', where: 'abc',
}, },
status: 422,
}); });
await ncAxiosGet({ await ncAxiosGet({
query: { query: {
fields: 'abc', fields: 'abc',
}, },
status: 422,
}); });
}); });
@ -2109,7 +2112,23 @@ function linkBased() {
linkId: getColumnId(columnsActor, 'Films'), linkId: getColumnId(columnsActor, 'Films'),
rowId: 1, rowId: 1,
}, },
body: [{Id:1}, {Id:3}, {Id:5}, {Id:7}, {Id:9}, {Id:11}, {Id:13}, {Id:15}, {Id:17}, {Id:19}, {Id:21}, {Id:23}, {Id:25}, {Id:27}, {Id:29}], body: [
{ Id: 1 },
{ Id: 3 },
{ Id: 5 },
{ Id: 7 },
{ Id: 9 },
{ Id: 11 },
{ Id: 13 },
{ Id: 15 },
{ Id: 17 },
{ Id: 19 },
{ Id: 21 },
{ Id: 23 },
{ Id: 25 },
{ Id: 27 },
{ Id: 29 },
],
}); });
// verify in Actor table // verify in Actor table

Loading…
Cancel
Save