diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaForExecutorBuilder.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaForExecutorBuilder.java index 672f6aca..0d80fa6d 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaForExecutorBuilder.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaForExecutorBuilder.java @@ -39,7 +39,6 @@ protected JavaForExecutorBuilder( WorkflowApplication application, ResourceLoader resourceLoader) { super(position, task, workflow, application, resourceLoader); - if (task instanceof ForTaskFunction taskFunctions) {} } protected Optional buildWhileFilter() { 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 2f1820a0..48f8b804 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 @@ -15,6 +15,8 @@ */ package io.serverlessworkflow.impl.expressions.func; +import io.serverlessworkflow.api.types.TaskBase; +import io.serverlessworkflow.api.types.TaskMetadata; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowFilter; @@ -22,6 +24,7 @@ import io.serverlessworkflow.impl.WorkflowModelFactory; import io.serverlessworkflow.impl.expressions.Expression; import io.serverlessworkflow.impl.expressions.ExpressionFactory; +import java.util.Optional; import java.util.function.BiFunction; import java.util.function.BiPredicate; import java.util.function.Function; @@ -29,6 +32,7 @@ public class JavaExpressionFactory implements ExpressionFactory { + public static final String IF_PREDICATE = "if_predicate"; private final WorkflowModelFactory modelFactory = new JavaModelFactory(); private final Expression dummyExpression = new Expression() { @@ -49,7 +53,7 @@ public WorkflowFilter buildFilter(String expr, Object value) { if (value instanceof Function func) { return (w, t, n) -> modelFactory.fromAny(func.apply(n.asJavaObject())); } else if (value instanceof Predicate pred) { - return (w, t, n) -> modelFactory.from(pred.test(n.asJavaObject())); + return fromPredicate(pred); } else if (value instanceof BiPredicate pred) { return (w, t, n) -> modelFactory.from(pred.test(w, t)); } else if (value instanceof BiFunction func) { @@ -61,6 +65,20 @@ public WorkflowFilter buildFilter(String expr, Object value) { } } + @SuppressWarnings({"rawtypes", "unchecked"}) + private WorkflowFilter fromPredicate(Predicate pred) { + return (w, t, n) -> modelFactory.from(pred.test(n.asJavaObject())); + } + + @Override + public Optional buildIfFilter(TaskBase task) { + TaskMetadata metadata = task.getMetadata(); + return metadata != null + && metadata.getAdditionalProperties().get(IF_PREDICATE) instanceof Predicate pred + ? Optional.of(fromPredicate(pred)) + : ExpressionFactory.super.buildIfFilter(task); + } + @Override public WorkflowModelFactory modelFactory() { return modelFactory; diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java index b32fd5e7..aaef75c8 100644 --- a/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java @@ -25,6 +25,7 @@ import io.serverlessworkflow.api.types.SwitchTask; import io.serverlessworkflow.api.types.Task; import io.serverlessworkflow.api.types.TaskItem; +import io.serverlessworkflow.api.types.TaskMetadata; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.api.types.func.CallJava; import io.serverlessworkflow.api.types.func.CallTaskJava; @@ -32,9 +33,11 @@ import io.serverlessworkflow.api.types.func.SwitchCaseFunction; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowDefinition; +import io.serverlessworkflow.impl.expressions.func.JavaExpressionFactory; import java.util.Collection; import java.util.List; import java.util.concurrent.ExecutionException; +import java.util.function.Predicate; import org.junit.jupiter.api.Test; class CallTest { @@ -140,6 +143,34 @@ void testSwitch() throws InterruptedException, ExecutionException { } } + @Test + void testIf() throws InterruptedException, ExecutionException { + try (WorkflowApplication app = WorkflowApplication.builder().build()) { + Workflow workflow = + new Workflow() + .withDocument( + new Document().withNamespace("test").withName("testIf").withVersion("1.0")) + .withDo( + List.of( + new TaskItem( + "java", + new Task() + .withCallTask( + new CallTaskJava( + withPredicate( + CallJava.function(CallTest::zero), CallTest::isOdd)))))); + WorkflowDefinition definition = app.workflowDefinition(workflow); + assertThat(definition.instance(3).start().get().asNumber().orElseThrow()).isEqualTo(0); + assertThat(definition.instance(4).start().get().asNumber().orElseThrow()).isEqualTo(4); + } + } + + private CallJava withPredicate(CallJava call, Predicate pred) { + return (CallJava) + call.withMetadata( + new TaskMetadata().withAdditionalProperty(JavaExpressionFactory.IF_PREDICATE, pred)); + } + public static boolean isEven(Object model, Integer number) { return !isOdd(number); } diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/FluentDSLCallTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/FluentDSLCallTest.java index 9706be4f..dcad95d8 100644 --- a/experimental/lambda/src/test/java/io/serverless/workflow/impl/FluentDSLCallTest.java +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/FluentDSLCallTest.java @@ -34,7 +34,7 @@ void testJavaFunction() throws InterruptedException, ExecutionException { try (WorkflowApplication app = WorkflowApplication.builder().build()) { final Workflow workflow = FuncWorkflowBuilder.workflow("testJavaCall") - .tasks(tasks -> tasks.callFn(f -> f.fn(JavaFunctions::getName))) + .tasks(tasks -> tasks.callFn(f -> f.function(JavaFunctions::getName))) .build(); assertThat( app.workflowDefinition(workflow) @@ -85,7 +85,7 @@ void testSwitch() throws InterruptedException, ExecutionException { switchOdd.items( item -> item.when(CallTest::isOdd).then(FlowDirectiveEnum.END))) - .callFn(callJava -> callJava.fn(CallTest::zero))) + .callFn(callJava -> callJava.function(CallTest::zero))) .build(); WorkflowDefinition definition = app.workflowDefinition(workflow); diff --git a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/DelegatingFuncDoTaskFluent.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/DelegatingFuncDoTaskFluent.java new file mode 100644 index 00000000..131330fa --- /dev/null +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/DelegatingFuncDoTaskFluent.java @@ -0,0 +1,86 @@ +/* + * 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.fluent.func; + +import io.serverlessworkflow.fluent.spec.HasDelegate; +import java.util.function.Consumer; + +/** + * Mixin that implements {@link FuncDoTaskFluent} by forwarding to another instance. + * + * @param concrete builder type + */ +public interface DelegatingFuncDoTaskFluent> + extends FuncDoTaskFluent, HasDelegate { + + @SuppressWarnings("unchecked") + default SELF self() { + return (SELF) this; + } + + @SuppressWarnings("unchecked") + private FuncDoTaskFluent d() { + return (FuncDoTaskFluent) this.delegate(); + } + + @Override + default SELF callFn(String name, Consumer cfg) { + d().callFn(name, cfg); + return self(); + } + + @Override + default SELF callFn(Consumer cfg) { + d().callFn(cfg); + return self(); + } + + @Override + default SELF forFn(String name, Consumer cfg) { + d().forFn(name, cfg); + return self(); + } + + @Override + default SELF forFn(Consumer cfg) { + d().forFn(cfg); + return self(); + } + + @Override + default SELF switchFn(String name, Consumer cfg) { + d().switchFn(name, cfg); + return self(); + } + + @Override + default SELF switchFn(Consumer cfg) { + d().switchFn(cfg); + return self(); + } + + @Override + default SELF forkFn(String name, Consumer cfg) { + d().forkFn(name, cfg); + return self(); + } + + @Override + default SELF forkFn(Consumer cfg) { + d().forkFn(cfg); + return self(); + } +} diff --git a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java index a8bcf08b..6c4c524d 100644 --- a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java @@ -35,7 +35,7 @@ protected FuncCallTaskBuilder self() { return this; } - public FuncCallTaskBuilder fn(Function function) { + public FuncCallTaskBuilder function(Function function) { this.callTaskJava = new CallTaskJava(CallJava.function(function)); super.setTask(this.callTaskJava.getCallJava()); return this; diff --git a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncDoTaskBuilder.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncDoTaskBuilder.java index ec721c1e..deefa099 100644 --- a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncDoTaskBuilder.java +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncDoTaskBuilder.java @@ -16,47 +16,17 @@ package io.serverlessworkflow.fluent.func; import io.serverlessworkflow.fluent.spec.BaseDoTaskBuilder; -import java.util.function.Consumer; public class FuncDoTaskBuilder extends BaseDoTaskBuilder - implements FuncTransformations { + implements FuncTransformations, + DelegatingFuncDoTaskFluent { - FuncDoTaskBuilder() { + public FuncDoTaskBuilder() { super(new FuncTaskItemListBuilder()); } @Override - protected FuncDoTaskBuilder self() { - return this; - } - - public FuncDoTaskBuilder callFn(String name, Consumer consumer) { - this.innerListBuilder().callJava(name, consumer); - return this; - } - - public FuncDoTaskBuilder callFn(Consumer consumer) { - this.innerListBuilder().callJava(consumer); - return this; - } - - public FuncDoTaskBuilder forFn(String name, Consumer consumer) { - this.innerListBuilder().forFn(name, consumer); - return this; - } - - public FuncDoTaskBuilder forFn(Consumer consumer) { - this.innerListBuilder().forFn(consumer); - return this; - } - - public FuncDoTaskBuilder switchFn(String name, Consumer consumer) { - this.innerListBuilder().switchFn(name, consumer); - return this; - } - - public FuncDoTaskBuilder switchFn(Consumer consumer) { - this.innerListBuilder().switchFn(consumer); + public FuncDoTaskBuilder self() { return this; } } diff --git a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncDoTaskFluent.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncDoTaskFluent.java new file mode 100644 index 00000000..9ec0cb39 --- /dev/null +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncDoTaskFluent.java @@ -0,0 +1,37 @@ +/* + * 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.fluent.func; + +import java.util.function.Consumer; + +public interface FuncDoTaskFluent> { + + SELF callFn(String name, Consumer cfg); + + SELF callFn(Consumer cfg); + + SELF forFn(String name, Consumer cfg); + + SELF forFn(Consumer cfg); + + SELF switchFn(String name, Consumer cfg); + + SELF switchFn(Consumer cfg); + + SELF forkFn(String name, Consumer cfg); + + SELF forkFn(Consumer cfg); +} diff --git a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForkTaskBuilder.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForkTaskBuilder.java new file mode 100644 index 00000000..7df66fab --- /dev/null +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForkTaskBuilder.java @@ -0,0 +1,74 @@ +/* + * 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.fluent.func; + +import io.serverlessworkflow.api.types.ForkTask; +import io.serverlessworkflow.api.types.ForkTaskConfiguration; +import io.serverlessworkflow.api.types.Task; +import io.serverlessworkflow.api.types.TaskItem; +import io.serverlessworkflow.api.types.func.CallJava; +import io.serverlessworkflow.api.types.func.CallTaskJava; +import io.serverlessworkflow.fluent.spec.TaskBaseBuilder; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.function.Function; + +public class FuncForkTaskBuilder extends TaskBaseBuilder + implements FuncTransformations { + + private final ForkTask forkTask; + private final List items; + + FuncForkTaskBuilder() { + this.forkTask = new ForkTask(); + this.forkTask.setFork(new ForkTaskConfiguration()); + this.items = new ArrayList<>(); + } + + @Override + protected FuncForkTaskBuilder self() { + return this; + } + + public FuncForkTaskBuilder branch(String name, Function function) { + this.items.add( + new TaskItem(name, new Task().withCallTask(new CallTaskJava(CallJava.function(function))))); + return this; + } + + public FuncForkTaskBuilder branch(Function function) { + return this.branch(UUID.randomUUID().toString(), function); + } + + public FuncForkTaskBuilder branches(Consumer consumer) { + final FuncTaskItemListBuilder builder = new FuncTaskItemListBuilder(); + consumer.accept(builder); + this.items.addAll(builder.build()); + return this; + } + + public FuncForkTaskBuilder compete(boolean compete) { + this.forkTask.getFork().setCompete(compete); + return this; + } + + public ForkTask build() { + this.forkTask.getFork().setBranches(this.items); + return forkTask; + } +} diff --git a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTaskItemListBuilder.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTaskItemListBuilder.java index e11063da..13ce0c29 100644 --- a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTaskItemListBuilder.java +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTaskItemListBuilder.java @@ -18,15 +18,21 @@ import io.serverlessworkflow.api.types.Task; import io.serverlessworkflow.api.types.TaskItem; import io.serverlessworkflow.fluent.spec.BaseTaskItemListBuilder; +import java.util.List; import java.util.UUID; import java.util.function.Consumer; -public class FuncTaskItemListBuilder extends BaseTaskItemListBuilder { +public class FuncTaskItemListBuilder extends BaseTaskItemListBuilder + implements FuncDoTaskFluent { - FuncTaskItemListBuilder() { + public FuncTaskItemListBuilder() { super(); } + public FuncTaskItemListBuilder(final List list) { + super(list); + } + @Override protected FuncTaskItemListBuilder self() { return this; @@ -37,17 +43,20 @@ protected FuncTaskItemListBuilder newItemListBuilder() { return new FuncTaskItemListBuilder(); } - public FuncTaskItemListBuilder callJava(String name, Consumer consumer) { + @Override + public FuncTaskItemListBuilder callFn(String name, Consumer consumer) { this.requireNameAndConfig(name, consumer); final FuncCallTaskBuilder callTaskJavaBuilder = new FuncCallTaskBuilder(); consumer.accept(callTaskJavaBuilder); return addTaskItem(new TaskItem(name, new Task().withCallTask(callTaskJavaBuilder.build()))); } - public FuncTaskItemListBuilder callJava(Consumer consumer) { - return this.callJava(UUID.randomUUID().toString(), consumer); + @Override + public FuncTaskItemListBuilder callFn(Consumer consumer) { + return this.callFn(UUID.randomUUID().toString(), consumer); } + @Override public FuncTaskItemListBuilder forFn(String name, Consumer consumer) { this.requireNameAndConfig(name, consumer); final FuncForTaskBuilder forTaskJavaBuilder = new FuncForTaskBuilder(); @@ -55,10 +64,12 @@ public FuncTaskItemListBuilder forFn(String name, Consumer c return this.addTaskItem(new TaskItem(name, new Task().withForTask(forTaskJavaBuilder.build()))); } + @Override public FuncTaskItemListBuilder forFn(Consumer consumer) { return this.forFn(UUID.randomUUID().toString(), consumer); } + @Override public FuncTaskItemListBuilder switchFn(String name, Consumer consumer) { this.requireNameAndConfig(name, consumer); final FuncSwitchTaskBuilder funcSwitchTaskBuilder = new FuncSwitchTaskBuilder(); @@ -67,7 +78,22 @@ public FuncTaskItemListBuilder switchFn(String name, Consumer consumer) { return this.switchFn(UUID.randomUUID().toString(), consumer); } + + @Override + public FuncTaskItemListBuilder forkFn(Consumer cfg) { + return this.forkFn(UUID.randomUUID().toString(), cfg); + } + + @Override + public FuncTaskItemListBuilder forkFn(String name, Consumer cfg) { + this.requireNameAndConfig(name, cfg); + final FuncForkTaskBuilder forkTaskJavaBuilder = new FuncForkTaskBuilder(); + cfg.accept(forkTaskJavaBuilder); + return this.addTaskItem( + new TaskItem(name, new Task().withForkTask(forkTaskJavaBuilder.build()))); + } } diff --git a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncWorkflowBuilder.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncWorkflowBuilder.java index aad21591..686b1773 100644 --- a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncWorkflowBuilder.java +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncWorkflowBuilder.java @@ -22,7 +22,7 @@ public class FuncWorkflowBuilder extends BaseWorkflowBuilder implements FuncTransformations { - private FuncWorkflowBuilder(final String name, final String namespace, final String version) { + protected FuncWorkflowBuilder(final String name, final String namespace, final String version) { super(name, namespace, version); } diff --git a/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/JavaWorkflowBuilderTest.java b/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/JavaWorkflowBuilderTest.java index 529a9b8d..72cf6a68 100644 --- a/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/JavaWorkflowBuilderTest.java +++ b/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/JavaWorkflowBuilderTest.java @@ -27,7 +27,6 @@ import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.api.types.func.*; import io.serverlessworkflow.fluent.spec.BaseWorkflowBuilder; -// if you reuse anything import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; @@ -166,7 +165,7 @@ void testJavaFunctionalIO() { ForTaskFunction fn = (ForTaskFunction) forTaskFnHolder.getForTask(); assertNotNull(fn); - // Inspect nested tasks inside the function loop + // Inspect nested branche inside the function loop List nested = fn.getDo(); assertEquals(1, nested.size()); TaskBase nestedTask = nested.get(0).getTask().getSetTask(); @@ -194,7 +193,7 @@ void testJavaFunctionalIO() { } @Test - @DisplayName("callJava task added and retains name + CallTask union") + @DisplayName("callFn task added and retains name + CallTask union") void testCallJavaTask() { Workflow wf = FuncWorkflowBuilder.workflow("callJavaFlow") @@ -214,20 +213,20 @@ void testCallJavaTask() { assertEquals("invokeHandler", ti.getName()); Task task = ti.getTask(); - assertNotNull(task.getCallTask(), "CallTask should be present for callJava"); + assertNotNull(task.getCallTask(), "CallTask should be present for callFn"); // Additional assertions if FuncCallTaskBuilder populates fields // e.g., assertEquals("com.acme.Handler", task.getCallTask().getCallJava().getClassName()); } @Test - @DisplayName("switchCaseFn (Java variant) coexists with spec tasks") + @DisplayName("switchCaseFn (Java variant) coexists with spec branche") void testSwitchCaseJava() { Workflow wf = FuncWorkflowBuilder.workflow("switchJava") .tasks( d -> d.set("prepare", s -> s.expr("$.ready = true")) - .switchC( + .switchCase( sw -> { // configure Java switch builder (cases / predicates) })) @@ -244,7 +243,7 @@ void testSwitchCaseJava() { } @Test - @DisplayName("Combined: spec set + java forE + callJava inside nested do") + @DisplayName("Combined: spec set + java forE + callFn inside nested do") void testCompositeScenario() { Workflow wf = FuncWorkflowBuilder.workflow("composite") @@ -257,7 +256,7 @@ void testCompositeScenario() { .tasks( inner -> inner - .callJava( + .callFn( cj -> { // customizing Java call }) diff --git a/fluent/pom.xml b/fluent/pom.xml index ff19f4d5..4d056c48 100644 --- a/fluent/pom.xml +++ b/fluent/pom.xml @@ -35,6 +35,11 @@ serverlessworkflow-fluent-spec ${project.version} + + io.serverlessworkflow + serverlessworkflow-fluent-func + ${project.version} + diff --git a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseDoTaskBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseDoTaskBuilder.java index d4c70aec..8e6a2410 100644 --- a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseDoTaskBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseDoTaskBuilder.java @@ -16,128 +16,31 @@ package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.DoTask; -import java.util.function.Consumer; public abstract class BaseDoTaskBuilder< - TASK extends TaskBaseBuilder, LIST extends BaseTaskItemListBuilder> - extends TaskBaseBuilder { - private final DoTask doTask; - private final BaseTaskItemListBuilder taskItemListBuilder; + SELF extends BaseDoTaskBuilder, LIST extends BaseTaskItemListBuilder> + extends TaskBaseBuilder implements DelegatingDoTaskFluent { - protected BaseDoTaskBuilder(BaseTaskItemListBuilder taskItemListBuilder) { - this.doTask = new DoTask(); - this.taskItemListBuilder = taskItemListBuilder; - this.setTask(doTask); - } - - protected abstract TASK self(); - - protected LIST innerListBuilder() { - return (LIST) taskItemListBuilder; - } - - public TASK set(String name, Consumer itemsConfigurer) { - taskItemListBuilder.set(name, itemsConfigurer); - return self(); - } - - public TASK set(Consumer itemsConfigurer) { - taskItemListBuilder.set(itemsConfigurer); - return self(); - } - - public TASK set(String name, final String expr) { - taskItemListBuilder.set(name, s -> s.expr(expr)); - return self(); - } - - public TASK set(final String expr) { - taskItemListBuilder.set(expr); - return self(); - } - - public TASK forEach(String name, Consumer> itemsConfigurer) { - taskItemListBuilder.forEach(name, itemsConfigurer); - return self(); - } - - public TASK forEach(Consumer> itemsConfigurer) { - taskItemListBuilder.forEach(itemsConfigurer); - return self(); - } - - public TASK switchC(String name, Consumer itemsConfigurer) { - taskItemListBuilder.switchC(name, itemsConfigurer); - return self(); - } - - public TASK switchC(Consumer itemsConfigurer) { - taskItemListBuilder.switchC(itemsConfigurer); - return self(); - } - - public TASK raise(String name, Consumer itemsConfigurer) { - taskItemListBuilder.raise(name, itemsConfigurer); - return self(); - } - - public TASK raise(Consumer itemsConfigurer) { - taskItemListBuilder.raise(itemsConfigurer); - return self(); - } - - public TASK fork(String name, Consumer itemsConfigurer) { - taskItemListBuilder.fork(name, itemsConfigurer); - return self(); - } - - public TASK fork(Consumer itemsConfigurer) { - taskItemListBuilder.fork(itemsConfigurer); - return self(); - } - - public TASK listen(String name, Consumer itemsConfigurer) { - taskItemListBuilder.listen(name, itemsConfigurer); - return self(); - } - - public TASK listen(Consumer itemsConfigurer) { - taskItemListBuilder.listen(itemsConfigurer); - return self(); - } - - public TASK emit(String name, Consumer itemsConfigurer) { - taskItemListBuilder.emit(name, itemsConfigurer); - return self(); - } - - public TASK emit(Consumer itemsConfigurer) { - taskItemListBuilder.emit(itemsConfigurer); - return self(); - } - - public TASK tryC(String name, Consumer> itemsConfigurer) { - taskItemListBuilder.tryC(name, itemsConfigurer); - return self(); - } + private final DoTask doTask = new DoTask(); + private final BaseTaskItemListBuilder itemsListBuilder; - public TASK tryC(Consumer> itemsConfigurer) { - taskItemListBuilder.tryC(itemsConfigurer); - return self(); + protected BaseDoTaskBuilder(BaseTaskItemListBuilder itemsListBuilder) { + this.itemsListBuilder = itemsListBuilder; + setTask(doTask); } - public TASK callHTTP(String name, Consumer itemsConfigurer) { - taskItemListBuilder.callHTTP(name, itemsConfigurer); - return self(); + @SuppressWarnings("unchecked") + @Override + public LIST delegate() { + return (LIST) itemsListBuilder; } - public TASK callHTTP(Consumer itemsConfigurer) { - taskItemListBuilder.callHTTP(itemsConfigurer); - return self(); + public LIST list() { + return (LIST) itemsListBuilder; } public DoTask build() { - this.doTask.setDo(this.taskItemListBuilder.build()); - return this.doTask; + doTask.setDo(itemsListBuilder.build()); + return doTask; } } diff --git a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseTaskItemListBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseTaskItemListBuilder.java index bb2a34dc..2233a418 100644 --- a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseTaskItemListBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseTaskItemListBuilder.java @@ -35,7 +35,8 @@ * * @param the concrete builder type */ -public abstract class BaseTaskItemListBuilder> { +public abstract class BaseTaskItemListBuilder> + implements DoTaskFluent { private final List list; @@ -43,21 +44,30 @@ public BaseTaskItemListBuilder() { this.list = new ArrayList<>(); } + public BaseTaskItemListBuilder(final List list) { + this.list = list; + } + protected abstract SELF self(); protected abstract SELF newItemListBuilder(); - protected SELF addTaskItem(TaskItem taskItem) { + protected final List mutableList() { + return this.list; + } + + protected final SELF addTaskItem(TaskItem taskItem) { Objects.requireNonNull(taskItem, "taskItem must not be null"); list.add(taskItem); return self(); } - protected void requireNameAndConfig(String name, Consumer cfg) { + protected final void requireNameAndConfig(String name, Consumer cfg) { Objects.requireNonNull(name, "Task name must not be null"); Objects.requireNonNull(cfg, "Configurer must not be null"); } + @Override public SELF set(String name, Consumer itemsConfigurer) { requireNameAndConfig(name, itemsConfigurer); final SetTaskBuilder setBuilder = new SetTaskBuilder(); @@ -65,18 +75,22 @@ public SELF set(String name, Consumer itemsConfigurer) { return addTaskItem(new TaskItem(name, new Task().withSetTask(setBuilder.build()))); } + @Override public SELF set(Consumer itemsConfigurer) { return this.set(UUID.randomUUID().toString(), itemsConfigurer); } + @Override public SELF set(String name, final String expr) { return this.set(name, s -> s.expr(expr)); } + @Override public SELF set(final String expr) { return this.set(UUID.randomUUID().toString(), s -> s.expr(expr)); } + @Override public SELF forEach(String name, Consumer> itemsConfigurer) { requireNameAndConfig(name, itemsConfigurer); final ForTaskBuilder forBuilder = new ForTaskBuilder<>(newItemListBuilder()); @@ -84,21 +98,25 @@ public SELF forEach(String name, Consumer> itemsConfigurer) return addTaskItem(new TaskItem(name, new Task().withForTask(forBuilder.build()))); } + @Override public SELF forEach(Consumer> itemsConfigurer) { return this.forEach(UUID.randomUUID().toString(), itemsConfigurer); } - public SELF switchC(String name, Consumer itemsConfigurer) { + @Override + public SELF switchCase(String name, Consumer itemsConfigurer) { requireNameAndConfig(name, itemsConfigurer); final SwitchTaskBuilder switchBuilder = new SwitchTaskBuilder(); itemsConfigurer.accept(switchBuilder); return addTaskItem(new TaskItem(name, new Task().withSwitchTask(switchBuilder.build()))); } - public SELF switchC(Consumer itemsConfigurer) { - return this.switchC(UUID.randomUUID().toString(), itemsConfigurer); + @Override + public SELF switchCase(Consumer itemsConfigurer) { + return this.switchCase(UUID.randomUUID().toString(), itemsConfigurer); } + @Override public SELF raise(String name, Consumer itemsConfigurer) { requireNameAndConfig(name, itemsConfigurer); final RaiseTaskBuilder raiseBuilder = new RaiseTaskBuilder(); @@ -106,10 +124,12 @@ public SELF raise(String name, Consumer itemsConfigurer) { return addTaskItem(new TaskItem(name, new Task().withRaiseTask(raiseBuilder.build()))); } + @Override public SELF raise(Consumer itemsConfigurer) { return this.raise(UUID.randomUUID().toString(), itemsConfigurer); } + @Override public SELF fork(String name, Consumer itemsConfigurer) { requireNameAndConfig(name, itemsConfigurer); final ForkTaskBuilder forkBuilder = new ForkTaskBuilder(); @@ -117,10 +137,12 @@ public SELF fork(String name, Consumer itemsConfigurer) { return addTaskItem(new TaskItem(name, new Task().withForkTask(forkBuilder.build()))); } + @Override public SELF fork(Consumer itemsConfigurer) { return this.fork(UUID.randomUUID().toString(), itemsConfigurer); } + @Override public SELF listen(String name, Consumer itemsConfigurer) { requireNameAndConfig(name, itemsConfigurer); final ListenTaskBuilder listenBuilder = new ListenTaskBuilder(); @@ -128,10 +150,12 @@ public SELF listen(String name, Consumer itemsConfigurer) { return addTaskItem(new TaskItem(name, new Task().withListenTask(listenBuilder.build()))); } + @Override public SELF listen(Consumer itemsConfigurer) { return this.listen(UUID.randomUUID().toString(), itemsConfigurer); } + @Override public SELF emit(String name, Consumer itemsConfigurer) { requireNameAndConfig(name, itemsConfigurer); final EmitTaskBuilder emitBuilder = new EmitTaskBuilder(); @@ -139,21 +163,25 @@ public SELF emit(String name, Consumer itemsConfigurer) { return addTaskItem(new TaskItem(name, new Task().withEmitTask(emitBuilder.build()))); } + @Override public SELF emit(Consumer itemsConfigurer) { return this.emit(UUID.randomUUID().toString(), itemsConfigurer); } - public SELF tryC(String name, Consumer> itemsConfigurer) { + @Override + public SELF tryCatch(String name, Consumer> itemsConfigurer) { requireNameAndConfig(name, itemsConfigurer); final TryTaskBuilder tryBuilder = new TryTaskBuilder<>(this.newItemListBuilder()); itemsConfigurer.accept(tryBuilder); return addTaskItem(new TaskItem(name, new Task().withTryTask(tryBuilder.build()))); } - public SELF tryC(Consumer> itemsConfigurer) { - return this.tryC(UUID.randomUUID().toString(), itemsConfigurer); + @Override + public SELF tryCatch(Consumer> itemsConfigurer) { + return this.tryCatch(UUID.randomUUID().toString(), itemsConfigurer); } + @Override public SELF callHTTP(String name, Consumer itemsConfigurer) { requireNameAndConfig(name, itemsConfigurer); final CallHTTPTaskBuilder callHTTPBuilder = new CallHTTPTaskBuilder(); @@ -163,6 +191,7 @@ public SELF callHTTP(String name, Consumer itemsConfigurer) name, new Task().withCallTask(new CallTask().withCallHTTP(callHTTPBuilder.build())))); } + @Override public SELF callHTTP(Consumer itemsConfigurer) { return this.callHTTP(UUID.randomUUID().toString(), itemsConfigurer); } diff --git a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/DelegatingDoTaskFluent.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/DelegatingDoTaskFluent.java new file mode 100644 index 00000000..bfaa6817 --- /dev/null +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/DelegatingDoTaskFluent.java @@ -0,0 +1,163 @@ +/* + * 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.fluent.spec; + +import java.util.function.Consumer; + +/** + * Mixin that implements {@link DoTaskFluent} by delegating to another instance. + * + * @param the concrete delegating type + * @param the list-builder type used by nested constructs like for/try + */ +public interface DelegatingDoTaskFluent< + SELF extends DelegatingDoTaskFluent, LIST extends BaseTaskItemListBuilder> + extends DoTaskFluent, HasDelegate { + + @SuppressWarnings("unchecked") + default SELF self() { + return (SELF) this; + } + + LIST list(); + + @SuppressWarnings("unchecked") + private DoTaskFluent d() { + return (DoTaskFluent) this.delegate(); + } + + /* ---------- Forwarders ---------- */ + + @Override + default SELF set(String name, Consumer cfg) { + d().set(name, cfg); + return self(); + } + + @Override + default SELF set(Consumer cfg) { + d().set(cfg); + return self(); + } + + @Override + default SELF set(String name, String expr) { + d().set(name, expr); + return self(); + } + + @Override + default SELF set(String expr) { + d().set(expr); + return self(); + } + + @Override + default SELF forEach(String name, Consumer> cfg) { + d().forEach(name, cfg); + return self(); + } + + @Override + default SELF forEach(Consumer> cfg) { + d().forEach(cfg); + return self(); + } + + @Override + default SELF switchCase(String name, Consumer cfg) { + d().switchCase(name, cfg); + return self(); + } + + @Override + default SELF switchCase(Consumer cfg) { + d().switchCase(cfg); + return self(); + } + + @Override + default SELF raise(String name, Consumer cfg) { + d().raise(name, cfg); + return self(); + } + + @Override + default SELF raise(Consumer cfg) { + d().raise(cfg); + return self(); + } + + @Override + default SELF fork(String name, Consumer cfg) { + d().fork(name, cfg); + return self(); + } + + @Override + default SELF fork(Consumer cfg) { + d().fork(cfg); + return self(); + } + + @Override + default SELF listen(String name, Consumer cfg) { + d().listen(name, cfg); + return self(); + } + + @Override + default SELF listen(Consumer cfg) { + d().listen(cfg); + return self(); + } + + @Override + default SELF emit(String name, Consumer cfg) { + d().emit(name, cfg); + return self(); + } + + @Override + default SELF emit(Consumer cfg) { + d().emit(cfg); + return self(); + } + + @Override + default SELF tryCatch(String name, Consumer> cfg) { + d().tryCatch(name, cfg); + return self(); + } + + @Override + default SELF tryCatch(Consumer> cfg) { + d().tryCatch(cfg); + return self(); + } + + @Override + default SELF callHTTP(String name, Consumer cfg) { + d().callHTTP(name, cfg); + return self(); + } + + @Override + default SELF callHTTP(Consumer cfg) { + d().callHTTP(cfg); + return self(); + } +} 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 a9d4f6cb..3b1a768c 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 @@ -22,7 +22,7 @@ public class DoTaskBuilder extends BaseDoTaskBuilderCNCF + * DSL Reference - Do + * @param The TaskBaseBuilder constructor that sub-tasks will build + * @param The specialized BaseTaskItemListBuilder for sub-tasks that require a list of + * sub-tasks, such as `for`. + */ +public interface DoTaskFluent< + SELF extends DoTaskFluent, LIST extends BaseTaskItemListBuilder> { + + SELF set(String name, Consumer itemsConfigurer); + + SELF set(Consumer itemsConfigurer); + + SELF set(String name, final String expr); + + SELF set(final String expr); + + SELF forEach(String name, Consumer> itemsConfigurer); + + SELF forEach(Consumer> itemsConfigurer); + + SELF switchCase(String name, Consumer itemsConfigurer); + + SELF switchCase(Consumer itemsConfigurer); + + SELF raise(String name, Consumer itemsConfigurer); + + SELF raise(Consumer itemsConfigurer); + + SELF fork(String name, Consumer itemsConfigurer); + + SELF fork(Consumer itemsConfigurer); + + SELF listen(String name, Consumer itemsConfigurer); + + SELF listen(Consumer itemsConfigurer); + + SELF emit(String name, Consumer itemsConfigurer); + + SELF emit(Consumer itemsConfigurer); + + SELF tryCatch(String name, Consumer> itemsConfigurer); + + SELF tryCatch(Consumer> itemsConfigurer); + + SELF callHTTP(String name, Consumer itemsConfigurer); + + SELF callHTTP(Consumer itemsConfigurer); + + // ----- shortcuts/aliases + + default SELF sc(String name, Consumer cfg) { + return switchCase(name, cfg); + } + + default SELF sc(Consumer cfg) { + return switchCase(cfg); + } + + default SELF tc(String name, Consumer> cfg) { + return tryCatch(name, cfg); + } + + default SELF tc(Consumer> cfg) { + return tryCatch(cfg); + } +} diff --git a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/HasDelegate.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/HasDelegate.java new file mode 100644 index 00000000..f0b1e0b9 --- /dev/null +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/HasDelegate.java @@ -0,0 +1,21 @@ +/* + * 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.fluent.spec; + +public interface HasDelegate { + + Object delegate(); +} diff --git a/fluent/spec/src/test/java/io/serverlessworkflow/fluent/spec/WorkflowBuilderTest.java b/fluent/spec/src/test/java/io/serverlessworkflow/fluent/spec/WorkflowBuilderTest.java index 49d41dbb..dbb9f1d3 100644 --- a/fluent/spec/src/test/java/io/serverlessworkflow/fluent/spec/WorkflowBuilderTest.java +++ b/fluent/spec/src/test/java/io/serverlessworkflow/fluent/spec/WorkflowBuilderTest.java @@ -128,7 +128,7 @@ void testDoTaskMultipleTypes() { d -> d.set("init", s -> s.expr("$.init = true")) .forEach("items", f -> f.each("item").in("$.list")) - .switchC( + .switchCase( "choice", sw -> { // no-op configuration @@ -258,7 +258,7 @@ void testDoTaskTryCatchWithRetry() { WorkflowBuilder.workflow("flowTry") .tasks( d -> - d.tryC( + d.tryCatch( "tryBlock", t -> t.tryHandler(tb -> tb.set("init", s -> s.expr("$.start = true"))) @@ -306,7 +306,7 @@ void testDoTaskTryCatchErrorsFiltering() { WorkflowBuilder.workflow("flowCatch") .tasks( d -> - d.tryC( + d.tryCatch( "tryBlock", t -> t.tryHandler(tb -> tb.set("foo", s -> s.expr("$.foo = 'bar'"))) diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java index d9bf2824..4a343e3c 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java @@ -116,6 +116,13 @@ public static Optional optionalFilter(WorkflowApplication app, S return str != null ? Optional.of(buildWorkflowFilter(app, str)) : Optional.empty(); } + public static Optional optionalFilter( + WorkflowApplication app, Object obj, String str) { + return str != null || obj != null + ? Optional.of(buildWorkflowFilter(app, str, obj)) + : Optional.empty(); + } + public static String toString(UriTemplate template) { URI uri = template.getLiteralUri(); return uri != null ? uri.toString() : template.getLiteralUriTemplate(); diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java index 414c82c6..a9681f39 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java @@ -15,7 +15,8 @@ */ package io.serverlessworkflow.impl.executors; -import static io.serverlessworkflow.impl.WorkflowUtils.*; +import static io.serverlessworkflow.impl.WorkflowUtils.buildWorkflowFilter; +import static io.serverlessworkflow.impl.WorkflowUtils.getSchemaValidator; import io.serverlessworkflow.api.types.Export; import io.serverlessworkflow.api.types.FlowDirective; @@ -101,7 +102,7 @@ protected AbstractTaskExecutorBuilder( this.contextSchemaValidator = getSchemaValidator(application.validatorFactory(), resourceLoader, export.getSchema()); } - this.ifFilter = optionalFilter(application, task.getIf()); + this.ifFilter = application.expressionFactory().buildIfFilter(task); } protected final TransitionInfoBuilder next( diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionFactory.java index 696e4fda..cb14555e 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionFactory.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionFactory.java @@ -15,8 +15,10 @@ */ package io.serverlessworkflow.impl.expressions; +import io.serverlessworkflow.api.types.TaskBase; import io.serverlessworkflow.impl.WorkflowFilter; import io.serverlessworkflow.impl.WorkflowModelFactory; +import java.util.Optional; public interface ExpressionFactory { /** @@ -29,4 +31,8 @@ public interface ExpressionFactory { WorkflowFilter buildFilter(String expr, Object value); WorkflowModelFactory modelFactory(); + + default Optional buildIfFilter(TaskBase task) { + return task.getIf() != null ? Optional.of(buildFilter(task.getIf(), null)) : Optional.empty(); + } } diff --git a/pom.xml b/pom.xml index b456e6a4..428e82bb 100644 --- a/pom.xml +++ b/pom.xml @@ -150,6 +150,11 @@ slf4j-api ${version.org.slf4j} + + org.slf4j + slf4j-simple + ${version.org.slf4j} + io.serverlessworkflow serverlessworkflow-api pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy