diff --git a/src/ParseQuery.js b/src/ParseQuery.js index 77fc9b146..796856c79 100644 --- a/src/ParseQuery.js +++ b/src/ParseQuery.js @@ -67,16 +67,19 @@ function handleSelectResult(data: any, select: Array){ pathComponents.forEach((component, index, arr) => { // add keys if the expected data is missing - if (!obj[component]) { - obj[component] = (index == arr.length-1) ? undefined : {}; + if (obj && !obj.hasOwnProperty(component)) { + obj[component] = undefined; + } + if (obj !== undefined) { + obj = obj[component]; } - obj = obj[component]; //add this path component to the server mask so we can fill it in later if needed if (index < arr.length-1) { if (!serverMask[component]) { serverMask[component] = {}; } + serverMask = serverMask[component]; } }); } @@ -100,8 +103,10 @@ function handleSelectResult(data: any, select: Array){ } } for (var key in mask) { - //traverse into objects as needed - copyMissingDataWithMask(src[key], dest[key], mask[key], true); + if (dest[key] !== undefined && dest[key] !== null && src !== undefined && src !== null) { + //traverse into objects as needed + copyMissingDataWithMask(src[key], dest[key], mask[key], true); + } } } diff --git a/src/__tests__/ParseQuery-test.js b/src/__tests__/ParseQuery-test.js index 61d3b6cc1..d1d329b39 100644 --- a/src/__tests__/ParseQuery-test.js +++ b/src/__tests__/ParseQuery-test.js @@ -1584,4 +1584,105 @@ describe('ParseQuery', () => { }); }); + + it('selecting sub-objects does not inject objects when sub-object does not exist', (done) => { + jest.dontMock("../ParseObject"); + jest.resetModules(); + ParseObject = require('../ParseObject').default; + CoreManager = require('../CoreManager'); + ParseQuery = require('../ParseQuery').default; + + ParseObject.enableSingleInstance(); + + var objectToReturn = { + objectId: 'T01', + name: 'Name', + tbd: 'exists', + className:"Thing", + createdAt: '2017-01-10T10:00:00Z' + }; + + CoreManager.setQueryController({ + find(className, params, options) { + return ParsePromise.as({ + results: [objectToReturn] + }); + } + }); + + var q = new ParseQuery("Thing"); + q.select("other", "tbd", "subObject.key1") + var testObject; + return q.find().then((results) => { + testObject = results[0]; + + expect(testObject.get("name")).toBe("Name"); + expect(testObject.has("other")).toBe(false); + expect(testObject.has("subObject")).toBe(false); + + }).then(() => { + done(); + }, (error) => { + done.fail(error); + }); + }); + + it('removes missing sub objects from the cached object when they are selected', (done) => { + jest.dontMock("../ParseObject"); + jest.resetModules(); + ParseObject = require('../ParseObject').default; + CoreManager = require('../CoreManager'); + ParseQuery = require('../ParseQuery').default; + + ParseObject.enableSingleInstance(); + + var objectToReturn = { + objectId: 'T01', + name: 'Name', + tbd: 'exists', + className:"Thing", + subObject1: {foo:"bar"}, + subObject2: {foo:"bar"}, + subObject3: {foo:"bar"}, + subObject5: {subSubObject:{foo:"foo", bar:"bar"}}, + createdAt: '2017-01-10T10:00:00Z' + }; + + CoreManager.setQueryController({ + find(className, params, options) { + return ParsePromise.as({ + results: [objectToReturn] + }); + } + }); + + var q = new ParseQuery("Thing"); + var testObject; + return q.find().then((results) => { + testObject = results[0]; + + expect(testObject.has("subObject1")).toBe(true); + expect(testObject.has("subObject2")).toBe(true); + expect(testObject.has("subObject3")).toBe(true); + expect(testObject.has("subObject4")).toBe(false); + + var q2 = new ParseQuery("Thing"); + q.select("name","subObject1", "subObject2.foo", "subObject4.foo", "subObject5.subSubObject.foo"); + objectToReturn = { objectId: 'T01', name:"Name", subObject4: {foo:"bar"}, subObject5: {subSubObject:{}}}; + return q.find(); + }).then((results)=>{ + expect(testObject.has("subObject1")).toBe(false); //selected and not returned + expect(testObject.has("subObject2")).toBe(false); //selected and not returned + expect(testObject.has("subObject3")).toBe(true); //not selected, so should still be there + expect(testObject.has("subObject4")).toBe(true); //selected and just added + expect(testObject.has("subObject5")).toBe(true); + expect(testObject.get("subObject5").subSubObject).toBeDefined(); + expect(testObject.get("subObject5").subSubObject.bar).toBeDefined(); //not selected but a sibiling was, so should still be there + }).then(() => { + done(); + }, (error) => { + done.fail(error); + }); + }); + });