better JsNull check for Oracle; test scenarios for nested cases (#15022)

* a couple comparison query scenarios with nested position
* same JSON null test, but different
- 'gt string' (scenario 0) fails without this change
- 'gt int' (scenario 1) succeeds regardless of this change

CHANGELOG_BEGIN
- [JSON API with Oracle] Fix some nested queries that were returning no
  data.  DisableContractPayloadIndexing must be set to true to take
  advantage of this fix.
CHANGELOG_END
This commit is contained in:
Stephen Compall 2022-09-21 06:21:35 -04:00 committed by GitHub
parent 5a3cb468f8
commit 77672d2896
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 113 additions and 6 deletions

View File

@ -987,7 +987,7 @@ private final class OracleQueries(
private[http] override def containsAtContractPath(path: JsonPath, literal: JsValue) = {
def ensureNotNull = {
// we are only trying to reject None for an Optional record/variant/list
val pred: Cord = ('$' -: pathSteps(path)) ++ "?(@ != null)"
val pred: Cord = ('$' -: pathSteps(path)) ++ "?(!(@ == null))"
sql"JSON_EXISTS($contractColumnName, ${oracleShortPathEscape(pred)})"
}
literal match {

View File

@ -327,6 +327,7 @@ alias(
"//ledger-service/http-json-testing:{}".format(edition),
"//ledger-service/db-backend",
"//ledger-service/jwt",
"//ledger-service/lf-value-json",
"//ledger-service/utils",
"//ledger/ledger-api-auth",
"//ledger/ledger-api-common",

View File

@ -526,6 +526,80 @@ abstract class AbstractHttpServiceIntegrationTestTokenIndependent
}): Future[Assertion]
}
}
"nested comparison filters" onlyIfLargeQueries_- {
import shapeless.Coproduct, shapeless.syntax.singleton._
val irrelevant = Ref.Identifier assertFromString "none:Discarded:Identifier"
val (_, bazRecordVA) = VA.record(irrelevant, ShRecord(baz = VA.text))
val (_, fooVA) =
VA.variant(irrelevant, ShRecord(Bar = VA.int64, Baz = bazRecordVA, Qux = VA.unit))
val fooVariant = Coproduct[fooVA.Inj]
val (_, kbvarVA) = VA.record(
irrelevant,
ShRecord(
name = VA.text,
party = VAx.partyDomain,
age = VA.int64,
fooVariant = fooVA,
bazRecord = bazRecordVA,
),
)
def withBazRecord(bazRecord: VA.text.Inj)(p: domain.Party): kbvarVA.Inj =
ShRecord(
name = "ABC DEF",
party = p,
age = 123L,
fooVariant = fooVariant(Symbol("Bar") ->> 42L),
bazRecord = ShRecord(baz = bazRecord),
)
def withFooVariant(v: VA.int64.Inj)(p: domain.Party): kbvarVA.Inj =
ShRecord(
name = "ABC DEF",
party = p,
age = 123L,
fooVariant = fooVariant(Symbol("Bar") ->> v),
bazRecord = ShRecord(baz = "another baz value"),
)
val kbvarId = TpId.Account.KeyedByVariantAndRecord
import FilterDiscriminatorScenario.Scenario
Seq(
Scenario(
"gt string",
kbvarId,
kbvarVA,
Map("bazRecord" -> Map("baz" -> Map("%gt" -> "b")).toJson),
)(
withBazRecord("c"),
withBazRecord("a"),
),
Scenario(
"gt int",
kbvarId,
kbvarVA,
Map("fooVariant" -> Map("tag" -> "Bar".toJson, "value" -> Map("%gt" -> 2).toJson).toJson),
)(withFooVariant(3), withFooVariant(1)),
).zipWithIndex.foreach { case (scenario, ix) =>
import scenario._
s"$label (scenario $ix)" in withHttpService { fixture =>
for {
(alice, headers) <- fixture.getUniquePartyAndAuthHeaders("Alice")
contracts <- searchExpectOk(
List(matches, doesNotMatch).map { payload =>
domain.CreateCommand(ctId, argToApi(va)(payload(alice)), None)
},
JsObject(Map("templateIds" -> Seq(ctId).toJson, "query" -> query.toJson)),
fixture,
headers,
)
} yield contracts.map(_.payload) should contain theSameElementsAs Seq(
LfValueCodec.apiValueToJsValue(va.inj(matches(alice)))
)
}
}
}
}
"query with invalid JSON query should return error" in withHttpService { fixture =>

View File

@ -414,6 +414,7 @@ trait AbstractHttpServiceIntegrationTestFuns
}
object Account {
val Account: TId = CtId.Template(None, "Account", "Account")
val KeyedByVariantAndRecord: TId = CtId.Template(None, "Account", "KeyedByVariantAndRecord")
}
object User {
val User: Id = domain.TemplateId(None, "User", "User")

View File

@ -0,0 +1,31 @@
// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.http
import com.daml.lf.value.test.TypedValueGenerators.{ValueAddend => VA}
import spray.json.JsValue
/** A query, a value that matches the query, and a value that doesn't match.
*/
class FilterDiscriminatorScenario[Inj](
val label: String,
val ctId: domain.ContractTypeId.OptionalPkg,
val va: VA.Aux[Inj],
val query: Map[String, JsValue],
val matches: domain.Party => Inj,
val doesNotMatch: domain.Party => Inj,
)
object FilterDiscriminatorScenario {
def Scenario(
label: String,
ctId: domain.ContractTypeId.OptionalPkg,
va: VA,
query: Map[String, JsValue],
)(
matches: domain.Party => va.Inj,
doesNotMatch: domain.Party => va.Inj,
): FilterDiscriminatorScenario[va.Inj] =
new FilterDiscriminatorScenario(label, ctId, va, query, matches, doesNotMatch)
}

View File

@ -257,7 +257,7 @@ class ValuePredicateTest
"{}",
tuple3VA,
sql"payload @> ${"""{"foo":{}}""".parseJson}::jsonb",
sql"""JSON_EXISTS(payload, '$$."foo"?(@ != null)')""",
sql"""JSON_EXISTS(payload, '$$."foo"?(!(@ == null))')""",
),
(
"""{"%lte": 42}""",

View File

@ -5,7 +5,7 @@
- Updating the package service succeeds with sufficient authorization: [AuthorizationTest.scala](ledger-service/http-json/src/it/scala/http/AuthorizationTest.scala#L85)
- accept user tokens: [TestMiddleware.scala](triggers/service/auth/src/test/scala/com/daml/auth/middleware/oauth2/TestMiddleware.scala#L152)
- badly-authorized create is rejected: [AuthorizationSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/AuthorizationSpec.scala#L60)
- badly-authorized create is rejected: [AbstractHttpServiceIntegrationTest.scala](ledger-service/http-json/src/itlib/scala/http/AbstractHttpServiceIntegrationTest.scala#L1136)
- badly-authorized create is rejected: [AbstractHttpServiceIntegrationTest.scala](ledger-service/http-json/src/itlib/scala/http/AbstractHttpServiceIntegrationTest.scala#L1210)
- badly-authorized exercise is rejected: [AuthorizationSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/AuthorizationSpec.scala#L158)
- badly-authorized exercise/create (create is unauthorized) is rejected: [AuthPropagationSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/AuthPropagationSpec.scala#L273)
- badly-authorized exercise/create (exercise is unauthorized) is rejected: [AuthPropagationSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/AuthPropagationSpec.scala#L241)
@ -18,7 +18,7 @@
- create with no signatories is rejected: [AuthorizationSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/AuthorizationSpec.scala#L50)
- create with non-signatory maintainers is rejected: [AuthorizationSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/AuthorizationSpec.scala#L72)
- exercise with no controllers is rejected: [AuthorizationSpec.scala](daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/AuthorizationSpec.scala#L148)
- fetch fails when readAs not authed, even if prior fetch succeeded: [AbstractHttpServiceIntegrationTest.scala](ledger-service/http-json/src/itlib/scala/http/AbstractHttpServiceIntegrationTest.scala#L1204)
- fetch fails when readAs not authed, even if prior fetch succeeded: [AbstractHttpServiceIntegrationTest.scala](ledger-service/http-json/src/itlib/scala/http/AbstractHttpServiceIntegrationTest.scala#L1278)
- forbid a non-authorized party to check the status of a trigger: [TriggerServiceTest.scala](triggers/service/src/test/scala/com/digitalasset/daml/lf/engine/trigger/TriggerServiceTest.scala#L680)
- forbid a non-authorized party to list triggers: [TriggerServiceTest.scala](triggers/service/src/test/scala/com/digitalasset/daml/lf/engine/trigger/TriggerServiceTest.scala#L670)
- forbid a non-authorized party to start a trigger: [TriggerServiceTest.scala](triggers/service/src/test/scala/com/digitalasset/daml/lf/engine/trigger/TriggerServiceTest.scala#L659)
@ -26,7 +26,7 @@
- forbid a non-authorized user to upload a DAR: [TriggerServiceTest.scala](triggers/service/src/test/scala/com/digitalasset/daml/lf/engine/trigger/TriggerServiceTest.scala#L712)
- multiple websocket requests over the same WebSocket connection are NOT allowed: [AbstractWebsocketServiceIntegrationTest.scala](ledger-service/http-json/src/itlib/scala/http/AbstractWebsocketServiceIntegrationTest.scala#L130)
- refresh a token after expiry on the server side: [TriggerServiceTest.scala](triggers/service/src/test/scala/com/digitalasset/daml/lf/engine/trigger/TriggerServiceTest.scala#L737)
- reject requests with missing auth header: [AbstractHttpServiceIntegrationTest.scala](ledger-service/http-json/src/itlib/scala/http/AbstractHttpServiceIntegrationTest.scala#L573)
- reject requests with missing auth header: [AbstractHttpServiceIntegrationTest.scala](ledger-service/http-json/src/itlib/scala/http/AbstractHttpServiceIntegrationTest.scala#L647)
- request a fresh token after expiry on user request: [TriggerServiceTest.scala](triggers/service/src/test/scala/com/digitalasset/daml/lf/engine/trigger/TriggerServiceTest.scala#L722)
- return the token from a cookie: [TestMiddleware.scala](triggers/service/auth/src/test/scala/com/daml/auth/middleware/oauth2/TestMiddleware.scala#L96)
- return unauthorized on an expired token: [TestMiddleware.scala](triggers/service/auth/src/test/scala/com/daml/auth/middleware/oauth2/TestMiddleware.scala#L139)
@ -208,7 +208,7 @@
## Performance:
- Tail call optimization: Tail recursion does not blow the scala JVM stack.: [TailCallTest.scala](daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/TailCallTest.scala#L16)
- archiving a large number of contracts should succeed: [AbstractHttpServiceIntegrationTest.scala](ledger-service/http-json/src/itlib/scala/http/AbstractHttpServiceIntegrationTest.scala#L1410)
- archiving a large number of contracts should succeed: [AbstractHttpServiceIntegrationTest.scala](ledger-service/http-json/src/itlib/scala/http/AbstractHttpServiceIntegrationTest.scala#L1484)
- creating and listing 20K users should be possible: [HttpServiceIntegrationTestUserManagement.scala](ledger-service/http-json/src/it/scala/http/HttpServiceIntegrationTestUserManagement.scala#L558)
## Input Validation: