mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 01:07:18 +03:00
Improve error reporting when running DAML Script over the JSON API (#6481)
It is pretty easy to hit this, e.g., when your templates haven’t been uploaded to the ledger. Just printing the response doesn’t actually include the response body which means that you don’t see the actual error so it’s pretty useless. This PR changes that by printing the status code and the response body. All the rest is just test setup to be able to submit a script with a template that has not been uploaded to the ledger. changelog_begin changelog_end
This commit is contained in:
parent
6ffdb8d6bc
commit
b0eebba51b
@ -579,9 +579,13 @@ class JsonLedgerClient(
|
||||
getResponseDataBytes(resp).map(description =>
|
||||
Left(new StatusRuntimeException(Status.UNKNOWN.withDescription(description))))
|
||||
} else {
|
||||
// A non-500 failure is something like invalid JSON. In that case
|
||||
// the script runner is just broken so fail hard.
|
||||
Future.failed(new RuntimeException(s"Request failed: $resp"))
|
||||
// A non-500 failure is something like invalid JSON or “cannot resolve template ID”.
|
||||
// We don’t want to treat that failures as ones that can be caught
|
||||
// via `submitMustFail` so fail hard.
|
||||
getResponseDataBytes(resp).flatMap(
|
||||
description =>
|
||||
Future.failed(
|
||||
new RuntimeException(s"Request failed: $description, status code: ${resp.status}")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,39 @@ EOF
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
# A variant of script-test that has not been uploaded to the ledger
|
||||
# to test missing template ids. We only care that this has a different package id.
|
||||
genrule(
|
||||
name = "script-test-no-ledger",
|
||||
srcs =
|
||||
glob(["**/*.daml"]) + [
|
||||
"//daml-script/daml:daml-script.dar",
|
||||
"//docs:source/daml-script/template-root/src/ScriptExample.daml",
|
||||
],
|
||||
outs = ["script-test-no-ledger.dar"],
|
||||
cmd = """
|
||||
set -eou pipefail
|
||||
TMP_DIR=$$(mktemp -d)
|
||||
mkdir -p $$TMP_DIR/daml
|
||||
cp -L $(location :daml/ScriptTest.daml) $$TMP_DIR/daml
|
||||
cp -L $(location //daml-script/daml:daml-script.dar) $$TMP_DIR/
|
||||
cat << EOF > $$TMP_DIR/daml.yaml
|
||||
sdk-version: {sdk}
|
||||
name: script-test-no-ledger
|
||||
source: daml
|
||||
version: 0.0.2
|
||||
dependencies:
|
||||
- daml-stdlib
|
||||
- daml-prim
|
||||
- daml-script.dar
|
||||
EOF
|
||||
$(location //compiler/damlc) build --project-root=$$TMP_DIR -o $$PWD/$(location script-test-no-ledger.dar)
|
||||
rm -rf $$TMP_DIR
|
||||
""".format(sdk = sdk_version),
|
||||
tools = ["//compiler/damlc"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
da_scala_library(
|
||||
name = "test-lib",
|
||||
srcs = glob(
|
||||
@ -95,7 +128,10 @@ da_scala_test_suite(
|
||||
srcs = [
|
||||
"src/com/digitalasset/daml/lf/engine/script/test/JsonApiIt.scala",
|
||||
],
|
||||
data = [":script-test.dar"],
|
||||
data = [
|
||||
":script-test.dar",
|
||||
":script-test-no-ledger.dar",
|
||||
],
|
||||
resources = glob(["src/main/resources/**/*"]),
|
||||
deps = [
|
||||
":test-lib",
|
||||
|
@ -236,6 +236,11 @@ jsonMultiParty (alice, bob) = do
|
||||
submit bob (exerciseCmd c Accept)
|
||||
pure ()
|
||||
|
||||
jsonMissingTemplateId : Party -> Script Int
|
||||
jsonMissingTemplateId p = do
|
||||
cid <- submit p (createCmd $ TProposal p p)
|
||||
snd <$> submit p (exerciseCmd cid Accept)
|
||||
|
||||
-- maxInboundMessageSize
|
||||
|
||||
template MessageSize
|
||||
|
@ -16,7 +16,7 @@ import scalaz.syntax.traverse._
|
||||
import spray.json._
|
||||
|
||||
import com.daml.bazeltools.BazelRunfiles._
|
||||
import com.daml.lf.archive.DarReader
|
||||
import com.daml.lf.archive.{Dar, DarReader}
|
||||
import com.daml.lf.archive.Decode
|
||||
import com.daml.lf.data.Ref._
|
||||
import com.daml.lf.engine.script.{
|
||||
@ -30,6 +30,7 @@ import com.daml.lf.engine.script.{
|
||||
}
|
||||
import com.daml.lf.iface.EnvironmentInterface
|
||||
import com.daml.lf.iface.reader.InterfaceReader
|
||||
import com.daml.lf.language.Ast.Package
|
||||
import com.daml.lf.speedy.SError._
|
||||
import com.daml.lf.speedy.SValue
|
||||
import com.daml.lf.speedy.SValue._
|
||||
@ -61,6 +62,7 @@ trait JsonApiFixture
|
||||
self: Suite =>
|
||||
|
||||
override protected def darFile = new File(rlocation("daml-script/test/script-test.dar"))
|
||||
protected val darFileNoLedger = new File(rlocation("daml-script/test/script-test-no-ledger.dar"))
|
||||
protected def server: SandboxServer = suiteResource.value._1
|
||||
override protected def serverPort: Port = server.port
|
||||
override protected def channel: Channel = suiteResource.value._2
|
||||
@ -134,11 +136,17 @@ final class JsonApiIt
|
||||
with SuiteResourceManagementAroundAll
|
||||
with TryValues {
|
||||
|
||||
private val dar = DarReader().readArchiveFromFile(darFile).get.map {
|
||||
case (pkgId, archive) => Decode.readArchivePayload(pkgId, archive)
|
||||
private def readDar(file: File): (Dar[(PackageId, Package)], EnvironmentInterface) = {
|
||||
val dar = DarReader().readArchiveFromFile(file).get.map {
|
||||
case (pkgId, archive) => Decode.readArchivePayload(pkgId, archive)
|
||||
}
|
||||
val ifaceDar = dar.map(pkg => InterfaceReader.readInterface(() => \/-(pkg))._2)
|
||||
val envIface = EnvironmentInterface.fromReaderInterfaces(ifaceDar)
|
||||
(dar, envIface)
|
||||
}
|
||||
private val ifaceDar = dar.map(pkg => InterfaceReader.readInterface(() => \/-(pkg))._2)
|
||||
private val envIface = EnvironmentInterface.fromReaderInterfaces(ifaceDar)
|
||||
|
||||
val (dar, envIface) = readDar(darFile)
|
||||
val (darNoLedger, envIfaceNoLedger) = readDar(darFileNoLedger)
|
||||
|
||||
def getToken(parties: List[String], admin: Boolean): String = {
|
||||
val payload = AuthServiceJWTPayload(
|
||||
@ -161,7 +169,8 @@ final class JsonApiIt
|
||||
private def getClients(
|
||||
parties: List[String] = List(party),
|
||||
defaultParty: Option[String] = None,
|
||||
admin: Boolean = false) = {
|
||||
admin: Boolean = false,
|
||||
envIface: EnvironmentInterface = envIface) = {
|
||||
// We give the default participant some nonsense party so the checks for party mismatch fail
|
||||
// due to the mismatch and not because the token does not allow inferring a party
|
||||
val defaultParticipant =
|
||||
@ -183,8 +192,9 @@ final class JsonApiIt
|
||||
private def run(
|
||||
clients: Participants[ScriptLedgerClient],
|
||||
name: QualifiedName,
|
||||
inputValue: Option[JsValue] = Some(JsString(party))): Future[SValue] = {
|
||||
val scriptId = Identifier(packageId, name)
|
||||
inputValue: Option[JsValue] = Some(JsString(party)),
|
||||
dar: Dar[(PackageId, Package)] = dar): Future[SValue] = {
|
||||
val scriptId = Identifier(dar.main._1, name)
|
||||
Runner.run(
|
||||
dar,
|
||||
scriptId,
|
||||
@ -305,5 +315,18 @@ final class JsonApiIt
|
||||
assert(result == SUnit)
|
||||
}
|
||||
}
|
||||
"missing template id" in {
|
||||
for {
|
||||
clients <- getClients(envIface = envIfaceNoLedger)
|
||||
ex <- recoverToExceptionIf[RuntimeException](
|
||||
run(
|
||||
clients,
|
||||
QualifiedName.assertFromString("ScriptTest:jsonMissingTemplateId"),
|
||||
dar = darNoLedger
|
||||
))
|
||||
} yield {
|
||||
assert(ex.toString.contains("Cannot resolve template ID"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user