diff --git a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/DoTaskBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/DoTaskBuilder.java index 4199811e..8e53c7ad 100644 --- a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/DoTaskBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/DoTaskBuilder.java @@ -18,30 +18,183 @@ import io.serverlessworkflow.fluent.spec.spi.DoFluent; import java.util.function.Consumer; +/** + * Builder for creating sequential task execution workflows using the "do" construct. + * + *
The {@code DoTaskBuilder} provides a fluent API for defining a sequence of tasks that execute + * in order. It serves as the primary mechanism for composing workflow logic, supporting all task + * types defined in the Serverless Workflow specification including HTTP calls, event emission, + * conditional branching, loops, and parallel execution. + * + *
This builder implements the "do" task pattern where tasks are executed sequentially unless + * explicitly configured for parallel execution (via {@link #fork(String, Consumer)}). Each task + * method returns the builder instance, enabling fluent method chaining. + * + *
{@code
+ * DoTaskBuilder tasks = new DoTaskBuilder(0)
+ * .set("initialize", set -> set
+ * .set("counter", "0")
+ * .set("status", "started"))
+ * .http("fetch-data", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://api.example.com/data"))
+ * .method("GET")))
+ * .switchCase("process-result", sw -> sw
+ * .switchItem(item -> item
+ * .when("${ .response.status == 200 }")
+ * .then("success-handler"))
+ * .switchItem(item -> item
+ * .when("${ .response.status >= 400 }")
+ * .then("error-handler")))
+ * .emit("notify", emit -> emit
+ * .event(event -> event
+ * .with(props -> props
+ * .type("workflow.completed")
+ * .source("workflow-engine"))));
+ * }
+ *
+ * Each task requires a unique name within the workflow. The builder uses an offset mechanism + * to support auto-generated names when tasks are added incrementally across multiple builder + * invocations. + * + *
This builder is not thread-safe. Task definitions should be constructed
+ * using a single thread. For concurrent workflow creation, use separate builder instances.
+ *
+ * @see BaseDoTaskBuilder for inherited task configuration methods
+ * @see TaskItemListBuilder for the underlying task list management
+ * @see DoFluent for the fluent task definition interface
+ * @see io.serverlessworkflow.api.types.DoTask for the resulting task model
+ * @since 1.0.0
+ */
public class DoTaskBuilder extends BaseDoTaskBuilder The offset ensures that auto-generated task names continue sequentially when tasks are
+ * added across multiple builder invocations.
+ *
+ * @param listSizeOffset the current number of tasks in the workflow, used for name generation
+ */
DoTaskBuilder(int listSizeOffset) {
super(new TaskItemListBuilder(listSizeOffset));
}
+ /**
+ * Returns this builder instance for method chaining.
+ *
+ * This method is part of the builder pattern implementation, enabling fluent method chaining
+ * in the type hierarchy.
+ *
+ * @return this DoTaskBuilder instance
+ */
@Override
protected DoTaskBuilder self() {
return this;
}
+ /**
+ * Adds an HTTP call task to the workflow.
+ *
+ * Creates a task that makes HTTP/REST API calls with configurable methods, headers, body,
+ * and authentication. Supports all standard HTTP methods (GET, POST, PUT, DELETE, etc.).
+ *
+ * Example:
+ * Creates a task that publishes events to configured event channels. Events can be used
+ * for workflow communication, notifications, or triggering other workflows.
+ *
+ * Example:
+ * Creates a task that iterates over a collection, executing a set of tasks for each item.
+ * Supports both sequential and parallel iteration modes.
+ *
+ * Example:
+ * Creates a task that executes multiple branches concurrently. Supports different
+ * competition modes (wait for all, wait for first, wait for N) and branch configuration.
+ *
+ * Example:
+ * Creates a task that waits for one or more events before continuing execution. Supports
+ * various consumption strategies (all, any, one) and event filtering.
+ *
+ * Example:
+ * Creates a task that explicitly raises an error, useful for validation failures or
+ * exceptional conditions that should halt workflow execution.
+ *
+ * Example:
+ * Creates a task that sets one or more workflow variables using expressions. This is the
+ * primary mechanism for manipulating workflow state.
+ *
+ * Example:
+ * Convenience method for setting a single variable with an expression. Equivalent to
+ * calling {@link #set(String, Consumer)} with a single assignment.
+ *
+ * Example:
+ * Creates a task that pauses workflow execution for a specified duration or until a
+ * specific time. Useful for implementing delays, rate limiting, or scheduled execution.
+ *
+ * Example:
+ * Creates a task that evaluates conditions and executes different branches based on the
+ * results. Supports multiple conditions with optional default case.
+ *
+ * Example:
+ * Creates a task that wraps other tasks with error handling logic. Supports catching
+ * specific error types, retry policies, and error recovery actions.
+ *
+ * Example:
+ * Creates a task that invokes an operation defined in an OpenAPI specification. The
+ * operation is resolved from the OpenAPI document and called with the provided parameters.
+ *
+ * Example:
+ * Creates a task that invokes a gRPC service method. Supports unary, client streaming,
+ * server streaming, and bidirectional streaming calls.
+ *
+ * Example:
+ * Creates a task that invokes another workflow as a sub-workflow. The sub-workflow executes
+ * independently and can return results to the parent workflow.
+ *
+ * Example:
+ * The {@code ForEachTaskBuilder} provides a fluent API for defining tasks that iterate over
+ * collections, executing a set of tasks for each item. This is the primary mechanism for
+ * implementing loops and batch processing in workflows.
+ *
+ * The builder supports:
+ * During iteration, the following variables are available in the workflow context:
+ * The iteration can be configured for different execution modes through the workflow
+ * configuration (not directly in this builder). Common modes include:
+ * This builder is not thread-safe. Each task definition should be
+ * constructed using a single thread.
+ *
+ * @param The task list builder is used to create the nested tasks that will be executed
+ * for each iteration.
+ *
+ * @param taskItemListBuilder the builder for creating nested tasks within the loop
+ */
public ForEachTaskBuilder(T taskItemListBuilder) {
super();
forTask = new ForTask();
@@ -38,30 +160,212 @@ public ForEachTaskBuilder(T taskItemListBuilder) {
super.setTask(forTask);
}
+ /**
+ * Returns this builder instance for method chaining.
+ *
+ * This method is part of the builder pattern implementation, enabling fluent method
+ * chaining in the type hierarchy.
+ *
+ * @return this ForEachTaskBuilder instance
+ */
protected ForEachTaskBuilder This variable will be available in the workflow context during task execution and
+ * can be referenced in expressions using {@code ${ .variableName }}.
+ *
+ * Examples:
+ * The expression should evaluate to an array or collection. Each element will be
+ * processed in order (or in parallel, depending on configuration).
+ *
+ * Examples:
+ * This optional variable provides access to the zero-based index of the current item
+ * in the collection. Useful for position-dependent logic or tracking progress.
+ *
+ * Examples:
+ * The while expression is evaluated before each iteration. If it evaluates to
+ * {@code false}, the iteration stops even if there are more items in the collection.
+ * This enables early termination based on runtime conditions.
+ *
+ * Examples:
+ * This method accepts a consumer that configures the nested tasks. These tasks have
+ * access to the iteration variables (item, index) and are executed for each element
+ * in the collection.
+ *
+ * Multiple calls to this method will accumulate tasks, allowing you to build up
+ * the iteration logic incrementally.
+ *
+ * Examples:
+ * This method finalizes the task configuration and creates the immutable ForTask
+ * instance with all iteration settings and nested tasks.
+ *
+ * Note: After calling this method, the builder should not be reused.
+ * Create a new builder instance for additional tasks.
+ *
+ * @return the configured ForTask ready for execution
+ */
public ForTask build() {
this.forTask.setFor(this.forTaskConfiguration);
return this.forTask;
diff --git a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/ForkTaskBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/ForkTaskBuilder.java
index 40ca8224..08a98a04 100644
--- a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/ForkTaskBuilder.java
+++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/ForkTaskBuilder.java
@@ -17,14 +17,180 @@
import io.serverlessworkflow.fluent.spec.spi.ForkTaskFluent;
+/**
+ * Builder for creating parallel execution tasks in workflows.
+ *
+ * The {@code ForkTaskBuilder} provides a fluent API for defining tasks that execute multiple
+ * branches concurrently. This is the primary mechanism for implementing parallel processing,
+ * fan-out patterns, and concurrent operations in workflows.
+ *
+ * The builder supports:
+ * The fork task supports two competition modes via {@link #compete(boolean)}:
+ * Each branch should have a unique name for identification and debugging. If no name is
+ * provided, an auto-generated name (e.g., "branch-0", "branch-1") will be used.
+ *
+ * When all branches complete (or the first in compete mode), their results are merged into
+ * the workflow context. Each branch's output is available under its branch name.
+ *
+ * This builder is not thread-safe. Each task definition should be
+ * constructed using a single thread. However, the branches themselves will execute concurrently
+ * at runtime.
+ *
+ * @see AbstractForkTaskBuilder for inherited fork configuration methods
+ * @see TaskItemListBuilder for branch task composition
+ * @see ForkTaskFluent for the fluent fork interface
+ * @see io.serverlessworkflow.api.types.ForkTask for the resulting task model
+ * @see DoTaskBuilder#fork(String, Consumer) for usage within workflows
+ * @since 1.0.0
+ */
public class ForkTaskBuilder extends AbstractForkTaskBuilder This method is part of the builder pattern implementation, enabling fluent method
+ * chaining in the type hierarchy.
+ *
+ * @return this ForkTaskBuilder instance
+ */
@Override
protected ForkTaskBuilder self() {
return this;
}
+ /**
+ * Creates a new task item list builder for branch task composition.
+ *
+ * This method is called internally to create builders for each branch's task sequence.
+ * The offset ensures proper task naming when branches are added incrementally.
+ *
+ * @param listOffsetSize the current number of tasks, used for name generation
+ * @return a new TaskItemListBuilder instance for the branch
+ */
@Override
protected TaskItemListBuilder newTaskItemListBuilder(int listOffsetSize) {
return new TaskItemListBuilder(listOffsetSize);
diff --git a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/SetTaskBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/SetTaskBuilder.java
index 0ce11ec4..956c6da6 100644
--- a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/SetTaskBuilder.java
+++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/SetTaskBuilder.java
@@ -19,32 +19,186 @@
import io.serverlessworkflow.api.types.SetTask;
import io.serverlessworkflow.api.types.SetTaskConfiguration;
+/**
+ * Builder for creating variable assignment tasks in workflows.
+ *
+ * The {@code SetTaskBuilder} provides a fluent API for defining tasks that set or modify
+ * workflow variables. This is the primary mechanism for manipulating workflow state, allowing
+ * you to assign values using expressions or key-value pairs.
+ *
+ * The builder supports two modes of operation:
+ * The builder uses the Serverless Workflow expression language (based on jq) for evaluating
+ * expressions. Expressions can:
+ * Variables set by this task are added to the workflow context and are available to
+ * subsequent tasks. Variables persist throughout the workflow execution unless explicitly
+ * modified or removed.
+ *
+ * This builder is not thread-safe. Each task definition should be
+ * constructed using a single thread.
+ *
+ * @see TaskBaseBuilder for inherited task configuration methods
+ * @see io.serverlessworkflow.api.types.SetTask for the resulting task model
+ * @see DoTaskBuilder#set(String, Consumer) for usage within workflows
+ * @since 1.0.0
+ */
public class SetTaskBuilder extends TaskBaseBuilder Initializes the builder with an empty set task configuration, ready to accept
+ * variable assignments via {@link #put(String, Object)} or {@link #expr(String)}.
+ */
public SetTaskBuilder() {
this.task = new SetTask();
this.setTaskConfiguration = new SetTaskConfiguration();
this.setTask(task);
}
+ /**
+ * Returns this builder instance for method chaining.
+ *
+ * This method is part of the builder pattern implementation, enabling fluent method
+ * chaining in the type hierarchy.
+ *
+ * @return this SetTaskBuilder instance
+ */
@Override
protected SetTaskBuilder self() {
return this;
}
+ /**
+ * Sets all variables using a single expression.
+ *
+ * This method allows you to define all variable assignments in one expression that
+ * evaluates to an object. The expression result should be an object whose properties
+ * become workflow variables.
+ *
+ * Note: Using this method replaces any previous {@link #put(String, Object)}
+ * calls. You should use either {@code expr()} or {@code put()}, not both.
+ *
+ * Examples:
+ * This method allows you to set individual workflow variables with specific values or
+ * expressions. Multiple calls to this method accumulate assignments that will all be
+ * executed when the task runs.
+ *
+ * The value can be:
+ * Examples:
+ * This method finalizes the task configuration and creates the immutable SetTask instance.
+ * If no expression was set via {@link #expr(String)}, the accumulated key-value pairs from
+ * {@link #put(String, Object)} calls are used.
+ *
+ * Note: After calling this method, the builder should not be reused.
+ * Create a new builder instance for additional tasks.
+ *
+ * @return the configured SetTask ready for execution
+ */
public SetTask build() {
if (this.task.getSet() == null) {
this.task.setSet(new Set().withSetTaskConfiguration(setTaskConfiguration));
diff --git a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/SwitchTaskBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/SwitchTaskBuilder.java
index 3db8c7a4..e7589f4f 100644
--- a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/SwitchTaskBuilder.java
+++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/SwitchTaskBuilder.java
@@ -22,12 +22,104 @@
import java.util.List;
import java.util.function.Consumer;
+/**
+ * Builder for creating conditional branching tasks in workflows.
+ *
+ * The {@code SwitchTaskBuilder} provides a fluent API for defining switch/case logic that
+ * evaluates conditions and executes different workflow paths based on the results. This is the
+ * primary mechanism for implementing conditional branching and decision logic in workflows.
+ *
+ * The builder supports multiple switch cases, each with:
+ * Switch cases are evaluated in the order they are defined. The first case whose condition
+ * evaluates to {@code true} is executed, and subsequent cases are skipped. This allows for
+ * priority-based routing where more specific conditions are checked before general ones.
+ *
+ * A default case can be defined by omitting the {@code when()} condition. This case will
+ * execute if no other conditions match. It's recommended to place the default case last,
+ * though it can appear anywhere in the switch definition.
+ *
+ * Conditions use the Serverless Workflow expression language (based on jq) and can:
+ * This builder is not thread-safe. Each task definition should be
+ * constructed using a single thread.
+ *
+ * @see TaskBaseBuilder for inherited task configuration methods
+ * @see SwitchTaskFluent for the fluent switch case interface
+ * @see io.serverlessworkflow.api.types.SwitchTask for the resulting task model
+ * @see DoTaskBuilder#switchCase(String, Consumer) for usage within workflows
+ * @since 1.0.0
+ */
public class SwitchTaskBuilder extends TaskBaseBuilder Initializes the builder with an empty list of switch cases, ready to accept
+ * case definitions via {@link #on(String, Consumer)}.
+ */
SwitchTaskBuilder() {
super();
this.switchTask = new SwitchTask();
@@ -35,11 +127,58 @@ public class SwitchTaskBuilder extends TaskBaseBuilder This method is part of the builder pattern implementation, enabling fluent method
+ * chaining in the type hierarchy.
+ *
+ * @return this SwitchTaskBuilder instance
+ */
@Override
protected SwitchTaskBuilder self() {
return this;
}
+ /**
+ * Adds a switch case to the conditional branching logic.
+ *
+ * Each switch case consists of an optional condition and a target. Cases are evaluated
+ * in the order they are added, and the first matching case is executed.
+ *
+ * The case name is used for identification and debugging purposes. If blank or null,
+ * a default name will be generated.
+ *
+ * Examples:
+ * This method is useful for validation or debugging to ensure the expected number
+ * of cases have been configured.
+ *
+ * @return the count of switch cases currently defined in this builder
+ */
@Override
public int switchItemCount() {
return this.switchItems.size();
}
+ /**
+ * Builds and returns the configured SwitchTask.
+ *
+ * This method finalizes the task configuration and creates the immutable SwitchTask
+ * instance with all defined switch cases. The cases will be evaluated in the order they
+ * were added to the builder.
+ *
+ * Note: After calling this method, the builder should not be reused.
+ * Create a new builder instance for additional tasks.
+ *
+ * @return the configured SwitchTask ready for execution
+ */
public SwitchTask build() {
this.switchTask.setSwitch(this.switchItems);
return this.switchTask;
diff --git a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/WorkflowBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/WorkflowBuilder.java
index fd74a283..419a66e6 100644
--- a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/WorkflowBuilder.java
+++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/WorkflowBuilder.java
@@ -17,35 +17,191 @@
import java.util.UUID;
+/**
+ * Primary entry point for building Serverless Workflow specifications using a fluent API.
+ *
+ * The {@code WorkflowBuilder} provides a type-safe, chainable interface for constructing
+ * workflow definitions programmatically. It serves as the main facade for the fluent API,
+ * offering factory methods to create workflow instances with various levels of detail.
+ *
+ * This builder follows the builder pattern and supports method chaining for a natural,
+ * readable workflow definition experience. All builder methods return {@code this} to enable
+ * fluent composition.
+ *
+ * The builder provides multiple factory methods to accommodate different use cases:
+ * This builder is not thread-safe. Each workflow definition should be
+ * constructed using a single thread. For concurrent workflow creation, use separate builder
+ * instances.
+ *
+ * @see BaseWorkflowBuilder for inherited configuration methods
+ * @see DoTaskBuilder for task composition
+ * @see io.serverlessworkflow.api.types.Workflow for the resulting workflow model
+ * @since 1.0.0
+ */
public class WorkflowBuilder
extends BaseWorkflowBuilder This method is called internally to create task builders with the correct naming offset,
+ * ensuring that auto-generated task names continue sequentially across multiple task additions.
+ *
+ * @param listSizeOffset the current number of tasks in the workflow's task list
+ * @return a new DoTaskBuilder instance configured with the specified offset
+ */
@Override
protected DoTaskBuilder newDo(int listSizeOffset) {
return new DoTaskBuilder(listSizeOffset);
}
+ /**
+ * Creates a new workflow builder with fully specified metadata.
+ *
+ * This is the most explicit factory method, allowing complete control over the workflow's
+ * identity. Use this when you need to specify all three components of the workflow identifier.
+ *
+ * Example:
+ * Uses the default version ({@value BaseWorkflowBuilder#DEFAULT_VERSION}) when version
+ * management is not critical or during development.
+ *
+ * Example:
+ * Uses default namespace ({@value BaseWorkflowBuilder#DEFAULT_NAMESPACE}) and version
+ * ({@value BaseWorkflowBuilder#DEFAULT_VERSION}). Suitable for simple workflows or prototyping.
+ *
+ * Example:
+ * Generates a random UUID as the workflow name, useful for temporary workflows, testing,
+ * or when the workflow name will be set later through other means.
+ *
+ * Example:
+ * This method is part of the builder pattern implementation, enabling fluent method chaining
+ * in the type hierarchy.
+ *
+ * @return this WorkflowBuilder instance
+ */
@Override
protected WorkflowBuilder self() {
return this;
{@code
+ * builder.http("fetch-user", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://api.example.com/users/${.userId}"))
+ * .method("GET")
+ * .headers(headers -> headers
+ * .header("Accept", "application/json"))));
+ * }
+ *
+ * @param name the unique name for this task
+ * @param itemsConfigurer consumer to configure the HTTP call task
+ * @return this builder for method chaining
+ * @see CallHttpTaskBuilder for HTTP task configuration options
+ */
@Override
public DoTaskBuilder http(String name, Consumer{@code
+ * builder.emit("notify-completion", emit -> emit
+ * .event(event -> event
+ * .with(props -> props
+ * .type("order.completed")
+ * .source("order-service")
+ * .data("${ .orderResult }"))));
+ * }
+ *
+ * @param name the unique name for this task
+ * @param itemsConfigurer consumer to configure the emit task
+ * @return this builder for method chaining
+ * @see EmitTaskBuilder for event emission configuration
+ */
@Override
public DoTaskBuilder emit(String name, Consumer{@code
+ * builder.forEach("process-items", forEach -> forEach
+ * .for_("item")
+ * .in("${ .items }")
+ * .do_(tasks -> tasks
+ * .http("process-item", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://api.example.com/process"))
+ * .method("POST")
+ * .body("${ .item }")))));
+ * }
+ *
+ * @param name the unique name for this task
+ * @param itemsConfigurer consumer to configure the for-each task
+ * @return this builder for method chaining
+ * @see ForEachTaskBuilder for iteration configuration
+ */
@Override
public DoTaskBuilder forEach(
String name, Consumer{@code
+ * builder.fork("parallel-processing", fork -> fork
+ * .compete(CompetitionMode.ALL_OF)
+ * .branch("branch-1", branch -> branch
+ * .http("call-service-1", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://service1.example.com")))))
+ * .branch("branch-2", branch -> branch
+ * .http("call-service-2", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://service2.example.com"))))));
+ * }
+ *
+ * @param name the unique name for this task
+ * @param itemsConfigurer consumer to configure the fork task
+ * @return this builder for method chaining
+ * @see ForkTaskBuilder for parallel execution configuration
+ */
@Override
public DoTaskBuilder fork(String name, Consumer{@code
+ * builder.listen("wait-for-approval", listen -> listen
+ * .to(to -> to
+ * .one(one -> one
+ * .with(props -> props
+ * .type("approval.granted")
+ * .source("approval-service")))));
+ * }
+ *
+ * @param name the unique name for this task
+ * @param itemsConfigurer consumer to configure the listen task
+ * @return this builder for method chaining
+ * @see ListenTaskBuilder for event listening configuration
+ */
@Override
public DoTaskBuilder listen(String name, Consumer{@code
+ * builder.raise("validation-error", raise -> raise
+ * .error(error -> error
+ * .type("ValidationError")
+ * .status(400)
+ * .title("Invalid Input")
+ * .detail("${ .validationMessage }")));
+ * }
+ *
+ * @param name the unique name for this task
+ * @param itemsConfigurer consumer to configure the raise task
+ * @return this builder for method chaining
+ * @see RaiseTaskBuilder for error raising configuration
+ */
@Override
public DoTaskBuilder raise(String name, Consumer{@code
+ * builder.set("calculate-total", set -> set
+ * .set("subtotal", "${ .items | map(.price) | add }")
+ * .set("tax", "${ .subtotal * 0.1 }")
+ * .set("total", "${ .subtotal + .tax }"));
+ * }
+ *
+ * @param name the unique name for this task
+ * @param itemsConfigurer consumer to configure the set task
+ * @return this builder for method chaining
+ * @see SetTaskBuilder for variable assignment configuration
+ */
@Override
public DoTaskBuilder set(String name, Consumer{@code
+ * builder.set("initialize-counter", "${ 0 }");
+ * }
+ *
+ * @param name the unique name for this task
+ * @param expr the expression to evaluate and assign
+ * @return this builder for method chaining
+ * @see #set(String, Consumer) for multiple assignments
+ */
@Override
public DoTaskBuilder set(String name, String expr) {
this.listBuilder().set(name, expr);
return this;
}
+ /**
+ * Adds a wait/delay task to the workflow.
+ *
+ * {@code
+ * builder.wait("delay-processing", wait -> wait
+ * .duration("PT30S")); // Wait 30 seconds
+ * }
+ *
+ * @param name the unique name for this task
+ * @param itemsConfigurer consumer to configure the wait task
+ * @return this builder for method chaining
+ * @see WaitTaskBuilder for wait configuration
+ */
@Override
public DoTaskBuilder wait(String name, Consumer{@code
+ * builder.switchCase("route-by-status", sw -> sw
+ * .switchItem(item -> item
+ * .when("${ .status == 'approved' }")
+ * .then("process-approval"))
+ * .switchItem(item -> item
+ * .when("${ .status == 'rejected' }")
+ * .then("process-rejection"))
+ * .switchItem(item -> item
+ * .then("process-pending"))); // Default case
+ * }
+ *
+ * @param name the unique name for this task
+ * @param itemsConfigurer consumer to configure the switch task
+ * @return this builder for method chaining
+ * @see SwitchTaskBuilder for conditional branching configuration
+ */
@Override
public DoTaskBuilder switchCase(String name, Consumer{@code
+ * builder.tryCatch("safe-http-call", tryCatch -> tryCatch
+ * .try_(tasks -> tasks
+ * .http("risky-call", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://api.example.com/data")))))
+ * .catch_(catch_ -> catch_
+ * .when(when -> when
+ * .with(error -> error
+ * .type("HttpError")))
+ * .do_(tasks -> tasks
+ * .set("error-handled", "${ true }"))));
+ * }
+ *
+ * @param name the unique name for this task
+ * @param itemsConfigurer consumer to configure the try-catch task
+ * @return this builder for method chaining
+ * @see TryTaskBuilder for error handling configuration
+ */
@Override
public DoTaskBuilder tryCatch(
String name, Consumer{@code
+ * builder.openapi("get-user", openapi -> openapi
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .operationId("getUserById"))
+ * .parameters(params -> params
+ * .parameter("userId", "${ .userId }"))));
+ * }
+ *
+ * @param name the unique name for this task
+ * @param itemsConfigurer consumer to configure the OpenAPI call task
+ * @return this builder for method chaining
+ * @see CallOpenAPITaskBuilder for OpenAPI call configuration
+ */
@Override
public DoTaskBuilder openapi(String name, Consumer{@code
+ * builder.grpc("call-user-service", grpc -> grpc
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("grpc://user-service:50051")
+ * .method("GetUser"))
+ * .arguments(args -> args
+ * .argument("userId", "${ .userId }"))));
+ * }
+ *
+ * @param name the unique name for this task
+ * @param itemsConfigurer consumer to configure the gRPC call task
+ * @return this builder for method chaining
+ * @see CallGrpcTaskBuilder for gRPC call configuration
+ */
@Override
public DoTaskBuilder grpc(String name, Consumer{@code
+ * builder.workflow("invoke-validation", workflow -> workflow
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .workflowId("validation-workflow"))
+ * .input("${ .dataToValidate }")));
+ * }
+ *
+ * @param name the unique name for this task
+ * @param itemsConfigurer consumer to configure the workflow call task
+ * @return this builder for method chaining
+ * @see WorkflowTaskBuilder for sub-workflow invocation configuration
+ */
@Override
public DoTaskBuilder workflow(String name, Consumer
+ *
+ *
+ * Usage Examples
+ *
+ * Basic Iteration
+ * {@code
+ * ForEachTaskBuilder
+ *
+ * Iteration with Index
+ * {@code
+ * ForEachTaskBuilder
+ *
+ * Conditional Iteration
+ * {@code
+ * ForEachTaskBuilder
+ *
+ * Batch Processing with Aggregation
+ * {@code
+ * ForEachTaskBuilder
+ *
+ * Iteration Variables
+ *
+ *
+ *
+ * Execution Modes
+ *
+ *
+ *
+ * Thread Safety
+ * {@code
+ * // Simple item reference
+ * builder.each("item")
+ * .in("${ .items }")
+ * .tasks(tasks -> tasks
+ * .set("process", set -> set
+ * .put("value", "${ .item.value }")));
+ *
+ * // Descriptive variable names
+ * builder.each("order")
+ * .in("${ .orders }")
+ * .tasks(tasks -> tasks
+ * .http("process-order", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://api.example.com/orders/${.order.id}")))));
+ * }
+ *
+ * @param each the variable name for the current item; must be a valid identifier
+ * @return this builder for method chaining
+ * @see #in(String) for specifying the collection to iterate
+ * @see #at(String) for accessing the iteration index
+ */
public ForEachTaskBuilder{@code
+ * // Iterate over a workflow variable
+ * builder.in("${ .items }");
+ *
+ * // Iterate over a filtered collection
+ * builder.in("${ .orders | select(.status == 'pending') }");
+ *
+ * // Iterate over a range
+ * builder.in("${ range(1; 11) }"); // Numbers 1 through 10
+ *
+ * // Iterate over transformed data
+ * builder.in("${ .users | map(.id) }");
+ * }
+ *
+ * @param in the expression that evaluates to the collection to iterate
+ * @return this builder for method chaining
+ * @see #each(String) for naming the current item variable
+ */
public ForEachTaskBuilder{@code
+ * // Track iteration position
+ * builder.each("item")
+ * .in("${ .items }")
+ * .at("index")
+ * .tasks(tasks -> tasks
+ * .set("add-position", set -> set
+ * .put("position", "${ .index + 1 }")
+ * .put("isFirst", "${ .index == 0 }")
+ * .put("isLast", "${ .index == (.items | length) - 1 }")));
+ *
+ * // Conditional logic based on position
+ * builder.each("record")
+ * .in("${ .records }")
+ * .at("idx")
+ * .tasks(tasks -> tasks
+ * .switchCase("check-position", sw -> sw
+ * .on("first", switchCase -> switchCase
+ * .when("${ .idx == 0 }")
+ * .then("initialize-batch"))
+ * .on("last", switchCase -> switchCase
+ * .when("${ .idx == (.records | length) - 1 }")
+ * .then("finalize-batch"))));
+ * }
+ *
+ * @param at the variable name for the iteration index; must be a valid identifier
+ * @return this builder for method chaining
+ * @see #each(String) for the current item variable
+ */
public ForEachTaskBuilder{@code
+ * // Stop when a condition is met
+ * builder.each("item")
+ * .in("${ .items }")
+ * .whileC("${ .item.status == 'active' }")
+ * .tasks(tasks -> tasks
+ * .http("process", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://api.example.com/process")))));
+ *
+ * // Limit iterations based on accumulated value
+ * builder.each("order")
+ * .in("${ .orders }")
+ * .whileC("${ .totalProcessed < 100 }")
+ * .tasks(tasks -> tasks
+ * .set("accumulate", set -> set
+ * .put("totalProcessed", "${ .totalProcessed + .order.amount }")));
+ *
+ * // Time-based termination
+ * builder.each("task")
+ * .in("${ .tasks }")
+ * .whileC("${ now() < .deadline }")
+ * .tasks(tasks -> tasks
+ * .http("execute-task", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://api.example.com/tasks/${.task.id}")))));
+ * }
+ *
+ * @param expression the conditional expression; iteration continues while this is true
+ * @return this builder for method chaining
+ */
public ForEachTaskBuilder{@code
+ * // Simple processing
+ * builder.tasks(tasks -> tasks
+ * .http("process-item", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://api.example.com/process"))
+ * .method("POST")
+ * .body("${ .item }"))));
+ *
+ * // Multiple tasks per iteration
+ * builder.tasks(tasks -> tasks
+ * .set("validate", set -> set
+ * .put("isValid", "${ .item.value != null }"))
+ * .switchCase("check-valid", sw -> sw
+ * .on("valid", switchCase -> switchCase
+ * .when("${ .isValid }")
+ * .then("process-valid"))
+ * .on("invalid", switchCase -> switchCase
+ * .when("${ !.isValid }")
+ * .then("handle-invalid")))
+ * .emit("notify", emit -> emit
+ * .event(event -> event
+ * .with(props -> props
+ * .type("item.processed")
+ * .data("${ .item }")))));
+ * }
+ *
+ * @param doBuilderConsumer consumer to configure the tasks for each iteration
+ * @return this builder for method chaining
+ */
public ForEachTaskBuilder
+ *
+ *
+ * Usage Examples
+ *
+ * Basic Parallel Execution
+ * {@code
+ * ForkTaskBuilder builder = new ForkTaskBuilder()
+ * .compete(false) // Wait for all branches
+ * .branch("fetch-user", branch -> branch
+ * .http("get-user", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://api.example.com/users/${.userId}"))
+ * .method("GET"))))
+ * .branch("fetch-orders", branch -> branch
+ * .http("get-orders", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://api.example.com/orders?userId=${.userId}"))
+ * .method("GET"))))
+ * .branch("fetch-preferences", branch -> branch
+ * .http("get-preferences", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://api.example.com/preferences/${.userId}"))
+ * .method("GET"))));
+ * }
+ *
+ * Race Condition (First to Complete)
+ * {@code
+ * ForkTaskBuilder builder = new ForkTaskBuilder()
+ * .compete(true) // Complete when first branch finishes
+ * .branch("primary-service", branch -> branch
+ * .http("call-primary", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://primary.example.com/api"))
+ * .method("GET"))))
+ * .branch("backup-service", branch -> branch
+ * .http("call-backup", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://backup.example.com/api"))
+ * .method("GET"))));
+ * }
+ *
+ * Complex Branch Processing
+ * {@code
+ * ForkTaskBuilder builder = new ForkTaskBuilder()
+ * .compete(false)
+ * .branch("process-images", branch -> branch
+ * .forEach("process-image", forEach -> forEach
+ * .each("image")
+ * .in("${ .images }")
+ * .tasks(tasks -> tasks
+ * .http("resize-image", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://image-service.example.com/resize"))
+ * .method("POST")
+ * .body("${ .image }"))))))
+ * .branch("process-videos", branch -> branch
+ * .forEach("process-video", forEach -> forEach
+ * .each("video")
+ * .in("${ .videos }")
+ * .tasks(tasks -> tasks
+ * .http("transcode-video", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://video-service.example.com/transcode"))
+ * .method("POST")
+ * .body("${ .video }"))))))
+ * .branch("generate-metadata", branch -> branch
+ * .set("create-metadata", set -> set
+ * .put("timestamp", "${ now() }")
+ * .put("totalFiles", "${ (.images | length) + (.videos | length) }")));
+ * }
+ *
+ * Fan-Out with Aggregation
+ * {@code
+ * ForkTaskBuilder builder = new ForkTaskBuilder()
+ * .compete(false)
+ * .branch("validate-email", branch -> branch
+ * .http("check-email", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://validation.example.com/email"))
+ * .method("POST")
+ * .body("${ { email: .user.email } }"))))
+ * .branch("validate-phone", branch -> branch
+ * .http("check-phone", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://validation.example.com/phone"))
+ * .method("POST")
+ * .body("${ { phone: .user.phone } }"))))
+ * .branch("validate-address", branch -> branch
+ * .http("check-address", http -> http
+ * .call(call -> call
+ * .with(endpoint -> endpoint
+ * .uri("https://validation.example.com/address"))
+ * .method("POST")
+ * .body("${ { address: .user.address } }"))));
+ * // After fork completes, all validation results are available in context
+ * }
+ *
+ * Competition Modes
+ *
+ *
+ *
+ * Branch Naming
+ * Result Handling
+ * Thread Safety
+ *
+ *
+ *
+ * Usage Examples
+ *
+ * Key-Value Assignments
+ * {@code
+ * SetTaskBuilder builder = new SetTaskBuilder()
+ * .put("userId", "${ .request.userId }")
+ * .put("timestamp", "${ now() }")
+ * .put("status", "processing")
+ * .put("counter", 0);
+ * }
+ *
+ * Expression-Based Assignment
+ * {@code
+ * SetTaskBuilder builder = new SetTaskBuilder()
+ * .expr("${ { userId: .request.userId, timestamp: now(), status: 'processing' } }");
+ * }
+ *
+ * Complex Calculations
+ * {@code
+ * SetTaskBuilder builder = new SetTaskBuilder()
+ * .put("subtotal", "${ .items | map(.price) | add }")
+ * .put("tax", "${ .subtotal * 0.1 }")
+ * .put("shipping", "${ .subtotal > 100 ? 0 : 10 }")
+ * .put("total", "${ .subtotal + .tax + .shipping }");
+ * }
+ *
+ * Expression Language
+ *
+ *
+ *
+ * Variable Scope
+ * Thread Safety
+ * {@code
+ * // Create multiple variables from an object expression
+ * builder.expr("${ { userId: .request.userId, status: 'active', count: 0 } }");
+ *
+ * // Transform and assign the entire context
+ * builder.expr("${ . | { userId: .user.id, email: .user.email } }");
+ *
+ * // Merge with existing context
+ * builder.expr("${ . + { processed: true, timestamp: now() } }");
+ * }
+ *
+ * @param expression the expression to evaluate; must evaluate to an object whose properties
+ * will be set as workflow variables
+ * @return this builder for method chaining
+ * @see #put(String, Object) for individual variable assignments
+ */
public SetTaskBuilder expr(String expression) {
this.task.setSet(new Set().withString(expression));
return this;
}
+ /**
+ * Adds a single variable assignment to the task.
+ *
+ *
+ *
+ *
+ * {@code
+ * // Literal values
+ * builder.put("status", "pending")
+ * .put("retryCount", 0)
+ * .put("enabled", true);
+ *
+ * // Expression values
+ * builder.put("userId", "${ .request.userId }")
+ * .put("timestamp", "${ now() }")
+ * .put("total", "${ .items | map(.price) | add }");
+ *
+ * // Complex values
+ * builder.put("metadata", Map.of("source", "api", "version", "1.0"))
+ * .put("tags", List.of("important", "urgent"));
+ * }
+ *
+ * @param key the name of the workflow variable to set
+ * @param value the value to assign; can be a literal or an expression string
+ * @return this builder for method chaining
+ * @see #expr(String) for setting all variables with a single expression
+ */
public SetTaskBuilder put(String key, Object value) {
setTaskConfiguration.withAdditionalProperty(key, value);
return this;
}
+ /**
+ * Builds and returns the configured SetTask.
+ *
+ *
+ *
+ *
+ * Usage Examples
+ *
+ * Simple Conditional Branching
+ * {@code
+ * SwitchTaskBuilder builder = new SwitchTaskBuilder()
+ * .on("approved", switchCase -> switchCase
+ * .when("${ .status == 'approved' }")
+ * .then("process-approval"))
+ * .on("rejected", switchCase -> switchCase
+ * .when("${ .status == 'rejected' }")
+ * .then("process-rejection"))
+ * .on("default", switchCase -> switchCase
+ * .then("process-pending")); // Default case (no when condition)
+ * }
+ *
+ * Multiple Conditions with Complex Logic
+ * {@code
+ * SwitchTaskBuilder builder = new SwitchTaskBuilder()
+ * .on("high-priority", switchCase -> switchCase
+ * .when("${ .priority == 'high' and .amount > 1000 }")
+ * .then("expedited-processing"))
+ * .on("medium-priority", switchCase -> switchCase
+ * .when("${ .priority == 'medium' or (.priority == 'high' and .amount <= 1000) }")
+ * .then("standard-processing"))
+ * .on("low-priority", switchCase -> switchCase
+ * .when("${ .priority == 'low' }")
+ * .then("batch-processing"));
+ * }
+ *
+ * Range-Based Routing
+ * {@code
+ * SwitchTaskBuilder builder = new SwitchTaskBuilder()
+ * .on("small-order", switchCase -> switchCase
+ * .when("${ .total < 100 }")
+ * .then("small-order-handler"))
+ * .on("medium-order", switchCase -> switchCase
+ * .when("${ .total >= 100 and .total < 1000 }")
+ * .then("medium-order-handler"))
+ * .on("large-order", switchCase -> switchCase
+ * .when("${ .total >= 1000 }")
+ * .then("large-order-handler"));
+ * }
+ *
+ * Evaluation Order
+ * Default Case
+ * Expression Language
+ *
+ *
+ *
+ * Thread Safety
+ * {@code
+ * // Case with condition
+ * builder.on("approved-case", switchCase -> switchCase
+ * .when("${ .status == 'approved' }")
+ * .then("approval-handler"));
+ *
+ * // Default case (no condition)
+ * builder.on("default-case", switchCase -> switchCase
+ * .then("default-handler"));
+ *
+ * // Complex condition
+ * builder.on("priority-case", switchCase -> switchCase
+ * .when("${ .priority == 'high' and .amount > 1000 and .customer.vip }")
+ * .then("vip-processing"));
+ *
+ * // Inline task execution
+ * builder.on("inline-case", switchCase -> switchCase
+ * .when("${ .needsProcessing }")
+ * .do_(tasks -> tasks
+ * .set("processed", set -> set
+ * .put("timestamp", "${ now() }")
+ * .put("status", "completed"))));
+ * }
+ *
+ * @param name the name for this switch case; used for identification and debugging
+ * @param switchCaseConsumer consumer to configure the switch case with condition and target
+ * @return this builder for method chaining
+ * @see SwitchTaskFluent.SwitchCaseBuilder for case configuration options
+ */
@Override
public SwitchTaskBuilder on(
String name, ConsumerUsage Example
+ * {@code
+ * Workflow workflow = WorkflowBuilder.workflow("order-processing", "com.example", "1.0.0")
+ * .document(doc -> doc
+ * .title("Order Processing Workflow")
+ * .description("Handles customer order processing"))
+ * .input(input -> input
+ * .schema(schema -> schema
+ * .format("application/json")))
+ * .tasks(tasks -> tasks
+ * .set(set -> set
+ * .name("validate-order")
+ * .set("isValid", "${ .order.total > 0 }"))
+ * .switchTask(sw -> sw
+ * .name("check-validation")
+ * .switchItem(item -> item
+ * .when("${ .isValid }")
+ * .then("process-order"))
+ * .switchItem(item -> item
+ * .when("${ !.isValid }")
+ * .then("reject-order"))))
+ * .output(output -> output
+ * .as("${ .result }"))
+ * .build();
+ * }
+ *
+ * Factory Methods
+ *
+ *
+ *
+ * Thread Safety
+ * {@code
+ * WorkflowBuilder builder = WorkflowBuilder.workflow(
+ * "payment-processing",
+ * "com.example.payments",
+ * "2.1.0"
+ * );
+ * }
+ *
+ * @param name the unique name identifying this workflow
+ * @param namespace the namespace for organizing related workflows (e.g., "com.example")
+ * @param version the semantic version string (e.g., "1.0.0")
+ * @return a new WorkflowBuilder instance ready for configuration
+ * @see #workflow(String, String) for default version
+ * @see #workflow(String) for default namespace and version
+ */
public static WorkflowBuilder workflow(
final String name, final String namespace, final String version) {
return new WorkflowBuilder(name, namespace, version);
}
+ /**
+ * Creates a new workflow builder with a default version.
+ *
+ * {@code
+ * WorkflowBuilder builder = WorkflowBuilder.workflow(
+ * "order-fulfillment",
+ * "com.example.orders"
+ * );
+ * // Version defaults to "0.0.1"
+ * }
+ *
+ * @param name the unique name identifying this workflow
+ * @param namespace the namespace for organizing related workflows
+ * @return a new WorkflowBuilder instance with default version
+ * @see #workflow(String, String, String) for explicit version control
+ */
public static WorkflowBuilder workflow(final String name, final String namespace) {
return new WorkflowBuilder(name, namespace, DEFAULT_VERSION);
}
+ /**
+ * Creates a new workflow builder with default namespace and version.
+ *
+ * {@code
+ * WorkflowBuilder builder = WorkflowBuilder.workflow("hello-world");
+ * // Namespace defaults to "org.acme"
+ * // Version defaults to "0.0.1"
+ * }
+ *
+ * @param name the unique name identifying this workflow
+ * @return a new WorkflowBuilder instance with default namespace and version
+ * @see #workflow(String, String) for custom namespace
+ */
public static WorkflowBuilder workflow(final String name) {
return new WorkflowBuilder(name, DEFAULT_NAMESPACE, DEFAULT_VERSION);
}
+ /**
+ * Creates a new workflow builder with auto-generated name and default metadata.
+ *
+ * {@code
+ * WorkflowBuilder builder = WorkflowBuilder.workflow();
+ * // Name is auto-generated UUID
+ * // Namespace defaults to "org.acme"
+ * // Version defaults to "0.0.1"
+ * }
+ *
+ * @return a new WorkflowBuilder instance with auto-generated UUID name
+ * @see #workflow(String) for explicit naming
+ */
public static WorkflowBuilder workflow() {
return new WorkflowBuilder(UUID.randomUUID().toString(), DEFAULT_NAMESPACE, DEFAULT_VERSION);
}
+ /**
+ * Returns this builder instance for method chaining.
+ *
+ *