Skip to content

Commit

Permalink
Remove ExercisedEvent in Event oneof.
Browse files Browse the repository at this point in the history
The Event message is only used in the Transaction message. Flat
transactions do not contain exercised events, but only created and
archived events. Therefore we can remove the ExercisedEvent from the
Event oneof, without breaking transport compatibility.

HOWEVER: The Java Bindings used the data.Event class for both flat
transactions and transaction trees. To properly represent the actual
event types in the two transaction structures, I added two new
interfaces FlatEvent and TreeEvent for flat transactions and transaction
trees respectively.

Some "pathological" cases where an occurrence of an exercised event
would have resulted only in an exception, are now removed (see change in
LedgerApiV1.scala).

Fixes #960.
  • Loading branch information
gerolf-da committed May 10, 2019
1 parent de54e8f commit b01f0cc
Show file tree
Hide file tree
Showing 27 changed files with 190 additions and 185 deletions.
12 changes: 12 additions & 0 deletions docs/source/support/release-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ HEAD — ongoing

- Sandbox: Transactions with a record time that is after the maximum record time (as provided in the original command)
are now properly rejected instead of committed to the ledger: `#987 <https://github.com/digital-asset/daml/issues/987>`_
- **BREAKING** Ledger API: Removed the unused field :ref:`com.digitalasset.ledger.api.v1.ExercisedEvent` from :ref:`com.digitalasset.ledger.api.v1.Event`.
``Event`` is only used in :ref:`com.digitalasset.ledger.api.v1.Transaction`, which in turn by definition never contains exercised events.
If you check for the presence of ``ExercisedEvent`` when handling a :ref:`com.digitalasset.ledger.api.v1.Transaction`, you have to remove this code now.
- **BREAKING** Java Bindings: Reflect breaking change of Ledger API.

- The ``data.Event`` class is now deprecated.
- Added interface ``data.FlatEvent``.
- Added interface ``data.TreeEvent``.
- ``data.CreatedEvent`` and ``data.ArchivedEvent`` now implement ``data.FlatEvent``.
- ``data.CreatedEvent`` and ``data.ExercisedEvent`` now implement ``data.TreeEvent``.
- ``data.Transaction#events`` is now ``List<FlatEvent>`` (was previously ``List<Event>``).
- ``data.TransactionTree#eventsById`` is now ``Map<String, TreeEvent>`` (was previously ``Map<String, Event>``).

