Merge branch 'merge-inclusion' of https://github.com/walkonsocial/loopback-datasource-juggler into walkonsocial-merge-inclusion
This commit is contained in:
commit
3aa90751be
|
@ -60,7 +60,7 @@ ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefres
|
|||
if (!self.__cachedRelations || self.__cachedRelations[name] === undefined
|
||||
|| actualRefresh) {
|
||||
// It either doesn't hit the cache or refresh is required
|
||||
var params = mergeQuery(actualCond, scopeParams);
|
||||
var params = mergeQuery(actualCond, scopeParams, {nestedInclude: true});
|
||||
var targetModel = this.targetModel(receiver);
|
||||
targetModel.find(params, function (err, data) {
|
||||
if (!err && saveOnCache) {
|
||||
|
|
98
lib/utils.js
98
lib/utils.js
|
@ -9,6 +9,7 @@ exports.defineCachedRelations = defineCachedRelations;
|
|||
exports.sortObjectsByIds = sortObjectsByIds;
|
||||
exports.setScopeValuesFromWhere = setScopeValuesFromWhere;
|
||||
exports.mergeQuery = mergeQuery;
|
||||
exports.mergeIncludes = mergeIncludes;
|
||||
exports.createPromiseCallback = createPromiseCallback
|
||||
|
||||
var traverse = require('traverse');
|
||||
|
@ -53,6 +54,87 @@ function setScopeValuesFromWhere(data, where, targetModel) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge include options of default scope with runtime include option.
|
||||
* exhibits the _.extend behaviour. Property value of source overrides
|
||||
* property value of destination if property name collision occurs
|
||||
* @param {String|Array|Object} destination The default value of `include` option
|
||||
* @param {String|Array|Object} source The runtime value of `include` option
|
||||
* @returns {Object}
|
||||
*/
|
||||
function mergeIncludes(destination, source) {
|
||||
var destArray = convertToArray(destination);
|
||||
var sourceArray = convertToArray(source);
|
||||
if (destArray.length === 0) {
|
||||
return sourceArray;
|
||||
}
|
||||
if (sourceArray.length === 0) {
|
||||
return destArray;
|
||||
}
|
||||
var relationNames = [];
|
||||
var resultArray = [];
|
||||
for (var j in sourceArray) {
|
||||
var sourceEntry = sourceArray[j];
|
||||
var sourceEntryRelationName = (typeof (sourceEntry.rel || sourceEntry.relation) === 'string') ?
|
||||
sourceEntry.relation : Object.keys(sourceEntry)[0];
|
||||
relationNames.push(sourceEntryRelationName);
|
||||
resultArray.push(sourceEntry);
|
||||
}
|
||||
for (var i in destArray) {
|
||||
var destEntry = destArray[i];
|
||||
var destEntryRelationName = (typeof (destEntry.rel || destEntry.relation) === 'string') ?
|
||||
destEntry.relation : Object.keys(destEntry)[0];
|
||||
if (relationNames.indexOf(destEntryRelationName) === -1) {
|
||||
resultArray.push(destEntry);
|
||||
}
|
||||
}
|
||||
return resultArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts input parameter into array of objects which wraps the value.
|
||||
* "someValue" is converted to [{"someValue":true}]
|
||||
* ["someValue"] is converted to [{"someValue":true}]
|
||||
* {"someValue":true} is converted to [{"someValue":true}]
|
||||
* @param {String|Array|Object} param - Input parameter to be converted
|
||||
* @returns {Array}
|
||||
*/
|
||||
function convertToArray(include) {
|
||||
if (typeof include === 'string') {
|
||||
var obj = {};
|
||||
obj[include] = true;
|
||||
return [obj];
|
||||
} else if (isPlainObject(include)) {
|
||||
//if include is of the form - {relation:'',scope:''}
|
||||
if (include.rel || include.relation) {
|
||||
return [include];
|
||||
}
|
||||
// Build an array of key/value pairs
|
||||
var newInclude = [];
|
||||
for (var key in include) {
|
||||
var obj = {};
|
||||
obj[key] = include[key];
|
||||
newInclude.push(obj);
|
||||
}
|
||||
return newInclude;
|
||||
} else if (Array.isArray(include)) {
|
||||
var normalized = [];
|
||||
for (var i in include) {
|
||||
var includeEntry = include[i];
|
||||
if (typeof includeEntry === 'string') {
|
||||
var obj = {};
|
||||
obj[includeEntry] = true;
|
||||
normalized.push(obj)
|
||||
}
|
||||
else{
|
||||
normalized.push(includeEntry);
|
||||
}
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/*!
|
||||
* Merge query parameters
|
||||
* @param {Object} base The base object to contain the merged results
|
||||
|
@ -81,9 +163,19 @@ function mergeQuery(base, update, spec) {
|
|||
if (!base.include) {
|
||||
base.include = update.include;
|
||||
} else {
|
||||
var saved = base.include;
|
||||
base.include = {};
|
||||
base.include[update.include] = saved;
|
||||
if (spec.nestedInclude === true){
|
||||
//specify nestedInclude=true to force nesting of inclusions on scoped
|
||||
//queries. e.g. In physician.patients.getAsync({include: 'address'}),
|
||||
//inclusion should be on patient model, not on physician model.
|
||||
var saved = base.include;
|
||||
base.include = {};
|
||||
base.include[update.include] = saved;
|
||||
}
|
||||
else{
|
||||
//default behaviour of inclusion merge - merge inclusions at the same
|
||||
//level. - https://github.com/strongloop/loopback-datasource-juggler/pull/569#issuecomment-95310874
|
||||
base.include = mergeIncludes(base.include, update.include);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ var utils = require('../lib/utils');
|
|||
var fieldsToArray = utils.fieldsToArray;
|
||||
var removeUndefined = utils.removeUndefined;
|
||||
var mergeSettings = utils.mergeSettings;
|
||||
var mergeIncludes = utils.mergeIncludes;
|
||||
var sortObjectsByIds = utils.sortObjectsByIds;
|
||||
|
||||
describe('util.fieldsToArray', function () {
|
||||
|
@ -218,3 +219,146 @@ describe('sortObjectsByIds', function () {
|
|||
});
|
||||
|
||||
});
|
||||
|
||||
describe('util.mergeIncludes', function () {
|
||||
|
||||
function checkInputOutput(baseInclude, updateInclude, expectedInclude) {
|
||||
var mergedInclude = mergeIncludes(baseInclude, updateInclude);
|
||||
should.deepEqual(mergedInclude, expectedInclude,
|
||||
'Merged include should match the expectation');
|
||||
}
|
||||
|
||||
it('Merge string values to object', function () {
|
||||
var baseInclude = 'relation1';
|
||||
var updateInclude = 'relation2';
|
||||
var expectedInclude = [
|
||||
{relation2: true},
|
||||
{relation1: true}
|
||||
];
|
||||
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||
});
|
||||
|
||||
it('Merge string & array values to object', function () {
|
||||
var baseInclude = 'relation1';
|
||||
var updateInclude = ['relation2'];
|
||||
var expectedInclude = [
|
||||
{relation2: true},
|
||||
{relation1: true}
|
||||
];
|
||||
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||
});
|
||||
|
||||
it('Merge string & object values to object', function () {
|
||||
var baseInclude = ['relation1'];
|
||||
var updateInclude = {relation2: 'relation2Include'};
|
||||
var expectedInclude = [
|
||||
{relation2: 'relation2Include'},
|
||||
{relation1: true}
|
||||
];
|
||||
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||
});
|
||||
|
||||
it('Merge array & array values to object', function () {
|
||||
var baseInclude = ['relation1'];
|
||||
var updateInclude = ['relation2'];
|
||||
var expectedInclude = [
|
||||
{relation2: true},
|
||||
{relation1: true}
|
||||
];
|
||||
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||
});
|
||||
|
||||
it('Merge array & object values to object', function () {
|
||||
var baseInclude = ['relation1'];
|
||||
var updateInclude = {relation2: 'relation2Include'};
|
||||
var expectedInclude = [
|
||||
{relation2: 'relation2Include'},
|
||||
{relation1: true}
|
||||
];
|
||||
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||
});
|
||||
|
||||
it('Merge object & object values to object', function () {
|
||||
var baseInclude = {relation1: 'relation1Include'};
|
||||
var updateInclude = {relation2: 'relation2Include'};
|
||||
var expectedInclude = [
|
||||
{relation2: 'relation2Include'},
|
||||
{relation1: 'relation1Include'}
|
||||
];
|
||||
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||
});
|
||||
|
||||
it('Override property collision with update value', function () {
|
||||
var baseInclude = {relation1: 'baseValue'};
|
||||
var updateInclude = {relation1: 'updateValue'};
|
||||
var expectedInclude = [
|
||||
{relation1: 'updateValue'}
|
||||
];
|
||||
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||
});
|
||||
|
||||
it('Merge string includes & include with relation syntax properly',
|
||||
function () {
|
||||
var baseInclude = 'relation1';
|
||||
var updateInclude = {relation: 'relation1'};
|
||||
var expectedInclude = [
|
||||
{relation: 'relation1'}
|
||||
];
|
||||
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||
});
|
||||
|
||||
it('Merge string includes & include with scope properly', function () {
|
||||
var baseInclude = 'relation1';
|
||||
var updateInclude = {
|
||||
relation: 'relation1',
|
||||
scope: {include: 'relation2'}
|
||||
};
|
||||
var expectedInclude = [
|
||||
{relation: 'relation1', scope: {include: 'relation2'}}
|
||||
];
|
||||
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||
});
|
||||
|
||||
it('Merge includes with and without relation syntax properly',
|
||||
function () {
|
||||
//w & w/o relation syntax - no collision
|
||||
var baseInclude = ['relation2'];
|
||||
var updateInclude = {
|
||||
relation: 'relation1',
|
||||
scope: {include: 'relation2'}
|
||||
};
|
||||
var expectedInclude = [{
|
||||
relation: 'relation1',
|
||||
scope: {include: 'relation2'}
|
||||
}, {relation2: true}];
|
||||
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||
|
||||
//w & w/o relation syntax - collision
|
||||
baseInclude = ['relation1'];
|
||||
updateInclude = {relation: 'relation1', scope: {include: 'relation2'}};
|
||||
expectedInclude =
|
||||
[{relation: 'relation1', scope: {include: 'relation2'}}];
|
||||
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||
|
||||
//w & w/o relation syntax - collision
|
||||
baseInclude = {relation: 'relation1', scope: {include: 'relation2'}};
|
||||
updateInclude = ['relation1'];
|
||||
expectedInclude = [{relation1: true}];
|
||||
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||
});
|
||||
|
||||
it('Merge includes with mixture of strings, arrays & objects properly', function () {
|
||||
var baseInclude = ['relation1', {relation2: true},
|
||||
{relation: 'relation3', scope: {where: {id: 'some id'}}},
|
||||
{relation: 'relation5', scope: {where: {id: 'some id'}}}
|
||||
];
|
||||
var updateInclude = ['relation4', {relation3: true},
|
||||
{relation: 'relation2', scope: {where: {id: 'some id'}}}];
|
||||
var expectedInclude = [{relation4: true}, {relation3: true},
|
||||
{relation: 'relation2', scope: {where: {id: 'some id'}}},
|
||||
{relation1: true},
|
||||
{relation: 'relation5', scope: {where: {id: 'some id'}}}];
|
||||
checkInputOutput(baseInclude, updateInclude, expectedInclude);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue