Skip to content

Commit 03c5e1a

Browse files
authored
Merge branch 'master' into dependabot/github_actions/actions/setup-go-6
2 parents c517d33 + 50aa661 commit 03c5e1a

File tree

75 files changed

+1743
-51
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+1743
-51
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
distribution: 'temurin'
3131
java-version: ${{ env.JDK_VER }}
3232
- name: Run tests
33-
run: ./mvnw clean install -B -q
33+
run: ./mvnw clean install -B -q -DskipITs=true
3434
- name: Codecov
3535
uses: codecov/[email protected]
3636
- name: Upload test report for sdk

.github/workflows/validate.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@ jobs:
123123
mm.py README.md
124124
env:
125125
DOCKER_HOST: ${{steps.setup_docker.outputs.sock}}
126-
- name: Validate Spring Boot Workflow examples
127-
working-directory: ./spring-boot-examples/workflows
126+
- name: Validate Spring Boot Workflow Patterns examples
127+
working-directory: ./spring-boot-examples/workflows/patterns
128128
run: |
129129
mm.py README.md
130130
env:

pom.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
<spotbugs.fail>true</spotbugs.fail>
4343
<spotbugs.exclude.filter.file>../spotbugs-exclude.xml</spotbugs.exclude.filter.file>
4444
<argLine>--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED</argLine>
45-
<failsafe.version>3.2.2</failsafe.version>
45+
<failsafe.version>3.5.3</failsafe.version>
4646
<surefire.version>3.2.2</surefire.version>
4747
<junit-bom.version>5.11.4</junit-bom.version>
4848
<snakeyaml.version>2.0</snakeyaml.version>
@@ -665,6 +665,7 @@
665665
<id>integration-tests</id>
666666
<modules>
667667
<module>sdk-tests</module>
668+
<module>spring-boot-examples</module>
668669
</modules>
669670
<build>
670671
<plugins>

