Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import io.serverlessworkflow.fluent.func.spi.FuncDoFluent;
import io.serverlessworkflow.fluent.func.spi.FuncTaskTransformations;
import io.serverlessworkflow.fluent.spec.BaseDoTaskBuilder;
import io.serverlessworkflow.fluent.spec.WorkflowTaskBuilder;
import java.util.function.Consumer;

public class FuncDoTaskBuilder extends BaseDoTaskBuilder<FuncDoTaskBuilder, FuncTaskItemListBuilder>
Expand Down Expand Up @@ -96,4 +97,10 @@ public FuncDoTaskBuilder openapi(
this.listBuilder().openapi(name, itemsConfigurer);
return this;
}

@Override
public FuncDoTaskBuilder workflow(String name, Consumer<WorkflowTaskBuilder> itemsConfigurer) {
this.listBuilder().workflow(name, itemsConfigurer);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import io.serverlessworkflow.api.types.TaskItem;
import io.serverlessworkflow.fluent.func.spi.FuncDoFluent;
import io.serverlessworkflow.fluent.spec.BaseTaskItemListBuilder;
import io.serverlessworkflow.fluent.spec.TaskItemListBuilder;
import io.serverlessworkflow.fluent.spec.WorkflowTaskBuilder;
import java.util.List;
import java.util.function.Consumer;

Expand Down Expand Up @@ -154,4 +156,17 @@ public FuncTaskItemListBuilder openapi(

return this.addTaskItem(new TaskItem(name, task));
}

@Override
public FuncTaskItemListBuilder workflow(
String name, Consumer<WorkflowTaskBuilder> itemsConfigurer) {
final TaskItemListBuilder delegate = new TaskItemListBuilder(this.mutableList().size());
delegate.workflow(name, itemsConfigurer);
final List<TaskItem> taskItems = delegate.build();
if (taskItems.size() != 1) {
throw new IllegalStateException(
"Expected workflow delegate to build exactly 1 TaskItem, but got " + taskItems.size());
}
return addTaskItem(taskItems.get(0));
}
Comment on lines +161 to +171
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

This implementation builds a separate spec TaskItemListBuilder, then builds a list only to extract a single TaskItem. That indirection adds unnecessary complexity and allocation, and also makes failures harder to diagnose. Prefer constructing the workflow task item directly (e.g., build a WorkflowTaskBuilder, apply itemsConfigurer, then add a single TaskItem), and if you keep the current approach, include name in the exception message to improve debuggability.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

what are we trying to do here?

}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import io.serverlessworkflow.fluent.spec.ScheduleBuilder;
import io.serverlessworkflow.fluent.spec.TimeoutBuilder;
import io.serverlessworkflow.fluent.spec.configurers.AuthenticationConfigurer;
import io.serverlessworkflow.fluent.spec.configurers.WorkflowConfigurer;
import io.serverlessworkflow.fluent.spec.dsl.DSL;
import io.serverlessworkflow.fluent.spec.dsl.UseSpec;
import io.serverlessworkflow.impl.TaskContextData;
Expand Down Expand Up @@ -1072,6 +1073,30 @@ public static FuncTaskConfigurer set(Map<String, Object> map) {
return list -> list.set(s -> s.expr(map));
}

/**
* Create a {@link FuncTaskConfigurer} that adds a workflow subflow task.
*
* @param configurer configurer for the nested workflow task
* @return a {@link FuncTaskConfigurer} that adds a workflow task to the tasks list
*/
public static FuncTaskConfigurer workflowTask(WorkflowConfigurer configurer) {
Objects.requireNonNull(configurer, "configurer");
return list -> list.workflow(configurer);
}

/**
* Create a {@link FuncTaskConfigurer} that adds a workflow subflow task.
*
* @param configurer configurer for the nested workflow task
* @return a {@link FuncTaskConfigurer} that adds a workflow task to the tasks list
* @deprecated use {@link #workflowTask(WorkflowConfigurer)} to avoid ambiguity with spec-side
* workflow factory methods
*/
@Deprecated
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

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

