add fiat rate service

This commit is contained in:
Ivan Socolsky 2016-01-07 12:29:47 -03:00
parent 86c36eba84
commit 450da4ecdc
7 changed files with 272 additions and 1 deletions

View File

@ -5,7 +5,6 @@ var async = require('async');
var log = require('npmlog');
var express = require('express');
var querystring = require('querystring');
var bodyParser = require('body-parser')
var WalletService = require('./server');
@ -501,6 +500,26 @@ ExpressApp.prototype.start = function(opts, cb) {
});
});
router.get('/v1/fiatrates/:code/', function(req, res) {
var opts = {
code: req.params['code'],
source: req.query.source,
ts: req.query.ts,
}
// if (_.isString(ts) && ts.indexOf(',') !== -1) {
// ts = ts.split(',');
// }
server.getFiatRate(opts, function(err, rates) {
if (err) returnError({
code: 500,
message: err,
});
res.json(rates);
res.end();
});
});
this.app.use(opts.basePath || '/bws/api', router);
WalletService.initialize(opts, cb);

View File

@ -0,0 +1,17 @@
var _ = require('lodash');
var provider = {
url: 'https://bitpay.com/api/rates/',
parseFn: function(raw) {
var rates = _.compact(_.map(raw, function(d) {
if (!d.code || !d.rate) return null;
return {
code: d.code,
value: d.rate,
};
}));
return rates;
},
};
module.exports = provider;

View File

@ -0,0 +1,11 @@
var provider = {
url: 'https://www.bitstamp.net/api/ticker/',
parseFn: function(raw) {
return [{
code: 'USD',
value: parseFloat(raw.last)
}];
}
};
module.exports = provider;

View File

@ -0,0 +1,6 @@
var Providers = {
BitPay: require('./bitpay'),
Bitstamp: require('./bitstamp'),
}
module.exports = Providers;

132
lib/fiatrateservice.js Normal file
View File

@ -0,0 +1,132 @@
'use strict';
var _ = require('lodash');
var $ = require('preconditions').singleton();
var async = require('async');
var log = require('npmlog');
log.debug = log.verbose;
var request = require('request');
var Utils = require('./common/utils');
var Storage = require('./storage');
var Model = require('./model');
var DEFAULT_PROVIDER = 'BitPay';
var FETCH_INTERVAL = 15; // In minutes
function FiatRateService() {};
FiatRateService.prototype.start = function(opts, cb) {
var self = this;
opts = opts || {};
if (_.isArray(opts.providers)) {
self.providers = opts.providers;
} else {
self.providers = require('./fiatrateproviders');
}
self.request = opts.request || request;
self.defaultProvider = opts.defaultProvider || DEFAULT_PROVIDER;
async.parallel([
function(done) {
if (opts.storage) {
self.storage = opts.storage;
done();
} else {
self.storage = new Storage();
self.storage.connect(opts.storageOpts, done);
}
},
], function(err) {
if (err) {
log.error(err);
return cb(err);
}
var interval = opts.fetchInterval || FETCH_INTERVAL;
if (interval) {
self._fetch();
setInterval(function() {
self._fetch();
}, interval * 60 * 1000);
}
return cb();
});
};
FiatRateService.prototype._fetch = function(cb) {
var self = this;
cb = cb || function() {};
async.each(_.values(self.providers), function(provider, next) {
self._retrieve(provider, function(err, res) {
if (err) {
log.warn(err);
return next();
}
self.storage.storeFiatRate(provider.name, res, function(err) {
return next();
});
});
}, cb);
};
FiatRateService.prototype._retrieve = function(provider, cb) {
var self = this;
log.debug('Fetching data for ' + provider.name);
self.request.get({
url: provider.url,
json: true,
}, function(err, res, body) {
if (err || !body) {
log.warn('Error fetching data for ' + provider.name, err);
return cb(err);
}
log.debug('Data for ' + provider.name + ' fetched successfully');
if (!provider.parseFn) {
return cb('No parse function for provider ' + provider.name);
}
var rates = provider.parseFn(body);
return cb(null, rates);
});
};
FiatRateService.prototype.getRate = function(code, opts, cb) {
var self = this;
$.shouldBeFunction(cb);
opts = opts || {};
var providerName = opts.providerName || DEFAULT_PROVIDER;
var ts = opts.ts || Date.now();
async.map([].concat(ts), function(ts, cb) {
self.storage.fetchFiatRate(providerName, code, ts, function(err, rate) {
if (err) return cb(err);
return cb(null, {
ts: +ts,
rate: rate
});
});
}, function(err, res) {
if (err) return cb(err);
if (!_.isArray(ts)) res = res[0];
return cb(null, res);
});
};
module.exports = FiatRateService;

View File

@ -21,6 +21,7 @@ var collections = {
PREFERENCES: 'preferences',
EMAIL_QUEUE: 'email_queue',
CACHE: 'cache',
FIAT_RATES: 'fiat_rates',
};
var Storage = function(opts) {
@ -572,6 +573,39 @@ Storage.prototype.fetchActiveAddresses = function(walletId, cb) {
});
};
Storage.prototype.storeFiatRate = function(providerName, rates, cb) {
var self = this;
var now = Date.now();
async.each(rates, function(rate, next) {
self.db.collection(collections.FIAT_RATES).insert({
provider: providerName,
ts: now,
code: rate.code,
value: rate.value,
}, {
w: 1
}, next);
}, cb);
};
Storage.prototype.fetchFiatRate = function(providerName, code, ts, cb) {
var self = this;
self.db.collection(collections.FIAT_RATES).find({
provider: providerName,
code: code,
ts: {
$lte: ts
},
}).sort({
ts: -1
}).limit(1).toArray(function(err, result) {
if (err || _.isEmpty(result)) return cb(err);
return cb(null, result[0]);
});
};
Storage.prototype._dump = function(cb, fn) {
fn = fn || console.log;
cb = cb || function() {};

View File

@ -0,0 +1,52 @@
'use strict';
var _ = require('lodash');
var async = require('async');
var chai = require('chai');
var sinon = require('sinon');
var should = chai.should();
var log = require('npmlog');
log.debug = log.verbose;
log.level = 'info';
var helpers = require('./helpers');
var FiatRateService = require('../../lib/fiatrateservice');
describe('Fiat rate service', function() {
var service, request;
before(function(done) {
helpers.before(done);
});
after(function(done) {
helpers.after(done);
});
beforeEach(function(done) {
helpers.beforeEach(function() {
service = new FiatRateService();
request = sinon.stub();
request.get = sinon.stub();
service.start({
storage: helpers.getStorage(),
request: request,
}, done);
});
});
describe.only('#getRate', function() {
it('should get current rate', function(done) {
service.storage.storeFiatRate('BitPay', [{
code: 'USD',
value: 123.45,
}], function(err) {
should.not.exist(err);
service.getRate('USD', {}, function(err, res) {
should.not.exist(err);
res.rate.value.should.equal(123.45);
done();
});
});
});
});
});