Merge pull request #386 from isocolsky/ref/notifications

Ref/notifications
This commit is contained in:
Matias Alejo Garcia 2015-10-20 15:27:45 -03:00
commit a0f99f6660
7 changed files with 139 additions and 80 deletions

View File

@ -83,7 +83,7 @@ BlockchainMonitor.prototype._initExplorer = function(explorer) {
log.error('Error connecting to ' + explorer.getConnectionInfo());
});
socket.on('tx', _.bind(self._handleIncommingTx, self));
socket.on('block', _.bind(self._handleNewBlock, self));
socket.on('block', _.bind(self._handleNewBlock, self, explorer.network));
};
BlockchainMonitor.prototype._handleTxId = function(data, processIt) {
@ -177,12 +177,13 @@ BlockchainMonitor.prototype._handleIncommingTx = function(data) {
this._handleTxOuts(data);
};
BlockchainMonitor.prototype._handleNewBlock = function(hash) {
BlockchainMonitor.prototype._handleNewBlock = function(network, hash) {
var self = this;
log.info('New block: ', hash);
log.info('New ' + network + ' block: ', hash);
var notification = Notification.create({
type: 'NewBlock',
walletId: network, // use network name as wallet id for global notifications
data: {
hash: hash,
},

View File

@ -439,8 +439,9 @@ ExpressApp.prototype.start = function(opts, cb) {
router.get('/v1/notifications/', function(req, res) {
getServerWithAuth(req, res, function(server) {
var timeSpan = req.query.timeSpan ? Math.min(+req.query.timeSpan || 0, 60) : 60;
var opts = {
minTs: +Date.now() - (60 * 1000),
minTs: +Date.now() - (timeSpan * 1000),
notificationId: req.query.notificationId,
};
server.getNotifications(opts, function(err, notifications) {

View File

@ -52,12 +52,6 @@ function WalletService() {
this.notifyTicker = 0;
};
WalletService.getServiceVersion = function() {
if (!serviceVersion)
serviceVersion = 'bws-' + require('../package').version;
return serviceVersion;
};
// Time after which a Tx proposal can be erased by any copayer. in seconds
WalletService.DELETE_LOCKTIME = 24 * 3600;
@ -89,6 +83,15 @@ WalletService.FEE_LEVELS = [{
}];
/**
* Gets the current version of BWS
*/
WalletService.getServiceVersion = function() {
if (!serviceVersion)
serviceVersion = 'bws-' + require('../package').version;
return serviceVersion;
};
/**
* Initializes global settings for all instances.
* @param {Object} opts
@ -1657,9 +1660,21 @@ WalletService.prototype.getNotifications = function(opts, cb) {
var self = this;
opts = opts || {};
self.storage.fetchNotifications(self.walletId, opts.notificationId, opts.minTs || 0, function(err, notifications) {
self.getWallet({}, function(err, wallet) {
if (err) return cb(err);
return cb(null, notifications);
async.map([wallet.network, self.walletId], function(walletId, next) {
self.storage.fetchNotifications(walletId, opts.notificationId, opts.minTs || 0, next);
}, function(err, res) {
if (err) return cb(err);
var notifications = _.sortBy(_.map(_.flatten(res), function(n) {
n.walletId = self.walletId;
return n;
}), 'id');
return cb(null, notifications);
});
});
};

View File

@ -277,44 +277,6 @@ Storage.prototype.fetchTxs = function(walletId, opts, cb) {
};
/**
* fetchNotifications
*
* @param walletId
* @param opts.minTs
* @param opts.maxTs
* @param opts.limit
* @param opts.reverse
*/
Storage.prototype.fetchNotifications = function(walletId, opts, cb) {
var self = this;
opts = opts || {};
var tsFilter = {};
if (_.isNumber(opts.minTs)) tsFilter.$gte = opts.minTs;
if (_.isNumber(opts.maxTs)) tsFilter.$lte = opts.maxTs;
var filter = {
walletId: walletId
};
if (!_.isEmpty(tsFilter)) filter.createdOn = tsFilter;
var mods = {};
if (_.isNumber(opts.limit)) mods.limit = opts.limit;
this.db.collection(collections.NOTIFICATIONS).find(filter, mods).sort({
id: opts.reverse ? -1 : 1,
}).toArray(function(err, result) {
if (err) return cb(err);
if (!result) return cb();
var notifications = _.map(result, function(notification) {
return Model.Notification.fromObj(notification);
});
return cb(null, notifications);
});
};
/**
* Retrieves notifications after a specific id or from a given ts (whichever is more recent).
*

View File

@ -2,7 +2,7 @@
"name": "bitcore-wallet-service",
"description": "A service for Mutisig HD Bitcoin Wallets",
"author": "BitPay Inc",
"version": "1.0.0",
"version": "1.1.0",
"keywords": [
"bitcoin",
"copay",

View File

@ -84,37 +84,85 @@ describe('ExpressApp', function() {
});
});
it('/v1/notifications', function(done) {
var clock = sinon.useFakeTimers(1234000, 'Date');
describe('/v1/notifications', function(done) {
var server, TestExpressApp, clock;
beforeEach(function() {
clock = sinon.useFakeTimers(1234000, 'Date');
var server = {
getNotifications: sinon.stub().callsArgWith(1, null, {})
};
var TestExpressApp = proxyquire('../lib/expressapp', {
'./server': {
initialize: sinon.stub().callsArg(1),
getInstanceWithAuth: sinon.stub().callsArgWith(1, null, server),
}
});
start(TestExpressApp, function() {
var requestOptions = {
url: testHost + ':' + testPort + config.basePath + '/v1/notifications' + '?notificationId=123&minTs=0',
headers: {
'x-identity': 'identity',
'x-signature': 'signature'
}
server = {
getNotifications: sinon.stub().callsArgWith(1, null, {})
};
request(requestOptions, function(err, res, body) {
should.not.exist(err);
res.statusCode.should.equal(200);
body.should.equal('{}');
server.getNotifications.calledWith({
notificationId: '123',
minTs: 1234000 - 60000, // override minTs argument with a hardcoded 60 seconds span
}).should.be.true;
TestExpressApp = proxyquire('../lib/expressapp', {
'./server': {
initialize: sinon.stub().callsArg(1),
getInstanceWithAuth: sinon.stub().callsArgWith(1, null, server),
}
});
});
afterEach(function() {
clock.restore();
});
clock.restore();
done();
it('should fetch notifications from a specified id', function(done) {
start(TestExpressApp, function() {
var requestOptions = {
url: testHost + ':' + testPort + config.basePath + '/v1/notifications' + '?notificationId=123',
headers: {
'x-identity': 'identity',
'x-signature': 'signature'
}
};
request(requestOptions, function(err, res, body) {
should.not.exist(err);
res.statusCode.should.equal(200);
body.should.equal('{}');
server.getNotifications.calledWith({
notificationId: '123',
minTs: +Date.now() - 60000,
}).should.be.true;
done();
});
});
});
it('should allow custom minTs within limits', function(done) {
start(TestExpressApp, function() {
var requestOptions = {
url: testHost + ':' + testPort + config.basePath + '/v1/notifications' + '?timeSpan=30',
headers: {
'x-identity': 'identity',
'x-signature': 'signature'
}
};
request(requestOptions, function(err, res, body) {
should.not.exist(err);
res.statusCode.should.equal(200);
server.getNotifications.calledWith({
notificationId: undefined,
minTs: +Date.now() - 30000,
}).should.be.true;
done();
});
});
});
it('should limit minTs to 60 seconds', function(done) {
start(TestExpressApp, function() {
var requestOptions = {
url: testHost + ':' + testPort + config.basePath + '/v1/notifications' + '?timeSpan=90',
headers: {
'x-identity': 'identity',
'x-signature': 'signature'
}
};
request(requestOptions, function(err, res, body) {
should.not.exist(err);
res.statusCode.should.equal(200);
body.should.equal('{}');
server.getNotifications.calledWith({
notificationId: undefined,
minTs: Date.now() - 60000, // override minTs argument with a hardcoded 60 seconds span
}).should.be.true;
done();
});
});
});
});

View File

@ -3923,6 +3923,38 @@ describe('Wallet service', function() {
});
});
it('should pull new block notifications along with wallet notifications in the last 60 seconds', function(done) {
// Simulate new block notification
server.walletId = 'livenet';
server._notify('NewBlock', {
hash: 'dummy hash',
}, {
isGlobal: true
}, function(err) {
should.not.exist(err);
server.walletId = 'testnet';
server._notify('NewBlock', {
hash: 'dummy hash',
}, {
isGlobal: true
}, function(err) {
should.not.exist(err);
server.walletId = wallet.id;
server.getNotifications({
minTs: +Date.now() - (60 * 1000),
}, function(err, notifications) {
should.not.exist(err);
var types = _.pluck(notifications, 'type');
types.should.deep.equal(['NewTxProposal', 'NewTxProposal', 'NewBlock']);
var walletIds = _.uniq(_.pluck(notifications, 'walletId'));
walletIds.length.should.equal(1);
walletIds[0].should.equal(wallet.id);
done();
});
});
});
});
it('should pull notifications in the last 60 seconds', function(done) {
server.getNotifications({
minTs: +Date.now() - (60 * 1000),
@ -4134,7 +4166,7 @@ describe('Wallet service', function() {
});
},
function(next) {
server.getNotifications({}, function(err, items) {
server.storage.fetchNotifications(wallet.id, null, 0, function(err, items) {
items.length.should.equal(0);
next();
});