mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 01:07:18 +03:00
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:
parent
b8d356d8e3
commit
9367d6658f
@ -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(_))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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",
|
||||
],
|
||||
|
@ -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))
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
41
ledger-service/lf-value-json/BUILD.bazel
Normal file
41
ledger-service/lf-value-json/BUILD.bazel
Normal 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,
|
||||
)
|
@ -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,
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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]
|
@ -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)
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
@ -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",
|
||||
|
@ -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"))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
|
@ -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._
|
||||
|
||||
|
@ -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._
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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._
|
||||
|
||||
|
@ -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._
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user