Skip to content
Permalink
Browse files

Expose a contract's agreement text on the Ledger API

Fixes #1110
  • Loading branch information...
gerolf-da committed May 15, 2019
1 parent 91f2414 commit d6236a9f1ddc6fc9e8851ffd95830dabdba7d6eb
Showing with 294 additions and 116 deletions.
  1. +2 −0 daml-lf/engine/src/main/scala/com/digitalasset/daml/lf/engine/Event.scala
  2. +1 −0 daml-lf/engine/src/test/scala/com/digitalasset/daml/lf/engine/EngineTest.scala
  3. +1 −0 daml-lf/testing-tools/src/main/scala/com/digitalasset/daml/lf/engine/testing/SemanticTester.scala
  4. +3 −3 docs/source/app-dev/bindings-java/codegen.rst
  5. +2 −2 ...tting-started/quickstart/template-root/src/main/java/com/digitalasset/quickstart/iou/IouMain.java
  6. +2 −1 language-support/java/bindings-rxjava/src/test/scala/com/daml/ledger/rxjava/components/BotTest.scala
  7. +17 −3 ...ava/bindings-rxjava/src/test/scala/com/daml/ledger/rxjava/grpc/helpers/TransactionGenerator.scala
  8. +21 −7 language-support/java/bindings/src/main/java/com/daml/ledger/javaapi/data/CreatedEvent.java
  9. +36 −4 language-support/java/codegen/src/it/java/com/digitalasset/TemplateMethodTest.java
  10. +1 −2 language-support/java/codegen/src/ledger-tests/scala/com/digitalasset/CodegenLedgerTest.scala
  11. +7 −10 ...ava/codegen/src/main/scala/com/digitalasset/daml/lf/codegen/backend/java/inner/DecoderClass.scala
  12. +81 −13 ...va/codegen/src/main/scala/com/digitalasset/daml/lf/codegen/backend/java/inner/TemplateClass.scala
  13. +6 −2 ...odegen/src/test/scala/com/digitalasset/daml/lf/codegen/backend/java/inner/TemplateClassSpec.scala
  14. +1 −1 ...ndings-akka/src/test/scala/com/digitalasset/ledger/client/binding/DomainTransactionMapperUT.scala
  15. +4 −1 ...pport/scala/bindings/src/main/scala/com/digitalasset/ledger/client/binding/binding/Contract.scala
  16. +2 −1 ...cala/bindings/src/main/scala/com/digitalasset/ledger/client/binding/binding/EventDecoderApi.scala
  17. +4 −1 ...age-support/scala/codegen-sample-app/src/test/scala/com/digitalasset/codegen/ScalaCodeGenIT.scala
  18. +1 −1 ...ples/quickstart-scala/application/src/main/scala/com/digitalasset/quickstart/iou/DecodeUtil.scala
  19. +5 −0 ledger-api/grpc-definitions/com/digitalasset/ledger/api/v1/event.proto
  20. +1 −1 ledger/ledger-api-common/src/main/scala/com/digitalasset/platform/participant/util/EventFilter.scala
  21. +2 −1 .../src/main/scala/com/digitalasset/platform/server/services/transaction/TransactionConversion.scala
  22. +2 −1 ledger/ledger-api-domain/src/main/scala/com/digitalasset/ledger/api/domain.scala
  23. +2 −2 ...suite/scala/com/digitalasset/platform/tests/integration/ledger/api/ActiveContractsServiceIT.scala
  24. +1 −1 ...e/scala/com/digitalasset/platform/tests/integration/ledger/api/commands/CommandStaticTimeIT.scala
  25. +1 −1 ...tion-tests/src/test/lib/scala/com/digitalasset/platform/apitesting/CommandTransactionChecks.scala
  26. +1 −24 ...ration-tests/src/test/lib/scala/com/digitalasset/platform/semantictest/ApiScenarioTransform.scala
  27. +1 −1 ledger/sandbox-perf/src/perf/benches/scala/com/digitalasset/platform/sandbox/perf/AcsBench.scala
  28. +1 −1 ledger/sandbox-perf/src/perf/lib/scala/com/digitalasset/platform/sandbox/perf/TestHelper.scala
  29. +1 −1 ledger/sandbox/src/test/lib/scala/com/digitalasset/platform/sandbox/ScenarioLoadingITBase.scala
  30. +4 −2 .../test/suite/scala/com/digitalasset/platform/sandbox/services/transaction/EventConverterSpec.scala
  31. +1 −1 navigator/backend/README.md
  32. +9 −3 navigator/backend/src/main/scala/com/digitalasset/navigator/data/ContractRow.scala
  33. +8 −4 navigator/backend/src/main/scala/com/digitalasset/navigator/data/EventRow.scala
  34. +2 −1 navigator/backend/src/main/scala/com/digitalasset/navigator/graphql/GraphQLSchema.scala
  35. +4 −2 navigator/backend/src/main/scala/com/digitalasset/navigator/json/ModelCodec.scala
  36. +1 −1 navigator/backend/src/main/scala/com/digitalasset/navigator/model/Ledger.scala
  37. +4 −2 navigator/backend/src/main/scala/com/digitalasset/navigator/model/Model.scala
  38. +2 −1 navigator/backend/src/main/scala/com/digitalasset/navigator/model/converter/LedgerApiV1.scala
  39. +7 −0 navigator/backend/src/main/scala/com/digitalasset/navigator/query/filter/package.scala
  40. +1 −0 navigator/backend/src/test/resources/schema.graphql
  41. +2 −1 navigator/backend/src/test/scala/com/digitalasset/navigator/backend/data/RowSpec.scala
  42. +8 −4 navigator/backend/src/test/scala/com/digitalasset/navigator/backend/model/LedgerSpec.scala
  43. +10 −3 navigator/backend/src/test/scala/com/digitalasset/navigator/backend/query/ContractFilterSpec.scala
  44. +12 −4 navigator/backend/src/test/scala/com/digitalasset/navigator/backend/query/ContractSortSpec.scala
  45. +1 −1 navigator/backend/src/test/scala/com/digitalasset/navigator/backend/query/FilterSpec.scala
  46. +1 −0 navigator/frontend/src/api/Queries.ts
  47. +5 −0 navigator/frontend/src/applets/contract/ContractComponent.tsx
  48. +1 −0 navigator/frontend/src/applets/contract/index.tsx
  49. +1 −0 navigator/frontend/src/ui-core/src/index.ts
@@ -37,6 +37,7 @@ final case class CreateEvent[Cid, Val](
contractId: Cid,
templateId: Identifier,
argument: Val,
agreementText: String,
stakeholders: Set[Party],
witnesses: Set[Party])
extends Event[Nothing, Cid, Val] {
@@ -153,6 +154,7 @@ object Event {
nc.coid,
templateId,
nc.coinst.arg,
nc.coinst.agreementText,
stakeholders intersect disclosure(nodeId),
disclosure(nodeId))
evts += (nodeId -> evt)
@@ -952,6 +952,7 @@ class EngineTest extends WordSpec with Matchers {
(Some[Name]("giver"), ValueParty("Alice")),
(Some[Name]("receiver"), ValueParty("Clara")))
)),
"",
Set("Clara", "Alice"),
Set("Bob", "Clara", "Alice"),
)
@@ -194,6 +194,7 @@ class SemanticTester(
nextScenarioCoidToLedgerCoid(scenarioCreateNode.coid),
scenarioCreateNode.coinst.template,
scenarioCreateNode.coinst.arg.mapContractId(nextScenarioCoidToLedgerCoid),
scenarioCreateNode.coinst.agreementText,
scenarioCreateNode.stakeholders intersect scenarioWitnesses(scenarioNodeId),
scenarioWitnesses(scenarioNodeId),
)
@@ -60,7 +60,7 @@ To avoid possible name clashes in the generated Java sources, you should specify
Generate the decoder utility class
----------------------------------

When reading transactions from the ledger, you typically want to convert a `CreatedEvent <https://docs.daml.com/app-dev/bindings-java/javadocs/com/daml/ledger/javaapi/data/CreatedEvent.html>`__ from the Ledger API to the corresponding generated ``Contract`` class. The Java codegen can optionally generate a decoder class based on the input DAR files that calls the ``fromIdAndRecord`` method of the respective generated ``Contract`` class (see :ref:`daml-codegen-java-templates`). The decoder class can do this for all templates in the input DAR files.
When reading transactions from the ledger, you typically want to convert a `CreatedEvent <https://docs.daml.com/app-dev/bindings-java/javadocs/com/daml/ledger/javaapi/data/CreatedEvent.html>`__ from the Ledger API to the corresponding generated ``Contract`` class. The Java codegen can optionally generate a decoder class based on the input DAR files that calls the ``fromCreatedEvent`` method of the respective generated ``Contract`` class (see :ref:`daml-codegen-java-templates`). The decoder class can do this for all templates in the input DAR files.

