Removed observers from a Model after finish of the stream
This commit is contained in:
Miroslav Bajtoš 2017-07-12 16:15:20 +02:00 committed by GitHub
commit f7b88e3435
2 changed files with 89 additions and 56 deletions

View File

@ -1851,36 +1851,42 @@ module.exports = function(registry) {
var idName = this.getIdName(); var idName = this.getIdName();
var Model = this; var Model = this;
var changes = new PassThrough({objectMode: true}); var changes = new PassThrough({objectMode: true});
var writeable = true;
changes.destroy = function() { changes._destroy = function() {
changes.removeAllListeners('error'); changes.end();
changes.removeAllListeners('end'); changes.emit('end');
writeable = false; changes.emit('close');
changes = null;
}; };
changes.on('error', function() { changes.destroy = changes.destroy || changes._destroy; // node 8 compability
writeable = false;
}); changes.on('error', removeHandlers);
changes.on('end', function() { changes.on('close', removeHandlers);
writeable = false; changes.on('finish', removeHandlers);
}); changes.on('end', removeHandlers);
process.nextTick(function() { process.nextTick(function() {
cb(null, changes); cb(null, changes);
}); });
Model.observe('after save', createChangeHandler('save')); Model.observe('after save', changeHandler);
Model.observe('after delete', createChangeHandler('delete')); Model.observe('after delete', deleteHandler);
function createChangeHandler(type) { function changeHandler(ctx, next) {
return function(ctx, next) { var change = createChangeObject(ctx, 'save');
// since it might have set to null via destroy changes.write(change);
if (!changes) {
return next();
}
next();
};
function deleteHandler(ctx, next) {
var change = createChangeObject(ctx, 'delete');
changes.write(change);
next();
};
function createChangeObject(ctx, type) {
var where = ctx.where; var where = ctx.where;
var data = ctx.instance || ctx.data; var data = ctx.instance || ctx.data;
var whereId = where && where[idName]; var whereId = where && where[idName];
@ -1917,13 +1923,12 @@ module.exports = function(registry) {
break; break;
} }
// TODO(ritch) this is ugly... maybe a ReadableStream would be better return change;
if (writeable) {
changes.write(change);
} }
next(); function removeHandlers() {
}; Model.removeObserver('after save', changeHandler);
Model.removeObserver('after delete', deleteHandler);
} }
}; };

View File

@ -5,6 +5,7 @@
'use strict'; 'use strict';
var expect = require('./helpers/expect'); var expect = require('./helpers/expect');
var sinon = require('sinon');
var loopback = require('../'); var loopback = require('../');
describe('PersistedModel.createChangeStream()', function() { describe('PersistedModel.createChangeStream()', function() {
@ -20,6 +21,8 @@ describe('PersistedModel.createChangeStream()', function() {
}); });
}); });
afterEach(verifyObserversRemoval);
it('should detect create', function(done) { it('should detect create', function(done) {
var Score = this.Score; var Score = this.Score;
@ -27,7 +30,6 @@ describe('PersistedModel.createChangeStream()', function() {
changes.on('data', function(change) { changes.on('data', function(change) {
expect(change.type).to.equal('create'); expect(change.type).to.equal('create');
changes.destroy(); changes.destroy();
done(); done();
}); });
@ -67,6 +69,32 @@ describe('PersistedModel.createChangeStream()', function() {
}); });
}); });
}); });
it('should not emit changes after destroy', function(done) {
var Score = this.Score;
var spy = sinon.spy();
Score.createChangeStream(function(err, changes) {
changes.on('data', function() {
spy();
changes.destroy();
});
Score.create({team: 'foo'})
.then(() => Score.deleteAll())
.then(() => {
expect(spy.calledOnce);
done();
});
});
});
function verifyObserversRemoval() {
var Score = this.Score;
expect(Score._observers['after save']).to.be.empty();
expect(Score._observers['after delete']).to.be.empty();
}
}); });
// TODO(ritch) implement multi-server support // TODO(ritch) implement multi-server support