diff --git a/integration/test/ParseObjectTest.js b/integration/test/ParseObjectTest.js index a1a3d8ac3..24285663c 100644 --- a/integration/test/ParseObjectTest.js +++ b/integration/test/ParseObjectTest.js @@ -1448,6 +1448,37 @@ describe('Parse Object', () => { }).catch(done.fail); }); + it('can fetchAllIfNeededWithInclude', async () => { + const pointer = new TestObject({ foo: 'bar' }); + const item1 = new Item({ x: 1}); + const item2 = new Item({ x: 2, pointer }); + const items = [item1, item2]; + + await Parse.Object.saveAll(items); + + const container = new Container(); + container.set('items', items); + await container.save(); + + const query = new Parse.Query(Container); + const containerAgain = await query.get(container.id); + + // Fetch objects with no data + const itemsAgain = containerAgain.get('items'); + const item1Again = itemsAgain[0].set('x', 100); + const item2Again = itemsAgain[1]; + + // Override item1 in database, this shouldn't fetch + await item1Again.save(); + + const fetchedItems = await Parse.Object.fetchAllIfNeededWithInclude([item1, item2Again], ['pointer']); + assert.equal(fetchedItems.length, items.length); + assert.equal(fetchedItems[0].get('x'), 1); + assert.equal(fetchedItems[1].get('x'), 2); // item2Again should update + assert.equal(fetchedItems[1].get('pointer').id, pointer.id); + assert.equal(fetchedItems[1].get('pointer').get('foo'), 'bar'); + }); + it('can fetchAllIfNeeded', (done) => { const numItems = 11; const container = new Container(); diff --git a/src/ParseObject.js b/src/ParseObject.js index 55341dd4e..61f9ad83d 100644 --- a/src/ParseObject.js +++ b/src/ParseObject.js @@ -1427,18 +1427,7 @@ class ParseObject { queryOptions.sessionToken = options.sessionToken; } if (options.hasOwnProperty('include')) { - queryOptions.include = []; - if (Array.isArray(options.include)) { - options.include.forEach((key) => { - if (Array.isArray(key)) { - queryOptions.include = queryOptions.include.concat(key); - } else { - queryOptions.include.push(key); - } - }); - } else { - queryOptions.include.push(options.include); - } + queryOptions.include = ParseObject.handleIncludeOptions(options); } return CoreManager.getObjectController().fetch( list, @@ -1481,6 +1470,41 @@ class ParseObject { return ParseObject.fetchAll(list, options); } + /** + * Fetches the given list of Parse.Object if needed. + * If any error is encountered, stops and calls the error handler. + * + * Includes nested Parse.Objects for the provided key. You can use dot + * notation to specify which fields in the included object are also fetched. + * + * If any error is encountered, stops and calls the error handler. + * + *
+   *   Parse.Object.fetchAllIfNeededWithInclude([object1, object2, ...], [pointer1, pointer2, ...])
+   *    .then((list) => {
+   *      // All the objects were fetched.
+   *    }, (error) => {
+   *      // An error occurred while fetching one of the objects.
+   *    });
+   * 
+ * + * @param {Array} list A list of Parse.Object. + * @param {String|Array>} keys The name(s) of the key(s) to include. + * @param {Object} options + * Valid options are: + * @static + */ + static fetchAllIfNeededWithInclude(list: Array, keys: String|Array>, options: RequestOptions) { + options = options || {}; + options.include = keys; + return ParseObject.fetchAllIfNeeded(list, options); + } + /** * Fetches the given list of Parse.Object if needed. * If any error is encountered, stops and calls the error handler. @@ -1508,6 +1532,9 @@ class ParseObject { if (options.hasOwnProperty('sessionToken')) { queryOptions.sessionToken = options.sessionToken; } + if (options.hasOwnProperty('include')) { + queryOptions.include = ParseObject.handleIncludeOptions(options); + } return CoreManager.getObjectController().fetch( list, false, @@ -1515,6 +1542,22 @@ class ParseObject { ); } + static handleIncludeOptions(options) { + let include = []; + if (Array.isArray(options.include)) { + options.include.forEach((key) => { + if (Array.isArray(key)) { + include = include.concat(key); + } else { + include.push(key); + } + }); + } else { + include.push(options.include); + } + return include; + } + /** * Destroy the given list of models on the server if it was already persisted. *