Merge branch 'release/1.3.13' into production
This commit is contained in:
commit
853c8a1c07
|
@ -179,7 +179,14 @@ Memory.prototype.updateOrCreate = function (model, data, callback) {
|
|||
};
|
||||
|
||||
Memory.prototype.save = function save(model, data, callback) {
|
||||
this.cache[model][this.getIdValue(model, data)] = serialize(data);
|
||||
var id = this.getIdValue(model, data);
|
||||
var cachedModels = this.cache[model];
|
||||
var modelData = cachedModels && this.cache[model][id];
|
||||
modelData = modelData && deserialize(modelData);
|
||||
if (modelData) {
|
||||
data = merge(modelData, data);
|
||||
}
|
||||
this.cache[model][id] = serialize(data);
|
||||
this.saveToFile(data, callback);
|
||||
};
|
||||
|
||||
|
@ -190,7 +197,6 @@ Memory.prototype.exists = function exists(model, id, callback) {
|
|||
};
|
||||
|
||||
Memory.prototype.find = function find(model, id, callback) {
|
||||
var self = this;
|
||||
process.nextTick(function () {
|
||||
callback(null, id in this.cache[model] && this.fromDb(model, this.cache[model][id]));
|
||||
}.bind(this));
|
||||
|
@ -393,10 +399,9 @@ Memory.prototype.updateAttributes = function updateAttributes(model, id, data, c
|
|||
|
||||
var cachedModels = this.cache[model];
|
||||
var modelData = cachedModels && this.cache[model][id];
|
||||
modelData = modelData && deserialize(modelData);
|
||||
|
||||
if (modelData) {
|
||||
this.save(model, merge(modelData, data), cb);
|
||||
this.save(model, data, cb);
|
||||
} else {
|
||||
cb(new Error('Could not update attributes. Object with id ' + id + ' does not exist!'));
|
||||
}
|
||||
|
@ -416,9 +421,17 @@ Memory.prototype.buildNearFilter = function (filter) {
|
|||
}
|
||||
|
||||
function merge(base, update) {
|
||||
if (!base) return update;
|
||||
Object.keys(update).forEach(function (key) {
|
||||
base[key] = update[key];
|
||||
});
|
||||
if (!base) {
|
||||
return update;
|
||||
}
|
||||
// We cannot use Object.keys(update) if the update is an instance of the model
|
||||
// class as the properties are defined at the ModelClass.prototype level
|
||||
for(var key in update) {
|
||||
var val = update[key];
|
||||
if(typeof val === 'function') {
|
||||
continue; // Skip methods
|
||||
}
|
||||
base[key] = val;
|
||||
}
|
||||
return base;
|
||||
}
|
66
lib/dao.js
66
lib/dao.js
|
@ -105,9 +105,10 @@ DataAccessObject.create = function (data, callback) {
|
|||
var errors = Array(data.length);
|
||||
var gotError = false;
|
||||
var wait = data.length;
|
||||
if (wait === 0) callback(null, []);
|
||||
if (wait === 0) {
|
||||
callback(null, []);
|
||||
}
|
||||
|
||||
var instances = [];
|
||||
for (var i = 0; i < data.length; i += 1) {
|
||||
(function (d, i) {
|
||||
instances.push(Model.create(d, function (err, inst) {
|
||||
|
@ -209,29 +210,42 @@ function stillConnecting(dataSource, obj, args) {
|
|||
|
||||
/**
|
||||
* Update or insert a model instance.
|
||||
* `updateOrCreate` is an alias
|
||||
* @param {Object} data The model instance data
|
||||
* @param {Function} callback The callback function (optional).
|
||||
*/
|
||||
DataAccessObject.upsert = DataAccessObject.updateOrCreate = function upsert(data, callback) {
|
||||
if (stillConnecting(this.getDataSource(), this, arguments)) return;
|
||||
if (stillConnecting(this.getDataSource(), this, arguments)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var Model = this;
|
||||
if (!getIdValue(this, data)) return this.create(data, callback);
|
||||
if (!getIdValue(this, data)) {
|
||||
return this.create(data, callback);
|
||||
}
|
||||
if (this.getDataSource().connector.updateOrCreate) {
|
||||
var inst = new Model(data);
|
||||
this.getDataSource().connector.updateOrCreate(Model.modelName, inst.toObject(true), function (err, data) {
|
||||
var update = data;
|
||||
var inst = data;
|
||||
if(!(data instanceof Model)) {
|
||||
inst = new Model(data);
|
||||
}
|
||||
update = inst.toObject(false);
|
||||
update = removeUndefined(update);
|
||||
this.getDataSource().connector.updateOrCreate(Model.modelName, update, function (err, data) {
|
||||
var obj;
|
||||
if (data) {
|
||||
if (data && !(data instanceof Model)) {
|
||||
inst._initProperties(data);
|
||||
obj = inst;
|
||||
} else {
|
||||
obj = null;
|
||||
obj = data;
|
||||
}
|
||||
callback(err, obj);
|
||||
});
|
||||
} else {
|
||||
this.findById(getIdValue(this, data), function (err, inst) {
|
||||
if (err) return callback(err);
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
if (inst) {
|
||||
inst.updateAttributes(data, callback);
|
||||
} else {
|
||||
|
@ -470,7 +484,6 @@ DataAccessObject._coerce = function (where) {
|
|||
* - limit: Number
|
||||
* - skip: Number
|
||||
*
|
||||
* @param {Object} params (optional)
|
||||
* @param {Function} callback (required) called with two arguments: err (null or Error), array of instances
|
||||
*/
|
||||
|
||||
|
@ -628,9 +641,7 @@ setRemoting(DataAccessObject.findOne, {
|
|||
* @param {Object} [where] An object that defines the criteria
|
||||
* @param {Function} [cb] Callback called with (err)
|
||||
*/
|
||||
DataAccessObject.remove =
|
||||
DataAccessObject.deleteAll =
|
||||
DataAccessObject.destroyAll = function destroyAll(where, cb) {
|
||||
DataAccessObject.remove = DataAccessObject.deleteAll = DataAccessObject.destroyAll = function destroyAll(where, cb) {
|
||||
if (stillConnecting(this.getDataSource(), this, arguments)) return;
|
||||
var Model = this;
|
||||
|
||||
|
@ -655,13 +666,13 @@ DataAccessObject.remove =
|
|||
};
|
||||
|
||||
/**
|
||||
* Destroy a record by id
|
||||
* Delete the record with the specified ID.
|
||||
* Aliases are `destroyById` and `deleteById`.
|
||||
* @param {*} id The id value
|
||||
* @param {Function} cb Callback called with (err)
|
||||
*/
|
||||
DataAccessObject.removeById =
|
||||
DataAccessObject.deleteById =
|
||||
DataAccessObject.destroyById = function deleteById(id, cb) {
|
||||
|
||||
DataAccessObject.removeById = DataAccessObject.deleteById = DataAccessObject.destroyById = function deleteById(id, cb) {
|
||||
if (stillConnecting(this.getDataSource(), this, arguments)) return;
|
||||
var Model = this;
|
||||
|
||||
|
@ -708,10 +719,12 @@ setRemoting(DataAccessObject.count, {
|
|||
});
|
||||
|
||||
/**
|
||||
* Save instance. When instance haven't id, create method called instead.
|
||||
* Triggers: validate, save, update | create
|
||||
* @param options {validate: true, throws: false} [optional]
|
||||
* @param callback(err, obj)
|
||||
* Save instance. If the instance does not have an ID, call `create` instead.
|
||||
* Triggers: validate, save, update or create.
|
||||
* @options {Object} options Optional options to use.
|
||||
* @property {Boolean} validate Default is true.
|
||||
* @property {Boolean} throws Default is false.
|
||||
* @param {Function} callback Callback function with err and object arguments
|
||||
*/
|
||||
DataAccessObject.prototype.save = function (options, callback) {
|
||||
if (stillConnecting(this.getDataSource(), this, arguments)) return;
|
||||
|
@ -763,6 +776,7 @@ DataAccessObject.prototype.save = function (options, callback) {
|
|||
function save() {
|
||||
inst.trigger('save', function (saveDone) {
|
||||
inst.trigger('update', function (updateDone) {
|
||||
data = removeUndefined(data);
|
||||
inst._adapter().save(modelName, inst.constructor._forDB(data), function (err) {
|
||||
if (err) {
|
||||
return callback(err, inst);
|
||||
|
@ -821,9 +835,8 @@ DataAccessObject.prototype.remove =
|
|||
};
|
||||
|
||||
/**
|
||||
* Update single attribute
|
||||
*
|
||||
* equals to `updateAttributes({name: value}, cb)
|
||||
* Update a single attribute.
|
||||
* Equivalent to `updateAttributes({name: value}, cb)`
|
||||
*
|
||||
* @param {String} name Name of property
|
||||
* @param {Mixed} value Value of property
|
||||
|
@ -836,9 +849,8 @@ DataAccessObject.prototype.updateAttribute = function updateAttribute(name, valu
|
|||
};
|
||||
|
||||
/**
|
||||
* Update set of attributes
|
||||
*
|
||||
* this method performs validation before updating
|
||||
* Update saet of attributes.
|
||||
* Performs validation before updating.
|
||||
*
|
||||
* @trigger `validation`, `save` and `update` hooks
|
||||
* @param {Object} data Data to update
|
||||
|
|
|
@ -718,7 +718,7 @@ DataSource.prototype.defineProperty = function (model, prop, params) {
|
|||
* Drop each model table and re-create.
|
||||
* This method applies only to SQL connectors.
|
||||
*
|
||||
* @param {String} Models to be migrated, if not present, apply to all models. This can also be an array of Strings.
|
||||
* @param {String} model Model to migrate. If not present, apply to all models. Can also be an array of Strings.
|
||||
* @param {Function} cb Callback function. Optional.
|
||||
*
|
||||
* WARNING: Calling this function will cause all data to be lost! Use autoupdate if you need to preserve data.
|
||||
|
@ -740,7 +740,7 @@ DataSource.prototype.automigrate = function (models, cb) {
|
|||
* Update existing database tables.
|
||||
* This method make sense only for sql connectors.
|
||||
*
|
||||
* @param {String} Models to be migrated, if not present, apply to all models. This can also be an array of Strings.
|
||||
* @param {String} model Model to migrate. If not present, apply to all models. Can also be an array of Strings.
|
||||
* @param {Function} [cb] The callback function
|
||||
*/
|
||||
DataSource.prototype.autoupdate = function (models, cb) {
|
||||
|
@ -760,15 +760,16 @@ DataSource.prototype.autoupdate = function (models, cb) {
|
|||
* Discover existing database tables.
|
||||
* This method returns an array of model objects, including {type, name, onwer}
|
||||
*
|
||||
* Kyes in options object:
|
||||
*
|
||||
* - all: true - Discovering all models, false - Discovering the models owned by the current user
|
||||
* - views: true - Including views, false - only tables
|
||||
* - limit: The page size
|
||||
* - offset: The starting index
|
||||
*
|
||||
* @param {Object} options The options
|
||||
* @param {Function} Callback function. Optional.
|
||||
* @options {Object} options Discovery options.
|
||||
*
|
||||
* Keys in options object:
|
||||
*
|
||||
* @property all {Boolean} If true, discover all models; if false, discover only models owned by the current user.
|
||||
* @property views {Boolean} If true, nclude views; if false, only tables.
|
||||
* @property limit {Number} Page size
|
||||
* @property offset {Number} Starting index
|
||||
*
|
||||
*/
|
||||
DataSource.prototype.discoverModelDefinitions = function (options, cb) {
|
||||
|
|
27
lib/model.js
27
lib/model.js
|
@ -14,7 +14,7 @@ var List = require('./list');
|
|||
var Hookable = require('./hooks');
|
||||
var validations = require('./validations.js');
|
||||
|
||||
var BASE_TYPES = ['String', 'Boolean', 'Number', 'Date', 'Text'];
|
||||
var BASE_TYPES = ['String', 'Boolean', 'Number', 'Date', 'Text', 'ObjectID'];
|
||||
|
||||
/**
|
||||
* Model class: base class for all persistent objects.
|
||||
|
@ -57,6 +57,10 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
|
|||
var self = this;
|
||||
var ctor = this.constructor;
|
||||
|
||||
if(data instanceof ctor) {
|
||||
// Convert the data to be plain object to avoid polutions
|
||||
data = data.toObject(false);
|
||||
}
|
||||
var properties = ctor.definition.build();
|
||||
data = data || {};
|
||||
|
||||
|
@ -246,7 +250,12 @@ ModelBaseClass.prototype.toObject = function (onlySchema, removeHidden) {
|
|||
var schemaLess = (strict === false) || !onlySchema;
|
||||
|
||||
this.constructor.forEachProperty(function (propertyName) {
|
||||
if(removeHidden && Model.isHiddenProperty(propertyName)) return;
|
||||
if (removeHidden && Model.isHiddenProperty(propertyName)) {
|
||||
return;
|
||||
}
|
||||
if (typeof self[propertyName] === 'function') {
|
||||
return;
|
||||
}
|
||||
if (self[propertyName] instanceof List) {
|
||||
data[propertyName] = self[propertyName].toObject(!schemaLess, removeHidden);
|
||||
} else if (self.__data.hasOwnProperty(propertyName)) {
|
||||
|
@ -266,9 +275,14 @@ ModelBaseClass.prototype.toObject = function (onlySchema, removeHidden) {
|
|||
// If the property is not declared in the model definition, no setter will be
|
||||
// triggered to add it to __data
|
||||
for (var propertyName in self) {
|
||||
if(removeHidden && Model.isHiddenProperty(propertyName)) continue;
|
||||
if(removeHidden && Model.isHiddenProperty(propertyName)) {
|
||||
continue;
|
||||
}
|
||||
if(self.hasOwnProperty(propertyName) && (!data.hasOwnProperty(propertyName))) {
|
||||
val = self[propertyName];
|
||||
if (typeof val === 'function') {
|
||||
continue;
|
||||
}
|
||||
if (val !== undefined && val !== null && val.toObject) {
|
||||
data[propertyName] = val.toObject(!schemaLess, removeHidden);
|
||||
} else {
|
||||
|
@ -279,8 +293,13 @@ ModelBaseClass.prototype.toObject = function (onlySchema, removeHidden) {
|
|||
// Now continue to check __data
|
||||
for (propertyName in self.__data) {
|
||||
if (!data.hasOwnProperty(propertyName)) {
|
||||
if(removeHidden && Model.isHiddenProperty(propertyName)) continue;
|
||||
if(removeHidden && Model.isHiddenProperty(propertyName)) {
|
||||
continue;
|
||||
}
|
||||
val = self.hasOwnProperty(propertyName) ? self[propertyName] : self.__data[propertyName];
|
||||
if (typeof val === 'function') {
|
||||
continue;
|
||||
}
|
||||
if (val !== undefined && val !== null && val.toObject) {
|
||||
data[propertyName] = val.toObject(!schemaLess, removeHidden);
|
||||
} else {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "loopback-datasource-juggler",
|
||||
"version": "1.3.12",
|
||||
"version": "1.3.13",
|
||||
"description": "LoopBack DataSoure Juggler",
|
||||
"keywords": [
|
||||
"StrongLoop",
|
||||
|
|
|
@ -576,6 +576,152 @@ describe('Load models with base', function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Models attached to a dataSource', function() {
|
||||
var Post;
|
||||
before(function() {
|
||||
var ds = new DataSource('memory');// define models
|
||||
Post = ds.define('Post', {
|
||||
title: { type: String, length: 255, index: true },
|
||||
content: { type: String }
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(function(done) {
|
||||
Post.destroyAll(done);
|
||||
});
|
||||
|
||||
it('updateOrCreate should update the instance', function (done) {
|
||||
Post.create({title: 'a', content: 'AAA'}, function (err, post) {
|
||||
post.title = 'b';
|
||||
Post.updateOrCreate(post, function (err, p) {
|
||||
should.not.exist(err);
|
||||
p.id.should.be.equal(post.id);
|
||||
p.content.should.be.equal(post.content);
|
||||
should.not.exist(p._id);
|
||||
|
||||
Post.findById(post.id, function (err, p) {
|
||||
p.id.should.be.equal(post.id);
|
||||
should.not.exist(p._id);
|
||||
p.content.should.be.equal(post.content);
|
||||
p.title.should.be.equal('b');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
it('updateOrCreate should update the instance without removing existing properties', function (done) {
|
||||
Post.create({title: 'a', content: 'AAA'}, function (err, post) {
|
||||
post = post.toObject();
|
||||
delete post.title;
|
||||
Post.updateOrCreate(post, function (err, p) {
|
||||
should.not.exist(err);
|
||||
p.id.should.be.equal(post.id);
|
||||
p.content.should.be.equal(post.content);
|
||||
should.not.exist(p._id);
|
||||
|
||||
Post.findById(post.id, function (err, p) {
|
||||
p.id.should.be.equal(post.id);
|
||||
should.not.exist(p._id);
|
||||
p.content.should.be.equal(post.content);
|
||||
p.title.should.be.equal('a');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
it('updateOrCreate should create a new instance if it does not exist', function (done) {
|
||||
var post = {id: 123, title: 'a', content: 'AAA'};
|
||||
Post.updateOrCreate(post, function (err, p) {
|
||||
should.not.exist(err);
|
||||
p.title.should.be.equal(post.title);
|
||||
p.content.should.be.equal(post.content);
|
||||
p.id.should.be.equal(post.id);
|
||||
|
||||
Post.findById(p.id, function (err, p) {
|
||||
p.id.should.be.equal(post.id);
|
||||
should.not.exist(p._id);
|
||||
p.content.should.be.equal(post.content);
|
||||
p.title.should.be.equal(post.title);
|
||||
p.id.should.be.equal(post.id);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('save should update the instance with the same id', function (done) {
|
||||
Post.create({title: 'a', content: 'AAA'}, function (err, post) {
|
||||
post.title = 'b';
|
||||
post.save(function (err, p) {
|
||||
should.not.exist(err);
|
||||
p.id.should.be.equal(post.id);
|
||||
p.content.should.be.equal(post.content);
|
||||
should.not.exist(p._id);
|
||||
|
||||
Post.findById(post.id, function (err, p) {
|
||||
p.id.should.be.equal(post.id);
|
||||
should.not.exist(p._id);
|
||||
p.content.should.be.equal(post.content);
|
||||
p.title.should.be.equal('b');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
it('save should update the instance without removing existing properties', function (done) {
|
||||
Post.create({title: 'a', content: 'AAA'}, function (err, post) {
|
||||
delete post.title;
|
||||
post.save(function (err, p) {
|
||||
should.not.exist(err);
|
||||
p.id.should.be.equal(post.id);
|
||||
p.content.should.be.equal(post.content);
|
||||
should.not.exist(p._id);
|
||||
|
||||
Post.findById(post.id, function (err, p) {
|
||||
p.id.should.be.equal(post.id);
|
||||
should.not.exist(p._id);
|
||||
p.content.should.be.equal(post.content);
|
||||
p.title.should.be.equal('a');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
it('save should create a new instance if it does not exist', function (done) {
|
||||
var post = new Post({id: '123', title: 'a', content: 'AAA'});
|
||||
post.save(post, function (err, p) {
|
||||
should.not.exist(err);
|
||||
p.title.should.be.equal(post.title);
|
||||
p.content.should.be.equal(post.content);
|
||||
p.id.should.be.equal(post.id);
|
||||
|
||||
Post.findById(p.id, function (err, p) {
|
||||
p.id.should.be.equal(post.id);
|
||||
should.not.exist(p._id);
|
||||
p.content.should.be.equal(post.content);
|
||||
p.title.should.be.equal(post.title);
|
||||
p.id.should.be.equal(post.id);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('DataSource connector types', function() {
|
||||
it('should return an array of types', function() {
|
||||
var ds = new DataSource('memory');
|
||||
|
|
Loading…
Reference in New Issue