From 288525b0b5a2d29fb93dc01bd7f1148cd2128a0e Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Thu, 14 Jun 2018 11:50:45 -0500 Subject: [PATCH 1/2] add includeAll option --- spec/ParseQuery.spec.js | 69 ++++++++++++++++++++++++++++++++++++ src/RestQuery.js | 29 +++++++++++++++ src/Routers/ClassesRouter.js | 5 ++- 3 files changed, 102 insertions(+), 1 deletion(-) diff --git a/spec/ParseQuery.spec.js b/spec/ParseQuery.spec.js index 533c23d980..00d57f571a 100644 --- a/spec/ParseQuery.spec.js +++ b/spec/ParseQuery.spec.js @@ -3684,6 +3684,75 @@ describe('Parse.Query testing', () => { }); }); + it("includeAll", (done) => { + const child1 = new TestObject({ foo: 'bar', name: 'al' }); + const child2 = new TestObject({ foo: 'baz', name: 'flo' }); + const child3 = new TestObject({ foo: 'bad', name: 'mo' }); + const parent = new Container({ child1, child2, child3 }); + Parse.Object.saveAll([parent, child1, child2, child3]).then(() => { + const options = Object.assign({}, masterKeyOptions, { + body: { + where: { objectId: parent.id }, + includeAll: true, + } + }); + return rp.get(Parse.serverURL + "/classes/Container", options); + }).then((resp) => { + const result = resp.results[0]; + equal(result.child1.foo, 'bar'); + equal(result.child2.foo, 'baz'); + equal(result.child3.foo, 'bad'); + equal(result.child1.name, 'al'); + equal(result.child2.name, 'flo'); + equal(result.child3.name, 'mo'); + done(); + }); + }); + + it('select nested keys 2 level includeAll', (done) => { + const Foobar = new Parse.Object('Foobar'); + const BarBaz = new Parse.Object('Barbaz'); + const Bazoo = new Parse.Object('Bazoo'); + const Tang = new Parse.Object('Tang'); + + Bazoo.set('some', 'thing'); + Bazoo.set('otherSome', 'value'); + Bazoo.save().then(() => { + BarBaz.set('key', 'value'); + BarBaz.set('otherKey', 'value'); + BarBaz.set('bazoo', Bazoo); + return BarBaz.save(); + }).then(() => { + Tang.set('clan', 'wu'); + return Tang.save(); + }).then(() => { + Foobar.set('foo', 'bar'); + Foobar.set('fizz', 'buzz'); + Foobar.set('barBaz', BarBaz); + Foobar.set('group', Tang); + return Foobar.save(); + }).then((savedFoobar) => { + const options = Object.assign({}, masterKeyOptions, { + body: { + where: { objectId: savedFoobar.id }, + includeAll: true, + keys: 'fizz,barBaz.key,barBaz.bazoo.some', + } + }); + return rp.get(Parse.serverURL + "/classes/Foobar", options); + }).then((resp) => { + const result = resp.results[0]; + equal(result.group.clan, 'wu'); + equal(result.foo, undefined); + equal(result.fizz, 'buzz'); + equal(result.barBaz.key, 'value'); + equal(result.barBaz.otherKey, undefined); + equal(result.barBaz.bazoo.some, 'thing'); + equal(result.barBaz.bazoo.otherSome, undefined); + done(); + }) + }); + it('select nested keys 2 level without include (issue #3185)', function(done) { const Foobar = new Parse.Object('Foobar'); const BarBaz = new Parse.Object('Barbaz'); diff --git a/src/RestQuery.js b/src/RestQuery.js index 8bf8d52992..21afbe3566 100644 --- a/src/RestQuery.js +++ b/src/RestQuery.js @@ -44,6 +44,7 @@ function RestQuery(config, auth, className, restWhere = {}, restOptions = {}, cl } this.doCount = false; + this.includeAll = false; // The format for this.include is not the same as the format for the // include option - it's the paths we should include, in order, @@ -86,6 +87,9 @@ function RestQuery(config, auth, className, restWhere = {}, restOptions = {}, cl case 'count': this.doCount = true; break; + case 'includeAll': + this.includeAll = true; + break; case 'distinct': case 'pipeline': case 'skip': @@ -149,6 +153,8 @@ function RestQuery(config, auth, className, restWhere = {}, restOptions = {}, cl RestQuery.prototype.execute = function(executeOptions) { return Promise.resolve().then(() => { return this.buildRestWhere(); + }).then(() => { + return this.handleIncludeAll(); }).then(() => { return this.runFind(executeOptions); }).then(() => { @@ -552,6 +558,29 @@ RestQuery.prototype.runCount = function() { }); }; +RestQuery.prototype.handleIncludeAll = function() { + if (!this.includeAll) { + return; + } + return this.config.database.loadSchema() + .then(schemaController => schemaController.getOneSchema(this.className)) + .then(schema => { + const includeFields = []; + const keyFields = []; + for (const field in schema.fields) { + if (schema.fields[field].type && schema.fields[field].type === 'Pointer') { + includeFields.push([field]); + keyFields.push(field); + } + } + // Add fields to include, keys, remove dups + this.include = [...new Set([...this.include, ...includeFields])]; + if (this.keys) { + this.keys = [...new Set([...this.keys, ...keyFields])]; + } + }); +}; + // Augments this.response with data at the paths provided in this.include. RestQuery.prototype.handleInclude = function() { if (this.include.length == 0) { diff --git a/src/Routers/ClassesRouter.js b/src/Routers/ClassesRouter.js index a9cb49333f..e7205015b8 100644 --- a/src/Routers/ClassesRouter.js +++ b/src/Routers/ClassesRouter.js @@ -100,7 +100,7 @@ export class ClassesRouter extends PromiseRouter { static optionsFromBody(body) { const allowConstraints = ['skip', 'limit', 'order', 'count', 'keys', - 'include', 'redirectClassNameForKey', 'where']; + 'include', 'includeAll', 'redirectClassNameForKey', 'where']; for (const key of Object.keys(body)) { if (allowConstraints.indexOf(key) === -1) { @@ -128,6 +128,9 @@ export class ClassesRouter extends PromiseRouter { if (body.include) { options.include = String(body.include); } + if (body.includeAll) { + options.includeAll = true; + } return options; } From 9ae094748e15707644674a6c609ae97acc1f6c24 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Sat, 16 Jun 2018 11:25:49 -0500 Subject: [PATCH 2/2] nit --- spec/ParseQuery.spec.js | 6 +++--- src/RestQuery.js | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/spec/ParseQuery.spec.js b/spec/ParseQuery.spec.js index 00d57f571a..ef4a19908a 100644 --- a/spec/ParseQuery.spec.js +++ b/spec/ParseQuery.spec.js @@ -3684,8 +3684,8 @@ describe('Parse.Query testing', () => { }); }); - it("includeAll", (done) => { - const child1 = new TestObject({ foo: 'bar', name: 'al' }); + it('includeAll', (done) => { + const child1 = new TestObject({ foo: 'bar', name: 'ac' }); const child2 = new TestObject({ foo: 'baz', name: 'flo' }); const child3 = new TestObject({ foo: 'bad', name: 'mo' }); const parent = new Container({ child1, child2, child3 }); @@ -3702,7 +3702,7 @@ describe('Parse.Query testing', () => { equal(result.child1.foo, 'bar'); equal(result.child2.foo, 'baz'); equal(result.child3.foo, 'bad'); - equal(result.child1.name, 'al'); + equal(result.child1.name, 'ac'); equal(result.child2.name, 'flo'); equal(result.child3.name, 'mo'); done(); diff --git a/src/RestQuery.js b/src/RestQuery.js index 21afbe3566..5134102808 100644 --- a/src/RestQuery.js +++ b/src/RestQuery.js @@ -558,6 +558,7 @@ RestQuery.prototype.runCount = function() { }); }; +// Augments this.response with all pointers on an object RestQuery.prototype.handleIncludeAll = function() { if (!this.includeAll) { return; @@ -575,6 +576,7 @@ RestQuery.prototype.handleIncludeAll = function() { } // Add fields to include, keys, remove dups this.include = [...new Set([...this.include, ...includeFields])]; + // if this.keys not set, then all keys are already included if (this.keys) { this.keys = [...new Set([...this.keys, ...keyFields])]; }