diff --git a/api/controllers/blocks.js b/api/controllers/blocks.js index e51266f3..12fa9972 100644 --- a/api/controllers/blocks.js +++ b/api/controllers/blocks.js @@ -20,6 +20,10 @@ Blocks.setNode = function(aNode) { /* * params */ + +/* + * Finds a block by its hash + */ Blocks.blockHashParam = function(req, res, next, blockHash) { // TODO: fetch block from service var block = mockBlocks[blockHash]; @@ -32,6 +36,9 @@ Blocks.blockHashParam = function(req, res, next, blockHash) { next(); }; +/* + * Finds a block by its height + */ Blocks.heightParam = function(req, res, next, height) { // TODO: fetch block from service height = parseInt(height); @@ -45,6 +52,7 @@ Blocks.heightParam = function(req, res, next, height) { next(); }; + /* * controllers */ diff --git a/api/controllers/transactions.js b/api/controllers/transactions.js new file mode 100644 index 00000000..d65b87ae --- /dev/null +++ b/api/controllers/transactions.js @@ -0,0 +1,51 @@ +'use strict'; + +var bitcore = require('bitcore'); +var _ = bitcore.deps._; +var $ = bitcore.util.preconditions; +var Transaction = bitcore.Transaction; + +var mockTransactions = require('../test/data/transactions'); + +var Transactions = {}; + +var node; +Transactions.setNode = function(aNode) { + node = aNode; +}; + + +/* + * params + */ + +/* + * Finds a transaction by its hash + */ +Transactions.txHashParam = function(req, res, next, txHash) { + // TODO: fetch tx from service + var tx = mockTransactions[txHash]; + + if (_.isUndefined(tx)) { + res.status(404).send('Transaction with id ' + txHash + ' not found'); + return; + } + req.tx = tx; + next(); +}; + + +/* + * controllers + */ + +Transactions.get = function(req, res) { + $.checkState(req.tx instanceof Transaction); + res.send(req.tx.toObject()); +}; +Transactions.getTxError = function(req, res) { + res.status(422); + res.send('/v1/transactions/ parameter must be a 64 digit hex'); +}; + +module.exports = Transactions; diff --git a/api/routes/v1.js b/api/routes/v1.js index 7fabad25..79aa649f 100644 --- a/api/routes/v1.js +++ b/api/routes/v1.js @@ -1,14 +1,15 @@ 'use strict'; var express = require('express'); -var Blocks = require('../controllers/blocks'); var NodeStatus = require('../controllers/node'); +var Blocks = require('../controllers/blocks'); +var Transactions = require('../controllers/transactions'); function initRouter(node) { var router = express.Router(); - [NodeStatus, Blocks].forEach(function(controller) { + [NodeStatus, Blocks, Transactions].forEach(function(controller) { controller.setNode(node); }); @@ -21,6 +22,7 @@ function initRouter(node) { // parameter middleware router.param('blockHash', Blocks.blockHashParam); router.param('height', Blocks.heightParam); + router.param('txHash', Transactions.txHashParam); // Node routes router.get('/node', NodeStatus.getStatus); @@ -30,15 +32,13 @@ function initRouter(node) { router.get('/blocks/latest', Blocks.getLatest); router.get('/blocks/:blockHash([A-Fa-f0-9]{64})', Blocks.get); router.get('/blocks/:height([0-9]+)', Blocks.get); - router.get('/blocks/*', Blocks.getBlockError); - router.get('/blocks/:blockHash/transactions/:txIndex', mockResponse); // Transaction routes router.get('/transactions', mockResponse); - router.get('/transactions/:txHash', mockResponse); - router.get('/transactions/:txHash/addresses', mockResponse); - router.get('/transactions/:txHash/outputs/addresses', mockResponse); - router.get('/transactions/:txHash/inputs/addresses', mockResponse); + router.get('/transactions/:txHash([A-Fa-f0-9]{64})', Transactions.get); + router.get('/transactions/:txHash([A-Fa-f0-9]{64})/addresses', mockResponse); + router.get('/transactions/:txHash([A-Fa-f0-9]{64})/outputs/addresses', mockResponse); + router.get('/transactions/:txHash([A-Fa-f0-9]{64})/inputs/addresses', mockResponse); router.post('/transactions/send', mockResponse); // Input routes @@ -56,6 +56,10 @@ function initRouter(node) { // TODO: check if this is really restful router.get('/addresses/:addresses/utxos', mockResponse); + // error routes + router.get('/blocks/*', Blocks.getBlockError); + router.get('/transactions/*', Transactions.getTxError); + return router; } diff --git a/api/test/data/transactions.js b/api/test/data/transactions.js new file mode 100644 index 00000000..4317a081 --- /dev/null +++ b/api/test/data/transactions.js @@ -0,0 +1,17 @@ +'use strict'; + +var bitcore = require('bitcore'); +var Block = bitcore.Block; + +var mockTransactions = {}; +var blockHexs = require('./blocks.json'); +blockHexs.map(function(hex) { + var block = new Block(new Buffer(hex, 'hex')); + return block; +}).forEach(function(block) { + block.transactions.forEach(function(tx) { + mockTransactions[tx.id] = tx; + }); +}); + +module.exports = mockTransactions; diff --git a/api/test/v1/blocks.js b/api/test/v1/blocks.js index 0aeab00c..7aa69cba 100644 --- a/api/test/v1/blocks.js +++ b/api/test/v1/blocks.js @@ -48,10 +48,13 @@ describe('BitcoreHTTP v1 blocks routes', function() { .expect(404) .expect('Block with id 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b600000000 not found', cb); }); - it('works with valid blockHash', function(cb) { - agent.get('/v1/blocks/' + b1.hash) - .expect(200) - .expect(b1.toJSON(), cb); + Object.keys(mockBlocks).forEach(function(hash) { + var block = mockBlocks[hash]; + it('works with valid blockHash ...' + hash.substring(hash.length - 8), function(cb) { + agent.get('/v1/blocks/' + hash) + .expect(200) + .expect(block.toJSON(), cb); + }); }); }); describe('/blocks/:height', function() { diff --git a/api/test/v1/transactions.js b/api/test/v1/transactions.js new file mode 100644 index 00000000..e3c9dbf1 --- /dev/null +++ b/api/test/v1/transactions.js @@ -0,0 +1,51 @@ +'use strict'; + +var chai = require('chai'); +var should = chai.should(); +var request = require('supertest'); + +var EventEmitter = require('eventemitter2').EventEmitter2; + +var BitcoreHTTP = require('../../lib/http'); +var mockTransactions = require('../data/transactions'); + +describe('BitcoreHTTP v1 transactions routes', function() { + + // mocks + var nodeMock, app, agent; + beforeEach(function() { + nodeMock = new EventEmitter(); + app = new BitcoreHTTP(nodeMock).app; + agent = request(app); + }); + + describe('/transactions', function() { + it('works with default parameters', function(cb) { + agent.get('/v1/transactions/') + .expect(200) + .expect({ + 'message': 'This is a mocked response' + }, cb); + }); + }); + describe('/transactions/:txHash', function() { + it('fails with invalid txHash', function(cb) { + agent.get('/v1/transactions/abad1dea') + .expect(422) + .expect('/v1/transactions/ parameter must be a 64 digit hex', cb); + }); + it('returns 404 with non existent transaction', function(cb) { + agent.get('/v1/transactions/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b600000000') + .expect(404) + .expect('Transaction with id 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b600000000 not found', cb); + }); + Object.keys(mockTransactions).forEach(function(hash) { + it('works with valid txHash ...' + hash.substring(hash.length - 8), function(cb) { + agent.get('/v1/transactions/' + hash) + .expect(200) + .expect(mockTransactions[hash].toJSON(), cb); + }); + }); + }); + +});