Merge branch 'release/2.9.0' into production
This commit is contained in:
commit
fd68cae20b
18
lib/dao.js
18
lib/dao.js
|
@ -137,14 +137,16 @@ DataAccessObject.create = function (data, callback) {
|
|||
}
|
||||
|
||||
var obj;
|
||||
var idValue = getIdValue(this, data);
|
||||
|
||||
// if we come from save
|
||||
if (data instanceof Model && !getIdValue(this, data)) {
|
||||
if (data instanceof Model && !idValue) {
|
||||
obj = data;
|
||||
} else {
|
||||
obj = new Model(data);
|
||||
}
|
||||
data = obj.toObject(true);
|
||||
|
||||
|
||||
// validation required
|
||||
obj.isValid(function (valid) {
|
||||
if (valid) {
|
||||
|
@ -170,6 +172,7 @@ DataAccessObject.create = function (data, callback) {
|
|||
if (err) {
|
||||
return callback(err, obj);
|
||||
}
|
||||
obj.__persisted = true;
|
||||
saveDone.call(obj, function () {
|
||||
createDone.call(obj, function () {
|
||||
callback(err, obj);
|
||||
|
@ -315,7 +318,7 @@ DataAccessObject.findById = function find(id, cb) {
|
|||
if (!getIdValue(this, data)) {
|
||||
setIdValue(this, data, id);
|
||||
}
|
||||
obj = new this(data, {applySetters: false});
|
||||
obj = new this(data, {applySetters: false, persisted: true});
|
||||
}
|
||||
cb(err, obj);
|
||||
}.bind(this));
|
||||
|
@ -732,7 +735,7 @@ DataAccessObject.find = function find(query, cb) {
|
|||
this.getDataSource().connector.all(this.modelName, query, function (err, data) {
|
||||
if (data && data.forEach) {
|
||||
data.forEach(function (d, i) {
|
||||
var obj = new self(d, {fields: query.fields, applySetters: false});
|
||||
var obj = new self(d, {fields: query.fields, applySetters: false, persisted: true});
|
||||
|
||||
if (query && query.include) {
|
||||
if (query.collect) {
|
||||
|
@ -928,7 +931,7 @@ DataAccessObject.prototype.save = function (options, callback) {
|
|||
var data = inst.toObject(true);
|
||||
var modelName = Model.modelName;
|
||||
|
||||
if (!getIdValue(Model, this)) {
|
||||
if (this.isNewRecord()) {
|
||||
return Model.create(this, callback);
|
||||
}
|
||||
|
||||
|
@ -959,7 +962,7 @@ DataAccessObject.prototype.save = function (options, callback) {
|
|||
if (err) {
|
||||
return callback(err, inst);
|
||||
}
|
||||
inst._initProperties(data);
|
||||
inst._initProperties(data, { persisted: true });
|
||||
updateDone.call(inst, function () {
|
||||
saveDone.call(inst, function () {
|
||||
callback(err, inst);
|
||||
|
@ -1027,7 +1030,7 @@ DataAccessObject.updateAll = function (where, data, cb) {
|
|||
};
|
||||
|
||||
DataAccessObject.prototype.isNewRecord = function () {
|
||||
return !getIdValue(this.constructor, this);
|
||||
return !this.__persisted;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1168,6 +1171,7 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, cb
|
|||
|
||||
inst._adapter().updateAttributes(model, getIdValue(inst.constructor, inst),
|
||||
inst.constructor._forDB(typedData), function (err) {
|
||||
if (!err) inst.__persisted = true;
|
||||
done.call(inst, function () {
|
||||
saveDone.call(inst, function () {
|
||||
if(cb) cb(err, inst);
|
||||
|
|
|
@ -489,6 +489,9 @@ DataSource.prototype.setupDataAccess = function (modelClass, settings) {
|
|||
var idType = this.connector.getDefaultIdType() || String;
|
||||
idProp.type = idType;
|
||||
modelClass.definition.properties[idName].type = idType;
|
||||
if (settings.forceId) {
|
||||
modelClass.validatesAbsenceOf(idName, { if: 'isNewRecord' });
|
||||
}
|
||||
}
|
||||
if (this.connector.define) {
|
||||
// pass control to connector
|
||||
|
|
|
@ -47,6 +47,7 @@ function ModelBaseClass(data, options) {
|
|||
* @param {Object} options An object to control the instantiation
|
||||
* @property {Boolean} applySetters Controls if the setters will be applied
|
||||
* @property {Boolean} strict Set the instance level strict mode
|
||||
* @property {Boolean} persisted Whether the instance has been persisted
|
||||
* @private
|
||||
*/
|
||||
ModelBaseClass.prototype._initProperties = function (data, options) {
|
||||
|
@ -67,7 +68,11 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
|
|||
if(strict === undefined) {
|
||||
strict = ctor.definition.settings.strict;
|
||||
}
|
||||
|
||||
|
||||
if (options.persisted !== undefined) {
|
||||
this.__persisted = options.persisted === true;
|
||||
}
|
||||
|
||||
if (ctor.hideInternalProperties) {
|
||||
// Object.defineProperty() is expensive. We only try to make the internal
|
||||
// properties hidden (non-enumerable) if the model class has the
|
||||
|
|
|
@ -11,9 +11,6 @@ var applyFilter = require('./connectors/memory').applyFilter;
|
|||
var ValidationError = require('./validations.js').ValidationError;
|
||||
var debug = require('debug')('loopback:relations');
|
||||
|
||||
exports.Relation = Relation;
|
||||
exports.RelationDefinition = RelationDefinition;
|
||||
|
||||
var RelationTypes = {
|
||||
belongsTo: 'belongsTo',
|
||||
hasMany: 'hasMany',
|
||||
|
@ -24,16 +21,6 @@ var RelationTypes = {
|
|||
embedsMany: 'embedsMany'
|
||||
};
|
||||
|
||||
exports.RelationTypes = RelationTypes;
|
||||
exports.HasMany = HasMany;
|
||||
exports.HasManyThrough = HasManyThrough;
|
||||
exports.HasOne = HasOne;
|
||||
exports.HasAndBelongsToMany = HasAndBelongsToMany;
|
||||
exports.BelongsTo = BelongsTo;
|
||||
exports.ReferencesMany = ReferencesMany;
|
||||
exports.EmbedsOne = EmbedsOne;
|
||||
exports.EmbedsMany = EmbedsMany;
|
||||
|
||||
var RelationClasses = {
|
||||
belongsTo: BelongsTo,
|
||||
hasMany: HasMany,
|
||||
|
@ -45,6 +32,21 @@ var RelationClasses = {
|
|||
embedsMany: EmbedsMany
|
||||
};
|
||||
|
||||
exports.Relation = Relation;
|
||||
exports.RelationDefinition = RelationDefinition;
|
||||
|
||||
exports.RelationTypes = RelationTypes;
|
||||
exports.RelationClasses = RelationClasses;
|
||||
|
||||
exports.HasMany = HasMany;
|
||||
exports.HasManyThrough = HasManyThrough;
|
||||
exports.HasOne = HasOne;
|
||||
exports.HasAndBelongsToMany = HasAndBelongsToMany;
|
||||
exports.BelongsTo = BelongsTo;
|
||||
exports.ReferencesMany = ReferencesMany;
|
||||
exports.EmbedsOne = EmbedsOne;
|
||||
exports.EmbedsMany = EmbedsMany;
|
||||
|
||||
function normalizeType(type) {
|
||||
if (!type) {
|
||||
return type;
|
||||
|
@ -1711,6 +1713,7 @@ RelationDefinition.embedsOne = function (modelFrom, modelTo, params) {
|
|||
relationMethod.build = relation.build.bind(relation);
|
||||
relationMethod.update = relation.update.bind(relation);
|
||||
relationMethod.destroy = relation.destroy.bind(relation);
|
||||
relationMethod.value = relation.embeddedValue.bind(relation);
|
||||
relationMethod._targetClass = definition.modelTo.modelName;
|
||||
return relationMethod;
|
||||
}
|
||||
|
@ -1749,6 +1752,8 @@ EmbedsOne.prototype.related = function (refresh, params) {
|
|||
}
|
||||
|
||||
var embeddedInstance = modelInstance[propertyName];
|
||||
embeddedInstance.__persisted = true;
|
||||
|
||||
if (typeof params === 'function') { // acts as async getter
|
||||
var cb = params;
|
||||
process.nextTick(function() {
|
||||
|
@ -1759,6 +1764,11 @@ EmbedsOne.prototype.related = function (refresh, params) {
|
|||
}
|
||||
};
|
||||
|
||||
EmbedsOne.prototype.embeddedValue = function(modelInstance) {
|
||||
modelInstance = modelInstance || this.modelInstance;
|
||||
return modelInstance[this.definition.keyFrom];
|
||||
};
|
||||
|
||||
EmbedsOne.prototype.create = function (targetModelData, cb) {
|
||||
var modelTo = this.definition.modelTo;
|
||||
var propertyName = this.definition.keyFrom;
|
||||
|
@ -1773,29 +1783,53 @@ EmbedsOne.prototype.create = function (targetModelData, cb) {
|
|||
|
||||
var inst = this.build(targetModelData);
|
||||
|
||||
var err = inst.isValid() ? null : new ValidationError(inst);
|
||||
|
||||
if (err) {
|
||||
return process.nextTick(function() {
|
||||
cb(err);
|
||||
var updateEmbedded = function() {
|
||||
modelInstance.updateAttribute(propertyName,
|
||||
inst, function(err) {
|
||||
cb(err, err ? null : inst);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
modelInstance.updateAttribute(propertyName,
|
||||
inst, function(err) {
|
||||
cb(err, err ? null : inst);
|
||||
});
|
||||
if (this.definition.options.persistent) {
|
||||
inst.save(function(err) { // will validate
|
||||
if (err) return cb(err, inst);
|
||||
updateEmbedded();
|
||||
});
|
||||
} else {
|
||||
var err = inst.isValid() ? null : new ValidationError(inst);
|
||||
if (err) {
|
||||
process.nextTick(function() {
|
||||
cb(err);
|
||||
});
|
||||
} else {
|
||||
updateEmbedded();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EmbedsOne.prototype.build = function (targetModelData) {
|
||||
var modelTo = this.definition.modelTo;
|
||||
var modelInstance = this.modelInstance;
|
||||
var propertyName = this.definition.keyFrom;
|
||||
var forceId = this.definition.options.forceId;
|
||||
var persistent = this.definition.options.persistent;
|
||||
var connector = modelTo.dataSource.connector;
|
||||
|
||||
targetModelData = targetModelData || {};
|
||||
|
||||
this.definition.applyProperties(modelInstance, targetModelData);
|
||||
|
||||
var pk = this.definition.keyTo;
|
||||
var pkProp = modelTo.definition.properties[pk];
|
||||
|
||||
var assignId = (forceId || targetModelData[pk] === undefined);
|
||||
assignId = assignId && !persistent && (pkProp && pkProp.generated);
|
||||
|
||||
if (assignId && typeof connector.generateId === 'function') {
|
||||
var id = connector.generateId(modelTo.modelName, targetModelData, pk);
|
||||
targetModelData[pk] = id;
|
||||
}
|
||||
|
||||
var embeddedInstance = new modelTo(targetModelData);
|
||||
modelInstance[propertyName] = embeddedInstance;
|
||||
|
||||
|
@ -1863,12 +1897,25 @@ RelationDefinition.embedsMany = function embedsMany(modelFrom, modelTo, params)
|
|||
embed: true
|
||||
});
|
||||
|
||||
modelFrom.dataSource.defineProperty(modelFrom.modelName, propertyName, {
|
||||
modelFrom.dataSource.defineProperty(modelFrom.modelName, propertyName, {
|
||||
type: [modelTo], default: function() { return []; }
|
||||
});
|
||||
|
||||
if (typeof modelTo.dataSource.connector.generateId !== 'function') {
|
||||
modelTo.validatesPresenceOf(idName); // unique id is required
|
||||
modelFrom.validate(propertyName, function(err) {
|
||||
var self = this;
|
||||
var embeddedList = this[propertyName] || [];
|
||||
var hasErrors = false;
|
||||
embeddedList.forEach(function(item, idx) {
|
||||
if (item instanceof modelTo && item[idName] == undefined) {
|
||||
hasErrors = true;
|
||||
var msg = 'contains invalid item at index `' + idx + '`:';
|
||||
msg += ' `' + idName + '` is blank';
|
||||
self.errors.add(propertyName, msg, 'invalid');
|
||||
}
|
||||
});
|
||||
if (hasErrors) err(false);
|
||||
});
|
||||
}
|
||||
|
||||
if (!params.polymorphic) {
|
||||
|
@ -1891,13 +1938,17 @@ RelationDefinition.embedsMany = function embedsMany(modelFrom, modelTo, params)
|
|||
var self = this;
|
||||
var embeddedList = this[propertyName] || [];
|
||||
var hasErrors = false;
|
||||
embeddedList.forEach(function(item) {
|
||||
embeddedList.forEach(function(item, idx) {
|
||||
if (item instanceof modelTo) {
|
||||
if (!item.isValid()) {
|
||||
hasErrors = true;
|
||||
var id = item[idName] || '(blank)';
|
||||
var id = item[idName];
|
||||
var first = Object.keys(item.errors)[0];
|
||||
var msg = 'contains invalid item: `' + id + '`';
|
||||
if (id) {
|
||||
var msg = 'contains invalid item: `' + id + '`';
|
||||
} else {
|
||||
var msg = 'contains invalid item at index `' + idx + '`';
|
||||
}
|
||||
msg += ' (`' + first + '` ' + item.errors[first] + ')';
|
||||
self.errors.add(propertyName, msg, 'invalid');
|
||||
}
|
||||
|
@ -1920,7 +1971,8 @@ RelationDefinition.embedsMany = function embedsMany(modelFrom, modelTo, params)
|
|||
get: scopeMethod(definition, 'get'),
|
||||
set: scopeMethod(definition, 'set'),
|
||||
unset: scopeMethod(definition, 'unset'),
|
||||
at: scopeMethod(definition, 'at')
|
||||
at: scopeMethod(definition, 'at'),
|
||||
value: scopeMethod(definition, 'embeddedValue')
|
||||
};
|
||||
|
||||
var findByIdFunc = scopeMethods.findById;
|
||||
|
@ -1968,6 +2020,12 @@ EmbedsMany.prototype.prepareEmbeddedInstance = function(inst) {
|
|||
var self = this;
|
||||
var propertyName = this.definition.keyFrom;
|
||||
var modelInstance = this.modelInstance;
|
||||
if (this.definition.options.persistent) {
|
||||
var pk = this.definition.keyTo;
|
||||
inst.__persisted = !!inst[pk];
|
||||
} else {
|
||||
inst.__persisted = true;
|
||||
}
|
||||
inst.triggerParent = function(actionName, callback) {
|
||||
if (actionName === 'save' || actionName === 'destroy') {
|
||||
var embeddedList = self.embeddedList();
|
||||
|
@ -2000,7 +2058,8 @@ EmbedsMany.prototype.prepareEmbeddedInstance = function(inst) {
|
|||
}
|
||||
};
|
||||
|
||||
EmbedsMany.prototype.embeddedList = function(modelInstance) {
|
||||
EmbedsMany.prototype.embeddedList =
|
||||
EmbedsMany.prototype.embeddedValue = function(modelInstance) {
|
||||
modelInstance = modelInstance || this.modelInstance;
|
||||
var embeddedList = modelInstance[this.definition.keyFrom] || [];
|
||||
embeddedList.forEach(this.prepareEmbeddedInstance.bind(this));
|
||||
|
@ -2028,7 +2087,7 @@ EmbedsMany.prototype.related = function(receiver, scopeParams, condOrRefresh, cb
|
|||
|
||||
var params = mergeQuery(actualCond, scopeParams);
|
||||
|
||||
if (params.where) { // TODO [fabien] Support order/sorting
|
||||
if (params.where && Object.keys(params.where).length > 0) { // TODO [fabien] Support order/sorting
|
||||
embeddedList = embeddedList ? embeddedList.filter(applyFilter(params)) : embeddedList;
|
||||
}
|
||||
|
||||
|
@ -2180,28 +2239,39 @@ EmbedsMany.prototype.create = function (targetModelData, cb) {
|
|||
|
||||
var inst = this.build(targetModelData);
|
||||
|
||||
var err = inst.isValid() ? null : new ValidationError(inst);
|
||||
|
||||
if (err) {
|
||||
return process.nextTick(function() {
|
||||
cb(err);
|
||||
var updateEmbedded = function() {
|
||||
modelInstance.updateAttribute(propertyName,
|
||||
embeddedList, function(err, modelInst) {
|
||||
cb(err, err ? null : inst);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
modelInstance.updateAttribute(propertyName,
|
||||
embeddedList, function(err, modelInst) {
|
||||
cb(err, err ? null : inst);
|
||||
});
|
||||
if (this.definition.options.persistent) {
|
||||
inst.save(function(err) { // will validate
|
||||
if (err) return cb(err, inst);
|
||||
updateEmbedded();
|
||||
});
|
||||
} else {
|
||||
var err = inst.isValid() ? null : new ValidationError(inst);
|
||||
if (err) {
|
||||
process.nextTick(function() {
|
||||
cb(err);
|
||||
});
|
||||
} else {
|
||||
updateEmbedded();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EmbedsMany.prototype.build = function(targetModelData) {
|
||||
var modelTo = this.definition.modelTo;
|
||||
var modelInstance = this.modelInstance;
|
||||
var forceId = this.definition.options.forceId;
|
||||
var persistent = this.definition.options.persistent;
|
||||
var connector = modelTo.dataSource.connector;
|
||||
|
||||
var pk = this.definition.keyTo;
|
||||
var pkProp = modelTo.definition.properties[pk]
|
||||
var pkProp = modelTo.definition.properties[pk];
|
||||
var pkType = pkProp && pkProp.type;
|
||||
|
||||
var embeddedList = this.embeddedList();
|
||||
|
@ -2209,6 +2279,7 @@ EmbedsMany.prototype.build = function(targetModelData) {
|
|||
targetModelData = targetModelData || {};
|
||||
|
||||
var assignId = (forceId || targetModelData[pk] === undefined);
|
||||
assignId = assignId && !persistent;
|
||||
|
||||
if (assignId && pkType === Number) {
|
||||
var ids = embeddedList.map(function(m) {
|
||||
|
|
16
lib/scope.js
16
lib/scope.js
|
@ -55,13 +55,13 @@ ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefres
|
|||
} else {
|
||||
throw new Error('Method can be only called with one or two arguments');
|
||||
}
|
||||
|
||||
|
||||
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 targetModel = this.targetModel(receiver);
|
||||
return targetModel.find(params, function (err, data) {
|
||||
targetModel.find(params, function (err, data) {
|
||||
if (!err && saveOnCache) {
|
||||
defineCachedRelations(self);
|
||||
self.__cachedRelations[name] = data;
|
||||
|
@ -149,10 +149,16 @@ function defineScope(cls, targetClass, name, params, methods, options) {
|
|||
var self = this;
|
||||
|
||||
var f = function(condOrRefresh, cb) {
|
||||
if(arguments.length === 1) {
|
||||
definition.related(self, f._scope, condOrRefresh);
|
||||
if (arguments.length === 0) {
|
||||
if (typeof f.value === 'function') {
|
||||
return f.value(self);
|
||||
} else if (self.__cachedRelations) {
|
||||
return self.__cachedRelations[name];
|
||||
}
|
||||
} else if (arguments.length === 1) {
|
||||
return definition.related(self, f._scope, condOrRefresh);
|
||||
} else {
|
||||
definition.related(self, f._scope, condOrRefresh, cb);
|
||||
return definition.related(self, f._scope, condOrRefresh, cb);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -641,13 +641,14 @@ function configure(cls, validation, args, opts) {
|
|||
conf.customValidator = args.pop();
|
||||
}
|
||||
conf.validation = validation;
|
||||
var attr = args[0];
|
||||
if (typeof attr === 'string') {
|
||||
var validation = extend({}, conf);
|
||||
validation.options = opts || {};
|
||||
cls.validations[attr] = cls.validations[attr] || [];
|
||||
cls.validations[attr].push(validation);
|
||||
}
|
||||
args.forEach(function (attr) {
|
||||
if (typeof attr === 'string') {
|
||||
var validation = extend({}, conf);
|
||||
validation.options = opts || {};
|
||||
cls.validations[attr] = cls.validations[attr] || [];
|
||||
cls.validations[attr].push(validation);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function Errors() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "loopback-datasource-juggler",
|
||||
"version": "2.8.0",
|
||||
"version": "2.9.0",
|
||||
"description": "LoopBack DataSoure Juggler",
|
||||
"keywords": [
|
||||
"StrongLoop",
|
||||
|
|
|
@ -16,7 +16,7 @@ describe('manipulation', function () {
|
|||
age: {type: Number, index: true},
|
||||
dob: Date,
|
||||
createdAt: {type: Number, default: Date.now}
|
||||
});
|
||||
}, { forceId: true });
|
||||
|
||||
db.automigrate(done);
|
||||
|
||||
|
@ -40,6 +40,18 @@ describe('manipulation', function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should instantiate an object', function (done) {
|
||||
var p = new Person({name: 'Anatoliy'});
|
||||
p.name.should.equal('Anatoliy');
|
||||
p.isNewRecord().should.be.true;
|
||||
p.save(function(err, inst) {
|
||||
should.not.exist(err);
|
||||
inst.isNewRecord().should.be.false;
|
||||
inst.should.equal(p);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return instance of object', function (done) {
|
||||
var person = Person.create(function (err, p) {
|
||||
|
@ -50,6 +62,31 @@ describe('manipulation', function () {
|
|||
person.should.be.an.instanceOf(Person);
|
||||
should.not.exist(person.id);
|
||||
});
|
||||
|
||||
it('should not allow user-defined value for the id of object - create', function (done) {
|
||||
Person.create({id: 123456}, function (err, p) {
|
||||
err.should.be.instanceof(ValidationError);
|
||||
err.message.should.equal('The `Person` instance is not valid. Details: `id` can\'t be set.');
|
||||
err.statusCode.should.equal(422);
|
||||
p.should.be.instanceof(Person);
|
||||
p.id.should.equal(123456);
|
||||
p.isNewRecord().should.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not allow user-defined value for the id of object - save', function (done) {
|
||||
var p = new Person({id: 123456});
|
||||
p.isNewRecord().should.be.true;
|
||||
p.save(function(err, inst) {
|
||||
err.should.be.instanceof(ValidationError);
|
||||
err.message.should.equal('The `Person` instance is not valid. Details: `id` can\'t be set.');
|
||||
err.statusCode.should.equal(422);
|
||||
inst.id.should.equal(123456);
|
||||
inst.isNewRecord().should.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work when called without callback', function (done) {
|
||||
Person.afterCreate = function (next) {
|
||||
|
|
|
@ -13,6 +13,10 @@ var getTransientDataSource = function(settings) {
|
|||
return new DataSource('transient', settings, db.modelBuilder);
|
||||
};
|
||||
|
||||
var getMemoryDataSource = function(settings) {
|
||||
return new DataSource('memory', settings, db.modelBuilder);
|
||||
};
|
||||
|
||||
describe('relations', function () {
|
||||
|
||||
describe('hasMany', function () {
|
||||
|
@ -91,10 +95,14 @@ describe('relations', function () {
|
|||
should.not.exist(err);
|
||||
should.exist(ch);
|
||||
ch.should.have.lengthOf(3);
|
||||
|
||||
|
||||
var chapters = book.chapters();
|
||||
chapters.should.eql(ch);
|
||||
|
||||
book.chapters({order: 'name DESC'}, function (e, c) {
|
||||
should.not.exist(e);
|
||||
should.exist(c);
|
||||
|
||||
c.shift().name.should.equal('z');
|
||||
c.pop().name.should.equal('a');
|
||||
done();
|
||||
|
@ -298,6 +306,10 @@ describe('relations', function () {
|
|||
});
|
||||
function verify(physician) {
|
||||
physician.patients(function (err, ch) {
|
||||
|
||||
var patients = physician.patients();
|
||||
patients.should.eql(ch);
|
||||
|
||||
should.not.exist(err);
|
||||
should.exist(ch);
|
||||
ch.should.have.lengthOf(3);
|
||||
|
@ -842,6 +854,10 @@ describe('relations', function () {
|
|||
Author.findOne(function (err, author) {
|
||||
author.avatar(function (err, p) {
|
||||
should.not.exist(err);
|
||||
|
||||
var avatar = author.avatar();
|
||||
avatar.should.equal(p);
|
||||
|
||||
p.name.should.equal('Avatar');
|
||||
p.imageableId.should.eql(author.id);
|
||||
p.imageableType.should.equal('Author');
|
||||
|
@ -971,6 +987,10 @@ describe('relations', function () {
|
|||
Author.findOne(function (err, author) {
|
||||
author.pictures(function (err, pics) {
|
||||
should.not.exist(err);
|
||||
|
||||
var pictures = author.pictures();
|
||||
pictures.should.eql(pics);
|
||||
|
||||
pics.should.have.length(1);
|
||||
pics[0].name.should.equal('Author Pic');
|
||||
done();
|
||||
|
@ -1638,6 +1658,9 @@ describe('relations', function () {
|
|||
article.tags(function (e, tags) {
|
||||
should.not.exist(e);
|
||||
should.exist(tags);
|
||||
|
||||
article.tags().should.eql(tags);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -1756,6 +1779,7 @@ describe('relations', function () {
|
|||
passport.toObject().should.eql({name: 'Fredric'});
|
||||
passport.should.be.an.instanceOf(Passport);
|
||||
passport.should.equal(p.passport);
|
||||
passport.should.equal(p.passportItem.value());
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -1829,6 +1853,81 @@ describe('relations', function () {
|
|||
|
||||
});
|
||||
|
||||
describe('embedsOne - persisted model', function () {
|
||||
|
||||
// This test spefically uses the Memory connector
|
||||
// in order to test the use of the auto-generated
|
||||
// id, in the sequence of the related model.
|
||||
|
||||
before(function () {
|
||||
db = getMemoryDataSource();
|
||||
Person = db.define('Person', {name: String});
|
||||
Passport = db.define('Passport',
|
||||
{name:{type:'string', required: true}}
|
||||
);
|
||||
});
|
||||
|
||||
it('can be declared using embedsOne method', function (done) {
|
||||
Person.embedsOne(Passport, {
|
||||
options: {persistent: true}
|
||||
});
|
||||
db.automigrate(done);
|
||||
});
|
||||
|
||||
it('should create an item - to offset id', function(done) {
|
||||
Passport.create({name:'Wilma'}, function(err, p) {
|
||||
should.not.exist(err);
|
||||
p.id.should.equal(1);
|
||||
p.name.should.equal('Wilma');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should create an embedded item on scope', function(done) {
|
||||
Person.create({name: 'Fred'}, function(err, p) {
|
||||
should.not.exist(err);
|
||||
p.passportItem.create({name: 'Fredric'}, function(err, passport) {
|
||||
should.not.exist(err);
|
||||
p.passport.id.should.eql(2);
|
||||
p.passport.name.should.equal('Fredric');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('embedsOne - generated id', function () {
|
||||
|
||||
before(function () {
|
||||
tmp = getTransientDataSource();
|
||||
db = getSchema();
|
||||
Person = db.define('Person', {name: String});
|
||||
Passport = tmp.define('Passport',
|
||||
{id: {type:'string', id: true, generated:true}},
|
||||
{name: {type:'string', required: true}}
|
||||
);
|
||||
});
|
||||
|
||||
it('can be declared using embedsOne method', function (done) {
|
||||
Person.embedsOne(Passport);
|
||||
db.automigrate(done);
|
||||
});
|
||||
|
||||
it('should create an embedded item on scope', function(done) {
|
||||
Person.create({name: 'Fred'}, function(err, p) {
|
||||
should.not.exist(err);
|
||||
p.passportItem.create({name: 'Fredric'}, function(err, passport) {
|
||||
should.not.exist(err);
|
||||
passport.id.should.match(/^[0-9a-fA-F]{24}$/);
|
||||
p.passport.name.should.equal('Fredric');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('embedsMany', function () {
|
||||
|
||||
var address1, address2;
|
||||
|
@ -1891,6 +1990,13 @@ describe('relations', function () {
|
|||
Person.findOne(function(err, p) {
|
||||
p.addressList(function(err, addresses) {
|
||||
should.not.exist(err);
|
||||
|
||||
var list = p.addressList();
|
||||
list.should.equal(addresses);
|
||||
list.should.equal(p.addresses);
|
||||
|
||||
p.addressList.value().should.equal(list);
|
||||
|
||||
addresses.should.have.length(2);
|
||||
addresses[0].id.should.eql(address1.id);
|
||||
addresses[0].street.should.equal('Street 1');
|
||||
|
@ -2012,6 +2118,45 @@ describe('relations', function () {
|
|||
|
||||
});
|
||||
|
||||
describe('embedsMany - numeric ids + forceId', function () {
|
||||
|
||||
before(function (done) {
|
||||
tmp = getTransientDataSource();
|
||||
db = getSchema();
|
||||
Person = db.define('Person', {name: String});
|
||||
Address = tmp.define('Address', {
|
||||
id: {type: Number, id:true},
|
||||
street: String
|
||||
});
|
||||
|
||||
db.automigrate(function () {
|
||||
Person.destroyAll(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('can be declared', function (done) {
|
||||
Person.embedsMany(Address, {options: {forceId: true}});
|
||||
db.automigrate(done);
|
||||
});
|
||||
|
||||
it('should create embedded items on scope', function(done) {
|
||||
Person.create({ name: 'Fred' }, function(err, p) {
|
||||
p.addressList.create({ street: 'Street 1' }, function(err, address) {
|
||||
should.not.exist(err);
|
||||
address.id.should.equal(1);
|
||||
p.addressList.create({ street: 'Street 2' }, function(err, address) {
|
||||
address.id.should.equal(2);
|
||||
p.addressList.create({ id: 12345, street: 'Street 3' }, function(err, address) {
|
||||
address.id.should.equal(3);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('embedsMany - explicit ids', function () {
|
||||
before(function (done) {
|
||||
tmp = getTransientDataSource();
|
||||
|
@ -2183,6 +2328,134 @@ describe('relations', function () {
|
|||
|
||||
});
|
||||
|
||||
describe('embedsMany - persisted model', function () {
|
||||
|
||||
var address0, address1, address2;
|
||||
var person;
|
||||
|
||||
// This test spefically uses the Memory connector
|
||||
// in order to test the use of the auto-generated
|
||||
// id, in the sequence of the related model.
|
||||
|
||||
before(function (done) {
|
||||
db = getMemoryDataSource();
|
||||
Person = db.define('Person', {name: String});
|
||||
Address = db.define('Address', {street: String});
|
||||
Address.validatesPresenceOf('street');
|
||||
|
||||
db.automigrate(function () {
|
||||
Person.destroyAll(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('can be declared', function (done) {
|
||||
// to save related model itself, set
|
||||
// persistent: true
|
||||
Person.embedsMany(Address, {
|
||||
scope: {order: 'street'},
|
||||
options: {persistent: true}
|
||||
});
|
||||
db.automigrate(done);
|
||||
});
|
||||
|
||||
it('should create individual items (0)', function(done) {
|
||||
Address.create({ street: 'Street 0' }, function(err, inst) {
|
||||
inst.id.should.equal(1); // offset sequence
|
||||
address0 = inst;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should create individual items (1)', function(done) {
|
||||
Address.create({ street: 'Street 1' }, function(err, inst) {
|
||||
inst.id.should.equal(2);
|
||||
address1 = inst;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should create individual items (2)', function(done) {
|
||||
Address.create({ street: 'Street 2' }, function(err, inst) {
|
||||
inst.id.should.equal(3);
|
||||
address2 = inst;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should create individual items (3)', function(done) {
|
||||
Address.create({ street: 'Street 3' }, function(err, inst) {
|
||||
inst.id.should.equal(4); // offset sequence
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should add embedded items on scope', function(done) {
|
||||
Person.create({ name: 'Fred' }, function(err, p) {
|
||||
person = p;
|
||||
p.addressList.create(address1.toObject(), function(err, address) {
|
||||
should.not.exist(err);
|
||||
address.id.should.eql(2);
|
||||
address.street.should.equal('Street 1');
|
||||
p.addressList.create(address2.toObject(), function(err, address) {
|
||||
should.not.exist(err);
|
||||
address.id.should.eql(3);
|
||||
address.street.should.equal('Street 2');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should create embedded items on scope', function(done) {
|
||||
Person.findById(person.id, function(err, p) {
|
||||
p.addressList.create({ street: 'Street 4' }, function(err, address) {
|
||||
should.not.exist(err);
|
||||
address.id.should.equal(5); // in Address sequence, correct offset
|
||||
address.street.should.equal('Street 4');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should have embedded items on scope', function(done) {
|
||||
Person.findById(person.id, function(err, p) {
|
||||
p.addressList(function(err, addresses) {
|
||||
should.not.exist(err);
|
||||
addresses.should.have.length(3);
|
||||
addresses[0].street.should.equal('Street 1');
|
||||
addresses[1].street.should.equal('Street 2');
|
||||
addresses[2].street.should.equal('Street 4');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should validate embedded items on scope - id', function(done) {
|
||||
Person.create({ name: 'Wilma' }, function(err, p) {
|
||||
p.addressList.create({ id: null, street: 'Street 1' }, function(err, address) {
|
||||
should.not.exist(err);
|
||||
address.street.should.equal('Street 1');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should validate embedded items on scope - street', function(done) {
|
||||
Person.create({ name: 'Wilma' }, function(err, p) {
|
||||
p.addressList.create({ id: 1234 }, function(err, address) {
|
||||
should.exist(err);
|
||||
err.name.should.equal('ValidationError');
|
||||
err.details.codes.street.should.eql(['presence']);
|
||||
var expected = 'The `Address` instance is not valid. ';
|
||||
expected += 'Details: `street` can\'t be blank.';
|
||||
err.message.should.equal(expected);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('embedsMany - relations, scope and properties', function () {
|
||||
|
||||
var category, job1, job2, job3;
|
||||
|
|
|
@ -145,9 +145,15 @@ describe('validations', function () {
|
|||
|
||||
it('should validate presence', function () {
|
||||
User.validatesPresenceOf('name', 'email');
|
||||
|
||||
var validations = User.validations;
|
||||
validations.name.should.eql([{validation: 'presence', options: {}}]);
|
||||
validations.email.should.eql([{validation: 'presence', options: {}}]);
|
||||
|
||||
var u = new User;
|
||||
u.isValid().should.not.be.true;
|
||||
u.name = 1;
|
||||
u.isValid().should.not.be.true;
|
||||
u.email = 2;
|
||||
u.isValid().should.be.true;
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue