Move navigator's ApiCodecCompressed to new lf-value-json library (#2136)

* move ApiCodecCompressed, ApiValueImplicits, and some aliases to new lf-value-json package

* adapt navigator to moved pieces

* start defining scalacheck extension to ApiCodecCompressedSpec

* experiment with an inductive case in TypedValueGenerators

* finish a List case for TypedValueGenerators; it's revealing

* remove accidentally readded duplicate aliases

* start tying knots in TypedValueGenerators

* verbatim copy ApiCodecCompressedSpec to lf-value-json

* shift some tests from navigator to lf-value-json

* test Optional and Map for ApiCodecCompressed

* heavier random testing of ApiCodecCompressed

* remove unused dependencies from lf-value-json
This commit is contained in:
Stephen Compall 2019-07-16 16:38:33 -04:00 committed by GitHub
parent b8d356d8e3
commit 9367d6658f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 529 additions and 164 deletions

View File

@ -3,10 +3,13 @@
package com.digitalasset.daml.lf.data
import scalaz.Equal
import scala.language.higherKinds
import scalaz.{Applicative, Equal, Traverse}
import scalaz.std.tuple._
import scalaz.std.string._
import scalaz.syntax.equal._
import scalaz.syntax.traverse._
import scala.collection.immutable.HashMap
@ -71,4 +74,11 @@ object SortedLookupList {
self.toImmArray === other.toImmArray
}
implicit val `SLL covariant instance`: Traverse[SortedLookupList] =
new Traverse[SortedLookupList] {
override def traverseImpl[G[_]: Applicative, A, B](fa: SortedLookupList[A])(
f: A => G[B]): G[SortedLookupList[B]] =
fa.toImmArray traverse (_ traverse f) map (new SortedLookupList(_))
}
}

View File

