Merge pull request #64 from strongloop/feature/fix-include
Fix the response for included models following relations
This commit is contained in:
commit
3b6070fe58
18
lib/dao.js
18
lib/dao.js
|
@ -542,11 +542,21 @@ DataAccessObject.find = function find(params, cb) {
|
||||||
|
|
||||||
obj._initProperties(d, false, params.fields);
|
obj._initProperties(d, false, params.fields);
|
||||||
|
|
||||||
if (params && params.include && params.collect) {
|
if (params && params.include) {
|
||||||
data[i] = obj.__cachedRelations[params.collect];
|
// Try to normalize the include
|
||||||
} else {
|
var includes = params.include;
|
||||||
data[i] = obj;
|
if(typeof includes === 'string') {
|
||||||
|
includes = [includes];
|
||||||
|
} else if(typeof includes === 'object') {
|
||||||
|
includes = Object.keys(includes);
|
||||||
|
}
|
||||||
|
includes.forEach(function (inc) {
|
||||||
|
// Promote the included model as a direct property
|
||||||
|
obj.__data[inc] = obj.__cachedRelations[inc];
|
||||||
|
});
|
||||||
|
delete obj.__data.__cachedRelations;
|
||||||
}
|
}
|
||||||
|
data[i] = obj;
|
||||||
});
|
});
|
||||||
if (data && data.countBeforeLimit) {
|
if (data && data.countBeforeLimit) {
|
||||||
data.countBeforeLimit = data.countBeforeLimit;
|
data.countBeforeLimit = data.countBeforeLimit;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
var utils = require('./utils');
|
||||||
|
var isPlainObject = utils.isPlainObject;
|
||||||
|
var defineCachedRelations = utils.defineCachedRelations;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Include mixin for ./model.js
|
* Include mixin for ./model.js
|
||||||
*/
|
*/
|
||||||
|
@ -29,8 +33,8 @@ Inclusion.include = function (objects, include, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(include.constructor.name == 'Array' && include.length == 0) ||
|
!include || (Array.isArray(include) && include.length === 0) ||
|
||||||
(include.constructor.name == 'Object' && Object.keys(include).length == 0)
|
(isPlainObject(include) && Object.keys(include).length === 0)
|
||||||
) {
|
) {
|
||||||
cb(null, objects);
|
cb(null, objects);
|
||||||
return;
|
return;
|
||||||
|
@ -48,7 +52,7 @@ Inclusion.include = function (objects, include, cb) {
|
||||||
nbCallbacks++;
|
nbCallbacks++;
|
||||||
callback(function () {
|
callback(function () {
|
||||||
nbCallbacks--;
|
nbCallbacks--;
|
||||||
if (nbCallbacks == 0) {
|
if (nbCallbacks === 0) {
|
||||||
cb(null, objects);
|
cb(null, objects);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -61,7 +65,7 @@ Inclusion.include = function (objects, include, cb) {
|
||||||
if (typeof ij === 'string') {
|
if (typeof ij === 'string') {
|
||||||
ij = [ij];
|
ij = [ij];
|
||||||
}
|
}
|
||||||
if (ij.constructor.name === 'Object') {
|
if (isPlainObject(ij)) {
|
||||||
var newIj = [];
|
var newIj = [];
|
||||||
for (var key in ij) {
|
for (var key in ij) {
|
||||||
var obj = {};
|
var obj = {};
|
||||||
|
@ -76,12 +80,13 @@ Inclusion.include = function (objects, include, cb) {
|
||||||
function processIncludeItem(objs, include, keyVals, objsByKeys) {
|
function processIncludeItem(objs, include, keyVals, objsByKeys) {
|
||||||
var relations = self.relations;
|
var relations = self.relations;
|
||||||
|
|
||||||
if (include.constructor.name === 'Object') {
|
var relationName, subInclude;
|
||||||
var relationName = Object.keys(include)[0];
|
if (isPlainObject(include)) {
|
||||||
var subInclude = include[relationName];
|
relationName = Object.keys(include)[0];
|
||||||
|
subInclude = include[relationName];
|
||||||
} else {
|
} else {
|
||||||
var relationName = include;
|
relationName = include;
|
||||||
var subInclude = [];
|
subInclude = [];
|
||||||
}
|
}
|
||||||
var relation = relations[relationName];
|
var relation = relations[relationName];
|
||||||
|
|
||||||
|
@ -89,7 +94,7 @@ Inclusion.include = function (objects, include, cb) {
|
||||||
return function () {
|
return function () {
|
||||||
cb(new Error('Relation "' + relationName + '" is not defined for '
|
cb(new Error('Relation "' + relationName + '" is not defined for '
|
||||||
+ self.modelName + ' model'));
|
+ self.modelName + ' model'));
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var req = {'where': {}};
|
var req = {'where': {}};
|
||||||
|
@ -117,18 +122,17 @@ Inclusion.include = function (objects, include, cb) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
req['where'][relation.keyTo] = {inq: inValues};
|
req.where[relation.keyTo] = {inq: inValues};
|
||||||
req['include'] = subInclude;
|
req.include = subInclude;
|
||||||
|
|
||||||
return function (cb) {
|
return function (cb) {
|
||||||
relation.modelTo.find(req, function (err, objsIncluded) {
|
relation.modelTo.find(req, function (err, objsIncluded) {
|
||||||
|
var objectsFrom, j;
|
||||||
for (var i = 0; i < objsIncluded.length; i++) {
|
for (var i = 0; i < objsIncluded.length; i++) {
|
||||||
delete keysToBeProcessed[objsIncluded[i][relation.keyTo]];
|
delete keysToBeProcessed[objsIncluded[i][relation.keyTo]];
|
||||||
var objectsFrom = objsByKeys[relation.keyFrom][objsIncluded[i][relation.keyTo]];
|
objectsFrom = objsByKeys[relation.keyFrom][objsIncluded[i][relation.keyTo]];
|
||||||
for (var j = 0; j < objectsFrom.length; j++) {
|
for (j = 0; j < objectsFrom.length; j++) {
|
||||||
if (!objectsFrom[j].__cachedRelations) {
|
defineCachedRelations(objectsFrom[j]);
|
||||||
objectsFrom[j].__cachedRelations = {};
|
|
||||||
}
|
|
||||||
if (relation.multiple) {
|
if (relation.multiple) {
|
||||||
if (!objectsFrom[j].__cachedRelations[relationName]) {
|
if (!objectsFrom[j].__cachedRelations[relationName]) {
|
||||||
objectsFrom[j].__cachedRelations[relationName] = [];
|
objectsFrom[j].__cachedRelations[relationName] = [];
|
||||||
|
@ -142,11 +146,9 @@ Inclusion.include = function (objects, include, cb) {
|
||||||
|
|
||||||
// No relation have been found for these keys
|
// No relation have been found for these keys
|
||||||
for (var key in keysToBeProcessed) {
|
for (var key in keysToBeProcessed) {
|
||||||
var objectsFrom = objsByKeys[relation.keyFrom][key];
|
objectsFrom = objsByKeys[relation.keyFrom][key];
|
||||||
for (var j = 0; j < objectsFrom.length; j++) {
|
for (j = 0; j < objectsFrom.length; j++) {
|
||||||
if (!objectsFrom[j].__cachedRelations) {
|
defineCachedRelations(objectsFrom[j]);
|
||||||
objectsFrom[j].__cachedRelations = {};
|
|
||||||
}
|
|
||||||
objectsFrom[j].__cachedRelations[relationName] =
|
objectsFrom[j].__cachedRelations[relationName] =
|
||||||
relation.multiple ? [] : null;
|
relation.multiple ? [] : null;
|
||||||
}
|
}
|
||||||
|
@ -158,5 +160,5 @@ Inclusion.include = function (objects, include, cb) {
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
|
@ -76,8 +76,8 @@ ModelBaseClass.prototype._initProperties = function (data, applySetters) {
|
||||||
value: {}
|
value: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (data['__cachedRelations']) {
|
if (data.__cachedRelations) {
|
||||||
this.__cachedRelations = data['__cachedRelations'];
|
this.__cachedRelations = data.__cachedRelations;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the strict option is set to false for the model
|
// Check if the strict option is set to false for the model
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
var utils = require('./utils');
|
||||||
|
var defineCachedRelations = utils.defineCachedRelations;
|
||||||
/**
|
/**
|
||||||
* Module exports
|
* Module exports
|
||||||
*/
|
*/
|
||||||
|
@ -54,14 +56,12 @@ function defineScope(cls, targetClass, name, params, methods) {
|
||||||
throw new Error('Method can be only called with one or two arguments');
|
throw new Error('Method can be only called with one or two arguments');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.__cachedRelations || (typeof this.__cachedRelations[name] == 'undefined') || actualRefresh) {
|
if (!this.__cachedRelations || (this.__cachedRelations[name] === undefined) || actualRefresh) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var params = mergeParams(actualCond, caller._scope);
|
var params = mergeParams(actualCond, caller._scope);
|
||||||
return targetClass.find(params, function (err, data) {
|
return targetClass.find(params, function (err, data) {
|
||||||
if (!err && saveOnCache) {
|
if (!err && saveOnCache) {
|
||||||
if (!self.__cachedRelations) {
|
defineCachedRelations(self);
|
||||||
self.__cachedRelations = {};
|
|
||||||
}
|
|
||||||
self.__cachedRelations[name] = data;
|
self.__cachedRelations[name] = data;
|
||||||
}
|
}
|
||||||
cb(err, data);
|
cb(err, data);
|
||||||
|
|
27
lib/utils.js
27
lib/utils.js
|
@ -4,6 +4,8 @@ exports.selectFields = selectFields;
|
||||||
exports.removeUndefined = removeUndefined;
|
exports.removeUndefined = removeUndefined;
|
||||||
exports.parseSettings = parseSettings;
|
exports.parseSettings = parseSettings;
|
||||||
exports.mergeSettings = mergeSettings;
|
exports.mergeSettings = mergeSettings;
|
||||||
|
exports.isPlainObject = isPlainObject;
|
||||||
|
exports.defineCachedRelations = defineCachedRelations;
|
||||||
|
|
||||||
var traverse = require('traverse');
|
var traverse = require('traverse');
|
||||||
|
|
||||||
|
@ -176,4 +178,29 @@ function mergeSettings(target, src) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return dst;
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define an non-enumerable __cachedRelations property
|
||||||
|
* @param {Object} obj The obj to receive the __cachedRelations
|
||||||
|
*/
|
||||||
|
function defineCachedRelations(obj) {
|
||||||
|
if (!obj.__cachedRelations) {
|
||||||
|
Object.defineProperty(obj, '__cachedRelations', {
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true,
|
||||||
|
value: {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the argument is plain object
|
||||||
|
* @param {*) obj The obj value
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function isPlainObject(obj) {
|
||||||
|
return (typeof obj === 'object') && (obj !== null)
|
||||||
|
&& (obj.constructor === Object);
|
||||||
}
|
}
|
|
@ -13,6 +13,12 @@ describe('include', function () {
|
||||||
passports.length.should.be.ok;
|
passports.length.should.be.ok;
|
||||||
passports.forEach(function (p) {
|
passports.forEach(function (p) {
|
||||||
p.__cachedRelations.should.have.property('owner');
|
p.__cachedRelations.should.have.property('owner');
|
||||||
|
|
||||||
|
// The relation should be promoted as the 'owner' property
|
||||||
|
p.should.have.property('owner');
|
||||||
|
// The __cachedRelations should be removed from json output
|
||||||
|
p.toJSON().should.not.have.property('__cachedRelations');
|
||||||
|
|
||||||
var owner = p.__cachedRelations.owner;
|
var owner = p.__cachedRelations.owner;
|
||||||
if (!p.ownerId) {
|
if (!p.ownerId) {
|
||||||
should.not.exist(owner);
|
should.not.exist(owner);
|
||||||
|
@ -31,6 +37,11 @@ describe('include', function () {
|
||||||
should.exist(users);
|
should.exist(users);
|
||||||
users.length.should.be.ok;
|
users.length.should.be.ok;
|
||||||
users.forEach(function (u) {
|
users.forEach(function (u) {
|
||||||
|
// The relation should be promoted as the 'owner' property
|
||||||
|
u.should.have.property('posts');
|
||||||
|
// The __cachedRelations should be removed from json output
|
||||||
|
u.toJSON().should.not.have.property('__cachedRelations');
|
||||||
|
|
||||||
u.__cachedRelations.should.have.property('posts');
|
u.__cachedRelations.should.have.property('posts');
|
||||||
u.__cachedRelations.posts.forEach(function (p) {
|
u.__cachedRelations.posts.forEach(function (p) {
|
||||||
p.userId.should.equal(u.id);
|
p.userId.should.equal(u.id);
|
||||||
|
@ -47,6 +58,12 @@ describe('include', function () {
|
||||||
passports.length.should.be.ok;
|
passports.length.should.be.ok;
|
||||||
passports.forEach(function (p) {
|
passports.forEach(function (p) {
|
||||||
p.__cachedRelations.should.have.property('owner');
|
p.__cachedRelations.should.have.property('owner');
|
||||||
|
|
||||||
|
// The relation should be promoted as the 'owner' property
|
||||||
|
p.should.have.property('owner');
|
||||||
|
// The __cachedRelations should be removed from json output
|
||||||
|
p.toJSON().should.not.have.property('__cachedRelations');
|
||||||
|
|
||||||
var user = p.__cachedRelations.owner;
|
var user = p.__cachedRelations.owner;
|
||||||
if (!p.ownerId) {
|
if (!p.ownerId) {
|
||||||
should.not.exist(user);
|
should.not.exist(user);
|
||||||
|
@ -97,6 +114,12 @@ describe('include', function () {
|
||||||
should.exist(users);
|
should.exist(users);
|
||||||
users.length.should.be.ok;
|
users.length.should.be.ok;
|
||||||
users.forEach(function (user) {
|
users.forEach(function (user) {
|
||||||
|
// The relation should be promoted as the 'owner' property
|
||||||
|
user.should.have.property('posts');
|
||||||
|
user.should.have.property('passports');
|
||||||
|
// The __cachedRelations should be removed from json output
|
||||||
|
user.toJSON().should.not.have.property('__cachedRelations');
|
||||||
|
|
||||||
user.__cachedRelations.should.have.property('posts');
|
user.__cachedRelations.should.have.property('posts');
|
||||||
user.__cachedRelations.should.have.property('passports');
|
user.__cachedRelations.should.have.property('passports');
|
||||||
user.__cachedRelations.posts.forEach(function (p) {
|
user.__cachedRelations.posts.forEach(function (p) {
|
||||||
|
|
Loading…
Reference in New Issue