Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
5fe056b
docs: Implementation of Java DSL Cookbook
domhanak Apr 1, 2026
f33f2fe
Fix EchoWorkflowYamlTest
domhanak Apr 9, 2026
0b15f1c
Implement CronWorkflow exampland test
domhanak Apr 9, 2026
8e7b5f7
Fix HttpWorkflow example & t, improve existing docs
domhanak Apr 9, 2026
1f82815
Add OpenApiWorkflow example, adjust mock resource
domhanak Apr 10, 2026
831cea5
Format newly added workflows
domhanak Apr 10, 2026
87931db
Add HttpOauth2Workflow for auth example + formatting
domhanak Apr 10, 2026
acc1a52
Add ListenWorkflow with tests and eventing setup
domhanak Apr 12, 2026
adf63f7
Add EmitWorkflow with tests, update wiremock version
domhanak Apr 12, 2026
ae0ad8c
Add ConditionalWorkflow with tests
domhanak Apr 12, 2026
f3c75d4
Add ForEachWorkflow with tests
domhanak Apr 12, 2026
227ec1c
Fix scope ofmock dependency on docs module
domhanak Apr 13, 2026
aa41b12
Fix formatting, add property needed for test execution
domhanak Apr 13, 2026
aa0a327
Add ParallelWorkflow with test
domhanak Apr 13, 2026
ad4be07
Comment unfinished Java examples
domhanak Apr 13, 2026
8be00a2
Add ContextWorkflow ample with te cookbook entry
domhanak Apr 24, 2026
e8ee924
Fix formatting of ContextWorkflow
domhanak Apr 24, 2026
5575d01
Improve Parall examples
domhanak Apr 24, 2026
c483859
Fix formatting of time in CronWorkflow
domhanak Apr 24, 2026
3b26d01
Fix Condtional example to use Java Function
domhanak Apr 24, 2026
9f6fb82
Reference issue in ForEachWorkflow example
domhanak Apr 24, 2026
2e10731
Fix OpenApiWorkflow
domhanak Apr 24, 2026
2da2b78
Fix HttpWorkflow to utilize query()
domhanak Apr 24, 2026
3128356
Remove obsolete comments from OpenApiWorkflow
domhanak Apr 24, 2026
67b3dc1
Fix HttpOauth2Workflow to use query()
domhanak Apr 24, 2026
7a723bf
Sanitaze the cookbook adoc
domhanak Apr 24, 2026
7d8c6bc
Fix ContextWorkflow entry
domhanak Apr 24, 2026
b496b0e
Fix formatting
domhanak Apr 24, 2026
dc5b89b
Update workflow starting, improve conWorkflow example
domhanak Apr 27, 2026
7e46edf
Remove commented TODOsrom adoc page
domhanak Apr 27, 2026
76161be
Add TaskContextWorkflow example
domhanak Apr 27, 2026
5c6c3d4
Fix ParallelWorkflow example to use fork()
domhanak May 11, 2026
fc45b3e
Fix forEach example to use OrdersPayload::orders
domhanak May 11, 2026
33177e9
Add example with Subflows and test
domhanak May 11, 2026
c9f53f8
Fix parentWorkflow test typos
domhanak May 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions docs/modules/ROOT/examples/org/acme/ConditionalWorkflow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.acme;

import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.*;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkiverse.flow.Flow;
import io.serverlessworkflow.api.types.FlowDirectiveEnum;
import io.serverlessworkflow.api.types.Workflow;
import io.serverlessworkflow.fluent.func.FuncWorkflowBuilder;

@ApplicationScoped
public class ConditionalWorkflow extends Flow {
@Override
public Workflow descriptor() {
return FuncWorkflowBuilder.workflow("conditional-routing")
.tasks(
// 1. Evaluate the condition and branch
Comment thread
domhanak marked this conversation as resolved.
switchWhenOrElse((ScorePayload p) -> p.score() >= 80, "approveTask", "rejectTask"),

// 2. Branch A: Score is 80 or higher
post("approveTask", "", "http://localhost:8089/approve")
.then(FlowDirectiveEnum.END), // equals to break; in switch cases

// 3. Branch B: Score is below 80
post("rejectTask", "", "http://localhost:8089/reject"))
.build();
}
}
24 changes: 24 additions & 0 deletions docs/modules/ROOT/examples/org/acme/ContextWorkflow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.acme;

import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.withContext;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkiverse.flow.Flow;
import io.serverlessworkflow.api.types.Workflow;
import io.serverlessworkflow.fluent.func.FuncWorkflowBuilder;
import io.serverlessworkflow.impl.WorkflowContextData;

@ApplicationScoped
public class ContextWorkflow extends Flow {
@Override
public Workflow descriptor() {
return FuncWorkflowBuilder.workflow("context-aware")
.tasks(
withContext((String input, WorkflowContextData contextData) -> {
System.out.println("Instance ID: " + contextData.instanceData().id());
return "Processed " + input;
}, String.class))
.build();
}
}
25 changes: 25 additions & 0 deletions docs/modules/ROOT/examples/org/acme/CronWorkflow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.acme;