The newly deprecated API uses a bare @Deprecated annotation. Consider using @Deprecated(since = \"<version>\", forRemoval = false/true) to make the deprecation timeline explicit for consumers (and tooling), especially since this method name is intentionally ambiguous with spec-side workflow factory methods.

Suggested change
@Deprecated
@Deprecated(since = "<version>", forRemoval = false)

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Whats the point of adding a new function that is also deprecated???

public static FuncTaskConfigurer workflow(WorkflowConfigurer configurer) {
return workflowTask(configurer);
}

// ---------------------------------------------------------------------------
// HTTP / OpenAPI
// ---------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import io.serverlessworkflow.fluent.func.FuncListenTaskBuilder;
import io.serverlessworkflow.fluent.func.FuncSetTaskBuilder;
import io.serverlessworkflow.fluent.func.FuncSwitchTaskBuilder;
import io.serverlessworkflow.fluent.spec.WorkflowTaskBuilder;
import io.serverlessworkflow.fluent.spec.spi.CallHttpFluent;
import io.serverlessworkflow.fluent.spec.spi.CallOpenAPIFluent;
import io.serverlessworkflow.fluent.spec.spi.EmitFluent;
Expand All @@ -32,6 +33,7 @@
import io.serverlessworkflow.fluent.spec.spi.ListenFluent;
import io.serverlessworkflow.fluent.spec.spi.SetFluent;
import io.serverlessworkflow.fluent.spec.spi.SwitchFluent;
import io.serverlessworkflow.fluent.spec.spi.WorkflowFluent;

public interface FuncDoFluent<SELF extends FuncDoFluent<SELF>>
extends SetFluent<FuncSetTaskBuilder, SELF>,
Expand All @@ -42,4 +44,5 @@ public interface FuncDoFluent<SELF extends FuncDoFluent<SELF>>
ListenFluent<FuncListenTaskBuilder, SELF>,
CallFnFluent<FuncCallTaskBuilder, SELF>,
CallHttpFluent<FuncCallHttpTaskBuilder, SELF>,
CallOpenAPIFluent<FuncCallOpenAPITaskBuilder, SELF> {}
CallOpenAPIFluent<FuncCallOpenAPITaskBuilder, SELF>,
WorkflowFluent<WorkflowTaskBuilder, SELF> {}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@
import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.produced;
import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.switchWhenOrElse;
import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.toOne;
import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.workflowTask;
import static io.serverlessworkflow.fluent.spec.dsl.DSL.use;
import static io.serverlessworkflow.fluent.spec.dsl.DSL.workflow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
Expand Down Expand Up @@ -174,6 +177,29 @@ void mixed_chaining_order_and_exports() {
assertNotNull(t2.getListenTask().getExport(), "listen step should carry export");
}

@Test
void workflow_task_builds_from_func_dsl() {
Workflow wf =
FuncWorkflowBuilder.workflow("step-workflow")
.tasks(
workflowTask(
workflow("child.ns", "child-flow", "2.3.4").input("id", 99).await(false)))
.build();

List<TaskItem> items = wf.getDo();
assertEquals(1, items.size());

Task t = items.get(0).getTask();
assertNotNull(t.getRunTask(), "RunTask expected");
var run = t.getRunTask().getRun().getRunWorkflow();
assertNotNull(run, "RunWorkflow should be present");
assertEquals("child.ns", run.getWorkflow().getNamespace());
assertEquals("child-flow", run.getWorkflow().getName());
assertEquals("2.3.4", run.getWorkflow().getVersion());
assertEquals(99, run.getWorkflow().getInput().getAdditionalProperties().get("id"));
assertFalse(run.isAwait(), "await(false) should be preserved as false");
}

@Test
void switchWhenOrElse_jq_to_taskName() {
Workflow wf =
Expand Down
Loading