.. _release-0-12-16:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,8 @@ object Event {
final implicit class ApiEventOps(val apiEvent: api.event.Event.Event) extends AnyVal {
def convert: String \/ Event = apiEvent match {
case api.event.Event.Event.Archived(event) =>
s"Unexpected `Archived` event: $event. Only `Created` and `Exercised` events are expected.".left
s"Unexpected `Archived` event: $event. Only `Created` events are expected.".left
case api.event.Event.Event.Created(event) => event.convert
case api.event.Event.Event.Exercised(event) => event.convert
case api.event.Event.Event.Empty => "Unexpected `Empty` event.".left
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ static <R> StateWithShouldEmit<R> accumulate(StateWithShouldEmit<R> stateWithSho
Transaction transaction = (Transaction) workflowEvent;
LedgerView<R> newLedgerView = stateWithShouldEmit.ledgerView;
TransactionContext transactionContext = TransactionContext.forTransaction(transaction);
for (Event event : transaction.getEvents()) {
for (FlatEvent event : transaction.getEvents()) {
if (event instanceof CreatedEvent) {
CreatedEvent createdEvent = (CreatedEvent) event;
R r = transform.apply(new CreatedContract(createdEvent.getTemplateId(), createdEvent.getArguments(), transactionContext));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import java.util.Objects;

/**
* A {@link Transaction} without the {@link com.daml.ledger.javaapi.data.Event}s.
* A {@link Transaction} without the {@link com.daml.ledger.javaapi.data.FlatEvent}s.
*/
public class TransactionContext implements CreatedContractContext {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class BotTest extends FlatSpec with Matchers with DataLayerHelpers {
"commandId",
"workflowId",
zeroTimestamp,
List[Event](contract1).asJava,
List[FlatEvent](contract1).asJava,
"1")
val testSub = workflowEvents.test()

Expand Down Expand Up @@ -435,7 +435,7 @@ class BotTest extends FlatSpec with Matchers with DataLayerHelpers {
event.getTemplateId,
event.getContractId)

def transaction(events: List[Event]): Transaction =
def transaction(events: List[FlatEvent]): Transaction =
new Transaction(
"tid",
s"cid_${random.nextInt()}",
Expand All @@ -444,10 +444,10 @@ class BotTest extends FlatSpec with Matchers with DataLayerHelpers {
events.asJava,
events.size.toString)

def transactionWithOffset(offset: String, events: List[Event]): Transaction =
def transactionWithOffset(offset: String, events: List[FlatEvent]): Transaction =
new Transaction("tid", s"cid_${random.nextInt()}", "wid", zeroTimestamp, events.asJava, offset)

def transactionArray(events: Event*): Transaction = transaction(events.toList)
def transactionArray(events: FlatEvent*): Transaction = transaction(events.toList)
}

@SuppressWarnings(Array("org.wartremover.warts.Any"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import java.util.Collections
import com.daml.ledger.javaapi.data
import com.daml.ledger.testkit.services.TransactionServiceImpl
import com.daml.ledger.testkit.services.TransactionServiceImpl.LedgerItem
import com.digitalasset.ledger.api.v1.event.Event.Event.{Archived, Created, Exercised}
import com.digitalasset.ledger.api.v1.event.Event.Event.{Archived, Created}
import com.digitalasset.ledger.api.v1.event.{ArchivedEvent, CreatedEvent, Event, ExercisedEvent}
import com.digitalasset.ledger.api.v1.transaction.TreeEvent.Kind.Exercised
import com.digitalasset.ledger.api.v1.value
import com.digitalasset.ledger.api.v1.value.Value.Sum
import com.digitalasset.ledger.api.v1.value.{Identifier, Record, RecordField, Value, Variant}
Expand Down Expand Up @@ -246,12 +247,12 @@ object TransactionGenerator {
)
)

val eventGen: Gen[(Event, data.Event)] =
Gen.oneOf(createdEventGen, archivedEventGen, exercisedEventGen).map {
val eventGen: Gen[(Event, data.FlatEvent)] =
Gen.oneOf(createdEventGen, archivedEventGen).map {
case (scalaEvent, javaEvent) => (Event(scalaEvent), javaEvent)
}

def eventsGen: Gen[(List[Event], util.List[data.Event])] = eventGen.map {
def eventsGen: Gen[(List[Event], util.List[data.FlatEvent])] = eventGen.map {
case (scalaEvent, javaEvent) => (List(scalaEvent), List(javaEvent).asJava)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import java.util.List;
import java.util.Objects;

public final class ArchivedEvent extends Event {
public final class ArchivedEvent extends Event implements FlatEvent {

private final List<String> witnessParties;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import java.util.List;
import java.util.Objects;

public final class CreatedEvent extends Event {
public final class CreatedEvent extends Event implements FlatEvent, TreeEvent {

private final @NonNull List<@NonNull String> witnessParties;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,23 @@
package com.daml.ledger.javaapi.data;

import com.digitalasset.ledger.api.v1.EventOuterClass;

import com.digitalasset.ledger.api.v1.TransactionOuterClass;
import org.checkerframework.checker.nullness.qual.NonNull;

import java.util.List;
import java.util.stream.Collectors;

/**
* @see FlatEvent
* @see TreeEvent
* @see <a href="https://github.com/digital-asset/daml/issues/960">#960</a>
* @deprecated This class is deprecated in favour of the interfaces {@link FlatEvent} used by {@link Transaction}
* and {@link TreeEvent} used by {@link TransactionTree}.
*/
@Deprecated
public abstract class Event {

public abstract @NonNull List<@NonNull String> getWitnessParties();
@NonNull
public abstract List<@NonNull String> getWitnessParties();

@NonNull
public abstract String getEventId();
Expand All @@ -24,80 +31,13 @@ public abstract class Event {
@NonNull
public abstract String getContractId();

public static Event fromProtoEvent(EventOuterClass.Event event) {

if (event.hasCreated()) {
EventOuterClass.CreatedEvent createdEvent = event.getCreated();
return new CreatedEvent(createdEvent.getWitnessPartiesList(), createdEvent.getEventId(),
Identifier.fromProto(createdEvent.getTemplateId()),
createdEvent.getContractId(), Record.fromProto(createdEvent.getCreateArguments()));
} else if (event.hasArchived()) {
EventOuterClass.ArchivedEvent archivedEvent = event.getArchived();
return new ArchivedEvent(archivedEvent.getWitnessPartiesList(), archivedEvent.getEventId(),
Identifier.fromProto(archivedEvent.getTemplateId()),
archivedEvent.getContractId());
} else if (event.hasExercised()) {
EventOuterClass.ExercisedEvent exercisedEvent = event.getExercised();
return new ExercisedEvent(exercisedEvent.getWitnessPartiesList(), exercisedEvent.getEventId(),
Identifier.fromProto(exercisedEvent.getTemplateId()),
exercisedEvent.getContractId(), exercisedEvent.getContractCreatingEventId(),
exercisedEvent.getChoice(), Value.fromProto(exercisedEvent.getChoiceArgument()),
exercisedEvent.getActingPartiesList(), exercisedEvent.getConsuming(),
exercisedEvent.getChildEventIdsList(), Value.fromProto(exercisedEvent.getExerciseResult()));
} else {
throw new UnsupportedEventTypeException(event.toString());
}
}

public static Event fromProtoTreeEvent(TransactionOuterClass.TreeEvent event) {

if (event.hasCreated()) {
EventOuterClass.CreatedEvent createdEvent = event.getCreated();
return new CreatedEvent(createdEvent.getWitnessPartiesList(), createdEvent.getEventId(),
Identifier.fromProto(createdEvent.getTemplateId()),
createdEvent.getContractId(), Record.fromProto(createdEvent.getCreateArguments()));
} else if (event.hasExercised()) {
EventOuterClass.ExercisedEvent exercisedEvent = event.getExercised();
return new ExercisedEvent(exercisedEvent.getWitnessPartiesList(), exercisedEvent.getEventId(),
Identifier.fromProto(exercisedEvent.getTemplateId()),
exercisedEvent.getContractId(), exercisedEvent.getContractCreatingEventId(),
exercisedEvent.getChoice(), Value.fromProto(exercisedEvent.getChoiceArgument()),
exercisedEvent.getActingPartiesList(), exercisedEvent.getConsuming(),
exercisedEvent.getChildEventIdsList(), Value.fromProto(exercisedEvent.getExerciseResult()));
} else {
throw new UnsupportedEventTypeException(event.toString());
}
}

public EventOuterClass.Event toProtoEvent() {
EventOuterClass.Event.Builder eventBuilder = EventOuterClass.Event.newBuilder();
if (this instanceof ArchivedEvent) {
ArchivedEvent event = (ArchivedEvent) this;
eventBuilder.setArchived(event.toProto());
} else if (this instanceof CreatedEvent) {
CreatedEvent event = (CreatedEvent) this;
eventBuilder.setCreated(event.toProto());
} else if (this instanceof ExercisedEvent) {
ExercisedEvent event = (ExercisedEvent) this;
eventBuilder.setExercised(event.toProto());
} else {
throw new RuntimeException("this should be ArchivedEvent or CreatedEvent or ExercisedEvent, found " + this.toString());
}
return eventBuilder.build();
public static TreeEvent fromProtoTreeEvent(TransactionOuterClass.TreeEvent event) {
return TreeEvent.fromProtoTreeEvent(event);
}

public TransactionOuterClass.TreeEvent toProtoTreeEvent() {
TransactionOuterClass.TreeEvent.Builder eventBuilder = TransactionOuterClass.TreeEvent.newBuilder();
if (this instanceof CreatedEvent) {
CreatedEvent event = (CreatedEvent) this;
eventBuilder.setCreated(event.toProto());
} else if (this instanceof ExercisedEvent) {
ExercisedEvent event = (ExercisedEvent) this;
eventBuilder.setExercised(event.toProto());
} else {
throw new RuntimeException("this should be CreatedEvent or ExercisedEvent, found " + this.toString());
}
return eventBuilder.build();
public static FlatEvent fromProtoEvent(EventOuterClass.Event event) {
return FlatEvent.fromProtoEvent(event);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class ExercisedEvent extends Event {
public class ExercisedEvent extends Event implements TreeEvent {

private final List<String> witnessParties;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.ledger.javaapi.data;

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

import java.util.List;

/**
* This interface represents events in {@link Transaction}s.
*
* @see CreatedEvent
* @see ArchivedEvent
*
*/
public interface FlatEvent {

@NonNull List<@NonNull String> getWitnessParties();

@NonNull String getEventId();

@NonNull Identifier getTemplateId();

@NonNull String getContractId();

default EventOuterClass.Event toProtoEvent() {
EventOuterClass.Event.Builder eventBuilder = EventOuterClass.Event.newBuilder();
if (this instanceof ArchivedEvent) {
ArchivedEvent event = (ArchivedEvent) this;
eventBuilder.setArchived(event.toProto());
} else if (this instanceof CreatedEvent) {
CreatedEvent event = (CreatedEvent) this;
eventBuilder.setCreated(event.toProto());
} else {
throw new RuntimeException("this should be ArchivedEvent or CreatedEvent or ExercisedEvent, found " + this.toString());
}
return eventBuilder.build();
}

static FlatEvent fromProtoEvent(EventOuterClass.Event event) {
if (event.hasCreated()) {
return CreatedEvent.fromProto(event.getCreated());
} else if (event.hasArchived()) {
return ArchivedEvent.fromProto(event.getArchived());
} else {
throw new UnsupportedEventTypeException(event.toString());
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ public class Transaction implements WorkflowEvent {

private final Instant effectiveAt;

private final java.util.List<Event> events;
private final java.util.List<FlatEvent> events;

private final String offset;

public Transaction(@NonNull String transactionId, @NonNull String commandId, @NonNull String workflowId, @NonNull Instant effectiveAt, @NonNull List<@NonNull Event> events, @NonNull String offset) {
public Transaction(@NonNull String transactionId, @NonNull String commandId, @NonNull String workflowId, @NonNull Instant effectiveAt, @NonNull List<@NonNull FlatEvent> events, @NonNull String offset) {
this.transactionId = transactionId;
this.commandId = commandId;
this.workflowId = workflowId;
Expand All @@ -39,7 +39,7 @@ public static Transaction fromProto(TransactionOuterClass.Transaction transactio
String commandId = transaction.getCommandId();
Instant effectiveAt = Instant.ofEpochSecond(transaction.getEffectiveAt().getSeconds(), transaction.getEffectiveAt().getNanos());
String workflowId = transaction.getWorkflowId();
java.util.List<Event> events = transaction.getEventsList().stream().map(Event::fromProtoEvent).collect(Collectors.toList());
java.util.List<FlatEvent> events = transaction.getEventsList().stream().map(FlatEvent::fromProtoEvent).collect(Collectors.toList());
String offset = transaction.getOffset();
return new Transaction(transactionId, commandId, workflowId, effectiveAt, events, offset);
}
Expand All @@ -49,7 +49,7 @@ public TransactionOuterClass.Transaction toProto() {
.setTransactionId(this.transactionId)
.setCommandId(this.commandId)
.setEffectiveAt(com.google.protobuf.Timestamp.newBuilder().setSeconds(this.effectiveAt.getEpochSecond()).setNanos(this.effectiveAt.getNano()).build())
.addAllEvents(this.events.stream().map(Event::toProtoEvent).collect(Collectors.toList()))
.addAllEvents(this.events.stream().map(FlatEvent::toProtoEvent).collect(Collectors.toList()))
.setOffset(this.offset)
.build();
}
Expand All @@ -70,7 +70,7 @@ public Instant getEffectiveAt() {
}

@NonNull
public List<Event> getEvents() {
public List<FlatEvent> getEvents() {
return events;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import com.digitalasset.ledger.api.v1.TransactionOuterClass;
import org.checkerframework.checker.nullness.qual.NonNull;

import javax.annotation.Nonnull;
import java.time.Instant;
import java.util.List;
import java.util.Map;
Expand All @@ -23,13 +22,13 @@ public class TransactionTree {

private final Instant effectiveAt;

private final Map<String, Event> eventsById;
private final Map<String, TreeEvent> eventsById;

private final List<String> rootEventIds;

private final String offset;

public TransactionTree(@NonNull String transactionId, @NonNull String commandId, @NonNull String workflowId, @NonNull Instant effectiveAt, @NonNull Map<@NonNull String, @NonNull Event> eventsById, List<String> rootEventIds, @NonNull String offset) {
public TransactionTree(@NonNull String transactionId, @NonNull String commandId, @NonNull String workflowId, @NonNull Instant effectiveAt, @NonNull Map<@NonNull String, @NonNull TreeEvent> eventsById, List<String> rootEventIds, @NonNull String offset) {
this.transactionId = transactionId;
this.commandId = commandId;
this.workflowId = workflowId;
Expand All @@ -44,11 +43,11 @@ public static TransactionTree fromProto(TransactionOuterClass.TransactionTree tr
String commandId = tree.getCommandId();
String workflowId = tree.getWorkflowId();
Instant effectiveAt = Instant.ofEpochSecond(tree.getEffectiveAt().getSeconds(), tree.getEffectiveAt().getNanos());
Map<String, Event> eventsById = tree.getEventsByIdMap().values().stream().collect(Collectors.toMap(e -> {
Map<String, TreeEvent> eventsById = tree.getEventsByIdMap().values().stream().collect(Collectors.toMap(e -> {
if (e.hasCreated()) return e.getCreated().getEventId();
else if (e.hasExercised()) return e.getExercised().getEventId();
else throw new IllegalArgumentException("Event is neither created not exercied: " + e);
}, Event::fromProtoTreeEvent));
}, TreeEvent::fromProtoTreeEvent));
List<String> rootEventIds = tree.getRootEventIdsList();
String offset = tree.getOffset();
return new TransactionTree(transactionId, commandId, workflowId, effectiveAt, eventsById, rootEventIds, offset);
Expand All @@ -60,7 +59,7 @@ public TransactionOuterClass.TransactionTree toProto() {
.setCommandId(this.commandId)
.setWorkflowId(this.workflowId)
.setEffectiveAt(com.google.protobuf.Timestamp.newBuilder().setSeconds(this.effectiveAt.getEpochSecond()).setNanos(this.effectiveAt.getNano()).build())
.putAllEventsById(this.eventsById.values().stream().collect(Collectors.toMap(Event::getEventId, Event::toProtoTreeEvent)))
.putAllEventsById(this.eventsById.values().stream().collect(Collectors.toMap(TreeEvent::getEventId, TreeEvent::toProtoTreeEvent)))
.addAllRootEventIds(this.rootEventIds)
.setOffset(this.offset)
.build();
Expand All @@ -87,7 +86,7 @@ public Instant getEffectiveAt() {
}

@NonNull
public Map<String, Event> getEventsById() {
public Map<String, TreeEvent> getEventsById() {
return eventsById;
}

Expand Down
Loading

0 comments on commit b01f0cc

Please sign in to comment.