Merge branch 'release/2.12.0' into production
This commit is contained in:
commit
acd20ae6f8
File diff suppressed because it is too large
Load Diff
|
@ -5,8 +5,9 @@ for interacting with databases, REST APIs, and other data sources. It was
|
|||
initially forked from [JugglingDB](https://github.com/1602/jugglingdb).
|
||||
|
||||
**For full documentation, see the official StrongLoop documentation**:
|
||||
* [Data sources and connectors](http://docs.strongloop.com/display/LB/Data+sources+and+connectors)
|
||||
* [Creating data sources and connected models](http://docs.strongloop.com/display/LB/Creating+data+sources+and+connected+models).
|
||||
[Connecting models to data sources](http://docs.strongloop.com/display/LB/Connecting+models+to+data+sources)
|
||||
|
||||
For information on creating data sources programmatically, see [Advanced topics: data sources](http://docs.strongloop.com/display/LB/Advanced+topics%3A+data+sources).
|
||||
|
||||
## Installation
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"content": [
|
||||
"lib/datasource.js",
|
||||
"lib/model.js",
|
||||
"lib/geo.js",
|
||||
"lib/hooks.js",
|
||||
"lib/include.js",
|
||||
|
|
|
@ -89,7 +89,7 @@ DataAccessObject.defaultScope = function(target, inst) {
|
|||
DataAccessObject.applyScope = function(query, inst) {
|
||||
var scope = this.defaultScope(query, inst) || {};
|
||||
if (typeof scope === 'object') {
|
||||
mergeQuery(query, scope || {}, this.definition.settings.scoping);
|
||||
mergeQuery(query, scope || {}, this.definition.settings.scope);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1245,7 +1245,8 @@ DataSource.prototype.discoverSchemas = function (modelName, options, cb) {
|
|||
var propName = fromDBName(item.columnName, true);
|
||||
schema.properties[propName] = {
|
||||
type: item.type,
|
||||
required: (item.nullable === 'N'),
|
||||
required: (item.nullable === 'N' || item.nullable === 'NO'
|
||||
|| item.nullable === 0 || item.nullable === false),
|
||||
length: item.dataLength,
|
||||
precision: item.dataPrecision,
|
||||
scale: item.dataScale
|
||||
|
|
|
@ -114,7 +114,7 @@ function RelationDefinition(definition) {
|
|||
}
|
||||
this.modelThrough = definition.modelThrough;
|
||||
this.keyThrough = definition.keyThrough;
|
||||
this.multiple = (this.type !== 'belongsTo' && this.type !== 'hasOne');
|
||||
this.multiple = definition.multiple;
|
||||
this.properties = definition.properties || {};
|
||||
this.options = definition.options || {};
|
||||
this.scope = definition.scope;
|
||||
|
@ -1139,6 +1139,7 @@ RelationDefinition.belongsTo = function (modelFrom, modelTo, params) {
|
|||
keyFrom: fk,
|
||||
keyTo: idName,
|
||||
modelTo: modelTo,
|
||||
multiple: false,
|
||||
properties: params.properties,
|
||||
scope: params.scope,
|
||||
options: params.options,
|
||||
|
@ -1447,7 +1448,9 @@ RelationDefinition.hasOne = function (modelFrom, modelTo, params) {
|
|||
keyFrom: pk,
|
||||
keyTo: fk,
|
||||
modelTo: modelTo,
|
||||
multiple: false,
|
||||
properties: params.properties,
|
||||
scope: params.scope,
|
||||
options: params.options,
|
||||
polymorphic: polymorphic
|
||||
});
|
||||
|
@ -1750,7 +1753,7 @@ RelationDefinition.embedsOne = function (modelFrom, modelTo, params) {
|
|||
configurable: true,
|
||||
get: function() {
|
||||
var relation = new EmbedsOne(definition, this);
|
||||
var relationMethod = relation.related.bind(relation)
|
||||
var relationMethod = relation.related.bind(relation);
|
||||
relationMethod.create = relation.create.bind(relation);
|
||||
relationMethod.build = relation.build.bind(relation);
|
||||
relationMethod.update = relation.update.bind(relation);
|
||||
|
@ -1794,7 +1797,10 @@ EmbedsOne.prototype.related = function (refresh, params) {
|
|||
}
|
||||
|
||||
var embeddedInstance = modelInstance[propertyName];
|
||||
embeddedInstance.__persisted = true;
|
||||
|
||||
if (embeddedInstance) {
|
||||
embeddedInstance.__persisted = true;
|
||||
}
|
||||
|
||||
if (typeof params === 'function') { // acts as async getter
|
||||
var cb = params;
|
||||
|
|
|
@ -505,7 +505,7 @@ function validationFailed(inst, attr, conf, cb) {
|
|||
// that can be specified in conf
|
||||
if (skipValidation(inst, conf, 'if')
|
||||
|| skipValidation(inst, conf, 'unless')) {
|
||||
if (cb) cb(true);
|
||||
if (cb) cb(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "loopback-datasource-juggler",
|
||||
"version": "2.11.0",
|
||||
"version": "2.12.0",
|
||||
"description": "LoopBack DataSoure Juggler",
|
||||
"keywords": [
|
||||
"StrongLoop",
|
||||
|
|
|
@ -1005,7 +1005,7 @@ describe('relations', function () {
|
|||
author.avatar.create({ name: 'Avatar' }, function (err, p) {
|
||||
should.not.exist(err);
|
||||
should.exist(p);
|
||||
p.oid.should.equal(author.username);
|
||||
p.oid.toString().should.equal(author.username.toString());
|
||||
p.type.should.equal('Author');
|
||||
done();
|
||||
});
|
||||
|
@ -1017,7 +1017,7 @@ describe('relations', function () {
|
|||
reader.mugshot.create({ name: 'Mugshot' }, function (err, p) {
|
||||
should.not.exist(err);
|
||||
should.exist(p);
|
||||
p.oid.should.equal(reader.username);
|
||||
p.oid.toString().should.equal(reader.username.toString());
|
||||
p.type.should.equal('Reader');
|
||||
done();
|
||||
});
|
||||
|
@ -1797,6 +1797,52 @@ describe('relations', function () {
|
|||
|
||||
});
|
||||
|
||||
describe('hasOne with scope', function () {
|
||||
|
||||
var Supplier, Account;
|
||||
var supplierId, accountId;
|
||||
|
||||
before(function () {
|
||||
db = getSchema();
|
||||
Supplier = db.define('Supplier', {name: String});
|
||||
Account = db.define('Account', {accountNo: String, supplierName: String, block: Boolean});
|
||||
Supplier.hasOne(Account, { scope: { where: { block: false } }, properties: { name: 'supplierName' } });
|
||||
});
|
||||
|
||||
it('can be used to query data', function (done) {
|
||||
db.automigrate(function () {
|
||||
Supplier.create({name: 'Supplier 1'}, function (e, supplier) {
|
||||
supplierId = supplier.id;
|
||||
should.not.exist(e);
|
||||
should.exist(supplier);
|
||||
supplier.account.create({accountNo: 'a01'}, function (err, account) {
|
||||
supplier.account(function (e, act) {
|
||||
accountId = act.id;
|
||||
should.not.exist(e);
|
||||
should.exist(act);
|
||||
act.should.be.an.instanceOf(Account);
|
||||
supplier.account().id.should.equal(act.id);
|
||||
act.supplierName.should.equal(supplier.name);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should find record that match scope', function (done) {
|
||||
Account.updateAll({ block: true }, function (err) {
|
||||
Supplier.findById(supplierId, function (err, supplier) {
|
||||
supplier.account(function (err, account) {
|
||||
should.not.exists(account);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('hasOne with non standard id', function () {
|
||||
var Supplier, Account;
|
||||
var supplierId, accountId;
|
||||
|
@ -1965,6 +2011,7 @@ describe('relations', function () {
|
|||
describe('embedsOne', function () {
|
||||
|
||||
var person;
|
||||
var Passport;
|
||||
var Other;
|
||||
|
||||
before(function () {
|
||||
|
@ -1975,6 +2022,7 @@ describe('relations', function () {
|
|||
{name:{type:'string', required: true}},
|
||||
{idInjection: false}
|
||||
);
|
||||
Address = tmp.define('Address', { street: String }, { idInjection: false });
|
||||
Other = db.define('Other', {name: String});
|
||||
});
|
||||
|
||||
|
@ -1982,6 +2030,7 @@ describe('relations', function () {
|
|||
Person.embedsOne(Passport, {
|
||||
default: {name: 'Anonymous'} // a bit contrived
|
||||
});
|
||||
Person.embedsOne(Address); // all by default
|
||||
db.automigrate(done);
|
||||
});
|
||||
|
||||
|
@ -1994,6 +2043,19 @@ describe('relations', function () {
|
|||
p.passportItem.destroy.should.be.a.function;
|
||||
});
|
||||
|
||||
it('should behave properly without default or being set', function (done) {
|
||||
var p = new Person();
|
||||
should.not.exist(p.address);
|
||||
var a = p.addressItem();
|
||||
should.not.exist(a);
|
||||
Person.create({}, function (err, p) {
|
||||
should.not.exist(p.address);
|
||||
var a = p.addressItem();
|
||||
should.not.exist(a);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return an instance with default values', function() {
|
||||
var p = new Person();
|
||||
p.passport.toObject().should.eql({name: 'Anonymous'});
|
||||
|
|
|
@ -52,16 +52,106 @@ describe('validations', function () {
|
|||
|
||||
describe('skipping', function () {
|
||||
|
||||
it('should allow to skip using if: attribute', function () {
|
||||
it('should NOT skip when `if` is fulfilled', function () {
|
||||
User.validatesPresenceOf('pendingPeriod', {if: 'createdByAdmin'});
|
||||
var user = new User;
|
||||
user.createdByAdmin = true;
|
||||
user.isValid().should.be.false;
|
||||
user.errors.pendingPeriod.should.eql(['can\'t be blank']);
|
||||
user.pendingPeriod = 1
|
||||
user.pendingPeriod = 1;
|
||||
user.isValid().should.be.true;
|
||||
});
|
||||
|
||||
it('should skip when `if` is NOT fulfilled', function () {
|
||||
User.validatesPresenceOf('pendingPeriod', {if: 'createdByAdmin'});
|
||||
var user = new User;
|
||||
user.createdByAdmin = false;
|
||||
user.isValid().should.be.true;
|
||||
user.errors.should.be.false;
|
||||
user.pendingPeriod = 1;
|
||||
user.isValid().should.be.true;
|
||||
});
|
||||
|
||||
it('should NOT skip when `unless` is fulfilled', function () {
|
||||
User.validatesPresenceOf('pendingPeriod', {unless: 'createdByAdmin'});
|
||||
var user = new User;
|
||||
user.createdByAdmin = false;
|
||||
user.isValid().should.be.false;
|
||||
user.errors.pendingPeriod.should.eql(['can\'t be blank']);
|
||||
user.pendingPeriod = 1;
|
||||
user.isValid().should.be.true;
|
||||
});
|
||||
|
||||
it('should skip when `unless` is NOT fulfilled', function () {
|
||||
User.validatesPresenceOf('pendingPeriod', {unless: 'createdByAdmin'});
|
||||
var user = new User;
|
||||
user.createdByAdmin = true;
|
||||
user.isValid().should.be.true;
|
||||
user.errors.should.be.false;
|
||||
user.pendingPeriod = 1;
|
||||
user.isValid().should.be.true;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('skipping in async validation', function () {
|
||||
|
||||
it('should skip when `if` is NOT fulfilled', function (done) {
|
||||
User.validateAsync('pendingPeriod', function (err, done) {
|
||||
if (!this.pendingPeriod) err();
|
||||
done();
|
||||
}, {if: 'createdByAdmin', code: 'presence', message: 'can\'t be blank'});
|
||||
var user = new User;
|
||||
user.createdByAdmin = false;
|
||||
user.isValid(function (valid) {
|
||||
valid.should.be.true;
|
||||
user.errors.should.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should NOT skip when `if` is fulfilled', function (done) {
|
||||
User.validateAsync('pendingPeriod', function (err, done) {
|
||||
if (!this.pendingPeriod) err();
|
||||
done();
|
||||
}, {if: 'createdByAdmin', code: 'presence', message: 'can\'t be blank'});
|
||||
var user = new User;
|
||||
user.createdByAdmin = true;
|
||||
user.isValid(function (valid) {
|
||||
valid.should.be.false;
|
||||
user.errors.pendingPeriod.should.eql(['can\'t be blank']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should skip when `unless` is NOT fulfilled', function (done) {
|
||||
User.validateAsync('pendingPeriod', function (err, done) {
|
||||
if (!this.pendingPeriod) err();
|
||||
done();
|
||||
}, {unless: 'createdByAdmin', code: 'presence', message: 'can\'t be blank'});
|
||||
var user = new User;
|
||||
user.createdByAdmin = true;
|
||||
user.isValid(function (valid) {
|
||||
valid.should.be.true;
|
||||
user.errors.should.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should NOT skip when `unless` is fulfilled', function (done) {
|
||||
User.validateAsync('pendingPeriod', function (err, done) {
|
||||
if (!this.pendingPeriod) err();
|
||||
done();
|
||||
}, {unless: 'createdByAdmin', code: 'presence', message: 'can\'t be blank'});
|
||||
var user = new User;
|
||||
user.createdByAdmin = false;
|
||||
user.isValid(function (valid) {
|
||||
valid.should.be.false;
|
||||
user.errors.pendingPeriod.should.eql(['can\'t be blank']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('lifecycle', function () {
|
||||
|
|
Loading…
Reference in New Issue