From 8898481747fb0952e9a8d3b8d0605c7d96d2bcba Mon Sep 17 00:00:00 2001 From: jeremypiednoel Date: Fri, 18 May 2018 10:57:47 -0400 Subject: [PATCH 1/9] adding nor to specialQuerykeys --- src/Controllers/DatabaseController.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index ba814fb0a5..1dbfda17b2 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -50,7 +50,7 @@ const transformObjectACL = ({ ACL, ...result }) => { return result; } -const specialQuerykeys = ['$and', '$or', '_rperm', '_wperm', '_perishable_token', '_email_verify_token', '_email_verify_token_expires_at', '_account_lockout_expires_at', '_failed_login_count']; +const specialQuerykeys = ['$and', '$or', '$nor', '_rperm', '_wperm', '_perishable_token', '_email_verify_token', '_email_verify_token_expires_at', '_account_lockout_expires_at', '_failed_login_count']; const isSpecialQueryKey = key => { return specialQuerykeys.indexOf(key) >= 0; From b6683569e169adf7026f80623ca339d660bfd331 Mon Sep 17 00:00:00 2001 From: jeremypiednoel Date: Fri, 18 May 2018 11:00:13 -0400 Subject: [PATCH 2/9] adding nor suport --- src/Adapters/Storage/Mongo/MongoTransform.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index f1952ea5da..c60db85d89 100644 --- a/src/Adapters/Storage/Mongo/MongoTransform.js +++ b/src/Adapters/Storage/Mongo/MongoTransform.js @@ -247,9 +247,9 @@ function transformQueryKeyValue(className, key, value, schema) { case '_perishable_token': case '_email_verify_token': return {key, value} case '$or': - return {key: '$or', value: value.map(subQuery => transformWhere(className, subQuery, schema))}; case '$and': - return {key: '$and', value: value.map(subQuery => transformWhere(className, subQuery, schema))}; + case '$nor': + return {key: key, value: value.map(subQuery => transformWhere(className, subQuery, schema))}; case 'lastUsed': if (valueAsDate(value)) { return {key: '_last_used', value: valueAsDate(value)} From 9431576c67d3c550737226dd77d38b438ed6a511 Mon Sep 17 00:00:00 2001 From: jeremypiednoel Date: Fri, 18 May 2018 11:00:46 -0400 Subject: [PATCH 3/9] adding test --- spec/ParseQuery.spec.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/spec/ParseQuery.spec.js b/spec/ParseQuery.spec.js index d3881d5e2e..faf6ddd089 100644 --- a/spec/ParseQuery.spec.js +++ b/spec/ParseQuery.spec.js @@ -2567,6 +2567,36 @@ describe('Parse.Query testing', () => { }); }); + it('$select inside $nor', (done) => { + const objects = Array.from(Array(10).keys()).map((rating) => { + return new TestObject({ 'rating': rating }); + }); + + const highValue = 5; + const lowValue = 3; + const options = Object.assign({}, masterKeyOptions, { + body: { + where: { + $nor: [ + { rating : { $gt : highValue } }, + { rating : { $lte : lowValue } }, + ] + }, + } + }); + + Parse.Object.saveAll(objects).then(() => { + return rp.get(Parse.serverURL + "/classes/TestObject", options); + }).then((results) => { + expect(results.results.length).toBe(highValue - lowValue); + expect(results.results.every(res => res.rating > lowValue && res.rating <= highValue)).toBe(true); + done(); + }, (error) => { + jfail(error); + done(); + }); + }); + it("dontSelect query", function(done) { const RestaurantObject = Parse.Object.extend("Restaurant"); const PersonObject = Parse.Object.extend("Person"); From 1c597b74d76a6e61daf4410c010da75a267063b1 Mon Sep 17 00:00:00 2001 From: jeremypiednoel Date: Fri, 18 May 2018 11:23:38 -0400 Subject: [PATCH 4/9] CRLF --- .github/ISSUE_TEMPLATE/Bug_report.md | 102 +++++++++++++-------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index ee2a65f461..9e9c19e399 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -1,51 +1,51 @@ ---- -name: Report an issue -about: Report an issue on parse-server - ---- - -We use GitHub Issues for bugs. - -If you have a non-bug question, ask on Stack Overflow or Server Fault: -- https://stackoverflow.com/questions/tagged/parse.com -- https://serverfault.com/tags/parse - -If you have a vulnerability disclosure, please follow our policy available here https://github.com/parse-community/parse-server/blob/master/SECURITY.md - -You may also search through existing issues before opening a new one: https://github.com/parse-community/parse-server/issues?utf8=%E2%9C%93&q=is%3Aissue - ---- Please use this template. If you don't use this template, your issue may be closed without comment. --- - -### Issue Description - -Describe your issue in as much detail as possible. - -### Steps to reproduce - -Please include a detailed list of steps that reproduce the issue. Include curl commands when applicable. - -#### Expected Results - -What you expected to happen. - -#### Actual Outcome - -What is happening instead. - -### Environment Setup - -- **Server** - - parse-server version (Be specific! Don't say 'latest'.) : [FILL THIS OUT] - - Operating System: [FILL THIS OUT] - - Hardware: [FILL THIS OUT] - - Localhost or remote server? (AWS, Heroku, Azure, Digital Ocean, etc): [FILL THIS OUT] - -- **Database** - - MongoDB version: [FILL THIS OUT] - - Storage engine: [FILL THIS OUT] - - Hardware: [FILL THIS OUT] - - Localhost or remote server? (AWS, mLab, ObjectRocket, Digital Ocean, etc): [FILL THIS OUT] - -### Logs/Trace - -Include all relevant logs. You can turn on additional logging by configuring VERBOSE=1 in your environment. +--- +name: Report an issue +about: Report an issue on parse-server + +--- + +We use GitHub Issues for bugs. + +If you have a non-bug question, ask on Stack Overflow or Server Fault: +- https://stackoverflow.com/questions/tagged/parse.com +- https://serverfault.com/tags/parse + +If you have a vulnerability disclosure, please follow our policy available here https://github.com/parse-community/parse-server/blob/master/SECURITY.md + +You may also search through existing issues before opening a new one: https://github.com/parse-community/parse-server/issues?utf8=%E2%9C%93&q=is%3Aissue + +--- Please use this template. If you don't use this template, your issue may be closed without comment. --- + +### Issue Description + +Describe your issue in as much detail as possible. + +### Steps to reproduce + +Please include a detailed list of steps that reproduce the issue. Include curl commands when applicable. + +#### Expected Results + +What you expected to happen. + +#### Actual Outcome + +What is happening instead. + +### Environment Setup + +- **Server** + - parse-server version (Be specific! Don't say 'latest'.) : [FILL THIS OUT] + - Operating System: [FILL THIS OUT] + - Hardware: [FILL THIS OUT] + - Localhost or remote server? (AWS, Heroku, Azure, Digital Ocean, etc): [FILL THIS OUT] + +- **Database** + - MongoDB version: [FILL THIS OUT] + - Storage engine: [FILL THIS OUT] + - Hardware: [FILL THIS OUT] + - Localhost or remote server? (AWS, mLab, ObjectRocket, Digital Ocean, etc): [FILL THIS OUT] + +### Logs/Trace + +Include all relevant logs. You can turn on additional logging by configuring VERBOSE=1 in your environment. From 64a9ea57554c7174a9f2ac14a21c8952d4c58453 Mon Sep 17 00:00:00 2001 From: jeremypiednoel Date: Fri, 18 May 2018 12:50:23 -0400 Subject: [PATCH 5/9] adding postgres NOR --- src/Adapters/Storage/Postgres/PostgresStorageAdapter.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js index ec98ade758..b3d2021354 100644 --- a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js +++ b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js @@ -306,12 +306,15 @@ const buildWhereClause = ({ schema, query, index }): WhereClause => { patterns.push(`$${index}:name = $${index + 1}`); values.push(fieldName, fieldValue); index += 2; - } else if (fieldName === '$or' || fieldName === '$and') { + } else if (['$or', '$nor', '$and'].includes(fieldName)) { const clauses = []; const clauseValues = []; fieldValue.forEach((subQuery) => { const clause = buildWhereClause({ schema, query: subQuery, index }); if (clause.pattern.length > 0) { + if (fieldName === '$nor') { + clause.pattern = `(NOT ${clause.pattern})`; + } clauses.push(clause.pattern); clauseValues.push(...clause.values); index += clause.values.length; From d493f391fbb6c1e5a6cee7acd745e37e60c3db34 Mon Sep 17 00:00:00 2001 From: jeremypiednoel Date: Fri, 18 May 2018 12:50:57 -0400 Subject: [PATCH 6/9] adding nor validation --- src/Controllers/DatabaseController.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 1dbfda17b2..e4aa3cc647 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -62,7 +62,7 @@ const validateQuery = (query: any): void => { } if (query.$or) { - if (query.$or instanceof Array) { + if (query.$or instanceof Array && query.$or.length > 1) { query.$or.forEach(validateQuery); /* In MongoDB, $or queries which are not alone at the top level of the @@ -99,15 +99,23 @@ const validateQuery = (query: any): void => { }); query.$or.forEach(validateQuery); } else { - throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Bad $or format - use an array value.'); + throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Bad $or format - use an array of at least 2 values.'); } } if (query.$and) { - if (query.$and instanceof Array) { + if (query.$and instanceof Array && query.$and.length > 1) { query.$and.forEach(validateQuery); } else { - throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Bad $and format - use an array value.'); + throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Bad $and format - use an array of at least 2 values.'); + } + } + + if (query.$nor) { + if (query.$nor instanceof Array && query.$nor.length > 0) { + query.$nor.forEach(validateQuery); + } else { + throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Bad $nor format - use an array of at least 1 value.'); } } From 6944639da5b1942980783014527bc1372ef7322e Mon Sep 17 00:00:00 2001 From: jeremypiednoel Date: Fri, 18 May 2018 12:51:12 -0400 Subject: [PATCH 7/9] adding NOR test --- spec/ParseQuery.spec.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/spec/ParseQuery.spec.js b/spec/ParseQuery.spec.js index faf6ddd089..a49512a46e 100644 --- a/spec/ParseQuery.spec.js +++ b/spec/ParseQuery.spec.js @@ -2591,8 +2591,20 @@ describe('Parse.Query testing', () => { expect(results.results.length).toBe(highValue - lowValue); expect(results.results.every(res => res.rating > lowValue && res.rating <= highValue)).toBe(true); done(); - }, (error) => { - jfail(error); + }); + }); + + it('$nor invalid query', (done) => { + const options = Object.assign({}, masterKeyOptions, { + body: { + where: { $nor: [] }, + } + }); + const obj = new TestObject(); + obj.save().then(() => { + return rp.get(Parse.serverURL + "/classes/TestObject", options); + }).then(done.fail).catch((error) => { + equal(error.error.code, Parse.Error.INVALID_JSON); done(); }); }); From 44157487cad1cd293cca0c64d0ee99ad0f483b84 Mon Sep 17 00:00:00 2001 From: jeremypiednoel Date: Fri, 18 May 2018 13:22:08 -0400 Subject: [PATCH 8/9] adding test amd fixing NOR cases --- spec/ParseQuery.spec.js | 21 ++++++++++++++++++--- src/Controllers/DatabaseController.js | 8 ++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/spec/ParseQuery.spec.js b/spec/ParseQuery.spec.js index a49512a46e..1a755d5a75 100644 --- a/spec/ParseQuery.spec.js +++ b/spec/ParseQuery.spec.js @@ -2567,7 +2567,7 @@ describe('Parse.Query testing', () => { }); }); - it('$select inside $nor', (done) => { + it('$nor valid query', (done) => { const objects = Array.from(Array(10).keys()).map((rating) => { return new TestObject({ 'rating': rating }); }); @@ -2594,7 +2594,7 @@ describe('Parse.Query testing', () => { }); }); - it('$nor invalid query', (done) => { + it('$nor invalid query - empty array', (done) => { const options = Object.assign({}, masterKeyOptions, { body: { where: { $nor: [] }, @@ -2604,7 +2604,22 @@ describe('Parse.Query testing', () => { obj.save().then(() => { return rp.get(Parse.serverURL + "/classes/TestObject", options); }).then(done.fail).catch((error) => { - equal(error.error.code, Parse.Error.INVALID_JSON); + equal(error.error.code, Parse.Error.INVALID_QUERY); + done(); + }); + }); + + it('$nor invalid query - wrong type', (done) => { + const options = Object.assign({}, masterKeyOptions, { + body: { + where: { $nor: 1337 }, + } + }); + const obj = new TestObject(); + obj.save().then(() => { + return rp.get(Parse.serverURL + "/classes/TestObject", options); + }).then(done.fail).catch((error) => { + equal(error.error.code, Parse.Error.INVALID_QUERY); done(); }); }); diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index e4aa3cc647..91c12ed55e 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -62,7 +62,7 @@ const validateQuery = (query: any): void => { } if (query.$or) { - if (query.$or instanceof Array && query.$or.length > 1) { + if (query.$or instanceof Array) { query.$or.forEach(validateQuery); /* In MongoDB, $or queries which are not alone at the top level of the @@ -99,15 +99,15 @@ const validateQuery = (query: any): void => { }); query.$or.forEach(validateQuery); } else { - throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Bad $or format - use an array of at least 2 values.'); + throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Bad $or format - use an array value.'); } } if (query.$and) { - if (query.$and instanceof Array && query.$and.length > 1) { + if (query.$and instanceof Array) { query.$and.forEach(validateQuery); } else { - throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Bad $and format - use an array of at least 2 values.'); + throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Bad $and format - use an array value.'); } } From 318501c923aefe99c4ad95001d76f2a7d80bed1e Mon Sep 17 00:00:00 2001 From: jeremypiednoel Date: Fri, 18 May 2018 15:01:45 -0400 Subject: [PATCH 9/9] revert the nor logic --- .../Storage/Postgres/PostgresStorageAdapter.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js index b3d2021354..cd8759b92d 100644 --- a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js +++ b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js @@ -312,16 +312,16 @@ const buildWhereClause = ({ schema, query, index }): WhereClause => { fieldValue.forEach((subQuery) => { const clause = buildWhereClause({ schema, query: subQuery, index }); if (clause.pattern.length > 0) { - if (fieldName === '$nor') { - clause.pattern = `(NOT ${clause.pattern})`; - } clauses.push(clause.pattern); clauseValues.push(...clause.values); index += clause.values.length; } }); - const orOrAnd = fieldName === '$or' ? ' OR ' : ' AND '; - patterns.push(`(${clauses.join(orOrAnd)})`); + + const orOrAnd = fieldName === '$and' ? ' AND ' : ' OR '; + const not = fieldName === '$nor' ? ' NOT ' : ''; + + patterns.push(`${not}(${clauses.join(orOrAnd)})`); values.push(...clauseValues); }