add fiat rate service
This commit is contained in:
parent
86c36eba84
commit
450da4ecdc
|
@ -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);
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -0,0 +1,6 @@
|
|||
var Providers = {
|
||||
BitPay: require('./bitpay'),
|
||||
Bitstamp: require('./bitstamp'),
|
||||
}
|
||||
|
||||
module.exports = Providers;
|
|
@ -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;
|
|
@ -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() {};
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue