Merge pull request #386 from isocolsky/ref/notifications
Ref/notifications
This commit is contained in:
commit
a0f99f6660
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,10 +1660,22 @@ 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);
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
WalletService.prototype._normalizeTxHistory = function(txs) {
|
||||
|
|
|
@ -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).
|
||||
*
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -84,21 +84,29 @@ 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 = {
|
||||
server = {
|
||||
getNotifications: sinon.stub().callsArgWith(1, null, {})
|
||||
};
|
||||
var TestExpressApp = proxyquire('../lib/expressapp', {
|
||||
TestExpressApp = proxyquire('../lib/expressapp', {
|
||||
'./server': {
|
||||
initialize: sinon.stub().callsArg(1),
|
||||
getInstanceWithAuth: sinon.stub().callsArgWith(1, null, server),
|
||||
}
|
||||
});
|
||||
});
|
||||
afterEach(function() {
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('should fetch notifications from a specified id', function(done) {
|
||||
start(TestExpressApp, function() {
|
||||
var requestOptions = {
|
||||
url: testHost + ':' + testPort + config.basePath + '/v1/notifications' + '?notificationId=123&minTs=0',
|
||||
url: testHost + ':' + testPort + config.basePath + '/v1/notifications' + '?notificationId=123',
|
||||
headers: {
|
||||
'x-identity': 'identity',
|
||||
'x-signature': 'signature'
|
||||
|
@ -110,14 +118,54 @@ describe('ExpressApp', function() {
|
|||
body.should.equal('{}');
|
||||
server.getNotifications.calledWith({
|
||||
notificationId: '123',
|
||||
minTs: 1234000 - 60000, // override minTs argument with a hardcoded 60 seconds span
|
||||
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;
|
||||
|
||||
clock.restore();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue