updating combined patterns

Signed-off-by: salaboy <Salaboy@gmail.com>
This commit is contained in:
salaboy 2025-08-11 10:01:16 +01:00
parent 1ea44f871f
commit 0bdbe01f5d
30 changed files with 373 additions and 222 deletions

View File

@ -41,6 +41,12 @@
<version>${dapr.spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.redis</groupId>
<artifactId>testcontainers-redis</artifactId>
<version>2.2.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>

View File

@ -1,94 +0,0 @@
/*
* Copyright 2025 The Dapr Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
limitations under the License.
*/
package io.dapr.springboot.examples;
import io.dapr.spring.workflows.config.EnableDaprWorkflows;
import io.dapr.springboot.examples.workflowapp.OrderStatus;
import io.dapr.springboot.examples.workflowapp.OrderWorkflow;
import io.dapr.springboot.examples.workflowapp.Order;
import io.dapr.workflows.client.DaprWorkflowClient;
import io.dapr.workflows.client.WorkflowInstanceStatus;
import io.dapr.workflows.client.WorkflowRuntimeStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.concurrent.TimeoutException;
@RestController
/*
* Dapr Workflows and Activities need to be registered in the DI container otherwise
* the Dapr runtime does not know this application contains workflows and activities.
* In Spring Boot Applications, the `@EnableDaprWorkflow` annotation takes care of registering
* all workflows and activities components found in the classpath.
*/
@EnableDaprWorkflows
public class WorkflowAppRestController {
private final Logger logger = LoggerFactory.getLogger(WorkflowAppRestController.class);
@Autowired
private DaprWorkflowClient daprWorkflowClient;
private String instanceId;
/**
* The DaprWorkflowClient is the API to manage workflows.
* Here it is used to schedule a new workflow instance.
*
* @return the instanceId of the ExternalEventsWorkflow execution
*/
@PostMapping("start")
public String basic(@RequestBody Order order) throws TimeoutException {
logger.info("Received order: {}", order);
instanceId = daprWorkflowClient.scheduleNewWorkflow(OrderWorkflow.class, order);
return instanceId;
}
/**
* Obtain the status of the workflow
*
* @return the status of the ExternalEventsWorkflow instance
*/
@GetMapping("status")
public String status() throws TimeoutException {
if(instanceId != null && !instanceId.isEmpty()) {
WorkflowInstanceStatus instanceState = daprWorkflowClient.getInstanceState(instanceId, true);
if (instanceState != null) {
if (instanceState.getRuntimeStatus().equals(WorkflowRuntimeStatus.COMPLETED)) {
var output = instanceState.readOutputAs(String.class);
if (output != null && !output.isEmpty()) {
return "Workflow Instance (" + instanceId + ") Status: " + instanceState.getRuntimeStatus().name() + "\n"
+ "Output: " + output;
}
}
return "Workflow Instance (" + instanceId + ") Status: " + instanceState.getRuntimeStatus().name();
}
}
return "N/A";
}
/**
* Raise a workflow event
*/
@PostMapping("event")
public void raiseEvent(@RequestBody OrderStatus approvalStatus) {
daprWorkflowClient.raiseEvent(instanceId, "approval-event", approvalStatus);
}
}

View File

@ -1,4 +0,0 @@
package io.dapr.springboot.examples.workflowapp;
public record RegisterShipmentResult() {
}

View File

@ -0,0 +1,19 @@
package io.dapr.springboot.workflowapp;
import io.dapr.client.DaprClient;
import io.dapr.springboot.workflowapp.model.ProductInventory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class InventoryManagementService {
@Autowired
private DaprClient daprClient;
public void createDefaultInventory(){
ProductInventory productInventory = new ProductInventory("RBD001", 50);
daprClient.saveState("inventory", productInventory.productId(), productInventory).block();
}
}

View File

@ -11,7 +11,7 @@
limitations under the License.
*/
package io.dapr.springboot.examples;
package io.dapr.springboot.workflowapp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

View File

@ -11,7 +11,7 @@
limitations under the License.
*/
package io.dapr.springboot.examples;
package io.dapr.springboot.workflowapp;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.web.client.RestTemplateBuilder;
@ -31,4 +31,6 @@ public class WorkflowAppConfiguration {
public ObjectMapper mapper() {
return new ObjectMapper();
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright 2025 The Dapr Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
limitations under the License.
*/
package io.dapr.springboot.workflowapp;
import io.dapr.client.DaprClient;
import io.dapr.client.domain.State;
import io.dapr.spring.workflows.config.EnableDaprWorkflows;
import io.dapr.springboot.workflowapp.model.Order;
import io.dapr.springboot.workflowapp.model.ProductInventory;
import io.dapr.springboot.workflowapp.model.ShipmentRegistrationStatus;
import io.dapr.springboot.workflowapp.workflow.OrderWorkflow;
import io.dapr.workflows.client.DaprWorkflowClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.concurrent.TimeoutException;
@RestController
/*
* Dapr Workflows and Activities need to be registered in the DI container otherwise
* the Dapr runtime does not know this application contains workflows and activities.
* In Spring Boot Applications, the `@EnableDaprWorkflow` annotation takes care of registering
* all workflows and activities components found in the classpath.
*/
@EnableDaprWorkflows
public class WorkflowAppRestController {
private final Logger logger = LoggerFactory.getLogger(WorkflowAppRestController.class);
public static final String DAPR_INVENTORY_COMPONENT = "inventory";
public static final String DAPR_PUBSUB_COMPONENT = "shippingpubsub";
public static final String DAPR_PUBSUB_REGISTRATION_TOPIC = "shipment-registration-events";
public static final String SHIPMENT_REGISTERED_EVENT = "shipment-registered-event";
@Autowired
private DaprWorkflowClient daprWorkflowClient;
@Autowired
private DaprClient daprClient;
private InventoryManagementService inventoryManagementService;
public WorkflowAppRestController(InventoryManagementService inventoryManagementService) {
this.inventoryManagementService = inventoryManagementService;
inventoryManagementService.createDefaultInventory();
}
/**
* The DaprWorkflowClient is the API to manage workflows.
* Here it is used to schedule a new workflow instance.
*
* @return the instanceId of the OrderWorkflow execution
*/
@PostMapping("start")
public String basic(@RequestBody Order order) throws TimeoutException {
logger.info("Received order: {}", order);
return daprWorkflowClient.scheduleNewWorkflow(OrderWorkflow.class, order);
}
/**
* This endpoint handles messages that are published to the shipment-registration-confirmed-events topic.
* It uses the workflow management API to raise an event to the workflow instance to indicate that the
* shipment has been registered by the ShippingApp.
* @param status ShipmentRegistrationStatus
*/
@PostMapping("shipmentRegistered")
public void shipmentRegistered(@RequestBody ShipmentRegistrationStatus status){
logger.info("Shipment registered for order {}", status);
daprWorkflowClient.raiseEvent(status.orderId(), SHIPMENT_REGISTERED_EVENT, status);
}
/**
* This endpoint is a manual helper method to restock the inventory.
* @param productInventory ProductInventory to restock
*/
@PostMapping("/inventory/restock")
public void shipmentRegistered(@RequestBody ProductInventory productInventory){
daprClient.saveState(DAPR_INVENTORY_COMPONENT, productInventory.productId(), productInventory).block();
}
/**
* This endpoint is a manual helper method to check the inventory.
* @param productId product Id that
*/
@PostMapping("/inventory/{productId}")
public ResponseEntity<ProductInventory> checkInventory(@PathVariable("productId") String productId){
State<ProductInventory> productInventoryState = daprClient.getState(DAPR_INVENTORY_COMPONENT, productId, ProductInventory.class).block();
if(productInventoryState != null) {
ProductInventory productInventory = productInventoryState.getValue();
if (productInventory == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(productInventory);
}else{
return ResponseEntity.notFound().build();
}
}
}

View File

@ -1,3 +1,3 @@
package io.dapr.springboot.examples.workflowapp;
package io.dapr.springboot.workflowapp.model;
public record ActivityResult(boolean isSuccess, String message) { }

View File

@ -1,3 +1,3 @@
package io.dapr.springboot.examples.workflowapp;
package io.dapr.springboot.workflowapp.model;
public record CustomerInfo(String id, String country) {}

View File

@ -1,3 +1,3 @@
package io.dapr.springboot.examples.workflowapp;
package io.dapr.springboot.workflowapp.model;
public record Order(String id, OrderItem orderItem, CustomerInfo customerInfo){}

View File

@ -1,3 +1,3 @@
package io.dapr.springboot.examples.workflowapp;
package io.dapr.springboot.workflowapp.model;
public record OrderStatus(boolean isSuccess, String message){}

View File

@ -0,0 +1,4 @@
package io.dapr.springboot.workflowapp.model;
public record PaymentResult(boolean isSuccess) {
}

View File

@ -1,3 +1,3 @@
package io.dapr.springboot.examples.workflowapp;
package io.dapr.springboot.workflowapp.model;
public record ProductInventory(String productId, int quantity) { }

View File

@ -0,0 +1,4 @@
package io.dapr.springboot.workflowapp.model;
public record RegisterShipmentResult(boolean isSuccess) {
}

View File

@ -0,0 +1,4 @@
package io.dapr.springboot.workflowapp.model;
public record ReimburseCustomerResult(boolean isSuccess) {
}

View File

@ -1,4 +1,4 @@
package io.dapr.springboot.examples.workflowapp;
package io.dapr.springboot.workflowapp.model;
public record ShipmentRegistrationStatus(String orderId, boolean isSuccess, String message) { }

View File

@ -1,3 +1,3 @@
package io.dapr.springboot.examples.workflowapp;
package io.dapr.springboot.workflowapp.model;
public record UpdateInventoryResult(boolean isSuccess, String message) { }

View File

@ -11,11 +11,12 @@
limitations under the License.
*/
package io.dapr.springboot.examples.workflowapp;
package io.dapr.springboot.workflowapp.workflow;
import io.dapr.durabletask.Task;
import io.dapr.durabletask.TaskCanceledException;
import io.dapr.springboot.examples.workflowapp.activities.*;
import io.dapr.springboot.workflowapp.model.*;
import io.dapr.springboot.workflowapp.workflow.activities.*;
import io.dapr.workflows.Workflow;
import io.dapr.workflows.WorkflowStub;
import org.springframework.stereotype.Component;
@ -43,16 +44,17 @@ public class OrderWorkflow implements Workflow {
if( tasksResult.stream().anyMatch(r -> !r.isSuccess())){
var message = "Order processing failed. Reason: " + tasksResult.get(0).message();
ctx.complete(new OrderStatus(false, message);
ctx.complete(new OrderStatus(false, message));
return;
}
// Two activities are called in sequence (chaining pattern) where the UpdateInventory
// activity is dependent on the result of the ProcessPayment activity:
var paymentResult = ctx.callActivity(ProcessPaymentActivity.class.getName(), order.orderItem(), ActivityResult.class).await();
var paymentResult = ctx.callActivity(ProcessPaymentActivity.class.getName(), order.orderItem(), PaymentResult.class).await();
if(paymentResult.isSuccess()){
var inventoryResult = ctx.callActivity(ProcessPaymentActivity.class.getName(), order.orderItem(), UpdateInventoryResult.class).await();
ctx.callActivity(UpdateInventoryActivity.class.getName(), order.orderItem(), UpdateInventoryResult.class).await();
}
ShipmentRegistrationStatus shipmentRegistrationStatus = null;

View File

@ -11,14 +11,12 @@
limitations under the License.
*/
package io.dapr.springboot.examples.workflowapp.activities;
package io.dapr.springboot.workflowapp.workflow.activities;
import io.dapr.client.DaprClient;
import io.dapr.client.domain.State;
import io.dapr.springboot.examples.workflowapp.ActivityResult;
import io.dapr.springboot.examples.workflowapp.Order;
import io.dapr.springboot.examples.workflowapp.OrderItem;
import io.dapr.springboot.examples.workflowapp.ProductInventory;
import io.dapr.springboot.workflowapp.model.ActivityResult;
import io.dapr.springboot.workflowapp.model.OrderItem;
import io.dapr.springboot.workflowapp.model.ProductInventory;
import io.dapr.workflows.WorkflowActivity;
import io.dapr.workflows.WorkflowActivityContext;
import org.slf4j.Logger;
@ -38,14 +36,15 @@ public class CheckInventoryActivity implements WorkflowActivity {
var orderItem = ctx.getInput(OrderItem.class);
logger.info("{} : Received input: {}", ctx.getName(), orderItem);
var productInventory = daprClient.getState("inventory", orderItem.productId(), ProductInventory.class).block();
var productInventoryState = daprClient.getState("inventory", orderItem.productId(), ProductInventory.class).block();
assert productInventoryState != null;
ProductInventory productInventory = productInventoryState.getValue();
if (productInventory == null)
{
return new ActivityResult(false, "");
}
var isAvailable = productInventory.getValue().quantity() >= orderItem.quantity();
var isAvailable = productInventory.quantity() >= orderItem.quantity();
return new ActivityResult(isAvailable, "");
}
}

View File

@ -11,9 +11,10 @@
limitations under the License.
*/
package io.dapr.springboot.examples.workflowapp.activities;
package io.dapr.springboot.workflowapp.workflow.activities;
import io.dapr.springboot.examples.workflowapp.Order;
import io.dapr.springboot.workflowapp.model.ActivityResult;
import io.dapr.springboot.workflowapp.model.Order;
import io.dapr.workflows.WorkflowActivity;
import io.dapr.workflows.WorkflowActivityContext;
import org.slf4j.Logger;
@ -36,6 +37,6 @@ public class CheckShippingDestinationActivity implements WorkflowActivity {
var order = ctx.getInput(Order.class);
// Imagine the order being processed by another system
logger.info("{} : Processed Order: {}", ctx.getName(), order.id());
return true;
return new ActivityResult(true, "");
}
}

View File

@ -11,8 +11,9 @@
limitations under the License.
*/
package io.dapr.springboot.examples.workflowapp.activities;
package io.dapr.springboot.workflowapp.workflow.activities;
import io.dapr.springboot.workflowapp.model.PaymentResult;
import io.dapr.workflows.WorkflowActivity;
import io.dapr.workflows.WorkflowActivityContext;
import org.slf4j.Logger;
@ -35,6 +36,6 @@ public class ProcessPaymentActivity implements WorkflowActivity {
// Imagine a notification being sent to the user
logger.info("{} : Sending Notification: {}", ctx.getName(), message);
return true;
return new PaymentResult(true);
}
}

View File

@ -11,9 +11,10 @@
limitations under the License.
*/
package io.dapr.springboot.examples.workflowapp.activities;
package io.dapr.springboot.workflowapp.workflow.activities;
import io.dapr.springboot.examples.workflowapp.Order;
import io.dapr.springboot.workflowapp.model.Order;
import io.dapr.springboot.workflowapp.model.RegisterShipmentResult;
import io.dapr.workflows.WorkflowActivity;
import io.dapr.workflows.WorkflowActivityContext;
import org.slf4j.Logger;
@ -27,7 +28,7 @@ public class RegisterShipmentActivity implements WorkflowActivity {
public Object run(WorkflowActivityContext ctx) {
Logger logger = LoggerFactory.getLogger(RegisterShipmentActivity.class);
var order = ctx.getInput(Order.class);
logger.info("{} : Request Approval for Order: {}", ctx.getName(), order.id());
return true;
logger.info("{} : RegisterShipmentActivity for Order: {}", ctx.getName(), order.id());
return new RegisterShipmentResult(true);
}
}

View File

@ -11,9 +11,10 @@
limitations under the License.
*/
package io.dapr.springboot.examples.workflowapp.activities;
package io.dapr.springboot.workflowapp.workflow.activities;
import io.dapr.springboot.examples.workflowapp.Order;
import io.dapr.springboot.workflowapp.model.Order;
import io.dapr.springboot.workflowapp.model.ReimburseCustomerResult;
import io.dapr.workflows.WorkflowActivity;
import io.dapr.workflows.WorkflowActivityContext;
import org.slf4j.Logger;
@ -28,6 +29,6 @@ public class ReimburseCustomerActivity implements WorkflowActivity {
Logger logger = LoggerFactory.getLogger(ReimburseCustomerActivity.class);
var order = ctx.getInput(Order.class);
logger.info("{} : Request Approval for Order: {}", ctx.getName(), order.id());
return true;
return new ReimburseCustomerResult(true);
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright 2023 The Dapr Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
limitations under the License.
*/
package io.dapr.springboot.workflowapp.workflow.activities;
import io.dapr.client.DaprClient;
import io.dapr.client.domain.State;
import io.dapr.springboot.workflowapp.model.ActivityResult;
import io.dapr.springboot.workflowapp.model.OrderItem;
import io.dapr.springboot.workflowapp.model.ProductInventory;
import io.dapr.springboot.workflowapp.model.UpdateInventoryResult;
import io.dapr.workflows.WorkflowActivity;
import io.dapr.workflows.WorkflowActivityContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class UpdateInventoryActivity implements WorkflowActivity {
@Autowired
private DaprClient daprClient;
@Override
public Object run(WorkflowActivityContext ctx) {
Logger logger = LoggerFactory.getLogger(UpdateInventoryActivity.class);
var orderItem = ctx.getInput(OrderItem.class);
logger.info("{} : Received input: {}", ctx.getName(), orderItem);
State<ProductInventory> inventory = daprClient.getState("inventory", orderItem.productId(), ProductInventory.class).block();
assert inventory != null;
ProductInventory productInventory = inventory.getValue();
if(productInventory == null){
return new UpdateInventoryResult(false, "Product not in inventory: " + orderItem.productName());
}
if (productInventory.quantity() < orderItem.quantity()){
return new UpdateInventoryResult(false, "Inventory not sufficient for: " + orderItem.productName());
}
var updateProductInventory = new ProductInventory(productInventory.productId(), productInventory.quantity() - orderItem.quantity());
daprClient.saveState("inventory", orderItem.productId(), updateProductInventory).block();
return new UpdateInventoryResult(true, "Inventory updated for: " + orderItem.productName());
}
}

View File

@ -1 +1,2 @@
spring.application.name=workflow-app
dapr.client.state=

View File

@ -1,43 +0,0 @@
/*
* Copyright 2025 The Dapr Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
limitations under the License.
*/
package io.dapr.springboot.examples;
import io.dapr.testcontainers.Component;
import io.dapr.testcontainers.DaprContainer;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;
import java.util.Collections;
import static io.dapr.testcontainers.DaprContainerConstants.DAPR_RUNTIME_IMAGE_TAG;
@TestConfiguration(proxyBeanMethods = false)
public class DaprTestContainersConfig {
@Bean
@ServiceConnection
public DaprContainer daprContainer() {
return new DaprContainer(DAPR_RUNTIME_IMAGE_TAG)
.withAppName("external-system-interactions")
.withComponent(new Component("kvstore", "state.in-memory", "v1", Collections.singletonMap("actorStateStore", String.valueOf(true))))
.withAppPort(8080)
.withAppHealthCheckPath("/actuator/health")
.withAppChannelAddress("host.testcontainers.internal");
}
}

View File

@ -0,0 +1,106 @@
/*
* Copyright 2025 The Dapr Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
limitations under the License.
*/
package io.dapr.springboot.workflowapp;
import com.redis.testcontainers.RedisContainer;
import io.dapr.testcontainers.Component;
import io.dapr.testcontainers.DaprContainer;
import io.dapr.testcontainers.DaprLogLevel;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.testcontainers.DockerClientFactory;
import org.testcontainers.containers.Network;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static io.dapr.testcontainers.DaprContainerConstants.DAPR_RUNTIME_IMAGE_TAG;
@TestConfiguration(proxyBeanMethods = false)
public class DaprTestContainersConfig {
@Bean
public RedisContainer redisContainer(Network daprNetwork){
return new RedisContainer(RedisContainer.DEFAULT_IMAGE_NAME)
.withNetwork(daprNetwork)
.withNetworkAliases("redis");
}
@Bean
@ServiceConnection
public DaprContainer daprContainer(Network daprNetwork, RedisContainer redisContainer) {
Map<String, String> redisProps = new HashMap<>();
redisProps.put("actorStateStore", String.valueOf(true));
redisProps.put("redisHost", "redis:6379");
redisProps.put("redisPassword", "");
return new DaprContainer(DAPR_RUNTIME_IMAGE_TAG)
.withAppName("workflow-app")
.withComponent(new Component("inventory", "state.redis", "v1", redisProps))
.withComponent(new Component("pubsub", "pubsub.redis", "v1", redisProps))
.withAppPort(8080)
.withNetwork(daprNetwork)
.withAppHealthCheckPath("/actuator/health")
.withAppChannelAddress("host.testcontainers.internal")
//.withDaprLogLevel(DaprLogLevel.DEBUG)
//.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
.dependsOn(redisContainer);
}
@Bean
public Network getDaprNetwork(Environment env) {
boolean reuse = env.getProperty("reuse", Boolean.class, false);
if (reuse) {
Network defaultDaprNetwork = new Network() {
@Override
public String getId() {
return "dapr-network";
}
@Override
public void close() {
}
@Override
public Statement apply(Statement base, Description description) {
return null;
}
};
List<com.github.dockerjava.api.model.Network> networks = DockerClientFactory.instance().client().listNetworksCmd()
.withNameFilter("dapr-network").exec();
if (networks.isEmpty()) {
Network.builder().createNetworkCmdModifier(cmd -> cmd.withName("dapr-network")).build().getId();
return defaultDaprNetwork;
} else {
return defaultDaprNetwork;
}
} else {
return Network.newNetwork();
}
}
}

View File

@ -11,7 +11,7 @@
limitations under the License.
*/
package io.dapr.springboot.examples;
package io.dapr.springboot.workflowapp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

View File

@ -11,12 +11,12 @@
limitations under the License.
*/
package io.dapr.springboot.examples;
package io.dapr.springboot.workflowapp;
import io.dapr.client.DaprClient;
import io.dapr.springboot.DaprAutoConfiguration;
import io.dapr.springboot.examples.workflowapp.OrderStatus;
import io.dapr.springboot.examples.workflowapp.Order;
import io.dapr.springboot.workflowapp.model.OrderStatus;
import io.dapr.springboot.workflowapp.model.Order;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import org.junit.jupiter.api.BeforeEach;
@ -30,7 +30,7 @@ import org.hamcrest.TypeSafeMatcher;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import static io.dapr.springboot.examples.StringMatchesUUIDPattern.matchesThePatternOfAUUID;
import static io.dapr.springboot.workflowapp.StringMatchesUUIDPattern.matchesThePatternOfAUUID;
import static io.restassured.RestAssured.given;
import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -52,48 +52,9 @@ class WorkflowAppTests {
@Test
void testExternalEventsWorkflow() throws InterruptedException {
var order = new Order("123", "Rubber ducks", 100, 500);
given().contentType(ContentType.JSON)
.body(order)
.when()
.post("/start")
.then()
.statusCode(200).body(matchesThePatternOfAUUID());
void testWorkflow() throws InterruptedException {
String status = given().contentType(ContentType.JSON)
.when()
.get("/status")
.then()
.statusCode(200).extract().asString();
assertTrue(status.contains("RUNNING"));
OrderStatus approvalStatus = new OrderStatus("123", true);
given().contentType(ContentType.JSON)
.body(approvalStatus)
.when()
.post("/event")
.then()
.statusCode(200);
// Wait for the workflow instance to complete
await().atMost(Duration.ofSeconds(2))
.pollDelay(500, TimeUnit.MILLISECONDS)
.pollInterval(500, TimeUnit.MILLISECONDS)
.until(() -> {
var completedStatus = given().contentType(ContentType.JSON)
.when()
.get("/status")
.then()
.statusCode(200).extract().asString();
assertTrue(completedStatus.contains("COMPLETED"));
assertTrue(completedStatus.contains("has been approved"));
return true;
});
}