From 510f0f09973d793f637c481c95bb0746f923f1fb Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Thu, 12 Mar 2015 17:16:13 -0300 Subject: [PATCH] add /addresses/:addresses/utxos --- api/controllers/addresses.js | 26 +++++++++++++++++-- api/routes/v1.js | 5 ++-- api/test/v1/addresses.js | 50 +++++++++++++++++++++++++++++++----- 3 files changed, 70 insertions(+), 11 deletions(-) diff --git a/api/controllers/addresses.js b/api/controllers/addresses.js index dbfbf047..7e58264f 100644 --- a/api/controllers/addresses.js +++ b/api/controllers/addresses.js @@ -32,6 +32,26 @@ Addresses.addressParam = function(req, res, next, address) { next(); }; +/* + * Parse address list + */ +Addresses.addressesParam = function(req, res, next, addresses) { + var addrList = addresses.split(','); + var allAddressesValid = _.every(addrList, function(addr) { + return Address.isValid(addr); + }); + + if (!allAddressesValid) { + res.status(422); + res.send('/v1/addresses/ parameter must be a bitcoin address list'); + return; + } + req.addresses = addrList.map(function (a) { + return new Address(a); + }); + next(); +}; + /* * controllers @@ -53,8 +73,10 @@ Addresses.get = function(req, res) { * Gets an address utxos */ Addresses.utxos = function(req, res) { - $.checkState(req.address instanceof Address); - node.getUTXOs(req.address) + $.checkState(_.all(req.addresses, function(addr) { + return addr instanceof Address; + })); + node.getUTXOs(req.addresses) .then(function(utxos) { res.send(utxos); }); diff --git a/api/routes/v1.js b/api/routes/v1.js index 10668b25..95680484 100644 --- a/api/routes/v1.js +++ b/api/routes/v1.js @@ -24,6 +24,7 @@ function initRouter(node) { router.param('height', Blocks.heightParam); router.param('txHash', Transactions.txHashParam); router.param('address', Addresses.addressParam); + router.param('addresses', Addresses.addressesParam); router.param('index', Transactions.indexParam); // Node routes @@ -50,9 +51,7 @@ function initRouter(node) { // Address routes router.get('/addresses/:address', Addresses.get); router.get('/addresses/:address/transactions', Transactions.list); - router.get('/addresses/:address/utxos', Addresses.utxos); - // TODO: check if this is really restful - router.get('/addresses/:addresses/utxos', mockResponse); + router.get('/addresses/:addresses/utxos', Addresses.utxos); // error routes router.get('/blocks/*', Blocks.getBlockError); diff --git a/api/test/v1/addresses.js b/api/test/v1/addresses.js index 193ffaea..256bcbcd 100644 --- a/api/test/v1/addresses.js +++ b/api/test/v1/addresses.js @@ -24,8 +24,29 @@ describe('BitcoreHTTP v1 addresses routes', function() { var amount = mockAddresses[addr].summary.transactions.length; return transactionList.slice(0, amount); }; - var utxos_for_addr = function(addr) { - return mockAddresses[addr].utxos; + var utxos_for_addrs = function(addrs) { + return _.reduce(addrs, function(utxos, addr) { + return utxos.concat(mockAddresses[addr].utxos); + }, []); + }; + + var powerset = function(set) { + if (set.length === 0) { + return [ + [] + ]; + } + var sets = []; + var head = set.shift(); + var tail = set; + powerset(tail).forEach(function(s) { + var copy = s.slice(); + copy.push(head); + + sets.push(copy); + sets.push(s); + }); + return sets; }; beforeEach(function() { @@ -36,8 +57,8 @@ describe('BitcoreHTTP v1 addresses routes', function() { nodeMock.listTransactions = function(opts) { return Promise.resolve(txs_for_addr(opts.address)); }; - nodeMock.getUTXOs = function(address) { - return Promise.resolve(utxos_for_addr(address)); + nodeMock.getUTXOs = function(addresses) { + return Promise.resolve(utxos_for_addrs(addresses)); }; app = new BitcoreHTTP(nodeMock).app; agent = request(app); @@ -76,13 +97,30 @@ describe('BitcoreHTTP v1 addresses routes', function() { }); describe('/addresses/:address/utxos', function() { it('fails with invalid address', function(cb) { - failsWithInvalidAddress(agent, '/v1/addresses/1BpbpfLdY7oBS9gK7aDXgvMgr1DpvNH3B2/utxos', cb); + agent.get('/v1/addresses/1BpbpfLdY7oBS9gK7aDXgvMgr1DpvNH3B2/utxos') + .expect(422) + .expect('/v1/addresses/ parameter must be a bitcoin address list', cb); + }); Object.keys(mockAddresses).forEach(function(addr) { it('works with valid address ' + addr, function(cb) { agent.get('/v1/addresses/' + addr + '/utxos') .expect(200) - .expect(JSON.stringify(utxos_for_addr(addr)), cb); + .expect(JSON.stringify(utxos_for_addrs([addr])), cb); + }); + }); + }); + describe('/addresses/:addresses/utxos', function() { + powerset(Object.keys(mockAddresses)).forEach(function(addresses) { + if (addresses.length === 0) { + return; + } + var list = addresses.join(','); + it('works with valid addresses ' + list, function(cb) { + var path = '/v1/addresses/' + list + '/utxos'; + agent.get(path) + .expect(200) + .expect(JSON.stringify(utxos_for_addrs(addresses)), cb); }); }); });