From 78b29852d38e59e394d32393475650d650543e08 Mon Sep 17 00:00:00 2001 From: Leonid Shlyapnikov Date: Thu, 23 Jan 2020 14:54:12 -0500 Subject: [PATCH] JSON API documentation update (#4173) Document error handling Document query store CHANGELOG_BEGIN CHANGELOG_END --- docs/README.md | 2 +- docs/source/json-api/index.rst | 790 ++++++++++++------ .../scala/com/digitalasset/http/Config.scala | 6 +- .../scala/com/digitalasset/http/Main.scala | 4 +- 4 files changed, 541 insertions(+), 261 deletions(-) diff --git a/docs/README.md b/docs/README.md index f8c972a321dd..a8f820b0b885 100644 --- a/docs/README.md +++ b/docs/README.md @@ -26,7 +26,7 @@ To edit those docs, edit the content inside the code source. ### Previewing -To preview the full docs, as deployed to docs.daml.com,run `scripts/preview.sh`. +To preview the full docs, as deployed to docs.daml.com, run `scripts/preview.sh`. To live-preview the docs, run `scripts/live-preview.sh`. The script accepts two flags: diff --git a/docs/source/json-api/index.rst b/docs/source/json-api/index.rst index a72dc3dc15d6..77694a38a127 100644 --- a/docs/source/json-api/index.rst +++ b/docs/source/json-api/index.rst @@ -12,22 +12,21 @@ tracker `_ or `on Slack `_. -The JSON API provides a significantly simpler way than :doc:`the Ledger -API ` to access *basic active contract set -functionality*: +The **JSON API** provides a significantly simpler way than :doc:`the Ledger +API ` to interact with a ledger by providing *basic active contract set functionality*: - creating contracts, -- exercising choices on contracts, and -- querying the current active contract set. +- exercising choices on contracts, +- querying the current active contract set, and +- retrieving all known parties. -The goal is to get you up and running writing effective -ledger-integrated applications quickly, so we have deliberately excluded -complicating concerns, including but not limited to +The goal of this API is to get you up and running distributed ledger applications quickly, so we have deliberately excluded +complicating concerns, including but not limited to: - inspecting transactions, - asynchronous submit/completion workflows, - temporal queries (e.g. active contracts *as of a certain time*), and -- ledger metaprogramming (e.g. packages and templates). +- ledger metaprogramming (e.g. retrieving packages and templates). For these and other features, use :doc:`the Ledger API ` instead. @@ -41,24 +40,30 @@ instead. How to start ************ -Start sandbox from a DAML project directory -=========================================== +Start sandbox +============= -:: +From a DAML project directory: + +.. code-block:: shell $ daml sandbox --wall-clock-time --ledgerid MyLedger ./.daml/dist/quickstart-0.0.1.dar -Start HTTP service from a DAML project directory -================================================ +.. _start-http-service: + +Start HTTP service +================== -:: +From a DAML project directory: + +.. code-block:: shell $ daml json-api --ledger-host localhost --ledger-port 6865 \ --http-port 7575 --max-inbound-message-size 4194304 --package-reload-interval 5s \ --application-id HTTP-JSON-API-Gateway --static-content "prefix=static,directory=./static-content" \ --query-store-jdbc-config "driver=org.postgresql.Driver,url=jdbc:postgresql://localhost:5432/test?&ssl=true,user=postgres,password=password,createSchema=false" -:: +.. code-block:: none $ daml json-api --help HTTP JSON API daemon @@ -81,20 +86,25 @@ Start HTTP service from a DAML project directory --max-inbound-message-size Optional max inbound message size in bytes. Defaults to 4194304 --query-store-jdbc-config "driver=,url=,user=,password=,createSchema=" - Optional query store JDBC configuration string. Contains comma-separated key-value pairs. Where: - driver -- JDBC driver class name, - url -- JDBC connection URL, + Optional query store JDBC configuration string. Query store is a search index, use it if you need to query large active contract sets. Contains comma-separated key-value pairs. Where: + driver -- JDBC driver class name, only org.postgresql.Driver supported right now, + url -- JDBC connection URL, only jdbc:postgresql supported right now, user -- database user name, - password -- database user password + password -- database user password, createSchema -- boolean flag, if set to true, the process will re-create database schema and terminate immediately. - Example: "driver=org.postgresql.Driver,url=jdbc:postgresql://localhost:5432/test?&ssl=true,user=postgres,password=password,createSchema=false" + Example: "driver=org.postgresql.Driver,url=jdbc:postgresql://localhost:5432/test?&ssl=true,user=postgres,password=password,createSchema=false" --static-content "prefix=,directory=" DEV MODE ONLY (not recommended for production). Optional static content configuration string. Contains comma-separated key-value pairs. Where: prefix -- URL prefix, directory -- local directory that will be mapped to the URL prefix. Example: "prefix=static,directory=./static-content" --access-token-file - provide the path from which the access token will be read, required to interact with an authenticated ledger, no default + provide the path from which the access token will be read, required to interact with an authenticated ledger, no default + --websocket-config "maxDuration=,heartBeatPer=Server-side heartBeat interval in seconds" + Optional websocket configuration string. Contains comma-separated key-value pairs. Where: + maxDuration -- Maximum websocket session duration in minutes + heartBeatPer -- Server-side heartBeat interval in seconds + Example: "maxDuration=120,heartBeatPer=5" With Authentication =================== @@ -114,7 +124,7 @@ If the token cannot be read from the provided path or the Ledger API reports an Example session *************** -:: +.. code-block:: shell $ daml new iou-quickstart-java quickstart-java $ cd iou-quickstart-java/ @@ -128,7 +138,9 @@ Choosing a party You specify your party and other settings with JWT. In testing environments, you can use https://jwt.io to generate your token. -The default "header" is fine. Under "Payload", fill in:: +The default "header" is fine. Under "Payload", fill in: + +.. code-block:: json { "https://daml.com/ledger-api": { @@ -152,243 +164,163 @@ add the subprotocols ``jwt.token.copy-paste-token-here`` and Here are two tokens you can use for testing: -- ``{"ledgerId": "MyLedger", "applicationId": "foobar", "party": "Alice"}`` - ``eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsZWRnZXJJZCI6Ik15TGVkZ2VyIiwiYXBwbGljYXRpb25JZCI6ImZvb2JhciIsInBhcnR5IjoiQWxpY2UifQ.4HYfzjlYr1ApUDot0a6a4zB49zS_jrwRUOCkAiPMqo0`` +- ``{"https://daml.com/ledger-api": {"ledgerId": "MyLedger", "applicationId": "foobar", "actAs": ["Alice"]}}`` + ``eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczovL2RhbWwuY29tL2xlZGdlci1hcGkiOnsibGVkZ2VySWQiOiJNeUxlZGdlciIsImFwcGxpY2F0aW9uSWQiOiJmb29iYXIiLCJhY3RBcyI6WyJBbGljZSJdfX0.VdDI96mw5hrfM5ZNxLyetSVwcD7XtLT4dIdHIOa9lcU`` -- ``{"ledgerId": "MyLedger", "applicationId": "foobar", "party": "Bob"}`` - ``eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsZWRnZXJJZCI6Ik15TGVkZ2VyIiwiYXBwbGljYXRpb25JZCI6ImZvb2JhciIsInBhcnR5IjoiQm9iIn0.2LE3fAvUzLx495JWpuSzHye9YaH3Ddt4d2Pj0L1jSjA`` +- ``{"https://daml.com/ledger-api": {"ledgerId": "MyLedger", "applicationId": "foobar", "actAs": ["Bob"]}}`` + ``eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczovL2RhbWwuY29tL2xlZGdlci1hcGkiOnsibGVkZ2VySWQiOiJNeUxlZGdlciIsImFwcGxpY2F0aW9uSWQiOiJmb29iYXIiLCJhY3RBcyI6WyJCb2IiXX19.zU-iMSFG90na8IHacrS25xho3u6AKnSlTKbvpkaSyYw`` For production use, we have a tool in development for generating proper RSA-encrypted tokens locally, which will arrive when the service also supports such tokens. -GET ``/contracts/search`` -========================= +Error Reporting +=============== -List all currently active contracts for all known templates. Note that the retrieved contracts do not get persisted into query store database. +The **JSON API** reports errors using standard HTTP status codes. It divides HTTP status codes in 3 groups indicating: -The response is the same as for the POST method below. +1. success (200) +2. failure due to a client-side problem (400, 401, 404) +3. failure due to a server-side problem (500) -POST ``/contracts/search`` -========================== +The **JSON API** can return one of the following HTTP status codes: -List currently active contracts that match a given query. +- 200 - OK +- 400 - Bad Request (Client Error) +- 401 - Unauthorized, authentication required +- 404 - Not Found +- 500 - Internal Server Error -Request -------- +If client's HTTP GET or POST request reaches an API endpoint, the corresponding response will always contain a JSON object with ``status`` field, either ``errors`` or ``result`` and optional ``warnings``: -application/json body, formatted according to the :doc:`search-query-language`: - -.. code-block:: json +.. code-block:: none { - "templateIds": ["Iou:Iou"], - "query": {"amount": 999.99} + "status": <400 | 401 | 404 | 500> + ,"errors": | ,"result": + [ ,"warnings": ] } -Empty Response --------------- +Where: -.. code-block:: json +- ``status`` -- a JSON number which matches the HTTP response status code returned in the HTTP header, +- ``errors`` -- a JSON array of strings, each string represents one error, +- ``result`` -- a JSON object or JSON array, representing one or many results, +- ``warnings`` -- optional field, a JSON object, representing one ore many warnings. - { - "status": 200, - "result": [] - } +See the following blog post for more details about error handling best practices: `REST API Error Codes 101 `_. -Nonempty Response ------------------ +Successful response, HTTP status: 200 OK +---------------------------------------- -Each contract formatted according to :doc:`lf-value-specification`. +- Content-Type: ``application/json`` +- Content: -.. code-block:: json +.. code-block:: none { - "result": [ - { - "observers": [], - "agreementText": "", - "payload": { - "observers": [], - "issuer": "Alice", - "amount": "999.99", - "currency": "USD", - "owner": "Alice" - }, - "signatories": [ - "Alice" - ], - "contractId": "#52:0", - "templateId": "b10d22d6c2f2fae41b353315cf893ed66996ecb0abe4424ea6a81576918f658a:Iou:Iou" - } - ], - "status": 200 + "status": 200, + "result": } -Nonempty Response with Unknown Template IDs Warning ---------------------------------------------------- +Successful response with a warning, HTTP status: 200 OK +------------------------------------------------------- -.. code-block:: json +- Content-Type: ``application/json`` +- Content: + +.. code-block:: none { - "warnings": { - "unknownTemplateIds": ["UnknownModule:UnknownEntity"] - }, - "result": [ - { - "observers": [], - "agreementText": "", - "payload": { - "observers": [], - "issuer": "Alice", - "amount": "999.99", - "currency": "USD", - "owner": "Alice" - }, - "signatories": [ - "Alice" - ], - "contractId": "#52:0", - "templateId": "b10d22d6c2f2fae41b353315cf893ed66996ecb0abe4424ea6a81576918f658a:Iou:Iou" - } - ], - "status": 200 + "status": 200, + "result": , + "warnings": } -WebSocket ``/contracts/searchForever`` -====================================== +Failure, HTTP status: 400 | 401 | 404 | 500 +------------------------------------------- -List currently active contracts that match a given query, with -continuous updates. +- Content-Type: ``application/json`` +- Content: -Two subprotocols must be passed, as described in `Choosing a party -<#choosing-a-party>`__. +.. code-block:: none -application/json body must be sent first, formatted according to the -:doc:`search-query-language`:: + { + "status": <400 | 401 | 404 | 500>, + "errors": + } - {"templateIds": ["Iou:Iou"]} +Examples +-------- -output a series of JSON documents, each ``payload`` formatted according -to :doc:`lf-value-specification`:: +.. code-block:: none - [{ - "created": { - "observers": [], - "agreementText": "", - "payload": { - "observers": [], - "issuer": "Alice", - "amount": "999.99", - "currency": "USD", - "owner": "Alice" - }, - "signatories": ["Alice"], - "contractId": "#1:0", - "templateId": "6cc82609ede6576e5092e8c89a2c7a658efe15ae1347fc38eb316f787fa132bc:Iou:Iou" - } - }] + {"status": 200, result = [{"templateId": "....", "moduleName" : "...", …}, … {....}]} -To keep the stream alive, you'll occasionally see messages like this, -which can be safely ignored:: +.. code-block:: none - {"heartbeat": "ping"} + {"status": 200, result = [...], "warnings": {"unknownTemplateIds": ["UnknownModule:UnknownEntity"]}} -After submitting an ``Iou_Split`` exercise, which creates two contracts -and archives the one above, the same stream will eventually produce:: +.. code-block:: json - [{ - "created": { - "observers": [], - "agreementText": "", - "payload": { - "observers": [], - "issuer": "Alice", - "amount": "42.42", - "currency": "USD", - "owner": "Alice" - }, - "signatories": ["Alice"], - "contractId": "#2:1", - "templateId": "6cc82609ede6576e5092e8c89a2c7a658efe15ae1347fc38eb316f787fa132bc:Iou:Iou" - } - }, { - "created": { - "observers": [], - "agreementText": "", - "payload": { - "observers": [], - "issuer": "Alice", - "amount": "957.57", - "currency": "USD", - "owner": "Alice" - }, - "signatories": ["Alice"], - "contractId": "#2:2", - "templateId": "6cc82609ede6576e5092e8c89a2c7a658efe15ae1347fc38eb316f787fa132bc:Iou:Iou" - } - }, { - "archived": "#1:0" - }] + {"status": 401, "errors": ["Authentication Required"]} -Aside from ``"created"`` and ``"archived"`` elements, ``"error"`` -elements may appear, which contain a string describing the error. The -stream will continue in these cases, rather than terminating. +.. code-block:: json -Some notes on behavior: + {"status": 400, "errors": ["JSON parser error: Unexpected character 'f' at input index 27 (line 1, position 28)"]} -1. Each result array means "this is what would have changed if you just - polled ``/contracts/search`` iteratively." In particular, just as - polling search can "miss" contracts (as a create and archive can be - paired between polls), such contracts may or may not appear in any - result object. +.. code-block:: json -2. No ``archived`` ever contains a contract ID occurring within an - ``created`` in the same array. So, for example, supposing you are - keeping an internal map of active contracts, you can apply the - ``created`` first or the ``archived`` first and be guaranteed to get - the same results. + {"status": 500, "errors": ["Cannot initialize Ledger API"]} -3. You will almost certainly receive contract IDs in ``archived`` that - you never received a ``created`` for. These are contracts that - query filtered out, but for which the server no longer is aware of - that. You can safely ignore these. However, such "phantom archives" - *are* guaranteed to represent an actual archival *on the ledger*, so - if you are keeping a more global dataset outside the context of this - specific search, you can use that archival information as you wish. +Create a new Contract +===================== -4. Within a single response array, the order of ``created`` and - ``archived`` is undefined and does not imply that any element - occurred "before" or "after" any other one. As specified in note #2, - order of application of changes doesn't matter; you will get the same - results if you walk the array forwards, backwards, or in random - order. +See the request documentation below on how to create an instance of ``Iou`` contract from the :doc:`Quickstart guide `: -POST ``/command/create`` -======================== +.. literalinclude:: ../getting-started/quickstart/template-root/daml/Iou.daml + :language: daml + :lines: 9-15 -Create a contract. +.. _create-request: -Request -------- +HTTP Request +------------ -application/json body, ``argument`` formatted according to :doc:`lf-value-specification`: +- URL: ``/command/create`` +- Method: ``POST`` +- Content-Type: ``application/json`` +- Content: .. code-block:: json { "templateId": "Iou:Iou", "argument": { - "observers": [], "issuer": "Alice", - "amount": "999.99", + "owner": "Alice", "currency": "USD", - "owner": "Alice" + "amount": "999.99", + "observers": [] } } -Response --------- +Where: + +- ``templateId`` is the contract template identifier, which can be formatted as either: + + + ``"::"`` or + + ``":"`` if contract template can be uniquely identified by it's module and entity name. + +- ``argument`` field contains contract fields as defined in the DAML template and formatted according to :doc:`lf-value-specification`. + +.. _create-response: + +HTTP Response +------------- + +- Content-Type: ``application/json`` +- Content: .. code-block:: json @@ -411,18 +343,59 @@ Response "templateId": "11c8f3ace75868d28136adc5cfc1de265a9ee5ad73fe8f2db97510e3631096a2:Iou:Iou" } } + +Where: + +- ``status`` field matches the HTTP response status code returned in the HTTP header, +- ``result`` field contains created contract details. Keep in mind that ``templateId`` in the **JSON API** response is always fully qualified (always contains package ID). + +.. _create-request-with-meta: + +Create a new Contract with optional meta field +============================================== + +When creating a new contract, client may specify an optional ``meta`` field: + +.. code-block:: json + + { + "templateId": "Iou:Iou", + "argument": { + "observers": [], + "issuer": "Alice", + "amount": "999.99", + "currency": "USD", + "owner": "Alice" + }, + "meta": { + "commandId": "a unique ID", + "ledgerEffectiveTime": 1579730994499, + "maximumRecordTime": 1579731004499 + } + } + +Where: + +- ``commandId`` -- optional field, a unique string identifying the command; +- ``ledgerEffectiveTime`` -- optional field, the number of milliseconds from the epoch of ``1970-01-01T00:00:00Z``, an approximation of the wall clock time on the ledger server; +- ``maximumRecordTime`` -- optional field, the number of milliseconds from the epoch of ``1970-01-01T00:00:00Z``, a deadline for observing this command in the completion stream before it can be considered to have timed out. -POST ``/command/exercise`` -========================== +Exercise by Contract ID +======================= -Exercise a choice on a contract. +The JSON command below, demonstrates how to exercise ``Iou_Transfer`` choice on ``Iou`` contract: -Exercise by ContractId Request ------------------------------- +.. literalinclude:: ../getting-started/quickstart/template-root/daml/Iou.daml + :language: daml + :lines: 23, 52-55 -``"contractId": "#124:0"`` is the value from the create output. +HTTP Request +------------ -application/json body: +- URL: ``/command/exercise`` +- Method: ``POST`` +- Content-Type: ``application/json`` +- Content: .. code-block:: json @@ -435,23 +408,20 @@ application/json body: } } -Exercise by Contract Key Request --------------------------------- +Where: -application/json body: +- ``templateId`` -- contract template identifier, same as in :ref:`create request `, +- ``contractId`` -- contract identifier, the value from the :ref:`create response `, +- ``choice`` -- DAML contract choice, that is being exercised, +- ``argument`` -- contract choice argument(s). -.. code-block:: json - - { - "templateId": "Account:Account", - "key": ["Alice", "abc123"], - "choice": "Archive", - "argument": {} - } +.. _exercise-response: +HTTP Response +------------- -Response --------- +- Content-Type: ``application/json`` +- Content: .. code-block:: json @@ -493,35 +463,71 @@ Response Where: -- ``exerciseResult`` -- the return value of the exercised contract choice. -- ``contracts`` -- an array containing contracts that were archived and created as part of the exercised choice. The array may contain: **zero or many** ``{"archived": {...}}`` and **zero or many** ``{"created": {...}}`` elements. The order of the contracts is the same as on the ledger. +- ``status`` field matches the HTTP response status code returned in the HTTP header, -GET ``/parties`` -================ +- ``result`` field contains contract choice execution details: -Response --------- + + ``exerciseResult`` field contains the return value of the exercised contract choice, + + ``contracts`` contains an array of contracts that were archived and created as part of the choice execution. The array may contain: **zero or many** ``{"archived": {...}}`` and **zero or many** ``{"created": {...}}`` elements. The order of the contracts is the same as on the ledger. + + +Exercise by Contract Key +======================== + +The JSON command below, demonstrates how to exercise ``Archive`` choice on ``Account`` contract with a ``(Party, Text)`` key defined like this: + +.. code-block:: daml + + template Account with + owner : Party + number : Text + status : AccountStatus + where + signatory owner + key (owner, number) : (Party, Text) + maintainer key._1 + + +HTT Request +----------- + +- URL: ``/command/exercise`` +- Method: ``POST`` +- Content-Type: ``application/json`` +- Content: .. code-block:: json { - "status": 200, - "result": [ - { - "party": "Alice", - "isLocal": true - } - ] + "templateId": "Account:Account", + "key": ["Alice", "abc123"], + "choice": "Archive", + "argument": {} } -POST ``/contracts/lookup`` -========================== +Where: + +- ``templateId`` -- contract template identifier, same as in :ref:`create request `, +- ``key`` -- contract key, formatted according to the :doc:`lf-value-specification`, +- ``choice`` -- DAML contract choice, that is being exercised, +- ``argument`` -- contract choice argument(s), empty, because ``Archive`` does not take any. + +HTTP Response +------------- -Lookup by Contract ID ---------------------- +Formatted similar to :ref:`Exercise by Contract ID response `. -Request -~~~~~~~ + +Fetch Contract by Contract ID +============================== + +HTTP Request +------------ + +- URL: ``/contracts/lookup`` +- Method: ``POST`` +- Content-Type: ``application/json`` +- Content: application/json body: @@ -531,8 +537,11 @@ application/json body: "contractId": "#201:1" } -Contract Not Found Response -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Contract Not Found HTTP Response +-------------------------------- + +- Content-Type: ``application/json`` +- Content: .. code-block:: json @@ -541,8 +550,11 @@ Contract Not Found Response "result": null } -Contract Found Response -~~~~~~~~~~~~~~~~~~~~~~~ +Contract Found HTTP Response +---------------------------- + +- Content-Type: ``application/json`` +- Content: .. code-block:: json @@ -569,26 +581,29 @@ Contract Found Response } } -Lookup by Contract Key ----------------------- +Fetch Contract by Key +============================== -Request -~~~~~~~ +HTTP Request +------------ -application/json body: +- URL: ``/contracts/lookup`` +- Method: ``POST`` +- Content-Type: ``application/json`` +- Content: .. code-block:: json { "templateId": "Account:Account", - "key": [ - "Alice", - "abc123" - ] + "key": ["Alice", "abc123"] } -Contract Not Found Response -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Contract Not Found HTTP Response +-------------------------------- + +- Content-Type: ``application/json`` +- Content: .. code-block:: json @@ -597,8 +612,11 @@ Contract Not Found Response "result": null } -Contract Found Response -~~~~~~~~~~~~~~~~~~~~~~~ +Contract Found HTTP Response +---------------------------- + +- Content-Type: ``application/json`` +- Content: .. code-block:: json @@ -626,3 +644,263 @@ Contract Found Response "templateId": "11c8f3ace75868d28136adc5cfc1de265a9ee5ad73fe8f2db97510e3631096a2:Account:Account" } } + + +Contract Search, All Templates +============================== + +List all currently active contracts for all known templates. + +Note that the retrieved contracts do not get persisted into query store database. Query store is a search index and can be used to optimize search latency. See :ref:`Start HTTP service ` for information on how to start JSON API service with query store enabled. + +HTTP Request +------------ + +- URL: ``/contracts/search`` +- Method: ``GET`` +- Content: + +HTTP Response +------------- + +The response is the same as for the POST method below. + +Contract Search +=============== + +List currently active contracts that match a given query. + +HTTP Request +------------ + +- URL: ``/contracts/search`` +- Method: ``POST`` +- Content-Type: ``application/json`` +- Content: + +.. code-block:: json + + { + "templateIds": ["Iou:Iou"], + "query": {"amount": 999.99} + } + +Where: + +- ``templateIds`` -- an array of contract template identifiers to search through, +- ``query`` -- search criteria to apply to the specified ``templateIds``, formatted according to the :doc:`search-query-language`: + +Empty HTTP Response +------------------- + +- Content-Type: ``application/json`` +- Content: + +.. code-block:: json + + { + "status": 200, + "result": [] + } + +Nonempty HTTP Response +----------------------- + +- Content-Type: ``application/json`` +- Content: + +.. code-block:: json + + { + "result": [ + { + "observers": [], + "agreementText": "", + "payload": { + "observers": [], + "issuer": "Alice", + "amount": "999.99", + "currency": "USD", + "owner": "Alice" + }, + "signatories": [ + "Alice" + ], + "contractId": "#52:0", + "templateId": "b10d22d6c2f2fae41b353315cf893ed66996ecb0abe4424ea6a81576918f658a:Iou:Iou" + } + ], + "status": 200 + } + +Where + +- ``result`` contains an array of contracts, each contract formatted according to :doc:`lf-value-specification`, +- ``status`` matches the HTTP status code returned in the HTTP header, + +Nonempty HTTP Response with Unknown Template IDs Warning +-------------------------------------------------------- + +- Content-Type: ``application/json`` +- Content: + +.. code-block:: json + + { + "warnings": { + "unknownTemplateIds": ["UnknownModule:UnknownEntity"] + }, + "result": [ + { + "observers": [], + "agreementText": "", + "payload": { + "observers": [], + "issuer": "Alice", + "amount": "999.99", + "currency": "USD", + "owner": "Alice" + }, + "signatories": [ + "Alice" + ], + "contractId": "#52:0", + "templateId": "b10d22d6c2f2fae41b353315cf893ed66996ecb0abe4424ea6a81576918f658a:Iou:Iou" + } + ], + "status": 200 + } + +WebSocket ``/contracts/searchForever`` +====================================== + +List currently active contracts that match a given query, with +continuous updates. + +Two subprotocols must be passed, as described in `Choosing a party +<#choosing-a-party>`__. + +application/json body must be sent first, formatted according to the +:doc:`search-query-language`:: + + {"templateIds": ["Iou:Iou"]} + +output a series of JSON documents, each ``payload`` formatted according +to :doc:`lf-value-specification`:: + + [{ + "created": { + "observers": [], + "agreementText": "", + "payload": { + "observers": [], + "issuer": "Alice", + "amount": "999.99", + "currency": "USD", + "owner": "Alice" + }, + "signatories": ["Alice"], + "contractId": "#1:0", + "templateId": "6cc82609ede6576e5092e8c89a2c7a658efe15ae1347fc38eb316f787fa132bc:Iou:Iou" + } + }] + +To keep the stream alive, you'll occasionally see messages like this, +which can be safely ignored:: + + {"heartbeat": "ping"} + +After submitting an ``Iou_Split`` exercise, which creates two contracts +and archives the one above, the same stream will eventually produce:: + + [{ + "created": { + "observers": [], + "agreementText": "", + "payload": { + "observers": [], + "issuer": "Alice", + "amount": "42.42", + "currency": "USD", + "owner": "Alice" + }, + "signatories": ["Alice"], + "contractId": "#2:1", + "templateId": "6cc82609ede6576e5092e8c89a2c7a658efe15ae1347fc38eb316f787fa132bc:Iou:Iou" + } + }, { + "created": { + "observers": [], + "agreementText": "", + "payload": { + "observers": [], + "issuer": "Alice", + "amount": "957.57", + "currency": "USD", + "owner": "Alice" + }, + "signatories": ["Alice"], + "contractId": "#2:2", + "templateId": "6cc82609ede6576e5092e8c89a2c7a658efe15ae1347fc38eb316f787fa132bc:Iou:Iou" + } + }, { + "archived": "#1:0" + }] + +Aside from ``"created"`` and ``"archived"`` elements, ``"error"`` +elements may appear, which contain a string describing the error. The +stream will continue in these cases, rather than terminating. + +Some notes on behavior: + +1. Each result array means "this is what would have changed if you just + polled ``/contracts/search`` iteratively." In particular, just as + polling search can "miss" contracts (as a create and archive can be + paired between polls), such contracts may or may not appear in any + result object. + +2. No ``archived`` ever contains a contract ID occurring within an + ``created`` in the same array. So, for example, supposing you are + keeping an internal map of active contracts, you can apply the + ``created`` first or the ``archived`` first and be guaranteed to get + the same results. + +3. You will almost certainly receive contract IDs in ``archived`` that + you never received a ``created`` for. These are contracts that + query filtered out, but for which the server no longer is aware of + that. You can safely ignore these. However, such "phantom archives" + *are* guaranteed to represent an actual archival *on the ledger*, so + if you are keeping a more global dataset outside the context of this + specific search, you can use that archival information as you wish. + +4. Within a single response array, the order of ``created`` and + ``archived`` is undefined and does not imply that any element + occurred "before" or "after" any other one. As specified in note #2, + order of application of changes doesn't matter; you will get the same + results if you walk the array forwards, backwards, or in random + order. + +Fetch All Known Parties +======================= + +- URL: ``/parties`` +- Method: ``GET`` +- Content: + +HTTP Response +------------- + +- Content-Type: ``application/json`` +- Content: + +.. code-block:: json + + { + "status": 200, + "result": [ + { + "party": "Alice", + "isLocal": true + } + ] + } diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/Config.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/Config.scala index 24d68ef2e7e9..8ed4dd813b3e 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/Config.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/Config.scala @@ -103,10 +103,10 @@ private[http] object JdbcConfig extends ConfigCompanion[JdbcConfig]("JdbcConfig" lazy val help: String = "Contains comma-separated key-value pairs. Where:\n" + - "\tdriver -- JDBC driver class name,\n" + - "\turl -- JDBC connection URL,\n" + + "\tdriver -- JDBC driver class name, only org.postgresql.Driver supported right now,\n" + + "\turl -- JDBC connection URL, only jdbc:postgresql supported right now,\n" + "\tuser -- database user name,\n" + - "\tpassword -- database user password\n" + + "\tpassword -- database user password,\n" + "\tcreateSchema -- boolean flag, if set to true, the process will re-create database schema and terminate immediately.\n" + "\tExample: " + helpString( "org.postgresql.Driver", diff --git a/ledger-service/http-json/src/main/scala/com/digitalasset/http/Main.scala b/ledger-service/http-json/src/main/scala/com/digitalasset/http/Main.scala index 1299e59835e5..c68efb6be2a6 100644 --- a/ledger-service/http-json/src/main/scala/com/digitalasset/http/Main.scala +++ b/ledger-service/http-json/src/main/scala/com/digitalasset/http/Main.scala @@ -181,7 +181,9 @@ object Main extends StrictLogging { .validate(JdbcConfig.validate) .optional() .valueName(JdbcConfig.usage) - .text(s"Optional query store JDBC configuration string. " + JdbcConfig.help) + .text(s"Optional query store JDBC configuration string." + + " Query store is a search index, use it if you need to query large active contract sets. " + + JdbcConfig.help) opt[Map[String, String]]("static-content") .action((x, c) => c.copy(staticContentConfig = Some(StaticContentConfig.createUnsafe(x))))