spring-boot-examples/consumer-app/src/test/java/io/dapr/springboot/examples/consumer/ConsumerAppTests.java renamed to spring-boot-examples/consumer-app/src/test/java/io/dapr/springboot/examples/consumer/ConsumerAppIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
@SpringBootTest(classes = {TestConsumerApplication.class, DaprTestContainersConfig.class,
3838
ConsumerAppTestConfiguration.class, DaprAutoConfiguration.class},
3939
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
40-
class ConsumerAppTests {
40+
class ConsumerAppIT {
4141

4242
private static final String SUBSCRIPTION_MESSAGE_PATTERN = ".*app is subscribed to the following topics.*";
4343

spring-boot-examples/pom.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,23 @@
5252
<skip>true</skip>
5353
</configuration>
5454
</plugin>
55+
<plugin>
56+
<groupId>org.apache.maven.plugins</groupId>
57+
<artifactId>maven-failsafe-plugin</artifactId>
58+
<configuration>
59+
<classesDirectory>${project.build.outputDirectory}</classesDirectory>
60+
</configuration>
61+
</plugin>
62+
<plugin>
63+
<groupId>org.jacoco</groupId>
64+
<artifactId>jacoco-maven-plugin</artifactId>
65+
<configuration>
66+
<excludes>
67+
<!-- Exclude full package from test coverage -->
68+
<exclude>**/*io/dapr/springboot/examples/**</exclude>
69+
</excludes>
70+
</configuration>
71+
</plugin>
5572
</plugins>
5673
</build>
5774
</project>

spring-boot-examples/producer-app/src/test/java/io/dapr/springboot/examples/producer/ProducerAppTests.java renamed to spring-boot-examples/producer-app/src/test/java/io/dapr/springboot/examples/producer/ProducerAppIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
DaprAutoConfiguration.class, CustomerWorkflow.class, CustomerFollowupActivity.class,
4141
RegisterCustomerActivity.class, CustomerStore.class},
4242
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
43-
class ProducerAppTests {
43+
class ProducerAppIT {
4444

4545
private static final String SUBSCRIPTION_MESSAGE_PATTERN = ".*app is subscribed to the following topics.*";
4646

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Multi App workflow Example
2+
3+
This example demonstrates how you can create distributed workflows where the orchestrator doesn't host the workflow activities.
4+
5+
For more documentation about how Multi App Workflows work [check the official documentation here](https://v1-16.docs.dapr.io/developing-applications/building-blocks/workflow/workflow-multi-app/).
6+
7+
This example is composed by three Spring Boot applications:
8+
- `orchestrator`: The `orchestrator` app contains the Dapr Workflow definition and expose REST endpoints to create and raise events against workflow instances.
9+
- `worker-one`: The `worker-one` app contains the `RegisterCustomerActivity` definition, which will be called by the `orchestrator` app.
10+
- `worker-two`: The `worker-two` app contains the `CustomerFollowupActivity` definition, which will be called by the `orchestrator` app.
11+
12+
To start the applications you need to run the following commands on separate terminals, starting from the `multi-app` directory.
13+
To start the `orchestrator` app run:
14+
```bash
15+
cd orchestrator/
16+
mvn -Dspring-boot.run.arguments="--reuse=true" clean spring-boot:test-run
17+
```
18+
19+
The `orchestrator` application will run on port `8080`.
20+
21+
On a separate terminal, to start the `worker-one` app run:
22+
```bash
23+
cd worker-one/
24+
mvn -Dspring-boot.run.arguments="--reuse=true" clean spring-boot:test-run
25+
```
26+
27+
The `worker-one` application will run on port `8081`.
28+
29+
On a separate terminal, to start the `worker-two` app run:
30+
```bash
31+
cd worker-two/
32+
mvn -Dspring-boot.run.arguments="--reuse=true" clean spring-boot:test-run
33+
```
34+
35+
The `worker-two` application will run on port `8082`.
36+
37+
You can create new workflow instances of the `CustomerWorkflow` by calling the `/customers` endpoint of the `orchestrator` application.
38+
39+
```bash
40+
curl -X POST localhost:8080/customers -H 'Content-Type: application/json' -d '{ "customerName": "salaboy" }'
41+
```
42+
43+
The workflow definition [`CustomerWorkflow`](orchstrator/src/main/java/io/dapr/springboot/examples/orchestrator/CustomerWorkflow.java) that you can find inside the `orchestrator` app,
44+
performs the following orchestration when a new workflow instance is created:
45+
46+
- Call the `RegisterCustomerActivity` activity which can be found inside the `worker-one` application.
47+
- You can find in the workflow definition the configuration to make reference to an Activity that is hosted by a different Dapr application.
48+
```java
49+
customer = ctx.callActivity("io.dapr.springboot.examples.workerone.RegisterCustomerActivity",
50+
customer,
51+
new WorkflowTaskOptions("worker-one"),
52+
Customer.class).
53+
await();
54+
```
55+
- Wait for an external event of type `CustomerReachOut` with a timeout of 5 minutes:
56+
```java
57+
ctx.waitForExternalEvent("CustomerReachOut", Duration.ofMinutes(5), Customer.class).await();
58+
```
59+
- You can check the status of the workflow for a given customer by sending the following request:
60+
```shell
61+
curl -X POST localhost:8080/customers/status -H 'Content-Type: application/json' -d '{ "customerName": "salaboy" }'
62+
```
63+
- You can call the following endpoint on the `orchestrator` app to raise the external event:
64+
```shell
65+
curl -X POST localhost:8080/customers/followup -H 'Content-Type: application/json' -d '{ "customerName": "salaboy" }'
66+
```
67+
- When the event is received, the workflow move forward to the last activity called `CustomerFollowUpActivity`, that can be found on the `worker-two` app.
68+
```java
69+
customer = ctx.callActivity("io.dapr.springboot.examples.workertwo.CustomerFollowupActivity",
70+
customer,
71+
new WorkflowTaskOptions("worker-two"),
72+
Customer.class).
73+
await();
74+
```
75+
- The workflow completes by handing out the final version of the `Customer` object that has been modified the workflow activities. You can retrieve the `Customer` payload
76+
by running the following command:
77+
```shell
78+
curl -X POST localhost:8080/customers/output -H 'Content-Type: application/json' -d '{ "customerName": "salaboy" }'
79+
```
80+
81+
## Testing Multi App Workflows
82+
83+
Testing becomes a complex task when you are dealing with multiple Spring Boot applications. For testing this workflow,
84+
we rely on [Testcontainers](https://testcontainers.com) to create the entire setup which enable us to run the workflow end to end.
85+
86+
You can find the end-to-end test in the [`OrchestratorAppIT.java`](orchestrator/src/test/java/io/dapr/springboot/examples/orchestrator/OrchestratorAppIT.java) class inside the `orchestrator` application.
87+
This test interact with the application REST endpoints to validate their correct execution.
88+
89+
But the magic behind the test can be located in the [`DaprTestContainersConfig.class`](orchestrator/src/test/java/io/dapr/springboot/examples/orchestrator/DaprTestContainersConfig.java) which defines the configuration for
90+
all the Dapr containers and the `worker-one` and `worker-two` applications. Check this class to gain a deeper understand how to configure
91+
multiple Dapr-enabled applications.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<parent>
7+
<groupId>io.dapr</groupId>
8+
<artifactId>multi-app</artifactId>
9+
<version>1.17.0-SNAPSHOT</version>
10+
</parent>
11+
12+
<artifactId>orchestrator</artifactId>
13+
<name>orchestrator</name>
14+
<description>Spring Boot, Testcontainers and Dapr Integration Examples :: Orchestrator App</description>
15+
16+
<dependencies>
17+
<dependency>
18+
<groupId>org.springframework.boot</groupId>
19+
<artifactId>spring-boot-starter-actuator</artifactId>
20+
</dependency>
21+
<dependency>
22+
<groupId>org.springframework.boot</groupId>
23+
<artifactId>spring-boot-starter-web</artifactId>
24+
</dependency>
25+
<dependency>
26+
<groupId>org.springframework.boot</groupId>
27+
<artifactId>spring-boot-starter-test</artifactId>
28+
</dependency>
29+
<dependency>
30+
<groupId>io.dapr.spring</groupId>
31+
<artifactId>dapr-spring-boot-starter</artifactId>
32+
</dependency>
33+
<dependency>
34+
<groupId>io.dapr.spring</groupId>
35+
<artifactId>dapr-spring-boot-starter-test</artifactId>
36+
<scope>test</scope>
37+
</dependency>
38+
<dependency>
39+
<groupId>com.redis</groupId>
40+
<artifactId>testcontainers-redis</artifactId>
41+
<version>2.2.2</version>
42+
<scope>test</scope>
43+
</dependency>
44+
<dependency>
45+
<groupId>io.rest-assured</groupId>
46+
<artifactId>rest-assured</artifactId>
47+
<scope>test</scope>
48+
</dependency>
49+
</dependencies>
50+
51+
<build>
52+
<plugins>
53+
<plugin>
54+
<groupId>org.springframework.boot</groupId>
55+
<artifactId>spring-boot-maven-plugin</artifactId>
56+
<executions>
57+
<execution>
58+
<goals>
59+
<goal>repackage</goal>
60+
</goals>
61+
</execution>
62+
</executions>
63+
</plugin>
64+
<plugin>
65+
<groupId>org.apache.maven.plugins</groupId>
66+
<artifactId>maven-site-plugin</artifactId>
67+
<configuration>
68+
<skip>true</skip>
69+
</configuration>
70+
</plugin>
71+
<plugin>
72+
<groupId>org.apache.maven.plugins</groupId>
73+
<artifactId>maven-checkstyle-plugin</artifactId>
74+
<configuration>
75+
<!-- Skip checkstyle for auto-generated code -->
76+
<skip>true</skip>
77+
</configuration>
78+
</plugin>
79+
</plugins>
80+
</build>
81+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright 2025 The Dapr Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package io.dapr.springboot.examples.orchestrator;
15+
16+
public class Customer {
17+
private String customerName;
18+
private String workflowId;
19+
private boolean inCustomerDB = false;
20+
private boolean followUp = false;
21+
22+
public boolean isFollowUp() {
23+
return followUp;
24+
}
25+
26+
public void setFollowUp(boolean followUp) {
27+
this.followUp = followUp;
28+
}
29+
30+
public boolean isInCustomerDB() {
31+
return inCustomerDB;
32+
}
33+
34+
public void setInCustomerDB(boolean inCustomerDB) {
35+
this.inCustomerDB = inCustomerDB;
36+
}
37+
38+
public String getWorkflowId() {
39+
return workflowId;
40+
}
41+
42+
public void setWorkflowId(String workflowId) {
43+
this.workflowId = workflowId;
44+
}
45+
46+
public String getCustomerName() {
47+
return customerName;
48+
}
49+
50+
public void setCustomerName(String customerName) {
51+
this.customerName = customerName;
52+
}
53+
54+
@Override
55+
public String toString() {
56+
return "Customer [customerName=" + customerName + ", workflowId=" + workflowId + ", inCustomerDB="
57+
+ inCustomerDB + ", followUp=" + followUp + "]";
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2025 The Dapr Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package io.dapr.springboot.examples.orchestrator;
15+
16+
import io.dapr.durabletask.TaskCanceledException;
17+
import io.dapr.durabletask.TaskFailedException;
18+
import io.dapr.workflows.Workflow;
19+
import io.dapr.workflows.WorkflowStub;
20+
import io.dapr.workflows.WorkflowTaskOptions;
21+
import org.springframework.stereotype.Component;
22+
23+
import java.time.Duration;
24+
25+
@Component
26+
public class CustomerWorkflow implements Workflow {
27+
28+
@Override
29+
public WorkflowStub create() {
30+
return ctx -> {
31+
String instanceId = ctx.getInstanceId();
32+
Customer customer = ctx.getInput(Customer.class);
33+
customer.setWorkflowId(instanceId);
34+
ctx.getLogger().info("Let's register the customer: {}", customer.getCustomerName());
35+
36+
customer = ctx.callActivity("io.dapr.springboot.examples.workerone.RegisterCustomerActivity", customer,
37+
new WorkflowTaskOptions("worker-one"), Customer.class).await();
38+
39+
ctx.getLogger().info("Let's wait for the customer: {} to request a follow up.", customer.getCustomerName());
40+
ctx.waitForExternalEvent("CustomerReachOut", Duration.ofMinutes(5), Customer.class).await();
41+
42+
ctx.getLogger().info("Let's book a follow up for the customer: {}", customer.getCustomerName());
43+
customer = ctx.callActivity("io.dapr.springboot.examples.workertwo.CustomerFollowupActivity",
44+
customer, new WorkflowTaskOptions("worker-two"), Customer.class).await();
45+
46+
ctx.getLogger().info("Congratulations the customer: {} is happy!", customer.getCustomerName());
47+
48+
ctx.getLogger().info("Final customer: {} ", customer);
49+
ctx.complete(customer);
50+
};
51+
}
52+
}

0 commit comments

Comments
 (0)