Skip to content

Commit

Permalink
Include contract ids in DAML script’s query (digital-asset#3546)
Browse files Browse the repository at this point in the history
  • Loading branch information
cocreature authored and mergify[bot] committed Nov 20, 2019
1 parent 6d2ed90 commit f94b832
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 13 deletions.
7 changes: 3 additions & 4 deletions daml-script/daml/Daml/Script.daml
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,11 @@ data ScriptF a
data QueryACS a = QueryACS
{ party : Party
, tplId : TemplateTypeRep
, continue : [AnyTemplate] -> a
, continue : [(ContractId (), AnyTemplate)] -> a
} deriving Functor

-- TODO This should also return contract ids.
query : forall t. Template t => Party -> Script [t]
query p = Script $ Free $ Query (QueryACS p (templateTypeRep @t) (pure . map (fromSome . fromAnyTemplate)))
query : forall t. Template t => Party -> Script [(ContractId t, t)]
query p = Script $ Free $ Query (QueryACS p (templateTypeRep @t) (pure . map (\(cid, tpl) -> (coerceContractId cid, fromSome $ fromAnyTemplate tpl))))

data AllocateParty a = AllocateParty
{ displayName : Text
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,13 +279,15 @@ object Converter {
entityName <- DottedName.fromString(id.entityName)
} yield Identifier(packageId, QualifiedName(moduleName, entityName))

// Convert a Created event to an AnyTemplate
// Convert a Created event to a pair of (ContractId (), AnyTemplate)
def fromCreated(
translator: ValueTranslator,
primPackageId: PackageId,
stdlibPackageId: PackageId,
created: CreatedEvent): Either[String, SValue] = {
val anyTemplateTyCon =
Identifier(stdlibPackageId, QualifiedName.assertFromString("DA.Internal.LF:AnyTemplate"))
val pairTyCon = Identifier(primPackageId, QualifiedName.assertFromString("DA.Types:Tuple2"))
for {
templateId <- created.templateId match {
case None => Left(s"Missing field templateId in $created")
Expand All @@ -297,7 +299,9 @@ object Converter {
case ResultDone(v) => Right(v)
case err => Left(s"Failure to translate value in create: $err")
}
} yield record(anyTemplateTyCon, ("getAnyTemplate", SAny(TTyCon(tyCon), argSValue)))
anyTpl = record(anyTemplateTyCon, ("getAnyTemplate", SAny(TTyCon(tyCon), argSValue)))
cid <- ContractIdString.fromString(created.contractId)
} yield record(pairTyCon, ("_1", SContractId(AbsoluteContractId(cid))), ("_2", anyTpl))
}

def fromStatusException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ class Runner(
}
.get
._1
val primPackageId =
dar.all
.find {
case (pkgId, pkg) =>
pkg.modules.contains(DottedName.assertFromString("DA.Types"))
}
.get
._1
def lookupChoiceTy(id: Identifier, choice: Name): Either[String, Type] =
for {
pkg <- darMap
Expand Down Expand Up @@ -283,7 +291,8 @@ class Runner(
acsResponses.flatMap(acsPages => {
val res =
FrontStack(acsPages.flatMap(page => page.activeContracts))
.traverseU(Converter.fromCreated(valueTranslator, stdlibPackageId, _))
.traverseU(
Converter.fromCreated(valueTranslator, primPackageId, stdlibPackageId, _))
.fold(s => throw new ConverterException(s), identity)
machine.ctrl =
Speedy.CtrlExpr(SEApp(SEValue(continue), Array(SEValue(SList(res)))))
Expand Down
11 changes: 9 additions & 2 deletions daml-script/test/daml/ScriptTest.daml
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,15 @@ test0 = do
ts <- query @T alice
tProposals <- query @TProposal alice
cs <- query @C alice
pure (alice, bob, ts, tProposals, cs)
pure (alice, bob, map snd ts, map snd tProposals, map snd cs)

test1 : Script (Numeric 11)
test1 = do
alice <- allocateParty "alice"
cid <- submit alice $ createCmd (NumericTpl alice 1.06)
ts <- query @NumericTpl alice
let v = case ts of
[NumericTpl _ v] -> v
[(_cid, NumericTpl _ v)] -> v
_ -> error $ "Expected exactly one NumericTpl but got " <> show ts
v' <- submit alice $ exerciseCmd cid GetV
pure (v + v')
Expand All @@ -88,3 +88,10 @@ test3 = do
cid <- submit alice $ createCmd (C alice 42)
submitMustFail alice $ exerciseCmd cid ShouldFail
pure ()

test4 : Script (ContractId C, ContractId C)
test4 = do
alice <- allocateParty "alice"
cid <- submit alice $ createCmd (C alice 42)
[(cid', _)] <- query @C alice
pure (cid, cid')
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,42 @@ case class Test3(dar: Dar[(PackageId, Package)], runner: TestRunner) {
}
}

case class Test4(dar: Dar[(PackageId, Package)], runner: TestRunner) {
val scriptId = Identifier(dar.main._1, QualifiedName.assertFromString("ScriptTest:test4"))
def runTests() = {
runner.genericTest(
"test4",
dar,
scriptId,
None,
result =>
result match {
case SRecord(_, _, vals) if vals.size == 2 =>
TestRunner.assertEqual(vals.get(0), vals.get(1), "ContractIds")
case v => Left(s"Expected record with 2 fields but got $v")
}
)
}
}

// Runs the example from the docs to make sure it doesn’t produce a runtime error.
case class ScriptExample(dar: Dar[(PackageId, Package)], runner: TestRunner) {
val scriptId = Identifier(dar.main._1, QualifiedName.assertFromString("ScriptExample:test"))
def runTests() = {
runner.genericTest(
"ScriptExample",
dar,
scriptId,
None,
result =>
result match {
case SUnit => Right(())
case v => Left(s"Expected SUnit but got $v")
}
)
}
}

object TestMain {

private val configParser = new scopt.OptionParser[Config]("daml_script_test") {
Expand Down Expand Up @@ -280,6 +316,8 @@ object TestMain {
Test1(dar, runner).runTests()
Test2(dar, runner).runTests()
Test3(dar, runner).runTests()
Test4(dar, runner).runTests()
ScriptExample(dar, runner).runTests()
}
}
}
4 changes: 3 additions & 1 deletion docs/source/daml-script/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@ function. We pass it the type of the template and a party. It will
then give us all active contracts of the given type visible to the
party. In our example, we expect to see one active ``CoinProposal``
for ``bank`` and one ``Coin`` contract for each of ``Alice`` and
``Bob``.
``Bob``. We get back list of ``(ContractId t, t)`` pairs from
``query``. In our tests, we do not need the contract ids, so we throw
them away using ``map snd``.

.. literalinclude:: ./template-root/src/ScriptExample.daml
:language: daml
Expand Down
6 changes: 3 additions & 3 deletions docs/source/daml-script/template-root/src/ScriptExample.daml
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,12 @@ test = do

-- TEST_QUERIES_BEGIN
proposals <- query @CoinProposal bank
assertEq [CoinProposal (Coin bank bank)] proposals
assertEq [CoinProposal (Coin bank bank)] (map snd proposals)

aliceCoins <- query @Coin alice
assertEq [Coin bank alice] aliceCoins
assertEq [Coin bank alice] (map snd aliceCoins)

bobCoins <- query @Coin bob
assertEq [Coin bank bob] bobCoins
assertEq [Coin bank bob] (map snd bobCoins)
-- TEST_QUERIES_END

0 comments on commit f94b832

Please sign in to comment.