Skip to content

Commit 0608eca

Browse files
Add more unit tests and docs for fluent (#679)
Signed-off-by: Ricardo Zanini <[email protected]>
1 parent 1241aa2 commit 0608eca

File tree

8 files changed

+429
-7
lines changed

8 files changed

+429
-7
lines changed

experimental/lambda-fluent/pom.xml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>io.serverlessworkflow</groupId>
8+
<artifactId>serverlessworkflow-experimental</artifactId>
9+
<version>8.0.0-SNAPSHOT</version>
10+
</parent>
11+
12+
<artifactId>serverlessworkflow-lambda-fluent</artifactId>
13+
<name>Serverless Workflow :: Experimental :: Lambda Fluent</name>
14+
<packaging>pom</packaging>
15+
16+
<properties>
17+
<maven.compiler.source>17</maven.compiler.source>
18+
<maven.compiler.target>17</maven.compiler.target>
19+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
20+
</properties>
21+
22+
<dependencies>
23+
<dependency>
24+
<groupId>io.serverlessworkflow</groupId>
25+
<artifactId>serverlessworkflow-fluent-func</artifactId>
26+
</dependency>
27+
<dependency>
28+
<groupId>io.serverlessworkflow</groupId>
29+
<artifactId>serverlessworkflow-experimental-lambda</artifactId>
30+
</dependency>
31+
</dependencies>
32+
</project>

experimental/lambda/pom.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<version>8.0.0-SNAPSHOT</version>
88
</parent>
99
<artifactId>serverlessworkflow-experimental-lambda</artifactId>
10-
<name>ServelessWorkflow:: Experimental:: lambda</name>
10+
<name>Serverless Workflow :: Experimental :: Lambda</name>
1111
<dependencies>
1212
<dependency>
1313
<groupId>io.serverlessworkflow</groupId>
@@ -20,6 +20,7 @@
2020
<dependency>
2121
<groupId>io.serverlessworkflow</groupId>
2222
<artifactId>serverlessworkflow-fluent-func</artifactId>
23+
<scope>test</scope>
2324
</dependency>
2425
<dependency>
2526
<groupId>org.junit.jupiter</groupId>

experimental/pom.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
</parent>
88
<artifactId>serverlessworkflow-experimental</artifactId>
99
<packaging>pom</packaging>
10-
<name>ServerlessWorkflow:: Experimental</name>
10+
<name>Serverless Workflow :: Experimental</name>
1111
<dependencyManagement>
1212
<dependencies>
1313
<dependency>
@@ -40,5 +40,6 @@
4040
<modules>
4141
<module>types</module>
4242
<module>lambda</module>
43+
<module>lambda-fluent</module>
4344
</modules>
4445
</project>

experimental/types/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<version>8.0.0-SNAPSHOT</version>
77
</parent>
88
<artifactId>serverlessworkflow-experimental-types</artifactId>
9-
<name>ServelessWorkflow:: Experimental:: Types</name>
9+
<name>Serverless Workflow :: Experimental:: Types</name>
1010
<dependencies>
1111
<dependency>
1212
<groupId>io.serverlessworkflow</groupId>

fluent/README.md

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
# CNCF Serverless Workflow SDK Java — Fluent DSL
2+
3+
> A programmatic, type‑safe Java API for building and running Serverless Workflows (and agentic workflows) without writing YAML.
4+
5+
---
6+
7+
## 📦 Modules
8+
9+
| Module | Purpose |
10+
| -------------- | --------------------------------------------------------------------------------------------- |
11+
| **spec** | Core DSL implementing the [Serverless Workflow Specification](https://github.com/serverlessworkflow/specification). Purely compliant fluent API. |
12+
| **func** | Java‑centric “functional” DSL on top of **spec**: adds `Function<>`/`Predicate<>` support, `callFn` for Java method calls, and richer flow controls. |
13+
| **agentic** | **Experimental** proof‑of‑concept DSL built on **func** for LangChain4j agentic workflows: `agent`, `sequence`, `loop`, `parallel`, etc. |
14+
15+
---
16+
17+
## 🔧 Getting Started
18+
19+
Add the modules you need to your Maven `pom.xml` (replace versions as appropriate):
20+
21+
```xml
22+
<!--
23+
Replace ${version.io.serverlessworkflow} with the actual released version:
24+
https://github.com/serverlessworkflow/sdk-java/releases
25+
-->
26+
<dependency>
27+
<groupId>io.serverlessworkflow</groupId>
28+
<artifactId>serverlessworkflow-fluent-spec</artifactId>
29+
<version>${version.io.serverlessworkflow}</version>
30+
</dependency>
31+
<dependency>
32+
<groupId>io.serverlessworkflow</groupId>
33+
<artifactId>serverlessworkflow-fluent-func</artifactId>
34+
<version>${version.io.serverlessworkflow}</version>
35+
</dependency>
36+
<dependency> <!-- optional, experimental -->
37+
<groupId>io.serverlessworkflow</groupId>
38+
<artifactId>serverlessworkflow-fluent-agentic</artifactId>
39+
<version>${version.io.serverlessworkflow}</version>
40+
</dependency>
41+
```
42+
43+
---
44+
45+
## 📖 Module Reference
46+
47+
### 1. Spec Fluent
48+
49+
Fully compliant with the CNCF Serverless Workflow spec.\
50+
Use it when you want a 1:1 mapping of the YAML DSL in Java.
51+
52+
```java
53+
import io.serverlessworkflow.api.types.Workflow;
54+
import io.serverlessworkflow.fluent.spec.WorkflowBuilder;
55+
56+
Workflow wf = WorkflowBuilder
57+
.workflow("flowDo")
58+
.tasks(tasks ->
59+
tasks
60+
.set("initCtx", "$.foo = 'bar'")
61+
.forEach("item", f -> f
62+
.each("item")
63+
.at("$.list")
64+
)
65+
)
66+
.build();
67+
```
68+
69+
> [!NOTE]
70+
> We rename reserved keywords (`for`, `do`, `if`, `while`, `switch`, `try`) to safe identifiers (`forEach`, `tasks`, `when`, etc.).
71+
72+
---
73+
74+
### 2. Func Fluent
75+
76+
A Java‑first DSL that builds on **spec**, adding:
77+
78+
- `callFn`: invoke arbitrary Java `Function<>` handlers
79+
- `Predicate<>` **guards** via `when(Predicate)`
80+
- Built‑in `Function`/`Predicate` support instead of JQ expressions
81+
82+
```java
83+
import io.serverlessworkflow.api.types.Workflow;
84+
import io.serverlessworkflow.fluent.func.FuncWorkflowBuilder;
85+
86+
Workflow wf = FuncWorkflowBuilder
87+
.workflow("callJavaFlow")
88+
.tasks(tasks ->
89+
tasks.callFn("invokeHandler", call -> call
90+
// e.g. call.className("com.acme.Handler")
91+
// .method("handle")
92+
// .arg("key", "value")
93+
.function(ctx -> {
94+
// your code here
95+
})
96+
)
97+
)
98+
.build();
99+
```
100+
101+
> [!WARNING]
102+
> The **func** DSL is *not* spec‑compliant. It adds Java‑specific tasks and control‑flow extensions for in‑JVM execution.
103+
104+
---
105+
106+
### 3. Agentic Fluent *(Experimental)*
107+
108+
Built on **func** for LangChain4j agentic workflows. Adds:
109+
110+
- `agent(instance)`: invoke a LangChain4j agent
111+
- `sequence(...)`: run agents in order
112+
- `loop(cfg)`: retry or repeated agent calls
113+
- `parallel(...)`: fork agent calls concurrently
114+
115+
```java
116+
import io.serverlessworkflow.api.types.Workflow;
117+
import io.serverlessworkflow.fluent.agentic.AgentWorkflowBuilder;
118+
119+
var scorer = AgentsUtils.newMovieExpert();
120+
var editor = AgentsUtils.newMovieExpert();
121+
122+
Workflow wf = AgentWorkflowBuilder
123+
.workflow("retryFlow")
124+
.tasks(tasks -> tasks.loop(
125+
"reviewLoop",
126+
loop -> loop
127+
.maxIterations(5)
128+
.exitCondition(c -> c.readState("score", 0).doubleValue() > 0.75)
129+
.subAgents("reviewer", scorer, editor)
130+
))
131+
.build();
132+
```
133+
134+
---
135+
136+
## 🚀 Real‑World Example: Order Fulfillment
137+
138+
```java
139+
import io.serverlessworkflow.api.types.Workflow;
140+
import io.serverlessworkflow.fluent.agentic.AgentWorkflowBuilder;
141+
import java.util.function.Predicate;
142+
143+
public class OrderFulfillment {
144+
145+
static class InventoryAgent { /**/ }
146+
static class NotificationAgent { /**/ }
147+
static class ShippingAgent { /**/ }
148+
149+
public Workflow buildWorkflow() {
150+
151+
Predicate<Object> inventoryOk = state ->
152+
Boolean.TRUE.equals(((java.util.Map<?,?>) state).get("inventoryAvailable"));
153+
154+
return AgentWorkflowBuilder
155+
.workflow("OrderFulfillment")
156+
.tasks(tasks -> tasks
157+
158+
// 1. initialize state
159+
.set("init", s -> s.expr("$.orderId = '.input.oriderId'"))
160+
161+
// 2. check inventory
162+
.agent("checkInventory", new InventoryAgent())
163+
164+
// 3. pull result into a flag
165+
.set("inventoryAvailable", s -> s.expr("$.checkInventory.available"))
166+
167+
// 4. retry until in stock (max 3 attempts)
168+
.loop("retryIfOutOfStock", loop -> loop
169+
.maxIterations(3)
170+
.exitCondition(inventoryOk)
171+
.subAgents("inventoryChecker", new InventoryAgent())
172+
)
173+
174+
// 5. notify systems in parallel
175+
.parallel("notifyAll",
176+
new NotificationAgent(),
177+
new ShippingAgent()
178+
)
179+
180+
// 6. mark order complete
181+
.set("complete", s -> s.expr("$.status = 'COMPLETED'"))
182+
)
183+
.build();
184+
}
185+
}
186+
```
187+
188+
---
189+
190+
## 🛠️ Next Steps & Roadmap
191+
192+
- **Error handling**: retries, back‑off, `onError` handlers
193+
- **Timers & delays**: `wait`, per‑task `timeout`
194+
- **Sub‑workflows** & composition: call one workflow from another
195+
- **Event tasks**: `onEvent`, `sendEvent`
196+
- **Human‑in‑the‑Loop**: approval/notification steps
197+
198+
Contributions welcome! Check out our [CONTRIBUTING.md](../CONTRIBUTING.md) and join the CNCF Slack channel for **Serverless Workflow**.
199+
200+
---
201+
202+
## 📜 License
203+
204+
Apache 2.0 © Serverless Workflow Authors

0 commit comments

Comments
 (0)