mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
* split up views per resource add error cases to get/post transaction update docs/test * remove apiary from docstring * PR review docs
This commit is contained in:
parent
7b767affc9
commit
87a57bae33
@ -11,7 +11,9 @@ import gunicorn.app.base
|
||||
|
||||
from bigchaindb import util
|
||||
from bigchaindb import Bigchain
|
||||
from bigchaindb.web import views
|
||||
from bigchaindb.web.views.info import info_views
|
||||
from bigchaindb.web.views.transactions import transaction_views
|
||||
|
||||
from bigchaindb.monitor import Monitor
|
||||
|
||||
|
||||
@ -62,8 +64,8 @@ def create_app(settings):
|
||||
app.config['bigchain_pool'] = util.pool(Bigchain, size=settings.get('threads', 4))
|
||||
app.config['monitor'] = Monitor()
|
||||
|
||||
app.register_blueprint(views.info_views, url_prefix='/')
|
||||
app.register_blueprint(views.basic_views, url_prefix='/api/v1')
|
||||
app.register_blueprint(info_views, url_prefix='/')
|
||||
app.register_blueprint(transaction_views, url_prefix='/api/v1')
|
||||
return app
|
||||
|
||||
|
||||
|
0
bigchaindb/web/views/__init__.py
Normal file
0
bigchaindb/web/views/__init__.py
Normal file
15
bigchaindb/web/views/base.py
Normal file
15
bigchaindb/web/views/base.py
Normal file
@ -0,0 +1,15 @@
|
||||
from flask import jsonify
|
||||
|
||||
|
||||
def make_error(status_code, message=None):
|
||||
|
||||
if status_code == 404 and message is None:
|
||||
message = 'Not found'
|
||||
|
||||
response = jsonify({
|
||||
'status': status_code,
|
||||
'message': message
|
||||
})
|
||||
response.status_code = status_code
|
||||
return response
|
||||
|
26
bigchaindb/web/views/info.py
Normal file
26
bigchaindb/web/views/info.py
Normal file
@ -0,0 +1,26 @@
|
||||
"""This module provides the blueprint for some basic API endpoints.
|
||||
|
||||
For more information please refer to the documentation on ReadTheDocs:
|
||||
- https://bigchaindb.readthedocs.io/en/latest/drivers-clients/http-client-server-api.html
|
||||
"""
|
||||
|
||||
import flask
|
||||
from flask import Blueprint
|
||||
|
||||
import bigchaindb
|
||||
from bigchaindb import version
|
||||
|
||||
|
||||
info_views = Blueprint('info_views', __name__)
|
||||
|
||||
|
||||
@info_views.route('/')
|
||||
def home():
|
||||
return flask.jsonify({
|
||||
'software': 'BigchainDB',
|
||||
'version': version.__version__,
|
||||
'public_key': bigchaindb.config['keypair']['public'],
|
||||
'keyring': bigchaindb.config['keyring'],
|
||||
'api_endpoint': bigchaindb.config['api_endpoint']
|
||||
})
|
||||
|
@ -1,24 +1,23 @@
|
||||
"""This module provides the blueprint for some basic API endpoints.
|
||||
|
||||
For more information please refer to the documentation in Apiary:
|
||||
- http://docs.bigchaindb.apiary.io/
|
||||
For more information please refer to the documentation on ReadTheDocs:
|
||||
- https://bigchaindb.readthedocs.io/en/latest/drivers-clients/http-client-server-api.html
|
||||
"""
|
||||
|
||||
import flask
|
||||
from flask import abort, current_app, request, Blueprint
|
||||
from flask import current_app, request, Blueprint
|
||||
|
||||
import bigchaindb
|
||||
from bigchaindb import util, version
|
||||
from bigchaindb import util
|
||||
from bigchaindb.web.views.base import make_error
|
||||
|
||||
|
||||
info_views = Blueprint('info_views', __name__)
|
||||
basic_views = Blueprint('basic_views', __name__)
|
||||
transaction_views = Blueprint('transaction_views', __name__)
|
||||
|
||||
|
||||
# Unfortunately I cannot find a reference to this decorator.
|
||||
# This answer on SO is quite useful tho:
|
||||
# - http://stackoverflow.com/a/13432373/597097
|
||||
@basic_views.record
|
||||
@transaction_views.record
|
||||
def record(state):
|
||||
"""This function checks if the blueprint can be initialized
|
||||
with the provided state."""
|
||||
@ -35,18 +34,8 @@ def record(state):
|
||||
'a monitor instance to record system '
|
||||
'performance.')
|
||||
|
||||
@info_views.route('/')
|
||||
def home():
|
||||
return flask.jsonify({
|
||||
'software': 'BigchainDB',
|
||||
'version': version.__version__,
|
||||
'public_key': bigchaindb.config['keypair']['public'],
|
||||
'keyring': bigchaindb.config['keyring'],
|
||||
'api_endpoint': bigchaindb.config['api_endpoint']
|
||||
})
|
||||
|
||||
|
||||
@basic_views.route('/transactions/<tx_id>')
|
||||
@transaction_views.route('/transactions/<tx_id>')
|
||||
def get_transaction(tx_id):
|
||||
"""API endpoint to get details about a transaction.
|
||||
|
||||
@ -63,12 +52,12 @@ def get_transaction(tx_id):
|
||||
tx = bigchain.get_transaction(tx_id)
|
||||
|
||||
if not tx:
|
||||
abort(404)
|
||||
return make_error(404)
|
||||
|
||||
return flask.jsonify(**tx)
|
||||
|
||||
|
||||
@basic_views.route('/transactions/', methods=['POST'])
|
||||
@transaction_views.route('/transactions/', methods=['POST'])
|
||||
def create_transaction():
|
||||
"""API endpoint to push transactions to the Federation.
|
||||
|
||||
@ -78,8 +67,6 @@ def create_transaction():
|
||||
pool = current_app.config['bigchain_pool']
|
||||
monitor = current_app.config['monitor']
|
||||
|
||||
val = {}
|
||||
|
||||
# `force` will try to format the body of the POST request even if the `content-type` header is not
|
||||
# set to `application/json`
|
||||
tx = request.get_json(force=True)
|
||||
@ -89,11 +76,11 @@ def create_transaction():
|
||||
tx = util.transform_create(tx)
|
||||
tx = bigchain.consensus.sign_transaction(tx, private_key=bigchain.me_private)
|
||||
|
||||
if not bigchain.consensus.validate_fulfillments(tx):
|
||||
val['error'] = 'Invalid transaction fulfillments'
|
||||
if not bigchain.is_valid_transaction(tx):
|
||||
return make_error(400, 'Invalid transaction')
|
||||
|
||||
with monitor.timer('write_transaction', rate=bigchaindb.config['statsd']['rate']):
|
||||
val = bigchain.write_transaction(tx)
|
||||
bigchain.write_transaction(tx)
|
||||
|
||||
return flask.jsonify(**tx)
|
||||
|
@ -13,17 +13,21 @@ There are other configuration settings related to the web server (serving the HT
|
||||
|
||||
The HTTP API currently exposes two endpoints, one to get information about a specific transaction, and one to push a new transaction to the BigchainDB cluster.
|
||||
|
||||
.. http:get:: /transactions/(tx_id)
|
||||
.. http:get:: /transactions/{tx_id}
|
||||
|
||||
The transaction with the transaction ID `tx_id`.
|
||||
Get the transaction with the ID ``tx_id``.
|
||||
|
||||
This endpoint returns only a transaction from a ``VALID`` or ``UNDECIDED`` block on ``bigchain``, if exists.
|
||||
|
||||
:param tx_id: transaction ID
|
||||
:type tx_id: hex string
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /transactions/96480ce68912aa39a54766ac16334a835fbf777039670352ff967bf6d65bf4f7 HTTP/1.1
|
||||
GET /transactions/7ad5a4b83bc8c70c4fd7420ff3c60693ab8e6d0e3124378ca69ed5acd2578792 HTTP/1.1
|
||||
Host: example.com
|
||||
TODO: Other headers?
|
||||
|
||||
**Example response**:
|
||||
|
||||
@ -31,30 +35,56 @@ The HTTP API currently exposes two endpoints, one to get information about a spe
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
TODO: Other headers?
|
||||
|
||||
{'id': '96480ce68912aa39a54766ac16334a835fbf777039670352ff967bf6d65bf4f7',
|
||||
'transaction': {'conditions': [{'cid': 0,
|
||||
'condition': {'details': {'bitmask': 32,
|
||||
'public_key': 'FoWUUY6kK7QhgCsgVrV2vpDWfW43mq5ewb16Uh7FBbSF',
|
||||
'signature': None,
|
||||
'type': 'fulfillment',
|
||||
'type_id': 4},
|
||||
'uri': 'cc:4:20:2-2pA2qKr2i-GM6REdqJCLEL_CEWpy-5iQky7YgRZTA:96'},
|
||||
'new_owners': ['FoWUUY6kK7QhgCsgVrV2vpDWfW43mq5ewb16Uh7FBbSF']}],
|
||||
'data': {'payload': None, 'uuid': 'f14dc5a6-510e-4307-89c6-aec42af8a1ae'},
|
||||
'fulfillments': [{'current_owners': ['Ftat68WVLsPxVFLz2Rh2Sbwrrt51uFE3UpjkxY73vGKZ'],
|
||||
'fid': 0,
|
||||
'fulfillment': 'cf:4:3TqMI1ZFolraqHWADT6nIvUUt4HOwqdr0_-yj5Cglbg1V5qQV2CF2Yup1l6fQH2uhLGGFo9uHhZ6HNv9lssiD0ZaG88Bg_MTkz6xg2SW2Cw_YgpM-CyESVT404g54ZsK',
|
||||
'input': None}],
|
||||
'operation': 'CREATE',
|
||||
'timestamp': '1468494923'},
|
||||
'version': 1}
|
||||
|
||||
{
|
||||
"id":"7ad5a4b83bc8c70c4fd7420ff3c60693ab8e6d0e3124378ca69ed5acd2578792",
|
||||
"transaction":{
|
||||
"conditions":[
|
||||
{
|
||||
"cid":0,
|
||||
"condition":{
|
||||
"details":{
|
||||
"bitmask":32,
|
||||
"public_key":"CwA8s2QYQBfNz4WvjEwmJi83zYr7JhxRhidx6uZ5KBVd",
|
||||
"signature":null,
|
||||
"type":"fulfillment",
|
||||
"type_id":4
|
||||
},
|
||||
"uri":"cc:4:20:sVA_3p8gvl8yRFNTomqm6MaavKewka6dGYcFAuPrRXQ:96"
|
||||
},
|
||||
"new_owners":[
|
||||
"CwA8s2QYQBfNz4WvjEwmJi83zYr7JhxRhidx6uZ5KBVd"
|
||||
]
|
||||
}
|
||||
],
|
||||
"data":{
|
||||
"payload":null,
|
||||
"uuid":"a9999d69-6cde-4b80-819d-ed57f6abe257"
|
||||
},
|
||||
"fulfillments":[
|
||||
{
|
||||
"current_owners":[
|
||||
"JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE"
|
||||
],
|
||||
"fid":0,
|
||||
"fulfillment":"cf:4:__Y_Um6H73iwPe6ejWXEw930SQhqVGjtAHTXilPp0P01vE_Cx6zs3GJVoO1jhPL18C94PIVkLTGMUB2aKC9qsbIb3w8ejpOf0_I3OCuTbPdkd6r2lKMeVftMyMxkeWoM",
|
||||
"input":{
|
||||
"cid":0,
|
||||
"txid":"598ce4e9a29837a1c6fc337ee4a41b61c20ad25d01646754c825b1116abd8761"
|
||||
}
|
||||
}
|
||||
],
|
||||
"operation":"TRANSFER",
|
||||
"timestamp":"1471423869",
|
||||
"version":1
|
||||
}
|
||||
}
|
||||
|
||||
:statuscode 200: A transaction with that ID was found.
|
||||
:statuscode 404: A transaction with that ID was not found.
|
||||
|
||||
|
||||
|
||||
.. http:post:: /transactions/
|
||||
|
||||
Push a new transaction.
|
||||
@ -66,9 +96,50 @@ The HTTP API currently exposes two endpoints, one to get information about a spe
|
||||
POST /transactions/ HTTP/1.1
|
||||
Host: example.com
|
||||
Content-Type: application/json
|
||||
TODO: Other headers?
|
||||
|
||||
(TODO) Insert example request body here
|
||||
{
|
||||
"id":"7ad5a4b83bc8c70c4fd7420ff3c60693ab8e6d0e3124378ca69ed5acd2578792",
|
||||
"transaction":{
|
||||
"conditions":[
|
||||
{
|
||||
"cid":0,
|
||||
"condition":{
|
||||
"details":{
|
||||
"bitmask":32,
|
||||
"public_key":"CwA8s2QYQBfNz4WvjEwmJi83zYr7JhxRhidx6uZ5KBVd",
|
||||
"signature":null,
|
||||
"type":"fulfillment",
|
||||
"type_id":4
|
||||
},
|
||||
"uri":"cc:4:20:sVA_3p8gvl8yRFNTomqm6MaavKewka6dGYcFAuPrRXQ:96"
|
||||
},
|
||||
"new_owners":[
|
||||
"CwA8s2QYQBfNz4WvjEwmJi83zYr7JhxRhidx6uZ5KBVd"
|
||||
]
|
||||
}
|
||||
],
|
||||
"data":{
|
||||
"payload":null,
|
||||
"uuid":"a9999d69-6cde-4b80-819d-ed57f6abe257"
|
||||
},
|
||||
"fulfillments":[
|
||||
{
|
||||
"current_owners":[
|
||||
"JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE"
|
||||
],
|
||||
"fid":0,
|
||||
"fulfillment":"cf:4:__Y_Um6H73iwPe6ejWXEw930SQhqVGjtAHTXilPp0P01vE_Cx6zs3GJVoO1jhPL18C94PIVkLTGMUB2aKC9qsbIb3w8ejpOf0_I3OCuTbPdkd6r2lKMeVftMyMxkeWoM",
|
||||
"input":{
|
||||
"cid":0,
|
||||
"txid":"598ce4e9a29837a1c6fc337ee4a41b61c20ad25d01646754c825b1116abd8761"
|
||||
}
|
||||
}
|
||||
],
|
||||
"operation":"TRANSFER",
|
||||
"timestamp":"1471423869",
|
||||
"version":1
|
||||
}
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
@ -76,10 +147,78 @@ The HTTP API currently exposes two endpoints, one to get information about a spe
|
||||
|
||||
HTTP/1.1 201 Created
|
||||
Content-Type: application/json
|
||||
TODO: Other headers?
|
||||
|
||||
(TODO) Insert example response body here
|
||||
{
|
||||
"assignee":"4XYfCbabAWVUCbjTmRTFEu2sc3dFEdkse4r6X498B1s8",
|
||||
"id":"7ad5a4b83bc8c70c4fd7420ff3c60693ab8e6d0e3124378ca69ed5acd2578792",
|
||||
"transaction":{
|
||||
"conditions":[
|
||||
{
|
||||
"cid":0,
|
||||
"condition":{
|
||||
"details":{
|
||||
"bitmask":32,
|
||||
"public_key":"CwA8s2QYQBfNz4WvjEwmJi83zYr7JhxRhidx6uZ5KBVd",
|
||||
"signature":null,
|
||||
"type":"fulfillment",
|
||||
"type_id":4
|
||||
},
|
||||
"uri":"cc:4:20:sVA_3p8gvl8yRFNTomqm6MaavKewka6dGYcFAuPrRXQ:96"
|
||||
},
|
||||
"new_owners":[
|
||||
"CwA8s2QYQBfNz4WvjEwmJi83zYr7JhxRhidx6uZ5KBVd"
|
||||
]
|
||||
}
|
||||
],
|
||||
"data":{
|
||||
"payload":null,
|
||||
"uuid":"a9999d69-6cde-4b80-819d-ed57f6abe257"
|
||||
},
|
||||
"fulfillments":[
|
||||
{
|
||||
"current_owners":[
|
||||
"JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE"
|
||||
],
|
||||
"fid":0,
|
||||
"fulfillment":"cf:4:__Y_Um6H73iwPe6ejWXEw930SQhqVGjtAHTXilPp0P01vE_Cx6zs3GJVoO1jhPL18C94PIVkLTGMUB2aKC9qsbIb3w8ejpOf0_I3OCuTbPdkd6r2lKMeVftMyMxkeWoM",
|
||||
"input":{
|
||||
"cid":0,
|
||||
"txid":"598ce4e9a29837a1c6fc337ee4a41b61c20ad25d01646754c825b1116abd8761"
|
||||
}
|
||||
}
|
||||
],
|
||||
"operation":"TRANSFER",
|
||||
"timestamp":"1471423869",
|
||||
"version":1
|
||||
}
|
||||
}
|
||||
|
||||
:statuscode 201: A new transaction was created.
|
||||
:statuscode 400: The transaction was invalid and not created.
|
||||
|
||||
(TODO) What's the response status code if the POST fails?
|
||||
**Disclaimer**
|
||||
|
||||
``CREATE`` transactions are treated differently from ``TRANSFER`` assets.
|
||||
The reason is that a ``CREATE`` transaction needs to be signed by a federation node and not by the client.
|
||||
|
||||
The following python snippet in a client can be used to generate ``CREATE`` transactions before they can be pushed to the API server:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from bigchaindb import util
|
||||
tx = util.create_and_sign_tx(my_privkey, my_pubkey, my_pubkey, None, 'CREATE')
|
||||
|
||||
When POSTing ``tx`` to the API, the ``CREATE`` transaction will be signed by a federation node.
|
||||
|
||||
A ``TRANSFER`` transaction, that takes an existing input transaction to change ownership can be generated in multiple ways:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from bigchaindb import util, Bigchain
|
||||
tx = util.create_and_sign_tx(my_privkey, my_pubkey, other_pubkey, input_tx, 'TRANSFER')
|
||||
# or
|
||||
b = Bigchain()
|
||||
tx_unsigned = b.create_transaction(my_pubkey, other_pubkey, input_tx, 'TRANSFER')
|
||||
tx = b.sign_transaction(tx_unsigned, my_privkey)
|
||||
|
||||
More information on generating transactions can be found in the `Python server API examples <python-server-api-examples.html>`_
|
@ -14,6 +14,7 @@ def test_get_transaction_endpoint(b, client, user_vk):
|
||||
tx = b.get_transaction(input_tx['txid'])
|
||||
res = client.get(TX_ENDPOINT + input_tx['txid'])
|
||||
assert tx == res.json
|
||||
assert res.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('inputs')
|
||||
@ -50,3 +51,14 @@ def test_post_transfer_transaction_endpoint(b, client, user_vk, user_sk):
|
||||
assert res.json['transaction']['fulfillments'][0]['current_owners'][0] == user_vk
|
||||
assert res.json['transaction']['conditions'][0]['new_owners'][0] == to_keypair[1]
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('inputs')
|
||||
def test_post_invalid_transfer_transaction_returns_400(b, client, user_vk, user_sk):
|
||||
to_keypair = crypto.generate_key_pair()
|
||||
input_valid = b.get_owned_ids(user_vk).pop()
|
||||
transfer = b.create_transaction(user_vk, to_keypair[0], input_valid, 'TRANSFER')
|
||||
# transfer is not signed
|
||||
res = client.post(TX_ENDPOINT, data=json.dumps(transfer))
|
||||
|
||||
assert res.status_code == 400
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user