To generate such a decoder class, provide the command line parameter ``-d`` or ``--decoderClass`` with a fully qualified class name:

@@ -254,7 +254,7 @@ The Java codegen generates three classes for a DAML template:
.. TODO: refer to another section explaining exactly that, when we have it.
**TemplateName.Contract**
Represents an actual contract on the ledger. It contains a field for the contract ID (of type ``TemplateName.ContractId``) and a field for the template data (of type ``TemplateName``). With the static method ``TemplateName.Contract.fromIdAndRecord``, you can deserialize a `CreatedEvent <https://docs.daml.com/app-dev/bindings-java/javadocs/com/daml/ledger/javaapi/data/CreatedEvent.html>`__ to an instance of ``TemplateName.Contract``.
Represents an actual contract on the ledger. It contains a field for the contract ID (of type ``TemplateName.ContractId``) and a field for the template data (of type ``TemplateName``). With the static method ``TemplateName.Contract.fromCreatedEvent``, you can deserialize a `CreatedEvent <https://docs.daml.com/app-dev/bindings-java/javadocs/com/daml/ledger/javaapi/data/CreatedEvent.html>`__ to an instance of ``TemplateName.Contract``.


.. literalinclude:: ./code-snippets/Templates.daml
@@ -300,7 +300,7 @@ A file is generated that defines three Java classes:
public final ContractId id;
public final Bar data;
public static Contract fromIdAndRecord(String contractId, Record record) { /* ... */ }
public static Contract fromCreatedEvent(CreatedEvent event) { /* ... */ }
}
}
@@ -70,7 +70,7 @@ public static void main(String[] args) {
.blockingForEach(response -> {
response.getOffset().ifPresent(offset -> acsOffset.set(new LedgerOffset.Absolute(offset)));
response.getCreatedEvents().stream()
.map(e -> Iou.Contract.fromIdAndRecord(e.getContractId(), e.getArguments()))
.map(Iou.Contract::fromCreatedEvent)
.forEach(contract -> {
long id = idCounter.getAndIncrement();
contracts.put(id, contract.data);
@@ -85,7 +85,7 @@ public static void main(String[] args) {
if (event instanceof CreatedEvent) {
CreatedEvent createdEvent = (CreatedEvent) event;
long id = idCounter.getAndIncrement();
Iou.Contract contract = Iou.Contract.fromIdAndRecord(createdEvent.getContractId(), createdEvent.getArguments());
Iou.Contract contract = Iou.Contract.fromCreatedEvent(createdEvent);
contracts.put(id, contract.data);
idMap.put(id, contract.id);
} else if (event instanceof ArchivedEvent) {
@@ -426,7 +426,8 @@ class BotTest extends FlatSpec with Matchers with DataLayerHelpers {
s"eid_$id",
templateId,
s"cid_$id",
new Record(List.empty[Record.Field].asJava))
new Record(List.empty[Record.Field].asJava),
Optional.empty())

def archive(event: CreatedEvent): ArchivedEvent =
new ArchivedEvent(
@@ -5,7 +5,7 @@ package com.daml.ledger.rxjava.grpc.helpers

import java.time.Instant
import java.util
import java.util.Collections
import java.util.{Collections, Optional}

import com.daml.ledger.javaapi.data
import com.daml.ledger.testkit.services.TransactionServiceImpl
@@ -183,13 +183,27 @@ object TransactionGenerator {
val createdEventGen: Gen[(Created, data.CreatedEvent)] = for {
eventId <- nonEmptyId
contractId <- nonEmptyId
agreementText <- Gen.option(Gen.asciiStr)
(scalaTemplateId, javaTemplateId) <- identifierGen
(scalaRecord, javaRecord) <- Gen.sized(recordGen)
parties <- Gen.listOf(nonEmptyId)
} yield
(
Created(CreatedEvent(eventId, contractId, Some(scalaTemplateId), Some(scalaRecord), parties)),
new data.CreatedEvent(parties.asJava, eventId, javaTemplateId, contractId, javaRecord)
Created(
CreatedEvent(
eventId,
contractId,
Some(scalaTemplateId),
Some(scalaRecord),
parties,
agreementText = agreementText)),
new data.CreatedEvent(
parties.asJava,
eventId,
javaTemplateId,
contractId,
javaRecord,
agreementText.map(Optional.of[String]).getOrElse(Optional.empty()))
)

val archivedEventGen: Gen[(Archived, data.ArchivedEvent)] = for {
@@ -4,10 +4,12 @@
package com.daml.ledger.javaapi.data;

import com.digitalasset.ledger.api.v1.EventOuterClass;
import com.google.protobuf.StringValue;
import org.checkerframework.checker.nullness.qual.NonNull;

import java.util.List;
import java.util.Objects;
import java.util.Optional;

public final class CreatedEvent implements Event, TreeEvent {

@@ -21,12 +23,15 @@

private final Record arguments;

public CreatedEvent(@NonNull List<@NonNull String> witnessParties, @NonNull String eventId, @NonNull Identifier templateId, @NonNull String contractId, @NonNull Record arguments) {
private final Optional<String> agreementText;

public CreatedEvent(@NonNull List<@NonNull String> witnessParties, @NonNull String eventId, @NonNull Identifier templateId, @NonNull String contractId, @NonNull Record arguments, Optional<String> agreementText) {
this.witnessParties = witnessParties;
this.eventId = eventId;
this.templateId = templateId;
this.contractId = contractId;
this.arguments = arguments;
this.agreementText = agreementText;
}

@NonNull
@@ -58,6 +63,11 @@ public Record getArguments() {
return arguments;
}

@NonNull
public Optional<String> getAgreementText() {
return agreementText;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -67,13 +77,14 @@ public boolean equals(Object o) {
Objects.equals(eventId, that.eventId) &&
Objects.equals(templateId, that.templateId) &&
Objects.equals(contractId, that.contractId) &&
Objects.equals(arguments, that.arguments);
Objects.equals(arguments, that.arguments) &&
Objects.equals(agreementText, that.agreementText);
}

@Override
public int hashCode() {

return Objects.hash(witnessParties, eventId, templateId, contractId, arguments);
return Objects.hash(witnessParties, eventId, templateId, contractId, arguments, agreementText);
}

@Override
@@ -84,17 +95,19 @@ public String toString() {
", templateId=" + templateId +
", contractId='" + contractId + '\'' +
", arguments=" + arguments +
", agreementText='" + agreementText + '\'' +
'}';
}

public EventOuterClass.@NonNull CreatedEvent toProto() {
return EventOuterClass.CreatedEvent.newBuilder()
EventOuterClass.CreatedEvent.Builder builder = EventOuterClass.CreatedEvent.newBuilder()
.setContractId(getContractId())
.setCreateArguments(getArguments().toProtoRecord())
.setEventId(getEventId())
.setTemplateId(getTemplateId().toProto())
.addAllWitnessParties(this.getWitnessParties())
.build();
.addAllWitnessParties(this.getWitnessParties());
agreementText.ifPresent(a -> builder.setAgreementText(StringValue.of(a)));
return builder.build();
}

public static CreatedEvent fromProto(EventOuterClass.CreatedEvent createdEvent) {
@@ -103,7 +116,8 @@ public static CreatedEvent fromProto(EventOuterClass.CreatedEvent createdEvent)
createdEvent.getEventId(),
Identifier.fromProto(createdEvent.getTemplateId()),
createdEvent.getContractId(),
Record.fromProto(createdEvent.getCreateArguments()));
Record.fromProto(createdEvent.getCreateArguments()),
createdEvent.hasAgreementText() ? Optional.of(createdEvent.getAgreementText().getValue()) : Optional.empty());

}
}
@@ -3,16 +3,17 @@

package com.digitalasset;

import com.daml.ledger.javaapi.data.CreateAndExerciseCommand;
import com.daml.ledger.javaapi.data.CreateCommand;
import com.daml.ledger.javaapi.data.ExerciseCommand;
import com.daml.ledger.javaapi.data.*;
import org.junit.jupiter.api.Test;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;
import tests.template1.SimpleTemplate;
import tests.template1.TestTemplate_Int;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.util.Collections;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;

@RunWith(JUnitPlatform.class)
public class TemplateMethodTest {
@@ -21,6 +22,8 @@
// at compilation time in case any of the methods are generated differently
// or not at all

private static Record simpleTemplateRecord = new Record(new Record.Field(new Party("Bob")));

@Test
void templateHasCreateMethods() {
CreateCommand fromStatic = SimpleTemplate.create("Bob");
@@ -50,5 +53,34 @@ void templateHasCreateAndExerciseMethods() {

assertNotNull(fromSplatted, "CreateAndExerciseCommand from splatted choice was null");
assertNotNull(fromRecord, "CreateAndExerciseCommand from record choice was null");
assertEquals(fromRecord, fromSplatted, "CreateAndExerciseCommands from both methods are not the same");
}

@Test
void contractHasDeprecatedFromIdAndRecord() {
SimpleTemplate.Contract contract = SimpleTemplate.Contract.fromIdAndRecord("SomeId", simpleTemplateRecord);
assertFalse(contract.agreementText.isPresent(), "Field agreementText should not be present");
}

@Test
void contractHasFromIdAndRecord() {
SimpleTemplate.Contract emptyAgreement = SimpleTemplate.Contract.fromIdAndRecord("SomeId", simpleTemplateRecord, Optional.empty());
assertFalse(emptyAgreement.agreementText.isPresent(), "Field agreementText should not be present");

SimpleTemplate.Contract nonEmptyAgreement = SimpleTemplate.Contract.fromIdAndRecord("SomeId", simpleTemplateRecord, Optional.of("I agree"));
assertTrue(nonEmptyAgreement.agreementText.isPresent(), "Field agreementText should be present");
assertEquals(nonEmptyAgreement.agreementText, Optional.of("I agree"), "Unexpected agreementText");
}

@Test
void contractHasFromCreatedEvent() {
CreatedEvent agreementEvent = new CreatedEvent(Collections.emptyList(), "eventId", SimpleTemplate.TEMPLATE_ID, "cid", simpleTemplateRecord, Optional.of("I agree"));
CreatedEvent noAgreementEvent = new CreatedEvent(Collections.emptyList(), "eventId", SimpleTemplate.TEMPLATE_ID, "cid", simpleTemplateRecord, Optional.empty());

SimpleTemplate.Contract withAgreement = SimpleTemplate.Contract.fromCreatedEvent(agreementEvent);
assertTrue(withAgreement.agreementText.isPresent(), "AgreementText was not present");

SimpleTemplate.Contract withoutAgreement = SimpleTemplate.Contract.fromCreatedEvent(noAgreementEvent);
assertFalse(withoutAgreement.agreementText.isPresent(), "AgreementText was present");
}
}
@@ -117,8 +117,7 @@ class CodegenLedgerTest extends FlatSpec with Matchers {
.foldLeft(Map[String, Wolpertinger.Contract]())((acc, event) =>
event match {
case e: CreatedEvent =>
acc + (e.getContractId -> Wolpertinger.Contract
.fromIdAndRecord(e.getContractId, e.getArguments))
acc + (e.getContractId -> Wolpertinger.Contract.fromCreatedEvent(e))
case a: ArchivedEvent => acc - a.getContractId
})
.toList
@@ -3,8 +3,6 @@

package com.digitalasset.daml.lf.codegen.backend.java.inner

import java.util.function.BiFunction

import com.daml.ledger.javaapi.data._
import com.squareup.javapoet._
import javax.lang.model.element.Modifier
@@ -29,9 +27,8 @@ object DecoderClass {
)

private val decoderFunctionType = ParameterizedTypeName.get(
ClassName.get(classOf[BiFunction[_, _, _]]),
ClassName.get(classOf[String]),
ClassName.get(classOf[Record]),
ClassName.get(classOf[java.util.function.Function[_, _]]),
ClassName.get(classOf[CreatedEvent]),
ClassName.get(classOf[Contract])
)

@@ -52,10 +49,10 @@ object DecoderClass {
.builder()
.addStatement("Identifier templateId = event.getTemplateId()")
.addStatement(
"BiFunction<String, Record, Contract> fromIdAndRecord = getDecoder(templateId).orElseThrow(() -> new IllegalArgumentException(\"No template found for identifier \" + templateId))")
.addStatement("String contractId = event.getContractId()")
.addStatement("Record arguments = event.getArguments()")
.addStatement("return fromIdAndRecord.apply(contractId, arguments)")
"$T decoderFunc = getDecoder(templateId).orElseThrow(() -> new IllegalArgumentException(\"No template found for identifier \" + templateId))",
decoderFunctionType
)
.addStatement("return decoderFunc.apply(event)")
.build())
.build()

@@ -78,7 +75,7 @@ object DecoderClass {
b.addStatement("$N = new $T()", decodersField, decodersMapType)
templateNames.foreach { template =>
b.addStatement(
"$N.put($T.TEMPLATE_ID, $T.Contract::fromIdAndRecord)",
"$N.put($T.TEMPLATE_ID, $T.Contract::fromCreatedEvent)",
decodersField,
template,
template)

0 comments on commit d6236a9

Please sign in to comment.
You can’t perform that action at this time.