From 11b7618c26c1dc9043acf743a46dbdcb0bae6d3e Mon Sep 17 00:00:00 2001 From: fjtirado Date: Wed, 8 Oct 2025 14:34:09 +0200 Subject: [PATCH] [Fix #863] Adding WorkflowContext and TaskContext as additional params User can now specify java filter functions that accepts WorkflowContext and TaskContext in their signature. Signed-off-by: fjtirado --- .../func/JavaExpressionFactory.java | 12 ++++ .../workflow/impl/JavaFunctions.java | 11 ++++ .../serverless/workflow/impl/ModelTest.java | 59 +++++++++++++++++++ experimental/types/pom.xml | 4 ++ .../api/types/func/ExportAsFunction.java | 20 +++++++ .../api/types/func/InputFromFunction.java | 20 +++++++ .../api/types/func/JavaContextFunction.java | 23 ++++++++ .../api/types/func/JavaFilterFunction.java | 24 ++++++++ .../api/types/func/OutputAsFunction.java | 20 +++++++ .../types/func/TypedJavaContextFunction.java | 19 ++++++ .../types/func/TypedJavaFilterFunction.java | 18 ++++++ 11 files changed, 230 insertions(+) create mode 100644 experimental/types/src/main/java/io/serverlessworkflow/api/types/func/JavaContextFunction.java create mode 100644 experimental/types/src/main/java/io/serverlessworkflow/api/types/func/JavaFilterFunction.java create mode 100644 experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedJavaContextFunction.java create mode 100644 experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedJavaFilterFunction.java diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaExpressionFactory.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaExpressionFactory.java index 69f81239..1faea783 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaExpressionFactory.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaExpressionFactory.java @@ -18,8 +18,12 @@ import io.cloudevents.CloudEventData; import io.serverlessworkflow.api.types.TaskBase; import io.serverlessworkflow.api.types.TaskMetadata; +import io.serverlessworkflow.api.types.func.JavaContextFunction; +import io.serverlessworkflow.api.types.func.JavaFilterFunction; import io.serverlessworkflow.api.types.func.TaskMetadataKeys; import io.serverlessworkflow.api.types.func.TypedFunction; +import io.serverlessworkflow.api.types.func.TypedJavaContextFunction; +import io.serverlessworkflow.api.types.func.TypedJavaFilterFunction; import io.serverlessworkflow.api.types.func.TypedPredicate; import io.serverlessworkflow.impl.WorkflowModelFactory; import io.serverlessworkflow.impl.WorkflowPredicate; @@ -44,6 +48,14 @@ public ObjectExpression buildExpression(ExpressionDescriptor descriptor) { return (w, t, n) -> func.apply(n.asJavaObject()); } else if (value instanceof TypedFunction func) { return (w, t, n) -> func.function().apply(n.as(func.argClass()).orElseThrow()); + } else if (value instanceof JavaFilterFunction func) { + return (w, t, n) -> func.apply(n.asJavaObject(), w, t); + } else if (value instanceof TypedJavaFilterFunction func) { + return (w, t, n) -> func.function().apply(n.as(func.argClass()).orElseThrow(), w, t); + } else if (value instanceof JavaContextFunction func) { + return (w, t, n) -> func.apply(n.asJavaObject(), w); + } else if (value instanceof TypedJavaContextFunction func) { + return (w, t, n) -> func.function().apply(n.as(func.argClass()).orElseThrow(), w); } else { return (w, t, n) -> value; } diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/JavaFunctions.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/JavaFunctions.java index f24766aa..455a1d97 100644 --- a/experimental/lambda/src/test/java/io/serverless/workflow/impl/JavaFunctions.java +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/JavaFunctions.java @@ -15,6 +15,8 @@ */ package io.serverless.workflow.impl; +import io.serverlessworkflow.impl.TaskContextData; +import io.serverlessworkflow.impl.WorkflowContextData; import java.util.Map; public class JavaFunctions { @@ -27,6 +29,15 @@ static String getName(Person person) { return person.name() + " Javierito"; } + static String getFilterName( + Person person, WorkflowContextData workflowContext, TaskContextData taskContext) { + return person.name() + "_" + workflowContext.instanceData().id() + "_" + taskContext.taskName(); + } + + static String getContextName(Person person, WorkflowContextData workflowContext) { + return person.name() + "_" + workflowContext.instanceData().id(); + } + static Map addJavierito(Map map) { return Map.of("name", map.get("name") + " Javierito"); } diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/ModelTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/ModelTest.java index 8c917dbe..1c521d88 100644 --- a/experimental/lambda/src/test/java/io/serverless/workflow/impl/ModelTest.java +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/ModelTest.java @@ -30,6 +30,7 @@ import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.api.types.func.OutputAsFunction; import io.serverlessworkflow.impl.WorkflowApplication; +import io.serverlessworkflow.impl.WorkflowInstance; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -173,4 +174,62 @@ void testPOJOStringExpression() throws InterruptedException, ExecutionException .isEqualTo("Francisco Javierito"); } } + + @Test + void testPOJOStringExpressionWithContext() throws InterruptedException, ExecutionException { + try (WorkflowApplication app = WorkflowApplication.builder().build()) { + Workflow workflow = + new Workflow() + .withDocument( + new Document().withNamespace("test").withName("testPojo").withVersion("1.0")) + .withDo( + List.of( + new TaskItem( + "doNothing", + new Task() + .withWaitTask( + new WaitTask() + .withWait( + new TimeoutAfter() + .withDurationInline( + new DurationInline().withMilliseconds(10))))))) + .withOutput( + new Output() + .withAs(new OutputAsFunction().withFunction(JavaFunctions::getContextName))); + WorkflowInstance instance = + app.workflowDefinition(workflow).instance(new Person("Francisco", 33)); + assertThat(instance.start().get().asText().orElseThrow()) + .isEqualTo("Francisco_" + instance.id()); + } + } + + @Test + void testPOJOStringExpressionWithFilter() throws InterruptedException, ExecutionException { + try (WorkflowApplication app = WorkflowApplication.builder().build()) { + Workflow workflow = + new Workflow() + .withDocument( + new Document().withNamespace("test").withName("testPojo").withVersion("1.0")) + .withDo( + List.of( + new TaskItem( + "doNothing", + new Task() + .withWaitTask( + new WaitTask() + .withOutput( + new Output() + .withAs( + new OutputAsFunction() + .withFunction(JavaFunctions::getFilterName))) + .withWait( + new TimeoutAfter() + .withDurationInline( + new DurationInline().withMilliseconds(10))))))); + WorkflowInstance instance = + app.workflowDefinition(workflow).instance(new Person("Francisco", 33)); + assertThat(instance.start().get().asText().orElseThrow()) + .isEqualTo("Francisco_" + instance.id() + "_doNothing"); + } + } } diff --git a/experimental/types/pom.xml b/experimental/types/pom.xml index 3165f28d..197c5097 100644 --- a/experimental/types/pom.xml +++ b/experimental/types/pom.xml @@ -12,5 +12,9 @@ io.serverlessworkflow serverlessworkflow-types + + io.serverlessworkflow + serverlessworkflow-impl-core + \ No newline at end of file diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ExportAsFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ExportAsFunction.java index 45a81892..15aa0861 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ExportAsFunction.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ExportAsFunction.java @@ -31,4 +31,24 @@ public ExportAs withFunction(Function value, Class argClass) { setObject(new TypedFunction<>(value, argClass)); return this; } + + public ExportAs withFunction(JavaFilterFunction value) { + setObject(value); + return this; + } + + public ExportAs withFunction(JavaFilterFunction value, Class argClass) { + setObject(new TypedJavaFilterFunction<>(value, argClass)); + return this; + } + + public ExportAs withFunction(JavaContextFunction value) { + setObject(value); + return this; + } + + public ExportAs withFunction(JavaContextFunction value, Class argClass) { + setObject(new TypedJavaContextFunction<>(value, argClass)); + return this; + } } diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/InputFromFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/InputFromFunction.java index 521dca87..d1eed103 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/InputFromFunction.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/InputFromFunction.java @@ -31,4 +31,24 @@ public InputFrom withFunction(Function value, Class argClass) { setObject(new TypedFunction<>(value, argClass)); return this; } + + public InputFrom withFunction(JavaFilterFunction value) { + setObject(value); + return this; + } + + public InputFrom withFunction(JavaFilterFunction value, Class argClass) { + setObject(new TypedJavaFilterFunction<>(value, argClass)); + return this; + } + + public InputFrom withFunction(JavaContextFunction value) { + setObject(value); + return this; + } + + public InputFrom withFunction(JavaContextFunction value, Class argClass) { + setObject(new TypedJavaContextFunction<>(value, argClass)); + return this; + } } diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/JavaContextFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/JavaContextFunction.java new file mode 100644 index 00000000..e74a090c --- /dev/null +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/JavaContextFunction.java @@ -0,0 +1,23 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.api.types.func; + +import io.serverlessworkflow.impl.WorkflowContextData; + +@FunctionalInterface +public interface JavaContextFunction { + R apply(T object, WorkflowContextData workflowContext); +} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/JavaFilterFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/JavaFilterFunction.java new file mode 100644 index 00000000..f6b9d378 --- /dev/null +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/JavaFilterFunction.java @@ -0,0 +1,24 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.api.types.func; + +import io.serverlessworkflow.impl.TaskContextData; +import io.serverlessworkflow.impl.WorkflowContextData; + +@FunctionalInterface +public interface JavaFilterFunction { + R apply(T object, WorkflowContextData workflowContext, TaskContextData taskContext); +} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/OutputAsFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/OutputAsFunction.java index 8d2d6dc5..20e0b4c8 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/OutputAsFunction.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/OutputAsFunction.java @@ -31,4 +31,24 @@ public OutputAs withFunction(Function value, Class argClass) { setObject(new TypedFunction<>(value, argClass)); return this; } + + public OutputAs withFunction(JavaFilterFunction value) { + setObject(value); + return this; + } + + public OutputAs withFunction(JavaFilterFunction value, Class argClass) { + setObject(new TypedJavaFilterFunction<>(value, argClass)); + return this; + } + + public OutputAs withFunction(JavaContextFunction value) { + setObject(value); + return this; + } + + public OutputAs withFunction(JavaContextFunction value, Class argClass) { + setObject(new TypedJavaContextFunction<>(value, argClass)); + return this; + } } diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedJavaContextFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedJavaContextFunction.java new file mode 100644 index 00000000..e4254287 --- /dev/null +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedJavaContextFunction.java @@ -0,0 +1,19 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.api.types.func; + +public record TypedJavaContextFunction( + JavaContextFunction function, Class argClass) {} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedJavaFilterFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedJavaFilterFunction.java new file mode 100644 index 00000000..90d3e130 --- /dev/null +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedJavaFilterFunction.java @@ -0,0 +1,18 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.api.types.func; + +public record TypedJavaFilterFunction(JavaFilterFunction function, Class argClass) {}