Skip to content

Commit

Permalink
#85 Support conditional sending of notifications (non-reactive code)
Browse files Browse the repository at this point in the history
  • Loading branch information
cer committed Sep 7, 2022
1 parent 02c173b commit b11bb74
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ build/
*.iml
*.log
out
**/bin/**
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ public InvokeParticipantStepBuilder<Data> withCompensationNotification(Function<
return this;
}

public InvokeParticipantStepBuilder<Data> withCompensationNotification(Predicate<Data> compensationPredicate, Function<Data, CommandWithDestination> compensation) {
this.compensation = Optional.of(new ParticipantInvocationImpl<>(Optional.of(compensationPredicate), compensation, true));
return this;
}

@Override
public InvokeParticipantStepBuilder<Data> withCompensation(Predicate<Data> compensationPredicate, Function<Data, CommandWithDestination> compensation) {
this.compensation = Optional.of(new ParticipantInvocationImpl<>(Optional.of(compensationPredicate), compensation));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,8 @@ public <C extends Command> InvokeParticipantStepBuilder<Data> withCompensation(P
public InvokeParticipantStepBuilder<Data> notifyParticipant(Function<Data, CommandWithDestination> notificationAction) {
return new InvokeParticipantStepBuilder<>(parent).withNotificationAction(Optional.empty(), notificationAction);
}

public InvokeParticipantStepBuilder<Data> notifyParticipant(Predicate<Data> participantInvocationPredicate, Function<Data, CommandWithDestination> notificationAction) {
return new InvokeParticipantStepBuilder<>(parent).withNotificationAction(Optional.of(participantInvocationPredicate), notificationAction);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.eventuate.tram.sagas.simpledsl.notifications;

import io.eventuate.tram.sagas.orchestration.SagaDefinition;
import io.eventuate.tram.sagas.simpledsl.SimpleSaga;

public class ConditionalNotificationBasedCreateOrderSaga implements SimpleSaga<ConditionalNotificationBasedCreateOrderSagaData> {

private final SagaDefinition<ConditionalNotificationBasedCreateOrderSagaData> sagaDefinition;

public ConditionalNotificationBasedCreateOrderSaga(ConditionalNotificationBasedCreateOrderSagaSteps steps) {
this.sagaDefinition =
step()
.invokeLocal(steps::createOrder)
.withCompensation(steps::rejectOrder)
.step()
.invokeParticipant(ConditionalNotificationBasedCreateOrderSagaData::reserveCredit)
.withCompensation(ConditionalNotificationBasedCreateOrderSagaData::releaseCredit)
.step()
.invokeParticipant(ConditionalNotificationBasedCreateOrderSagaData::reserveInventory)
.withCompensationNotification(ConditionalNotificationBasedCreateOrderSagaData::isReleaseInventory, ConditionalNotificationBasedCreateOrderSagaData::releaseInventory)
.step()
.invokeLocal(steps::approveOrder)
.step()
.notifyParticipant(ConditionalNotificationBasedCreateOrderSagaData::isFulfillOrder, ConditionalNotificationBasedCreateOrderSagaData::fulfillOrder)
.build();
}


@Override
public SagaDefinition<ConditionalNotificationBasedCreateOrderSagaData> getSagaDefinition() {
return this.sagaDefinition;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package io.eventuate.tram.sagas.simpledsl.notifications;

import io.eventuate.tram.commands.consumer.CommandWithDestination;
import io.eventuate.tram.sagas.simpledsl.ReleaseCreditCommand;
import io.eventuate.tram.sagas.simpledsl.ReserveCreditCommand;

public class ConditionalNotificationBasedCreateOrderSagaData {

private boolean fulfillOrder = true;
private boolean releaseInventory = true;

public CommandWithDestination reserveCredit() {
return new CommandWithDestination("customerService", null, new ReserveCreditCommand());
}

public CommandWithDestination releaseCredit() {
return new CommandWithDestination("customerService", null, new ReleaseCreditCommand());
}

public CommandWithDestination reserveInventory() {
return new CommandWithDestination("inventoryService", null, new ReserveInventory());
}

public CommandWithDestination releaseInventory() {
return new CommandWithDestination("inventoryService", null, new ReleaseInventory());
}

public CommandWithDestination fulfillOrder() {
return new CommandWithDestination("fulfillmentService", null, new FulfillOrder());
}

public ConditionalNotificationBasedCreateOrderSagaData skipFulfillOrder() {
this.fulfillOrder = false;
return this;
}

public ConditionalNotificationBasedCreateOrderSagaData skipReleaseInventory() {
this.releaseInventory = false;
return this;
}

public boolean isFulfillOrder() {
return fulfillOrder;
}

public boolean isReleaseInventory() {
return releaseInventory;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.eventuate.tram.sagas.simpledsl.notifications;

public interface ConditionalNotificationBasedCreateOrderSagaSteps {

void createOrder(ConditionalNotificationBasedCreateOrderSagaData data);
void rejectOrder(ConditionalNotificationBasedCreateOrderSagaData data);
void approveOrder(ConditionalNotificationBasedCreateOrderSagaData data);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package io.eventuate.tram.sagas.simpledsl.notifications;

import io.eventuate.tram.sagas.simpledsl.ReleaseCreditCommand;
import io.eventuate.tram.sagas.simpledsl.ReserveCreditCommand;
import org.junit.Before;
import org.junit.Test;

import static io.eventuate.tram.sagas.testing.SagaUnitTestSupport.given;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;

public class ConditionalNotificationBasedCreateOrderSagaTest {

private ConditionalNotificationBasedCreateOrderSagaSteps steps;

@Before
public void setUp() throws Exception {
steps = mock(ConditionalNotificationBasedCreateOrderSagaSteps.class);
}

@Test
public void shouldExecuteAllStepsSuccessfully() {
given().
saga(new ConditionalNotificationBasedCreateOrderSaga(steps), new ConditionalNotificationBasedCreateOrderSagaData()).
expect().
command(new ReserveCreditCommand()).
to("customerService").
andGiven().
successReply().
expect().
command(new ReserveInventory()).
to("inventoryService").
andGiven().
successReply().
expect().
notification(new FulfillOrder()).
to("fulfillmentService").
expectCompletedSuccessfully()
;
}

@Test
public void shouldExecuteAllStepsSuccessfullyWithSkippedFulfilOrder() {
given().
saga(new ConditionalNotificationBasedCreateOrderSaga(steps),
new ConditionalNotificationBasedCreateOrderSagaData().skipFulfillOrder()).
expect().
command(new ReserveCreditCommand()).
to("customerService").
andGiven().
successReply().
expect().
command(new ReserveInventory()).
to("inventoryService").
andGiven().
successReply().
expectCompletedSuccessfully()
;
}

@Test
public void shouldHandleFailureOfLastLocalStep() {
doThrow(new RuntimeException()).when(steps).approveOrder(any());
given().
saga(new ConditionalNotificationBasedCreateOrderSaga(steps), new ConditionalNotificationBasedCreateOrderSagaData()).
expect().
command(new ReserveCreditCommand()).
to("customerService").
andGiven().
successReply().
expect().
command(new ReserveInventory()).
to("inventoryService").
andGiven().
successReply().
expect().
multiple().
notification(new ReleaseInventory()).
to("inventoryService").
command(new ReleaseCreditCommand()).
to("customerService").
verify().
andGiven().
successReply().
expectRolledBack()
;
}


@Test
public void shouldHandleFailureOfLastLocalStepWithSkippedReleaseInventory() {
doThrow(new RuntimeException()).when(steps).approveOrder(any());
given().
saga(new ConditionalNotificationBasedCreateOrderSaga(steps),
new ConditionalNotificationBasedCreateOrderSagaData()
.skipReleaseInventory()).
expect().
command(new ReserveCreditCommand()).
to("customerService").
andGiven().
successReply().
expect().
command(new ReserveInventory()).
to("inventoryService").
andGiven().
successReply().
expect().
multiple().
command(new ReleaseCreditCommand()).
to("customerService").
verify().
andGiven().
successReply().
expectRolledBack()
;
}


}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ dockerImageTag=latest

eventuateMavenRepoUrl=file:///Users/cer/.m2/testdeploy,https://snapshots.repositories.eventuate.io/repository

springBootVersion=2.4.5
springBootVersion=2.6.11

springCloudContractDependenciesVersion=2.0.0.RELEASE
springDependencyManagementPluginVersion=1.0.3.RELEASE
Expand Down

0 comments on commit b11bb74

Please sign in to comment.