diff --git a/docs/server/generate_http_server_api_documentation.py b/docs/server/generate_http_server_api_documentation.py index b32bda02..4407cda4 100644 --- a/docs/server/generate_http_server_api_documentation.py +++ b/docs/server/generate_http_server_api_documentation.py @@ -7,14 +7,28 @@ import os.path from bigchaindb.common.transaction import Transaction, Input, TransactionLink from bigchaindb.core import Bigchain from bigchaindb.models import Block - +from bigchaindb.web import server TPLS = {} +TPLS['index-response'] = """\ +HTTP/1.1 200 OK +Content-Type: application/json + +%(index)s +""" + +TPLS['api-index-response'] = """\ +HTTP/1.1 200 OK +Content-Type: application/json + +%(api_index)s +""" + TPLS['get-tx-id-request'] = """\ -GET /transactions/%(txid)s HTTP/1.1 +GET /api/v1/transactions/%(txid)s HTTP/1.1 Host: example.com """ @@ -28,23 +42,8 @@ Content-Type: application/json """ -TPLS['get-tx-unspent-request'] = """\ -GET /transactions?unspent=true&public_keys=%(public_keys_transfer_last)s HTTP/1.1 -Host: example.com - -""" - - -TPLS['get-tx-unspent-response'] = """\ -HTTP/1.1 200 OK -Content-Type: application/json - -[%(tx_transfer_last)s] -""" - - TPLS['get-tx-by-asset-request'] = """\ -GET /transactions?operation=transfer&asset_id=%(txid)s HTTP/1.1 +GET /api/v1/transactions?operation=TRANSFER&asset_id=%(txid)s HTTP/1.1 Host: example.com """ @@ -59,7 +58,7 @@ Content-Type: application/json """ TPLS['post-tx-request'] = """\ -POST /transactions/ HTTP/1.1 +POST /api/v1/transactions/ HTTP/1.1 Host: example.com Content-Type: application/json @@ -68,12 +67,10 @@ Content-Type: application/json TPLS['post-tx-response'] = """\ -HTTP/1.1 202 Accepted +HTTP/1.1 200 OK Content-Type: application/json -{ - "status": "/statuses?tx_id=%(txid)s" -} +%(tx)s """ @@ -108,7 +105,7 @@ Content-Type: application/json TPLS['get-statuses-block-request'] = """\ -GET /statuses?block_id=%(blockid)s HTTP/1.1 +GET /api/v1/statuses?block_id=%(blockid)s HTTP/1.1 Host: example.com """ @@ -138,7 +135,7 @@ Content-Type: application/json TPLS['get-block-request'] = """\ -GET /blocks/%(blockid)s HTTP/1.1 +GET /api/v1/blocks/%(blockid)s HTTP/1.1 Host: example.com """ @@ -153,7 +150,7 @@ Content-Type: application/json TPLS['get-block-txid-request'] = """\ -GET /blocks?tx_id=%(txid)s HTTP/1.1 +GET /api/v1/blocks?tx_id=%(txid)s HTTP/1.1 Host: example.com """ @@ -168,7 +165,7 @@ Content-Type: application/json TPLS['get-vote-request'] = """\ -GET /votes?block_id=%(blockid)s HTTP/1.1 +GET /api/v1/votes?block_id=%(blockid)s HTTP/1.1 Host: example.com """ @@ -185,13 +182,37 @@ Content-Type: application/json def main(): """ Main function """ + ctx = {} + + def pretty_json(data): + return json.dumps(data, indent=2, sort_keys=True) + + client = server.create_app().test_client() + + host = 'example.com:9984' + + # HTTP Index + res = client.get('/', environ_overrides={'HTTP_HOST': host}) + res_data = json.loads(res.data.decode()) + res_data['keyring'] = [ + "6qHyZew94NMmUTYyHnkZsB8cxJYuRNEiEpXHe1ih9QX3", + "AdDuyrTyjrDt935YnFu4VBCVDhHtY2Y6rcy7x2TFeiRi" + ] + ctx['index'] = pretty_json(res_data) + + # API index + res = client.get('/api/v1/', environ_overrides={'HTTP_HOST': host}) + ctx['api_index'] = pretty_json(json.loads(res.data.decode())) + # tx create privkey = 'CfdqtD7sS7FgkMoGPXw55MVGGFwQLAoHYTcBhZDtF99Z' pubkey = '4K9sWUMFwTgaDGPfdynrbxWqWS6sWmKbZoTjxLtVUibD' asset = {'msg': 'Hello BigchainDB!'} tx = Transaction.create([pubkey], [([pubkey], 1)], asset=asset, metadata={'sequence': 0}) tx = tx.sign([privkey]) - tx_json = json.dumps(tx.to_dict(), indent=2, sort_keys=True) + ctx['tx'] = pretty_json(tx.to_dict()) + ctx['public_keys'] = tx.outputs[0].public_keys[0] + ctx['txid'] = tx.id # tx transfer privkey_transfer = '3AeWpPdhEZzWLYfkfYHBfMFC2r1f8HEaGS9NtbbKssya' @@ -203,41 +224,48 @@ def main(): owners_before=tx.outputs[cid].public_keys) tx_transfer = Transaction.transfer([input_], [([pubkey_transfer], 1)], asset_id=tx.id, metadata={'sequence': 1}) tx_transfer = tx_transfer.sign([privkey]) - tx_transfer_json = json.dumps(tx_transfer.to_dict(), indent=2, sort_keys=True) + ctx['tx_transfer'] = pretty_json(tx_transfer.to_dict()) + ctx['public_keys_transfer'] = tx_transfer.outputs[0].public_keys[0] + ctx['tx_transfer_id'] = tx_transfer.id - privkey_transfer_last = 'sG3jWDtdTXUidBJK53ucSTrosktG616U3tQHBk81eQe' + # privkey_transfer_last = 'sG3jWDtdTXUidBJK53ucSTrosktG616U3tQHBk81eQe' pubkey_transfer_last = '3Af3fhhjU6d9WecEM9Uw5hfom9kNEwE7YuDWdqAUssqm' cid = 0 input_ = Input(fulfillment=tx_transfer.outputs[cid].fulfillment, fulfills=TransactionLink(txid=tx_transfer.id, output=cid), owners_before=tx_transfer.outputs[cid].public_keys) - tx_transfer_last = Transaction.transfer([input_], [([pubkey_transfer_last], 1)], asset_id=tx.id, metadata={'sequence': 2}) + tx_transfer_last = Transaction.transfer([input_], [([pubkey_transfer_last], 1)], + asset_id=tx.id, metadata={'sequence': 2}) tx_transfer_last = tx_transfer_last.sign([privkey_transfer]) - tx_transfer_last_json = json.dumps(tx_transfer_last.to_dict(), indent=2, sort_keys=True) + ctx['tx_transfer_last'] = pretty_json(tx_transfer_last.to_dict()) + ctx['tx_transfer_last_id'] = tx_transfer_last.id + ctx['public_keys_transfer_last'] = tx_transfer_last.outputs[0].public_keys[0] # block node_private = "5G2kE1zJAgTajkVSbPAQWo4c2izvtwqaNHYsaNpbbvxX" node_public = "DngBurxfeNVKZWCEcDnLj1eMPAS7focUZTE5FndFGuHT" signature = "53wxrEQDYk1dXzmvNSytbCfmNVnPqPkDQaTnAe8Jf43s6ssejPxezkCvUnGTnduNUmaLjhaan1iRLi3peu6s5DzA" block = Block(transactions=[tx], node_pubkey=node_public, voters=[node_public], signature=signature) - block_json = json.dumps(block.to_dict(), indent=2, sort_keys=True) + ctx['block'] = pretty_json(block.to_dict()) + ctx['blockid'] = block.id - block_transfer = Block(transactions=[tx_transfer], node_pubkey=node_public, voters=[node_public], signature=signature) - block_transfer_json = json.dumps(block.to_dict(), indent=2, sort_keys=True) + block_transfer = Block(transactions=[tx_transfer], node_pubkey=node_public, + voters=[node_public], signature=signature) + ctx['block_transfer'] = pretty_json(block.to_dict()) # vote DUMMY_SHA3 = '0123456789abcdef' * 4 b = Bigchain(public_key=node_public, private_key=node_private) vote = b.vote(block.id, DUMMY_SHA3, True) - vote_json = json.dumps(vote, indent=2, sort_keys=True) + ctx['vote'] = pretty_json(vote) # block status block_list = [ block_transfer.id, block.id ] - block_list_json = json.dumps(block_list, indent=2, sort_keys=True) + ctx['block_list'] = pretty_json(block_list) base_path = os.path.join(os.path.dirname(__file__), 'source/drivers-clients/samples') @@ -246,19 +274,7 @@ def main(): for name, tpl in TPLS.items(): path = os.path.join(base_path, name + '.http') - code = tpl % {'tx': tx_json, - 'txid': tx.id, - 'tx_transfer': tx_transfer_json, - 'tx_transfer_id': tx_transfer.id, - 'tx_transfer_last': tx_transfer_last_json, - 'tx_transfer_last_id': tx_transfer_last.id, - 'public_keys': tx.outputs[0].public_keys[0], - 'public_keys_transfer': tx_transfer.outputs[0].public_keys[0], - 'public_keys_transfer_last': tx_transfer_last.outputs[0].public_keys[0], - 'block': block_json, - 'blockid': block.id, - 'block_list': block_list_json, - 'vote': vote_json} + code = tpl % ctx with open(path, 'w') as handle: handle.write(code) @@ -270,5 +286,3 @@ def setup(*_): if __name__ == '__main__': main() - - diff --git a/docs/server/source/drivers-clients/http-client-server-api.rst b/docs/server/source/drivers-clients/http-client-server-api.rst index 75dbffe0..72c22870 100644 --- a/docs/server/source/drivers-clients/http-client-server-api.rst +++ b/docs/server/source/drivers-clients/http-client-server-api.rst @@ -21,21 +21,8 @@ or ``https://example.com:9984`` then you should get an HTTP response with something like the following in the body: -.. code-block:: json - - { - "_links": { - "docs": "https://docs.bigchaindb.com/projects/server/en/v0.9.0/", - "api_v1": "http://example.com:9984/api/v1/" - }, - "keyring": [ - "6qHyZew94NMmUTYyHnkZsB8cxJYuRNEiEpXHe1ih9QX3", - "AdDuyrTyjrDt935YnFu4VBCVDhHtY2Y6rcy7x2TFeiRi" - ], - "public_key": "AiygKSRhZWTxxYT4AfgKoTG4TZAoPsWoEt6C6bLq4jJR", - "software": "BigchainDB", - "version": "0.9.0" - } +.. literalinclude:: samples/index-response.http + :language: http API Root Endpoint @@ -47,22 +34,13 @@ or ``https://example.com:9984/api/v1/``, then you should get an HTTP response that allows you to discover the BigchainDB API endpoints: -.. code-block:: json - - { - "_links": { - "docs": "https://docs.bigchaindb.com/projects/server/en/v0.9.0/drivers-clients/http-client-server-api.html", - "self": "https://example.com:9984/api/v1", - "statuses": "https://example.com:9984/api/v1/statuses", - "transactions": "https://example.com:9984/api/v1/transactions", - }, - "version" : "0.9.0" - } +.. literalinclude:: samples/api-index-response.http + :language: http Transactions ------------------- -.. http:get:: /transactions/{tx_id} +.. http:get:: /api/v1/transactions/{tx_id} Get the transaction with the ID ``tx_id``. @@ -87,107 +65,38 @@ Transactions :statuscode 200: A transaction with that ID was found. :statuscode 404: A transaction with that ID was not found. -.. http:get:: /transactions +.. http:get:: /api/v1/transactions - The unfiltered ``/transactions`` endpoint without any query parameters + The unfiltered ``/api/v1/transactions`` endpoint without any query parameters returns a status code `400`. For valid filters, see the sections below. - **Example request**: - - .. sourcecode:: http - - GET /transactions HTTP/1.1 - Host: example.com - - **Example response**: - - .. sourcecode:: http - - HTTP/1.1 400 Bad Request - - :statuscode 400: The request wasn't understood by the server, a mandatory querystring was not included in the request. - There are however filtered requests that might come of use, given the endpoint is queried correctly. Some of them include retrieving a list of transactions that include: - * `Unspent outputs <#get--transactions?unspent=true&public_keys=public_keys>`_ * `Transactions related to a specific asset <#get--transactions?operation=CREATE|TRANSFER&asset_id=asset_id>`_ In this section, we've listed those particular requests, as they will likely to be very handy when implementing your application on top of BigchainDB. .. note:: - Looking up transactions with a specific ``metadata`` field is currently not supported. - This functionality requires something like custom indexing per client or read-only followers, - which is not yet on the roadmap. + Looking up transactions with a specific ``metadata`` field is currently not supported, + however, providing a way to query based on ``metadata`` data is on our roadmap. A generalization of those parameters follows: - :query boolean unspent: A flag to indicate whether only transactions with unspent outputs should be returned. - - :query string public_keys: Public key able to validly spend an output of a transaction, assuming the user also has the corresponding private key. - :query string operation: One of the two supported operations of a transaction: ``CREATE``, ``TRANSFER``. - :query string asset_id: asset ID. + :query string asset_id: The ID of the asset. - -.. http:get:: /transactions?unspent=true&public_keys={public_keys} - - Get a list of transactions with unspent outputs. - - If the querystring ``unspent`` is set to ``false`` and all outputs for - ``public_keys`` happen to be spent already, this endpoint will return - an empty list. Transactions with multiple outputs that have not all been spent - will be included in the response. - - This endpoint returns transactions only if they are - included in the ``BACKLOG`` or in a ``VALID`` or ``UNDECIDED`` block on ``bigchain``. - - :query boolean unspent: A flag to indicate if transactions with unspent outputs should be returned. - - :query string public_keys: Public key able to validly spend an output of a transaction, assuming the user also has the corresponding private key. - - **Example request**: - - - .. literalinclude:: samples/get-tx-unspent-request.http - :language: http - - - **Example response**: - - .. literalinclude:: samples/get-tx-unspent-response.http - :language: http - - :resheader Content-Type: ``application/json`` - - :statuscode 200: A list of transactions containing unspent outputs was found and returned. - :statuscode 400: The request wasn't understood by the server, e.g. the ``public_keys`` querystring was not included in the request. - -.. http:get:: /transactions?operation={CREATE|TRANSFER}&asset_id={asset_id} +.. http:get:: /api/v1/transactions?operation={CREATE|TRANSFER}&asset_id={asset_id} Get a list of transactions that use an asset with the ID ``asset_id``. Every ``TRANSFER`` transaction that originates from a ``CREATE`` transaction with ``asset_id`` will be included. This allows users to query the entire history or provenance of an asset. - This endpoint returns transactions only if they are - included in the ``BACKLOG`` or in a ``VALID`` or ``UNDECIDED`` block on ``bigchain``. - - .. note:: - The BigchainDB API currently doesn't expose an - ``/assets/{asset_id}`` endpoint, as there wouldn't be any way for a - client to verify that what was received is consistent with what was - persisted in the database. - However, BigchainDB's consensus ensures that any ``asset_id`` is - a unique key identifying an asset, meaning that when calling - ``/transactions?operation=CREATE&asset_id={asset_id}``, there will in - any case only be one transaction returned (in a list though, since - ``/transactions`` is a list-returning endpoint). - Leaving out the ``asset_id`` query and calling - ``/transactions?operation=CREATE`` returns the list of assets. + This endpoint returns transactions only if they are decided ``VALID`` by the server. :query string operation: One of the two supported operations of a transaction: ``CREATE``, ``TRANSFER``. @@ -209,14 +118,14 @@ Transactions :statuscode 400: The request wasn't understood by the server, e.g. the ``asset_id`` querystring was not included in the request. -.. http:post:: /transactions +.. http:post:: /api/v1/transactions - Push a new transaction. The endpoint will return a ``statuses`` endpoint to track - the status of the transaction. + Push a new transaction. .. note:: - The posted transaction should be valid `transaction - `_. + The posted `transaction + `_ + should be structurally valid and not spending an already spent output. The steps to build a valid transaction are beyond the scope of this page. One would normally use a driver such as the `BigchainDB Python Driver `_ @@ -233,16 +142,59 @@ Transactions :language: http :resheader Content-Type: ``application/json`` - :resheader Location: As the transaction will be persisted asynchronously, an endpoint to monitor its status is provided in this header. - :statuscode 202: The pushed transaction was accepted in the ``BACKLOG``, but the processing has not been completed. + :statuscode 200: The pushed transaction was accepted in the ``BACKLOG``, but the processing has not been completed. :statuscode 400: The transaction was malformed and not accepted in the ``BACKLOG``. +Transaction Outputs +------------------- + +The ``/api/v1/outputs`` endpoint returns transactions outputs filtered by a +given public key, and optionally filtered to only include outputs that have +not already been spent. + + +.. http:get:: /api/v1/outputs?public_key={public_key} + + Get transaction outputs by public key. The `public_key` parameter must be + a base58 encoded ed25519 public key associated with transaction output + ownership. + + Returns a list of links to transaction outputs. + + :param public_key: Base58 encoded public key associated with output ownership. This parameter is mandatory and without it the endpoint will return a ``400`` response code. + :param unspent: Boolean value ("true" or "false") indicating if the result set should be limited to outputs that are available to spend. + + + **Example request**: + + .. sourcecode:: http + + GET /api/v1/outputs?public_key=1AAAbbb...ccc HTTP/1.1 + Host: example.com + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + "../transactions/2d431073e1477f3073a4693ac7ff9be5634751de1b8abaa1f4e19548ef0b4b0e/outputs/0", + "../transactions/2d431073e1477f3073a4693ac7ff9be5634751de1b8abaa1f4e19548ef0b4b0e/outputs/1" + ] + + :statuscode 200: A list of outputs were found and returned in the body of the response. + :statuscode 400: The request wasn't understood by the server, e.g. the ``public_key`` querystring was not included in the request. + + + Statuses -------------------------------- -.. http:get:: /statuses +.. http:get:: /api/v1/statuses Get the status of an asynchronously written transaction or block by their id. @@ -264,7 +216,7 @@ Statuses <#get--statuses?block_id=block_id>`_). -.. http:get:: /statuses?tx_id={tx_id} +.. http:get:: /api/v1/statuses?tx_id={tx_id} Get the status of a transaction. @@ -290,7 +242,7 @@ Statuses :statuscode 404: A transaction with that ID was not found. -.. http:get:: /statuses?block_id={block_id} +.. http:get:: /api/v1/statuses?block_id={block_id} Get the status of a block. @@ -334,7 +286,7 @@ The `votes endpoint <#votes>`_ contains all the voting information for a specifi Blocks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. http:get:: /blocks/{block_id} +.. http:get:: /api/v1/blocks/{block_id} Get the block with the ID ``block_id``. Any blocks, be they ``VALID``, ``UNDECIDED`` or ``INVALID`` will be returned. To check a block's status independently, use the `Statuses endpoint <#status>`_. @@ -361,7 +313,7 @@ Blocks :statuscode 404: A block with that ID was not found. -.. http:get:: /blocks +.. http:get:: /api/v1/blocks The unfiltered ``/blocks`` endpoint without any query parameters returns a `400` status code. The list endpoint should be filtered with a ``tx_id`` query parameter, @@ -373,7 +325,7 @@ Blocks .. sourcecode:: http - GET /blocks HTTP/1.1 + GET /api/v1/blocks HTTP/1.1 Host: example.com **Example response**: @@ -384,7 +336,7 @@ Blocks :statuscode 400: The request wasn't understood by the server, e.g. just requesting ``/blocks`` without the ``block_id``. -.. http:get:: /blocks?tx_id={tx_id}&status={UNDECIDED|VALID|INVALID} +.. http:get:: /api/v1/blocks?tx_id={tx_id}&status={UNDECIDED|VALID|INVALID} Retrieve a list of ``block_id`` with their corresponding status that contain a transaction with the ID ``tx_id``. @@ -419,7 +371,7 @@ Blocks Votes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. http:get:: /votes?block_id={block_id} +.. http:get:: /api/v1/votes?block_id={block_id} Retrieve a list of votes for a certain block with ID ``block_id``. To check for the validity of a vote, a user of this endpoint needs to