Ivan Socolsky
9 years ago
7 changed files with 272 additions and 1 deletions
@ -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; |
@ -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