From bc4076f35e7d06b4ccf638a5f29d2b631602a4fc Mon Sep 17 00:00:00 2001 From: Fabien Franzen <info@atelierfabien.be> Date: Tue, 22 Jul 2014 22:09:29 +0200 Subject: [PATCH 1/6] Implement scope/properties for BelongsTo (+ fix foreign key matching) --- lib/relation-definition.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/relation-definition.js b/lib/relation-definition.js index 9dd478a7..8429fdaa 100644 --- a/lib/relation-definition.js +++ b/lib/relation-definition.js @@ -819,6 +819,8 @@ RelationDefinition.belongsTo = function (modelFrom, modelTo, params) { keyFrom: fk, keyTo: idName, modelTo: modelTo, + properties: params.properties, + scope: params.scope, options: params.options }); @@ -860,6 +862,8 @@ BelongsTo.prototype.create = function(targetModelData, cb) { targetModelData = {}; } + this.definition.applyProperties(modelInstance, targetModelData || {}); + modelTo.create(targetModelData, function(err, targetModel) { if(!err) { modelInstance[fk] = targetModel[pk]; @@ -873,6 +877,7 @@ BelongsTo.prototype.create = function(targetModelData, cb) { BelongsTo.prototype.build = function(targetModelData) { var modelTo = this.definition.modelTo; + this.definition.applyProperties(this.modelInstance, targetModelData || {}); return new modelTo(targetModelData); }; @@ -911,7 +916,12 @@ BelongsTo.prototype.related = function (refresh, params) { } else if (typeof params === 'function') { // acts as async getter var cb = params; if (cachedValue === undefined) { - modelTo.findById(modelInstance[fk], function (err, inst) { + var query = {where: {}}; + query.where[pk] = modelInstance[fk]; + + this.definition.applyScope(modelInstance, query); + + modelTo.findOne(query, function (err, inst) { if (err) { return cb(err); } @@ -919,7 +929,7 @@ BelongsTo.prototype.related = function (refresh, params) { return cb(null, null); } // Check if the foreign key matches the primary key - if (inst[pk] === modelInstance[fk]) { + if (inst[pk] && inst[pk].toString() === modelInstance[fk].toString()) { self.resetCache(inst); cb(null, inst); } else { @@ -1193,7 +1203,7 @@ HasOne.prototype.related = function (refresh, params) { return cb(null, null); } // Check if the foreign key matches the primary key - if (inst[fk] === modelInstance[pk]) { + if (inst[fk] && inst[fk].toString() === modelInstance[pk].toString()) { self.resetCache(inst); cb(null, inst); } else { From 687eb9888b73e6c8bd35c8808a5d69c5c853e2ad Mon Sep 17 00:00:00 2001 From: Fabien Franzen <info@atelierfabien.be> Date: Wed, 23 Jul 2014 11:10:44 +0200 Subject: [PATCH 2/6] Added test for belongsTo scope/properties Note: its seems that keyFrom and keyTo were mistakingly reversed in BelongsTo.prototype.create, please double check. The added test cases now pass with pk/fk switched. --- lib/relation-definition.js | 10 ++++++---- test/relations.test.js | 41 +++++++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/lib/relation-definition.js b/lib/relation-definition.js index 8429fdaa..2aaa70b9 100644 --- a/lib/relation-definition.js +++ b/lib/relation-definition.js @@ -853,8 +853,8 @@ RelationDefinition.belongsTo = function (modelFrom, modelTo, params) { BelongsTo.prototype.create = function(targetModelData, cb) { var self = this; var modelTo = this.definition.modelTo; - var fk = this.definition.keyTo; - var pk = this.definition.keyFrom; + var fk = this.definition.keyFrom; + var pk = this.definition.keyTo; var modelInstance = this.modelInstance; if (typeof targetModelData === 'function' && !cb) { @@ -929,7 +929,8 @@ BelongsTo.prototype.related = function (refresh, params) { return cb(null, null); } // Check if the foreign key matches the primary key - if (inst[pk] && inst[pk].toString() === modelInstance[fk].toString()) { + if (inst[pk] && modelInstance[fk] + && inst[pk].toString() === modelInstance[fk].toString()) { self.resetCache(inst); cb(null, inst); } else { @@ -1203,7 +1204,8 @@ HasOne.prototype.related = function (refresh, params) { return cb(null, null); } // Check if the foreign key matches the primary key - if (inst[fk] && inst[fk].toString() === modelInstance[pk].toString()) { + if (inst[fk] && modelInstance[pk] + && inst[fk].toString() === modelInstance[pk].toString()) { self.resetCache(inst); cb(null, inst); } else { diff --git a/test/relations.test.js b/test/relations.test.js index 0691ded8..8218c84a 100644 --- a/test/relations.test.js +++ b/test/relations.test.js @@ -404,7 +404,7 @@ describe('relations', function () { }); }); - describe('hasMany with scope', function () { + describe('hasMany with scope and properties', function () { it('can be declared with properties', function (done) { db = getSchema(); Category = db.define('Category', {name: String, productType: String}); @@ -556,6 +556,45 @@ describe('relations', function () { }); }); + + describe('belongsTo with scope', function () { + var Person, Passport; + + it('can be declared with scope and properties', function (done) { + Person = db.define('Person', {name: String, age: Number}); + Passport = db.define('Passport', {name: String, notes: String}); + Passport.belongsTo(Person, { + properties: { notes: 'passportNotes' }, + scope: { fields: { id: true, name: true } } + }); + db.automigrate(done); + }); + + it('should create record on scope', function (done) { + var p = new Passport({ name: 'Passport', notes: 'Some notes...' }); + p.person.create({ id: 3, name: 'Fred', age: 36 }, function(err, person) { + p.personId.should.equal(person.id); + p.save(function (err, p) { + person.name.should.equal('Fred'); + person.passportNotes.should.equal('Some notes...'); + done(); + }); + }); + }); + + it('should find record on scope', function (done) { + Passport.findOne(function (err, p) { + p.personId.should.equal(3); + p.person(function(err, person) { + person.name.should.equal('Fred'); + person.should.not.have.property('age'); + person.should.not.have.property('passportNotes'); + done(); + }); + }); + }); + + }); describe('hasOne', function () { var Supplier, Account; From 973c96268f03c193d6f37d473a22cd31da4ad381 Mon Sep 17 00:00:00 2001 From: Fabien Franzen <info@atelierfabien.be> Date: Thu, 24 Jul 2014 15:55:00 +0200 Subject: [PATCH 3/6] Fix scoped destroyAll: only use 'where', not full 'filter' args --- lib/scope.js | 3 ++- test/relations.test.js | 29 ++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/scope.js b/lib/scope.js index 51c97002..c1c90eba 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -221,7 +221,8 @@ function defineScope(cls, targetClass, name, params, methods) { - If fetching the Elements on which destroyAll is called results in an error */ function destroyAll(cb) { - targetClass.destroyAll(this._scope, cb); + var where = (this._scope && this._scope.where) || {}; + targetClass.destroyAll(where, cb); } } diff --git a/test/relations.test.js b/test/relations.test.js index 8218c84a..5f52b4c0 100644 --- a/test/relations.test.js +++ b/test/relations.test.js @@ -437,7 +437,7 @@ describe('relations', function () { }); }); - it('should find record on scope', function (done) { + it('should find records on scope', function (done) { Category.findOne(function (err, c) { c.products(function(err, products) { products.should.have.length(2); @@ -476,6 +476,15 @@ describe('relations', function () { }); }); + it('should find records on scope', function (done) { + Category.findOne(function (err, c) { + c.products(function(err, products) { + products.should.have.length(3); + done(); + }); + }); + }); + it('should find record on scope - scoped', function (done) { Category.findOne(function (err, c) { c.productType = 'book'; // temporary, for scoping @@ -497,6 +506,24 @@ describe('relations', function () { }); }); }); + + it('should delete records on scope - scoped', function (done) { + Category.findOne(function (err, c) { + c.productType = 'tool'; // temporary, for scoping + c.products.destroyAll(function(err, result) { + done(); + }); + }); + }); + + it('should find record on scope - verify', function (done) { + Category.findOne(function (err, c) { + c.products(function(err, products) { + products.should.have.length(2); + done(); + }); + }); + }); }); From cd4dba79dc63f354c98bd16345c7b5ef40d8af04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= <miroslav@strongloop.com> Date: Thu, 24 Jul 2014 19:52:55 +0200 Subject: [PATCH 4/6] relation: add `scope._target` for `hasOne` Add `_target` property to the scope method created by `hasOne` relationship. The `_target` property is used by loopback-sdk-angular. --- lib/relation-definition.js | 1 + test/relations.test.js | 3 +++ 2 files changed, 4 insertions(+) diff --git a/lib/relation-definition.js b/lib/relation-definition.js index 2aaa70b9..9279fc7e 100644 --- a/lib/relation-definition.js +++ b/lib/relation-definition.js @@ -1055,6 +1055,7 @@ RelationDefinition.hasOne = function (modelFrom, modelTo, params) { var relationMethod = relation.related.bind(relation) relationMethod.create = relation.create.bind(relation); relationMethod.build = relation.build.bind(relation); + relationMethod._targetClass = relationDef.modelTo.modelName; return relationMethod; } }); diff --git a/test/relations.test.js b/test/relations.test.js index 5f52b4c0..f091486d 100644 --- a/test/relations.test.js +++ b/test/relations.test.js @@ -658,6 +658,9 @@ describe('relations', function () { }); }); + it('should set targetClass on scope property', function() { + should.equal(Supplier.prototype.account._targetClass, 'Account'); + }); }); describe('hasAndBelongsToMany', function () { From ebd0bc62ee04eebf5583947483c0a43b44c4bd62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= <miroslav@strongloop.com> Date: Fri, 25 Jul 2014 11:32:40 +0200 Subject: [PATCH 5/6] datasource: support connectors without `getTypes` Asking connectors to provide `getTypes` function is a breaking change, connectors working with loopback 1.3 no longer works in newer versions. --- lib/datasource.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/datasource.js b/lib/datasource.js index 774362c7..34f40e0b 100644 --- a/lib/datasource.js +++ b/lib/datasource.js @@ -647,7 +647,8 @@ DataSource.prototype.getModelDefinition = function (name) { * ['rest'], or ['db', 'rdbms', 'mysql'] */ DataSource.prototype.getTypes = function () { - var types = this.connector && this.connector.getTypes() || []; + var getTypes = this.connector && this.connector.getTypes; + var types = getTypes && getTypes() || []; if (typeof types === 'string') { types = types.split(/[\s,\/]+/); } From 4f80890489ab7ca9ef0565bd8a2326bd602f2435 Mon Sep 17 00:00:00 2001 From: Raymond Feng <raymond@strongloop.com> Date: Sat, 26 Jul 2014 23:58:51 -0700 Subject: [PATCH 6/6] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5dd5eb03..797eb50d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-datasource-juggler", - "version": "2.0.0", + "version": "2.1.0", "description": "LoopBack DataSoure Juggler", "keywords": [ "StrongLoop",