implement GET /blocks endpoint

This commit is contained in:
diminator 2017-01-10 14:41:02 +01:00 committed by Brett Sun
parent b12ea854f7
commit 1b57ace714
3 changed files with 169 additions and 0 deletions

View File

@ -1,6 +1,7 @@
""" API routes definition """
from flask_restful import Api
from bigchaindb.web.views import (
blocks,
info,
statuses,
transactions as tx,
@ -23,6 +24,8 @@ def r(*args, **kwargs):
ROUTES_API_V1 = [
r('/', info.ApiV1Index),
r('blocks/<string:block_id>', blocks.BlockApi),
r('blocks/', blocks.BlockListApi),
r('statuses/', statuses.StatusApi),
r('transactions/<string:tx_id>', tx.TransactionApi),
r('transactions', tx.TransactionListApi),

View File

@ -0,0 +1,62 @@
"""This module provides the blueprint for the blocks API endpoints.
For more information please refer to the documentation on ReadTheDocs:
- https://docs.bigchaindb.com/projects/server/en/latest/drivers-clients/
http-client-server-api.html
"""
from flask import current_app
from flask_restful import Resource, reqparse
from bigchaindb.web.views.base import make_error
class BlockApi(Resource):
def get(self, block_id):
"""API endpoint to get details about a block.
Args:
block_id (str): the id of the block.
Return:
A JSON string containing the data about the block.
"""
pool = current_app.config['bigchain_pool']
with pool() as bigchain:
block = bigchain.get_block(block_id=block_id)
if not block:
return make_error(404)
return block
class BlockListApi(Resource):
def get(self):
"""API endpoint to get the related blocks and statuses of a transaction.
Return:
A ``dict`` in the format ``{'block_id': <status>}``, where
``<status>`` is one of "valid", "invalid", "undecided", "backlog".
It's possible to return multiple keys of 'block_id'.
"""
parser = reqparse.RequestParser()
parser.add_argument('tx_id', type=str)
args = parser.parse_args(strict=True)
if sum(arg is not None for arg in args.values()) != 1:
return make_error(400, "Provide exactly one query parameter. Choices are: block_id, tx_id")
pool = current_app.config['bigchain_pool']
blocks = None
with pool() as bigchain:
if args['tx_id']:
blocks = bigchain.get_blocks_status_containing_tx(args['tx_id'])
if not blocks:
return make_error(404)
return blocks

104
tests/web/test_blocks.py Normal file
View File

@ -0,0 +1,104 @@
import pytest
from bigchaindb.models import Transaction
BLOCKS_ENDPOINT = '/api/v1/blocks/'
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_get_block_endpoint(b, client):
tx = Transaction.create([b.me], [([b.me], 1)])
tx = tx.sign([b.me_private])
block = b.create_block([tx])
b.write_block(block)
res = client.get(BLOCKS_ENDPOINT + block.id)
assert block.to_dict() == res.json
assert res.status_code == 200
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_get_block_returns_404_if_not_found(client):
res = client.get(BLOCKS_ENDPOINT + '123')
assert res.status_code == 404
res = client.get(BLOCKS_ENDPOINT + '123/')
assert res.status_code == 404
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_get_blocks_by_txid_endpoint(b, client):
tx = Transaction.create([b.me], [([b.me], 1)])
tx = tx.sign([b.me_private])
tx2 = Transaction.create([b.me], [([b.me], 10)])
tx2 = tx2.sign([b.me_private])
block_invalid = b.create_block([tx])
b.write_block(block_invalid)
res = client.get(BLOCKS_ENDPOINT + "?tx_id=" + tx.id)
# test if block is retrieved as undecided
assert res.status_code == 200
assert block_invalid.id in res.json
assert res.json[block_invalid.id] == b.block_election_status(block_invalid.id, block_invalid.voters)
assert res.json[block_invalid.id] == 'undecided'
assert len(res.json) == 1
# vote the block invalid
vote = b.vote(block_invalid.id, b.get_last_voted_block().id, False)
b.write_vote(vote)
res = client.get(BLOCKS_ENDPOINT + "?tx_id=" + tx.id)
# test if block is retrieved as invalid
assert res.status_code == 200
assert block_invalid.id in res.json
assert res.json[block_invalid.id] == b.block_election_status(block_invalid.id, block_invalid.voters)
assert res.json[block_invalid.id] == 'invalid'
assert len(res.json) == 1
# create a new block containing the same tx (and tx2 to avoid block id collision)
block_valid = b.create_block([tx, tx2])
b.write_block(block_valid)
res = client.get(BLOCKS_ENDPOINT + "?tx_id=" + tx.id)
# test if block is retrieved as undecided
assert res.status_code == 200
assert block_valid.id in res.json
assert res.json[block_valid.id] == b.block_election_status(block_valid.id, block_valid.voters)
assert res.json[block_valid.id] == 'undecided'
assert len(res.json) == 2
# vote the block valid
vote = b.vote(block_valid.id, block_invalid.id, True)
b.write_vote(vote)
res = client.get(BLOCKS_ENDPOINT + "?tx_id=" + tx.id)
# test if block is retrieved as valid
assert res.status_code == 200
assert block_valid.id in res.json
assert res.json[block_valid.id] == b.block_election_status(block_valid.id, block_valid.voters)
assert res.json[block_valid.id] == 'valid'
assert len(res.json) == 2
@pytest.mark.bdb
def test_get_blocks_by_txid_endpoint_returns_404_if_not_found(client):
res = client.get(BLOCKS_ENDPOINT + "?tx_id=123")
assert res.status_code == 404
@pytest.mark.bdb
def test_get_blocks_by_txid_endpoint_returns_400_bad_query_params(client):
res = client.get(BLOCKS_ENDPOINT)
assert res.status_code == 400
res = client.get(BLOCKS_ENDPOINT + "?ts_id=123")
assert res.status_code == 400
res = client.get(BLOCKS_ENDPOINT + "?tx_id=123&block_id=123")
assert res.status_code == 400