mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 09:17:43 +03:00
in query argument, rename %templates to templateIds, and nest query under 'query' field (#4082)
* in query argument, rename %templates to templateIds, and nest query under 'query' field CHANGELOG_BEGIN - [JSON API - Experimental] In 'search' endpoint arguments, %templates is now templateIds. Additionally, all contract query fields must occur under 'query'. See `issue #3450 <https://github.com/digital-asset/daml/issues/3450>`__. CHANGELOG_END * fix other old query format usages
This commit is contained in:
parent
fa8a92f772
commit
0520fdfa84
@ -180,8 +180,8 @@ application/json body, formatted according to the :doc:`search-query-language`:
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"%templates": ["Iou:Iou"],
|
||||
"amount": 999.99
|
||||
"templateIds": ["Iou:Iou"],
|
||||
"query": {"amount": 999.99}
|
||||
}
|
||||
|
||||
Empty Response
|
||||
@ -265,7 +265,7 @@ Two subprotocols must be passed, as described in `Choosing a party
|
||||
application/json body must be sent first, formatted according to the
|
||||
:doc:`search-query-language`::
|
||||
|
||||
{"%templates": ["Iou:Iou"]}
|
||||
{"templateIds": ["Iou:Iou"]}
|
||||
|
||||
output a series of JSON documents, each ``argument`` formatted according
|
||||
to :doc:`lf-value-specification`::
|
||||
|
@ -6,8 +6,8 @@
|
||||
|
||||
The body of ``POST /contracts/search`` looks like so::
|
||||
|
||||
{"%templates": [...template IDs...],
|
||||
...other query elements...}
|
||||
{"templateIds": [...template IDs...],
|
||||
"query": {...query elements...}}
|
||||
|
||||
The elements of that query are defined here.
|
||||
|
||||
@ -108,10 +108,10 @@ For these reasons, as with LF value input via JSON, queries written in
|
||||
JSON are also always interpreted with respect to some specified LF types
|
||||
(e.g. template IDs). For example::
|
||||
|
||||
{"%templates": [{"moduleName": "Foo", "entityName": "A"},
|
||||
{"moduleName": "Foo", "entityName": "B"},
|
||||
{"moduleName": "Foo", "entityName": "C"}],
|
||||
"foo": "bar"}
|
||||
{"templateIds": [{"moduleName": "Foo", "entityName": "A"},
|
||||
{"moduleName": "Foo", "entityName": "B"},
|
||||
{"moduleName": "Foo", "entityName": "C"}],
|
||||
"query": {"foo": "bar"}}
|
||||
|
||||
will treat ``"foo"`` as a field equality query for A and B, and
|
||||
(supposing templates' associated data types were permitted to be
|
||||
|
@ -127,7 +127,7 @@ class Ledger {
|
||||
* for a description of the query language.
|
||||
*/
|
||||
async query<T extends object, K>(template: Template<T, K>, query: Query<T>): Promise<CreateEvent<T, K>[]> {
|
||||
const payload = {"%templates": [template.templateId], ...query};
|
||||
const payload = {templateIds: [template.templateId], query};
|
||||
const json = await this.submit('contracts/search', payload);
|
||||
return jtv.Result.withException(jtv.array(decodeCreateEvent(template)).run(json));
|
||||
}
|
||||
|
@ -196,17 +196,28 @@ object JsonProtocol extends DefaultJsonProtocol {
|
||||
implicit val ArchivedContractFormat: RootJsonFormat[domain.ArchivedContract] =
|
||||
jsonFormat2(domain.ArchivedContract.apply)
|
||||
|
||||
private val templatesKey = "%templates"
|
||||
|
||||
/** Derived from autogenerated with 3 extra features:
|
||||
* 1. template IDs are required
|
||||
* 2. query key may be absent
|
||||
* 3. special error if you appear to have queried outside 'query'
|
||||
*/
|
||||
implicit val GetActiveContractsRequestFormat: RootJsonReader[domain.GetActiveContractsRequest] = {
|
||||
case JsObject(fields) =>
|
||||
val templates = (fields get templatesKey)
|
||||
.map(_.convertTo[Set[domain.TemplateId.OptionalPkg]])
|
||||
.filter(_.nonEmpty)
|
||||
.getOrElse(
|
||||
deserializationError("/contracts/search requires at least one item in '%templates'"))
|
||||
domain.GetActiveContractsRequest(templateIds = templates, query = fields - templatesKey)
|
||||
case _ => deserializationError("/contracts/search must receive an object")
|
||||
case class GACR(
|
||||
templateIds: Set[domain.TemplateId.OptionalPkg],
|
||||
query: Option[Map[String, JsValue]])
|
||||
val validKeys = Set("templateIds", "query")
|
||||
implicit val primitive: JsonReader[GACR] = jsonFormat2(GACR.apply)
|
||||
jsv =>
|
||||
{
|
||||
val GACR(tids, q) = jsv.convertTo[GACR]
|
||||
val extras = jsv.asJsObject.fields.keySet diff validKeys
|
||||
if (tids.isEmpty)
|
||||
deserializationError("search requires at least one item in 'templateIds'")
|
||||
else if (extras.nonEmpty)
|
||||
deserializationError(
|
||||
s"unsupported query fields ${extras}; likely should be within 'query' subobject")
|
||||
domain.GetActiveContractsRequest(tids, q getOrElse Map.empty)
|
||||
}
|
||||
}
|
||||
|
||||
implicit val CommandMetaFormat: RootJsonFormat[domain.CommandMeta] = jsonFormat3(
|
||||
|
@ -143,7 +143,7 @@ abstract class AbstractHttpServiceIntegrationTest
|
||||
"contracts/search POST with empty query" in withHttpService { (uri, encoder, _) =>
|
||||
searchWithQuery(
|
||||
searchDataSet,
|
||||
jsObject("""{"%templates": ["Iou:Iou"]}"""),
|
||||
jsObject("""{"templateIds": ["Iou:Iou"]}"""),
|
||||
uri,
|
||||
encoder
|
||||
).map { acl: List[domain.ActiveContract[JsValue]] =>
|
||||
@ -154,7 +154,7 @@ abstract class AbstractHttpServiceIntegrationTest
|
||||
"contracts/search with query, one field" in withHttpService { (uri, encoder, _) =>
|
||||
searchWithQuery(
|
||||
searchDataSet,
|
||||
jsObject("""{"%templates": ["Iou:Iou"], "currency": "EUR"}"""),
|
||||
jsObject("""{"templateIds": ["Iou:Iou"], "query": {"currency": "EUR"}}"""),
|
||||
uri,
|
||||
encoder
|
||||
).map { acl: List[domain.ActiveContract[JsValue]] =>
|
||||
@ -165,7 +165,8 @@ abstract class AbstractHttpServiceIntegrationTest
|
||||
|
||||
"contracts/search returns unknown Template IDs as warnings" in withHttpService { (uri, _, _) =>
|
||||
val query =
|
||||
jsObject("""{"%templates": ["Iou:Iou", "UnknownModule:UnknownEntity"], "currency": "EUR"}""")
|
||||
jsObject(
|
||||
"""{"templateIds": ["Iou:Iou", "UnknownModule:UnknownEntity"], "query": {"currency": "EUR"}}""")
|
||||
|
||||
postJsonRequest(uri.withPath(Uri.Path("/contracts/search")), query)
|
||||
.map {
|
||||
@ -186,8 +187,10 @@ abstract class AbstractHttpServiceIntegrationTest
|
||||
rs: List[(StatusCode, JsValue)] =>
|
||||
rs.map(_._1) shouldBe List.fill(searchDataSet.size)(StatusCodes.OK)
|
||||
|
||||
val queryAmountAsString = jsObject("""{"%templates": ["Iou:Iou"], "amount": "111.11"}""")
|
||||
val queryAmountAsNumber = jsObject("""{"%templates": ["Iou:Iou"], "amount": 111.11}""")
|
||||
def queryAmountAs(s: String) =
|
||||
jsObject(s"""{"templateIds": ["Iou:Iou"], "query": {"amount": $s}}""")
|
||||
val queryAmountAsString = queryAmountAs("\"111.11\"")
|
||||
val queryAmountAsNumber = queryAmountAs("111.11")
|
||||
|
||||
List(
|
||||
postJsonRequest(uri.withPath(Uri.Path("/contracts/search")), queryAmountAsString),
|
||||
@ -212,7 +215,8 @@ abstract class AbstractHttpServiceIntegrationTest
|
||||
"contracts/search with query, two fields" in withHttpService { (uri, encoder, _) =>
|
||||
searchWithQuery(
|
||||
searchDataSet,
|
||||
jsObject("""{"%templates": ["Iou:Iou"], "currency": "EUR", "amount": "111.11"}"""),
|
||||
jsObject(
|
||||
"""{"templateIds": ["Iou:Iou"], "query": {"currency": "EUR", "amount": "111.11"}}"""),
|
||||
uri,
|
||||
encoder).map { acl: List[domain.ActiveContract[JsValue]] =>
|
||||
acl.size shouldBe 1
|
||||
@ -224,7 +228,8 @@ abstract class AbstractHttpServiceIntegrationTest
|
||||
"contracts/search with query, no results" in withHttpService { (uri, encoder, _) =>
|
||||
searchWithQuery(
|
||||
searchDataSet,
|
||||
jsObject("""{"%templates": ["Iou:Iou"], "currency": "RUB", "amount": "666.66"}"""),
|
||||
jsObject(
|
||||
"""{"templateIds": ["Iou:Iou"], "query": {"currency": "RUB", "amount": "666.66"}}"""),
|
||||
uri,
|
||||
encoder
|
||||
).map { acl: List[domain.ActiveContract[JsValue]] =>
|
||||
@ -653,9 +658,11 @@ abstract class AbstractHttpServiceIntegrationTest
|
||||
val contractId: ContractId = getContractId(getResult(output))
|
||||
|
||||
val query = jsObject(s"""{
|
||||
"%templates": ["$packageId:Account:Account"],
|
||||
"number" : "abc123",
|
||||
"status" : {"tag": "Enabled", "value": "${nowStr: String}"}
|
||||
"templateIds": ["$packageId:Account:Account"],
|
||||
"query": {
|
||||
"number" : "abc123",
|
||||
"status" : {"tag": "Enabled", "value": "${nowStr: String}"}
|
||||
}
|
||||
}""")
|
||||
|
||||
postJsonRequest(uri.withPath(Uri.Path("/contracts/search")), query).map {
|
||||
|
@ -35,7 +35,7 @@ class HttpServiceWithPostgresIntTest
|
||||
"contracts/search persists all active contracts" in withHttpService { (uri, encoder, _) =>
|
||||
searchWithQuery(
|
||||
searchDataSet,
|
||||
jsObject("""{"%templates": ["Iou:Iou"], "currency": "EUR"}"""),
|
||||
jsObject("""{"templateIds": ["Iou:Iou"], "query": {"currency": "EUR"}}"""),
|
||||
uri,
|
||||
encoder
|
||||
).flatMap { searchResult: List[domain.ActiveContract[JsValue]] =>
|
||||
|
@ -83,7 +83,7 @@ class WebsocketServiceIntegrationTest
|
||||
subprotocol = validSubprotocol))
|
||||
|
||||
clientMsg <- Source
|
||||
.single(TextMessage("""{"%templates": ["Iou:Iou"]}"""))
|
||||
.single(TextMessage("""{"templateIds": ["Iou:Iou"]}"""))
|
||||
.via(webSocketFlow)
|
||||
.runWith(collectResultsAsRawString)
|
||||
} yield
|
||||
@ -148,7 +148,7 @@ class WebsocketServiceIntegrationTest
|
||||
subprotocol = validSubprotocol))
|
||||
|
||||
val query =
|
||||
TextMessage.Strict("""{"%templates": ["Iou:Iou"]}""")
|
||||
TextMessage.Strict("""{"templateIds": ["Iou:Iou"]}""")
|
||||
|
||||
val parseResp: Flow[Message, JsValue, NotUsed] =
|
||||
Flow[Message]
|
||||
|
Loading…
Reference in New Issue
Block a user