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",