mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Merge pull request #1031 from bigchaindb/feat/api/blocks
Implement blocks endpoint
This commit is contained in:
commit
b59ea00118
@ -1,6 +1,7 @@
|
||||
""" API routes definition """
|
||||
from flask_restful import Api
|
||||
from bigchaindb.web.views import (
|
||||
blocks,
|
||||
info,
|
||||
statuses,
|
||||
transactions as tx,
|
||||
@ -24,6 +25,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),
|
||||
|
61
bigchaindb/web/views/blocks.py
Normal file
61
bigchaindb/web/views/blocks.py
Normal file
@ -0,0 +1,61 @@
|
||||
"""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 import Bigchain
|
||||
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 for a transaction.
|
||||
|
||||
Return:
|
||||
A ``list`` of ``block_id``s that contain the given transaction. The
|
||||
list may be filtered when provided a status query parameter:
|
||||
"valid", "invalid", "undecided".
|
||||
"""
|
||||
parser = reqparse.RequestParser()
|
||||
parser.add_argument('tx_id', type=str, required=True)
|
||||
parser.add_argument('status', type=str,
|
||||
choices=[Bigchain.BLOCK_VALID, Bigchain.BLOCK_INVALID, Bigchain.BLOCK_UNDECIDED])
|
||||
|
||||
args = parser.parse_args(strict=True)
|
||||
tx_id = args['tx_id']
|
||||
status = args['status']
|
||||
|
||||
pool = current_app.config['bigchain_pool']
|
||||
|
||||
with pool() as bigchain:
|
||||
block_statuses = bigchain.get_blocks_status_containing_tx(tx_id)
|
||||
blocks = [block_id for block_id, block_status in block_statuses.items()
|
||||
if not status or block_status == status]
|
||||
|
||||
return blocks
|
178
tests/web/test_blocks.py
Normal file
178
tests/web/test_blocks.py
Normal file
@ -0,0 +1,178 @@
|
||||
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 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 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 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 len(res.json) == 2
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
@pytest.mark.usefixtures('inputs')
|
||||
def test_get_blocks_by_txid_and_status_endpoint(b, client):
|
||||
from bigchaindb import Bigchain
|
||||
|
||||
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)
|
||||
|
||||
# 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("{}?tx_id={}&status={}".format(BLOCKS_ENDPOINT, tx.id, Bigchain.BLOCK_INVALID))
|
||||
# test if no blocks are retrieved as invalid
|
||||
assert res.status_code == 200
|
||||
assert len(res.json) == 0
|
||||
|
||||
res = client.get("{}?tx_id={}&status={}".format(BLOCKS_ENDPOINT, tx.id, Bigchain.BLOCK_UNDECIDED))
|
||||
# test if both blocks are retrieved as undecided
|
||||
assert res.status_code == 200
|
||||
assert block_valid.id in res.json
|
||||
assert block_invalid.id in res.json
|
||||
assert len(res.json) == 2
|
||||
|
||||
res = client.get("{}?tx_id={}&status={}".format(BLOCKS_ENDPOINT, tx.id, Bigchain.BLOCK_VALID))
|
||||
# test if no blocks are retrieved as valid
|
||||
assert res.status_code == 200
|
||||
assert len(res.json) == 0
|
||||
|
||||
# vote one of the blocks invalid
|
||||
vote = b.vote(block_invalid.id, b.get_last_voted_block().id, False)
|
||||
b.write_vote(vote)
|
||||
|
||||
# vote the other block valid
|
||||
vote = b.vote(block_valid.id, block_invalid.id, True)
|
||||
b.write_vote(vote)
|
||||
|
||||
res = client.get("{}?tx_id={}&status={}".format(BLOCKS_ENDPOINT, tx.id, Bigchain.BLOCK_INVALID))
|
||||
# test if the invalid block is retrieved as invalid
|
||||
assert res.status_code == 200
|
||||
assert block_invalid.id in res.json
|
||||
assert len(res.json) == 1
|
||||
|
||||
res = client.get("{}?tx_id={}&status={}".format(BLOCKS_ENDPOINT, tx.id, Bigchain.BLOCK_UNDECIDED))
|
||||
# test if no blocks are retrieved as undecided
|
||||
assert res.status_code == 200
|
||||
assert len(res.json) == 0
|
||||
|
||||
res = client.get("{}?tx_id={}&status={}".format(BLOCKS_ENDPOINT, tx.id, Bigchain.BLOCK_VALID))
|
||||
# test if the valid block is retrieved as valid
|
||||
assert res.status_code == 200
|
||||
assert block_valid.id in res.json
|
||||
assert len(res.json) == 1
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_get_blocks_by_txid_endpoint_returns_empty_list_not_found(client):
|
||||
res = client.get(BLOCKS_ENDPOINT + "?tx_id=")
|
||||
assert res.status_code == 200
|
||||
assert len(res.json) == 0
|
||||
|
||||
res = client.get(BLOCKS_ENDPOINT + "?tx_id=123")
|
||||
assert res.status_code == 200
|
||||
assert len(res.json) == 0
|
||||
|
||||
|
||||
@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
|
||||
assert res.json == {
|
||||
'message': {
|
||||
'tx_id': 'Missing required parameter in the JSON body or the post body or the query string'
|
||||
}
|
||||
}
|
||||
|
||||
res = client.get(BLOCKS_ENDPOINT + "?tx_id=123&foo=123")
|
||||
assert res.status_code == 400
|
||||
assert res.json == {
|
||||
'message': 'Unknown arguments: foo'
|
||||
}
|
||||
|
||||
res = client.get(BLOCKS_ENDPOINT + "?tx_id=123&status=123")
|
||||
assert res.status_code == 400
|
||||
assert res.json == {
|
||||
'message': {
|
||||
'status': '123 is not a valid choice'
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user