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 util
|
||||||
from bigchaindb import Bigchain
|
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
|
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['bigchain_pool'] = util.pool(Bigchain, size=settings.get('threads', 4))
|
||||||
app.config['monitor'] = Monitor()
|
app.config['monitor'] = Monitor()
|
||||||
|
|
||||||
app.register_blueprint(views.info_views, url_prefix='/')
|
app.register_blueprint(info_views, url_prefix='/')
|
||||||
app.register_blueprint(views.basic_views, url_prefix='/api/v1')
|
app.register_blueprint(transaction_views, url_prefix='/api/v1')
|
||||||
return app
|
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.
|
"""This module provides the blueprint for some basic API endpoints.
|
||||||
|
|
||||||
For more information please refer to the documentation in Apiary:
|
For more information please refer to the documentation on ReadTheDocs:
|
||||||
- http://docs.bigchaindb.apiary.io/
|
- https://bigchaindb.readthedocs.io/en/latest/drivers-clients/http-client-server-api.html
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
from flask import abort, current_app, request, Blueprint
|
from flask import current_app, request, Blueprint
|
||||||
|
|
||||||
import bigchaindb
|
import bigchaindb
|
||||||
from bigchaindb import util, version
|
from bigchaindb import util
|
||||||
|
from bigchaindb.web.views.base import make_error
|
||||||
|
|
||||||
|
transaction_views = Blueprint('transaction_views', __name__)
|
||||||
info_views = Blueprint('info_views', __name__)
|
|
||||||
basic_views = Blueprint('basic_views', __name__)
|
|
||||||
|
|
||||||
|
|
||||||
# Unfortunately I cannot find a reference to this decorator.
|
# Unfortunately I cannot find a reference to this decorator.
|
||||||
# This answer on SO is quite useful tho:
|
# This answer on SO is quite useful tho:
|
||||||
# - http://stackoverflow.com/a/13432373/597097
|
# - http://stackoverflow.com/a/13432373/597097
|
||||||
@basic_views.record
|
@transaction_views.record
|
||||||
def record(state):
|
def record(state):
|
||||||
"""This function checks if the blueprint can be initialized
|
"""This function checks if the blueprint can be initialized
|
||||||
with the provided state."""
|
with the provided state."""
|
||||||
@ -35,18 +34,8 @@ def record(state):
|
|||||||
'a monitor instance to record system '
|
'a monitor instance to record system '
|
||||||
'performance.')
|
'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']
|
|
||||||
})
|
|
||||||
|
|
||||||
|
@transaction_views.route('/transactions/<tx_id>')
|
||||||
@basic_views.route('/transactions/<tx_id>')
|
|
||||||
def get_transaction(tx_id):
|
def get_transaction(tx_id):
|
||||||
"""API endpoint to get details about a transaction.
|
"""API endpoint to get details about a transaction.
|
||||||
|
|
||||||
@ -63,12 +52,12 @@ def get_transaction(tx_id):
|
|||||||
tx = bigchain.get_transaction(tx_id)
|
tx = bigchain.get_transaction(tx_id)
|
||||||
|
|
||||||
if not tx:
|
if not tx:
|
||||||
abort(404)
|
return make_error(404)
|
||||||
|
|
||||||
return flask.jsonify(**tx)
|
return flask.jsonify(**tx)
|
||||||
|
|
||||||
|
|
||||||
@basic_views.route('/transactions/', methods=['POST'])
|
@transaction_views.route('/transactions/', methods=['POST'])
|
||||||
def create_transaction():
|
def create_transaction():
|
||||||
"""API endpoint to push transactions to the Federation.
|
"""API endpoint to push transactions to the Federation.
|
||||||
|
|
||||||
@ -78,8 +67,6 @@ def create_transaction():
|
|||||||
pool = current_app.config['bigchain_pool']
|
pool = current_app.config['bigchain_pool']
|
||||||
monitor = current_app.config['monitor']
|
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
|
# `force` will try to format the body of the POST request even if the `content-type` header is not
|
||||||
# set to `application/json`
|
# set to `application/json`
|
||||||
tx = request.get_json(force=True)
|
tx = request.get_json(force=True)
|
||||||
@ -89,11 +76,11 @@ def create_transaction():
|
|||||||
tx = util.transform_create(tx)
|
tx = util.transform_create(tx)
|
||||||
tx = bigchain.consensus.sign_transaction(tx, private_key=bigchain.me_private)
|
tx = bigchain.consensus.sign_transaction(tx, private_key=bigchain.me_private)
|
||||||
|
|
||||||
if not bigchain.consensus.validate_fulfillments(tx):
|
if not bigchain.is_valid_transaction(tx):
|
||||||
val['error'] = 'Invalid transaction fulfillments'
|
return make_error(400, 'Invalid transaction')
|
||||||
|
|
||||||
with monitor.timer('write_transaction', rate=bigchaindb.config['statsd']['rate']):
|
with monitor.timer('write_transaction', rate=bigchaindb.config['statsd']['rate']):
|
||||||
val = bigchain.write_transaction(tx)
|
bigchain.write_transaction(tx)
|
||||||
|
|
||||||
return flask.jsonify(**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.
|
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**:
|
**Example request**:
|
||||||
|
|
||||||
.. sourcecode:: http
|
.. sourcecode:: http
|
||||||
|
|
||||||
GET /transactions/96480ce68912aa39a54766ac16334a835fbf777039670352ff967bf6d65bf4f7 HTTP/1.1
|
GET /transactions/7ad5a4b83bc8c70c4fd7420ff3c60693ab8e6d0e3124378ca69ed5acd2578792 HTTP/1.1
|
||||||
Host: example.com
|
Host: example.com
|
||||||
TODO: Other headers?
|
|
||||||
|
|
||||||
**Example response**:
|
**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
|
HTTP/1.1 200 OK
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
TODO: Other headers?
|
|
||||||
|
{
|
||||||
{'id': '96480ce68912aa39a54766ac16334a835fbf777039670352ff967bf6d65bf4f7',
|
"id":"7ad5a4b83bc8c70c4fd7420ff3c60693ab8e6d0e3124378ca69ed5acd2578792",
|
||||||
'transaction': {'conditions': [{'cid': 0,
|
"transaction":{
|
||||||
'condition': {'details': {'bitmask': 32,
|
"conditions":[
|
||||||
'public_key': 'FoWUUY6kK7QhgCsgVrV2vpDWfW43mq5ewb16Uh7FBbSF',
|
{
|
||||||
'signature': None,
|
"cid":0,
|
||||||
'type': 'fulfillment',
|
"condition":{
|
||||||
'type_id': 4},
|
"details":{
|
||||||
'uri': 'cc:4:20:2-2pA2qKr2i-GM6REdqJCLEL_CEWpy-5iQky7YgRZTA:96'},
|
"bitmask":32,
|
||||||
'new_owners': ['FoWUUY6kK7QhgCsgVrV2vpDWfW43mq5ewb16Uh7FBbSF']}],
|
"public_key":"CwA8s2QYQBfNz4WvjEwmJi83zYr7JhxRhidx6uZ5KBVd",
|
||||||
'data': {'payload': None, 'uuid': 'f14dc5a6-510e-4307-89c6-aec42af8a1ae'},
|
"signature":null,
|
||||||
'fulfillments': [{'current_owners': ['Ftat68WVLsPxVFLz2Rh2Sbwrrt51uFE3UpjkxY73vGKZ'],
|
"type":"fulfillment",
|
||||||
'fid': 0,
|
"type_id":4
|
||||||
'fulfillment': 'cf:4:3TqMI1ZFolraqHWADT6nIvUUt4HOwqdr0_-yj5Cglbg1V5qQV2CF2Yup1l6fQH2uhLGGFo9uHhZ6HNv9lssiD0ZaG88Bg_MTkz6xg2SW2Cw_YgpM-CyESVT404g54ZsK',
|
},
|
||||||
'input': None}],
|
"uri":"cc:4:20:sVA_3p8gvl8yRFNTomqm6MaavKewka6dGYcFAuPrRXQ:96"
|
||||||
'operation': 'CREATE',
|
},
|
||||||
'timestamp': '1468494923'},
|
"new_owners":[
|
||||||
'version': 1}
|
"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 200: A transaction with that ID was found.
|
||||||
:statuscode 404: A transaction with that ID was not found.
|
:statuscode 404: A transaction with that ID was not found.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. http:post:: /transactions/
|
.. http:post:: /transactions/
|
||||||
|
|
||||||
Push a new transaction.
|
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
|
POST /transactions/ HTTP/1.1
|
||||||
Host: example.com
|
Host: example.com
|
||||||
Content-Type: application/json
|
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**:
|
**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
|
HTTP/1.1 201 Created
|
||||||
Content-Type: application/json
|
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 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'])
|
tx = b.get_transaction(input_tx['txid'])
|
||||||
res = client.get(TX_ENDPOINT + input_tx['txid'])
|
res = client.get(TX_ENDPOINT + input_tx['txid'])
|
||||||
assert tx == res.json
|
assert tx == res.json
|
||||||
|
assert res.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('inputs')
|
@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']['fulfillments'][0]['current_owners'][0] == user_vk
|
||||||
assert res.json['transaction']['conditions'][0]['new_owners'][0] == to_keypair[1]
|
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