diff --git a/packages/adapter-commons/lib/filter-query.js b/packages/adapter-commons/lib/filter-query.js index 42d79b069b..c3d1788574 100644 --- a/packages/adapter-commons/lib/filter-query.js +++ b/packages/adapter-commons/lib/filter-query.js @@ -1,4 +1,5 @@ const { _ } = require('@feathersjs/commons'); +const { BadRequest } = require('@feathersjs/errors'); function parse (number) { if (typeof number !== 'undefined') { @@ -33,15 +34,21 @@ function convertSort (sort) { }, {}); } -function cleanQuery (query, operators) { +function cleanQuery (query, operators, filters) { if (_.isObject(query) && query.constructor === {}.constructor) { const result = {}; - _.each(query, (query, key) => { - if (key[0] === '$' && operators.indexOf(key) === -1) { - return; + _.each(query, (value, key) => { + if (key[0] === '$') { + if(filters[key] !== undefined) { + return; + } + + if(!operators.includes(key)) { + throw new BadRequest(`Invalid query parameter ${key}`, query); + } } - result[key] = cleanQuery(query, operators); + result[key] = cleanQuery(value, operators, filters); }); return result; } @@ -91,7 +98,7 @@ module.exports = function filterQuery (query, options = {}) { result.filters = assignFilters({}, query, FILTERS, options); result.filters = assignFilters(result.filters, query, additionalFilters, options); - result.query = cleanQuery(query, OPERATORS.concat(additionalOperators)); + result.query = cleanQuery(query, OPERATORS.concat(additionalOperators), result.filters); return result; }; diff --git a/packages/adapter-commons/test/filter-query.test.js b/packages/adapter-commons/test/filter-query.test.js index 69c852a1e6..60223cdc06 100644 --- a/packages/adapter-commons/test/filter-query.test.js +++ b/packages/adapter-commons/test/filter-query.test.js @@ -37,8 +37,19 @@ describe('@feathersjs/adapter-commons/filterQuery', () => { assert.deepStrictEqual(query, {}); }); + it('throws an error when special parameter is not known', () => { + try { + const query = { $foo: 1 }; + filterQuery(query); + assert.ok(false, 'Should never get here'); + } catch (error) { + assert.strictEqual(error.name, 'BadRequest'); + assert.strictEqual(error.message, 'Invalid query parameter $foo'); + } + }); + it('returns undefined when not present in query', () => { - const query = { $foo: 1 }; + const query = { foo: 1 }; const { filters } = filterQuery(query); assert.strictEqual(filters.$sort, undefined); @@ -58,7 +69,7 @@ describe('@feathersjs/adapter-commons/filterQuery', () => { }); it('returns undefined when not present in query', () => { - const query = { $foo: 1 }; + const query = { foo: 1 }; const { filters } = filterQuery(query); assert.strictEqual(filters.$limit, undefined); @@ -113,7 +124,7 @@ describe('@feathersjs/adapter-commons/filterQuery', () => { }); it('returns undefined when not present in query', () => { - const query = { $foo: 1 }; + const query = { foo: 1 }; const { filters } = filterQuery(query); assert.strictEqual(filters.$skip, undefined); @@ -142,7 +153,7 @@ describe('@feathersjs/adapter-commons/filterQuery', () => { }); it('returns undefined when not present in query', () => { - const query = { $foo: 1 }; + const query = { foo: 1 }; const { filters } = filterQuery(query); assert.strictEqual(filters.$select, undefined); @@ -161,28 +172,29 @@ describe('@feathersjs/adapter-commons/filterQuery', () => { }); describe('additional filters', () => { - beforeEach(() => { - this.query = { $select: 1, $known: 1, $unknown: 1 }; - }); - - it('returns only default filters when no additionals', () => { - const { filters } = filterQuery(this.query); - - assert.strictEqual(filters.$unknown, undefined); - assert.strictEqual(filters.$known, undefined); - assert.strictEqual(filters.$select, 1); + it('throw error when not set as additionals', () => { + try { + filterQuery({ $select: 1, $known: 1 }); + assert.ok(false, 'Should never get here'); + } catch(error) { + assert.strictEqual(error.message, 'Invalid query parameter $known'); + } }); it('returns default and known additional filters (array)', () => { - const { filters } = filterQuery(this.query, { filters: [ '$known' ] }); + const query = { $select: ['a', 'b'], $known: 1, $unknown: 1 }; + const { filters } = filterQuery(query, { filters: [ '$known', '$unknown' ] }); - assert.strictEqual(filters.$unknown, undefined); + assert.strictEqual(filters.$unknown, 1); assert.strictEqual(filters.$known, 1); - assert.strictEqual(filters.$select, 1); + assert.deepStrictEqual(filters.$select, [ 'a', 'b' ]); }); it('returns default and known additional filters (object)', () => { - const { filters } = filterQuery(this.query, { filters: { $known: (value) => value.toString() } }); + const { filters } = filterQuery({ + $known: 1, + $select: 1 + }, { filters: { $known: (value) => value.toString() } }); assert.strictEqual(filters.$unknown, undefined); assert.strictEqual(filters.$known, '1'); @@ -191,32 +203,14 @@ describe('@feathersjs/adapter-commons/filterQuery', () => { }); describe('additional operators', () => { - beforeEach(() => { - this.query = { $ne: 1, $known: 1, $unknown: 1 }; - }); - - it('returns query with only default operators when no additionals', () => { - const { query } = filterQuery(this.query); - - assert.strictEqual(query.$ne, 1); - assert.strictEqual(query.$known, undefined); - assert.strictEqual(query.$unknown, undefined); - }); - it('returns query with default and known additional operators', () => { - const { query } = filterQuery(this.query, { operators: [ '$known' ] }); + const { query } = filterQuery({ + $ne: 1, $known: 1 + }, { operators: [ '$known' ] }); assert.strictEqual(query.$ne, 1); assert.strictEqual(query.$known, 1); assert.strictEqual(query.$unknown, undefined); }); - - it('returns query with default and known additional operators (nested)', () => { - const { query } = filterQuery({ field: this.query }, { operators: [ '$known' ] }); - - assert.strictEqual(query.field.$ne, 1); - assert.strictEqual(query.field.$known, 1); - assert.strictEqual(query.field.$unknown, undefined); - }); }); });