* Adds tests that reproduce the issue

* Use values from keys to force include when needed
This commit is contained in:
Florent Vilmart
2016-12-06 16:28:55 -05:00
committed by GitHub
parent 998b271994
commit fb36dfa26f
2 changed files with 86 additions and 20 deletions

View File

@@ -5,6 +5,7 @@ var SchemaController = require('./Controllers/SchemaController');
var Parse = require('parse/node').Parse;
const triggers = require('./triggers');
const AlwaysSelectedKeys = ['objectId', 'createdAt', 'updatedAt'];
// restOptions can include:
// skip
// limit
@@ -52,15 +53,36 @@ function RestQuery(config, auth, className, restWhere = {}, restOptions = {}, cl
// this.include = [['foo'], ['foo', 'baz'], ['foo', 'bar']]
this.include = [];
// If we have keys, we probably want to force some includes (n-1 level)
// See issue: https://github.com/ParsePlatform/parse-server/issues/3185
if (restOptions.hasOwnProperty('keys')) {
const keysForInclude = restOptions.keys.split(',').filter((key) => {
// At least 2 components
return key.split(".").length > 1;
}).map((key) => {
// Slice the last component (a.b.c -> a.b)
// Otherwise we'll include one level too much.
return key.slice(0, key.lastIndexOf("."));
}).join(',');
// Concat the possibly present include string with the one from the keys
// Dedup / sorting is handle in 'include' case.
if (keysForInclude.length > 0) {
if (!restOptions.include || restOptions.include.length == 0) {
restOptions.include = keysForInclude;
} else {
restOptions.include += "," + keysForInclude;
}
}
}
for (var option in restOptions) {
switch(option) {
case 'keys':
this.keys = new Set(restOptions.keys.split(','));
// Add the default
this.keys.add('objectId');
this.keys.add('createdAt');
this.keys.add('updatedAt');
case 'keys': {
const keys = restOptions.keys.split(',').concat(AlwaysSelectedKeys);
this.keys = Array.from(new Set(keys));
break;
}
case 'count':
this.doCount = true;
break;
@@ -80,22 +102,26 @@ function RestQuery(config, auth, className, restWhere = {}, restOptions = {}, cl
}
this.findOptions.sort = sortMap;
break;
case 'include':
var paths = restOptions.include.split(',');
var pathSet = {};
for (var path of paths) {
// Add all prefixes with a .-split to pathSet
var parts = path.split('.');
for (var len = 1; len <= parts.length; len++) {
pathSet[parts.slice(0, len).join('.')] = true;
}
}
this.include = Object.keys(pathSet).sort((a, b) => {
return a.length - b.length;
}).map((s) => {
case 'include': {
const paths = restOptions.include.split(',');
// Load the existing includes (from keys)
const pathSet = paths.reduce((memo, path) => {
// Split each paths on . (a.b.c -> [a,b,c])
// reduce to create all paths
// ([a,b,c] -> {a: true, 'a.b': true, 'a.b.c': true})
return path.split('.').reduce((memo, path, index, parts) => {
memo[parts.slice(0, index+1).join('.')] = true;
return memo;
}, memo);
}, {});
this.include = Object.keys(pathSet).map((s) => {
return s.split('.');
}).sort((a, b) => {
return a.length - b.length; // Sort by number of components
});
break;
}
case 'redirectClassNameForKey':
this.redirectKey = restOptions.redirectClassNameForKey;
this.redirectClassName = null;
@@ -421,7 +447,7 @@ RestQuery.prototype.runFind = function(options = {}) {
}
let findOptions = Object.assign({}, this.findOptions);
if (this.keys) {
findOptions.keys = Array.from(this.keys).map((key) => {
findOptions.keys = this.keys.map((key) => {
return key.split('.')[0];
});
}