mirror of
https://github.com/digital-asset/daml.git
synced 2024-11-05 03:56:26 +03:00
remove scala bindings and ledger-api-client (#18301)
* remove scala bindings from daml-script * remove bindings-pekko * clean test-common * clean rx-java * clean the rest * remove ledger and scala-bindings * format * correcting from code-review
This commit is contained in:
parent
c6e1ace2b7
commit
90d90ea759
@ -168,6 +168,7 @@ da_scala_library(
|
||||
unused_dependency_checker_mode = "error",
|
||||
visibility = [
|
||||
"//daml-script:__subpackages__",
|
||||
"//language-support/java/bindings-rxjava:__subpackages__",
|
||||
"//test-common/canton:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
@ -264,6 +265,7 @@ da_scala_library(
|
||||
"//compiler/repl-service:__subpackages__",
|
||||
"//daml-script:__subpackages__",
|
||||
"//language-support/java:__subpackages__",
|
||||
"//ledger-service/utils:__subpackages__",
|
||||
"//test-common/canton:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
|
@ -343,7 +343,6 @@ maven_install(
|
||||
name = "maven",
|
||||
artifacts = [
|
||||
"com.daml:bindings-akka_2.13:{}".format("2.7.6"),
|
||||
"com.daml:bindings-pekko_2.13:{}".format(latest_stable_version),
|
||||
"com.daml:daml-lf-archive-reader_2.13:{}".format(latest_stable_version),
|
||||
"com.daml:daml-lf-transaction_2.13:{}".format(latest_stable_version),
|
||||
"com.daml:ledger-api-common_2.13:{}".format(latest_stable_version),
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -22,7 +22,10 @@ da_scala_binary(
|
||||
resources = ["src/main/resources/logback.xml"],
|
||||
scala_deps = [
|
||||
"@maven//:com_github_scopt_scopt",
|
||||
"@maven//:com_typesafe_scala_logging_scala_logging",
|
||||
"@maven//:io_spray_spray_json",
|
||||
"@maven//:org_apache_pekko_pekko_actor",
|
||||
"@maven//:org_apache_pekko_pekko_stream",
|
||||
"@maven//:org_scalaz_scalaz_core",
|
||||
],
|
||||
scalacopts = lf_scalacopts_stricter,
|
||||
@ -38,13 +41,15 @@ da_scala_binary(
|
||||
"//daml-lf/language",
|
||||
"//daml-lf/transaction",
|
||||
"//daml-script/runner:script-runner-lib",
|
||||
"//language-support/scala/bindings-pekko",
|
||||
"//ledger-service/lf-value-json",
|
||||
"//libs-scala/auth-utils",
|
||||
"//libs-scala/rs-grpc-bridge",
|
||||
"//libs-scala/rs-grpc-pekko",
|
||||
"//libs-scala/scala-utils",
|
||||
"@maven//:com_google_protobuf_protobuf_java",
|
||||
"@maven//:io_grpc_grpc_api",
|
||||
"@maven//:io_grpc_grpc_netty",
|
||||
"@maven//:io_grpc_grpc_stub",
|
||||
"@maven//:io_netty_netty_handler",
|
||||
],
|
||||
)
|
||||
|
@ -14,8 +14,6 @@ genrule(
|
||||
"//canton:bindings-java_pom.xml",
|
||||
"//language-support/java/bindings-rxjava:libbindings-rxjava.jar",
|
||||
"//language-support/java/bindings-rxjava:bindings-rxjava_pom.xml",
|
||||
"//ledger/ledger-api-auth-client:libledger-api-auth-client.jar",
|
||||
"//ledger/ledger-api-auth-client:ledger-api-auth-client_pom.xml",
|
||||
"//language-support/java/codegen:shaded_binary.jar",
|
||||
"//language-support/java/codegen:shaded_binary_pom.xml",
|
||||
"//libs-scala/rs-grpc-bridge:librs-grpc-bridge.jar",
|
||||
@ -60,10 +58,6 @@ genrule(
|
||||
"com.daml" "rs-grpc-bridge" \\
|
||||
$(location //libs-scala/rs-grpc-bridge:librs-grpc-bridge.jar) \\
|
||||
$(location //libs-scala/rs-grpc-bridge:rs-grpc-bridge_pom.xml)
|
||||
install_mvn \\
|
||||
"com.daml" "ledger-api-auth-client" \\
|
||||
$(location //ledger/ledger-api-auth-client:libledger-api-auth-client.jar) \\
|
||||
$(location //ledger/ledger-api-auth-client:ledger-api-auth-client_pom.xml)
|
||||
"$$MVN" -q -Dmaven.repo.local=$$MVN_DB -f "$$TMP_DIR/quickstart-java/pom.xml" dependency:resolve dependency:resolve-plugins
|
||||
$(execpath //bazel_tools/sh:mktar) $@ -C $$(dirname $$MVN_DB) $$(basename $$MVN_DB)
|
||||
""".format(mvn = mvn_version),
|
||||
|
@ -26,6 +26,7 @@ da_scala_binary(
|
||||
resources = glob(["src/main/resources/**/*"]),
|
||||
scala_deps = [
|
||||
"@maven//:com_github_scopt_scopt",
|
||||
"@maven//:org_apache_pekko_pekko_actor",
|
||||
"@maven//:org_apache_pekko_pekko_stream",
|
||||
"@maven//:io_spray_spray_json",
|
||||
"@maven//:org_scalaz_scalaz_core",
|
||||
@ -39,15 +40,16 @@ da_scala_binary(
|
||||
deps = [
|
||||
"//canton:community_ledger_ledger-common",
|
||||
"//canton:community_util-logging",
|
||||
"//canton:ledger_api_proto_scala",
|
||||
"//daml-lf/archive:daml_lf_archive_reader",
|
||||
"//daml-lf/archive:daml_lf_dev_archive_proto_java",
|
||||
"//daml-lf/data",
|
||||
"//daml-lf/language",
|
||||
"//language-support/scala/bindings",
|
||||
"//language-support/scala/bindings-pekko",
|
||||
"//libs-scala/auth-utils",
|
||||
"//libs-scala/rs-grpc-bridge",
|
||||
"//libs-scala/rs-grpc-pekko",
|
||||
"//libs-scala/scala-utils",
|
||||
"@maven//:io_netty_netty_handler",
|
||||
"@maven//:org_apache_commons_commons_text",
|
||||
],
|
||||
)
|
||||
@ -63,6 +65,7 @@ da_scala_test(
|
||||
"@maven//:org_scalatest_scalatest_freespec",
|
||||
"@maven//:org_scalatest_scalatest_matchers_core",
|
||||
"@maven//:org_scalatest_scalatest_shouldmatchers",
|
||||
"@maven//:org_scalaz_scalaz_core",
|
||||
"@maven//:org_typelevel_paiges_core",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
@ -70,9 +73,9 @@ da_scala_test(
|
||||
":export",
|
||||
"//bazel_tools/runfiles:scala_runfiles",
|
||||
"//canton:community_ledger_ledger-common",
|
||||
"//canton:ledger_api_proto_scala",
|
||||
"//daml-lf/data",
|
||||
"//daml-lf/language",
|
||||
"//language-support/scala/bindings",
|
||||
"//libs-scala/auth-utils",
|
||||
],
|
||||
)
|
||||
|
@ -13,6 +13,8 @@ da_scala_binary(
|
||||
main_class = "com.daml.script.export.ExampleExportClient",
|
||||
scala_deps = [
|
||||
"@maven//:com_github_scopt_scopt",
|
||||
"@maven//:org_apache_pekko_pekko_actor",
|
||||
"@maven//:org_scalaz_scalaz_core",
|
||||
],
|
||||
scalacopts = lf_scalacopts_stricter,
|
||||
visibility = ["//visibility:public"],
|
||||
@ -20,13 +22,15 @@ da_scala_binary(
|
||||
"//:sdk-version-scala-lib",
|
||||
"//canton:community_ledger_ledger-common",
|
||||
"//canton:community_util-logging",
|
||||
"//canton:ledger_api_proto_scala",
|
||||
"//daml-lf/data",
|
||||
"//daml-script/export",
|
||||
"//daml-script/runner:script-runner-lib",
|
||||
"//language-support/scala/bindings",
|
||||
"//language-support/scala/bindings-pekko",
|
||||
"//libs-scala/auth-utils",
|
||||
"//libs-scala/fs-utils",
|
||||
"//libs-scala/rs-grpc-bridge",
|
||||
"//libs-scala/rs-grpc-pekko",
|
||||
"@maven//:com_google_protobuf_protobuf_java",
|
||||
"@maven//:io_netty_netty_handler",
|
||||
],
|
||||
)
|
||||
|
@ -11,7 +11,7 @@ import com.daml.SdkVersion
|
||||
import com.daml.fs.Utils.deleteRecursively
|
||||
import com.daml.grpc.adapter.{ExecutionSequencerFactory, PekkoExecutionSequencerPool}
|
||||
import com.digitalasset.canton.ledger.client.LedgerClient
|
||||
import com.daml.ledger.api.refinements.ApiTypes.Party
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes.Party
|
||||
import com.digitalasset.canton.ledger.api.tls.TlsConfiguration
|
||||
import com.daml.lf.engine.script.{ParticipantMode, RunnerMain, RunnerMainConfig}
|
||||
import com.digitalasset.canton.ledger.client.configuration.{
|
||||
|
@ -36,13 +36,14 @@ da_scala_test(
|
||||
"//bazel_tools/runfiles:scala_runfiles",
|
||||
"//canton:community_ledger_ledger-api-core",
|
||||
"//canton:community_ledger_ledger-common",
|
||||
"//canton:ledger_api_proto_scala",
|
||||
"//daml-lf/archive:daml_lf_archive_reader",
|
||||
"//daml-lf/data",
|
||||
"//daml-lf/interpreter",
|
||||
"//daml-lf/language",
|
||||
"//daml-script/export",
|
||||
"//daml-script/export/transaction-eq",
|
||||
"//daml-script/runner:script-runner-lib",
|
||||
"//language-support/scala/bindings",
|
||||
"//libs-scala/fs-utils",
|
||||
"//libs-scala/ledger-resources",
|
||||
"//libs-scala/ports",
|
||||
|
@ -9,7 +9,7 @@ import org.apache.pekko.stream.scaladsl.Sink
|
||||
import com.daml.SdkVersion
|
||||
import com.daml.bazeltools.BazelRunfiles
|
||||
import com.daml.integrationtest.CantonFixture
|
||||
import com.daml.ledger.api.refinements.ApiTypes
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes
|
||||
import com.digitalasset.canton.ledger.api.tls.TlsConfiguration
|
||||
import com.daml.ledger.api.v2.command_service.SubmitAndWaitRequest
|
||||
import com.daml.ledger.api.v2.commands.Commands
|
||||
|
@ -7,7 +7,7 @@ import java.nio.file.{Path, Paths}
|
||||
import java.io.File
|
||||
|
||||
import com.daml.auth.TokenHolder
|
||||
import com.daml.ledger.api.refinements.ApiTypes.Party
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes.Party
|
||||
import com.digitalasset.canton.ledger.api.tls.{TlsConfiguration, TlsConfigurationCli}
|
||||
import com.daml.ledger.api.v2.participant_offset.ParticipantOffset
|
||||
|
||||
|
@ -6,7 +6,7 @@ package com.daml.script.export
|
||||
import java.io.FileOutputStream
|
||||
import java.nio.file.Path
|
||||
import com.daml.daml_lf_dev.DamlLf
|
||||
import com.daml.ledger.api.refinements.ApiTypes
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes
|
||||
import com.daml.ledger.api.v1.value
|
||||
import com.digitalasset.canton.ledger.client.LedgerClient
|
||||
import com.daml.lf.{VersionRange, archive}
|
||||
|
@ -6,7 +6,13 @@ package com.daml.script.export
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.{LocalDate, ZoneId, ZonedDateTime}
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes.{Choice, ContractId, InterfaceId, Party, TemplateId}
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes.{
|
||||
Choice,
|
||||
ContractId,
|
||||
InterfaceId,
|
||||
Party,
|
||||
TemplateId,
|
||||
}
|
||||
import com.daml.ledger.api.v1.event.{CreatedEvent, ExercisedEvent}
|
||||
import com.daml.ledger.api.v1.value.Value.Sum
|
||||
import com.daml.ledger.api.v1.value.{Identifier, Record, RecordField, Value, Variant}
|
||||
|
@ -5,7 +5,7 @@ package com.daml.script.export
|
||||
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.{Files, Path}
|
||||
import com.daml.ledger.api.refinements.ApiTypes.{ContractId, Party, TemplateId}
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes.{ContractId, Party, TemplateId}
|
||||
import com.daml.ledger.api.v1.event.CreatedEvent
|
||||
import com.daml.ledger.api.v2.transaction.TransactionTree
|
||||
import com.daml.ledger.api.v1.value.Value.Sum
|
||||
|
@ -7,14 +7,14 @@ import org.apache.pekko.stream.Materializer
|
||||
import org.apache.pekko.stream.scaladsl.Sink
|
||||
import com.daml.auth.TokenHolder
|
||||
import com.digitalasset.canton.ledger.api.domain
|
||||
import com.daml.ledger.api.refinements.ApiTypes.{ContractId, Party}
|
||||
import com.daml.ledger.api.v2.update_service.{GetUpdatesResponse, GetUpdateTreesResponse}
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes.{ContractId, Party}
|
||||
import com.daml.ledger.api.v1.event.CreatedEvent
|
||||
import com.daml.ledger.api.v1.event.Event.Event
|
||||
import com.daml.ledger.api.v1.transaction_filter.Filters
|
||||
import com.daml.ledger.api.v2.participant_offset.ParticipantOffset
|
||||
import com.daml.ledger.api.v2.transaction.TransactionTree
|
||||
import com.daml.ledger.api.v2.transaction_filter.TransactionFilter
|
||||
import com.daml.ledger.api.v1.transaction_filter.Filters
|
||||
import com.daml.ledger.api.v2.update_service.{GetUpdatesResponse, GetUpdateTreesResponse}
|
||||
import com.digitalasset.canton.ledger.client.LedgerClient
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
@ -4,7 +4,13 @@
|
||||
package com.daml.script.export
|
||||
|
||||
import java.time.Instant
|
||||
import com.daml.ledger.api.refinements.ApiTypes.{Choice, ContractId, InterfaceId, Party, TemplateId}
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes.{
|
||||
Choice,
|
||||
ContractId,
|
||||
InterfaceId,
|
||||
Party,
|
||||
TemplateId,
|
||||
}
|
||||
import com.daml.ledger.api.v1.event.{CreatedEvent, ExercisedEvent}
|
||||
import com.daml.ledger.api.v2.transaction.TransactionTree
|
||||
import com.daml.ledger.api.v1.transaction.TreeEvent
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
package com.daml.script.export
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes.ContractId
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes.ContractId
|
||||
import com.daml.lf.data.Time.Timestamp
|
||||
import com.daml.script.export.TreeUtils.{SetTime, SubmitSimpleSingle, Action}
|
||||
import org.scalatest.freespec.AnyFreeSpec
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
package com.daml.script.export
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes.ContractId
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes.ContractId
|
||||
import com.daml.ledger.api.v1.{value => v}
|
||||
import org.scalatest.freespec.AnyFreeSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
package com.daml.script.export
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes
|
||||
import com.daml.ledger.api.v1.{value => V}
|
||||
import com.daml.lf.data.Ref
|
||||
import com.daml.lf.language.Ast
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
package com.daml.script.export
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes
|
||||
import com.daml.ledger.api.refinements.ApiTypes.{ContractId, Party}
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes.{ContractId, Party}
|
||||
import com.daml.ledger.api.v1.value.Value
|
||||
import com.daml.script.export.TreeUtils.SubmitSimpleMulti
|
||||
import org.scalatest.freespec.AnyFreeSpec
|
||||
|
@ -7,7 +7,7 @@ import com.daml.ledger.api.v1.{value => v}
|
||||
import java.time.{Instant, LocalDate, OffsetDateTime, ZoneOffset}
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes.{ContractId, Party}
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes.{ContractId, Party}
|
||||
import com.google.protobuf.empty.Empty
|
||||
import org.scalatest.freespec.AnyFreeSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
package com.daml.script.export
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes.ContractId
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes.ContractId
|
||||
import com.daml.ledger.api.v1.{value => v}
|
||||
import org.scalatest.freespec.AnyFreeSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
package com.daml.script.export
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes.ContractId
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes.ContractId
|
||||
import com.daml.ledger.api.v1.value.{Identifier, Record, RecordField, Value}
|
||||
import com.daml.script.export.TreeUtils.{
|
||||
Command,
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
package com.daml.script.export
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes.ContractId
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes.ContractId
|
||||
import com.daml.ledger.api.v1.value.Value
|
||||
import com.daml.script.export.TreeUtils.{Command, ExerciseByKeyCommand, SimpleCommand}
|
||||
import org.scalatest.freespec.AnyFreeSpec
|
||||
|
@ -7,7 +7,7 @@ import com.daml.ledger.api.v1.{value => v}
|
||||
import java.time.{Instant, LocalDate, OffsetDateTime, ZoneOffset}
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes.{ContractId, Party}
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes.{ContractId, Party}
|
||||
import com.daml.script.export.TreeUtils.{contractsReferences, treesReferences, valueRefs}
|
||||
import com.google.protobuf.empty.Empty
|
||||
import org.scalatest.freespec.AnyFreeSpec
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
package com.daml.script.export
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes.{ContractId, Party}
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes.{ContractId, Party}
|
||||
import com.daml.ledger.api.v1.event.{CreatedEvent, ExercisedEvent}
|
||||
import com.daml.ledger.api.v2.transaction.TransactionTree
|
||||
import com.daml.ledger.api.v1.transaction.TreeEvent
|
||||
|
@ -20,7 +20,8 @@ da_scala_library(
|
||||
scalacopts = lf_scalacopts_stricter,
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//language-support/scala/bindings",
|
||||
"//canton:community_ledger_ledger-common",
|
||||
"//canton:ledger_api_proto_scala",
|
||||
],
|
||||
)
|
||||
|
||||
@ -32,11 +33,13 @@ da_scala_test(
|
||||
"@maven//:org_scalatest_scalatest_freespec",
|
||||
"@maven//:org_scalatest_scalatest_matchers_core",
|
||||
"@maven//:org_scalatest_scalatest_shouldmatchers",
|
||||
"@maven//:org_scalaz_scalaz_core",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":transaction-eq",
|
||||
"//language-support/scala/bindings",
|
||||
"//canton:community_ledger_ledger-common",
|
||||
"//canton:ledger_api_proto_scala",
|
||||
"@maven//:org_scalatest_scalatest_compatible",
|
||||
],
|
||||
)
|
||||
|
@ -11,7 +11,7 @@ import com.daml.ledger.api.v1.event.{CreatedEvent, ExercisedEvent}
|
||||
import com.daml.ledger.api.v1.transaction.TreeEvent
|
||||
import com.daml.ledger.api.v2.transaction.TransactionTree
|
||||
import com.daml.ledger.api.v1.{value => v}
|
||||
import com.daml.ledger.api.refinements.ApiTypes._
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes._
|
||||
|
||||
object TransactionEq {
|
||||
// Helper which constructs a temporary
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
package com.daml.ledger.testing.utils
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes._
|
||||
import com.digitalasset.canton.ledger.api.refinements.ApiTypes._
|
||||
import com.daml.ledger.api.v1.{value => v}
|
||||
|
||||
import org.scalatest.freespec.AnyFreeSpec
|
||||
|
@ -36,7 +36,6 @@ da_java_library(
|
||||
],
|
||||
deps = [
|
||||
"//canton:bindings-java",
|
||||
"//ledger/ledger-api-auth-client",
|
||||
"//libs-scala/rs-grpc-bridge",
|
||||
"@maven//:com_google_api_grpc_proto_google_common_protos",
|
||||
"@maven//:com_google_protobuf_protobuf_java",
|
||||
@ -63,6 +62,7 @@ da_scala_library(
|
||||
],
|
||||
),
|
||||
scala_deps = [
|
||||
"@maven//:com_typesafe_scala_logging_scala_logging",
|
||||
"@maven//:org_scalacheck_scalacheck",
|
||||
"@maven//:org_scalactic_scalactic",
|
||||
"@maven//:org_scalatest_scalatest_core",
|
||||
@ -73,13 +73,14 @@ da_scala_library(
|
||||
deps = [
|
||||
":bindings-rxjava",
|
||||
"//canton:bindings-java",
|
||||
"//canton:community_ledger_ledger-api-core",
|
||||
"//canton:community_ledger_ledger-common",
|
||||
"//canton:community_util-logging",
|
||||
"//canton:ledger_api_proto_scala",
|
||||
"//daml-lf/data",
|
||||
"//ledger/ledger-api-auth",
|
||||
"//ledger/ledger-api-domain",
|
||||
"//ledger/participant-local-store",
|
||||
"//libs-scala/contextualized-logging",
|
||||
"//libs-scala/rs-grpc-bridge",
|
||||
"//observability/tracing",
|
||||
"@maven//:com_google_protobuf_protobuf_java",
|
||||
"@maven//:io_grpc_grpc_netty",
|
||||
"@maven//:io_reactivex_rxjava2_rxjava",
|
||||
@ -115,9 +116,9 @@ da_scala_test_suite(
|
||||
":bindings-java-tests-lib",
|
||||
":bindings-rxjava",
|
||||
"//canton:bindings-java",
|
||||
"//canton:community_ledger_ledger-api-core",
|
||||
"//canton:community_util-logging",
|
||||
"//canton:ledger_api_proto_scala",
|
||||
"//ledger/ledger-api-auth",
|
||||
"//ledger/ledger-api-common",
|
||||
"//libs-scala/contextualized-logging",
|
||||
"@maven//:com_google_api_grpc_proto_google_common_protos",
|
||||
"@maven//:com_google_protobuf_protobuf_java",
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.api.auth.client;
|
||||
package com.daml.ledger.rxjava.grpc.helpers;
|
||||
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.Metadata;
|
||||
@ -32,9 +32,4 @@ public final class LedgerCallCredentials extends CallCredentials {
|
||||
LedgerCallCredentials.header, token.startsWith("Bearer ") ? token : "Bearer " + token);
|
||||
applier.apply(metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void thisUsesUnstableApi() {
|
||||
// No need to implement this, it's used as a warning from upstream
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@
|
||||
|
||||
package com.daml.ledger.rxjava.grpc.helpers;
|
||||
|
||||
import com.daml.ledger.api.auth.client.LedgerCallCredentials;
|
||||
import io.grpc.stub.AbstractStub;
|
||||
import java.util.Optional;
|
||||
|
||||
|
@ -14,7 +14,7 @@ import com.daml.ledger.javaapi.data.{
|
||||
Identifier,
|
||||
}
|
||||
import com.daml.ledger.rxjava.grpc.helpers._
|
||||
import com.daml.ledger.api.auth.{AuthService, AuthServiceWildcard}
|
||||
import com.digitalasset.canton.ledger.api.auth.{AuthService, AuthServiceWildcard}
|
||||
import com.daml.ledger.api.v1.command_completion_service.CompletionStreamResponse
|
||||
import com.daml.ledger.api.v1.command_service.{
|
||||
SubmitAndWaitForTransactionIdResponse,
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
package com.daml.ledger.rxjava.grpc
|
||||
|
||||
import com.daml.ledger.api.auth.{AuthService, AuthServiceWildcard}
|
||||
import com.digitalasset.canton.ledger.api.auth.{AuthService, AuthServiceWildcard}
|
||||
import com.daml.ledger.api.v1.command_service.{
|
||||
SubmitAndWaitForTransactionIdResponse,
|
||||
SubmitAndWaitForTransactionResponse,
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
package com.daml.ledger.rxjava.grpc
|
||||
|
||||
import com.daml.ledger.api.auth.{AuthService, AuthServiceWildcard}
|
||||
import com.digitalasset.canton.ledger.api.auth.{AuthService, AuthServiceWildcard}
|
||||
import com.daml.ledger.rxjava.grpc.helpers.TestConfiguration
|
||||
import com.daml.ledger.api.v1.event_query_service.{
|
||||
GetEventsByContractIdResponse,
|
||||
|
@ -27,7 +27,8 @@ final class TimeClientImplTest
|
||||
behavior of "[9.1] TimeClientImpl.setTime"
|
||||
|
||||
it should "send requests with the correct ledger ID, current time and new time" in {
|
||||
val (timeService, timeServiceImpl) = TimeServiceImpl.createWithRef(Seq.empty, authorizer)
|
||||
val (timeService, timeServiceImpl) =
|
||||
TimeServiceImpl.createWithRef(Seq.empty, ledgerServices.authorizer)
|
||||
ledgerServices.withTimeClient(Seq(timeService)) { timeClient =>
|
||||
val currentLedgerTimeSeconds = 1L
|
||||
val currentLedgerTimeNanos = 2L
|
||||
@ -53,7 +54,7 @@ final class TimeClientImplTest
|
||||
it should "return the responses received" in {
|
||||
val getTimeResponse = genGetTimeResponse
|
||||
ledgerServices.withTimeClient(
|
||||
Seq(TimeServiceImpl.createWithRef(Seq(getTimeResponse), authorizer)._1)
|
||||
Seq(TimeServiceImpl.createWithRef(Seq(getTimeResponse), ledgerServices.authorizer)._1)
|
||||
) { timeClient =>
|
||||
val response = timeClient.getTime
|
||||
.timeout(TestConfiguration.timeoutInSeconds, TimeUnit.SECONDS)
|
||||
@ -68,7 +69,8 @@ final class TimeClientImplTest
|
||||
it should "send requests with the correct ledger ID" in {
|
||||
val getTimeResponse =
|
||||
genGetTimeResponse // we use the first element to block on the first element
|
||||
val (service, impl) = TimeServiceImpl.createWithRef(Seq(getTimeResponse), authorizer)
|
||||
val (service, impl) =
|
||||
TimeServiceImpl.createWithRef(Seq(getTimeResponse), ledgerServices.authorizer)
|
||||
ledgerServices.withTimeClient(Seq(service)) { timeClient =>
|
||||
val _ = timeClient
|
||||
.getTime()
|
||||
@ -81,15 +83,16 @@ final class TimeClientImplTest
|
||||
behavior of "[9.4] TimeClientImpl.setTime"
|
||||
|
||||
it should "return an error without sending a request when the time to set if bigger than the current time" in {
|
||||
ledgerServices.withTimeClient(Seq(TimeServiceImpl.createWithRef(Seq.empty, authorizer)._1)) {
|
||||
timeClient =>
|
||||
val currentTime = Instant.ofEpochSecond(1L, 2L)
|
||||
intercept[RuntimeException](
|
||||
timeClient
|
||||
.setTime(currentTime, currentTime)
|
||||
.timeout(TestConfiguration.timeoutInSeconds, TimeUnit.SECONDS)
|
||||
.blockingGet()
|
||||
)
|
||||
ledgerServices.withTimeClient(
|
||||
Seq(TimeServiceImpl.createWithRef(Seq.empty, ledgerServices.authorizer)._1)
|
||||
) { timeClient =>
|
||||
val currentTime = Instant.ofEpochSecond(1L, 2L)
|
||||
intercept[RuntimeException](
|
||||
timeClient
|
||||
.setTime(currentTime, currentTime)
|
||||
.timeout(TestConfiguration.timeoutInSeconds, TimeUnit.SECONDS)
|
||||
.blockingGet()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,7 +100,7 @@ final class TimeClientImplTest
|
||||
|
||||
def toAuthenticatedServer(fn: TimeClient => Any): Any =
|
||||
ledgerServices.withTimeClient(
|
||||
Seq(TimeServiceImpl.createWithRef(Seq(genGetTimeResponse), authorizer)._1),
|
||||
Seq(TimeServiceImpl.createWithRef(Seq(genGetTimeResponse), ledgerServices.authorizer)._1),
|
||||
mockedAuthService,
|
||||
)(fn)
|
||||
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
package com.daml.ledger.rxjava.grpc.helpers
|
||||
|
||||
import com.daml.ledger.api.auth.Authorizer
|
||||
import com.daml.ledger.api.auth.services.ActiveContractsServiceAuthorization
|
||||
import com.digitalasset.canton.ledger.api.auth.Authorizer
|
||||
import com.digitalasset.canton.ledger.api.auth.services.ActiveContractsServiceAuthorization
|
||||
import com.daml.ledger.api.v1.active_contracts_service.ActiveContractsServiceGrpc.ActiveContractsService
|
||||
import com.daml.ledger.api.v1.active_contracts_service.{
|
||||
ActiveContractsServiceGrpc,
|
||||
|
@ -1,11 +1,13 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.api.auth
|
||||
|
||||
import com.daml.ledger.api.auth.AuthService.AUTHORIZATION_KEY
|
||||
package com.daml.ledger.rxjava.grpc.helpers
|
||||
|
||||
import java.util.concurrent.{CompletableFuture, CompletionStage}
|
||||
|
||||
import com.digitalasset.canton.ledger.api.auth.AuthService.AUTHORIZATION_KEY
|
||||
import com.digitalasset.canton.ledger.api.auth.{AuthService, ClaimSet}
|
||||
import com.digitalasset.canton.tracing.TraceContext
|
||||
import io.grpc.Metadata
|
||||
|
||||
/** An AuthService that matches the value of the `Authorization` HTTP header against
|
||||
@ -14,7 +16,9 @@ import io.grpc.Metadata
|
||||
* Note: This AuthService is meant to be used for testing purposes only.
|
||||
*/
|
||||
final class AuthServiceStatic(claims: PartialFunction[String, ClaimSet]) extends AuthService {
|
||||
override def decodeMetadata(headers: Metadata): CompletionStage[ClaimSet] = {
|
||||
override def decodeMetadata(headers: Metadata)(implicit
|
||||
traceContext: TraceContext
|
||||
): CompletionStage[ClaimSet] = {
|
||||
if (headers.containsKey(AUTHORIZATION_KEY)) {
|
||||
val authorizationValue = headers.get(AUTHORIZATION_KEY).stripPrefix("Bearer ")
|
||||
CompletableFuture.completedFuture(
|
@ -3,8 +3,8 @@
|
||||
|
||||
package com.daml.ledger.rxjava.grpc.helpers
|
||||
|
||||
import com.daml.ledger.api.auth.Authorizer
|
||||
import com.daml.ledger.api.auth.services.CommandCompletionServiceAuthorization
|
||||
import com.digitalasset.canton.ledger.api.auth.Authorizer
|
||||
import com.digitalasset.canton.ledger.api.auth.services.CommandCompletionServiceAuthorization
|
||||
import com.daml.ledger.api.v1.command_completion_service.CommandCompletionServiceGrpc.CommandCompletionService
|
||||
import com.daml.ledger.api.v1.command_completion_service._
|
||||
import io.grpc.ServerServiceDefinition
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
package com.daml.ledger.rxjava.grpc.helpers
|
||||
|
||||
import com.daml.ledger.api.auth.Authorizer
|
||||
import com.daml.ledger.api.auth.services.CommandServiceAuthorization
|
||||
import com.digitalasset.canton.ledger.api.auth.Authorizer
|
||||
import com.digitalasset.canton.ledger.api.auth.services.CommandServiceAuthorization
|
||||
import com.daml.ledger.api.v1.command_service.CommandServiceGrpc.CommandService
|
||||
import com.daml.ledger.api.v1.command_service._
|
||||
import com.google.protobuf.empty.Empty
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
package com.daml.ledger.rxjava.grpc.helpers
|
||||
|
||||
import com.daml.ledger.api.auth.Authorizer
|
||||
import com.daml.ledger.api.auth.services.CommandSubmissionServiceAuthorization
|
||||
import com.digitalasset.canton.ledger.api.auth.Authorizer
|
||||
import com.digitalasset.canton.ledger.api.auth.services.CommandSubmissionServiceAuthorization
|
||||
import com.daml.ledger.api.v1.command_submission_service.CommandSubmissionServiceGrpc.CommandSubmissionService
|
||||
import com.daml.ledger.api.v1.command_submission_service.{
|
||||
CommandSubmissionServiceGrpc,
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
package com.daml.ledger.rxjava.grpc.helpers
|
||||
|
||||
import com.daml.ledger.api.auth.Authorizer
|
||||
import com.daml.ledger.api.auth.services.EventQueryServiceAuthorization
|
||||
import com.digitalasset.canton.ledger.api.auth.Authorizer
|
||||
import com.digitalasset.canton.ledger.api.auth.services.EventQueryServiceAuthorization
|
||||
import com.daml.ledger.api.v1.event_query_service.EventQueryServiceGrpc.EventQueryService
|
||||
import com.daml.ledger.api.v1.event_query_service._
|
||||
import io.grpc.ServerServiceDefinition
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
package com.daml.ledger.rxjava.grpc.helpers
|
||||
|
||||
import com.daml.ledger.api.auth.Authorizer
|
||||
import com.daml.ledger.api.auth.services.LedgerConfigurationServiceAuthorization
|
||||
import com.digitalasset.canton.ledger.api.auth.Authorizer
|
||||
import com.digitalasset.canton.ledger.api.auth.services.LedgerConfigurationServiceAuthorization
|
||||
import com.daml.ledger.api.v1.ledger_configuration_service.LedgerConfigurationServiceGrpc.LedgerConfigurationService
|
||||
import com.daml.ledger.api.v1.ledger_configuration_service.{
|
||||
GetLedgerConfigurationRequest,
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
package com.daml.ledger.rxjava.grpc.helpers
|
||||
|
||||
import com.daml.ledger.api.auth.Authorizer
|
||||
import com.daml.ledger.api.auth.services.LedgerIdentityServiceAuthorization
|
||||
import com.digitalasset.canton.ledger.api.auth.Authorizer
|
||||
import com.digitalasset.canton.ledger.api.auth.services.LedgerIdentityServiceAuthorization
|
||||
import com.daml.ledger.api.v1.ledger_identity_service.LedgerIdentityServiceGrpc.LedgerIdentityService
|
||||
import com.daml.ledger.api.v1.ledger_identity_service.{
|
||||
GetLedgerIdentityRequest,
|
||||
|
@ -6,6 +6,7 @@ package com.daml.ledger.rxjava.grpc.helpers
|
||||
import java.net.{InetSocketAddress, SocketAddress}
|
||||
import java.time.{Clock, Duration}
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import org.apache.pekko.actor.ActorSystem
|
||||
import com.daml.ledger.rxjava.grpc._
|
||||
import com.daml.ledger.rxjava.grpc.helpers.TransactionsServiceImpl.LedgerItem
|
||||
@ -16,8 +17,16 @@ import com.daml.ledger.rxjava.{
|
||||
PackageClient,
|
||||
}
|
||||
import com.daml.grpc.adapter.{ExecutionSequencerFactory, SingleThreadExecutionSequencerPool}
|
||||
import com.daml.ledger.api.auth.interceptor.AuthorizationInterceptor
|
||||
import com.daml.ledger.api.auth.{AuthService, AuthServiceWildcard, Authorizer, ClaimSet}
|
||||
import com.digitalasset.canton.ledger.api.auth.interceptor.{
|
||||
AuthorizationInterceptor,
|
||||
IdentityProviderAwareAuthService,
|
||||
}
|
||||
import com.digitalasset.canton.ledger.api.auth.{
|
||||
AuthService,
|
||||
AuthServiceWildcard,
|
||||
Authorizer,
|
||||
ClaimSet,
|
||||
}
|
||||
import com.daml.ledger.api.v1.active_contracts_service.GetActiveContractsResponse
|
||||
import com.daml.ledger.api.v1.command_completion_service.{
|
||||
CompletionEndResponse,
|
||||
@ -39,8 +48,9 @@ import com.daml.ledger.api.v1.package_service.{
|
||||
ListPackagesResponse,
|
||||
}
|
||||
import com.daml.ledger.api.v1.testing.time_service.GetTimeResponse
|
||||
import com.daml.logging.LoggingContext
|
||||
import com.daml.platform.localstore.InMemoryUserManagementStore
|
||||
import com.daml.tracing.NoOpTelemetry
|
||||
import com.digitalasset.canton.logging.{LoggingContextWithTrace, NamedLoggerFactory}
|
||||
import com.digitalasset.canton.platform.localstore.InMemoryUserManagementStore
|
||||
import com.google.protobuf.empty.Empty
|
||||
import io.grpc._
|
||||
import io.grpc.netty.NettyServerBuilder
|
||||
@ -57,16 +67,20 @@ final class LedgerServices(val ledgerId: String) {
|
||||
private val esf: ExecutionSequencerFactory = new SingleThreadExecutionSequencerPool(ledgerId)
|
||||
private val pekkoSystem = ActorSystem("LedgerServicesParticipant")
|
||||
private val participantId = "LedgerServicesParticipant"
|
||||
private val authorizer =
|
||||
private val loggerFactory = NamedLoggerFactory.root
|
||||
val authorizer: Authorizer =
|
||||
new Authorizer(
|
||||
now = () => Clock.systemUTC().instant(),
|
||||
ledgerId = ledgerId,
|
||||
participantId = participantId,
|
||||
userManagementStore = new InMemoryUserManagementStore(),
|
||||
userManagementStore = new InMemoryUserManagementStore(createAdmin = false, loggerFactory),
|
||||
ec = executionContext,
|
||||
userRightsCheckIntervalInSeconds = 1,
|
||||
pekkoScheduler = pekkoSystem.scheduler,
|
||||
)(LoggingContext.ForTesting)
|
||||
jwtTimestampLeeway = None,
|
||||
telemetry = NoOpTelemetry,
|
||||
loggerFactory = loggerFactory,
|
||||
)
|
||||
|
||||
def newServerBuilder(): NettyServerBuilder = NettyServerBuilder.forAddress(nextAddress())
|
||||
|
||||
@ -101,14 +115,23 @@ final class LedgerServices(val ledgerId: String) {
|
||||
}
|
||||
}
|
||||
|
||||
private class IDPAuthService extends IdentityProviderAwareAuthService {
|
||||
override def decodeMetadata(headers: Metadata)(implicit
|
||||
loggingContext: LoggingContextWithTrace
|
||||
): Future[ClaimSet] =
|
||||
Future.successful(ClaimSet.Unauthenticated)
|
||||
}
|
||||
|
||||
private def createServer(
|
||||
authService: AuthService,
|
||||
services: Seq[ServerServiceDefinition],
|
||||
): Server = {
|
||||
val authorizationInterceptor = AuthorizationInterceptor(
|
||||
authService,
|
||||
Some(new InMemoryUserManagementStore()),
|
||||
{ _ => Future.successful(ClaimSet.Unauthenticated) },
|
||||
Some(new InMemoryUserManagementStore(false, loggerFactory)),
|
||||
new IDPAuthService,
|
||||
NoOpTelemetry,
|
||||
loggerFactory,
|
||||
executionContext,
|
||||
)
|
||||
services
|
||||
@ -272,7 +295,7 @@ final class LedgerServices(val ledgerId: String) {
|
||||
accessToken: java.util.Optional[String] = java.util.Optional.empty[String],
|
||||
)(f: (UserManagementClientImpl, UserManagementServiceImpl) => Any): Any = {
|
||||
val (service, serviceImpl) =
|
||||
UserManagementServiceImpl.createWithRef(authorizer)(executionContext)
|
||||
UserManagementServiceImpl.createWithRef(authorizer, loggerFactory)(executionContext)
|
||||
withServerAndChannel(authService, Seq(service)) { channel =>
|
||||
f(new UserManagementClientImpl(channel, accessToken), serviceImpl)
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
package com.daml.ledger.rxjava.grpc.helpers
|
||||
|
||||
import com.daml.ledger.api.auth.Authorizer
|
||||
import com.digitalasset.canton.ledger.api.auth.Authorizer
|
||||
import com.daml.ledger.api.v1.active_contracts_service.GetActiveContractsResponse
|
||||
import com.daml.ledger.api.v1.command_completion_service.{
|
||||
CompletionEndResponse,
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
package com.daml.ledger.rxjava.grpc.helpers
|
||||
|
||||
import com.daml.ledger.api.auth.Authorizer
|
||||
import com.daml.ledger.api.auth.services.PackageServiceAuthorization
|
||||
import com.digitalasset.canton.ledger.api.auth.Authorizer
|
||||
import com.digitalasset.canton.ledger.api.auth.services.PackageServiceAuthorization
|
||||
import com.daml.ledger.api.v1.package_service.PackageServiceGrpc.PackageService
|
||||
import com.daml.ledger.api.v1.package_service._
|
||||
import io.grpc.ServerServiceDefinition
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
package com.daml.ledger.rxjava.grpc.helpers
|
||||
|
||||
import com.daml.ledger.api.auth.Authorizer
|
||||
import com.daml.ledger.api.auth.services.TimeServiceAuthorization
|
||||
import com.digitalasset.canton.ledger.api.auth.Authorizer
|
||||
import com.digitalasset.canton.ledger.api.auth.services.TimeServiceAuthorization
|
||||
import com.daml.ledger.api.v1.testing.time_service.TimeServiceGrpc.TimeService
|
||||
import com.daml.ledger.api.v1.testing.time_service.{
|
||||
GetTimeRequest,
|
||||
|
@ -9,8 +9,8 @@ import com.daml.ledger.rxjava.grpc.helpers.TransactionsServiceImpl.{
|
||||
LedgerItem,
|
||||
ledgerOffsetOrdering,
|
||||
}
|
||||
import com.daml.ledger.api.auth.Authorizer
|
||||
import com.daml.ledger.api.auth.services.TransactionServiceAuthorization
|
||||
import com.digitalasset.canton.ledger.api.auth.Authorizer
|
||||
import com.digitalasset.canton.ledger.api.auth.services.TransactionServiceAuthorization
|
||||
import com.daml.ledger.api.v1.event.Event
|
||||
import com.daml.ledger.api.v1.event.Event.Event.{Archived, Created, Empty}
|
||||
import com.daml.ledger.api.v1.ledger_offset.LedgerOffset
|
||||
|
@ -3,11 +3,11 @@
|
||||
|
||||
package com.daml.ledger.rxjava.grpc.helpers
|
||||
|
||||
import com.daml.ledger.api.auth.Authorizer
|
||||
import com.daml.ledger.api.auth.services.UserManagementServiceAuthorization
|
||||
import com.digitalasset.canton.ledger.api.auth.Authorizer
|
||||
import com.digitalasset.canton.ledger.api.auth.services.UserManagementServiceAuthorization
|
||||
import com.daml.ledger.api.v1.admin.user_management_service.UserManagementServiceGrpc.UserManagementService
|
||||
import com.daml.ledger.api.v1.admin.user_management_service._
|
||||
import com.daml.logging.LoggingContext
|
||||
import com.digitalasset.canton.logging.NamedLoggerFactory
|
||||
import io.grpc.ServerServiceDefinition
|
||||
|
||||
import scala.collection.mutable
|
||||
@ -60,11 +60,11 @@ object UserManagementServiceImpl {
|
||||
|
||||
// for testing only
|
||||
private[helpers] def createWithRef(
|
||||
authorizer: Authorizer
|
||||
authorizer: Authorizer,
|
||||
loggerFactory: NamedLoggerFactory,
|
||||
)(implicit ec: ExecutionContext): (ServerServiceDefinition, UserManagementServiceImpl) = {
|
||||
implicit val loggingContext: LoggingContext = LoggingContext.ForTesting
|
||||
val impl = new UserManagementServiceImpl
|
||||
val authImpl = new UserManagementServiceAuthorization(impl, authorizer)
|
||||
val authImpl = new UserManagementServiceAuthorization(impl, authorizer, loggerFactory)
|
||||
(UserManagementServiceGrpc.bindService(authImpl, ec), impl)
|
||||
}
|
||||
|
||||
|
@ -3,16 +3,12 @@
|
||||
|
||||
package com.daml.ledger
|
||||
|
||||
import java.time.Clock
|
||||
import java.util.UUID
|
||||
|
||||
import org.apache.pekko.actor.ActorSystem
|
||||
|
||||
import scala.concurrent.ExecutionContext
|
||||
import com.daml.lf.data.Ref
|
||||
import com.daml.ledger.api.auth.{
|
||||
AuthServiceStatic,
|
||||
Authorizer,
|
||||
import com.digitalasset.canton.ledger.api.auth.{
|
||||
Claim,
|
||||
ClaimActAsParty,
|
||||
ClaimAdmin,
|
||||
@ -20,27 +16,12 @@ import com.daml.ledger.api.auth.{
|
||||
ClaimReadAsParty,
|
||||
ClaimSet,
|
||||
}
|
||||
import com.daml.logging.LoggingContext
|
||||
import com.daml.platform.localstore.InMemoryUserManagementStore
|
||||
|
||||
package object rxjava {
|
||||
|
||||
private[rxjava] def untestedEndpoint: Nothing =
|
||||
throw new UnsupportedOperationException("Untested endpoint, implement if needed")
|
||||
private val pekkoSystem = ActorSystem("testActorSystem")
|
||||
sys.addShutdownHook(pekkoSystem.terminate(): Unit)
|
||||
|
||||
private[rxjava] val authorizer =
|
||||
new Authorizer(
|
||||
() => Clock.systemUTC().instant(),
|
||||
"testLedgerId",
|
||||
"testParticipantId",
|
||||
new InMemoryUserManagementStore(),
|
||||
ExecutionContext.parasitic,
|
||||
userRightsCheckIntervalInSeconds = 1,
|
||||
pekkoScheduler = pekkoSystem.scheduler,
|
||||
)(LoggingContext.ForTesting)
|
||||
|
||||
private[rxjava] val emptyToken = "empty"
|
||||
private[rxjava] val publicToken = "public"
|
||||
private[rxjava] val adminToken = "admin"
|
||||
@ -54,7 +35,7 @@ package object rxjava {
|
||||
private[rxjava] val someOtherPartyReadWriteToken = UUID.randomUUID.toString
|
||||
|
||||
private[rxjava] val mockedAuthService =
|
||||
AuthServiceStatic {
|
||||
grpc.helpers.AuthServiceStatic {
|
||||
case `emptyToken` => ClaimSet.Unauthenticated
|
||||
case `publicToken` => ClaimSet.Claims.Empty.copy(claims = Seq[Claim](ClaimPublic))
|
||||
case `adminToken` => ClaimSet.Claims.Empty.copy(claims = Seq[Claim](ClaimAdmin))
|
||||
|
@ -413,7 +413,6 @@ da_scala_test(
|
||||
"//canton:community_ledger_ledger-api-core",
|
||||
"//canton:community_ledger_ledger-common",
|
||||
"//daml-lf/data",
|
||||
"//language-support/scala/bindings-pekko",
|
||||
"//libs-scala/ledger-resources",
|
||||
"//libs-scala/ledger-resources:ledger-resources-test-lib",
|
||||
"//libs-scala/ports",
|
||||
|
@ -1,35 +0,0 @@
|
||||
# Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
load(
|
||||
"//bazel_tools:scala.bzl",
|
||||
"da_scala_library",
|
||||
)
|
||||
|
||||
da_scala_library(
|
||||
name = "bindings-pekko-testing",
|
||||
srcs = glob(["src/main/scala/**/*.scala"]),
|
||||
resources = glob(["src/main/resources/**/*"]),
|
||||
scala_deps = [
|
||||
"@maven//:org_apache_pekko_pekko_actor",
|
||||
"@maven//:org_apache_pekko_pekko_stream",
|
||||
"@maven//:com_typesafe_scala_logging_scala_logging",
|
||||
"@maven//:org_scalactic_scalactic",
|
||||
"@maven//:org_scalatest_scalatest_core",
|
||||
],
|
||||
scala_runtime_deps = [
|
||||
"@maven//:org_apache_pekko_pekko_slf4j",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
exports = [],
|
||||
runtime_deps = [
|
||||
"@maven//:ch_qos_logback_logback_classic",
|
||||
],
|
||||
deps = [
|
||||
"//libs-scala/rs-grpc-bridge",
|
||||
"@maven//:com_typesafe_config",
|
||||
"@maven//:org_scalatest_scalatest_compatible",
|
||||
],
|
||||
)
|
@ -1,16 +0,0 @@
|
||||
test-dispatcher {
|
||||
# Dispatcher is the name of the event-based dispatcher
|
||||
type = Dispatcher
|
||||
# What kind of ExecutionService to use
|
||||
executor = "thread-pool-executor"
|
||||
# Configuration for the fork join pool
|
||||
thread-pool-executor {
|
||||
# Define a fixed thread pool size with this property. The corePoolSize
|
||||
# and the maximumPoolSize of the ThreadPoolExecutor will be set to this
|
||||
# value, if it is defined. Then the other pool-size properties will not
|
||||
# be used.
|
||||
#
|
||||
# Valid values are: `off` or a positive integer.
|
||||
fixed-pool-size = 1
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.testing
|
||||
|
||||
import java.util
|
||||
import java.util.concurrent.{Executors, ScheduledExecutorService}
|
||||
|
||||
import org.apache.pekko.NotUsed
|
||||
import org.apache.pekko.actor.{ActorSystem, Scheduler}
|
||||
import org.apache.pekko.stream.scaladsl.{Sink, Source}
|
||||
import org.apache.pekko.stream.Materializer
|
||||
import org.apache.pekko.util.ByteString
|
||||
import com.daml.grpc.adapter.{ExecutionSequencerFactory, SingleThreadExecutionSequencerPool}
|
||||
import com.typesafe.config.{Config, ConfigFactory, ConfigValueFactory}
|
||||
import com.typesafe.scalalogging.LazyLogging
|
||||
import org.scalatest.{BeforeAndAfterAll, Suite}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{Await, ExecutionContextExecutor, Future}
|
||||
import scala.util.control.NonFatal
|
||||
|
||||
trait PekkoTest extends BeforeAndAfterAll with LazyLogging { self: Suite =>
|
||||
// TestEventListener is needed for log testing
|
||||
private val loggers =
|
||||
util.Arrays.asList(
|
||||
"org.apache.pekko.event.slf4j.Slf4jLogger",
|
||||
"org.apache.pekko.testkit.TestEventListener",
|
||||
)
|
||||
protected implicit val sysConfig: Config = ConfigFactory
|
||||
.load()
|
||||
.withValue("pekko.loggers", ConfigValueFactory.fromIterable(loggers))
|
||||
.withValue("pekko.logger-startup-timeout", ConfigValueFactory.fromAnyRef("30s"))
|
||||
.withValue("pekko.stdout-loglevel", ConfigValueFactory.fromAnyRef("INFO"))
|
||||
protected implicit val system: ActorSystem = ActorSystem("test", sysConfig)
|
||||
protected implicit val ec: ExecutionContextExecutor =
|
||||
system.dispatchers.lookup("test-dispatcher")
|
||||
protected implicit val scheduler: Scheduler = system.scheduler
|
||||
protected implicit val schedulerService: ScheduledExecutorService =
|
||||
Executors.newSingleThreadScheduledExecutor()
|
||||
protected implicit val materializer: Materializer = Materializer(system)
|
||||
protected implicit val esf: ExecutionSequencerFactory =
|
||||
new SingleThreadExecutionSequencerPool("testSequencerPool")
|
||||
protected val timeout: FiniteDuration = 2.minutes
|
||||
protected val shortTimeout: FiniteDuration = 5.seconds
|
||||
|
||||
protected def await[T](fun: => Future[T]): T = Await.result(fun, timeout)
|
||||
|
||||
protected def awaitShort[T](fun: => Future[T]): T = Await.result(fun, shortTimeout)
|
||||
|
||||
protected def drain(source: Source[ByteString, NotUsed]): ByteString = {
|
||||
val futureResult: Future[ByteString] = source.runFold(ByteString.empty) { (a, b) =>
|
||||
a.concat(b)
|
||||
}
|
||||
awaitShort(futureResult)
|
||||
}
|
||||
|
||||
protected def drain[A, B](source: Source[A, B]): Seq[A] = {
|
||||
val futureResult: Future[Seq[A]] = source.runWith(Sink.seq)
|
||||
awaitShort(futureResult)
|
||||
}
|
||||
|
||||
override protected def afterAll(): Unit = {
|
||||
try {
|
||||
val _ = await(system.terminate())
|
||||
} catch {
|
||||
case NonFatal(_) => ()
|
||||
}
|
||||
schedulerService.shutdownNow()
|
||||
super.afterAll()
|
||||
}
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
# Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
load(
|
||||
"//bazel_tools:scala.bzl",
|
||||
"da_scala_library",
|
||||
"da_scala_test_suite",
|
||||
"kind_projector_plugin",
|
||||
)
|
||||
|
||||
da_scala_library(
|
||||
name = "bindings-pekko",
|
||||
srcs = glob(["src/main/**/*.scala"]),
|
||||
plugins = [
|
||||
kind_projector_plugin,
|
||||
],
|
||||
resources = glob(["src/main/resources/**/*"]),
|
||||
scala_deps = [
|
||||
"@maven//:com_chuusai_shapeless",
|
||||
"@maven//:com_github_pureconfig_pureconfig_core",
|
||||
"@maven//:org_apache_pekko_pekko_actor",
|
||||
"@maven//:org_apache_pekko_pekko_stream",
|
||||
"@maven//:com_typesafe_scala_logging_scala_logging",
|
||||
"@maven//:org_scalaz_scalaz_core",
|
||||
],
|
||||
scala_exports = [
|
||||
"@maven//:com_chuusai_shapeless",
|
||||
"@maven//:com_github_pureconfig_pureconfig_core",
|
||||
"@maven//:org_apache_pekko_pekko_actor",
|
||||
"@maven//:org_apache_pekko_pekko_stream",
|
||||
"@maven//:com_typesafe_scala_logging_scala_logging",
|
||||
"@maven//:org_scalaz_scalaz_core",
|
||||
],
|
||||
tags = ["maven_coordinates=com.daml:bindings-pekko:__VERSION__"],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
exports = [
|
||||
"//canton:bindings-java",
|
||||
"//language-support/scala/bindings",
|
||||
"//ledger/ledger-api-client",
|
||||
"//ledger/ledger-api-domain",
|
||||
"//libs-scala/rs-grpc-pekko",
|
||||
"@maven//:ch_qos_logback_logback_classic",
|
||||
"@maven//:ch_qos_logback_logback_core",
|
||||
"@maven//:com_google_api_grpc_proto_google_common_protos",
|
||||
"@maven//:com_typesafe_config",
|
||||
"@maven//:io_grpc_grpc_netty",
|
||||
"@maven//:io_netty_netty_handler",
|
||||
"@maven//:io_netty_netty_tcnative_boringssl_static",
|
||||
"@maven//:org_slf4j_slf4j_api",
|
||||
],
|
||||
deps = [
|
||||
"//canton:bindings-java",
|
||||
"//language-support/scala/bindings",
|
||||
"//ledger/ledger-api-client",
|
||||
"//ledger/ledger-api-common",
|
||||
"//ledger/ledger-api-domain",
|
||||
"//libs-scala/rs-grpc-pekko",
|
||||
"//observability/tracing",
|
||||
"@maven//:ch_qos_logback_logback_classic",
|
||||
"@maven//:ch_qos_logback_logback_core",
|
||||
"@maven//:com_google_api_grpc_proto_google_common_protos",
|
||||
"@maven//:com_typesafe_config",
|
||||
"@maven//:io_grpc_grpc_netty",
|
||||
"@maven//:io_netty_netty_handler",
|
||||
"@maven//:io_netty_netty_tcnative_boringssl_static",
|
||||
"@maven//:org_slf4j_slf4j_api",
|
||||
],
|
||||
)
|
||||
|
||||
da_scala_test_suite(
|
||||
name = "tests",
|
||||
srcs = glob(
|
||||
[
|
||||
"src/test/**/*.scala",
|
||||
],
|
||||
),
|
||||
scala_deps = [
|
||||
"@maven//:org_apache_pekko_pekko_actor",
|
||||
"@maven//:org_apache_pekko_pekko_stream",
|
||||
"@maven//:com_typesafe_scala_logging_scala_logging",
|
||||
"@maven//:org_scalatest_scalatest_core",
|
||||
"@maven//:org_scalatest_scalatest_matchers_core",
|
||||
"@maven//:org_scalatest_scalatest_shouldmatchers",
|
||||
"@maven//:org_scalatest_scalatest_wordspec",
|
||||
"@maven//:org_scalaz_scalaz_core",
|
||||
],
|
||||
scala_runtime_deps = [
|
||||
"@maven//:org_apache_pekko_pekko_stream_testkit",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
deps = [
|
||||
":bindings-pekko",
|
||||
"//language-support/scala/bindings",
|
||||
"//language-support/scala/bindings-pekko-testing",
|
||||
"//ledger/ledger-api-client",
|
||||
"//libs-scala/rs-grpc-bridge",
|
||||
"//observability/tracing",
|
||||
"@maven//:com_google_api_grpc_proto_google_common_protos",
|
||||
"@maven//:com_typesafe_config",
|
||||
"@maven//:org_scalatest_scalatest_compatible",
|
||||
],
|
||||
)
|
@ -1,18 +0,0 @@
|
||||
# Ledger Client Binding
|
||||
|
||||
This module contains the glue code between `nanobot-framework`, `scala-codegen`
|
||||
and `prototype-client` (Scala binding for the ledger API). The gRPC API provided
|
||||
by `prototype-client` as it is is not type-safe. The incomming `Transaction`s
|
||||
from `LedgerClient` is routed to `DomainTransactionMapper`, which will:
|
||||
* convert the data in `Transaction` to type-safe types coming from `api-refinements`
|
||||
* call the `EventDecoder` provided by `scala-codegen` for created events
|
||||
* verify that the messages contain all the neccessary fields, and remove `Optional` wrappers
|
||||
|
||||
The result of this will be the `Domain*` classes.
|
||||
|
||||
|
||||
In the other directions (for the commands coming out from the nanobots) the
|
||||
'CompositeCommandAdapter' will be used to transform back to the gRPC interface
|
||||
(`SubmitRequest`). After the command submission, the outcoming `Completion` will
|
||||
be used to check the result, and based on that the `CommandRetryFlow` can decide
|
||||
wether the command should be retried or an error should be reported.
|
@ -1,9 +0,0 @@
|
||||
ledger-client {
|
||||
command-client {
|
||||
max-commands-in-flight = 256
|
||||
max-parallel-submissions = 32
|
||||
default-deduplication-time = PT30S
|
||||
}
|
||||
max-retry-time = PT1M
|
||||
}
|
||||
|
@ -1,39 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes._
|
||||
|
||||
import scala.collection.immutable
|
||||
|
||||
sealed trait DomainEvent {
|
||||
|
||||
/** The id of the event */
|
||||
def eventId: EventId
|
||||
|
||||
/** The id of the target contract */
|
||||
def contractId: ContractId
|
||||
|
||||
/** The template ID of the target contract */
|
||||
def templateId: TemplateId
|
||||
|
||||
/** Which parties are notified of the events */
|
||||
def witnessParties: immutable.Seq[Party]
|
||||
}
|
||||
|
||||
final case class DomainCreatedEvent(
|
||||
eventId: EventId,
|
||||
contractId: ContractId,
|
||||
templateId: TemplateId,
|
||||
witnessParties: immutable.Seq[Party],
|
||||
createArguments: CreateArguments,
|
||||
contractData: Contract.OfAny,
|
||||
) extends DomainEvent
|
||||
|
||||
final case class DomainArchivedEvent(
|
||||
eventId: EventId,
|
||||
contractId: ContractId,
|
||||
templateId: TemplateId,
|
||||
witnessParties: immutable.Seq[Party],
|
||||
) extends DomainEvent
|
@ -1,17 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes.{CommandId, TransactionId, WorkflowId}
|
||||
import com.daml.ledger.api.v1.ledger_offset.LedgerOffset
|
||||
import com.google.protobuf.timestamp.Timestamp
|
||||
|
||||
case class DomainTransaction(
|
||||
transactionId: TransactionId,
|
||||
workflowId: WorkflowId,
|
||||
offset: LedgerOffset,
|
||||
commandId: CommandId,
|
||||
effectiveAt: Timestamp,
|
||||
events: Seq[DomainEvent],
|
||||
)
|
@ -1,147 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
import org.apache.pekko.NotUsed
|
||||
import org.apache.pekko.stream.scaladsl.Flow
|
||||
import com.daml.ledger.api.refinements.ApiTypes._
|
||||
import com.daml.ledger.api.v1.event.Event.Event.{Archived, Created, Empty}
|
||||
import com.daml.ledger.api.v1.event._
|
||||
import com.daml.ledger.api.v1.ledger_offset.LedgerOffset
|
||||
import com.daml.ledger.api.v1.ledger_offset.LedgerOffset.Value.Absolute
|
||||
import com.daml.ledger.api.v1.transaction.Transaction
|
||||
import com.daml.ledger.client.binding.DomainTransactionMapper.DecoderType
|
||||
import com.typesafe.scalalogging.LazyLogging
|
||||
import scalaz.std.either._
|
||||
import scalaz.std.list._
|
||||
import scalaz.syntax.traverse._
|
||||
|
||||
import scala.collection.immutable
|
||||
|
||||
object DomainTransactionMapper {
|
||||
|
||||
type DecoderType = CreatedEvent => Either[EventDecoderError, Contract.OfAny]
|
||||
|
||||
def apply(decoder: DecoderType): Flow[Transaction, DomainTransaction, NotUsed] =
|
||||
new DomainTransactionMapper(decoder).transactionsMapper
|
||||
|
||||
private sealed trait InputValidationError extends Product with Serializable
|
||||
private final case class RequiredFieldDoesNotExistError(field: String)
|
||||
extends InputValidationError
|
||||
private final case object EmptyEvent extends InputValidationError
|
||||
}
|
||||
|
||||
class DomainTransactionMapper(decoder: DecoderType) extends LazyLogging {
|
||||
|
||||
import DomainTransactionMapper._
|
||||
|
||||
def transactionsMapper: Flow[Transaction, DomainTransaction, NotUsed] =
|
||||
Flow[Transaction].mapConcat { transaction =>
|
||||
domainTransaction(transaction) match {
|
||||
case Left(error) =>
|
||||
logger.warn(
|
||||
s"Input validation error when converting to domain transaction: $error. Transaction is discarded."
|
||||
)
|
||||
List.empty
|
||||
case Right(t) =>
|
||||
List(t)
|
||||
}
|
||||
}
|
||||
|
||||
private def domainTransaction(t: Transaction): Either[InputValidationError, DomainTransaction] =
|
||||
for {
|
||||
effectiveAt <- checkExists("effectiveAt", t.effectiveAt)
|
||||
events <- domainEvents(t.events)
|
||||
transactionId = TransactionId(t.transactionId)
|
||||
workflowId = WorkflowId(t.workflowId)
|
||||
offset = LedgerOffset(Absolute(t.offset))
|
||||
commandId = CommandId(t.commandId)
|
||||
} yield DomainTransaction(
|
||||
transactionId,
|
||||
workflowId,
|
||||
offset,
|
||||
commandId,
|
||||
effectiveAt,
|
||||
events,
|
||||
)
|
||||
|
||||
private def checkExists[T](
|
||||
fieldName: String,
|
||||
maybeElement: Option[T],
|
||||
): Either[InputValidationError, T] =
|
||||
maybeElement match {
|
||||
case Some(element) => Right(element)
|
||||
case None => Left(RequiredFieldDoesNotExistError(fieldName))
|
||||
}
|
||||
|
||||
private def domainEvents(events: Seq[Event]): Either[InputValidationError, Seq[DomainEvent]] =
|
||||
events.toList
|
||||
.traverse { event =>
|
||||
for {
|
||||
domainEvent <- mapEvent(event)
|
||||
} yield domainEvent.toList
|
||||
}
|
||||
.map(_.flatten)
|
||||
|
||||
private def mapEvent(event: Event): Either[InputValidationError, Option[DomainEvent]] =
|
||||
event.event match {
|
||||
case Created(createdEvent) =>
|
||||
decoder(createdEvent)
|
||||
.fold(logAndDiscard(createdEvent), mapCreatedEvent(createdEvent, _).map(Some.apply))
|
||||
case Archived(archivedEvent) =>
|
||||
mapArchivedEvent(archivedEvent).map(Some.apply)
|
||||
case Empty =>
|
||||
Left(EmptyEvent)
|
||||
}
|
||||
|
||||
private def logAndDiscard(
|
||||
event: CreatedEvent
|
||||
)(err: EventDecoderError): Either[InputValidationError, Option[DomainEvent]] = {
|
||||
// TODO: improve error handling (make discarding error log message configurable)
|
||||
logger.warn(s"Unhandled create event ${event.toString}. Error: ${err.toString}")
|
||||
Right(None)
|
||||
}
|
||||
|
||||
private def mapCreatedEvent(
|
||||
createdEvent: CreatedEvent,
|
||||
contract: Contract.OfAny,
|
||||
): Either[InputValidationError, DomainCreatedEvent] =
|
||||
for {
|
||||
tid <- checkExists("events.witnessedEvents.event.created.templateId", createdEvent.templateId)
|
||||
|
||||
arguments <- checkExists(
|
||||
"events.witnessedEvents.event.created.createArguments",
|
||||
createdEvent.createArguments,
|
||||
)
|
||||
|
||||
eventId = EventId(createdEvent.eventId)
|
||||
contractId = ContractId(createdEvent.contractId)
|
||||
templateId = TemplateId(tid)
|
||||
witnessParties = createdEvent.witnessParties.map(Party.apply).to(immutable.Seq)
|
||||
createArguments = CreateArguments(arguments)
|
||||
} yield DomainCreatedEvent(
|
||||
eventId,
|
||||
contractId,
|
||||
templateId,
|
||||
witnessParties,
|
||||
createArguments,
|
||||
contract,
|
||||
)
|
||||
|
||||
private def mapArchivedEvent(
|
||||
archivedEvent: ArchivedEvent
|
||||
): Either[InputValidationError, DomainArchivedEvent] =
|
||||
for {
|
||||
tid <- checkExists(
|
||||
"events.witnessedEvents.event.archived.templateId",
|
||||
archivedEvent.templateId,
|
||||
)
|
||||
|
||||
eventId = EventId(archivedEvent.eventId)
|
||||
contractId = ContractId(archivedEvent.contractId)
|
||||
templateId = TemplateId(tid)
|
||||
witnessParties = archivedEvent.witnessParties.map(Party.apply).to(immutable.Seq)
|
||||
} yield DomainArchivedEvent(eventId, contractId, templateId, witnessParties)
|
||||
|
||||
}
|
@ -1,176 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
import java.time.Duration
|
||||
import java.util.concurrent.TimeUnit.MINUTES
|
||||
|
||||
import org.apache.pekko.NotUsed
|
||||
import org.apache.pekko.stream.scaladsl.{Flow, Source}
|
||||
import com.daml.api.util.TimeProvider
|
||||
import com.daml.ledger.api.refinements.ApiTypes.{ApplicationId, LedgerId, Party}
|
||||
import com.daml.ledger.api.refinements.{CompositeCommand, CompositeCommandAdapter}
|
||||
import com.daml.ledger.api.v1.event.Event
|
||||
import com.daml.ledger.api.v1.ledger_identity_service.{
|
||||
GetLedgerIdentityRequest,
|
||||
LedgerIdentityServiceGrpc,
|
||||
}
|
||||
import com.daml.ledger.api.v1.ledger_offset.LedgerOffset
|
||||
import com.daml.ledger.api.v1.transaction_filter.TransactionFilter
|
||||
import com.daml.ledger.client.LedgerClient
|
||||
import com.daml.ledger.client.binding.DomainTransactionMapper.DecoderType
|
||||
import com.daml.ledger.client.binding.retrying.CommandRetryFlow
|
||||
import com.daml.ledger.client.binding.util.Slf4JLogger
|
||||
import com.daml.ledger.client.configuration.LedgerClientConfiguration
|
||||
import com.daml.ledger.client.services.commands.CommandSubmission
|
||||
import com.daml.ledger.client.services.commands.tracker.CompletionResponse.{
|
||||
CompletionFailure,
|
||||
CompletionSuccess,
|
||||
}
|
||||
import com.daml.util.Ctx
|
||||
import io.grpc.ManagedChannel
|
||||
import io.grpc.netty.NegotiationType.TLS
|
||||
import io.grpc.netty.NettyChannelBuilder
|
||||
import io.netty.handler.ssl.SslContext
|
||||
import org.slf4j.LoggerFactory
|
||||
import scalaz.syntax.tag._
|
||||
|
||||
import scala.annotation.nowarn
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
class LedgerClientBinding(
|
||||
val ledgerClient: LedgerClient,
|
||||
val ledgerClientConfig: LedgerClientConfiguration,
|
||||
val channel: ManagedChannel,
|
||||
retryTimeout: Duration,
|
||||
timeProvider: TimeProvider,
|
||||
decoder: DecoderType,
|
||||
) {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(this.getClass)
|
||||
|
||||
import LedgerClientBinding._
|
||||
|
||||
def transactionSource(
|
||||
party: Party,
|
||||
templateSelector: TemplateSelector,
|
||||
startOffset: LedgerOffset,
|
||||
endOffset: Option[LedgerOffset],
|
||||
token: Option[String] = None,
|
||||
): Source[DomainTransaction, NotUsed] = {
|
||||
|
||||
logger.debug(
|
||||
"[tx {}] subscription start with offset template selector {}, start {}, end {}",
|
||||
Party.unwrap(party),
|
||||
templateSelector,
|
||||
startOffset,
|
||||
endOffset,
|
||||
)
|
||||
|
||||
ledgerClient.transactionClient
|
||||
.getTransactions(
|
||||
startOffset,
|
||||
endOffset,
|
||||
transactionFilter(party, templateSelector),
|
||||
token = token,
|
||||
)
|
||||
.via(
|
||||
Slf4JLogger(
|
||||
logger,
|
||||
s"tx $party",
|
||||
tx =>
|
||||
s"CID ${tx.commandId} TX ${tx.transactionId} CONTAINS ${tx.events
|
||||
.map {
|
||||
case Event(Event.Event.Created(value)) => s"C ${value.contractId}"
|
||||
case Event(Event.Event.Archived(value)) => s"A ${value.contractId}"
|
||||
case other => sys.error(s"Expected Created or Archived, got $other"): String
|
||||
}
|
||||
.mkString("[", ",", "]")}",
|
||||
)
|
||||
)
|
||||
.via(DomainTransactionMapper(decoder))
|
||||
}
|
||||
|
||||
type CommandTrackingFlow[C] =
|
||||
Flow[Ctx[C, CompositeCommand], Ctx[C, Either[CompletionFailure, CompletionSuccess]], NotUsed]
|
||||
|
||||
private val compositeCommandAdapter = new CompositeCommandAdapter(
|
||||
LedgerId(ledgerClient.ledgerId.unwrap),
|
||||
ApplicationId(ledgerClientConfig.applicationId),
|
||||
)
|
||||
|
||||
def retryingConfirmedCommands[C](
|
||||
party: Party
|
||||
)(implicit ec: ExecutionContext): Future[CommandTrackingFlow[C]] =
|
||||
for {
|
||||
tracking <- CommandRetryFlow[C](
|
||||
party,
|
||||
ledgerClient.commandClient,
|
||||
timeProvider,
|
||||
retryTimeout,
|
||||
)
|
||||
} yield Flow[Ctx[C, CompositeCommand]]
|
||||
.map(
|
||||
_.map(compositeCommand =>
|
||||
CommandSubmission(compositeCommandAdapter.transform(compositeCommand))
|
||||
)
|
||||
)
|
||||
.via(tracking)
|
||||
|
||||
type CommandsFlow[C] =
|
||||
Flow[Ctx[C, CompositeCommand], Ctx[C, Either[CompletionFailure, CompletionSuccess]], NotUsed]
|
||||
|
||||
def commands[C](party: Party)(implicit ec: ExecutionContext): Future[CommandsFlow[C]] = {
|
||||
for {
|
||||
trackCommandsFlow <- ledgerClient.commandClient
|
||||
.trackCommands[C](List(party.unwrap), token = None)
|
||||
} yield Flow[Ctx[C, CompositeCommand]]
|
||||
.map(
|
||||
_.map(compositeCommand =>
|
||||
CommandSubmission(compositeCommandAdapter.transform(compositeCommand))
|
||||
)
|
||||
)
|
||||
.via(trackCommandsFlow)
|
||||
.mapMaterializedValue(_ => NotUsed)
|
||||
}
|
||||
|
||||
def shutdown()(implicit ec: ExecutionContext): Future[Unit] = Future {
|
||||
channel.shutdown()
|
||||
channel.awaitTermination(1, MINUTES)
|
||||
()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object LedgerClientBinding {
|
||||
|
||||
def createChannel(host: String, port: Int, sslContext: Option[SslContext]): ManagedChannel = {
|
||||
val builder = NettyChannelBuilder.forAddress(host, port)
|
||||
|
||||
sslContext match {
|
||||
case Some(context) => builder.sslContext(context).negotiationType(TLS)
|
||||
case None => builder.usePlaintext()
|
||||
}
|
||||
|
||||
builder.build()
|
||||
}
|
||||
|
||||
@nowarn(
|
||||
"msg=parameter config .* is never used"
|
||||
) // public function, unsure whether arg needed
|
||||
def askLedgerId(
|
||||
channel: ManagedChannel,
|
||||
config: LedgerClientConfiguration,
|
||||
)(implicit ec: ExecutionContext): Future[String] =
|
||||
LedgerIdentityServiceGrpc
|
||||
.stub(channel)
|
||||
.getLedgerIdentity(GetLedgerIdentityRequest())
|
||||
.map(_.ledgerId): @nowarn(
|
||||
"cat=deprecation&origin=com\\.daml\\.ledger\\.api\\.v1\\.ledger_identity_service\\..*"
|
||||
)
|
||||
|
||||
def transactionFilter(party: Party, templateSelector: TemplateSelector): TransactionFilter =
|
||||
TransactionFilter(Map(party.unwrap -> templateSelector.toApi))
|
||||
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes.TemplateId
|
||||
import com.daml.ledger.api.v1.transaction_filter.{Filters, InclusiveFilters}
|
||||
import scalaz.Tag
|
||||
|
||||
sealed trait TemplateSelector extends Product with Serializable {
|
||||
def toApi: Filters
|
||||
}
|
||||
|
||||
object TemplateSelector {
|
||||
case object All extends TemplateSelector {
|
||||
val toApi = Filters.defaultInstance
|
||||
}
|
||||
|
||||
final case class Templates(templates: Set[TemplateId]) extends TemplateSelector {
|
||||
val toApi = Filters(Some(InclusiveFilters(Tag.unsubst(templates).toSeq)))
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding.log
|
||||
|
||||
object Labels {
|
||||
val CREATED = "created"
|
||||
val ARCHIVED = "archived"
|
||||
val EXERCISED = "exercised"
|
||||
val WORKFLOW_ID = "workflowId"
|
||||
val LET = "let"
|
||||
val RECORDTIME = "recordedAt"
|
||||
val NANOBOT = "nanobot"
|
||||
val BLOCK_HEIGHT = "blockHeight"
|
||||
val EVENT_COUNT = "eventCount"
|
||||
val COMMAND_COUNT = "commandCount"
|
||||
val RESULT = "result"
|
||||
val EVENTS = "events"
|
||||
val APPID = "applicationid"
|
||||
val ERROR_CODE = "error-code"
|
||||
val ERROR_MESSAGE = "error-message"
|
||||
val ERROR_DETAILS = "error-details"
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding.log
|
||||
|
||||
import ch.qos.logback.classic.filter.ThresholdFilter
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent
|
||||
import ch.qos.logback.core.spi.FilterReply
|
||||
|
||||
/** Logs at specific level and above, excluded logger logs everything.
|
||||
* If you don't have exclusions, use regular ThresholdFilter. Without provided excludeLogger this
|
||||
* implementation logs every event with no filtering at all.
|
||||
*/
|
||||
class LogbackThresholdFilterWithExclusion extends ThresholdFilter {
|
||||
@volatile private var excludeLogger: String = _
|
||||
|
||||
def setExcludeLogger(a: String): Unit = this.excludeLogger = a
|
||||
|
||||
def getExcludeLogger: String = this.excludeLogger
|
||||
|
||||
override def decide(event: ILoggingEvent): FilterReply = {
|
||||
if (!this.isStarted) FilterReply.NEUTRAL
|
||||
else if (event.getLoggerName.startsWith(excludeLogger)) FilterReply.NEUTRAL
|
||||
else super.decide(event)
|
||||
}
|
||||
|
||||
override def start(): Unit = {
|
||||
if (this.excludeLogger != null) {
|
||||
super.start()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding.retrying
|
||||
|
||||
import java.time.temporal.TemporalAmount
|
||||
|
||||
import org.apache.pekko.NotUsed
|
||||
import org.apache.pekko.stream.FlowShape
|
||||
import org.apache.pekko.stream.scaladsl.{Flow, GraphDSL, Merge, Partition}
|
||||
import com.daml.api.util.TimeProvider
|
||||
import com.daml.ledger.api.refinements.ApiTypes.Party
|
||||
import com.daml.ledger.client.services.commands.tracker.CompletionResponse
|
||||
import com.daml.ledger.client.services.commands.tracker.CompletionResponse.{
|
||||
CompletionFailure,
|
||||
CompletionSuccess,
|
||||
}
|
||||
import com.daml.ledger.client.services.commands.{CommandClient, CommandSubmission}
|
||||
import com.daml.util.Ctx
|
||||
import com.google.rpc.Code
|
||||
import scalaz.syntax.tag._
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
object CommandRetryFlow {
|
||||
|
||||
type In[C] = Ctx[C, CommandSubmission]
|
||||
type Out[C] = Ctx[C, Either[CompletionFailure, CompletionSuccess]]
|
||||
type SubmissionFlowType[C] = Flow[In[C], Out[C], NotUsed]
|
||||
|
||||
private val RETRY_PORT = 0
|
||||
private val PROPAGATE_PORT = 1
|
||||
|
||||
def apply[C](
|
||||
party: Party,
|
||||
commandClient: CommandClient,
|
||||
timeProvider: TimeProvider,
|
||||
maxRetryTime: TemporalAmount,
|
||||
)(implicit ec: ExecutionContext): Future[SubmissionFlowType[C]] =
|
||||
for {
|
||||
submissionFlow <- commandClient
|
||||
.trackCommands[RetryInfo[C, CommandSubmission]](List(party.unwrap))
|
||||
submissionFlowWithoutMat = submissionFlow.mapMaterializedValue(_ => NotUsed)
|
||||
graph = createGraph(submissionFlowWithoutMat, timeProvider, maxRetryTime)
|
||||
} yield wrapGraph(graph, timeProvider)
|
||||
|
||||
def wrapGraph[C](
|
||||
graph: SubmissionFlowType[RetryInfo[C, CommandSubmission]],
|
||||
timeProvider: TimeProvider,
|
||||
): SubmissionFlowType[C] =
|
||||
Flow[In[C]]
|
||||
.map(RetryInfo.wrap(timeProvider))
|
||||
.via(graph)
|
||||
.map(RetryInfo.unwrap)
|
||||
|
||||
def createGraph[C](
|
||||
commandSubmissionFlow: SubmissionFlowType[RetryInfo[C, CommandSubmission]],
|
||||
timeProvider: TimeProvider,
|
||||
maxRetryTime: TemporalAmount,
|
||||
): SubmissionFlowType[RetryInfo[C, CommandSubmission]] =
|
||||
Flow
|
||||
.fromGraph(GraphDSL.createGraph(commandSubmissionFlow) { implicit b => commandSubmission =>
|
||||
import GraphDSL.Implicits._
|
||||
|
||||
val merge =
|
||||
b.add(Merge[In[RetryInfo[C, CommandSubmission]]](inputPorts = 2, eagerComplete = true))
|
||||
|
||||
val retryDecider = b.add(
|
||||
Partition[Out[RetryInfo[C, CommandSubmission]]](
|
||||
outputPorts = 2,
|
||||
{
|
||||
case Ctx(
|
||||
RetryInfo(value, nrOfRetries, firstSubmissionTime, _),
|
||||
Left(notOk: CompletionResponse.NotOkResponse),
|
||||
_,
|
||||
) if RETRYABLE_ERROR_CODES.contains(notOk.grpcStatus.code) =>
|
||||
if ((firstSubmissionTime plus maxRetryTime) isBefore timeProvider.getCurrentTime) {
|
||||
RetryLogger.logStopRetrying(
|
||||
value,
|
||||
notOk.grpcStatus,
|
||||
nrOfRetries,
|
||||
firstSubmissionTime,
|
||||
)
|
||||
PROPAGATE_PORT
|
||||
} else {
|
||||
RetryLogger.logNonFatal(value, notOk.grpcStatus, nrOfRetries)
|
||||
RETRY_PORT
|
||||
}
|
||||
case Ctx(
|
||||
RetryInfo(value, nrOfRetries, _, _),
|
||||
Left(notOk: CompletionResponse.NotOkResponse),
|
||||
_,
|
||||
) =>
|
||||
RetryLogger.logFatal(value, notOk.grpcStatus, nrOfRetries)
|
||||
PROPAGATE_PORT
|
||||
case Ctx(_, Left(CompletionResponse.TimeoutResponse(_)), _) =>
|
||||
PROPAGATE_PORT
|
||||
case Ctx(_, Left(statusNotFound: CompletionResponse.NoStatusInResponse), _) =>
|
||||
statusNotFoundError(statusNotFound.commandId)
|
||||
case Ctx(_, Right(_), _) =>
|
||||
PROPAGATE_PORT
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
val convertToRetry = b.add(Flow[Out[RetryInfo[C, CommandSubmission]]].map {
|
||||
case Ctx(retryInfo, _, telemetryContext) =>
|
||||
Ctx(retryInfo.newRetry, retryInfo.value, telemetryContext)
|
||||
})
|
||||
|
||||
// format: off
|
||||
merge.out ~> commandSubmission ~> retryDecider.in
|
||||
merge.in(RETRY_PORT) <~ convertToRetry <~ retryDecider.out(RETRY_PORT)
|
||||
// format: on
|
||||
|
||||
FlowShape(merge.in(PROPAGATE_PORT), retryDecider.out(PROPAGATE_PORT))
|
||||
})
|
||||
|
||||
private[retrying] val RETRYABLE_ERROR_CODES =
|
||||
Set(Code.RESOURCE_EXHAUSTED_VALUE, Code.UNAVAILABLE_VALUE)
|
||||
|
||||
private def statusNotFoundError(commandId: String): Int =
|
||||
throw new RuntimeException(s"Status for command $commandId is missing.")
|
||||
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding.retrying
|
||||
|
||||
import java.time.Instant
|
||||
|
||||
import com.daml.api.util.TimeProvider
|
||||
import com.daml.ledger.client.binding.retrying.CommandRetryFlow.Out
|
||||
import com.daml.util.Ctx
|
||||
|
||||
final case class RetryInfo[C, T](
|
||||
value: T,
|
||||
nrOfRetries: Int,
|
||||
firstSubmissionTime: Instant,
|
||||
ctx: C,
|
||||
) {
|
||||
def newRetry: RetryInfo[C, T] = copy(nrOfRetries = nrOfRetries + 1)
|
||||
}
|
||||
|
||||
object RetryInfo {
|
||||
|
||||
def wrap[C, T](timeProvider: TimeProvider)(request: Ctx[C, T]): Ctx[RetryInfo[C, T], T] =
|
||||
request match {
|
||||
case Ctx(context, value, telemetryContext) =>
|
||||
Ctx(
|
||||
RetryInfo(value, 0, timeProvider.getCurrentTime, context),
|
||||
value,
|
||||
telemetryContext,
|
||||
)
|
||||
}
|
||||
|
||||
def unwrap[C, T](request: Out[RetryInfo[C, T]]): Out[C] = request match {
|
||||
case Ctx(RetryInfo(_, _, _, context), completion, telemetryContext) =>
|
||||
Ctx(context, completion, telemetryContext)
|
||||
}
|
||||
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding.retrying
|
||||
|
||||
import java.time.Instant
|
||||
|
||||
import com.daml.ledger.api.v1.commands.Commands
|
||||
import com.daml.ledger.api.validation.CommandsValidator
|
||||
import com.daml.ledger.client.binding.log.Labels.{
|
||||
ERROR_CODE,
|
||||
ERROR_DETAILS,
|
||||
ERROR_MESSAGE,
|
||||
WORKFLOW_ID,
|
||||
}
|
||||
import com.daml.ledger.client.services.commands.CommandSubmission
|
||||
import com.google.rpc.status.Status
|
||||
import com.typesafe.scalalogging.LazyLogging
|
||||
|
||||
object RetryLogger extends LazyLogging {
|
||||
|
||||
def logFatal(submission: CommandSubmission, status: Status, nrOfRetries: Int): Unit = {
|
||||
logger.warn(
|
||||
s"Encountered fatal error when submitting command after $nrOfRetries retries, therefore retry halted: " +
|
||||
format(submission.commands, status)
|
||||
)
|
||||
}
|
||||
|
||||
def logStopRetrying(
|
||||
submission: CommandSubmission,
|
||||
status: Status,
|
||||
nrOfRetries: Int,
|
||||
firstSubmissionTime: Instant,
|
||||
): Unit = {
|
||||
logger.warn(
|
||||
s"Retrying of command stopped after $nrOfRetries retries. Attempting since $firstSubmissionTime: " +
|
||||
format(submission.commands, status)
|
||||
)
|
||||
}
|
||||
|
||||
def logNonFatal(submission: CommandSubmission, status: Status, nrOfRetries: Int): Unit = {
|
||||
logger.warn(
|
||||
s"Encountered non-fatal error when submitting command after $nrOfRetries retries, therefore will retry: " +
|
||||
format(submission.commands, status)
|
||||
)
|
||||
}
|
||||
|
||||
private def format(commands: Commands, status: Status): String =
|
||||
format(
|
||||
(BIM, commands.commandId),
|
||||
(ACT_AS, CommandsValidator.effectiveSubmitters(commands).actAs),
|
||||
(WORKFLOW_ID, commands.workflowId),
|
||||
(ERROR_CODE, status.code),
|
||||
(ERROR_MESSAGE, status.message),
|
||||
(ERROR_DETAILS, status.details.mkString(",")),
|
||||
)
|
||||
|
||||
@SuppressWarnings(Array("org.wartremover.warts.JavaSerializable", "org.wartremover.warts.Any"))
|
||||
private def format(fs: (String, Any)*): String = fs.map(f => s"${f._1} = ${f._2}").mkString(", ")
|
||||
|
||||
private val ACT_AS = "act-as"
|
||||
private val BIM = "bim"
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding.util
|
||||
|
||||
import java.util.concurrent.{ScheduledExecutorService, ScheduledFuture, TimeUnit}
|
||||
|
||||
import com.typesafe.scalalogging.LazyLogging
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{ExecutionContext, Future, Promise, TimeoutException}
|
||||
|
||||
object ScalaUtil {
|
||||
|
||||
implicit class FutureOps[T](val future: Future[T]) extends LazyLogging {
|
||||
|
||||
def timeout(
|
||||
name: String,
|
||||
failTimeout: FiniteDuration = 1.minute,
|
||||
warnTimeout: FiniteDuration = 30.seconds,
|
||||
)(implicit ec: ExecutionContext, scheduler: ScheduledExecutorService): Future[T] = {
|
||||
|
||||
val promise = Promise[T]()
|
||||
|
||||
@SuppressWarnings(Array("org.wartremover.warts.JavaSerializable"))
|
||||
val warningTask = schedule(warnTimeout) {
|
||||
logger.warn("Function {} takes more than {}", name, warnTimeout: Any)
|
||||
}
|
||||
|
||||
val errorTask = schedule(failTimeout) {
|
||||
val error = new TimeoutException(s"Function call $name took more than $failTimeout")
|
||||
promise.tryFailure(error)
|
||||
()
|
||||
}
|
||||
|
||||
future.onComplete { outcome =>
|
||||
warningTask.cancel(false)
|
||||
errorTask.cancel(false)
|
||||
promise.tryComplete(outcome)
|
||||
}
|
||||
|
||||
promise.future
|
||||
}
|
||||
|
||||
private def schedule(
|
||||
timeout: FiniteDuration
|
||||
)(f: => Unit)(implicit scheduler: ScheduledExecutorService): ScheduledFuture[_] = {
|
||||
|
||||
val runnable = new Runnable {
|
||||
override def run(): Unit = f
|
||||
}
|
||||
|
||||
scheduler.schedule(runnable, timeout.toMillis, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
|
||||
def timeoutWithDefaultWarn(name: String, failTimeout: FiniteDuration)(implicit
|
||||
ec: ExecutionContext,
|
||||
scheduler: ScheduledExecutorService,
|
||||
): Future[T] = timeout(name, failTimeout, 10.seconds)
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding.util
|
||||
|
||||
import org.apache.pekko.stream._
|
||||
import org.apache.pekko.stream.stage.{GraphStage, GraphStageLogic, InHandler, OutHandler}
|
||||
import org.slf4j.Logger
|
||||
|
||||
final case class Slf4JLogger[T, U](
|
||||
logger: Logger,
|
||||
prefix: String,
|
||||
project: T => U,
|
||||
logDemand: Boolean = false,
|
||||
) extends GraphStage[FlowShape[T, T]] {
|
||||
|
||||
override def toString = "Slf4JLog"
|
||||
|
||||
val in: Inlet[T] = Inlet[T]("in")
|
||||
val out: Outlet[T] = Outlet[T]("out")
|
||||
|
||||
override def shape: FlowShape[T, T] = FlowShape(in, out)
|
||||
|
||||
override def createLogic(inheritedAttributes: Attributes): GraphStageLogic =
|
||||
new GraphStageLogic(shape) with OutHandler with InHandler {
|
||||
|
||||
override def onPush(): Unit = {
|
||||
|
||||
val elem = grab(in)
|
||||
if (logger.isDebugEnabled) logger.debug("[{}] Element: {}", prefix, project(elem))
|
||||
push(out, elem)
|
||||
}
|
||||
|
||||
override def onPull(): Unit = {
|
||||
if (logDemand) logger.debug("[{}] Demand", prefix)
|
||||
pull(in)
|
||||
}
|
||||
|
||||
override def onUpstreamFailure(cause: Throwable): Unit = {
|
||||
logger.warn(s"[$prefix] Upstream failed", cause)
|
||||
|
||||
super.onUpstreamFailure(cause)
|
||||
}
|
||||
|
||||
override def onUpstreamFinish(): Unit = {
|
||||
logger.debug("[{}] Upstream finished.", prefix)
|
||||
|
||||
super.onUpstreamFinish()
|
||||
}
|
||||
|
||||
override def onDownstreamFinish(cause: Throwable): Unit = {
|
||||
logger.debug("[{}] Downstream finished.", prefix)
|
||||
|
||||
super.onDownstreamFinish(cause)
|
||||
}
|
||||
|
||||
setHandlers(in, out, this)
|
||||
}
|
||||
}
|
||||
|
||||
object Slf4JLogger {
|
||||
def apply[T](logger: Logger, prefix: String): Slf4JLogger[T, T] =
|
||||
new Slf4JLogger(logger, prefix, identity)
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
import java.time.Instant
|
||||
|
||||
import org.apache.pekko.stream.scaladsl.Source
|
||||
import com.daml.ledger.api.refinements.ApiTypes._
|
||||
import com.daml.ledger.api.v1.event.Event.Event.{Archived, Created}
|
||||
import com.daml.ledger.api.v1.event._
|
||||
import com.daml.ledger.api.v1.ledger_offset.LedgerOffset
|
||||
import com.daml.ledger.api.v1.ledger_offset.LedgerOffset.Value.Absolute
|
||||
import com.daml.ledger.api.v1.transaction.Transaction
|
||||
import com.daml.ledger.api.v1.value.{Identifier, Record}
|
||||
import com.daml.ledger.client.testing.PekkoTest
|
||||
import com.google.protobuf.timestamp.Timestamp
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AnyWordSpec
|
||||
|
||||
import scala.collection.immutable
|
||||
|
||||
class DomainTransactionMapperUT extends AnyWordSpec with Matchers with PekkoTest {
|
||||
private val mockContract =
|
||||
Contract(Primitive.ContractId("contractId"), MockTemplate(), Seq.empty, Seq.empty, None)
|
||||
private val transactionMapper = new DomainTransactionMapper(_ => Right(mockContract))
|
||||
|
||||
private def getResult(source: immutable.Iterable[Transaction]): Seq[DomainTransaction] =
|
||||
drain(
|
||||
Source(source)
|
||||
.via(transactionMapper.transactionsMapper)
|
||||
)
|
||||
|
||||
def createdEvent(contractId: String) =
|
||||
Event(
|
||||
Created(
|
||||
CreatedEvent(
|
||||
"createdEventId",
|
||||
contractId,
|
||||
Some(Identifier("pkgId", "modName", "createdTemplateId")),
|
||||
None,
|
||||
Some(Record()),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def archivedEvent(contractId: String) =
|
||||
Event(
|
||||
Archived(
|
||||
ArchivedEvent(
|
||||
"archivedEventId",
|
||||
contractId,
|
||||
Some(Identifier("pkgId", "modName", "archivedTemplateId")),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def domainCreatedEvent(contractId: String) =
|
||||
DomainCreatedEvent(
|
||||
EventId("createdEventId"),
|
||||
ContractId(contractId),
|
||||
TemplateId(Identifier("pkgId", "modName", "createdTemplateId")),
|
||||
List.empty,
|
||||
CreateArguments(Record()),
|
||||
mockContract,
|
||||
)
|
||||
|
||||
def domainArchivedEvent(contractId: String) =
|
||||
DomainArchivedEvent(
|
||||
EventId("archivedEventId"),
|
||||
ContractId(contractId),
|
||||
TemplateId(Identifier("pkgId", "modName", "archivedTemplateId")),
|
||||
List.empty,
|
||||
)
|
||||
|
||||
case class MockTemplate() extends Template[MockTemplate] {
|
||||
override protected[this] def templateCompanion(implicit
|
||||
d: DummyImplicit
|
||||
): TemplateCompanion[MockTemplate] =
|
||||
new TemplateCompanion.Empty[MockTemplate] {
|
||||
override val onlyInstance = MockTemplate()
|
||||
override val id: Primitive.TemplateId[MockTemplate] =
|
||||
` templateId`("packageId", "modName", "templateId")
|
||||
override val consumingChoices: Set[Choice] = Set.empty
|
||||
}
|
||||
}
|
||||
|
||||
private val now = Instant.now()
|
||||
private val time = Timestamp(now.getEpochSecond, now.getNano)
|
||||
|
||||
private def transaction(events: Event*) =
|
||||
Transaction("tid", "cid", "wid", Some(time), events, "0")
|
||||
|
||||
private def domainTransaction(events: DomainEvent*) =
|
||||
DomainTransaction(
|
||||
TransactionId("tid"),
|
||||
WorkflowId("wid"),
|
||||
LedgerOffset(Absolute("0")),
|
||||
CommandId("cid"),
|
||||
time,
|
||||
events,
|
||||
)
|
||||
|
||||
"DomainTransactionMapper" should {
|
||||
"should map events to domain events" in {
|
||||
val result = getResult(
|
||||
List(
|
||||
transaction(createdEvent("1")),
|
||||
transaction(createdEvent("2")),
|
||||
transaction(archivedEvent("2")),
|
||||
)
|
||||
)
|
||||
result should contain theSameElementsInOrderAs List(
|
||||
domainTransaction(domainCreatedEvent("1")),
|
||||
domainTransaction(domainCreatedEvent("2")),
|
||||
domainTransaction(domainArchivedEvent("2")),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,152 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding.retrying
|
||||
|
||||
import java.time.{Duration, Instant}
|
||||
|
||||
import org.apache.pekko.stream.scaladsl.{Flow, Sink, Source}
|
||||
import com.daml.api.util.TimeProvider
|
||||
import com.daml.ledger.api.v1.commands.Commands
|
||||
import com.daml.ledger.api.v1.completion.Completion
|
||||
import com.daml.ledger.client.binding.retrying.CommandRetryFlow.{In, Out, SubmissionFlowType}
|
||||
import com.daml.ledger.client.services.commands.CommandSubmission
|
||||
import com.daml.ledger.client.services.commands.tracker.CompletionResponse
|
||||
import com.daml.ledger.client.services.commands.tracker.CompletionResponse.NotOkResponse
|
||||
import com.daml.ledger.client.testing.PekkoTest
|
||||
import com.daml.util.Ctx
|
||||
import com.google.rpc.Code
|
||||
import com.google.rpc.status.Status
|
||||
import org.scalatest.Inside
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AsyncWordSpec
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
class CommandRetryFlowUT extends AsyncWordSpec with Matchers with PekkoTest with Inside {
|
||||
|
||||
/** Uses the status received in the context for the first time,
|
||||
* then replies OK status as the ledger effective time is stepped.
|
||||
*/
|
||||
val mockCommandSubmission: SubmissionFlowType[RetryInfo[Status, CommandSubmission]] =
|
||||
Flow[In[RetryInfo[Status, CommandSubmission]]]
|
||||
.map {
|
||||
case Ctx(
|
||||
context @ RetryInfo(_, nrOfRetries, _, status),
|
||||
CommandSubmission(commands, _),
|
||||
_,
|
||||
) =>
|
||||
// Return a completion based on the input status code only on the first submission.
|
||||
if (nrOfRetries == 0) {
|
||||
Ctx(
|
||||
context,
|
||||
CompletionResponse(
|
||||
completion = Completion(commands.commandId, Some(status)),
|
||||
checkpoint = None,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
Ctx(
|
||||
context,
|
||||
Right(
|
||||
CompletionResponse.CompletionSuccess(
|
||||
completion = Completion(commands.commandId, Some(status)),
|
||||
checkpoint = None,
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
case x =>
|
||||
throw new RuntimeException(s"Unexpected input: '$x'")
|
||||
}
|
||||
|
||||
private val timeProvider = TimeProvider.Constant(Instant.ofEpochSecond(60))
|
||||
private val maxRetryTime = Duration.ofSeconds(30)
|
||||
|
||||
val retryFlow: SubmissionFlowType[RetryInfo[Status, CommandSubmission]] =
|
||||
CommandRetryFlow.createGraph(mockCommandSubmission, timeProvider, maxRetryTime)
|
||||
|
||||
private def submitRequest(
|
||||
statusCode: Int,
|
||||
time: Instant,
|
||||
): Future[Seq[Out[RetryInfo[Status, CommandSubmission]]]] = {
|
||||
val request = CommandSubmission(
|
||||
Commands(
|
||||
ledgerId = "ledgerId",
|
||||
workflowId = "workflowId",
|
||||
applicationId = "applicationId",
|
||||
commandId = "commandId",
|
||||
party = "party",
|
||||
commands = Seq.empty,
|
||||
)
|
||||
)
|
||||
val input = Ctx(
|
||||
context = RetryInfo(
|
||||
value = request,
|
||||
nrOfRetries = 0,
|
||||
firstSubmissionTime = time,
|
||||
ctx = Status(statusCode, "message", Seq.empty),
|
||||
),
|
||||
value = request,
|
||||
)
|
||||
Source.single(input).via(retryFlow).runWith(Sink.seq)
|
||||
}
|
||||
|
||||
"command retry flow" should {
|
||||
|
||||
"propagate OK status" in {
|
||||
submitRequest(Code.OK_VALUE, Instant.ofEpochSecond(45)) map { result =>
|
||||
inside(result) { case Seq(Ctx(context, Right(_), _)) =>
|
||||
context.nrOfRetries shouldBe 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"fail on all codes but RESOURCE_EXHAUSTED and UNAVAILABLE status" in {
|
||||
val codesToFail =
|
||||
Code
|
||||
.values()
|
||||
.toList
|
||||
.filterNot(c =>
|
||||
c == Code.UNRECOGNIZED || CommandRetryFlow.RETRYABLE_ERROR_CODES
|
||||
.contains(c.getNumber) || c == Code.OK
|
||||
)
|
||||
val failedSubmissions = codesToFail.map { code =>
|
||||
submitRequest(code.getNumber, Instant.ofEpochSecond(45)) map { result =>
|
||||
inside(result) { case Seq(Ctx(context, Left(notOk: NotOkResponse), _)) =>
|
||||
context.nrOfRetries shouldBe 0
|
||||
notOk.grpcStatus.code shouldBe code.getNumber
|
||||
}
|
||||
}
|
||||
}
|
||||
Future.sequence(failedSubmissions).map(_ => succeed)
|
||||
}
|
||||
|
||||
"retry RESOURCE_EXHAUSTED status" in {
|
||||
submitRequest(Code.RESOURCE_EXHAUSTED_VALUE, Instant.ofEpochSecond(45)) map { result =>
|
||||
inside(result) { case Seq(Ctx(context, Right(_), _)) =>
|
||||
context.nrOfRetries shouldBe 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"retry UNAVAILABLE status" in {
|
||||
submitRequest(Code.UNAVAILABLE_VALUE, Instant.ofEpochSecond(45)) map { result =>
|
||||
inside(result) { case Seq(Ctx(context, Right(_), _)) =>
|
||||
context.nrOfRetries shouldBe 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"stop retrying after maxRetryTime" in {
|
||||
submitRequest(Code.RESOURCE_EXHAUSTED_VALUE, Instant.ofEpochSecond(15)) map { result =>
|
||||
inside(result) { case Seq(Ctx(context, Left(notOk: NotOkResponse), _)) =>
|
||||
context.nrOfRetries shouldBe 0
|
||||
notOk.grpcStatus.code shouldBe Code.RESOURCE_EXHAUSTED_VALUE.intValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding.util
|
||||
|
||||
import java.util.concurrent.{Executors, ScheduledExecutorService}
|
||||
|
||||
import com.daml.ledger.client.binding.util.ScalaUtil.FutureOps
|
||||
import org.scalatest.concurrent.AsyncTimeLimitedTests
|
||||
import org.scalatest.time.Span
|
||||
import org.scalatest.time.SpanSugar._
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AsyncWordSpec
|
||||
|
||||
import scala.concurrent.{Future, Promise, TimeoutException}
|
||||
|
||||
class ScalaUtilIT
|
||||
extends AsyncWordSpec
|
||||
with AsyncTimeLimitedTests
|
||||
with Matchers
|
||||
with BeforeAndAfterAll {
|
||||
|
||||
implicit val scheduler: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor()
|
||||
|
||||
override def afterAll(): Unit = {
|
||||
scheduler.shutdownNow()
|
||||
super.afterAll()
|
||||
}
|
||||
|
||||
"FutureOps" can {
|
||||
|
||||
"future with timeout" should {
|
||||
|
||||
"fail Future with TimoutException after specified duration" in {
|
||||
val promise = Promise[Unit]() // never completes
|
||||
val future = promise.future.timeout("name", 1000.millis, 100.millis)
|
||||
recoverToSucceededIf[TimeoutException](future)
|
||||
}
|
||||
|
||||
"be able to complete within specified duration" in {
|
||||
val future = Future {
|
||||
"result"
|
||||
}.timeoutWithDefaultWarn("name", 1.second)
|
||||
|
||||
future.map(_ shouldBe "result")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
override lazy val timeLimit: Span = 10.seconds
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
# Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
load(
|
||||
"//bazel_tools:scala.bzl",
|
||||
"da_scala_library",
|
||||
"da_scala_test_suite",
|
||||
"kind_projector_plugin",
|
||||
)
|
||||
load("@scala_version//:index.bzl", "scala_major_version")
|
||||
|
||||
da_scala_library(
|
||||
name = "bindings",
|
||||
srcs =
|
||||
glob(["src/main/scala/**/*.scala"]) + glob([
|
||||
"src/main/{}/**/*.scala".format(scala_major_version),
|
||||
]),
|
||||
plugins = [
|
||||
kind_projector_plugin,
|
||||
],
|
||||
scala_deps = [
|
||||
"@maven//:org_scalaz_scalaz_core",
|
||||
],
|
||||
scala_exports = [
|
||||
"@maven//:org_scalaz_scalaz_core",
|
||||
],
|
||||
tags = ["maven_coordinates=com.daml:bindings-scala:__VERSION__"],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
exports = [
|
||||
"//canton:ledger_api_proto_java",
|
||||
"//canton:ledger_api_proto_scala",
|
||||
"//daml-lf/data",
|
||||
"@maven//:io_grpc_grpc_core",
|
||||
],
|
||||
deps = [
|
||||
"//canton:ledger_api_proto_scala",
|
||||
"//daml-lf/data",
|
||||
"@maven//:io_grpc_grpc_core",
|
||||
],
|
||||
)
|
||||
|
||||
da_scala_test_suite(
|
||||
name = "tests",
|
||||
size = "small",
|
||||
srcs = glob(["src/test/**/*.scala"]),
|
||||
plugins = [
|
||||
kind_projector_plugin,
|
||||
],
|
||||
scala_deps = [
|
||||
"@maven//:com_chuusai_shapeless",
|
||||
"@maven//:org_scalacheck_scalacheck",
|
||||
"@maven//:org_scalatest_scalatest_core",
|
||||
"@maven//:org_scalatest_scalatest_matchers_core",
|
||||
"@maven//:org_scalatest_scalatest_shouldmatchers",
|
||||
"@maven//:org_scalatest_scalatest_wordspec",
|
||||
"@maven//:org_scalatestplus_scalacheck_1_15",
|
||||
"@maven//:org_scalaz_scalaz_core",
|
||||
],
|
||||
deps = [
|
||||
":bindings",
|
||||
"@maven//:org_scalatest_scalatest_compatible",
|
||||
],
|
||||
)
|
@ -1,20 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
import scala.collection.immutable
|
||||
|
||||
object Compat {
|
||||
private[binding] type MapLike[
|
||||
K,
|
||||
+V,
|
||||
+CC[X, +Y] <: immutable.MapOps[X, Y, CC, _],
|
||||
+C <: immutable.MapOps[K, V, CC, C],
|
||||
] =
|
||||
immutable.MapOps[K, V, CC, C]
|
||||
|
||||
private[binding] type MapFactory[CC[K, V]] = scala.collection.MapFactory[CC]
|
||||
|
||||
type DummyImplicit = scala.DummyImplicit
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.api.util
|
||||
|
||||
import java.time.{Duration => JDuration}
|
||||
|
||||
import com.google.protobuf.duration.{Duration => PDuration}
|
||||
|
||||
object DurationConversion {
|
||||
|
||||
def toProto(jDuration: JDuration): PDuration = PDuration(jDuration.getSeconds, jDuration.getNano)
|
||||
|
||||
def fromProto(pDuration: PDuration): JDuration =
|
||||
JDuration.ofSeconds(pDuration.seconds, pDuration.nanos.toLong)
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.api.util
|
||||
|
||||
import java.time.Duration
|
||||
|
||||
final case class LedgerEffectiveTimeTolerance(transactionLatency: Duration, skew: Duration)
|
||||
extends ToleranceWindow {
|
||||
|
||||
override val toleranceInPast: Duration = transactionLatency.plus(skew)
|
||||
|
||||
override val toleranceInFuture: Duration = skew
|
||||
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.api.util
|
||||
|
||||
import java.time.{Clock, Instant}
|
||||
|
||||
import com.daml.api.util.TimeProvider.MappedTimeProvider
|
||||
import com.daml.lf.data.Time.Timestamp
|
||||
|
||||
trait TimeProvider { self =>
|
||||
|
||||
def getCurrentTime: Instant
|
||||
|
||||
def getCurrentTimestamp: Timestamp = Timestamp.assertFromInstant(getCurrentTime)
|
||||
|
||||
def map(transform: Instant => Instant): TimeProvider = MappedTimeProvider(this, transform)
|
||||
}
|
||||
|
||||
object TimeProvider {
|
||||
final case class MappedTimeProvider(timeProvider: TimeProvider, transform: Instant => Instant)
|
||||
extends TimeProvider {
|
||||
override def getCurrentTime: Instant = transform(timeProvider.getCurrentTime)
|
||||
}
|
||||
|
||||
final case class Constant(getCurrentTime: Instant) extends TimeProvider
|
||||
|
||||
case object UTC extends TimeProvider {
|
||||
|
||||
private val utcClock = Clock.systemUTC()
|
||||
|
||||
override def getCurrentTime: Instant = utcClock.instant()
|
||||
}
|
||||
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.api.util
|
||||
|
||||
import java.time.Instant
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import com.daml.ledger.api.v1.value.Value
|
||||
import com.google.protobuf.timestamp.{Timestamp => ProtoTimestamp}
|
||||
import com.daml.lf.data.Time.{Timestamp => LfTimestamp}
|
||||
|
||||
object TimestampConversion {
|
||||
val MIN = Instant parse "0001-01-01T00:00:00Z"
|
||||
val MAX = Instant parse "9999-12-31T23:59:59.999999Z"
|
||||
|
||||
def microsToInstant(micros: Value.Sum.Timestamp): Instant = {
|
||||
val seconds = TimeUnit.MICROSECONDS.toSeconds(micros.value)
|
||||
val deltaMicros = micros.value - TimeUnit.SECONDS.toMicros(seconds)
|
||||
Instant.ofEpochSecond(seconds, TimeUnit.MICROSECONDS.toNanos(deltaMicros))
|
||||
}
|
||||
|
||||
def instantToMicros(t: Instant): Value.Sum.Timestamp = {
|
||||
if (t.getNano % 1000 != 0)
|
||||
throw new IllegalArgumentException(
|
||||
s"Conversion of Instant $t to microsecond granularity would result in loss of precision."
|
||||
)
|
||||
else
|
||||
Value.Sum.Timestamp(
|
||||
TimeUnit.SECONDS.toMicros(t.getEpochSecond) + TimeUnit.NANOSECONDS
|
||||
.toMicros(t.getNano.toLong)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
def roundInstantToMicros(t: Instant): Value.Sum.Timestamp = {
|
||||
instantToMicros(roundToMicros(t, ConversionMode.HalfUp))
|
||||
}
|
||||
|
||||
def toInstant(protoTimestamp: ProtoTimestamp): Instant = {
|
||||
Instant.ofEpochSecond(protoTimestamp.seconds, protoTimestamp.nanos.toLong)
|
||||
}
|
||||
|
||||
def fromInstant(instant: Instant): ProtoTimestamp = {
|
||||
new ProtoTimestamp().withSeconds(instant.getEpochSecond).withNanos(instant.getNano)
|
||||
}
|
||||
|
||||
def toLf(protoTimestamp: ProtoTimestamp, mode: ConversionMode): LfTimestamp = {
|
||||
val instant = roundToMicros(toInstant(protoTimestamp), mode)
|
||||
LfTimestamp.assertFromInstant(instant)
|
||||
}
|
||||
|
||||
def fromLf(timestamp: LfTimestamp): ProtoTimestamp = {
|
||||
fromInstant(timestamp.toInstant)
|
||||
}
|
||||
|
||||
private def roundToMicros(t: Instant, mode: ConversionMode): Instant = {
|
||||
val fractionNanos = t.getNano % 1000L
|
||||
if (fractionNanos != 0) {
|
||||
mode match {
|
||||
case ConversionMode.Exact =>
|
||||
throw new IllegalArgumentException(
|
||||
s"Conversion of $t to microsecond granularity would result in loss of precision."
|
||||
)
|
||||
case ConversionMode.HalfUp =>
|
||||
t.plusNanos(if (fractionNanos >= 500L) 1000L - fractionNanos else -fractionNanos)
|
||||
}
|
||||
} else {
|
||||
t
|
||||
}
|
||||
}
|
||||
|
||||
sealed trait ConversionMode
|
||||
object ConversionMode {
|
||||
|
||||
/** Throw an exception if the input can not be represented in microsecond resolution */
|
||||
case object Exact extends ConversionMode
|
||||
|
||||
/** Round to the nearest microsecond */
|
||||
case object HalfUp extends ConversionMode
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.api.util
|
||||
|
||||
import java.time.Duration
|
||||
|
||||
trait ToleranceWindow {
|
||||
|
||||
def toleranceInPast: Duration
|
||||
def toleranceInFuture: Duration
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.api.refinements
|
||||
|
||||
import com.daml.ledger.api.v1.value.{Identifier, Record, Value}
|
||||
import scalaz.{@@, Tag}
|
||||
|
||||
object ApiTypes {
|
||||
|
||||
sealed trait TransactionIdTag
|
||||
type TransactionId = String @@ TransactionIdTag
|
||||
val TransactionId = Tag.of[TransactionIdTag]
|
||||
|
||||
sealed trait CommandIdTag
|
||||
type CommandId = String @@ CommandIdTag
|
||||
val CommandId = Tag.of[CommandIdTag]
|
||||
|
||||
sealed trait WorkflowIdTag
|
||||
type WorkflowId = String @@ WorkflowIdTag
|
||||
val WorkflowId = Tag.of[WorkflowIdTag]
|
||||
|
||||
sealed trait EventIdTag
|
||||
type EventId = String @@ EventIdTag
|
||||
val EventId = Tag.of[EventIdTag]
|
||||
|
||||
sealed trait TemplateIdTag
|
||||
type TemplateId = Identifier @@ TemplateIdTag
|
||||
val TemplateId = Tag.of[TemplateIdTag]
|
||||
|
||||
sealed trait InterfaceIdTag
|
||||
type InterfaceId = Identifier @@ InterfaceIdTag
|
||||
val InterfaceId = Tag.of[InterfaceIdTag]
|
||||
|
||||
sealed trait ApplicationIdTag
|
||||
type ApplicationId = String @@ ApplicationIdTag
|
||||
val ApplicationId = Tag.of[ApplicationIdTag]
|
||||
|
||||
sealed trait LedgerIdTag
|
||||
type LedgerId = String @@ LedgerIdTag
|
||||
val LedgerId = Tag.of[LedgerIdTag]
|
||||
|
||||
sealed trait ContractIdTag
|
||||
type ContractId = String @@ ContractIdTag
|
||||
val ContractId = Tag.of[ContractIdTag]
|
||||
|
||||
sealed trait ChoiceTag
|
||||
type Choice = String @@ ChoiceTag
|
||||
val Choice = Tag.of[ChoiceTag]
|
||||
|
||||
sealed trait CreateArgumentsTag
|
||||
type CreateArguments = Record @@ CreateArgumentsTag
|
||||
val CreateArguments = Tag.of[CreateArgumentsTag]
|
||||
|
||||
sealed trait ChoiceArgumentTag
|
||||
type ChoiceArgument = Value @@ ChoiceArgumentTag
|
||||
val ChoiceArgument = Tag.of[ChoiceArgumentTag]
|
||||
|
||||
sealed trait PartyTag
|
||||
type Party = String @@ PartyTag
|
||||
val Party = Tag.of[PartyTag]
|
||||
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.api.refinements
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes.{CommandId, Party, WorkflowId}
|
||||
import com.daml.ledger.api.v1.commands.Command
|
||||
|
||||
final case class CompositeCommand(
|
||||
commands: Seq[Command],
|
||||
party: Party,
|
||||
commandId: CommandId,
|
||||
workflowId: WorkflowId,
|
||||
)
|
@ -1,30 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.api.refinements
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes.{ApplicationId, LedgerId}
|
||||
import com.daml.ledger.api.v1.commands.Commands
|
||||
import scalaz.syntax.tag._
|
||||
|
||||
class CompositeCommandAdapter(
|
||||
ledgerId: LedgerId,
|
||||
applicationId: ApplicationId,
|
||||
) {
|
||||
def transform(c: CompositeCommand): Commands =
|
||||
Commands(
|
||||
ledgerId = ledgerId.unwrap,
|
||||
workflowId = c.workflowId.unwrap,
|
||||
applicationId = applicationId.unwrap,
|
||||
commandId = c.commandId.unwrap,
|
||||
party = c.party.unwrap,
|
||||
commands = c.commands,
|
||||
)
|
||||
}
|
||||
|
||||
object CompositeCommandAdapter {
|
||||
def apply(
|
||||
ledgerId: LedgerId,
|
||||
applicationId: ApplicationId,
|
||||
) = new CompositeCommandAdapter(ledgerId, applicationId)
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.api.refinements
|
||||
|
||||
import scalaz.{@@, Tag}
|
||||
|
||||
import scala.util.Random
|
||||
|
||||
class IdGenerator[TypeTag](seed: Long) {
|
||||
|
||||
private val rand = new Random(seed)
|
||||
|
||||
def generateRandom: String @@ TypeTag = Tag.apply(rand.nextLong().toHexString)
|
||||
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
import com.daml.ledger.api.v1.{value => rpcvalue}
|
||||
|
||||
/** A class representing a Daml contract of specific type (Daml template) with assigned contract ID.
|
||||
*
|
||||
* @param contractId Contract ID.
|
||||
* @param value Contract instance as defined in Daml template (without `contractId`).
|
||||
* @param signatories Signatories of the contract as defined in the Daml template
|
||||
* @param observers Observers of the contract, both explicitly as defined in the Daml template and implicitly as
|
||||
* choice controllers.
|
||||
* @param key The value of the key of this contract, if defined by the template.
|
||||
*
|
||||
* @tparam T Contract template type parameter.
|
||||
*/
|
||||
final case class Contract[+T](
|
||||
contractId: Primitive.ContractId[T],
|
||||
value: T with Template[T],
|
||||
signatories: Seq[String],
|
||||
observers: Seq[String],
|
||||
key: Option[rpcvalue.Value],
|
||||
) {
|
||||
def arguments: rpcvalue.Record = value.arguments
|
||||
}
|
||||
|
||||
object Contract {
|
||||
type OfAny = Contract[Any]
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes.TemplateId
|
||||
import com.daml.ledger.api.v1.{value => rpcvalue}
|
||||
import encoding.ExerciseOn
|
||||
|
||||
/** Common superclass of template and interface companions objects. */
|
||||
abstract class ContractTypeCompanion[T] extends ValueRefCompanion {
|
||||
|
||||
/** Alias for contract IDs for this template or interface. Can be used
|
||||
* interchangeably with its expansion.
|
||||
*/
|
||||
type ContractId = Primitive.ContractId[T]
|
||||
|
||||
val id: Primitive.TemplateId[T]
|
||||
|
||||
override protected lazy val ` dataTypeId` = TemplateId.unwrap(id)
|
||||
|
||||
protected final def ` templateId`(
|
||||
packageId: String,
|
||||
moduleName: String,
|
||||
entityName: String,
|
||||
): Primitive.TemplateId[T] =
|
||||
Primitive.TemplateId(packageId, moduleName, entityName)
|
||||
|
||||
protected final def ` exercise`[ExOn, Out](
|
||||
receiver: ExOn,
|
||||
choiceId: String,
|
||||
arguments: Option[rpcvalue.Value],
|
||||
)(implicit exon: ExerciseOn[ExOn, T]): Primitive.Update[Out] =
|
||||
Primitive.exercise(this, receiver, choiceId, arguments getOrElse Value.encode(()))
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
import com.daml.ledger.api.v1.commands.Command
|
||||
|
||||
case class DomainCommand(command: Command, template: ContractTypeCompanion[_])
|
@ -1,41 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
import com.daml.ledger.api.v1.{value => rpcvalue}
|
||||
import com.daml.ledger.client.binding.encoding.{LfEncodable, LfTypeEncoding}
|
||||
import scalaz.Liskov.<~<
|
||||
import scalaz.OneAnd
|
||||
import scalaz.syntax.functor._
|
||||
import scalaz.std.vector._
|
||||
|
||||
abstract class EnumCompanion[T](implicit isEnum: T <~< EnumRef) extends ValueRefCompanion {
|
||||
|
||||
val firstValue: T
|
||||
val otherValues: Vector[T]
|
||||
|
||||
final def values: Vector[T] = firstValue +: otherValues
|
||||
|
||||
implicit final lazy val `the enum Value`: Value[T] = new `Value ValueRef`[T] {
|
||||
private[this] val readers = values.map(e => (isEnum(e).constructor: String) -> e).toMap
|
||||
|
||||
override def read(argValue: rpcvalue.Value.Sum): Option[T] =
|
||||
argValue.enum flatMap (e => readers.get(e.constructor))
|
||||
|
||||
private[this] val rpcValues = values.map(e => ` enum`(isEnum(e).constructor))
|
||||
|
||||
override def write(enumeration: T): rpcvalue.Value.Sum =
|
||||
rpcValues(isEnum(enumeration).index)
|
||||
}
|
||||
|
||||
implicit final lazy val `the enum LfEncodable`: LfEncodable[T] = new LfEncodable[T] {
|
||||
|
||||
private[this] val cases = OneAnd(firstValue, otherValues).map(x => isEnum(x).constructor -> x)
|
||||
|
||||
override def encoding(lte: LfTypeEncoding): lte.Out[T] =
|
||||
lte.enumAll(` dataTypeId`, isEnum(_).index, cases)
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
abstract class EnumRef extends ValueRef {
|
||||
val constructor: String
|
||||
val index: Int
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes
|
||||
|
||||
import scala.collection.immutable.{Map, Seq}
|
||||
import scalaz.Id.Id
|
||||
import com.daml.ledger.api.v1.{event => rpcevent, value => rpcvalue}
|
||||
|
||||
abstract class EventDecoderApi(val templateTypes: Seq[TemplateCompanion[_]]) {
|
||||
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Any"))
|
||||
val decoderTable: Map[ApiTypes.TemplateId, rpcevent.CreatedEvent => Option[Template[_]]] =
|
||||
templateTypes.map(_.decoderEntry).toMap
|
||||
|
||||
private[this] val dtl = {
|
||||
type F[A] = A => Option[rpcevent.CreatedEvent => Option[Template[_]]]
|
||||
ApiTypes.TemplateId.unsubst[F, rpcvalue.Identifier](decoderTable.lift)
|
||||
}
|
||||
|
||||
@SuppressWarnings(Array("org.wartremover.warts.Any"))
|
||||
final def createdEventToContractRef(
|
||||
createdEvent: rpcevent.CreatedEvent
|
||||
): Either[EventDecoderError, Contract.OfAny] = {
|
||||
for {
|
||||
templateToContract <- createdEvent.templateId flatMap dtl toRight DecoderTableLookupFailure
|
||||
tadt <- templateToContract(createdEvent).toRight(
|
||||
CreateEventToContractMappingError: EventDecoderError
|
||||
)
|
||||
} yield Contract(
|
||||
Primitive.substContractId[Id, Nothing](ApiTypes.ContractId(createdEvent.contractId)),
|
||||
tadt,
|
||||
createdEvent.signatories,
|
||||
createdEvent.observers,
|
||||
createdEvent.contractKey,
|
||||
)
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
sealed trait EventDecoderError extends Product with Serializable
|
||||
case object DecoderTableLookupFailure extends EventDecoderError
|
||||
case object CreateEventToContractMappingError extends EventDecoderError
|
@ -1,39 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes
|
||||
|
||||
import scala.annotation.nowarn
|
||||
|
||||
/** Common superclass of interface marker types. There are no instances of
|
||||
* subclasses of this class; it is strictly a marker type to aid in implicit
|
||||
* resolution, and only occurs within contract IDs.
|
||||
*/
|
||||
abstract class Interface extends VoidValueRef
|
||||
|
||||
object Interface {
|
||||
import Primitive.ContractId, ContractId.subst
|
||||
|
||||
implicit final class `interface ContractId syntax`[I](private val self: ContractId[I])
|
||||
extends AnyVal {
|
||||
|
||||
/** Convert an interface contract ID to a template contract ID. Sometimes
|
||||
* this is needed if you got an interface contract ID from a choice, but
|
||||
* you need to assert that the contract ID is of a particular template
|
||||
* so that you can exercise contracts on it.
|
||||
*
|
||||
* This checks at compile-time that `T` is in fact a template that
|
||||
* implements interface `I`, but it does not check that the specific
|
||||
* contract ID is actually associated with `T` on the ledger, hence the
|
||||
* `unsafe` in the name.
|
||||
*/
|
||||
@nowarn("cat=unused&msg=parameter ev in method")
|
||||
def unsafeToTemplate[T](implicit ev: Template.Implements[T, I]): ContractId[T] = {
|
||||
type K[C] = C => ApiTypes.ContractId
|
||||
type K2[C] = ContractId[I] => C
|
||||
subst[K2, T](subst[K, I](identity))(self)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
/** Common superclass of interface companion objects. */
|
||||
abstract class InterfaceCompanion[T] extends ContractTypeCompanion[T]
|
@ -1,337 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
import java.time.{Instant, LocalDate, LocalDateTime}
|
||||
import java.util.TimeZone
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes
|
||||
import com.daml.ledger.api.v1.{commands => rpccmd, value => rpcvalue}
|
||||
import com.daml.ledger.client.binding.encoding.ExerciseOn
|
||||
import scalaz.syntax.std.boolean._
|
||||
import scalaz.syntax.tag._
|
||||
|
||||
import scala.annotation.nowarn
|
||||
import scala.collection.{Factory, mutable, immutable => imm}
|
||||
|
||||
sealed abstract class Primitive extends PrimitiveInstances {
|
||||
type Int64 = Long
|
||||
type Numeric = BigDecimal
|
||||
type Party = ApiTypes.Party
|
||||
val Party: ApiTypes.Party.type = ApiTypes.Party
|
||||
type Text = String
|
||||
|
||||
/** A [[LocalDate]] in the range [0001-01-01, 9999-12-31] (`[Date.MIN,
|
||||
* Date.MAX]`). This is the range that can be stored as primitive
|
||||
* `Date`s on a ledger, and matches the set of representable dates in
|
||||
* the [RFC-3339](https://www.ietf.org/rfc/rfc3339.txt) date-time
|
||||
* format. Any [[LocalDate]] in that range can be converted to a
|
||||
* [[Date]] by calling `Date.fromLocalDate`.
|
||||
*/
|
||||
type Date <: LocalDate
|
||||
val Date: DateApi
|
||||
|
||||
/** An [[Instant]] with only microsecond resolution in the range
|
||||
* [0001-01-01T00:00:00Z, 9999-12-31T23:59:59.999999Z] (`[Timestamp.MIN,
|
||||
* Timestamp.MAX]`). Only such times can be stored as primitive `Time`s
|
||||
* on the ledger. Any [[Instant]] in that range can be converted to
|
||||
* a [[Timestamp]] by calling `Time.discardNanos`.
|
||||
*/
|
||||
type Timestamp <: Instant
|
||||
val Timestamp: TimeApi
|
||||
type Unit = scala.Unit
|
||||
type Bool = scala.Boolean
|
||||
type List[+A] = imm.Seq[A]
|
||||
val List: imm.Seq.type = imm.Seq
|
||||
|
||||
type Optional[+A] = scala.Option[A]
|
||||
val Optional: scala.Option.type = scala.Option
|
||||
|
||||
type TextMap[+V] <: imm.Map[String, V] with Compat.MapLike[String, V, imm.Map, TextMap[V]]
|
||||
val TextMap: TextMapApi
|
||||
|
||||
@deprecated("Use TextMap", since = "0.13.40")
|
||||
type Map[+V] = TextMap[V]
|
||||
@deprecated("Use TextMap", since = "0.13.40")
|
||||
val Map: TextMap.type
|
||||
|
||||
type GenMap[K, +V] <: imm.Map[K, V] with Compat.MapLike[K, V, GenMap, GenMap[K, V]]
|
||||
val GenMap: Compat.MapFactory[GenMap]
|
||||
private[binding] def substGenMap[F[_[_, _]]](tc: F[imm.Map]): F[GenMap]
|
||||
|
||||
type ChoiceId = ApiTypes.Choice
|
||||
val ChoiceId: ApiTypes.Choice.type = ApiTypes.Choice
|
||||
|
||||
// abstract primitives
|
||||
type ContractId[+Tpl] <: ApiTypes.ContractId
|
||||
val ContractId: ContractIdApi
|
||||
type TemplateId[+Tpl] <: ApiTypes.TemplateId
|
||||
val TemplateId: TemplateIdApi
|
||||
type Update[+A] <: DomainCommand
|
||||
|
||||
sealed abstract class TextMapApi {
|
||||
def empty[V]: TextMap[V]
|
||||
def apply[V](elems: (String, V)*): TextMap[V]
|
||||
def newBuilder[V]: mutable.Builder[(String, V), TextMap[V]]
|
||||
implicit def factory[V]: Factory[(String, V), TextMap[V]]
|
||||
final def fromMap[V](map: imm.Map[String, V]): TextMap[V] = leibniz[V](map)
|
||||
def subst[F[_[_]]](fa: F[imm.Map[String, *]]): F[TextMap]
|
||||
final def leibniz[V]: imm.Map[String, V] =:= TextMap[V] =
|
||||
subst[Lambda[g[_] => imm.Map[String, V] =:= g[V]]](
|
||||
implicitly[imm.Map[String, V] =:= imm.Map[String, V]]
|
||||
)
|
||||
}
|
||||
|
||||
sealed abstract class DateApi {
|
||||
val MIN: Date
|
||||
val MAX: Date
|
||||
|
||||
/** Narrow `ld` if it's in the `[MIN, MAX]` range, `None` otherwise. */
|
||||
def fromLocalDate(ld: LocalDate): Option[Date]
|
||||
|
||||
// bypass the value test
|
||||
private[binding] def subst[F[_]](tc: F[LocalDate]): F[Date]
|
||||
}
|
||||
|
||||
sealed abstract class TimeApi {
|
||||
val MIN: Timestamp
|
||||
val MAX: Timestamp
|
||||
|
||||
/** Reduce `t`'s resolution to exclude nanoseconds; return `None` if outside
|
||||
* the `[MIN, MAX]` range, the reduced-resolution value
|
||||
* otherwise.
|
||||
*/
|
||||
def discardNanos(t: Instant): Option[Timestamp]
|
||||
|
||||
// bypass the value test
|
||||
private[binding] def subst[F[_]](tc: F[Instant]): F[Timestamp]
|
||||
}
|
||||
|
||||
sealed abstract class ContractIdApi {
|
||||
def apply[Tpl](contractId: String): ContractId[Tpl]
|
||||
def subst[F[_], Tpl](tc: F[ApiTypes.ContractId]): F[ContractId[Tpl]]
|
||||
}
|
||||
|
||||
sealed abstract class TemplateIdApi {
|
||||
// the sole source of valid Template-associated template IDs is
|
||||
// their codegenned companions
|
||||
def apply[Tpl <: Template[Tpl]](
|
||||
packageId: String,
|
||||
moduleName: String,
|
||||
entityName: String,
|
||||
): TemplateId[Tpl]
|
||||
|
||||
private[binding] def substEx[F[_]](fa: F[rpcvalue.Identifier]): F[TemplateId[_]]
|
||||
|
||||
/** Package ID, module name, and entity name.
|
||||
*/
|
||||
def unapply[Tpl](t: TemplateId[Tpl]): Option[(String, String, String)]
|
||||
}
|
||||
|
||||
private[binding] def substContractId[F[_], Tpl](tc: F[ApiTypes.ContractId]): F[ContractId[Tpl]]
|
||||
|
||||
private[binding] def createFromArgs[Tpl](
|
||||
companion: TemplateCompanion[_ <: Tpl],
|
||||
na: rpcvalue.Record,
|
||||
): Update[ContractId[Tpl]]
|
||||
|
||||
private[binding] def exercise[ExOn, Tpl, Out](
|
||||
templateCompanion: ContractTypeCompanion[Tpl],
|
||||
receiver: ExOn,
|
||||
choiceId: String,
|
||||
argument: rpcvalue.Value,
|
||||
)(implicit ev: ExerciseOn[ExOn, Tpl]): Update[Out]
|
||||
|
||||
private[binding] def arguments(
|
||||
recordId: rpcvalue.Identifier,
|
||||
args: Seq[(String, rpcvalue.Value)],
|
||||
): rpcvalue.Record
|
||||
}
|
||||
|
||||
private[client] object OnlyPrimitive extends Primitive {
|
||||
type Date = LocalDate
|
||||
type Timestamp = Instant
|
||||
type ContractId[+Tpl] = ApiTypes.ContractId
|
||||
type TemplateId[+Tpl] = ApiTypes.TemplateId
|
||||
type Update[+A] = DomainCommand
|
||||
|
||||
type TextMap[+V] = imm.Map[String, V]
|
||||
|
||||
object TextMap extends TextMapApi {
|
||||
override def empty[V]: TextMap[V] = imm.Map.empty
|
||||
override def apply[V](elems: (String, V)*): TextMap[V] = imm.Map(elems: _*)
|
||||
override def newBuilder[V]: mutable.Builder[(String, V), TextMap[V]] = imm.Map.newBuilder
|
||||
override def factory[V]: Factory[(String, V), TextMap[V]] =
|
||||
implicitly[Factory[(String, V), imm.Map[String, V]]]
|
||||
override def subst[F[_[_]]](fa: F[TextMap]): F[TextMap] = fa
|
||||
}
|
||||
|
||||
override val Map = TextMap
|
||||
|
||||
type GenMap[K, +V] = imm.Map[K, V]
|
||||
override val GenMap = imm.Map
|
||||
private[binding] override def substGenMap[F[_[_, _]]](tc: F[GenMap]) = tc
|
||||
|
||||
object Date extends DateApi {
|
||||
import com.daml.api.util.TimestampConversion
|
||||
private val UTC = TimeZone.getTimeZone("UTC")
|
||||
override val MIN = LocalDateTime.ofInstant(TimestampConversion.MIN, UTC.toZoneId).toLocalDate
|
||||
override val MAX = LocalDateTime.ofInstant(TimestampConversion.MAX, UTC.toZoneId).toLocalDate
|
||||
|
||||
override def fromLocalDate(ld: LocalDate) = {
|
||||
import scala.math.Ordering.Implicits._
|
||||
val ldc: java.time.chrono.ChronoLocalDate = ld
|
||||
(ldc >= MIN && ldc <= MAX) option ld
|
||||
}
|
||||
|
||||
private[binding] override def subst[F[_]](tc: F[LocalDate]) = tc
|
||||
}
|
||||
|
||||
object Timestamp extends TimeApi {
|
||||
import com.daml.api.util.TimestampConversion
|
||||
override val MIN = TimestampConversion.MIN
|
||||
override val MAX = TimestampConversion.MAX
|
||||
|
||||
override def discardNanos(t: Instant) = {
|
||||
import scala.math.Ordering.Implicits._
|
||||
(t >= MIN && t <= MAX) option (t truncatedTo java.time.temporal.ChronoUnit.MICROS)
|
||||
}
|
||||
|
||||
private[binding] override def subst[F[_]](tc: F[Instant]) = tc
|
||||
}
|
||||
|
||||
object TemplateId extends TemplateIdApi {
|
||||
// the ledger api still uses names with only dots in them, while QualifiedName.toString
|
||||
// separates the module and the name in the module with colon.
|
||||
override def apply[Tpl <: Template[Tpl]](
|
||||
packageId: String,
|
||||
moduleName: String,
|
||||
entityName: String,
|
||||
): TemplateId[Tpl] =
|
||||
ApiTypes.TemplateId(
|
||||
rpcvalue
|
||||
.Identifier(packageId = packageId, moduleName = moduleName, entityName = entityName)
|
||||
)
|
||||
|
||||
private[binding] override def substEx[F[_]](fa: F[rpcvalue.Identifier]) =
|
||||
ApiTypes.TemplateId subst fa
|
||||
|
||||
override def unapply[Tpl](t: TemplateId[Tpl]): Some[(String, String, String)] = {
|
||||
val rpcvalue.Identifier(packageId, moduleName, entityName) = t.unwrap
|
||||
Some((packageId, moduleName, entityName))
|
||||
}
|
||||
}
|
||||
|
||||
object ContractId extends ContractIdApi {
|
||||
override def apply[Tpl](contractId: String) =
|
||||
ApiTypes.ContractId(contractId)
|
||||
|
||||
override def subst[F[_], Tpl](tc: F[ApiTypes.ContractId]): F[ContractId[Tpl]] = tc
|
||||
}
|
||||
|
||||
private[binding] override def substContractId[F[_], Tpl](
|
||||
tc: F[ApiTypes.ContractId]
|
||||
): F[ContractId[Tpl]] = tc
|
||||
|
||||
private[binding] override def createFromArgs[Tpl](
|
||||
companion: TemplateCompanion[_ <: Tpl],
|
||||
na: rpcvalue.Record,
|
||||
): Update[ContractId[Tpl]] =
|
||||
DomainCommand(
|
||||
rpccmd.Command(
|
||||
rpccmd.Command.Command
|
||||
.Create(rpccmd.CreateCommand(templateId = Some(companion.id.unwrap), Some(na)))
|
||||
),
|
||||
companion,
|
||||
)
|
||||
|
||||
private[binding] override def exercise[ExOn, Tpl, Out](
|
||||
exerciseTarget: ContractTypeCompanion[Tpl],
|
||||
receiver: ExOn,
|
||||
choiceId: String,
|
||||
argument: rpcvalue.Value,
|
||||
)(implicit ev: ExerciseOn[ExOn, Tpl]): Update[Out] =
|
||||
DomainCommand(
|
||||
rpccmd.Command {
|
||||
ev match {
|
||||
case _: ExerciseOn.OnId[Tpl] =>
|
||||
rpccmd.Command.Command.Exercise(
|
||||
rpccmd.ExerciseCommand(
|
||||
templateId = Some(exerciseTarget.id.unwrap),
|
||||
contractId = (receiver: ContractId[Tpl]).unwrap,
|
||||
choice = choiceId,
|
||||
choiceArgument = Some(argument),
|
||||
)
|
||||
)
|
||||
case _: ExerciseOn.CreateAndOnTemplate[Tpl] =>
|
||||
val cfe: Template.CreateForExercise[Tpl] = receiver
|
||||
rpccmd.Command.Command.CreateAndExercise(
|
||||
// TODO #13993 pass exerciseTarget.id.unwrap as interface ID
|
||||
rpccmd.CreateAndExerciseCommand(
|
||||
templateId = Some(cfe.value.templateId.unwrap),
|
||||
createArguments = Some(cfe.value.arguments),
|
||||
choice = choiceId,
|
||||
choiceArgument = Some(argument),
|
||||
)
|
||||
)
|
||||
case _: ExerciseOn.OnKey[Tpl] =>
|
||||
val k: Template.Key[Tpl] = receiver
|
||||
// TODO #13993 pass exerciseTarget.id.unwrap as interface ID
|
||||
rpccmd.Command.Command.ExerciseByKey(
|
||||
rpccmd.ExerciseByKeyCommand(
|
||||
templateId = Some(k.origin.id.unwrap),
|
||||
contractKey = Some(k.encodedKey),
|
||||
choice = choiceId,
|
||||
choiceArgument = Some(argument),
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
exerciseTarget,
|
||||
)
|
||||
|
||||
private[binding] override def arguments(
|
||||
recordId: rpcvalue.Identifier,
|
||||
args: Seq[(String, rpcvalue.Value)],
|
||||
): rpcvalue.Record =
|
||||
rpcvalue.Record(
|
||||
recordId = Some(recordId),
|
||||
args.map { case (k, v) =>
|
||||
rpcvalue.RecordField(k, Some(v))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
sealed abstract class PrimitiveInstances
|
||||
|
||||
// do not import this._, use -Xsource:2.13 scalac option instead
|
||||
object PrimitiveInstances {
|
||||
import Primitive.{GenMap, TextMap}
|
||||
|
||||
import language.implicitConversions
|
||||
|
||||
implicit def textMapFactory[V]: Factory[(String, V), TextMap[V]] =
|
||||
TextMap.factory
|
||||
|
||||
implicit def genMapFactory[K, V]: Factory[(K, V), GenMap[K, V]] = {
|
||||
type CBF[M[_, _]] = Factory[(K, V), M[K, V]]
|
||||
@nowarn("msg=local val genMapFactory in method genMapFactory is never used")
|
||||
val genMapFactory = () // prevent recursion
|
||||
GenMap.subst[CBF](implicitly[CBF[imm.Map]])
|
||||
}
|
||||
|
||||
/** Applied in contexts that ''expect'' a `TextMap`, iff -Xsource:2.13. */
|
||||
implicit def textMapFromMap[V](m: imm.Map[String, V]): TextMap[V] = TextMap fromMap m
|
||||
|
||||
/** Applied in contexts that ''expect'' a `GenMap`, iff -Xsource:2.13. */
|
||||
implicit def genMapFromMap[K, V](m: imm.Map[K, V]): GenMap[K, V] = {
|
||||
type Id2[M[_, _]] = M[K, V]
|
||||
GenMap.subst[Id2](m)
|
||||
}
|
||||
|
||||
implicit final class GenMapCompanionMethods(private val self: GenMap.type) extends AnyVal {
|
||||
private[binding] def subst[F[_[_, _]]](tc: F[imm.Map]): F[GenMap] =
|
||||
Primitive substGenMap tc
|
||||
}
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes
|
||||
import ApiTypes.Choice
|
||||
import com.daml.ledger.api.v1.{value => rpcvalue}
|
||||
|
||||
import scala.annotation.{nowarn, implicitNotFound}
|
||||
|
||||
abstract class Template[+T] extends ValueRef { self: T =>
|
||||
final def create(implicit d: DummyImplicit): Primitive.Update[Primitive.ContractId[T]] =
|
||||
Primitive.createFromArgs(templateCompanion, templateCompanion.toNamedArguments(self))
|
||||
|
||||
/** Part of a `CreateAndExercise` command.
|
||||
*
|
||||
* {{{
|
||||
* Iou(foo, bar).createAnd.exerciseTransfer(controller, ...)
|
||||
* }}}
|
||||
*/
|
||||
final def createAnd(implicit d: DummyImplicit): Template.CreateForExercise[T] =
|
||||
Template.CreateForExercise(self)
|
||||
|
||||
final def arguments(implicit d: DummyImplicit): rpcvalue.Record =
|
||||
templateCompanion.toNamedArguments(self)
|
||||
|
||||
final def templateId(implicit d: DummyImplicit): Primitive.TemplateId[T] =
|
||||
templateCompanion.id
|
||||
|
||||
final def consumingChoices(implicit d: DummyImplicit): Set[Choice] =
|
||||
templateCompanion.consumingChoices
|
||||
|
||||
// arguments and templateId are provided in lieu of making templateCompanion
|
||||
// public, though the latter might be more "powerful"
|
||||
protected[this] def templateCompanion(implicit
|
||||
d: DummyImplicit
|
||||
): TemplateCompanion[_ >: self.type <: T]
|
||||
}
|
||||
|
||||
object Template {
|
||||
|
||||
/** Part of a `CreateAndExercise` command.
|
||||
*
|
||||
* {{{
|
||||
* Iou(foo, bar).createAnd.exerciseTransfer(controller, ...)
|
||||
* }}}
|
||||
*/
|
||||
final case class CreateForExercise[+T](
|
||||
private[binding] val value: Template[_]
|
||||
) {
|
||||
|
||||
/** Get access to interface choices.
|
||||
*
|
||||
* {{{
|
||||
* MyTemplate(foo, bar).createAnd
|
||||
* .toInterface[MyInterface]
|
||||
* .exerciseInterfaceChoice(controller, ...)
|
||||
* }}}
|
||||
*/
|
||||
@nowarn("cat=unused&msg=parameter ev in method")
|
||||
def toInterface[I](implicit ev: ToInterface[T, I]): CreateForExercise[I] =
|
||||
CreateForExercise(value)
|
||||
}
|
||||
|
||||
/** Part of an `ExerciseByKey` command.
|
||||
*
|
||||
* {{{
|
||||
* Iou.key(foo).exerciseTransfer(controller, ...)
|
||||
* }}}
|
||||
*/
|
||||
final case class Key[+T](
|
||||
private[binding] val encodedKey: rpcvalue.Value,
|
||||
private[binding] val origin: TemplateCompanion[_],
|
||||
)
|
||||
|
||||
/** Evidence that coercing from template-IDed to interface-IDed is sound,
|
||||
* i.e. `toInterface`. Not safe at all for the opposite coercion.
|
||||
*
|
||||
* This weaker marker is defined so that [[Key]] and [[CreateForExercise]] can
|
||||
* have simple `toInterface` methods.
|
||||
*/
|
||||
@implicitNotFound("${T} is not a template that implements interface ${I}")
|
||||
sealed abstract class ToInterface[-T, I]
|
||||
|
||||
/** As with [[ToInterface]], but also proves that `T` implements `I`.
|
||||
* This is a subtle distinction, but [[ToInterface]] allows a universe of
|
||||
* subtypes for which this would not be true. So this can be used for
|
||||
* `unsafeToTemplate` as well as `toInterface`.
|
||||
*/
|
||||
@implicitNotFound("${T} is not a template that implements interface ${I}")
|
||||
final class Implements[T, I] extends ToInterface[T, I]
|
||||
|
||||
import Primitive.ContractId, ContractId.subst
|
||||
implicit final class `template ContractId syntax`[T](private val self: ContractId[T])
|
||||
extends AnyVal {
|
||||
|
||||
/** Widen a contract ID to the same contract ID for one of `T`'s implemented
|
||||
* interfaces. Do this to get access to the interface choices.
|
||||
*/
|
||||
@nowarn("cat=unused&msg=parameter ev in method")
|
||||
def toInterface[I](implicit ev: ToInterface[T, I]): ContractId[I] = {
|
||||
type K[C] = C => ApiTypes.ContractId
|
||||
type K2[C] = ContractId[T] => C
|
||||
subst[K2, I](subst[K, T](identity))(self)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes.Choice
|
||||
import com.daml.ledger.api.v1.{event => rpcevent, value => rpcvalue}
|
||||
import rpcvalue.Value.{Sum => VSum}
|
||||
import encoding.{LfEncodable, LfTypeEncoding, RecordView}
|
||||
|
||||
/** Common superclass of template classes' companions.
|
||||
*
|
||||
* @tparam T The companion class's type. We can get away with this here, but
|
||||
* not for [[ValueRefCompanion]], because templates' associated
|
||||
* types are guaranteed to have zero tparams.
|
||||
*/
|
||||
abstract class TemplateCompanion[T](implicit isTemplate: T <:< Template[T])
|
||||
extends ContractTypeCompanion[T]
|
||||
with LfEncodable.ViaFields[T] {
|
||||
|
||||
val consumingChoices: Set[Choice]
|
||||
|
||||
/** Permits TemplateCompanion to be optionally treated as a typeclass for
|
||||
* template types.
|
||||
*/
|
||||
implicit final def `the TemplateCompanion`: this.type = this
|
||||
|
||||
/** The template's key type, or [[Nothing]] if there is no key type. */
|
||||
type key
|
||||
|
||||
/** Prepare an exercise-by-key Update. */
|
||||
final def key(k: key)(implicit enc: ValueEncoder[key]): Template.Key[T] =
|
||||
Template.Key(Value encode k, this)
|
||||
|
||||
/** Proof that T <: Template[T]. Expressed here instead of as a type parameter
|
||||
* bound because the latter is much more inconvenient in practice.
|
||||
*/
|
||||
val describesTemplate: T <:< Template[T] = isTemplate
|
||||
|
||||
def toNamedArguments(associatedType: T): rpcvalue.Record
|
||||
|
||||
def fromNamedArguments(namedArguments: rpcvalue.Record): Option[T]
|
||||
|
||||
implicit val `the template Value`: Value[T] = new `Value ValueRef`[T] {
|
||||
override def read(argumentValue: VSum): Option[T] =
|
||||
argumentValue.record flatMap fromNamedArguments
|
||||
override def write(obj: T): VSum = VSum.Record(toNamedArguments(obj))
|
||||
}
|
||||
|
||||
implicit val `the template LfEncodable`: LfEncodable[T] = new LfEncodable[T] {
|
||||
override def encoding(lte: LfTypeEncoding): lte.Out[T] =
|
||||
TemplateCompanion.this.encoding(lte)(TemplateCompanion.this.fieldEncoding(lte))
|
||||
}
|
||||
|
||||
/** Helper for EventDecoderApi. */
|
||||
private[binding] final def decoderEntry
|
||||
: (Primitive.TemplateId[T], rpcevent.CreatedEvent => Option[Template[T]]) = {
|
||||
type K[+A] = (Primitive.TemplateId[T], rpcevent.CreatedEvent => Option[A])
|
||||
describesTemplate.substituteCo[K](
|
||||
(id, _.createArguments flatMap fromNamedArguments)
|
||||
)
|
||||
}
|
||||
|
||||
protected final def ` arguments`(elems: (String, rpcvalue.Value)*): rpcvalue.Record =
|
||||
Primitive.arguments(` dataTypeId`, elems)
|
||||
}
|
||||
|
||||
object TemplateCompanion {
|
||||
abstract class Empty[T](implicit isTemplate: T <:< Template[T]) extends TemplateCompanion[T] {
|
||||
protected def onlyInstance: T
|
||||
override type key = Nothing
|
||||
type view[C[_]] = RecordView.Empty[C]
|
||||
override def toNamedArguments(associatedType: T) = ` arguments`()
|
||||
override def fromNamedArguments(namedArguments: rpcvalue.Record): Option[T] = Some(onlyInstance)
|
||||
override def fieldEncoding(lte: LfTypeEncoding): view[lte.Field] = RecordView.Empty
|
||||
override def encoding(lte: LfTypeEncoding)(view: view[lte.Field]): lte.Out[T] =
|
||||
lte.emptyRecord(` dataTypeId`, () => onlyInstance)
|
||||
}
|
||||
}
|
@ -1,232 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
import com.daml.ledger.api.refinements.ApiTypes.{ContractId, Party}
|
||||
import com.daml.ledger.api.v1.value.Value.{Sum => VSum}
|
||||
import com.daml.ledger.api.v1.{value => rpcvalue}
|
||||
import com.daml.ledger.client.binding.{Primitive => P}
|
||||
import com.daml.lf.{data => lf}
|
||||
import scalaz.std.option._
|
||||
import scalaz.syntax.traverse._
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.{specialized => sp}
|
||||
|
||||
sealed trait DamlCodecs // always include `object DamlCodecs` in implicit search
|
||||
|
||||
// Should be covariant, but that prevents P.List resolution from happening (see
|
||||
// test "be found for lists" in ValueSpec)
|
||||
sealed trait ValueDecoder[@sp(Long) A] extends DamlCodecs {
|
||||
def read(argumentValue: VSum): Option[A]
|
||||
}
|
||||
|
||||
sealed trait ValueEncoder[@sp(Long) -A] extends DamlCodecs {
|
||||
def write(obj: A): VSum
|
||||
}
|
||||
|
||||
/** Typeclass of "serializable" types as defined by the LF specification.
|
||||
*
|
||||
* @tparam A Scala representation for some Daml ''serializable''
|
||||
* type. Specialized to match [[Primitive]].
|
||||
*/
|
||||
sealed trait Value[@sp(Long) A] extends ValueDecoder[A] with ValueEncoder[A]
|
||||
|
||||
object Value {
|
||||
// Replace all three with imp if available.
|
||||
@inline def apply[@sp(Long) A](implicit A: Value[A]): Value[A] = A
|
||||
|
||||
@inline def Decoder[@sp(Long) A](implicit A: ValueDecoder[A]): ValueDecoder[A] = A
|
||||
|
||||
@inline def Encoder[@sp(Long) A](implicit A: ValueEncoder[A]): ValueEncoder[A] = A
|
||||
|
||||
@inline def decode[@sp(Long) A](a: rpcvalue.Value)(implicit A: ValueDecoder[A]): Option[A] =
|
||||
A read a.sum
|
||||
|
||||
@inline def encode[@sp(Long) A](a: A)(implicit A: ValueEncoder[A]): rpcvalue.Value =
|
||||
rpcvalue.Value(A write a)
|
||||
|
||||
// TODO remove this suppression, sadly now we do inherit from `Value ValueRef`
|
||||
@SuppressWarnings(Array("org.wartremover.warts.LeakingSealed"))
|
||||
private[binding] trait InternalImpl[A] extends Value[A]
|
||||
|
||||
private[binding] def splattedVariantId(
|
||||
baseVariantId: rpcvalue.Identifier,
|
||||
caseName: String,
|
||||
): rpcvalue.Identifier =
|
||||
baseVariantId copy (entityName = s"${baseVariantId.entityName}.$caseName")
|
||||
}
|
||||
|
||||
object DamlCodecs extends encoding.ValuePrimitiveEncoding[Value] {
|
||||
@inline private[this] def fromArgumentValueFuns[@sp(Long) A](
|
||||
_read: VSum => Option[A],
|
||||
_write: A => VSum,
|
||||
): Value[A] =
|
||||
new Value[A] {
|
||||
override def read(argumentValue: VSum): Option[A] = _read(argumentValue)
|
||||
override def write(obj: A): VSum = _write(obj)
|
||||
}
|
||||
|
||||
implicit override val valueInt64: Value[P.Int64] = new Value[P.Int64] {
|
||||
override def read(argumentValue: VSum): Option[P.Int64] =
|
||||
argumentValue.int64
|
||||
|
||||
override def write(obj: P.Int64): VSum = VSum.Int64(obj)
|
||||
}
|
||||
|
||||
implicit override val valueNumeric: Value[P.Numeric] =
|
||||
fromArgumentValueFuns(
|
||||
_.numeric flatMap (lf.Numeric.fromString(_).toOption.map(BigDecimal(_))),
|
||||
bd => VSum.Numeric(lf.Numeric.toString(bd.bigDecimal)),
|
||||
)
|
||||
|
||||
implicit override val valueParty: Value[P.Party] =
|
||||
Party.subst(fromArgumentValueFuns(_.party, VSum.Party))
|
||||
|
||||
implicit override val valueText: Value[P.Text] =
|
||||
fromArgumentValueFuns(_.text, VSum.Text)
|
||||
|
||||
implicit val valueDate: Value[P.Date] = P.Date.subst {
|
||||
import java.time.LocalDate
|
||||
fromArgumentValueFuns(
|
||||
_.date flatMap (i => P.Date.fromLocalDate(LocalDate ofEpochDay i.toLong)),
|
||||
ld => VSum.Date(ld.toEpochDay.toInt),
|
||||
)
|
||||
}
|
||||
|
||||
implicit override val valueTimestamp: Value[P.Timestamp] = P.Timestamp.subst {
|
||||
import com.daml.api.util.TimestampConversion.{instantToMicros, microsToInstant}
|
||||
fromArgumentValueFuns(
|
||||
{
|
||||
case ts @ VSum.Timestamp(_) => Some(microsToInstant(ts))
|
||||
case _ => None
|
||||
},
|
||||
instantToMicros,
|
||||
)
|
||||
}
|
||||
|
||||
implicit override val valueUnit: Value[P.Unit] = {
|
||||
val decoded = Some(())
|
||||
val encoded = VSum.Unit(com.google.protobuf.empty.Empty())
|
||||
fromArgumentValueFuns(_ => decoded, _ => encoded)
|
||||
}
|
||||
|
||||
implicit override val valueBool: Value[P.Bool] =
|
||||
fromArgumentValueFuns(_.bool, VSum.Bool)
|
||||
|
||||
private[this] def seqAlterTraverse[A, B, That](
|
||||
xs: Iterable[A]
|
||||
)(f: A => Option[B])(implicit factory: collection.Factory[B, That]): Option[That] = {
|
||||
val bs = factory.newBuilder
|
||||
val i = xs.iterator
|
||||
@tailrec def go(): Option[That] =
|
||||
if (i.hasNext) f(i.next()) match {
|
||||
case Some(b) =>
|
||||
bs += b
|
||||
go()
|
||||
case None => None
|
||||
}
|
||||
else Some(bs.result())
|
||||
go()
|
||||
}
|
||||
|
||||
implicit override def valueList[A](implicit A: Value[A]): Value[P.List[A]] =
|
||||
fromArgumentValueFuns(
|
||||
_.list flatMap (gl => seqAlterTraverse(gl.elements)(Value.decode[A](_))),
|
||||
as => VSum.List(rpcvalue.List(as map (Value.encode(_)))),
|
||||
)
|
||||
|
||||
implicit override def valueContractId[A]: Value[P.ContractId[A]] =
|
||||
Primitive.substContractId(
|
||||
ContractId.subst(fromArgumentValueFuns(_.contractId, VSum.ContractId))
|
||||
)
|
||||
|
||||
implicit override def valueOptional[A](implicit A: Value[A]): Value[P.Optional[A]] =
|
||||
fromArgumentValueFuns(
|
||||
_.optional flatMap (_.value traverse (Value.decode[A](_))),
|
||||
oa => VSum.Optional(rpcvalue.Optional(oa map (Value.encode(_)))),
|
||||
)
|
||||
|
||||
implicit override def valueTextMap[A](implicit A: Value[A]): Value[P.TextMap[A]] =
|
||||
fromArgumentValueFuns(
|
||||
_.map.flatMap(gm =>
|
||||
seqAlterTraverse(gm.entries)(e => e.value.flatMap(Value.decode[A](_)).map(e.key -> _))
|
||||
),
|
||||
oa =>
|
||||
VSum.Map(rpcvalue.Map(oa.map { case (key, value) =>
|
||||
rpcvalue.Map.Entry(
|
||||
key = key,
|
||||
value = Some(Value.encode(value)),
|
||||
)
|
||||
}.toSeq)),
|
||||
)
|
||||
|
||||
implicit override def valueGenMap[K, V](implicit
|
||||
K: Value[K],
|
||||
V: Value[V],
|
||||
): Value[P.GenMap[K, V]] =
|
||||
fromArgumentValueFuns(
|
||||
_.genMap.flatMap(gm =>
|
||||
seqAlterTraverse(gm.entries)(e =>
|
||||
for {
|
||||
optK <- e.key
|
||||
k <- Value.decode[K](optK)
|
||||
optV <- e.value
|
||||
v <- Value.decode[V](optV)
|
||||
} yield k -> v
|
||||
)
|
||||
),
|
||||
oa =>
|
||||
VSum.GenMap(rpcvalue.GenMap(oa.map { case (key, value) =>
|
||||
rpcvalue.GenMap.Entry(
|
||||
key = Some(Value.encode(key)),
|
||||
value = Some(Value.encode(value)),
|
||||
)
|
||||
}.toSeq)),
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
/** Common superclass of record and variant classes' companions. */
|
||||
abstract class ValueRefCompanion {
|
||||
// This class is not generally safe for generic records/variants,
|
||||
// so is hidden. It's the only externally visible opening in the
|
||||
// Value typeclass.
|
||||
//
|
||||
// TODO remove this suppression, sadly now we do inherit from `Value ValueRef`
|
||||
@SuppressWarnings(Array("org.wartremover.warts.LeakingSealed"))
|
||||
protected abstract class `Value ValueRef`[A] extends Value[A]
|
||||
|
||||
protected val ` dataTypeId`: rpcvalue.Identifier
|
||||
// recordId and variantId are optional when submitting commands, according to
|
||||
// value.proto
|
||||
|
||||
protected final def ` mkDataTypeId`(
|
||||
packageId: String,
|
||||
moduleName: String,
|
||||
entityName: String,
|
||||
): rpcvalue.Identifier =
|
||||
rpcvalue.Identifier(packageId = packageId, moduleName = moduleName, entityName = entityName)
|
||||
|
||||
protected final def ` record`(elements: (String, rpcvalue.Value)*): VSum.Record =
|
||||
VSum.Record(Primitive.arguments(` dataTypeId`, elements))
|
||||
|
||||
protected final def ` variant`(constructor: String, value: rpcvalue.Value): VSum.Variant =
|
||||
VSum.Variant(
|
||||
rpcvalue
|
||||
.Variant(variantId = Some(` dataTypeId`), constructor = constructor, Some(value))
|
||||
)
|
||||
|
||||
protected final def ` enum`(constructor: String): VSum.Enum =
|
||||
VSum.Enum(rpcvalue.Enum(enumId = Some(` dataTypeId`), constructor))
|
||||
|
||||
protected final def ` createVariantOfSynthRecord`(
|
||||
k: String,
|
||||
o: (String, rpcvalue.Value)*
|
||||
): VSum.Variant =
|
||||
` variant`(
|
||||
k,
|
||||
rpcvalue.Value(VSum.Record(Primitive.arguments(Value.splattedVariantId(` dataTypeId`, k), o))),
|
||||
)
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
abstract class ValueRef extends Product with Serializable
|
@ -1,23 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
|
||||
import com.daml.ledger.api.v1.value.Value.{Sum => VSum}
|
||||
|
||||
abstract class VoidValueRef extends ValueRef {
|
||||
val cannotExist: Nothing
|
||||
}
|
||||
|
||||
object VoidValueRef {
|
||||
|
||||
/** Automatically provides the [[Value]] instance for all subclasses of
|
||||
* [[VoidValueRef]], which are produced by codegen for zero-constructor
|
||||
* variants.
|
||||
*/
|
||||
implicit def `VoidValueRef Value`[A <: VoidValueRef]: Value[A] =
|
||||
new Value.InternalImpl[A] {
|
||||
override def read(argumentValue: VSum): Option[A] = None
|
||||
override def write(obj: A): VSum = obj.cannotExist
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.client.binding
|
||||
package encoding
|
||||
|
||||
import Primitive.ContractId
|
||||
import Template.CreateForExercise
|
||||
|
||||
import scala.annotation.implicitNotFound
|
||||
|
||||
@implicitNotFound(
|
||||
"""Cannot decide how to exercise a choice on ${Self}; only well-typed contract IDs, Templates (.createAnd), and keys (TemplateType key k) are candidates for choice exercise"""
|
||||
)
|
||||
sealed abstract class ExerciseOn[-Self, Tpl]
|
||||
|
||||
object ExerciseOn {
|
||||
implicit def OnId[T]: ExerciseOn[ContractId[T], T] = new OnId
|
||||
implicit def CreateAndOnTemplate[T]: ExerciseOn[CreateForExercise[T], T] =
|
||||
new CreateAndOnTemplate
|
||||
implicit def OnKey[T]: ExerciseOn[Template.Key[T], T] = new OnKey
|
||||
|
||||
private[binding] final class OnId[T] extends ExerciseOn[ContractId[T], T]
|
||||
private[binding] final class CreateAndOnTemplate[T] extends ExerciseOn[CreateForExercise[T], T]
|
||||
private[binding] final class OnKey[T] extends ExerciseOn[Template.Key[T], T]
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user