mirror of https://github.com/dapr/java-sdk.git
Spring boot workflow patterns examples with mechanical markdown and tests (#1377)
* Feat Add TLS & mTLS support for gRPC with root CA and insecure mode (#1361) * feat: Support for GRPC ssl Signed-off-by: Javier Aliaga <javier@diagrid.io> * add tests Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * fix CI Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * add back else if Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * channel cleanup Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * add root ca support Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * checkstyles Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * add insecure Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * fix checkstyles Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * use InsecureTrustManagerFactory Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * fix test Signed-off-by: Cassandra Coyle <cassie@diagrid.io> --------- Signed-off-by: Javier Aliaga <javier@diagrid.io> Signed-off-by: Cassandra Coyle <cassie@diagrid.io> Co-authored-by: Javier Aliaga <javier@diagrid.io> Signed-off-by: salaboy <Salaboy@gmail.com> * spring boot workflow patterns initial version Signed-off-by: salaboy <Salaboy@gmail.com> * adding README for workflows Signed-off-by: salaboy <Salaboy@gmail.com> * adding child example Signed-off-by: salaboy <Salaboy@gmail.com> * updating examples to work with markdown tests Signed-off-by: salaboy <Salaboy@gmail.com> * running mechanical markdown for workflow examples Signed-off-by: salaboy <Salaboy@gmail.com> * Fix the issue with retries not happening correctly for Activities and Workflows (#1343) * Add coverage for some properties (#1297) Signed-off-by: sirivarma <siri.varma@outlook.com> * Make the DAPR version being used consistent across all tests (#1299) Signed-off-by: sirivarma <siri.varma@outlook.com> * Separate Dapr constants from IT container constants (#1315) Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> Co-authored-by: Artur Ciocanu <ciocanu@adobe.com> Signed-off-by: sirivarma <siri.varma@outlook.com> * Use Java Bean for connection details and add more tests (#1317) * Use Java Bean for connection details and add more tests Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Simplify mock setup Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Adding even more tests for test coverage Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> --------- Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> Co-authored-by: Artur Ciocanu <ciocanu@adobe.com> Co-authored-by: Cassie Coyle <cassie@diagrid.io> Signed-off-by: sirivarma <siri.varma@outlook.com> * Update CONTRIBUTING.md Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com> Signed-off-by: sirivarma <siri.varma@outlook.com> * Bump codecov/codecov-action from 5.4.0 to 5.4.2 (#1318) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.4.0 to 5.4.2. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v5.4.0...v5.4.2) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-version: 5.4.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Cassie Coyle <cassie@diagrid.io> Co-authored-by: Dapr Bot <56698301+dapr-bot@users.noreply.github.com> Signed-off-by: sirivarma <siri.varma@outlook.com> * Fix URL building logic (#1320) * Fix URL building logic Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Add test for query params Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Fix the assertion in the test Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Adjust the tests Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Remove uneeded changes from IT test Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Revert some unintended changes Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Simplify the testing a little bit Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Adjust the test to use ServerRequest Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Test removing things from method invoke controller Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Add query param encoding test Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Revert some unintended changes Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> * Some tiny styles Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> --------- Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> Co-authored-by: Artur Ciocanu <ciocanu@adobe.com> Signed-off-by: sirivarma <siri.varma@outlook.com> * Generate updated javadocs for 1.14.1 Signed-off-by: Dapr Bot <daprweb@microsoft.com> Signed-off-by: sirivarma <siri.varma@outlook.com> * Add Conversation AI to Java SDK (#1235) * Conversation first commit Signed-off-by: Siri Varma Vegiraju <svegiraju@microsoft.com> Signed-off-by: sirivarma <siri.varma@outlook.com> Signed-off-by: siri-varma <siri.varma@outlook.com> * Add unit tests Signed-off-by: sirivarma <siri.varma@outlook.com> Signed-off-by: siri-varma <siri.varma@outlook.com> * change ai to conv Signed-off-by: sirivarma <siri.varma@outlook.com> Signed-off-by: siri-varma <siri.varma@outlook.com> * Move to single module Signed-off-by: sirivarma <siri.varma@outlook.com> Signed-off-by: siri-varma <siri.varma@outlook.com> * Remove module Signed-off-by: sirivarma <siri.varma@outlook.com> Signed-off-by: siri-varma <siri.varma@outlook.com> * Add Integration tests Signed-off-by: siri-varma <siri.varma@outlook.com> * Update sdk-tests/src/test/java/io/dapr/it/testcontainers/DaprConversationIT.java Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com> Signed-off-by: siri-varma <siri.varma@outlook.com> * Fix things Signed-off-by: siri-varma <siri.varma@outlook.com> * Address comments Signed-off-by: siri-varma <siri.varma@outlook.com> * Import tag Signed-off-by: siri-varma <siri.varma@outlook.com> * Address comments Signed-off-by: siri-varma <siri.varma@outlook.com> * Make common config Signed-off-by: siri-varma <siri.varma@outlook.com> * Address comments Signed-off-by: siri-varma <siri.varma@outlook.com> * fix constant Signed-off-by: siri-varma <siri.varma@outlook.com> * fix constant Signed-off-by: siri-varma <siri.varma@outlook.com> * fix constant Signed-off-by: siri-varma <siri.varma@outlook.com> * fix s Signed-off-by: siri-varma <siri.varma@outlook.com> * Fix things Signed-off-by: siri-varma <siri.varma@outlook.com> * Fix things Signed-off-by: siri-varma <siri.varma@outlook.com> * Fix things Signed-off-by: siri-varma <siri.varma@outlook.com> * Make common config Signed-off-by: siri-varma <siri.varma@outlook.com> * Update README.md Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com> * Update README.md Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com> --------- Signed-off-by: Siri Varma Vegiraju <svegiraju@microsoft.com> Signed-off-by: sirivarma <siri.varma@outlook.com> Signed-off-by: siri-varma <siri.varma@outlook.com> Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com> Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Co-authored-by: Cassie Coyle <cassie@diagrid.io> Signed-off-by: sirivarma <siri.varma@outlook.com> * Add docs for usage of Jobs SDK (#1323) * Add doc for jobs Signed-off-by: siri-varma <siri.varma@outlook.com> * Add docs for Jobs Signed-off-by: siri-varma <siri.varma@outlook.com> * Apply suggestions from code review Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com> --------- Signed-off-by: siri-varma <siri.varma@outlook.com> Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com> Co-authored-by: artur-ciocanu <artur.ciocanu@gmail.com> Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Signed-off-by: sirivarma <siri.varma@outlook.com> * Use dapr/durabletask-java (#1336) * microsoft durabletask-java -> dapr durabletask-java Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * update another ref Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * 1.5.2 release Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * fix import order Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * Sdk new changes Signed-off-by: siri-varma <siri.varma@outlook.com> * Refine workflows Signed-off-by: siri-varma <siri.varma@outlook.com> * add ; Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * rm try Signed-off-by: Cassandra Coyle <cassie@diagrid.io> --------- Signed-off-by: Cassandra Coyle <cassie@diagrid.io> Signed-off-by: siri-varma <siri.varma@outlook.com> Co-authored-by: siri-varma <siri.varma@outlook.com> Signed-off-by: sirivarma <siri.varma@outlook.com> * Update master version to 1.16.0-SNAPSHOT Signed-off-by: Dapr Bot <daprweb@microsoft.com> Signed-off-by: sirivarma <siri.varma@outlook.com> * Fix NPE Signed-off-by: siri-varma <siri.varma@outlook.com> Signed-off-by: sirivarma <siri.varma@outlook.com> * Fix NPE Signed-off-by: siri-varma <siri.varma@outlook.com> Signed-off-by: sirivarma <siri.varma@outlook.com> * Fix NPE Signed-off-by: siri-varma <siri.varma@outlook.com> Signed-off-by: sirivarma <siri.varma@outlook.com> * Fix NPE Signed-off-by: siri-varma <siri.varma@outlook.com> Signed-off-by: sirivarma <siri.varma@outlook.com> * Fix NPE Signed-off-by: siri-varma <siri.varma@outlook.com> Signed-off-by: sirivarma <siri.varma@outlook.com> * Fix NPE Signed-off-by: siri-varma <siri.varma@outlook.com> Signed-off-by: sirivarma <siri.varma@outlook.com> * Fix things Signed-off-by: siri-varma <siri.varma@outlook.com> Signed-off-by: sirivarma <siri.varma@outlook.com> * Renaming and exposing connection details (#1341) Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> Co-authored-by: Artur Ciocanu <ciocanu@adobe.com> Signed-off-by: sirivarma <siri.varma@outlook.com> * [Master] Fix Vulnerabilities (#1354) * update okio Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * rm unused dep Signed-off-by: Cassandra Coyle <cassie@diagrid.io> --------- Signed-off-by: Cassandra Coyle <cassie@diagrid.io> Signed-off-by: sirivarma <siri.varma@outlook.com> * Feat Add TLS & mTLS support for gRPC with root CA and insecure mode (#1361) * feat: Support for GRPC ssl Signed-off-by: Javier Aliaga <javier@diagrid.io> * add tests Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * fix CI Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * add back else if Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * channel cleanup Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * add root ca support Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * checkstyles Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * add insecure Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * fix checkstyles Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * use InsecureTrustManagerFactory Signed-off-by: Cassandra Coyle <cassie@diagrid.io> * fix test Signed-off-by: Cassandra Coyle <cassie@diagrid.io> --------- Signed-off-by: Javier Aliaga <javier@diagrid.io> Signed-off-by: Cassandra Coyle <cassie@diagrid.io> Co-authored-by: Javier Aliaga <javier@diagrid.io> Signed-off-by: sirivarma <siri.varma@outlook.com> * Address comments Signed-off-by: siri-varma <siri.varma@outlook.com> Signed-off-by: sirivarma <siri.varma@outlook.com> * Fix things Signed-off-by: siri-varma <siri.varma@outlook.com> Signed-off-by: sirivarma <siri.varma@outlook.com> * Fix things Signed-off-by: siri-varma <siri.varma@outlook.com> Signed-off-by: sirivarma <siri.varma@outlook.com> --------- Signed-off-by: sirivarma <siri.varma@outlook.com> Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com> Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Dapr Bot <daprweb@microsoft.com> Signed-off-by: Siri Varma Vegiraju <svegiraju@microsoft.com> Signed-off-by: siri-varma <siri.varma@outlook.com> Signed-off-by: Cassandra Coyle <cassie@diagrid.io> Signed-off-by: Javier Aliaga <javier@diagrid.io> Co-authored-by: Matheus Cruz <56329339+mcruzdev@users.noreply.github.com> Co-authored-by: artur-ciocanu <artur.ciocanu@gmail.com> Co-authored-by: Artur Ciocanu <ciocanu@adobe.com> Co-authored-by: Cassie Coyle <cassie@diagrid.io> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Dapr Bot <56698301+dapr-bot@users.noreply.github.com> Co-authored-by: Dapr Bot <daprweb@microsoft.com> Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Co-authored-by: Javier Aliaga <javier@diagrid.io> Signed-off-by: salaboy <Salaboy@gmail.com> * 1.5.4 (#1375) Signed-off-by: Cassandra Coyle <cassie@diagrid.io> Signed-off-by: salaboy <Salaboy@gmail.com> * fixing order Id correlation Signed-off-by: salaboy <Salaboy@gmail.com> * fixing waiting time for tests to run Signed-off-by: salaboy <Salaboy@gmail.com> * fixing app name Signed-off-by: salaboy <Salaboy@gmail.com> * adding app name and removing log lines Signed-off-by: salaboy <Salaboy@gmail.com> * Bump codecov/codecov-action from 5.4.2 to 5.4.3 (#1379) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.4.2 to 5.4.3. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v5.4.2...v5.4.3) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-version: 5.4.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Signed-off-by: salaboy <Salaboy@gmail.com> * Bump fossas/fossa-action from 1.6.0 to 1.7.0 (#1380) Bumps [fossas/fossa-action](https://github.com/fossas/fossa-action) from 1.6.0 to 1.7.0. - [Release notes](https://github.com/fossas/fossa-action/releases) - [Commits](https://github.com/fossas/fossa-action/compare/v1.6.0...v1.7.0) --- updated-dependencies: - dependency-name: fossas/fossa-action dependency-version: 1.7.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Signed-off-by: salaboy <Salaboy@gmail.com> * Fix component spec parsing (#1370) * Fix component spec parsing Signed-off-by: Deepak <sdeepaksharma15@gmail.com> * Fix component spec parsing Signed-off-by: Deepak <sdeepaksharma15@gmail.com> * Fix component spec metadata parsing Signed-off-by: Deepak <sdeepaksharma15@gmail.com> * fix checkstyle-error Signed-off-by: Deepak <sdeepaksharma15@gmail.com> --------- Signed-off-by: Deepak <sdeepaksharma15@gmail.com> Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Co-authored-by: artur-ciocanu <artur.ciocanu@gmail.com> Signed-off-by: salaboy <Salaboy@gmail.com> * Update spring-boot-examples/workflows/README.md Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Signed-off-by: salaboy <Salaboy@gmail.com> * fixing comments Signed-off-by: salaboy <Salaboy@gmail.com> * Update body.json Signed-off-by: artur-ciocanu <artur.ciocanu@gmail.com> Signed-off-by: salaboy <Salaboy@gmail.com> * Update FanOutInWorkflow.java Signed-off-by: artur-ciocanu <artur.ciocanu@gmail.com> Signed-off-by: salaboy <Salaboy@gmail.com> * clean up logs for multiple executions, for standalone mode Signed-off-by: salaboy <Salaboy@gmail.com> * Update application.properties Signed-off-by: artur-ciocanu <artur.ciocanu@gmail.com> --------- Signed-off-by: Javier Aliaga <javier@diagrid.io> Signed-off-by: Cassandra Coyle <cassie@diagrid.io> Signed-off-by: salaboy <Salaboy@gmail.com> Signed-off-by: sirivarma <siri.varma@outlook.com> Signed-off-by: Artur Ciocanu <ciocanu@adobe.com> Signed-off-by: Siri Varma Vegiraju <siri.varma@outlook.com> Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Dapr Bot <daprweb@microsoft.com> Signed-off-by: Siri Varma Vegiraju <svegiraju@microsoft.com> Signed-off-by: siri-varma <siri.varma@outlook.com> Signed-off-by: Deepak <sdeepaksharma15@gmail.com> Signed-off-by: artur-ciocanu <artur.ciocanu@gmail.com> Co-authored-by: Cassie Coyle <cassie.i.coyle@gmail.com> Co-authored-by: Javier Aliaga <javier@diagrid.io> Co-authored-by: Siri Varma Vegiraju <siri.varma@outlook.com> Co-authored-by: Matheus Cruz <56329339+mcruzdev@users.noreply.github.com> Co-authored-by: artur-ciocanu <artur.ciocanu@gmail.com> Co-authored-by: Artur Ciocanu <ciocanu@adobe.com> Co-authored-by: Cassie Coyle <cassie@diagrid.io> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Dapr Bot <56698301+dapr-bot@users.noreply.github.com> Co-authored-by: Dapr Bot <daprweb@microsoft.com> Co-authored-by: iddeepak <87892182+iddeepak@users.noreply.github.com>
This commit is contained in:
parent
c3592b446d
commit
1e4bcf9b9f
|
@ -216,4 +216,10 @@ jobs:
|
|||
run: |
|
||||
mm.py README.md
|
||||
env:
|
||||
DOCKER_HOST: ${{steps.setup_docker.outputs.sock}}
|
||||
DOCKER_HOST: ${{steps.setup_docker.outputs.sock}}
|
||||
- name: Validate Spring Boot Workflow examples
|
||||
working-directory: ./spring-boot-examples/workflows
|
||||
run: |
|
||||
mm.py README.md
|
||||
env:
|
||||
DOCKER_HOST: ${{steps.setup_docker.outputs.sock}}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
<modules>
|
||||
<module>producer-app</module>
|
||||
<module>consumer-app</module>
|
||||
<module>workflows</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
|
|
|
@ -0,0 +1,399 @@
|
|||
# Dapr Spring Boot Workflow Examples
|
||||
|
||||
This application allows you to run different [workflow patterns](https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-patterns) including:
|
||||
- Chained Activities
|
||||
- Parent/Child Workflows
|
||||
- Continue workflow by sending External Events
|
||||
- Fan Out/In activities for parallel execution
|
||||
|
||||
## Running these examples from source code
|
||||
|
||||
To run these examples you will need:
|
||||
- Java SDK
|
||||
- Maven
|
||||
- Docker or a container runtime such as Podman
|
||||
|
||||
From the `spring-boot-examples/workflows` directory you can start the service by running the following command:
|
||||
|
||||
<!-- STEP
|
||||
name: Run Demo Workflow Application
|
||||
match_order: none
|
||||
output_match_mode: substring
|
||||
expected_stdout_lines:
|
||||
- 'Started WorkflowPatternsApplication'
|
||||
background: true
|
||||
expected_return_code: 143
|
||||
sleep: 30
|
||||
timeout_seconds: 45
|
||||
-->
|
||||
<!-- Timeout for above service must be more than sleep + timeout for the client-->
|
||||
|
||||
```sh
|
||||
../../mvnw spring-boot:test-run
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
By running the `spring-boot:test-run` goal, the application is loaded using the [test configurations](src/test/java/io/dapr/springboot/examples/wfp/DaprTestContainersConfig.java)
|
||||
configured using [Testcontainers](https://testcontainers.com) to boostrap the [Dapr](https://dapr.io) sidecar and control plane.
|
||||
|
||||
Once the application is running you can trigger the different patterns by sending the following requests:
|
||||
|
||||
### Chaining Activities Workflow example
|
||||
|
||||
The `io.dapr.springboot.examples.wfp.chain.ChainWorkflow` executes three chained activities. For this example the
|
||||
`ToUpperCaseActivity.java` is used to transform to upper case three strings from an array.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
SW((Start
|
||||
Workflow))
|
||||
A1[Activity1]
|
||||
A2[Activity2]
|
||||
A3[Activity3]
|
||||
EW((End
|
||||
Workflow))
|
||||
SW --> A1
|
||||
A1 --> A2
|
||||
A2 --> A3
|
||||
A3 --> EW
|
||||
```
|
||||
|
||||
<!-- STEP
|
||||
name: Start Chain Activities Workflow
|
||||
match_order: none
|
||||
output_match_mode: substring
|
||||
expected_stdout_lines:
|
||||
- 'TOKYO, LONDON, SEATTLE'
|
||||
background: true
|
||||
sleep: 1
|
||||
timeout_seconds: 2
|
||||
-->
|
||||
<!-- Timeout for above service must be more than sleep + timeout for the client-->
|
||||
|
||||
To start the workflow with the three chained activities you can run:
|
||||
|
||||
```sh
|
||||
curl -X POST localhost:8080/wfp/chain -H 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
|
||||
As result from executing the request you should see:
|
||||
|
||||
```bash
|
||||
TOKYO, LONDON, SEATTLE
|
||||
```
|
||||
|
||||
In the application output you should see the workflow activities being executed.
|
||||
|
||||
```bash
|
||||
io.dapr.workflows.WorkflowContext : Starting Workflow: io.dapr.springboot.examples.wfp.chain.ChainWorkflow
|
||||
i.d.s.e.w.WorkflowPatternsRestController : Workflow instance 7625b4af-8c04-408a-93dc-bad753466e43 started
|
||||
i.d.s.e.wfp.chain.ToUpperCaseActivity : Starting Activity: io.dapr.springboot.examples.wfp.chain.ToUpperCaseActivity
|
||||
i.d.s.e.wfp.chain.ToUpperCaseActivity : Message Received from input: Tokyo
|
||||
i.d.s.e.wfp.chain.ToUpperCaseActivity : Sending message to output: TOKYO
|
||||
i.d.s.e.wfp.chain.ToUpperCaseActivity : Starting Activity: io.dapr.springboot.examples.wfp.chain.ToUpperCaseActivity
|
||||
i.d.s.e.wfp.chain.ToUpperCaseActivity : Message Received from input: London
|
||||
i.d.s.e.wfp.chain.ToUpperCaseActivity : Sending message to output: LONDON
|
||||
i.d.s.e.wfp.chain.ToUpperCaseActivity : Starting Activity: io.dapr.springboot.examples.wfp.chain.ToUpperCaseActivity
|
||||
i.d.s.e.wfp.chain.ToUpperCaseActivity : Message Received from input: Seattle
|
||||
i.d.s.e.wfp.chain.ToUpperCaseActivity : Sending message to output: SEATTLE
|
||||
io.dapr.workflows.WorkflowContext : Workflow finished with result: TOKYO, LONDON, SEATTLE
|
||||
```
|
||||
|
||||
### Parent / Child Workflows example
|
||||
|
||||
In this example we start a Parent workflow that calls a child workflow that execute one activity that reverses an input string.
|
||||
|
||||
The Parent workflow looks like this:
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
SW((Start
|
||||
Workflow))
|
||||
subgraph for each word in the input
|
||||
GWL[Call child workflow]
|
||||
end
|
||||
EW((End
|
||||
Workflow))
|
||||
SW --> GWL
|
||||
GWL --> EW
|
||||
```
|
||||
|
||||
The Child workflow looks like this:
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
SW((Start
|
||||
Workflow))
|
||||
A1[Activity1]
|
||||
EW((End
|
||||
Workflow))
|
||||
SW --> A1
|
||||
A1 --> EW
|
||||
```
|
||||
|
||||
To start the parent workflow you can run:
|
||||
|
||||
<!-- STEP
|
||||
name: Start Parent/Child Workflow
|
||||
match_order: none
|
||||
output_match_mode: substring
|
||||
expected_stdout_lines:
|
||||
- '!wolfkroW rpaD olleH'
|
||||
background: true
|
||||
sleep: 1
|
||||
timeout_seconds: 2
|
||||
-->
|
||||
<!-- Timeout for above service must be more than sleep + timeout for the client-->
|
||||
|
||||
To start the workflow with the three chained activities you can run:
|
||||
|
||||
```sh
|
||||
curl -X POST localhost:8080/wfp/child -H 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
|
||||
As result from executing the request you should see:
|
||||
|
||||
```bash
|
||||
!wolfkroW rpaD olleH
|
||||
```
|
||||
|
||||
In the application output you should see the workflow activities being executed.
|
||||
|
||||
```bash
|
||||
io.dapr.workflows.WorkflowContext : Starting Workflow: io.dapr.springboot.examples.wfp.child.ParentWorkflow
|
||||
io.dapr.workflows.WorkflowContext : calling childworkflow with input: Hello Dapr Workflow!
|
||||
i.d.s.e.w.WorkflowPatternsRestController : Workflow instance f3ec9566-a0fc-4d28-8912-3f3ded3cd8a9 started
|
||||
io.dapr.workflows.WorkflowContext : Starting ChildWorkflow: io.dapr.springboot.examples.wfp.child.ChildWorkflow
|
||||
io.dapr.workflows.WorkflowContext : ChildWorkflow received input: Hello Dapr Workflow!
|
||||
io.dapr.workflows.WorkflowContext : ChildWorkflow is calling Activity: io.dapr.springboot.examples.wfp.child.ReverseActivity
|
||||
i.d.s.e.wfp.child.ReverseActivity : Starting Activity: io.dapr.springboot.examples.wfp.child.ReverseActivity
|
||||
i.d.s.e.wfp.child.ReverseActivity : Message Received from input: Hello Dapr Workflow!
|
||||
i.d.s.e.wfp.child.ReverseActivity : Sending message to output: !wolfkroW rpaD olleH
|
||||
io.dapr.workflows.WorkflowContext : ChildWorkflow finished with: !wolfkroW rpaD olleH
|
||||
io.dapr.workflows.WorkflowContext : childworkflow finished with: !wolfkroW rpaD olleH
|
||||
```
|
||||
|
||||
### ContinueAsNew Workflows example
|
||||
|
||||
In this example we start a workflow that every 3 seconds schedule a new workflow consistently. This workflow executes
|
||||
one activity called CleanUpActivity that takes 2 seconds to complete. This loops repeat consistently for 5 times.
|
||||
|
||||
To start the workflow you can run:
|
||||
|
||||
<!-- STEP
|
||||
name: Start ContinueAsNew Workflow
|
||||
match_order: none
|
||||
output_match_mode: substring
|
||||
expected_stdout_lines:
|
||||
- '{"cleanUpTimes":5}'
|
||||
background: true
|
||||
sleep: 10
|
||||
timeout_seconds: 15
|
||||
-->
|
||||
<!-- Timeout for above service must be more than sleep + timeout for the client-->
|
||||
|
||||
```sh
|
||||
curl -X POST localhost:8080/wfp/continueasnew -H 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
As result from executing the request you should see:
|
||||
|
||||
```bash
|
||||
{"cleanUpTimes":5}
|
||||
```
|
||||
|
||||
In the application output you should see the workflow activities being executed.
|
||||
|
||||
```bash
|
||||
io.dapr.workflows.WorkflowContext : Starting Workflow: io.dapr.springboot.examples.wfp.continueasnew.ContinueAsNewWorkflow
|
||||
io.dapr.workflows.WorkflowContext : call CleanUpActivity to do the clean up
|
||||
i.d.s.e.w.WorkflowPatternsRestController : Workflow instance b808e7d6-ab47-4eba-8188-dc9ff8780764 started
|
||||
i.d.s.e.w.continueasnew.CleanUpActivity : Starting Activity: io.dapr.springboot.examples.wfp.continueasnew.CleanUpActivity
|
||||
i.d.s.e.w.continueasnew.CleanUpActivity : start clean up work, it may take few seconds to finish... Time:10:48:45
|
||||
io.dapr.workflows.WorkflowContext : CleanUpActivity finished
|
||||
io.dapr.workflows.WorkflowContext : wait 5 seconds for next clean up
|
||||
io.dapr.workflows.WorkflowContext : Let's do more cleaning.
|
||||
io.dapr.workflows.WorkflowContext : Starting Workflow: io.dapr.springboot.examples.wfp.continueasnew.ContinueAsNewWorkflow
|
||||
io.dapr.workflows.WorkflowContext : call CleanUpActivity to do the clean up
|
||||
i.d.s.e.w.continueasnew.CleanUpActivity : Starting Activity: io.dapr.springboot.examples.wfp.continueasnew.CleanUpActivity
|
||||
i.d.s.e.w.continueasnew.CleanUpActivity : start clean up work, it may take few seconds to finish... Time:10:48:50
|
||||
io.dapr.workflows.WorkflowContext : CleanUpActivity finished
|
||||
io.dapr.workflows.WorkflowContext : wait 5 seconds for next clean up
|
||||
io.dapr.workflows.WorkflowContext : Let's do more cleaning.
|
||||
io.dapr.workflows.WorkflowContext : Starting Workflow: io.dapr.springboot.examples.wfp.continueasnew.ContinueAsNewWorkflow
|
||||
io.dapr.workflows.WorkflowContext : call CleanUpActivity to do the clean up
|
||||
i.d.s.e.w.continueasnew.CleanUpActivity : Starting Activity: io.dapr.springboot.examples.wfp.continueasnew.CleanUpActivity
|
||||
i.d.s.e.w.continueasnew.CleanUpActivity : start clean up work, it may take few seconds to finish... Time:10:48:55
|
||||
io.dapr.workflows.WorkflowContext : CleanUpActivity finished
|
||||
io.dapr.workflows.WorkflowContext : wait 5 seconds for next clean up
|
||||
io.dapr.workflows.WorkflowContext : Let's do more cleaning.
|
||||
io.dapr.workflows.WorkflowContext : Starting Workflow: io.dapr.springboot.examples.wfp.continueasnew.ContinueAsNewWorkflow
|
||||
io.dapr.workflows.WorkflowContext : call CleanUpActivity to do the clean up
|
||||
i.d.s.e.w.continueasnew.CleanUpActivity : Starting Activity: io.dapr.springboot.examples.wfp.continueasnew.CleanUpActivity
|
||||
i.d.s.e.w.continueasnew.CleanUpActivity : start clean up work, it may take few seconds to finish... Time:10:49:0
|
||||
io.dapr.workflows.WorkflowContext : CleanUpActivity finished
|
||||
io.dapr.workflows.WorkflowContext : wait 5 seconds for next clean up
|
||||
io.dapr.workflows.WorkflowContext : Let's do more cleaning.
|
||||
io.dapr.workflows.WorkflowContext : Starting Workflow: io.dapr.springboot.examples.wfp.continueasnew.ContinueAsNewWorkflow
|
||||
io.dapr.workflows.WorkflowContext : call CleanUpActivity to do the clean up
|
||||
i.d.s.e.w.continueasnew.CleanUpActivity : Starting Activity: io.dapr.springboot.examples.wfp.continueasnew.CleanUpActivity
|
||||
i.d.s.e.w.continueasnew.CleanUpActivity : start clean up work, it may take few seconds to finish... Time:10:49:5
|
||||
io.dapr.workflows.WorkflowContext : CleanUpActivity finished
|
||||
io.dapr.workflows.WorkflowContext : wait 5 seconds for next clean up
|
||||
io.dapr.workflows.WorkflowContext : We did enough cleaning
|
||||
```
|
||||
|
||||
### External Event Workflow example
|
||||
|
||||
In this example we start a workflow that as part of its execution waits for an external event to continue. To correlate
|
||||
workflows and events we use the parameter `orderId`
|
||||
|
||||
To start the workflow you can run:
|
||||
|
||||
<!-- STEP
|
||||
name: Start External Event Workflow
|
||||
match_order: none
|
||||
output_match_mode: substring
|
||||
background: true
|
||||
sleep: 1
|
||||
timeout_seconds: 2
|
||||
-->
|
||||
<!-- Timeout for above service must be more than sleep + timeout for the client-->
|
||||
|
||||
```sh
|
||||
curl -X POST "localhost:8080/wfp/externalevent?orderId=123" -H 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
|
||||
In the application output you should see the workflow activities being executed.
|
||||
|
||||
```bash
|
||||
io.dapr.workflows.WorkflowContext : Starting Workflow: io.dapr.springboot.examples.wfp.externalevent.ExternalEventWorkflow
|
||||
io.dapr.workflows.WorkflowContext : Waiting for approval...
|
||||
i.d.s.e.w.WorkflowPatternsRestController : Workflow instance 8a55cf6d-9059-49b1-8c83-fbe17567a02e started
|
||||
```
|
||||
|
||||
You should see the Workflow ID that was created, in this example you don't need to remember this id,
|
||||
as you can use the orderId to find the right instance.
|
||||
When you are ready to approve the order you can send the following request:
|
||||
|
||||
<!-- STEP
|
||||
name: Send External Event
|
||||
match_order: none
|
||||
output_match_mode: substring
|
||||
expected_stdout_lines:
|
||||
- '{"approved":true}'
|
||||
background: true
|
||||
sleep: 5
|
||||
timeout_seconds: 10
|
||||
-->
|
||||
<!-- Timeout for above service must be more than sleep + timeout for the client-->
|
||||
|
||||
To send the event you can run:
|
||||
|
||||
```sh
|
||||
curl -X POST "localhost:8080/wfp/externalevent-continue?orderId=123&decision=true" -H 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
```bash
|
||||
{"approved":true}
|
||||
```
|
||||
|
||||
In the application output you should see the workflow activities being executed.
|
||||
|
||||
```bash
|
||||
i.d.s.e.w.WorkflowPatternsRestController : Workflow instance e86bc464-6166-434d-8c91-d99040d6f54e continue
|
||||
io.dapr.workflows.WorkflowContext : approval granted - do the approved action
|
||||
i.d.s.e.w.externalevent.ApproveActivity : Starting Activity: io.dapr.springboot.examples.wfp.externalevent.ApproveActivity
|
||||
i.d.s.e.w.externalevent.ApproveActivity : Running approval activity...
|
||||
io.dapr.workflows.WorkflowContext : approval-activity finished
|
||||
```
|
||||
|
||||
### Fan Out/In Workflow example
|
||||
|
||||
In this example we start a workflow that takes an ArrayList of strings and calls one activity per item in the ArrayList. The activities
|
||||
are executed and the workflow waits for all of them to complete to aggregate the results.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
SW((Start
|
||||
Workflow))
|
||||
subgraph for each word in the input
|
||||
GWL[GetWordLength]
|
||||
end
|
||||
ALL[Wait until all tasks
|
||||
are completed]
|
||||
EW((End
|
||||
Workflow))
|
||||
SW --> GWL
|
||||
GWL --> ALL
|
||||
ALL --> EW
|
||||
```
|
||||
|
||||
To start the workflow you can run:
|
||||
|
||||
<!-- STEP
|
||||
name: Start Parent/Child Workflow
|
||||
match_order: none
|
||||
output_match_mode: substring
|
||||
expected_stdout_lines:
|
||||
- '{"wordCount":60}'
|
||||
background: true
|
||||
sleep: 1
|
||||
timeout_seconds: 2
|
||||
-->
|
||||
<!-- Timeout for above service must be more than sleep + timeout for the client-->
|
||||
|
||||
```sh
|
||||
curl -X POST localhost:8080/wfp/fanoutin -H 'Content-Type: application/json' -d @body.json
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
As result from executing the request you should see:
|
||||
|
||||
```bash
|
||||
{"wordCount":60}
|
||||
```
|
||||
|
||||
In the application output you should see the workflow activities being executed.
|
||||
|
||||
```bash
|
||||
io.dapr.workflows.WorkflowContext : Starting Workflow: io.dapr.springboot.examples.wfp.fanoutin.FanOutInWorkflow
|
||||
i.d.s.e.w.WorkflowPatternsRestController : Workflow instance a771a7ba-f9fb-4399-aaee-a2fb0b102e5d started
|
||||
i.d.s.e.wfp.fanoutin.CountWordsActivity : Starting Activity: io.dapr.springboot.examples.wfp.fanoutin.CountWordsActivity
|
||||
i.d.s.e.wfp.fanoutin.CountWordsActivity : Starting Activity: io.dapr.springboot.examples.wfp.fanoutin.CountWordsActivity
|
||||
i.d.s.e.wfp.fanoutin.CountWordsActivity : Starting Activity: io.dapr.springboot.examples.wfp.fanoutin.CountWordsActivity
|
||||
i.d.s.e.wfp.fanoutin.CountWordsActivity : Activity returned: 2.
|
||||
i.d.s.e.wfp.fanoutin.CountWordsActivity : Activity finished
|
||||
i.d.s.e.wfp.fanoutin.CountWordsActivity : Activity returned: 11.
|
||||
i.d.s.e.wfp.fanoutin.CountWordsActivity : Activity returned: 17.
|
||||
i.d.s.e.wfp.fanoutin.CountWordsActivity : Activity finished
|
||||
i.d.s.e.wfp.fanoutin.CountWordsActivity : Activity finished
|
||||
i.d.s.e.wfp.fanoutin.CountWordsActivity : Starting Activity: io.dapr.springboot.examples.wfp.fanoutin.CountWordsActivity
|
||||
i.d.s.e.wfp.fanoutin.CountWordsActivity : Activity returned: 21.
|
||||
i.d.s.e.wfp.fanoutin.CountWordsActivity : Activity finished
|
||||
i.d.s.e.wfp.fanoutin.CountWordsActivity : Starting Activity: io.dapr.springboot.examples.wfp.fanoutin.CountWordsActivity
|
||||
i.d.s.e.wfp.fanoutin.CountWordsActivity : Activity returned: 9.
|
||||
i.d.s.e.wfp.fanoutin.CountWordsActivity : Activity finished
|
||||
io.dapr.workflows.WorkflowContext : Workflow finished with result: 60
|
||||
```
|
||||
|
||||
|
||||
## Testing workflow executions
|
||||
|
||||
Workflow execution can be tested using Testcontainers and you can find all the tests for the patterns covered in this
|
||||
application [here](test/java/io/dapr/springboot/examples/wfp/TestWorkflowPatternsApplication.java).
|
|
@ -0,0 +1,5 @@
|
|||
["Hello, world!",
|
||||
"The quick brown fox jumps over the lazy dog.",
|
||||
"If a tree falls in the forest and there is no one there to hear it, does it make a sound?",
|
||||
"The greatest glory in living lies not in never falling, but in rising every time we fall.",
|
||||
"Always remember that you are absolutely unique. Just like everyone else."]
|
|
@ -0,0 +1,68 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>spring-boot-examples</artifactId>
|
||||
<version>0.16.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>workflows</artifactId>
|
||||
<name>workflows</name>
|
||||
<description>Spring Boot, Testcontainers and Dapr Integration Examples :: Workflows</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.dapr.spring</groupId>
|
||||
<artifactId>dapr-spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-site-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<configuration>
|
||||
<!-- Skip checkstyle for auto-generated code -->
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.wfp;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
|
||||
@SpringBootApplication
|
||||
public class WorkflowPatternsApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(WorkflowPatternsApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.wfp;
|
||||
|
||||
import io.dapr.springboot.examples.wfp.continueasnew.CleanUpLog;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class WorkflowPatternsConfiguration {
|
||||
@Bean
|
||||
public CleanUpLog cleanUpLog(){
|
||||
return new CleanUpLog();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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.wfp;
|
||||
|
||||
import io.dapr.spring.workflows.config.EnableDaprWorkflows;
|
||||
import io.dapr.springboot.examples.wfp.chain.ChainWorkflow;
|
||||
import io.dapr.springboot.examples.wfp.child.ParentWorkflow;
|
||||
import io.dapr.springboot.examples.wfp.continueasnew.CleanUpLog;
|
||||
import io.dapr.springboot.examples.wfp.continueasnew.ContinueAsNewWorkflow;
|
||||
import io.dapr.springboot.examples.wfp.externalevent.Decision;
|
||||
import io.dapr.springboot.examples.wfp.externalevent.ExternalEventWorkflow;
|
||||
import io.dapr.springboot.examples.wfp.fanoutin.FanOutInWorkflow;
|
||||
import io.dapr.springboot.examples.wfp.fanoutin.Result;
|
||||
import io.dapr.workflows.client.DaprWorkflowClient;
|
||||
import io.dapr.workflows.client.WorkflowInstanceStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
@RestController
|
||||
@EnableDaprWorkflows
|
||||
public class WorkflowPatternsRestController {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(WorkflowPatternsRestController.class);
|
||||
|
||||
@Autowired
|
||||
private DaprWorkflowClient daprWorkflowClient;
|
||||
|
||||
@Autowired
|
||||
private CleanUpLog cleanUpLog;
|
||||
|
||||
private Map<String, String> ordersToApprove = new HashMap<>();
|
||||
|
||||
|
||||
/**
|
||||
* Run Chain Demo Workflow
|
||||
* @return the output of the ChainWorkflow execution
|
||||
*/
|
||||
@PostMapping("wfp/chain")
|
||||
public String chain() throws TimeoutException {
|
||||
String instanceId = daprWorkflowClient.scheduleNewWorkflow(ChainWorkflow.class);
|
||||
logger.info("Workflow instance " + instanceId + " started");
|
||||
return daprWorkflowClient
|
||||
.waitForInstanceCompletion(instanceId, Duration.ofSeconds(2), true)
|
||||
.readOutputAs(String.class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run Child Demo Workflow
|
||||
* @return confirmation that the workflow instance was created for the workflow pattern child
|
||||
*/
|
||||
@PostMapping("wfp/child")
|
||||
public String child() throws TimeoutException {
|
||||
String instanceId = daprWorkflowClient.scheduleNewWorkflow(ParentWorkflow.class);
|
||||
logger.info("Workflow instance " + instanceId + " started");
|
||||
return daprWorkflowClient
|
||||
.waitForInstanceCompletion(instanceId, Duration.ofSeconds(2), true)
|
||||
.readOutputAs(String.class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run Fan Out/in Demo Workflow
|
||||
* @return confirmation that the workflow instance was created for the workflow pattern faninout
|
||||
*/
|
||||
@PostMapping("wfp/fanoutin")
|
||||
public Result fanOutIn(@RequestBody List<String> listOfStrings) throws TimeoutException {
|
||||
|
||||
String instanceId = daprWorkflowClient.scheduleNewWorkflow(FanOutInWorkflow.class, listOfStrings);
|
||||
logger.info("Workflow instance " + instanceId + " started");
|
||||
|
||||
// Block until the orchestration completes. Then print the final status, which includes the output.
|
||||
WorkflowInstanceStatus workflowInstanceStatus = daprWorkflowClient.waitForInstanceCompletion(
|
||||
instanceId,
|
||||
Duration.ofSeconds(30),
|
||||
true);
|
||||
logger.info("workflow instance with ID: %s completed with result: %s%n", instanceId,
|
||||
workflowInstanceStatus.readOutputAs(Result.class));
|
||||
return workflowInstanceStatus.readOutputAs(Result.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run External Event Workflow Pattern
|
||||
* @return confirmation that the workflow instance was created for the workflow pattern externalevent
|
||||
*/
|
||||
@PostMapping("wfp/externalevent")
|
||||
public String externalEvent(@RequestParam("orderId") String orderId) {
|
||||
String instanceId = daprWorkflowClient.scheduleNewWorkflow(ExternalEventWorkflow.class);
|
||||
ordersToApprove.put(orderId, instanceId);
|
||||
logger.info("Workflow instance " + instanceId + " started");
|
||||
return instanceId;
|
||||
}
|
||||
|
||||
@PostMapping("wfp/externalevent-continue")
|
||||
public Decision externalEventContinue(@RequestParam("orderId") String orderId, @RequestParam("decision") Boolean decision)
|
||||
throws TimeoutException {
|
||||
String instanceId = ordersToApprove.get(orderId);
|
||||
logger.info("Workflow instance " + instanceId + " continue");
|
||||
daprWorkflowClient.raiseEvent(instanceId, "Approval", decision);
|
||||
WorkflowInstanceStatus workflowInstanceStatus = daprWorkflowClient
|
||||
.waitForInstanceCompletion(instanceId, null, true);
|
||||
return workflowInstanceStatus.readOutputAs(Decision.class);
|
||||
}
|
||||
|
||||
@PostMapping("wfp/continueasnew")
|
||||
public CleanUpLog continueAsNew()
|
||||
throws TimeoutException {
|
||||
|
||||
cleanUpLog.clearLog();
|
||||
String instanceId = daprWorkflowClient.scheduleNewWorkflow(ContinueAsNewWorkflow.class);
|
||||
logger.info("Workflow instance " + instanceId + " started");
|
||||
|
||||
WorkflowInstanceStatus workflowInstanceStatus = daprWorkflowClient.waitForInstanceCompletion(instanceId, null, true);
|
||||
System.out.printf("workflow instance with ID: %s completed.", instanceId);
|
||||
return workflowInstanceStatus.readOutputAs(CleanUpLog.class);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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.examples.wfp.chain;
|
||||
|
||||
import io.dapr.workflows.Workflow;
|
||||
import io.dapr.workflows.WorkflowStub;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ChainWorkflow implements Workflow {
|
||||
@Override
|
||||
public WorkflowStub create() {
|
||||
return ctx -> {
|
||||
ctx.getLogger().info("Starting Workflow: " + ctx.getName());
|
||||
|
||||
String result = "";
|
||||
result += ctx.callActivity(ToUpperCaseActivity.class.getName(), "Tokyo", String.class).await() + ", ";
|
||||
result += ctx.callActivity(ToUpperCaseActivity.class.getName(), "London", String.class).await() + ", ";
|
||||
result += ctx.callActivity(ToUpperCaseActivity.class.getName(), "Seattle", String.class).await();
|
||||
|
||||
ctx.getLogger().info("Workflow finished with result: " + result);
|
||||
ctx.complete(result);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.examples.wfp.chain;
|
||||
|
||||
import io.dapr.workflows.WorkflowActivity;
|
||||
import io.dapr.workflows.WorkflowActivityContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ToUpperCaseActivity implements WorkflowActivity {
|
||||
|
||||
@Override
|
||||
public Object run(WorkflowActivityContext ctx) {
|
||||
Logger logger = LoggerFactory.getLogger(ToUpperCaseActivity.class);
|
||||
logger.info("Starting Activity: " + ctx.getName());
|
||||
|
||||
var message = ctx.getInput(String.class);
|
||||
var newMessage = message.toUpperCase();
|
||||
|
||||
logger.info("Message Received from input: " + message);
|
||||
logger.info("Sending message to output: " + newMessage);
|
||||
|
||||
return newMessage;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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.examples.wfp.child;
|
||||
|
||||
import io.dapr.workflows.Workflow;
|
||||
import io.dapr.workflows.WorkflowStub;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ChildWorkflow implements Workflow {
|
||||
@Override
|
||||
public WorkflowStub create() {
|
||||
return ctx -> {
|
||||
ctx.getLogger().info("Starting ChildWorkflow: " + ctx.getName());
|
||||
|
||||
var childWorkflowInput = ctx.getInput(String.class);
|
||||
ctx.getLogger().info("ChildWorkflow received input: " + childWorkflowInput);
|
||||
|
||||
ctx.getLogger().info("ChildWorkflow is calling Activity: " + ReverseActivity.class.getName());
|
||||
String result = ctx.callActivity(ReverseActivity.class.getName(), childWorkflowInput, String.class).await();
|
||||
|
||||
ctx.getLogger().info("ChildWorkflow finished with: " + result);
|
||||
ctx.complete(result);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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.examples.wfp.child;
|
||||
|
||||
import io.dapr.workflows.Workflow;
|
||||
import io.dapr.workflows.WorkflowStub;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ParentWorkflow implements Workflow {
|
||||
@Override
|
||||
public WorkflowStub create() {
|
||||
return ctx -> {
|
||||
ctx.getLogger().info("Starting Workflow: " + ctx.getName());
|
||||
|
||||
var childWorkflowInput = "Hello Dapr Workflow!";
|
||||
ctx.getLogger().info("calling childworkflow with input: " + childWorkflowInput);
|
||||
|
||||
var childWorkflowOutput =
|
||||
ctx.callChildWorkflow(ChildWorkflow.class.getName(), childWorkflowInput, String.class).await();
|
||||
|
||||
ctx.getLogger().info("childworkflow finished with: " + childWorkflowOutput);
|
||||
ctx.complete(childWorkflowOutput);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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.examples.wfp.child;
|
||||
|
||||
import io.dapr.workflows.WorkflowActivity;
|
||||
import io.dapr.workflows.WorkflowActivityContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ReverseActivity implements WorkflowActivity {
|
||||
@Override
|
||||
public Object run(WorkflowActivityContext ctx) {
|
||||
Logger logger = LoggerFactory.getLogger(ReverseActivity.class);
|
||||
logger.info("Starting Activity: " + ctx.getName());
|
||||
|
||||
var message = ctx.getInput(String.class);
|
||||
var newMessage = new StringBuilder(message).reverse().toString();
|
||||
|
||||
logger.info("Message Received from input: " + message);
|
||||
logger.info("Sending message to output: " + newMessage);
|
||||
|
||||
return newMessage;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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.examples.wfp.continueasnew;
|
||||
|
||||
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;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
public class CleanUpActivity implements WorkflowActivity {
|
||||
|
||||
@Autowired
|
||||
private CleanUpLog cleanUpLog;
|
||||
|
||||
@Override
|
||||
public Object run(WorkflowActivityContext ctx) {
|
||||
Logger logger = LoggerFactory.getLogger(CleanUpActivity.class);
|
||||
logger.info("Starting Activity: " + ctx.getName());
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
String cleanUpTimeString = now.getHour() + ":" + now.getMinute() + ":" + now.getSecond();
|
||||
logger.info("start clean up work, it may take few seconds to finish... Time:" + cleanUpTimeString);
|
||||
|
||||
//Sleeping for 2 seconds to simulate long running operation
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(2);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
cleanUpLog.increment();
|
||||
|
||||
return "clean up finish.";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.wfp.continueasnew;
|
||||
|
||||
public class CleanUpLog {
|
||||
private Integer cleanUpTimes = 0;
|
||||
|
||||
public CleanUpLog() {
|
||||
}
|
||||
|
||||
public void increment() {
|
||||
this.cleanUpTimes += 1;
|
||||
}
|
||||
|
||||
public Integer getCleanUpTimes() {
|
||||
return cleanUpTimes;
|
||||
}
|
||||
|
||||
public void clearLog(){
|
||||
this.cleanUpTimes = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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.examples.wfp.continueasnew;
|
||||
|
||||
import io.dapr.workflows.Workflow;
|
||||
import io.dapr.workflows.WorkflowStub;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@Component
|
||||
public class ContinueAsNewWorkflow implements Workflow {
|
||||
/*
|
||||
Compared with a CRON schedule, this periodic workflow example will never overlap.
|
||||
For example, a CRON schedule that executes a cleanup every hour will execute it at 1:00, 2:00, 3:00 etc.
|
||||
and could potentially run into overlap issues if the cleanup takes longer than an hour.
|
||||
In this example, however, if the cleanup takes 30 minutes, and we create a timer for 1 hour between cleanups,
|
||||
then it will be scheduled at 1:00, 2:30, 4:00, etc. and there is no chance of overlap.
|
||||
*/
|
||||
|
||||
@Autowired
|
||||
private CleanUpLog cleanUpLog;
|
||||
|
||||
@Override
|
||||
public WorkflowStub create() {
|
||||
return ctx -> {
|
||||
ctx.getLogger().info("Starting Workflow: " + ctx.getName());
|
||||
|
||||
ctx.getLogger().info("call CleanUpActivity to do the clean up");
|
||||
ctx.callActivity(CleanUpActivity.class.getName(), cleanUpLog).await();
|
||||
ctx.getLogger().info("CleanUpActivity finished");
|
||||
|
||||
ctx.getLogger().info("wait 5 seconds for next clean up");
|
||||
ctx.createTimer(Duration.ofSeconds(3)).await();
|
||||
|
||||
if(cleanUpLog.getCleanUpTimes() < 5) {
|
||||
// continue the workflow.
|
||||
ctx.getLogger().info("Let's do more cleaning.");
|
||||
ctx.continueAsNew(null);
|
||||
} else{
|
||||
ctx.getLogger().info("We did enough cleaning");
|
||||
ctx.complete(cleanUpLog);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.examples.wfp.externalevent;
|
||||
|
||||
import io.dapr.workflows.WorkflowActivity;
|
||||
import io.dapr.workflows.WorkflowActivityContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
public class ApproveActivity implements WorkflowActivity {
|
||||
@Override
|
||||
public Object run(WorkflowActivityContext ctx) {
|
||||
Logger logger = LoggerFactory.getLogger(ApproveActivity.class);
|
||||
logger.info("Starting Activity: " + ctx.getName());
|
||||
|
||||
logger.info("Running approval activity...");
|
||||
//Sleeping for 5 seconds to simulate long running operation
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(5);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return new Decision(true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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.wfp.externalevent;
|
||||
|
||||
public class Decision {
|
||||
private Boolean approved;
|
||||
|
||||
public Decision() {
|
||||
}
|
||||
|
||||
public Decision(Boolean approved) {
|
||||
this.approved = approved;
|
||||
}
|
||||
|
||||
public Boolean getApproved() {
|
||||
return approved;
|
||||
}
|
||||
|
||||
public void setApproved(Boolean approved) {
|
||||
this.approved = approved;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.examples.wfp.externalevent;
|
||||
|
||||
import io.dapr.workflows.WorkflowActivity;
|
||||
import io.dapr.workflows.WorkflowActivityContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
public class DenyActivity implements WorkflowActivity {
|
||||
@Override
|
||||
public Object run(WorkflowActivityContext ctx) {
|
||||
Logger logger = LoggerFactory.getLogger(DenyActivity.class);
|
||||
logger.info("Starting Activity: " + ctx.getName());
|
||||
|
||||
logger.info("Running denied activity...");
|
||||
//Sleeping for 5 seconds to simulate long running operation
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(5);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return new Decision(false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.examples.wfp.externalevent;
|
||||
|
||||
import io.dapr.workflows.Workflow;
|
||||
import io.dapr.workflows.WorkflowStub;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ExternalEventWorkflow implements Workflow {
|
||||
@Override
|
||||
public WorkflowStub create() {
|
||||
return ctx -> {
|
||||
ctx.getLogger().info("Starting Workflow: " + ctx.getName());
|
||||
|
||||
ctx.getLogger().info("Waiting for approval...");
|
||||
Boolean approved = ctx.waitForExternalEvent("Approval", boolean.class).await();
|
||||
Decision decision = null;
|
||||
if (approved) {
|
||||
ctx.getLogger().info("approval granted - do the approved action");
|
||||
decision = ctx.callActivity(ApproveActivity.class.getName(), Decision.class).await();
|
||||
|
||||
ctx.getLogger().info("approval-activity finished");
|
||||
} else {
|
||||
ctx.getLogger().info("approval denied - send a notification");
|
||||
decision = ctx.callActivity(DenyActivity.class.getName(), Decision.class).await();
|
||||
ctx.getLogger().info("denied-activity finished");
|
||||
}
|
||||
ctx.complete(decision);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.examples.wfp.fanoutin;
|
||||
|
||||
import io.dapr.workflows.WorkflowActivity;
|
||||
import io.dapr.workflows.WorkflowActivityContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
@Component
|
||||
public class CountWordsActivity implements WorkflowActivity {
|
||||
@Override
|
||||
public Object run(WorkflowActivityContext ctx) {
|
||||
Logger logger = LoggerFactory.getLogger(CountWordsActivity.class);
|
||||
logger.info("Starting Activity: {}", ctx.getName());
|
||||
|
||||
String input = ctx.getInput(String.class);
|
||||
StringTokenizer tokenizer = new StringTokenizer(input);
|
||||
int result = tokenizer.countTokens();
|
||||
|
||||
logger.info("Activity returned: {}.", result);
|
||||
logger.info("Activity finished");
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.examples.wfp.fanoutin;
|
||||
|
||||
import io.dapr.durabletask.Task;
|
||||
import io.dapr.workflows.Workflow;
|
||||
import io.dapr.workflows.WorkflowStub;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
public class FanOutInWorkflow implements Workflow {
|
||||
@Override
|
||||
public WorkflowStub create() {
|
||||
return ctx -> {
|
||||
|
||||
ctx.getLogger().info("Starting Workflow: " + ctx.getName());
|
||||
|
||||
|
||||
// The input is a list of objects that need to be operated on.
|
||||
// In this example, inputs are expected to be strings.
|
||||
List<?> inputs = ctx.getInput(List.class);
|
||||
|
||||
// Fan-out to multiple concurrent activity invocations, each of which does a word count.
|
||||
List<Task<Integer>> tasks = inputs.stream()
|
||||
.map(input -> ctx.callActivity(CountWordsActivity.class.getName(), input.toString(), Integer.class))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Fan-in to get the total word count from all the individual activity results.
|
||||
List<Integer> allWordCountResults = ctx.allOf(tasks).await();
|
||||
int totalWordCount = allWordCountResults.stream().mapToInt(Integer::intValue).sum();
|
||||
|
||||
ctx.getLogger().info("Workflow finished with result: " + totalWordCount);
|
||||
// Save the final result as the orchestration output.
|
||||
ctx.complete(new Result(totalWordCount));
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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.wfp.fanoutin;
|
||||
|
||||
public class Result {
|
||||
private Integer wordCount;
|
||||
|
||||
public Result() {
|
||||
}
|
||||
|
||||
public Result(Integer wordCount) {
|
||||
this.wordCount = wordCount;
|
||||
}
|
||||
|
||||
public Integer getWordCount() {
|
||||
return wordCount;
|
||||
}
|
||||
|
||||
public void setWordCount(Integer wordCount) {
|
||||
this.wordCount = wordCount;
|
||||
}
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
spring.application.name=workflow-patterns-app
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.wfp;
|
||||
|
||||
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("workflow-patterns-app")
|
||||
.withComponent(new Component("kvstore", "state.in-memory", "v1", Collections.singletonMap("actorStateStore", String.valueOf(true))))
|
||||
.withAppPort(8080)
|
||||
.withAppHealthCheckPath("/actuator/health")
|
||||
.withAppChannelAddress("host.testcontainers.internal");
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.wfp;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
|
||||
@SpringBootApplication
|
||||
public class TestWorkflowPatternsApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
SpringApplication.from(WorkflowPatternsApplication::main)
|
||||
.with(DaprTestContainersConfig.class)
|
||||
.run(args);
|
||||
org.testcontainers.Testcontainers.exposeHostPorts(8080);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* 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.wfp;
|
||||
|
||||
import io.dapr.client.DaprClient;
|
||||
import io.dapr.springboot.DaprAutoConfiguration;
|
||||
import io.dapr.springboot.examples.wfp.continueasnew.CleanUpLog;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@SpringBootTest(classes = {TestWorkflowPatternsApplication.class, DaprTestContainersConfig.class,
|
||||
DaprAutoConfiguration.class, },
|
||||
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
|
||||
class WorkflowPatternsAppTests {
|
||||
|
||||
@Autowired
|
||||
private DaprClient daprClient;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
RestAssured.baseURI = "http://localhost:" + 8080;
|
||||
org.testcontainers.Testcontainers.exposeHostPorts(8080);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testChainWorkflow() {
|
||||
given().contentType(ContentType.JSON)
|
||||
.body("")
|
||||
.when()
|
||||
.post("/wfp/chain")
|
||||
.then()
|
||||
.statusCode(200).body(containsString("TOKYO, LONDON, SEATTLE"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testChildWorkflow() {
|
||||
given().contentType(ContentType.JSON)
|
||||
.body("")
|
||||
.when()
|
||||
.post("/wfp/child")
|
||||
.then()
|
||||
.statusCode(200).body(containsString("!wolfkroW rpaD olleH"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFanOutIn() {
|
||||
List<String> listOfStrings = Arrays.asList(
|
||||
"Hello, world!",
|
||||
"The quick brown fox jumps over the lazy dog.",
|
||||
"If a tree falls in the forest and there is no one there to hear it, does it make a sound?",
|
||||
"The greatest glory in living lies not in never falling, but in rising every time we fall.",
|
||||
"Always remember that you are absolutely unique. Just like everyone else.");
|
||||
|
||||
given().contentType(ContentType.JSON)
|
||||
.body(listOfStrings)
|
||||
.when()
|
||||
.post("/wfp/fanoutin")
|
||||
.then()
|
||||
.statusCode(200).body("wordCount",equalTo(60));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExternalEventApprove() {
|
||||
|
||||
given()
|
||||
.queryParam("orderId", "123")
|
||||
.when()
|
||||
.post("/wfp/externalevent")
|
||||
.then()
|
||||
.statusCode(200).extract().asString();
|
||||
|
||||
|
||||
|
||||
given()
|
||||
.queryParam("orderId", "123")
|
||||
.queryParam("decision", true)
|
||||
.when()
|
||||
.post("/wfp/externalevent-continue")
|
||||
.then()
|
||||
.statusCode(200).body("approved", equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExternalEventDeny() {
|
||||
|
||||
given()
|
||||
.queryParam("orderId", "123")
|
||||
.when()
|
||||
.post("/wfp/externalevent")
|
||||
.then()
|
||||
.statusCode(200).extract().asString();
|
||||
|
||||
|
||||
|
||||
given()
|
||||
.queryParam("orderId", "123")
|
||||
.queryParam("decision", false)
|
||||
.when()
|
||||
.post("/wfp/externalevent-continue")
|
||||
.then()
|
||||
.statusCode(200).body("approved", equalTo(false));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testContinueAsNew() {
|
||||
//This call blocks until all the clean up activities are executed
|
||||
CleanUpLog cleanUpLog = given().contentType(ContentType.JSON)
|
||||
.body("")
|
||||
.when()
|
||||
.post("/wfp/continueasnew")
|
||||
.then()
|
||||
.statusCode(200).extract().as(CleanUpLog.class);
|
||||
|
||||
assertEquals(5, cleanUpLog.getCleanUpTimes());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
spring.application.name=workflow-patterns-app
|
Loading…
Reference in New Issue