// Static imports recommended for brevity:
import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.*;

import java.util.Date;
import java.util.Map;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkiverse.flow.Flow;
import io.serverlessworkflow.api.types.Workflow;
import io.serverlessworkflow.fluent.func.FuncWorkflowBuilder;

@ApplicationScoped
public class CronWorkflow extends Flow {
@Override
public Workflow descriptor() {
return FuncWorkflowBuilder.workflow("cron-workflow")
.schedule(cron("* * * * * ?")) // Every second
.tasks(
set(Map.of("message", "Executed Cron Workflow at: " + new Date())))
.build();
}
}
20 changes: 20 additions & 0 deletions docs/modules/ROOT/examples/org/acme/EmitWorkflow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.acme;

import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.emitJson;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkiverse.flow.Flow;
import io.serverlessworkflow.api.types.Workflow;
import io.serverlessworkflow.fluent.func.FuncWorkflowBuilder;

@ApplicationScoped
public class EmitWorkflow extends Flow {
@Override
public Workflow descriptor() {
return FuncWorkflowBuilder.workflow("emit-event-workflow", "org.acme", "1.0")
.tasks(
emitJson("orderPlaced", "com.petstore.order.placed.v1", Message.class))
.build();
}
}
5 changes: 5 additions & 0 deletions docs/modules/ROOT/examples/org/acme/ExampleEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.acme;

public record ExampleEvent(String eventName) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package org.acme;

import static com.github.tomakehurst.wiremock.client.WireMock.*;

import java.util.Map;

import com.github.tomakehurst.wiremock.WireMockServer;

import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;

