From c9c30dd3af7809b6b7553845faac94fae92c8fc4 Mon Sep 17 00:00:00 2001 From: Ricardo Zanini Date: Tue, 10 Mar 2026 15:59:07 -0400 Subject: [PATCH 1/2] Add infer types to Step Signed-off-by: Ricardo Zanini --- .../fluent/func/dsl/Step.java | 122 +++++++++++------- ...cDSLDataFlowTransformationHelpersTest.java | 16 +-- 2 files changed, 84 insertions(+), 54 deletions(-) diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/Step.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/Step.java index efbddba0..5e22a65d 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/Step.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/Step.java @@ -47,8 +47,11 @@ protected SELF self() { // --------------------------------------------------------------------------- /** Queue a {@code when(predicate)} to be applied on the concrete builder. */ - public SELF when(Predicate predicate) { - postConfigurers.add(b -> ((ConditionalTaskBuilder) b).when(predicate)); + public SELF when(SerializablePredicate predicate) { + postConfigurers.add( + b -> + ((ConditionalTaskBuilder) b) + .when(predicate, ReflectionUtils.inferInputType(predicate))); return self(); } @@ -115,13 +118,16 @@ public SELF then(FlowDirectiveEnum directive) { * } * * @param the task result type - * @param the export type (what gets forwarded to the next step) + * @param the export type (what gets forwarded to the next step) * @param function the transformation function * @return this step for method chaining * @see io.serverlessworkflow.fluent.func.spi.FuncTaskTransformations#exportAs(Function) */ - public SELF exportAs(Function function) { - postConfigurers.add(b -> ((FuncTaskTransformations) b).exportAs(function)); + public SELF exportAs(SerializableFunction function) { + postConfigurers.add( + b -> + ((FuncTaskTransformations) b) + .exportAs(function, ReflectionUtils.inferInputType(function))); return self(); } @@ -132,13 +138,13 @@ public SELF exportAs(Function function) { *

This variant allows you to explicitly specify the input type class for better type safety. * * @param the task result type - * @param the export type (what gets forwarded to the next step) + * @param the export type (what gets forwarded to the next step) * @param function the transformation function * @param taskResultClass the class of the task result type * @return this step for method chaining * @see io.serverlessworkflow.fluent.func.spi.FuncTaskTransformations#exportAs(Function, Class) */ - public SELF exportAs(Function function, Class taskResultClass) { + public SELF exportAs(Function function, Class taskResultClass) { postConfigurers.add(b -> ((FuncTaskTransformations) b).exportAs(function, taskResultClass)); return self(); } @@ -150,13 +156,16 @@ public SELF exportAs(Function function, Class taskResultClass) { * metadata when shaping the export. * * @param the task result type - * @param the export type (what gets forwarded to the next step) + * @param the export type (what gets forwarded to the next step) * @param function the filter function with workflow and task context * @return this step for method chaining * @see io.serverlessworkflow.fluent.func.spi.FuncTaskTransformations#exportAs(FilterFunction) */ - public SELF exportAs(FilterFunction function) { - postConfigurers.add(b -> ((FuncTaskTransformations) b).exportAs(function)); + public SELF exportAs(FilterFunction function) { + postConfigurers.add( + b -> + ((FuncTaskTransformations) b) + .exportAs(function, ReflectionUtils.inferInputType(function))); return self(); } @@ -172,7 +181,7 @@ public SELF exportAs(FilterFunction function) { * @see io.serverlessworkflow.fluent.func.spi.FuncTaskTransformations#exportAs(FilterFunction, * Class) */ - public SELF exportAs(FilterFunction function, Class taskResultClass) { + public SELF exportAs(FilterFunction function, Class taskResultClass) { postConfigurers.add(b -> ((FuncTaskTransformations) b).exportAs(function, taskResultClass)); return self(); } @@ -184,13 +193,16 @@ public SELF exportAs(FilterFunction function, Class taskResultCl * when shaping the export. * * @param the task result type - * @param the export type (what gets forwarded to the next step) + * @param the export type (what gets forwarded to the next step) * @param function the context function with workflow context * @return this step for method chaining * @see io.serverlessworkflow.fluent.func.spi.FuncTaskTransformations#exportAs(ContextFunction) */ - public SELF exportAs(ContextFunction function) { - postConfigurers.add(b -> ((FuncTaskTransformations) b).exportAs(function)); + public SELF exportAs(ContextFunction function) { + postConfigurers.add( + b -> + ((FuncTaskTransformations) b) + .exportAs(function, ReflectionUtils.inferInputType(function))); return self(); } @@ -199,14 +211,14 @@ public SELF exportAs(ContextFunction function) { * explicit input type. * * @param the task result type - * @param the export type (what gets forwarded to the next step) + * @param the export type (what gets forwarded to the next step) * @param function the context function with workflow context * @param taskResultClass the class of the task result type * @return this step for method chaining * @see io.serverlessworkflow.fluent.func.spi.FuncTaskTransformations#exportAs(ContextFunction, * Class) */ - public SELF exportAs(ContextFunction function, Class taskResultClass) { + public SELF exportAs(ContextFunction function, Class taskResultClass) { postConfigurers.add(b -> ((FuncTaskTransformations) b).exportAs(function, taskResultClass)); return self(); } @@ -253,13 +265,16 @@ public SELF exportAs(String jqExpression) { * } * * @param the task result type - * @param the output type (what gets written to workflow data) + * @param the output type (what gets written to workflow data) * @param function the transformation function * @return this step for method chaining * @see io.serverlessworkflow.fluent.func.spi.FuncTransformations#outputAs(Function) */ - public SELF outputAs(Function function) { - postConfigurers.add(b -> ((FuncTaskTransformations) b).outputAs(function)); + public SELF outputAs(SerializableFunction function) { + postConfigurers.add( + b -> + ((FuncTaskTransformations) b) + .outputAs(function, ReflectionUtils.inferInputType(function))); return self(); } @@ -270,13 +285,13 @@ public SELF outputAs(Function function) { *

This variant allows you to explicitly specify the input type class for better type safety. * * @param the task result type - * @param the output type (what gets written to workflow data) + * @param the output type (what gets written to workflow data) * @param function the transformation function * @param taskResultClass the class of the task result type * @return this step for method chaining * @see io.serverlessworkflow.fluent.func.spi.FuncTransformations#outputAs(Function, Class) */ - public SELF outputAs(Function function, Class taskResultClass) { + public SELF outputAs(Function function, Class taskResultClass) { postConfigurers.add(b -> ((FuncTaskTransformations) b).outputAs(function, taskResultClass)); return self(); } @@ -306,13 +321,16 @@ public SELF outputAs(Function function, Class taskResultClass) { * } * * @param the task result type - * @param the output type (what gets written to workflow data) + * @param the output type (what gets written to workflow data) * @param function the filter function with workflow and task context * @return this step for method chaining * @see io.serverlessworkflow.fluent.func.spi.FuncTransformations#outputAs(FilterFunction) */ - public SELF outputAs(FilterFunction function) { - postConfigurers.add(b -> ((FuncTaskTransformations) b).outputAs(function)); + public SELF outputAs(FilterFunction function) { + postConfigurers.add( + b -> + ((FuncTaskTransformations) b) + .outputAs(function, ReflectionUtils.inferInputType(function))); return self(); } @@ -321,13 +339,13 @@ public SELF outputAs(FilterFunction function) { * function with explicit input type. * * @param the task result type - * @param the output type (what gets written to workflow data) + * @param the output type (what gets written to workflow data) * @param function the filter function with workflow and task context * @param taskResultClass the class of the task result type * @return this step for method chaining * @see io.serverlessworkflow.fluent.func.spi.FuncTransformations#outputAs(FilterFunction, Class) */ - public SELF outputAs(FilterFunction function, Class taskResultClass) { + public SELF outputAs(FilterFunction function, Class taskResultClass) { postConfigurers.add(b -> ((FuncTaskTransformations) b).outputAs(function, taskResultClass)); return self(); } @@ -339,13 +357,16 @@ public SELF outputAs(FilterFunction function, Class taskResultCl * when shaping the committed output. * * @param the task result type - * @param the output type (what gets written to workflow data) + * @param the output type (what gets written to workflow data) * @param function the context function with workflow context * @return this step for method chaining * @see io.serverlessworkflow.fluent.func.spi.FuncTransformations#outputAs(ContextFunction) */ - public SELF outputAs(ContextFunction function) { - postConfigurers.add(b -> ((FuncTaskTransformations) b).outputAs(function)); + public SELF outputAs(ContextFunction function) { + postConfigurers.add( + b -> + ((FuncTaskTransformations) b) + .outputAs(function, ReflectionUtils.inferInputType(function))); return self(); } @@ -354,13 +375,13 @@ public SELF outputAs(ContextFunction function) { * with explicit input type. * * @param the task result type - * @param the output type (what gets written to workflow data) + * @param the output type (what gets written to workflow data) * @param function the context function with workflow context * @param taskResultClass the class of the task result type * @return this step for method chaining * @see io.serverlessworkflow.fluent.func.spi.FuncTransformations#outputAs(ContextFunction, Class) */ - public SELF outputAs(ContextFunction function, Class taskResultClass) { + public SELF outputAs(ContextFunction function, Class taskResultClass) { postConfigurers.add(b -> ((FuncTaskTransformations) b).outputAs(function, taskResultClass)); return self(); } @@ -406,13 +427,16 @@ public SELF outputAs(String jqExpression) { * } * * @param the input type (workflow data or task input) - * @param the result type (what the task will see as input) + * @param the result type (what the task will see as input) * @param function the transformation function * @return this step for method chaining * @see io.serverlessworkflow.fluent.func.spi.FuncTransformations#inputFrom(Function) */ - public SELF inputFrom(Function function) { - postConfigurers.add(b -> ((FuncTaskTransformations) b).inputFrom(function)); + public SELF inputFrom(SerializableFunction function) { + postConfigurers.add( + b -> + ((FuncTaskTransformations) b) + .inputFrom(function, ReflectionUtils.inferInputType(function))); return self(); } @@ -430,13 +454,13 @@ public SELF inputFrom(Function function) { * } * * @param the input type (workflow data or task input) - * @param the result type (what the task will see as input) + * @param the result type (what the task will see as input) * @param function the transformation function * @param inputClass the class of the input type * @return this step for method chaining * @see io.serverlessworkflow.fluent.func.spi.FuncTransformations#inputFrom(Function, Class) */ - public SELF inputFrom(Function function, Class inputClass) { + public SELF inputFrom(Function function, Class inputClass) { postConfigurers.add(b -> ((FuncTaskTransformations) b).inputFrom(function, inputClass)); return self(); } @@ -462,13 +486,16 @@ public SELF inputFrom(Function function, Class inputClass) { * } * * @param the input type (workflow data or task input) - * @param the result type (what the task will see as input) + * @param the result type (what the task will see as input) * @param function the filter function with workflow and task context * @return this step for method chaining * @see io.serverlessworkflow.fluent.func.spi.FuncTransformations#inputFrom(FilterFunction) */ - public SELF inputFrom(FilterFunction function) { - postConfigurers.add(b -> ((FuncTaskTransformations) b).inputFrom(function)); + public SELF inputFrom(FilterFunction function) { + postConfigurers.add( + b -> + ((FuncTaskTransformations) b) + .inputFrom(function, ReflectionUtils.inferInputType(function))); return self(); } @@ -479,13 +506,13 @@ public SELF inputFrom(FilterFunction function) { * specification. * * @param the input type (workflow data or task input) - * @param the result type (what the task will see as input) + * @param the result type (what the task will see as input) * @param function the filter function with workflow and task context * @param inputClass the class of the input type * @return this step for method chaining * @see io.serverlessworkflow.fluent.func.spi.FuncTransformations#inputFrom(FilterFunction, Class) */ - public SELF inputFrom(FilterFunction function, Class inputClass) { + public SELF inputFrom(FilterFunction function, Class inputClass) { postConfigurers.add(b -> ((FuncTaskTransformations) b).inputFrom(function, inputClass)); return self(); } @@ -497,13 +524,16 @@ public SELF inputFrom(FilterFunction function, Class inputClass) * you to inspect workflow metadata and current data. * * @param the input type (workflow data or task input) - * @param the result type (what the task will see as input) + * @param the result type (what the task will see as input) * @param function the context function with workflow context * @return this step for method chaining * @see io.serverlessworkflow.fluent.func.spi.FuncTransformations#inputFrom(ContextFunction) */ - public SELF inputFrom(ContextFunction function) { - postConfigurers.add(b -> ((FuncTaskTransformations) b).inputFrom(function)); + public SELF inputFrom(ContextFunction function) { + postConfigurers.add( + b -> + ((FuncTaskTransformations) b) + .inputFrom(function, ReflectionUtils.inferInputType(function))); return self(); } @@ -514,14 +544,14 @@ public SELF inputFrom(ContextFunction function) { * specification. * * @param the input type (workflow data or task input) - * @param the result type (what the task will see as input) + * @param the result type (what the task will see as input) * @param function the context function with workflow context * @param inputClass the class of the input type * @return this step for method chaining * @see io.serverlessworkflow.fluent.func.spi.FuncTransformations#inputFrom(ContextFunction, * Class) */ - public SELF inputFrom(ContextFunction function, Class inputClass) { + public SELF inputFrom(ContextFunction function, Class inputClass) { postConfigurers.add(b -> ((FuncTaskTransformations) b).inputFrom(function, inputClass)); return self(); } diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/FuncDSLDataFlowTransformationHelpersTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/FuncDSLDataFlowTransformationHelpersTest.java index 4d307ce2..c96ac241 100644 --- a/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/FuncDSLDataFlowTransformationHelpersTest.java +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/FuncDSLDataFlowTransformationHelpersTest.java @@ -19,9 +19,12 @@ import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.input; import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.output; +import io.serverlessworkflow.api.types.Task; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.fluent.func.FuncWorkflowBuilder; +import io.serverlessworkflow.impl.TaskContextData; import io.serverlessworkflow.impl.WorkflowApplication; +import io.serverlessworkflow.impl.WorkflowContextData; import io.serverlessworkflow.impl.WorkflowDefinition; import io.serverlessworkflow.impl.WorkflowModel; import org.assertj.core.api.SoftAssertions; @@ -42,17 +45,15 @@ void test_input_with_inputFrom() { (Long input) -> { softly.assertThat(input).isEqualTo(10L); return input + 5; - }, - Long.class), + }), function("returnEnriched", (Long enrichedValue) -> enrichedValue, Long.class) .inputFrom( - (object, workflowContext) -> { + (Long object, WorkflowContextData workflowContext) -> { softly.assertThat(object).isEqualTo(15L); Long input = input(workflowContext, Long.class); softly.assertThat(input).isEqualTo(10L); return object + input; - }, - Long.class)) + })) .build(); try (WorkflowApplication app = WorkflowApplication.builder().build()) { @@ -115,10 +116,9 @@ void test_output_with_exportAs() { (Long input) -> { softly.assertThat(input).isEqualTo(10L); return input + 5; - }, - Long.class) + }, Long.class) .exportAs( - (object, workflowContext, taskContextData) -> { + (Long object, WorkflowContextData workflowContext, TaskContextData taskContextData) -> { Long taskOutput = output(taskContextData, Long.class); softly.assertThat(taskOutput).isEqualTo(15L); return taskOutput * 2; From ae62455faf93d5f2459ed9516d6497dafe996fd3 Mon Sep 17 00:00:00 2001 From: Ricardo Zanini Date: Tue, 10 Mar 2026 16:04:51 -0400 Subject: [PATCH 2/2] Adjust testing Signed-off-by: Ricardo Zanini --- .../FuncDSLDataFlowTransformationHelpersTest.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/FuncDSLDataFlowTransformationHelpersTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/FuncDSLDataFlowTransformationHelpersTest.java index c96ac241..5a294790 100644 --- a/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/FuncDSLDataFlowTransformationHelpersTest.java +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/FuncDSLDataFlowTransformationHelpersTest.java @@ -19,7 +19,6 @@ import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.input; import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.output; -import io.serverlessworkflow.api.types.Task; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.fluent.func.FuncWorkflowBuilder; import io.serverlessworkflow.impl.TaskContextData; @@ -116,14 +115,16 @@ void test_output_with_exportAs() { (Long input) -> { softly.assertThat(input).isEqualTo(10L); return input + 5; - }, Long.class) + }, + Long.class) .exportAs( - (Long object, WorkflowContextData workflowContext, TaskContextData taskContextData) -> { + (Long object, + WorkflowContextData workflowContext, + TaskContextData taskContextData) -> { Long taskOutput = output(taskContextData, Long.class); softly.assertThat(taskOutput).isEqualTo(15L); return taskOutput * 2; - }, - Object.class)) + })) .build(); try (WorkflowApplication app = WorkflowApplication.builder().build()) {