From 610866bd7c0f2e8183daa12eecb246acce1ba51b Mon Sep 17 00:00:00 2001 From: Fabien Franzen Date: Sat, 6 Sep 2014 11:13:47 +0200 Subject: [PATCH] Extract mergeQuery and setScopeValuesFromWhere Related to #274 - in preparation of default scope --- lib/dao.js | 36 +++++++++++++-- lib/model.js | 4 ++ lib/relation-definition.js | 3 +- lib/scope.js | 89 +------------------------------------- lib/utils.js | 88 +++++++++++++++++++++++++++++++++++++ test/loopback-dl.test.js | 2 +- 6 files changed, 129 insertions(+), 93 deletions(-) diff --git a/lib/dao.js b/lib/dao.js index c4236a91..1d308f73 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -13,11 +13,12 @@ var Relation = require('./relations.js'); var Inclusion = require('./include.js'); var List = require('./list.js'); var geo = require('./geo'); -var mergeQuery = require('./scope.js').mergeQuery; var Memory = require('./connectors/memory').Memory; var utils = require('./utils'); var fieldsToArray = utils.fieldsToArray; var removeUndefined = utils.removeUndefined; +var setScopeValuesFromWhere = utils.setScopeValuesFromWhere; +var mergeQuery = utils.mergeQuery; var util = require('util'); var assert = require('assert'); @@ -69,6 +70,18 @@ DataAccessObject._forDB = function (data) { return res; }; +DataAccessObject.applyProperties = function(data) { + var scope = this.definition.settings.scope || {}; + if (typeof scope.where === 'object' + && this.definition.settings.applyProperties !== false) { + setScopeValuesFromWhere(data, scope.where, this); + } +}; + +DataAccessObject.applyScope = function(cond) { + +}; + /** * Create an instance of Model with given data and save to the attached data source. Callback is optional. * Example: @@ -136,6 +149,7 @@ DataAccessObject.create = function (data, callback) { } } + var enforced = {}; var obj; var idValue = getIdValue(this, data); @@ -145,6 +159,10 @@ DataAccessObject.create = function (data, callback) { } else { obj = new Model(data); } + + this.applyProperties(enforced); + obj.setAttributes(enforced); + data = obj.toObject(true); // validation required @@ -220,6 +238,7 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data inst = new Model(data); } update = inst.toObject(false); + this.applyProperties(update); update = removeUndefined(update); this.getDataSource().connector.updateOrCreate(Model.modelName, update, function (err, data) { var obj; @@ -926,13 +945,17 @@ DataAccessObject.prototype.save = function (options, callback) { if (!('throws' in options)) { options.throws = false; } - + var inst = this; var data = inst.toObject(true); var modelName = Model.modelName; - + + Model.applyProperties(data, this); + if (this.isNewRecord()) { return Model.create(this, callback); + } else { + inst.setAttributes(data); } // validate first @@ -1025,6 +1048,9 @@ DataAccessObject.updateAll = function (where, data, cb) { cb && cb(err); }); } + + this.applyProperties(data); + var connector = this.getDataSource().connector; connector.update(this.modelName, where, data, cb); }; @@ -1075,7 +1101,7 @@ DataAccessObject.prototype.remove = * @param {Mixed} value Value of property */ DataAccessObject.prototype.setAttribute = function setAttribute(name, value) { - this[name] = value; + this[name] = value; // TODO [fabien] - currently not protected by applyProperties }; /** @@ -1101,6 +1127,8 @@ DataAccessObject.prototype.updateAttribute = function updateAttribute(name, valu DataAccessObject.prototype.setAttributes = function setAttributes(data) { if (typeof data !== 'object') return; + this.constructor.applyProperties(data); + var Model = this.constructor; var inst = this; diff --git a/lib/model.js b/lib/model.js index e12b9b4b..7bb19eb7 100644 --- a/lib/model.js +++ b/lib/model.js @@ -60,6 +60,10 @@ ModelBaseClass.prototype._initProperties = function (data, options) { } var properties = _extend({}, ctor.definition.properties); data = data || {}; + + if (typeof ctor.applyProperties === 'function') { + ctor.applyProperties(data); + } options = options || {}; var applySetters = options.applySetters; diff --git a/lib/relation-definition.js b/lib/relation-definition.js index 2590a3a5..1fa299e4 100644 --- a/lib/relation-definition.js +++ b/lib/relation-definition.js @@ -3,9 +3,10 @@ */ var assert = require('assert'); var util = require('util'); +var utils = require('./utils'); var i8n = require('inflection'); var defineScope = require('./scope.js').defineScope; -var mergeQuery = require('./scope.js').mergeQuery; +var mergeQuery = utils.mergeQuery; var ModelBaseClass = require('./model.js'); var applyFilter = require('./connectors/memory').applyFilter; var ValidationError = require('./validations.js').ValidationError; diff --git a/lib/scope.js b/lib/scope.js index 6b4dafb1..dfdb364d 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -1,13 +1,14 @@ var i8n = require('inflection'); var utils = require('./utils'); var defineCachedRelations = utils.defineCachedRelations; +var setScopeValuesFromWhere = utils.setScopeValuesFromWhere; +var mergeQuery = utils.mergeQuery; var DefaultModelBaseClass = require('./model.js'); /** * Module exports */ exports.defineScope = defineScope; -exports.mergeQuery = mergeQuery; function ScopeDefinition(definition) { this.isStatic = definition.isStatic; @@ -229,35 +230,6 @@ function defineScope(cls, targetClass, name, params, methods, options) { cls['__count__' + name] = fn_count; - /* - * Extracting fixed property values for the scope from the where clause into - * the data object - * - * @param {Object} The data object - * @param {Object} The where clause - */ - function setScopeValuesFromWhere(data, where, targetModel) { - for (var i in where) { - if (i === 'and') { - // Find fixed property values from each subclauses - for (var w = 0, n = where[i].length; w < n; w++) { - setScopeValuesFromWhere(data, where[i][w], targetModel); - } - continue; - } - var prop = targetModel.definition.properties[i]; - if (prop) { - var val = where[i]; - if (typeof val !== 'object' || val instanceof prop.type - || prop.type.name === 'ObjectID') // MongoDB key - { - // Only pick the {propertyName: propertyValue} - data[i] = where[i]; - } - } - } - } - // and it should have create/build methods with binded thisModelNameId param function build(data) { data = data || {}; @@ -300,60 +272,3 @@ function defineScope(cls, targetClass, name, params, methods, options) { return definition; } - -/*! - * Merge query parameters - * @param {Object} base The base object to contain the merged results - * @param {Object} update The object containing updates to be merged - * @returns {*|Object} The base object - * @private - */ -function mergeQuery(base, update) { - if (!update) { - return; - } - base = base || {}; - if (update.where && Object.keys(update.where).length > 0) { - if (base.where && Object.keys(base.where).length > 0) { - base.where = {and: [base.where, update.where]}; - } else { - base.where = update.where; - } - } - - // Merge inclusion - if (update.include) { - if (!base.include) { - base.include = update.include; - } else { - var saved = base.include; - base.include = {}; - base.include[update.include] = saved; - } - } - if (update.collect) { - base.collect = update.collect; - } - - // set order - if (!base.order && update.order) { - base.order = update.order; - } - - // overwrite pagination - if (update.limit !== undefined) { - base.limit = update.limit; - } - if (update.skip !== undefined) { - base.skip = update.skip; - } - if (update.offset !== undefined) { - base.offset = update.offset; - } - - // Overwrite fields - if (update.fields !== undefined) { - base.fields = update.fields; - } - return base; -} diff --git a/lib/utils.js b/lib/utils.js index c774ecae..bbde56fc 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -7,6 +7,8 @@ exports.mergeSettings = mergeSettings; exports.isPlainObject = isPlainObject; exports.defineCachedRelations = defineCachedRelations; exports.sortObjectsByIds = sortObjectsByIds; +exports.setScopeValuesFromWhere = setScopeValuesFromWhere; +exports.mergeQuery = mergeQuery; var traverse = require('traverse'); @@ -21,6 +23,92 @@ function safeRequire(module) { } } +/* + * Extracting fixed property values for the scope from the where clause into + * the data object + * + * @param {Object} The data object + * @param {Object} The where clause + */ +function setScopeValuesFromWhere(data, where, targetModel) { + for (var i in where) { + if (i === 'and') { + // Find fixed property values from each subclauses + for (var w = 0, n = where[i].length; w < n; w++) { + setScopeValuesFromWhere(data, where[i][w], targetModel); + } + continue; + } + var prop = targetModel.definition.properties[i]; + if (prop) { + var val = where[i]; + if (typeof val !== 'object' || val instanceof prop.type + || prop.type.name === 'ObjectID') // MongoDB key + { + // Only pick the {propertyName: propertyValue} + data[i] = where[i]; + } + } + } +} + +/*! + * Merge query parameters + * @param {Object} base The base object to contain the merged results + * @param {Object} update The object containing updates to be merged + * @returns {*|Object} The base object + * @private + */ +function mergeQuery(base, update) { + if (!update) { + return; + } + base = base || {}; + if (update.where && Object.keys(update.where).length > 0) { + if (base.where && Object.keys(base.where).length > 0) { + base.where = {and: [base.where, update.where]}; + } else { + base.where = update.where; + } + } + + // Merge inclusion + if (update.include) { + if (!base.include) { + base.include = update.include; + } else { + var saved = base.include; + base.include = {}; + base.include[update.include] = saved; + } + } + if (update.collect) { + base.collect = update.collect; + } + + // set order + if (!base.order && update.order) { + base.order = update.order; + } + + // overwrite pagination + if (update.limit !== undefined) { + base.limit = update.limit; + } + if (update.skip !== undefined) { + base.skip = update.skip; + } + if (update.offset !== undefined) { + base.offset = update.offset; + } + + // Overwrite fields + if (update.fields !== undefined) { + base.fields = update.fields; + } + return base; +} + function fieldsToArray(fields, properties) { if (!fields) return; diff --git a/test/loopback-dl.test.js b/test/loopback-dl.test.js index 10c9c10e..41d600f6 100644 --- a/test/loopback-dl.test.js +++ b/test/loopback-dl.test.js @@ -639,7 +639,7 @@ describe('Load models with base', function () { assert(Customer.prototype.instanceMethod === User.prototype.instanceMethod); assert.equal(Customer.base, User); assert.equal(Customer.base, Customer.super_); - + try { var Customer1 = ds.define('Customer1', {vip: Boolean}, {base: 'User1'}); } catch (e) {