@ -10,6 +10,9 @@ load(
da_scala_library(
name = "transaction-scalacheck",
srcs = glob(["src/main/**/*.scala"]),
plugins = [
"//external:jar/org/spire_math/kind_projector_2_12",
],
scalacopts = lf_scalacopts,
tags = ["maven_coordinates=com.digitalasset:daml-lf-transaction-scalacheck:__VERSION__"],
visibility = ["//visibility:public"],
@ -19,6 +22,7 @@ da_scala_library(
"//3rdparty/jvm/org/scalaz:scalaz_core",
"//3rdparty/jvm/org/scalaz:scalaz_scalacheck_binding",
"//daml-lf/data",
"//daml-lf/interface",
"//daml-lf/transaction",
"//daml-lf/transaction/src/main/protobuf:value_java_proto",
],

View File

@ -0,0 +1,171 @@
// Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.digitalasset.daml.lf
package value
import scala.language.higherKinds
import data.{Decimal, FrontStack, Ref, SortedLookupList, Time}
import data.ImmArray.ImmArraySeq
import iface.{PrimType => PT, Type, TypePrim}
import scalaz.Id.Id
import scalaz.syntax.traverse._
import scalaz.std.option._
import org.scalacheck.{Arbitrary, Gen, Shrink}
import Arbitrary.arbitrary
/** [[ValueGenerators]] produce untyped values; for example, if you use the list gen,
* you get a heterogeneous list. The generation target here, on the other hand, is
* ''a pair of a type and a generator of values of that type''. For example, you might
* generate the type `[Map Decimal]`, which would be accompanied by a `Gen` that produced
* [[Value]]s each guaranteed to be a list of maps, whose values are all guaranteed to
* be Decimals.
*
* As a user, you will probably be most interested in one of the generators derived
* from `genAddend`; if you need a generator in this theme not already supported by
* one such generator, you can probably derive a new one from `genAddend` yourself.
*/
object TypedValueGenerators {
sealed abstract class ValueAddend {
type Inj[Cid]
def t: Type
def inj[Cid]: Inj[Cid] => Value[Cid]
def prj[Cid]: Value[Cid] => Option[Inj[Cid]]
def injgen[Cid](cid: Gen[Cid]): Gen[Inj[Cid]]
implicit def injshrink[Cid: Shrink]: Shrink[Inj[Cid]]
}
@SuppressWarnings(Array("org.wartremover.warts.Any"))
object ValueAddend extends PrimInstances[Lambda[a => ValueAddend { type Inj[_] = a }]] {
type Aux[Inj0[_]] = ValueAddend {
type Inj[Cid] = Inj0[Cid]
}
type NoCid[Inj0] = ValueAddend {
type Inj[_] = Inj0
}
def noCid[Inj0](pt: PT, inj0: Inj0 => Value[Nothing])(prj0: Value[Any] PartialFunction Inj0)(
implicit arb: Arbitrary[Inj0],
shr: Shrink[Inj0]): NoCid[Inj0] = new ValueAddend {
type Inj[Cid] = Inj0
override val t = TypePrim(pt, ImmArraySeq.empty)
override def inj[Cid] = inj0
override def prj[Cid] = prj0.lift
override def injgen[Cid](cid: Gen[Cid]) = arb.arbitrary
override def injshrink[Cid: Shrink] = shr
}
import Value._, ValueGenerators.Implicits._
val text = noCid(PT.Text, ValueText) { case ValueText(t) => t }
val int64 = noCid(PT.Int64, ValueInt64) { case ValueInt64(i) => i }
val decimal = noCid(PT.Decimal, ValueDecimal) { case ValueDecimal(d) => d }
val unit = noCid(PT.Unit, (_: Unit) => ValueUnit) { case ValueUnit => () }
val date = noCid(PT.Date, ValueDate) { case ValueDate(d) => d }
val timestamp = noCid(PT.Timestamp, ValueTimestamp) { case ValueTimestamp(t) => t }
val bool = noCid(PT.Bool, ValueBool) { case ValueBool(b) => b }
val party = noCid(PT.Party, ValueParty) { case ValueParty(p) => p }
val contractId: Aux[Id] = new ValueAddend {
type Inj[Cid] = Cid
// TODO SC it probably doesn't make much difference for our initial use case,
// but the proper arg should probably end up here, not Unit
override val t = TypePrim(PT.ContractId, ImmArraySeq(TypePrim(PT.Unit, ImmArraySeq.empty)))
override def inj[Cid] = ValueContractId(_)
override def prj[Cid] = {
case ValueContractId(cid) => Some(cid)
case _ => None
}
override def injgen[Cid](cid: Gen[Cid]) = cid
override def injshrink[Cid](implicit shr: Shrink[Cid]) = shr
}
type Compose[F[_], G[_], A] = F[G[A]]
def list(elt: ValueAddend): Aux[Compose[Vector, elt.Inj, ?]] = new ValueAddend {
import scalaz.std.vector._
type Inj[Cid] = Vector[elt.Inj[Cid]]
override val t = TypePrim(PT.List, ImmArraySeq(elt.t))
override def inj[Cid] = elts => ValueList(elts.map(elt.inj).to[FrontStack])
override def prj[Cid] = {
case ValueList(v) => v.toImmArray.toSeq.to[Vector] traverse elt.prj
case _ => None
}
override def injgen[Cid](cid: Gen[Cid]) = {
implicit val ae: Arbitrary[elt.Inj[Cid]] = Arbitrary(elt.injgen(cid))
arbitrary[Vector[elt.Inj[Cid]]] // TODO SC propagate smaller size
}
override def injshrink[Cid: Shrink] = {
import elt.injshrink
implicitly[Shrink[Vector[elt.Inj[Cid]]]]
}
}
def optional(elt: ValueAddend): Aux[Compose[Option, elt.Inj, ?]] = new ValueAddend {
type Inj[Cid] = Option[elt.Inj[Cid]]
override val t = TypePrim(PT.Optional, ImmArraySeq(elt.t))
override def inj[Cid] = oe => ValueOptional(oe map elt.inj)
override def prj[Cid] = {
case ValueOptional(v) => v traverse elt.prj
case _ => None
}
override def injgen[Cid](cid: Gen[Cid]) = Gen.option(elt.injgen(cid))
override def injshrink[Cid: Shrink] = {
import elt.injshrink
implicitly[Shrink[Option[elt.Inj[Cid]]]]
}
}
def map(elt: ValueAddend): Aux[Compose[SortedLookupList, elt.Inj, ?]] = new ValueAddend {
type Inj[Cid] = SortedLookupList[elt.Inj[Cid]]
override val t = TypePrim(PT.Map, ImmArraySeq(elt.t))
override def inj[Cid] =
(sll: SortedLookupList[elt.Inj[Cid]]) => ValueMap(sll map elt.inj)
override def prj[Cid] = {
case ValueMap(sll) => sll traverse elt.prj
case _ => None
}
override def injgen[Cid](cid: Gen[Cid]) =
Gen.mapOf(Gen.zip(Gen.asciiPrintableStr, elt.injgen(cid))) map (SortedLookupList(_))
override def injshrink[Cid: Shrink] = Shrink.shrinkAny // XXX descend
}
}
trait PrimInstances[F[_]] {
def text: F[String]
def int64: F[Long]
def decimal: F[Decimal]
def unit: F[Unit]
def date: F[Time.Date]
def timestamp: F[Time.Timestamp]
def bool: F[Boolean]
def party: F[Ref.Party]
def leafInstances: Seq[F[_]] = Seq(text, int64, decimal, unit, date, timestamp, bool, party)
}
/** This is the key member of interest, supporting many patterns:
*
* 1. generating a type and value
* 2. generating a type and many values
* 3. generating well-typed values of different types
*
* All of which are derivable from what [[ValueAddend]] is, ''a type, a
* prism into [[Value]], a [[Type]] describing that type, and
* Scalacheck support surrounding that type.''
*/
@SuppressWarnings(Array("org.wartremover.warts.Any"))
val genAddend: Gen[ValueAddend] =
Gen.frequency(
(ValueAddend.leafInstances.length, Gen.oneOf(ValueAddend.leafInstances)),
(1, Gen.const(ValueAddend.contractId)),
(1, Gen.lzy(genAddend).map(ValueAddend.list(_))),
(1, Gen.lzy(genAddend).map(ValueAddend.optional(_))),
(1, Gen.lzy(genAddend).map(ValueAddend.map(_))),
)
/** Generate a type and value guaranteed to conform to that type. */
def genTypeAndValue[Cid](cid: Gen[Cid]): Gen[(Type, Value[Cid])] =
for {
addend <- genAddend
value <- addend.injgen(cid)
} yield (addend.t, addend.inj(value))
}

View File

@ -426,4 +426,11 @@ object ValueGenerators {
stringVersionGen
.map(TransactionVersion)
.filter(x => !TransactionVersions.acceptedVersions.contains(x))
object Implicits {
implicit val vdecimalArb: Arbitrary[Decimal] = Arbitrary(decimalGen map (_.value))
implicit val vdateArb: Arbitrary[Time.Date] = Arbitrary(dateGen)
implicit val vtimestampArb: Arbitrary[Time.Timestamp] = Arbitrary(timestampGen)
implicit val vpartyArb: Arbitrary[Ref.Party] = Arbitrary(party)
}
}

View File

@ -0,0 +1,41 @@
# Copyright (c) 2019 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",
)
lf_value_json_deps = [
"//3rdparty/jvm/org/scalaz:scalaz_core",
"//3rdparty/jvm/io/spray:spray_json",
"//daml-lf/data",
"//daml-lf/interface",
"//daml-lf/transaction",
]
da_scala_library(
name = "lf-value-json",
srcs = glob(["src/main/scala/**/*.scala"]),
scalacopts = ["-Xsource:2.13"],
tags = ["maven_coordinates=com.digitalasset.ledger-service:lf-value-json:__VERSION__"],
visibility = [
"//visibility:public",
],
deps = lf_value_json_deps,
)
da_scala_test(
name = "tests",
size = "medium",
srcs = glob(["src/test/scala/**/*.scala"]),
# data = ["//docs:quickstart-model.dar"],
deps = [
":lf-value-json",
"//3rdparty/jvm/org/scalacheck",
"//3rdparty/jvm/org/scalaz:scalaz_scalacheck_binding",
"//3rdparty/jvm/org/scalatest",
"//daml-lf/transaction-scalacheck",
] + lf_value_json_deps,
)

View File

@ -1,13 +1,14 @@
// Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.digitalasset.navigator.json
package com.digitalasset.daml.lf.value.json
import com.digitalasset.daml.lf.data.{Decimal => LfDecimal, FrontStack, Ref, SortedLookupList}
import com.digitalasset.navigator.model.{ApiValue, DamlLfIdentifier, DamlLfType, DamlLfTypeLookup}
import com.digitalasset.navigator.{model => Model}
import com.digitalasset.daml.lf.data.ImmArray.ImmArraySeq
import com.digitalasset.daml.lf.value.json.{NavigatorModelAliases => Model}
import Model.{ApiValue, DamlLfIdentifier, DamlLfType, DamlLfTypeLookup}
import spray.json._
import Model.ApiValueImplicits._
import ApiValueImplicits._
/**
* A compressed encoding of API values.
@ -207,7 +208,7 @@ object ApiCodecCompressed {
value: JsValue,
id: Model.DamlLfIdentifier,
defs: Model.DamlLfTypeLookup): Model.ApiValue = {
val typeCon = Model.DamlLfTypeCon(Model.DamlLfTypeConName(id), Model.DamlLfImmArraySeq())
val typeCon = Model.DamlLfTypeCon(Model.DamlLfTypeConName(id), ImmArraySeq())
// val dt = typeCon.instantiate(defs(id).getOrElse(deserializationError(s"Type $id not found")))
val dt = Model.damlLfInstantiate(
typeCon,

View File

@ -0,0 +1,59 @@
// Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.digitalasset.daml.lf.value.json
import java.time.{Instant, LocalDate}
import java.time.format.{DateTimeFormatter, DateTimeFormatterBuilder}
import com.digitalasset.daml.lf.data.{ImmArray, Ref, Time}
import com.digitalasset.daml.lf.value.{Value => V}
import com.digitalasset.daml.lf.value.json.NavigatorModelAliases._
object ApiValueImplicits {
implicit final class `ApiTimestamp additions`(private val it: ApiTimestamp) extends AnyVal {
import it._
def toInstant: Instant = value.toInstant
def toIso8601: String = DateTimeFormatter.ISO_INSTANT.format(toInstant)
}
implicit final class `ApiDate additions`(private val it: ApiDate) extends AnyVal {
import it._
def toLocalDate: LocalDate = LocalDate.ofEpochDay((value.days: Int).toLong)
def toInstant: Instant = Instant.from(toLocalDate)
def toIso8601: String = DateTimeFormatter.ISO_LOCAL_DATE.format(toLocalDate)
}
// Timestamp has microsecond resolution
private val formatter: DateTimeFormatter =
new DateTimeFormatterBuilder().appendInstant(6).toFormatter()
implicit final class `ApiTimestamp.type additions`(private val it: ApiTimestamp.type)
extends AnyVal {
def fromIso8601(t: String): ApiTimestamp = fromInstant(Instant.parse(t))
def fromInstant(t: Instant): ApiTimestamp =
ApiTimestamp(Time.Timestamp.assertFromInstant(t))
def fromMillis(t: Long): ApiTimestamp =
ApiTimestamp(Time.Timestamp.assertFromLong(micros = t * 1000L))
}
implicit final class `ApiDate.type additions`(private val it: ApiDate.type) extends AnyVal {
def fromIso8601(t: String): ApiDate =
fromLocalDate(LocalDate.parse(t, DateTimeFormatter.ISO_LOCAL_DATE))
def fromLocalDate(t: LocalDate): ApiDate =
ApiDate(Time.Date.assertFromDaysSinceEpoch(t.toEpochDay.toInt))
}
object FullyNamedApiRecord {
def unapply[Cid](
r: V.ValueRecord[Cid]): Option[(Option[Ref.Identifier], ImmArray[(Ref.Name, V[Cid])])] = {
val V.ValueRecord(tycon, fields) = r
if (fields.toSeq.forall(_._1.isDefined))
Some((tycon, fields.map {
case (Some(n), v) => (n, v)
case (None, _) => sys.error("impossible None")
}))
else None
}
}
}

View File

@ -0,0 +1,128 @@
// Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.digitalasset.daml.lf
package value.json
import data.{Ref => DamlLfRef}
import value.{Value => V}
/** Aliases used by navigator's Scala backend, from which this package
* was derived.
*/
trait NavigatorModelAliases[Cid] {
/**
* An absolute reference of a DAML-LF entity.
* Contains a DAML-LF package ID and a qualified name.
* Currently, such identifiers can point to:
* - Templates
* - User-defined records
* - User-defined variants
*/
type DamlLfIdentifier = DamlLfRef.Identifier
val DamlLfIdentifier = DamlLfRef.Identifier
/**
* A simple DAML-LF type
* Currently, these can be:
* - Primitive types
* - Type constructor applications (i.e., dereferencing a DamlLfIdentifier)
* - Type variables
*/
type DamlLfType = iface.Type
type DamlLfTypeCon = iface.TypeCon
val DamlLfTypeCon = iface.TypeCon
type DamlLfTypePrim = iface.TypePrim
val DamlLfTypePrim = iface.TypePrim
type DamlLfTypeVar = iface.TypeVar
val DamlLfTypeVar = iface.TypeVar
type DamlLfTypeConName = iface.TypeConName
val DamlLfTypeConName = iface.TypeConName
type DamlLfPrimType = iface.PrimType
val DamlLfPrimType = iface.PrimType
/** A user-defined DAML-LF type (closed form). Can be a record or variant. */
type DamlLfDataType = iface.DataType.FWT
val DamlLfDataType = iface.DataType
/** A user-defined DAML-LF type (generic form). Can be a record or variant. */
type DamlLfDefDataType = iface.DefDataType.FWT
val DamlLfDefDataType = iface.DefDataType
type DamlLfTypeLookup = DamlLfIdentifier => Option[DamlLfDefDataType]
/** A user-defined DAML-LF record */
type DamlLfRecord = iface.Record.FWT
val DamlLfRecord = iface.Record
/** A user-defined DAML-LF variant */
type DamlLfVariant = iface.Variant.FWT
val DamlLfVariant = iface.Variant
/** A user-defined DAML-LF enum */
type DamlLfEnum = iface.Enum
val DamlLfEnum = iface.Enum
def damlLfInstantiate(typeCon: DamlLfTypeCon, defn: DamlLfDefDataType): DamlLfDataType =
if (defn.typeVars.length != typeCon.typArgs.length) {
throw new RuntimeException(
s"Mismatching type vars and applied types, expected ${defn.typeVars} but got ${typeCon.typArgs} types")
} else {
if (defn.typeVars.isEmpty) { // optimization
defn.dataType
} else {
val paramsMap = Map(defn.typeVars.zip(typeCon.typArgs): _*)
def mapTypeVars(typ: DamlLfType, f: DamlLfTypeVar => DamlLfType): DamlLfType = typ match {
case t @ DamlLfTypeVar(_) => f(t)
case t @ DamlLfTypeCon(_, _) => DamlLfTypeCon(t.name, t.typArgs.map(mapTypeVars(_, f)))
case t @ DamlLfTypePrim(_, _) => DamlLfTypePrim(t.typ, t.typArgs.map(mapTypeVars(_, f)))
}
val withTyp: iface.Type => iface.Type = { typ =>
mapTypeVars(typ, v => paramsMap.getOrElse(v.name, v))
}
defn.dataType.bimap(withTyp, withTyp)
}
}
import scala.language.higherKinds
type OfCid[V[_]] = V[Cid]
type ApiValue = OfCid[V]
type ApiRecordField = (Option[DamlLfRef.Name], ApiValue)
val ApiRecordField = Tuple2
type ApiRecord = OfCid[V.ValueRecord]
val ApiRecord = V.ValueRecord
type ApiVariant = OfCid[V.ValueVariant]
val ApiVariant = V.ValueVariant
type ApiEnum = V.ValueEnum
val ApiEnum = V.ValueEnum
type ApiList = OfCid[V.ValueList]
val ApiList = V.ValueList
type ApiOptional = OfCid[V.ValueOptional]
val ApiOptional = V.ValueOptional
type ApiMap = OfCid[V.ValueMap]
val ApiMap = V.ValueMap
type ApiContractId = OfCid[V.ValueContractId]
val ApiContractId = V.ValueContractId
type ApiInt64 = V.ValueInt64
val ApiInt64 = V.ValueInt64
type ApiDecimal = V.ValueDecimal
val ApiDecimal = V.ValueDecimal
type ApiText = V.ValueText
val ApiText = V.ValueText
type ApiParty = V.ValueParty
val ApiParty = V.ValueParty
type ApiBool = V.ValueBool
val ApiBool = V.ValueBool
type ApiUnit = V.ValueUnit.type
val ApiUnit = V.ValueUnit
type ApiTimestamp = V.ValueTimestamp
val ApiTimestamp = V.ValueTimestamp
type ApiDate = V.ValueDate
val ApiDate = V.ValueDate
type ApiImpossible = OfCid[V.ValueTuple]
val ApiImpossible = V.ValueTuple
}
object NavigatorModelAliases extends NavigatorModelAliases[String]

View File

@ -0,0 +1,80 @@
// Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.digitalasset.daml.lf
package value.json
import value.json.{NavigatorModelAliases => model}
import value.TypedValueGenerators.{genTypeAndValue, genAddend}
import org.scalatest.{Matchers, WordSpec}
import org.scalatest.prop.GeneratorDrivenPropertyChecks
import org.scalacheck.{Arbitrary, Gen}
import scala.util.{Success, Try}
class ApiCodecCompressedSpec extends WordSpec with Matchers with GeneratorDrivenPropertyChecks {
/** XXX SC replace when TypedValueGenerators supports TypeCons */
private val typeLookup: NavigatorModelAliases.DamlLfTypeLookup = _ => None
/** Serializes the API value to JSON, then parses it back to an API value */
private def serializeAndParse(
value: model.ApiValue,
typ: model.DamlLfType): Try[model.ApiValue] = {
import ApiCodecCompressed.JsonImplicits._
import spray.json._
for {
serialized <- Try(value.toJson.prettyPrint)
json <- Try(serialized.parseJson)
parsed <- Try(ApiCodecCompressed.jsValueToApiValue(json, typ, typeLookup))
} yield parsed
}
type Cid = String
private val genCid = Gen.alphaStr.filter(_.nonEmpty)
"API compressed JSON codec" when {
"serializing and parsing a value" should {
"work for arbitrary reference-free types" in forAll(genTypeAndValue(genCid)) {
case (typ, value) =>
serializeAndParse(value, typ) shouldBe Success(value)
}
"work for many, many values in raw format" in forAll(genAddend) { va =>
import va.injshrink
implicit val arbInj: Arbitrary[va.Inj[Cid]] = Arbitrary(va.injgen(genCid))
forAll { v: va.Inj[Cid] =>
va.prj(
ApiCodecCompressed.jsValueToApiValue(
ApiCodecCompressed.apiValueToJsValue(va.inj(v)),
va.t,
typeLookup)) should ===(Some(v))
}
}
/*
"work for EmptyRecord" in {
serializeAndParse(C.emptyRecordV, C.emptyRecordTC) shouldBe Success(C.emptyRecordV)
}
"work for SimpleRecord" in {
serializeAndParse(C.simpleRecordV, C.simpleRecordTC) shouldBe Success(C.simpleRecordV)
}
"work for SimpleVariant" in {
serializeAndParse(C.simpleVariantV, C.simpleVariantTC) shouldBe Success(C.simpleVariantV)
}
"work for ComplexRecord" in {
serializeAndParse(C.complexRecordV, C.complexRecordTC) shouldBe Success(C.complexRecordV)
}
"work for Tree" in {
serializeAndParse(C.treeV, C.treeTC) shouldBe Success(C.treeV)
}
"work for Enum" in {
serializeAndParse(C.redV, C.redTC) shouldBe Success(C.redV)
}
*/
}
}
}

View File

@ -81,6 +81,7 @@ compileDependencies = [
"//language-support/scala/bindings",
"//ledger/ledger-api-client",
"//ledger/ledger-api-domain",
"//ledger-service/lf-value-json",
"//daml-assistant/scala-daml-project-config",
"//3rdparty/jvm/com/github/pureconfig:pureconfig",
"//3rdparty/jvm/com/github/scopt:scopt",

View File

@ -5,7 +5,7 @@ package com.digitalasset.navigator.console.commands
import com.digitalasset.ledger.api.refinements.ApiTypes
import com.digitalasset.navigator.console._
import com.digitalasset.navigator.json.ApiCodecCompressed
import com.digitalasset.daml.lf.value.json.ApiCodecCompressed
import com.digitalasset.navigator.model
@SuppressWarnings(Array("org.wartremover.warts.Product", "org.wartremover.warts.Serializable"))

View File

@ -7,7 +7,7 @@ import java.util.concurrent.TimeUnit
import com.digitalasset.ledger.api.refinements.ApiTypes
import com.digitalasset.navigator.console._
import com.digitalasset.navigator.json.ApiCodecCompressed
import com.digitalasset.daml.lf.value.json.ApiCodecCompressed
import com.digitalasset.navigator.model
import com.digitalasset.navigator.store.Store.CreateContract
import akka.pattern.ask

View File

@ -5,7 +5,7 @@ package com.digitalasset.navigator.console.commands
import com.digitalasset.ledger.api.refinements.ApiTypes
import com.digitalasset.navigator.console._
import com.digitalasset.navigator.json.ApiCodecCompressed
import com.digitalasset.daml.lf.value.json.ApiCodecCompressed
import com.digitalasset.navigator.model
import com.digitalasset.navigator.model.ApiValue
import spray.json.JsValue

View File

@ -5,7 +5,7 @@ package com.digitalasset.navigator.console.commands
import com.digitalasset.ledger.api.refinements.ApiTypes
import com.digitalasset.navigator.console._
import com.digitalasset.navigator.json.ApiCodecCompressed
import com.digitalasset.daml.lf.value.json.ApiCodecCompressed
import com.digitalasset.navigator.model
case object Event extends SimpleCommand {

View File

@ -7,7 +7,7 @@ import java.util.concurrent.TimeUnit
import com.digitalasset.ledger.api.refinements.ApiTypes
import com.digitalasset.navigator.console._
import com.digitalasset.navigator.json.ApiCodecCompressed
import com.digitalasset.daml.lf.value.json.ApiCodecCompressed
import com.digitalasset.navigator.model
import com.digitalasset.navigator.store.Store.ExerciseChoice
import akka.pattern.ask

View File

@ -6,8 +6,9 @@ package com.digitalasset.navigator.data
import java.time.Instant
import com.digitalasset.ledger.api.refinements.ApiTypes
import com.digitalasset.navigator.json.{ApiCodecCompressed, ApiCodecVerbose}
import com.digitalasset.navigator.json.ApiCodecCompressed.JsonImplicits._
import com.digitalasset.navigator.json.ApiCodecVerbose
import com.digitalasset.daml.lf.value.json.ApiCodecCompressed
import ApiCodecCompressed.JsonImplicits._
import com.digitalasset.navigator.model._
import scala.util.{Failure, Try}

View File

@ -4,8 +4,8 @@
package com.digitalasset.navigator.data
import com.digitalasset.ledger.api.refinements.ApiTypes
import com.digitalasset.navigator.json.ApiCodecCompressed
import com.digitalasset.navigator.json.ApiCodecCompressed.JsonImplicits._
import com.digitalasset.daml.lf.value.json.ApiCodecCompressed
import ApiCodecCompressed.JsonImplicits._
import com.digitalasset.navigator.json.ModelCodec.JsonImplicits._
import com.digitalasset.navigator.model._

View File

@ -4,9 +4,9 @@
package com.digitalasset.navigator.data
import com.digitalasset.ledger.api.refinements.ApiTypes
import com.digitalasset.navigator.json.ApiCodecCompressed
import com.digitalasset.daml.lf.value.json.ApiCodecCompressed
import ApiCodecCompressed.JsonImplicits._
import com.digitalasset.navigator.json.ModelCodec.JsonImplicits._
import com.digitalasset.navigator.json.ApiCodecCompressed.JsonImplicits._
import com.digitalasset.navigator.json.DamlLfCodec.JsonImplicits._
import com.digitalasset.navigator.model._

View File

@ -11,7 +11,7 @@ import com.digitalasset.daml.lf.data.{
SortedLookupList
}
import com.digitalasset.navigator.{model => Model}
import Model.ApiValueImplicits._
import com.digitalasset.daml.lf.value.json.ApiValueImplicits._
import com.digitalasset.navigator.json.DamlLfCodec.JsonImplicits._
import com.digitalasset.navigator.json.Util._
import com.digitalasset.navigator.model.DamlLfIdentifier

View File

@ -7,11 +7,11 @@ import scalaz.{@@, Tag}
import com.digitalasset.daml.lf.{data => DamlLfData}
import com.digitalasset.daml.lf.data.{Ref => DamlLfRef}
import com.digitalasset.daml.lf.{iface => DamlLfIface}
import com.digitalasset.daml.lf.value.{Value => V}
import com.digitalasset.daml.lf.value.json.NavigatorModelAliases
import com.digitalasset.ledger.api.{v1 => ApiV1}
import com.digitalasset.ledger.api.refinements.ApiTypes
package object model {
package object model extends NavigatorModelAliases[String] {
/**
* An opaque identifier used for templates.
@ -44,90 +44,16 @@ package object model {
type DamlLfQualifiedName = DamlLfRef.QualifiedName
val DamlLfQualifiedName = DamlLfRef.QualifiedName
/**
* An absolute reference of a DAML-LF entity.
* Contains a DAML-LF package ID and a qualified name.
* Currently, such identifiers can point to:
* - Templates
* - User-defined records
* - User-defined variants
*/
type DamlLfIdentifier = DamlLfRef.Identifier
val DamlLfIdentifier = DamlLfRef.Identifier
/**
* A simple DAML-LF type
* Currently, these can be:
* - Primitive types
* - Type constructor applications (i.e., dereferencing a DamlLfIdentifier)
* - Type variables
*/
type DamlLfType = DamlLfIface.Type
type DamlLfTypeCon = DamlLfIface.TypeCon
val DamlLfTypeCon = DamlLfIface.TypeCon
type DamlLfTypePrim = DamlLfIface.TypePrim
val DamlLfTypePrim = DamlLfIface.TypePrim
type DamlLfTypeVar = DamlLfIface.TypeVar
val DamlLfTypeVar = DamlLfIface.TypeVar
type DamlLfTypeConName = DamlLfIface.TypeConName
val DamlLfTypeConName = DamlLfIface.TypeConName
type DamlLfTypeConNameOrPrimType = DamlLfIface.TypeConNameOrPrimType
type DamlLfPrimType = DamlLfIface.PrimType
val DamlLfPrimType = DamlLfIface.PrimType
type DamlLfImmArraySeq[T] = DamlLfData.ImmArray.ImmArraySeq[T]
val DamlLfImmArraySeq = DamlLfData.ImmArray.ImmArraySeq
type DamlLfImmArray[T] = DamlLfData.ImmArray[T]
val DamlLfImmArray = DamlLfData.ImmArray
/** A user-defined DAML-LF type (generic form). Can be a record or variant. */
type DamlLfDefDataType = DamlLfIface.DefDataType.FWT
val DamlLfDefDataType = DamlLfIface.DefDataType
/** A user-defined DAML-LF type (closed form). Can be a record or variant. */
type DamlLfDataType = DamlLfIface.DataType.FWT
val DamlLfDataType = DamlLfIface.DataType
/** A user-defined DAML-LF record */
type DamlLfRecord = DamlLfIface.Record.FWT
val DamlLfRecord = DamlLfIface.Record
/** A user-defined DAML-LF variant */
type DamlLfVariant = DamlLfIface.Variant.FWT
val DamlLfVariant = DamlLfIface.Variant
/** A user-defined DAML-LF enum */
type DamlLfEnum = DamlLfIface.Enum
val DamlLfEnum = DamlLfIface.Enum
type DamlLfFieldWithType = DamlLfIface.FieldWithType
type DamlLfTypeLookup = DamlLfIdentifier => Option[DamlLfDefDataType]
def damlLfInstantiate(typeCon: DamlLfTypeCon, defn: DamlLfDefDataType): DamlLfDataType =
if (defn.typeVars.length != typeCon.typArgs.length) {
throw new RuntimeException(
s"Mismatching type vars and applied types, expected ${defn.typeVars} but got ${typeCon.typArgs} types")
} else {
if (defn.typeVars.isEmpty) { // optimization
defn.dataType
} else {
val paramsMap = Map(defn.typeVars.zip(typeCon.typArgs): _*)
def mapTypeVars(typ: DamlLfType, f: DamlLfTypeVar => DamlLfType): DamlLfType = typ match {
case t @ DamlLfTypeVar(_) => f(t)
case t @ DamlLfTypeCon(_, _) => DamlLfTypeCon(t.name, t.typArgs.map(mapTypeVars(_, f)))
case t @ DamlLfTypePrim(_, _) => DamlLfTypePrim(t.typ, t.typArgs.map(mapTypeVars(_, f)))
}
val withTyp: DamlLfIface.Type => DamlLfIface.Type = { typ =>
mapTypeVars(typ, v => paramsMap.getOrElse(v.name, v))
}
defn.dataType.bimap(withTyp, withTyp)
}
}
// ----------------------------------------------------------------------------------------------
// Conversion between API Identifier, DAML-LF Identifier, and String
// ----------------------------------------------------------------------------------------------
@ -175,42 +101,4 @@ package object model {
def parseOpaqueIdentifier(id: TemplateStringId): Option[DamlLfRef.Identifier] =
parseOpaqueIdentifier(TemplateStringId.unwrap(id))
import scala.language.higherKinds
type OfCid[V[_]] = V[String]
type ApiValue = OfCid[V]
type ApiRecordField = (Option[DamlLfRef.Name], ApiValue)
val ApiRecordField = Tuple2
type ApiRecord = OfCid[V.ValueRecord]
val ApiRecord = V.ValueRecord
type ApiVariant = OfCid[V.ValueVariant]
val ApiVariant = V.ValueVariant
type ApiEnum = V.ValueEnum
val ApiEnum = V.ValueEnum
type ApiList = OfCid[V.ValueList]
val ApiList = V.ValueList
type ApiOptional = OfCid[V.ValueOptional]
val ApiOptional = V.ValueOptional
type ApiMap = OfCid[V.ValueMap]
val ApiMap = V.ValueMap
type ApiContractId = OfCid[V.ValueContractId]
val ApiContractId = V.ValueContractId
type ApiInt64 = V.ValueInt64
val ApiInt64 = V.ValueInt64
type ApiDecimal = V.ValueDecimal
val ApiDecimal = V.ValueDecimal
type ApiText = V.ValueText
val ApiText = V.ValueText
type ApiParty = V.ValueParty
val ApiParty = V.ValueParty
type ApiBool = V.ValueBool
val ApiBool = V.ValueBool
type ApiUnit = V.ValueUnit.type
val ApiUnit = V.ValueUnit
type ApiTimestamp = V.ValueTimestamp
val ApiTimestamp = V.ValueTimestamp
type ApiDate = V.ValueDate
val ApiDate = V.ValueDate
type ApiImpossible = OfCid[V.ValueTuple]
val ApiImpossible = V.ValueTuple
}

View File

@ -5,7 +5,7 @@ package com.digitalasset.navigator.query
import com.digitalasset.navigator.dotnot._
import com.digitalasset.navigator.model._
import ApiValueImplicits._
import com.digitalasset.daml.lf.value.json.ApiValueImplicits._
import scalaz.Tag
import scalaz.syntax.tag._

View File

@ -5,7 +5,7 @@ package com.digitalasset.navigator.query
import com.digitalasset.navigator.dotnot._
import com.digitalasset.navigator.model._
import ApiValueImplicits._
import com.digitalasset.daml.lf.value.json.ApiValueImplicits._
import scalaz.Tag
import scalaz.syntax.tag._

View File

@ -12,7 +12,7 @@ import com.digitalasset.daml.lf.data.{
}
import com.digitalasset.navigator.model._
import com.digitalasset.daml.lf.{iface => DamlLfIface}
import ApiValueImplicits._
import com.digitalasset.daml.lf.value.json.ApiValueImplicits._
import scala.language.implicitConversions

View File

@ -15,7 +15,8 @@ class ApiCodecCompressedSpec extends WordSpec with Matchers {
private def serializeAndParse(
value: model.ApiValue,
typ: model.DamlLfType): Try[model.ApiValue] = {
import com.digitalasset.navigator.json.ApiCodecCompressed.JsonImplicits._
import com.digitalasset.daml.lf.value.json.ApiCodecCompressed
import ApiCodecCompressed.JsonImplicits._
import spray.json._
for {
@ -28,30 +29,6 @@ class ApiCodecCompressedSpec extends WordSpec with Matchers {
"API verbose JSON codec" when {
"serializing and parsing a value" should {
"work for Text" in {
serializeAndParse(C.simpleTextV, C.simpleTextT) shouldBe Success(C.simpleTextV)
}
"work for Int64" in {
serializeAndParse(C.simpleInt64V, C.simpleInt64T) shouldBe Success(C.simpleInt64V)
}
"work for Decimal" in {
serializeAndParse(C.simpleDecimalV, C.simpleDecimalT) shouldBe Success(C.simpleDecimalV)
}
"work for Unit" in {
serializeAndParse(C.simpleUnitV, C.simpleUnitT) shouldBe Success(C.simpleUnitV)
}
"work for Date" in {
serializeAndParse(C.simpleDateV, C.simpleDateT) shouldBe Success(C.simpleDateV)
}
"work for Timestamp" in {
serializeAndParse(C.simpleTimestampV, C.simpleTimestampT) shouldBe Success(
C.simpleTimestampV)
}
"work for Optional" in {
serializeAndParse(C.simpleOptionalV, C.simpleOptionalT(C.simpleTextT)) shouldBe Success(
C.simpleOptionalV)
}
"work for EmptyRecord" in {
serializeAndParse(C.emptyRecordV, C.emptyRecordTC) shouldBe Success(C.emptyRecordV)
}
@ -70,9 +47,6 @@ class ApiCodecCompressedSpec extends WordSpec with Matchers {
"work for Enum" in {
serializeAndParse(C.redV, C.redTC) shouldBe Success(C.redV)
}
"work for Map" in {
serializeAndParse(C.simpleMapV, C.simpleMapT(C.simpleInt64T)) shouldBe Success(C.simpleMapV)
}
}
}
}

View File

@ -8,7 +8,7 @@ import java.time.{Instant, LocalDate}
import org.scalatest.{Matchers, WordSpec}
import com.digitalasset.daml.lf.data.{Time => LfTime}
import ApiValueImplicits._
import com.digitalasset.daml.lf.value.json.ApiValueImplicits._
class ApiValueSpec extends WordSpec with Matchers {