From a4db8874935a9e59fc90617c5d3c256977be8a38 Mon Sep 17 00:00:00 2001 From: Fabien Franzen Date: Tue, 26 Aug 2014 14:54:19 +0200 Subject: [PATCH 1/4] Implement where arg on scoped count and destroyAll --- lib/scope.js | 19 ++++---- test/relations.test.js | 26 ++++++++++- test/scope.test.js | 99 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 9 deletions(-) diff --git a/lib/scope.js b/lib/scope.js index d5f6b38e..0dd2b40b 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -245,21 +245,25 @@ function defineScope(cls, targetClass, name, params, methods, options) { } this.build(data).save(cb); } - + /* Callback - The callback will be called after all elements are destroyed - For every destroy call which results in an error - If fetching the Elements on which destroyAll is called results in an error */ - function destroyAll(cb) { - var where = (this._scope && this._scope.where) || {}; - targetClass.destroyAll(where, cb); + function destroyAll(where, cb) { + if (typeof where === 'function') cb = where, where = {}; + var scoped = (this._scope && this._scope.where) || {}; + var filter = mergeQuery({ where: scoped }, { where: where || {} }); + targetClass.destroyAll(filter.where, cb); } - function count(cb) { - var where = (this._scope && this._scope.where) || {}; - targetClass.count(where, cb); + function count(where, cb) { + if (typeof where === 'function') cb = where, where = {}; + var scoped = (this._scope && this._scope.where) || {}; + var filter = mergeQuery({ where: scoped }, { where: where || {} }); + targetClass.count(filter.where, cb); } return definition; @@ -321,4 +325,3 @@ function mergeQuery(base, update) { } return base; } - diff --git a/test/relations.test.js b/test/relations.test.js index f4b8e79e..ef212363 100644 --- a/test/relations.test.js +++ b/test/relations.test.js @@ -119,6 +119,30 @@ describe('relations', function () { }); } }); + + it('should count scoped records - all and filtered', function (done) { + Book.create(function (err, book) { + book.chapters.create({name: 'a'}, function (err, ch) { + book.chapters.create({name: 'b'}, function () { + book.chapters.create({name: 'c'}, function () { + verify(book); + }); + }); + }); + }); + + function verify(book) { + book.chapters.count(function (err, count) { + should.not.exist(err); + count.should.equal(3); + book.chapters.count({ name: 'b' }, function (err, count) { + should.not.exist(err); + count.should.equal(1); + done(); + }); + }); + } + }); it('should set targetClass on scope property', function() { should.equal(Book.prototype.chapters._targetClass, 'Chapter'); @@ -2657,4 +2681,4 @@ describe('relations', function () { }); -}); +}); \ No newline at end of file diff --git a/test/scope.test.js b/test/scope.test.js index c8033c82..cb464ce4 100644 --- a/test/scope.test.js +++ b/test/scope.test.js @@ -75,6 +75,7 @@ describe('scope', function () { }); }); }); + }); describe('scope - order', function () { @@ -130,3 +131,101 @@ describe('scope - order', function () { }); +describe('scope - filtered count and destroyAll', function () { + + before(function () { + db = getSchema(); + Station = db.define('Station', { + name: {type: String, index: true}, + order: {type: Number, index: true}, + active: {type: Boolean, index: true, default: true} + }); + Station.scope('ordered', {order: 'order'}); + Station.scope('active', {where: { active: true}}); + Station.scope('inactive', {where: { active: false}}); + }); + + beforeEach(function (done) { + Station.destroyAll(done); + }); + + beforeEach(function (done) { + Station.create({ name: 'b', order: 2, active: false }, done); + }); + + beforeEach(function (done) { + Station.create({ name: 'a', order: 1 }, done); + }); + + beforeEach(function (done) { + Station.create({ name: 'd', order: 4, active: false }, done); + }); + + beforeEach(function (done) { + Station.create({ name: 'c', order: 3 }, done); + }); + + it('should find all - verify', function(done) { + Station.ordered(function(err, stations) { + should.not.exist(err); + stations.should.have.length(4); + stations[0].name.should.equal('a'); + stations[1].name.should.equal('b'); + stations[2].name.should.equal('c'); + stations[3].name.should.equal('d'); + done(); + }); + }); + + it('should count all in scope - active', function(done) { + Station.active.count(function(err, count) { + should.not.exist(err); + count.should.equal(2); + done(); + }); + }); + + it('should count all in scope - inactive', function(done) { + Station.inactive.count(function(err, count) { + should.not.exist(err); + count.should.equal(2); + done(); + }); + }); + + it('should count filtered - active', function(done) { + Station.active.count({ order: { gt: 1 } }, function(err, count) { + should.not.exist(err); + count.should.equal(1); + done(); + }); + }); + + it('should count filtered - inactive', function(done) { + Station.inactive.count({ order: 2 }, function(err, count) { + should.not.exist(err); + count.should.equal(1); + done(); + }); + }); + + it('should allow filtered destroyAll', function(done) { + Station.ordered.destroyAll({ active: false }, function(err) { + should.not.exist(err); + verify(); + }); + + var verify = function() { + Station.ordered.count(function(err, count) { + should.not.exist(err); + count.should.equal(2); + Station.inactive.count(function(err, count) { + should.not.exist(err); + count.should.equal(0); + done(); + }); + }); + }; + }); + +}); From fde5b426e4b499c57f6279087a87c1404710b6bf Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Wed, 27 Aug 2014 09:14:17 -0700 Subject: [PATCH 2/4] Make sure error events are emitted by data source --- lib/datasource.js | 15 +++++++++++++++ test/loopback-dl.test.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/lib/datasource.js b/lib/datasource.js index ffd2608c..1730051a 100644 --- a/lib/datasource.js +++ b/lib/datasource.js @@ -291,6 +291,7 @@ DataSource.prototype.setup = function (name, settings) { connector = result.connector; if (!connector) { console.error(result.error); + this.emit('error', new Error(result.error)); return; } } @@ -310,6 +311,7 @@ DataSource.prototype.setup = function (name, settings) { } else { // The connection fails, let's report it and hope it will be recovered in the next call console.error('Connection fails: ', err, '\nIt will be retried for the next request.'); + this.emit('error', err); this.connecting = false; } @@ -1878,6 +1880,19 @@ DataSource.prototype.ready = function (obj, args) { }; this.once('connected', onConnected); this.once('error', onError); + + // Set up a timeout to cancel the invocation + var timeout = this.settings.connectionTimeout || 5000; + setTimeout(function() { + self.removeListener('error', onError); + self.removeListener('connected', onConnected); + var params = [].slice.call(args); + var cb = params.pop(); + if (typeof cb === 'function') { + cb(new Error('Timeout in connecting after ' + timeout + ' ms')); + } + }, timeout); + if (!this.connecting) { this.connect(); } diff --git a/test/loopback-dl.test.js b/test/loopback-dl.test.js index 972d7aab..940c9a79 100644 --- a/test/loopback-dl.test.js +++ b/test/loopback-dl.test.js @@ -245,6 +245,38 @@ describe('ModelBuilder define model', function () { }); +describe('DataSource ping', function() { + var ds = new DataSource('memory'); + ds.settings.connectionTimeout = 50; // ms + ds.connector.connect = function(cb) { + // Mock up the long delay + setTimeout(cb, 100); + }; + ds.connector.ping = function(cb) { + cb(new Error('bad connection 2')); + } + + it('should report connection errors during ping', function(done) { + ds.ping(function(err) { + (!!err).should.be.true; + err.message.should.be.eql('bad connection 2'); + done(); + }); + }); + + it('should cancel invocation after timeout', function(done) { + ds.connected = false; // Force connect + var Post = ds.define('Post', { + title: { type: String, length: 255 } + }); + Post.create(function(err) { + (!!err).should.be.true; + err.message.should.be.eql('Timeout in connecting after 50 ms'); + done(); + }); + }); +}); + describe('DataSource define model', function () { it('should be able to define plain models', function () { var ds = new DataSource('memory'); From 32313c1df92b0c8196005a44afb9d9b8b3148cad Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Wed, 27 Aug 2014 09:42:16 -0700 Subject: [PATCH 3/4] Make sure timeout handle is cleared --- lib/datasource.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/datasource.js b/lib/datasource.js index 1730051a..c55ed685 100644 --- a/lib/datasource.js +++ b/lib/datasource.js @@ -1863,15 +1863,21 @@ DataSource.prototype.ready = function (obj, args) { var method = args.callee; // Set up a callback after the connection is established to continue the method call - var onConnected = null, onError = null; + var onConnected = null, onError = null, timeoutHandle = null; onConnected = function () { // Remove the error handler self.removeListener('error', onError); + if (timeoutHandle) { + clearTimeout(timeoutHandle); + } method.apply(obj, [].slice.call(args)); }; onError = function (err) { // Remove the connected listener self.removeListener('connected', onConnected); + if (timeoutHandle) { + clearTimeout(timeoutHandle); + } var params = [].slice.call(args); var cb = params.pop(); if (typeof cb === 'function') { @@ -1883,7 +1889,7 @@ DataSource.prototype.ready = function (obj, args) { // Set up a timeout to cancel the invocation var timeout = this.settings.connectionTimeout || 5000; - setTimeout(function() { + timeoutHandle = setTimeout(function () { self.removeListener('error', onError); self.removeListener('connected', onConnected); var params = [].slice.call(args); From 9cad519d97d6688c12cc888f9b15d08098eeb7d9 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Wed, 27 Aug 2014 11:15:57 -0700 Subject: [PATCH 4/4] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9dcb04d6..c2d3588f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "loopback-datasource-juggler", - "version": "2.6.1", + "version": "2.7.0", "description": "LoopBack DataSoure Juggler", "keywords": [ "StrongLoop",