public class ExampleWorkflowsWireMockResource implements QuarkusTestResourceLifecycleManager {

private WireMockServer wireMockServer;

@Override
public Map<String, String> start() {
// Start WireMock on the fixed port expected by the example workflows
wireMockServer = new WireMockServer(8089);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
wireMockServer = new WireMockServer(8089);
WireMockConfiguration config = WireMockConfiguration.wireMockConfig().dynamicPort();
wireMockServer = new WireMockServer(config);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And you will probably have to change how Quarkus reads this port like:

  return Map.of("wiremock.port", String.valueOf(wireMockServer.port()));

Then use it in the endpoint:

  .endpoint("http://localhost:${wiremock.port}/api/people")

We run tests in parallel in many instances.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, however this is not really a problem now as docs module is skipping tests. I can update here or in the follow-up that specifically aims to make these updates.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you run make test-all locally and passed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not like this though, will check, thanks for pointer

wireMockServer.start();

// ---------------------------------------------------------
// STUBS FOR HTTP WORKFLOW TESTS
// ---------------------------------------------------------
wireMockServer.stubFor(get(urlEqualTo("/api/people?search=luke"))
.withHeader("Accept", equalTo("application/json"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json")
.withBody("""
{
"count": 1,
"results": [
{
"name": "Luke Skywalker Mock"
}
]
}
""")));

// ---------------------------------------------------------
// STUBS FOR OPENAPI WORKFLOW TESTS
// ---------------------------------------------------------

// 1. Stub the Swagger Document
String mockedSwaggerDoc = """
{
"swagger": "2.0",
"info": { "version": "1.0.0", "title": "Mock Petstore" },
"host": "localhost:8089",
"basePath": "/v2",
"schemes": [ "http" ],
"paths": {
"/pet/findByStatus": {
"get": {
"operationId": "findPetsByStatus",
"parameters": [
{
"name": "status",
"in": "query",
"required": true,
"type": "string"
}
],
"responses": { "200": { "description": "OK" } }
}
}
}
}
""";

// Use any as the workflow first query with HEAD and the GET
wireMockServer.stubFor(any(urlEqualTo("/v2/swagger.json"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json")
.withBody(mockedSwaggerDoc)));

// 2. Stub the actual Petstore Endpoint defined in the document above
wireMockServer.stubFor(get(urlPathEqualTo("/v2/pet/findByStatus"))
.withQueryParam("status", equalTo("available"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json")
// The Petstore API returns a JSON array
.withBody("[{\"id\": 101, \"name\": \"Mocked Doggo\", \"status\": \"available\"}]")));

// ---------------------------------------------------------
// 3. STUB FOR LISTEN WORKFLOW (Event Wakeup)
// ---------------------------------------------------------
wireMockServer.stubFor(post(urlEqualTo("/start"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("{\"status\": \"started successfully\"}")));

// ---------------------------------------------------------
// 4. STUBS FOR CONDITIONAL WORKFLOW
// ---------------------------------------------------------
wireMockServer.stubFor(post(urlEqualTo("/approve"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("{}")));

wireMockServer.stubFor(post(urlEqualTo("/reject"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("{}")));

// ---------------------------------------------------------
// 5. STUBS FOR ITERATION WORKFLOW
// ---------------------------------------------------------
wireMockServer.stubFor(post(urlEqualTo("/process-order"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("{\"processed_orders_status\": \"success\"}")));

// ---------------------------------------------------------
// 7. STUBS FOR PARALLEL WORKFLOW
// ---------------------------------------------------------
wireMockServer.stubFor(post(urlEqualTo("/inventory-check"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("{}")));

wireMockServer.stubFor(post(urlEqualTo("/credit-check"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("{}")));

return Map.of(); // No properties to override
}

@Override
public void stop() {
if (wireMockServer != null) {
wireMockServer.stop();
}
}
}
24 changes: 24 additions & 0 deletions docs/modules/ROOT/examples/org/acme/ForEachWorkflow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.acme;

import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.*;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkiverse.flow.Flow;
import io.serverlessworkflow.api.types.Workflow;
import io.serverlessworkflow.fluent.func.FuncWorkflowBuilder;

@ApplicationScoped
public class ForEachWorkflow extends Flow {
@Override
public Workflow descriptor() {
return FuncWorkflowBuilder.workflow("foreach-workflow")
.tasks(
forEach(OrdersPayload::orders,
tasks(
post("$item.id",
"http://localhost:8089/process-order")
.exportAsTaskOutput())))
.build();
}
}
32 changes: 32 additions & 0 deletions docs/modules/ROOT/examples/org/acme/HttpOauth2Workflow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.acme;

import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.*;
import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.oauth2;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkiverse.flow.Flow;
import io.serverlessworkflow.api.types.OAuth2AuthenticationData;
import io.serverlessworkflow.api.types.Workflow;
import io.serverlessworkflow.fluent.func.FuncWorkflowBuilder;

@ApplicationScoped
public class HttpOauth2Workflow extends Flow {
@Override
public Workflow descriptor() {
return FuncWorkflowBuilder.workflow("oauth2-authentication-workflow")
.tasks(
call("getPets",
http()
.GET()
.query("petId", "${ .petId }")
.uri("http://localhost:8090/v2/pet",
oauth2("http://localhost:8090/realms/fake-authority",
OAuth2AuthenticationData.OAuth2AuthenticationDataGrant.CLIENT_CREDENTIALS,
"workflow-runtime-id",
"workflow-runtime-secret"))

))
.build();
}
}
31 changes: 31 additions & 0 deletions docs/modules/ROOT/examples/org/acme/HttpWorkflow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.acme;

// Static imports recommended for brevity:
import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.*;
import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.call;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkiverse.flow.Flow;
import io.serverlessworkflow.api.types.Workflow;
import io.serverlessworkflow.fluent.func.FuncWorkflowBuilder;

@ApplicationScoped
public class HttpWorkflow extends Flow {
@Override
public Workflow descriptor() {
return FuncWorkflowBuilder.workflow("http-with-query-headers", "org.acme", "1.0")
.tasks(
call("searchStarWarsCharacters",
http()
.GET()
// search value is taken from workflow input, jq expression is used
.query("search", "${ .searchQuery }")
.endpoint("http://localhost:8089/api/people")
// Accept value is taken from workflow input, jq expression is used
.header("Accept", "${ .acceptHeaderValue }")
// export the results of the GET request as taskOutput
.exportAsTaskOutput()))
.build();
}
}
24 changes: 24 additions & 0 deletions docs/modules/ROOT/examples/org/acme/ListenWorkflow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.acme;

import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.*;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkiverse.flow.Flow;
import io.serverlessworkflow.api.types.Workflow;
import io.serverlessworkflow.fluent.func.FuncWorkflowBuilder;

@ApplicationScoped
public class ListenWorkflow extends Flow {
@Override
public Workflow descriptor() {
return FuncWorkflowBuilder.workflow("listen-to-one-workflow")
.tasks(
// The workflow will pause here until the engine receives this specific CloudEvent
listen("waitForStartup", toOne("race.started.v1")),

// Once awakened, it executes this HTTP call
call("startup", post("", "http://localhost:8089/start")))
.build();
}
}
26 changes: 26 additions & 0 deletions docs/modules/ROOT/examples/org/acme/OpenApiWorkflow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.acme;

import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.*;

// Static imports recommended for brevity:
import java.util.Map;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkiverse.flow.Flow;
import io.serverlessworkflow.api.types.Workflow;
import io.serverlessworkflow.fluent.func.FuncWorkflowBuilder;

@ApplicationScoped
public class OpenApiWorkflow extends Flow {
@Override
public Workflow descriptor() {
return FuncWorkflowBuilder.workflow("openapi-call-workflow")
.tasks(
openapi()
.document("http://localhost:8089/v2/swagger.json")
.operation("findPetsByStatus")
.parameters(Map.of("status", "available")))
.build();
}
}
5 changes: 5 additions & 0 deletions docs/modules/ROOT/examples/org/acme/Order.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.acme;

public record Order(String id) {

}
Loading
Loading