-
Notifications
You must be signed in to change notification settings - Fork 26
docs: Implementation of Java DSL Cookbook #394
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
5fe056b
f33f2fe
0b15f1c
8e7b5f7
1f82815
831cea5
87931db
acc1a52
adf63f7
ae0ad8c
f3c75d4
227ec1c
aa41b12
aa0a327
ad4be07
8be00a2
e8ee924
5575d01
c483859
3b26d01
9f6fb82
2e10731
2da2b78
3128356
67b3dc1
7a723bf
7d8c6bc
b496b0e
dc5b89b
7e46edf
76161be
5c6c3d4
fc45b3e
33177e9
c9f53f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
| 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(); | ||
| } | ||
| } | ||
| 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(); | ||
| } | ||
| } |
| 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(); | ||
| } | ||
| } |
| 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(); | ||
| } | ||
| } |
| 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); | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: We run tests in parallel in many instances.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, however this is not really a problem now as
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you run
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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(); | ||||||||
| } | ||||||||
| } | ||||||||
| } | ||||||||
| 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(); | ||
| } | ||
| } |
| 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(); | ||
| } | ||
| } |
| 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(); | ||
| } | ||
| } |
| 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(); | ||
| } | ||
| } |
| 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(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| package org.acme; | ||
|
|
||
| public record Order(String id) { | ||
|
|
||
| } |
Uh oh!
There was an error while loading. Please reload this page.