diff --git a/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java b/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java index e64b07e0f..0a09beb6f 100644 --- a/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java +++ b/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java @@ -53,6 +53,7 @@ private static ObjectMapper configure(ObjectMapper mapper) { .configure(SerializationFeature.INDENT_OUTPUT, true) .configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false) .configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false) + .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false) .registerModule(validationModule) .registerModule(new JacksonMixInModule()) .findAndRegisterModules(); diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java index 31bf37b46..ab3cdf9df 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java @@ -15,10 +15,11 @@ */ package io.serverlessworkflow.fluent.func; +import io.serverlessworkflow.api.types.CallTask; import io.serverlessworkflow.api.types.func.CallJava; -import io.serverlessworkflow.api.types.func.CallTaskJava; import io.serverlessworkflow.api.types.func.ContextFunction; import io.serverlessworkflow.api.types.func.FilterFunction; +import io.serverlessworkflow.api.types.func.SerializableFunction; import io.serverlessworkflow.fluent.func.spi.ConditionalTaskBuilder; import io.serverlessworkflow.fluent.func.spi.FuncTaskTransformations; import io.serverlessworkflow.fluent.spec.TaskBaseBuilder; @@ -29,19 +30,16 @@ public class FuncCallTaskBuilder extends TaskBaseBuilder implements FuncTaskTransformations, ConditionalTaskBuilder { - private CallTaskJava callTaskJava; + private CallTask callTaskJava; - FuncCallTaskBuilder() { - callTaskJava = new CallTaskJava(new CallJava() {}); - super.setTask(callTaskJava.getCallJava()); - } + FuncCallTaskBuilder() {} @Override protected FuncCallTaskBuilder self() { return this; } - public FuncCallTaskBuilder function(Function function) { + public FuncCallTaskBuilder function(SerializableFunction function) { return function(function, null); } @@ -51,8 +49,9 @@ public FuncCallTaskBuilder function(Function function, Class arg public FuncCallTaskBuilder function( Function function, Class argClass, Class returnClass) { - this.callTaskJava = new CallTaskJava(CallJava.function(function, argClass, returnClass)); - super.setTask(this.callTaskJava.getCallJava()); + this.callTaskJava = + new CallTask().withCallFunction(CallJava.function(function, argClass, returnClass)); + super.setTask(this.callTaskJava.getCallFunction()); return this; } @@ -66,8 +65,9 @@ public FuncCallTaskBuilder function(ContextFunction function, Class public FuncCallTaskBuilder function( ContextFunction function, Class argClass, Class returnClass) { - this.callTaskJava = new CallTaskJava(CallJava.function(function, argClass, returnClass)); - super.setTask(this.callTaskJava.getCallJava()); + this.callTaskJava = + new CallTask().withCallFunction(CallJava.function(function, argClass, returnClass)); + super.setTask(this.callTaskJava.getCallFunction()); return this; } @@ -81,26 +81,31 @@ public FuncCallTaskBuilder function(FilterFunction function, Class< public FuncCallTaskBuilder function( FilterFunction function, Class argClass, Class outputClass) { - this.callTaskJava = new CallTaskJava(CallJava.function(function, argClass, outputClass)); - super.setTask(this.callTaskJava.getCallJava()); + this.callTaskJava = + new CallTask().withCallFunction(CallJava.function(function, argClass, outputClass)); + super.setTask(this.callTaskJava.getCallFunction()); return this; } /** Accept a side-effect Consumer; engine should pass input through unchanged. */ public FuncCallTaskBuilder consumer(Consumer consumer) { - this.callTaskJava = new CallTaskJava(CallJava.consumer(consumer)); - super.setTask(this.callTaskJava.getCallJava()); + this.callTaskJava = new CallTask().withCallFunction(CallJava.consumer(consumer)); + super.setTask(this.callTaskJava.getCallFunction()); return this; } /** Accept a Consumer with explicit input type hint. */ public FuncCallTaskBuilder consumer(Consumer consumer, Class argClass) { - this.callTaskJava = new CallTaskJava(CallJava.consumer(consumer, argClass)); - super.setTask(this.callTaskJava.getCallJava()); + this.callTaskJava = new CallTask().withCallFunction(CallJava.consumer(consumer, argClass)); + super.setTask(this.callTaskJava.getCallFunction()); return this; } - public CallTaskJava build() { + public CallTask build() { + if (this.callTaskJava == null) { + throw new IllegalStateException( + "Call task is not configured. Call function(...) or consumer(...) before build()."); + } return this.callTaskJava; } } diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncEmitEventPropertiesBuilder.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncEmitEventPropertiesBuilder.java index 625b224af..a71e383ee 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncEmitEventPropertiesBuilder.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncEmitEventPropertiesBuilder.java @@ -16,10 +16,10 @@ package io.serverlessworkflow.fluent.func; import io.cloudevents.CloudEventData; -import io.serverlessworkflow.api.reflection.func.SerializableFunction; import io.serverlessworkflow.api.types.func.ContextFunction; import io.serverlessworkflow.api.types.func.EventDataFunction; import io.serverlessworkflow.api.types.func.FilterFunction; +import io.serverlessworkflow.api.types.func.SerializableFunction; import io.serverlessworkflow.fluent.spec.AbstractEventPropertiesBuilder; import java.util.function.Function; diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForTaskBuilder.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForTaskBuilder.java index 3e0a59d60..cf58071ac 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForTaskBuilder.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForTaskBuilder.java @@ -15,15 +15,16 @@ */ package io.serverlessworkflow.fluent.func; +import io.serverlessworkflow.api.types.CallTask; +import io.serverlessworkflow.api.types.ForTask; import io.serverlessworkflow.api.types.ForTaskConfiguration; 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.api.types.func.ForTaskFunction; import io.serverlessworkflow.api.types.func.LoopFunction; import io.serverlessworkflow.api.types.func.LoopPredicate; import io.serverlessworkflow.api.types.func.LoopPredicateIndex; +import io.serverlessworkflow.api.types.utils.ForTaskFunction; import io.serverlessworkflow.fluent.func.spi.ConditionalTaskBuilder; import io.serverlessworkflow.fluent.func.spi.FuncTaskTransformations; import io.serverlessworkflow.fluent.spec.TaskBaseBuilder; @@ -39,14 +40,14 @@ public class FuncForTaskBuilder extends TaskBaseBuilder ConditionalTaskBuilder, ForEachTaskFluent { - private final ForTaskFunction forTaskFunction; + private final ForTask forTask; private final List items; FuncForTaskBuilder() { - this.forTaskFunction = new ForTaskFunction(); - this.forTaskFunction.withFor(new ForTaskConfiguration()); + this.forTask = new ForTask(); + this.forTask.withFor(new ForTaskConfiguration()); this.items = new ArrayList<>(); - super.setTask(forTaskFunction); + super.setTask(forTask); } @Override @@ -55,23 +56,23 @@ protected FuncForTaskBuilder self() { } public FuncForTaskBuilder whileC(LoopPredicate predicate) { - this.forTaskFunction.withWhile(predicate); + ForTaskFunction.withWhile(forTask, predicate); return this; } public FuncForTaskBuilder whileC(LoopPredicateIndex predicate) { - this.forTaskFunction.withWhile(predicate); + ForTaskFunction.withWhile(forTask, predicate); return this; } public FuncForTaskBuilder collection(Function> collectionF) { - this.forTaskFunction.withCollection(collectionF); + ForTaskFunction.withCollection(forTask, collectionF); return this; } public FuncForTaskBuilder collection( Function> collectionF, Class clazz) { - this.forTaskFunction.withCollection(collectionF, clazz); + ForTaskFunction.withCollection(forTask, collectionF, clazz); return this; } @@ -84,9 +85,9 @@ public FuncForTaskBuilder tasks(String name, LoopFunction fun name, new Task() .withCallTask( - new CallTaskJava( - CallJava.loopFunction( - function, this.forTaskFunction.getFor().getEach()))))); + new CallTask() + .withCallFunction( + CallJava.loopFunction(function, this.forTask.getFor().getEach()))))); return this; } @@ -96,25 +97,25 @@ public FuncForTaskBuilder tasks(LoopFunction function) { @Override public FuncForTaskBuilder each(String each) { - this.forTaskFunction.getFor().withEach(each); + this.forTask.getFor().withEach(each); return this; } @Override public FuncForTaskBuilder in(String in) { - this.forTaskFunction.getFor().withIn(in); + this.forTask.getFor().withIn(in); return this; } @Override public FuncForTaskBuilder at(String at) { - this.forTaskFunction.getFor().withAt(at); + this.forTask.getFor().withAt(at); return this; } @Override public FuncForTaskBuilder whileC(String expression) { - this.forTaskFunction.setWhile(expression); + this.forTask.setWhile(expression); return this; } @@ -125,8 +126,8 @@ public FuncForTaskBuilder tasks(Consumer consumer) { return this; } - public ForTaskFunction build() { - this.forTaskFunction.setDo(this.items); - return this.forTaskFunction; + public ForTask build() { + this.forTask.setDo(this.items); + return this.forTask; } } diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForkTaskBuilder.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForkTaskBuilder.java index f7f87e62d..9d43116ce 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForkTaskBuilder.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForkTaskBuilder.java @@ -15,10 +15,10 @@ */ package io.serverlessworkflow.fluent.func; +import io.serverlessworkflow.api.types.CallTask; 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.func.spi.ConditionalTaskBuilder; import io.serverlessworkflow.fluent.func.spi.FuncTaskTransformations; import io.serverlessworkflow.fluent.spec.AbstractForkTaskBuilder; @@ -61,7 +61,8 @@ public FuncForkTaskBuilder branch( this.defaultBranchName(name, this.currentOffset()), new Task() .withCallTask( - new CallTaskJava(CallJava.function(function, argParam, returnClass))))); + new CallTask() + .withCallFunction(CallJava.function(function, argParam, returnClass))))); return this; } diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncListenTaskBuilder.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncListenTaskBuilder.java index 91f9b7dfa..b2478157a 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncListenTaskBuilder.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncListenTaskBuilder.java @@ -15,9 +15,8 @@ */ package io.serverlessworkflow.fluent.func; -import io.serverlessworkflow.api.types.AnyEventConsumptionStrategy; -import io.serverlessworkflow.api.types.ListenTask; -import io.serverlessworkflow.api.types.func.UntilPredicate; +import io.serverlessworkflow.api.types.utils.TaskPredicate; +import io.serverlessworkflow.api.types.utils.TypesUtils; import io.serverlessworkflow.fluent.func.spi.ConditionalTaskBuilder; import io.serverlessworkflow.fluent.func.spi.FuncTaskTransformations; import io.serverlessworkflow.fluent.spec.AbstractListenTaskBuilder; @@ -28,14 +27,13 @@ public class FuncListenTaskBuilder implements ConditionalTaskBuilder, FuncTaskTransformations { - private UntilPredicate untilPredicate; - FuncListenTaskBuilder(FuncTaskItemListBuilder factory) { super(factory); } public FuncListenTaskBuilder until(Predicate predicate, Class predClass) { - untilPredicate = new UntilPredicate().withPredicate(predicate, predClass); + TaskPredicate.withPredicate( + super.getListenTask(), TypesUtils.UNTIL_PRED_NAME, predicate, predClass); return this; } @@ -46,17 +44,6 @@ protected FuncListenTaskBuilder self() { @Override protected FuncListenToBuilder newEventConsumptionStrategyBuilder() { - return new FuncListenToBuilder(); - } - - @Override - public ListenTask build() { - ListenTask task = super.build(); - AnyEventConsumptionStrategy anyEvent = - task.getListen().getTo().getAnyEventConsumptionStrategy(); - if (untilPredicate != null && anyEvent != null) { - anyEvent.withUntil(untilPredicate); - } - return task; + return new FuncListenToBuilder(super.getListenTask()); } } diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncListenToBuilder.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncListenToBuilder.java index 54dc2394e..1f6585ca0 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncListenToBuilder.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncListenToBuilder.java @@ -17,12 +17,14 @@ import io.serverlessworkflow.api.types.AllEventConsumptionStrategy; import io.serverlessworkflow.api.types.AnyEventConsumptionStrategy; +import io.serverlessworkflow.api.types.ListenTask; import io.serverlessworkflow.api.types.ListenTo; import io.serverlessworkflow.api.types.OneEventConsumptionStrategy; import io.serverlessworkflow.api.types.Until; import io.serverlessworkflow.api.types.func.ContextPredicate; import io.serverlessworkflow.api.types.func.FilterPredicate; -import io.serverlessworkflow.api.types.func.UntilPredicate; +import io.serverlessworkflow.api.types.utils.TaskPredicate; +import io.serverlessworkflow.api.types.utils.TypesUtils; import io.serverlessworkflow.fluent.spec.AbstractEventConsumptionStrategyBuilder; import java.util.function.Predicate; @@ -31,6 +33,11 @@ public class FuncListenToBuilder FuncListenToBuilder, ListenTo, FuncEventFilterBuilder> { private final ListenTo listenTo = new ListenTo(); + private final ListenTask listenTask; + + public FuncListenToBuilder(ListenTask listenTask) { + this.listenTask = listenTask; + } @Override protected FuncEventFilterBuilder newEventFilterBuilder() { @@ -65,17 +72,17 @@ protected void setUntilForAny(Until until) { } public FuncListenToBuilder until(Predicate predicate, Class predClass) { - this.setUntil(new UntilPredicate().withPredicate(predicate, predClass)); + TaskPredicate.withPredicate(listenTask, TypesUtils.UNTIL_PRED_NAME, predicate, predClass); return this; } public FuncListenToBuilder until(ContextPredicate predicate, Class predClass) { - this.setUntil(new UntilPredicate().withPredicate(predicate, predClass)); + TaskPredicate.withPredicate(listenTask, TypesUtils.UNTIL_PRED_NAME, predicate, predClass); return this; } public FuncListenToBuilder until(FilterPredicate predicate, Class predClass) { - this.setUntil(new UntilPredicate().withPredicate(predicate, predClass)); + TaskPredicate.withPredicate(listenTask, TypesUtils.UNTIL_PRED_NAME, predicate, predClass); return this; } } diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncSetTaskBuilder.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncSetTaskBuilder.java index d8c3d0f0f..aaad58c84 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncSetTaskBuilder.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncSetTaskBuilder.java @@ -15,7 +15,7 @@ */ package io.serverlessworkflow.fluent.func; -import io.serverlessworkflow.api.types.func.MapSetTaskConfiguration; +import io.serverlessworkflow.api.types.utils.MapSetTaskConfiguration; import io.serverlessworkflow.fluent.func.spi.ConditionalTaskBuilder; import io.serverlessworkflow.fluent.spec.SetTaskBuilder; import java.util.Map; @@ -24,7 +24,7 @@ public class FuncSetTaskBuilder extends SetTaskBuilder implements ConditionalTaskBuilder { public FuncSetTaskBuilder expr(Map map) { - this.setTaskConfiguration = new MapSetTaskConfiguration(map); + this.setTaskConfiguration = MapSetTaskConfiguration.map(map); return this; } } diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncSwitchTaskBuilder.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncSwitchTaskBuilder.java index 5e3838702..90fb37d5e 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncSwitchTaskBuilder.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncSwitchTaskBuilder.java @@ -20,7 +20,7 @@ import io.serverlessworkflow.api.types.SwitchCase; import io.serverlessworkflow.api.types.SwitchItem; import io.serverlessworkflow.api.types.SwitchTask; -import io.serverlessworkflow.api.types.func.SwitchCasePredicate; +import io.serverlessworkflow.api.types.utils.TaskPredicate; import io.serverlessworkflow.fluent.func.spi.ConditionalTaskBuilder; import io.serverlessworkflow.fluent.func.spi.FuncTaskTransformations; import io.serverlessworkflow.fluent.spec.TaskBaseBuilder; @@ -56,20 +56,21 @@ public FuncSwitchTaskBuilder onPredicate(Consumer co public FuncSwitchTaskBuilder onPredicate( String name, Consumer consumer) { - final SwitchCasePredicateBuilder switchCase = new SwitchCasePredicateBuilder(); + final SwitchCasePredicateBuilder switchCase = + new SwitchCasePredicateBuilder(defaultItemNameIfBlank(name)); consumer.accept(switchCase); - final SwitchCasePredicate switchCaseValue = (SwitchCasePredicate) switchCase.build(); + final SwitchCase switchCaseValue = switchCase.build(); // Handling default cases - if (switchCaseValue.predicate() == null) { - Objects.requireNonNull(switchCaseValue.getThen(), "When is required"); + if (TaskPredicate.predicate(switchTask, switchCase.name) == null) { + Objects.requireNonNull(switchCaseValue.getThen(), "Then is required"); if (switchCaseValue.getThen().getFlowDirectiveEnum() != null) { return this.onDefault(switchCaseValue.getThen().getFlowDirectiveEnum()); } else { return this.onDefault(switchCaseValue.getThen().getString()); } } - this.switchItems.add(new SwitchItem(defaultItemNameIfBlank(name), switchCase.build())); + this.switchItems.add(new SwitchItem(switchCase.name, switchCase.build())); return this; } @@ -91,20 +92,22 @@ public SwitchTask build() { return switchTask; } - public static final class SwitchCasePredicateBuilder { - private final SwitchCasePredicate switchCase; + public final class SwitchCasePredicateBuilder { + private final SwitchCase switchCase; + private final String name; - SwitchCasePredicateBuilder() { - this.switchCase = new SwitchCasePredicate(); + SwitchCasePredicateBuilder(String name) { + this.name = name; + this.switchCase = new SwitchCase(); } public SwitchCasePredicateBuilder when(Predicate when) { - this.switchCase.withPredicate(when); + TaskPredicate.withPredicate(FuncSwitchTaskBuilder.this.switchTask, name, when); return this; } public SwitchCasePredicateBuilder when(Predicate when, Class whenClass) { - this.switchCase.withPredicate(when, whenClass); + TaskPredicate.withPredicate(FuncSwitchTaskBuilder.this.switchTask, name, when, whenClass); return this; } diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTaskItemListBuilder.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTaskItemListBuilder.java index 07b32c5f8..86daac8cd 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTaskItemListBuilder.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTaskItemListBuilder.java @@ -27,6 +27,7 @@ import io.serverlessworkflow.fluent.spec.WaitTaskBuilder; import io.serverlessworkflow.fluent.spec.WorkflowTaskBuilder; import java.util.List; +import java.util.Map; import java.util.function.Consumer; public class FuncTaskItemListBuilder extends BaseTaskItemListBuilder @@ -78,6 +79,10 @@ public FuncTaskItemListBuilder set(String name, String expr) { return this.set(name, s -> s.expr(expr)); } + public FuncTaskItemListBuilder set(String name, Map map) { + return this.set(name, s -> s.expr(map)); + } + @Override public FuncTaskItemListBuilder emit(String name, Consumer itemsConfigurer) { name = this.defaultNameAndRequireConfig(name, itemsConfigurer, TYPE_EMIT); diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncWorkflowBuilder.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncWorkflowBuilder.java index c84eed601..6f5dd62f7 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncWorkflowBuilder.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncWorkflowBuilder.java @@ -15,6 +15,9 @@ */ package io.serverlessworkflow.fluent.func; +import static io.serverlessworkflow.types.Defaults.DEFAULT_NAMESPACE; +import static io.serverlessworkflow.types.Defaults.DEFAULT_VERSION; + import io.serverlessworkflow.fluent.func.spi.FuncTransformations; import io.serverlessworkflow.fluent.spec.BaseWorkflowBuilder; import java.util.UUID; diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/CommonFuncOps.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/CommonFuncOps.java index 202813c7b..1bc386294 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/CommonFuncOps.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/CommonFuncOps.java @@ -15,10 +15,10 @@ */ package io.serverlessworkflow.fluent.func.dsl; -import io.serverlessworkflow.api.reflection.func.ReflectionUtils; -import io.serverlessworkflow.api.reflection.func.SerializableFunction; -import io.serverlessworkflow.api.reflection.func.SerializablePredicate; import io.serverlessworkflow.api.types.FlowDirectiveEnum; +import io.serverlessworkflow.api.types.func.SerializableFunction; +import io.serverlessworkflow.api.types.func.SerializablePredicate; +import io.serverlessworkflow.api.types.utils.ReflectionUtils; import io.serverlessworkflow.fluent.func.FuncCallTaskBuilder; import io.serverlessworkflow.fluent.func.FuncSwitchTaskBuilder; import io.serverlessworkflow.fluent.func.configurers.SwitchCaseConfigurer; diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncDSL.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncDSL.java index 160d1b084..34e37b3fc 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncDSL.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncDSL.java @@ -16,17 +16,17 @@ package io.serverlessworkflow.fluent.func.dsl; import io.cloudevents.CloudEventData; -import io.serverlessworkflow.api.reflection.func.InstanceIdFunction; -import io.serverlessworkflow.api.reflection.func.ReflectionUtils; -import io.serverlessworkflow.api.reflection.func.SerializableConsumer; -import io.serverlessworkflow.api.reflection.func.SerializableFunction; -import io.serverlessworkflow.api.reflection.func.SerializablePredicate; -import io.serverlessworkflow.api.reflection.func.UniqueIdBiFunction; import io.serverlessworkflow.api.types.FlowDirectiveEnum; import io.serverlessworkflow.api.types.OAuth2AuthenticationData; import io.serverlessworkflow.api.types.func.ContextFunction; import io.serverlessworkflow.api.types.func.FilterFunction; +import io.serverlessworkflow.api.types.func.InstanceIdFunction; import io.serverlessworkflow.api.types.func.LoopFunction; +import io.serverlessworkflow.api.types.func.SerializableConsumer; +import io.serverlessworkflow.api.types.func.SerializableFunction; +import io.serverlessworkflow.api.types.func.SerializablePredicate; +import io.serverlessworkflow.api.types.func.UniqueIdBiFunction; +import io.serverlessworkflow.api.types.utils.ReflectionUtils; import io.serverlessworkflow.fluent.func.FuncCallTaskBuilder; import io.serverlessworkflow.fluent.func.FuncEmitTaskBuilder; import io.serverlessworkflow.fluent.func.FuncRaiseTaskBuilder; @@ -1600,7 +1600,7 @@ public static FuncTaskConfigurer forEach( } public static FuncTaskConfigurer forEachItem( - SerializableFunction> collection, Function function) { + SerializableFunction> collection, SerializableFunction function) { return forEachItem(null, collection, function); } diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncEmitSpec.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncEmitSpec.java index 7b7f9beec..0e4e079f4 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncEmitSpec.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncEmitSpec.java @@ -18,10 +18,10 @@ import io.cloudevents.CloudEventData; import io.cloudevents.core.data.BytesCloudEventData; import io.cloudevents.core.data.PojoCloudEventData; -import io.serverlessworkflow.api.reflection.func.ReflectionUtils; -import io.serverlessworkflow.api.reflection.func.SerializableFunction; import io.serverlessworkflow.api.types.func.ContextFunction; import io.serverlessworkflow.api.types.func.EventDataFunction; +import io.serverlessworkflow.api.types.func.SerializableFunction; +import io.serverlessworkflow.api.types.utils.ReflectionUtils; import io.serverlessworkflow.fluent.func.FuncEmitEventPropertiesBuilder; import io.serverlessworkflow.fluent.func.FuncEmitTaskBuilder; import io.serverlessworkflow.fluent.func.configurers.FuncEmitConfigurer; diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncEventFilterSpec.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncEventFilterSpec.java index 3b8c08236..49f92f0f6 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncEventFilterSpec.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncEventFilterSpec.java @@ -21,9 +21,9 @@ import io.cloudevents.core.CloudEventUtils; import io.cloudevents.core.data.PojoCloudEventData; import io.cloudevents.jackson.PojoCloudEventDataMapper; -import io.serverlessworkflow.api.reflection.func.SerializablePredicate; import io.serverlessworkflow.api.types.func.ContextPredicate; import io.serverlessworkflow.api.types.func.FilterPredicate; +import io.serverlessworkflow.api.types.func.SerializablePredicate; import io.serverlessworkflow.fluent.func.FuncEventFilterBuilder; import io.serverlessworkflow.fluent.func.FuncEventFilterPropertiesBuilder; import io.serverlessworkflow.fluent.spec.dsl.AbstractEventFilterSpec; 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 2f5055b09..c5d218791 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 @@ -15,12 +15,12 @@ */ package io.serverlessworkflow.fluent.func.dsl; -import io.serverlessworkflow.api.reflection.func.ReflectionUtils; -import io.serverlessworkflow.api.reflection.func.SerializableFunction; -import io.serverlessworkflow.api.reflection.func.SerializablePredicate; import io.serverlessworkflow.api.types.FlowDirectiveEnum; import io.serverlessworkflow.api.types.func.ContextFunction; import io.serverlessworkflow.api.types.func.FilterFunction; +import io.serverlessworkflow.api.types.func.SerializableFunction; +import io.serverlessworkflow.api.types.func.SerializablePredicate; +import io.serverlessworkflow.api.types.utils.ReflectionUtils; import io.serverlessworkflow.fluent.func.FuncTaskItemListBuilder; import io.serverlessworkflow.fluent.func.configurers.FuncTaskConfigurer; import io.serverlessworkflow.fluent.func.spi.ConditionalTaskBuilder; diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/spi/ConditionalTaskBuilderHelper.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/spi/ConditionalTaskBuilderHelper.java index 839dce07d..3d09eddfc 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/spi/ConditionalTaskBuilderHelper.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/spi/ConditionalTaskBuilderHelper.java @@ -16,19 +16,13 @@ package io.serverlessworkflow.fluent.func.spi; import io.serverlessworkflow.api.types.TaskBase; -import io.serverlessworkflow.api.types.TaskMetadata; -import io.serverlessworkflow.api.types.func.TaskMetadataKeys; +import io.serverlessworkflow.api.types.utils.TypesUtils; class ConditionalTaskBuilderHelper { private ConditionalTaskBuilderHelper() {} static void setMetadata(TaskBase task, Object predicate) { - TaskMetadata metadata = task.getMetadata(); - if (metadata == null) { - metadata = new TaskMetadata(); - task.setMetadata(metadata); - } - metadata.setAdditionalProperty(TaskMetadataKeys.IF_PREDICATE, predicate); + TypesUtils.initMetadata(task).setAdditionalProperty(TypesUtils.IF_PREDICATE, predicate); } } diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/spi/FuncTaskTransformations.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/spi/FuncTaskTransformations.java index 74be14047..0d741d251 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/spi/FuncTaskTransformations.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/spi/FuncTaskTransformations.java @@ -16,9 +16,12 @@ package io.serverlessworkflow.fluent.func.spi; import io.serverlessworkflow.api.types.Export; +import io.serverlessworkflow.api.types.ExportAs; import io.serverlessworkflow.api.types.func.ContextFunction; -import io.serverlessworkflow.api.types.func.ExportAsFunction; import io.serverlessworkflow.api.types.func.FilterFunction; +import io.serverlessworkflow.api.types.func.TypedContextFunction; +import io.serverlessworkflow.api.types.func.TypedFilterFunction; +import io.serverlessworkflow.api.types.func.TypedFunction; import io.serverlessworkflow.fluent.spec.spi.TaskTransformationHandlers; import java.util.function.Function; @@ -27,37 +30,43 @@ public interface FuncTaskTransformations SELF exportAs(Function function) { - setExport(new Export().withAs(new ExportAsFunction().withFunction(function))); + setExport(new Export().withAs(new ExportAs().withObject(function))); return (SELF) this; } @SuppressWarnings("unchecked") default SELF exportAs(Function function, Class argClass) { - setExport(new Export().withAs(new ExportAsFunction().withFunction(function, argClass))); + setExport( + new Export() + .withAs(new ExportAs().withObject(new TypedFunction(function, argClass)))); return (SELF) this; } @SuppressWarnings("unchecked") default SELF exportAs(FilterFunction function) { - setExport(new Export().withAs(new ExportAsFunction().withFunction(function))); + setExport(new Export().withAs(new ExportAs().withObject(function))); return (SELF) this; } @SuppressWarnings("unchecked") default SELF exportAs(FilterFunction function, Class argClass) { - setExport(new Export().withAs(new ExportAsFunction().withFunction(function, argClass))); + setExport( + new Export() + .withAs(new ExportAs().withObject(new TypedFilterFunction<>(function, argClass)))); return (SELF) this; } @SuppressWarnings("unchecked") default SELF exportAs(ContextFunction function) { - setExport(new Export().withAs(new ExportAsFunction().withFunction(function))); + setExport(new Export().withAs(new ExportAs().withObject(function))); return (SELF) this; } @SuppressWarnings("unchecked") default SELF exportAs(ContextFunction function, Class argClass) { - setExport(new Export().withAs(new ExportAsFunction().withFunction(function, argClass))); + setExport( + new Export() + .withAs(new ExportAs().withObject(new TypedContextFunction<>(function, argClass)))); return (SELF) this; } } diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/spi/FuncTransformations.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/spi/FuncTransformations.java index eb21f7aeb..8d1dc8ec2 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/spi/FuncTransformations.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/spi/FuncTransformations.java @@ -21,8 +21,9 @@ import io.serverlessworkflow.api.types.OutputAs; import io.serverlessworkflow.api.types.func.ContextFunction; import io.serverlessworkflow.api.types.func.FilterFunction; -import io.serverlessworkflow.api.types.func.InputFromFunction; -import io.serverlessworkflow.api.types.func.OutputAsFunction; +import io.serverlessworkflow.api.types.func.TypedContextFunction; +import io.serverlessworkflow.api.types.func.TypedFilterFunction; +import io.serverlessworkflow.api.types.func.TypedFunction; import io.serverlessworkflow.fluent.spec.spi.TransformationHandlers; import java.util.function.Function; @@ -31,37 +32,42 @@ public interface FuncTransformations> @SuppressWarnings("unchecked") default SELF inputFrom(Function function) { - setInput(new Input().withFrom(new InputFromFunction().withFunction(function))); + setInput(new Input().withFrom(new InputFrom().withObject(function))); return (SELF) this; } @SuppressWarnings("unchecked") default SELF inputFrom(Function function, Class argClass) { - setInput(new Input().withFrom(new InputFromFunction().withFunction(function, argClass))); + setInput( + new Input().withFrom(new InputFrom().withObject(new TypedFunction<>(function, argClass)))); return (SELF) this; } @SuppressWarnings("unchecked") default SELF inputFrom(FilterFunction function) { - setInput(new Input().withFrom(new InputFromFunction().withFunction(function))); + setInput(new Input().withFrom(new InputFrom().withObject(function))); return (SELF) this; } @SuppressWarnings("unchecked") default SELF inputFrom(FilterFunction function, Class argClass) { - setInput(new Input().withFrom(new InputFromFunction().withFunction(function, argClass))); + setInput( + new Input() + .withFrom(new InputFrom().withObject(new TypedFilterFunction<>(function, argClass)))); return (SELF) this; } @SuppressWarnings("unchecked") default SELF inputFrom(ContextFunction function) { - setInput(new Input().withFrom(new InputFromFunction().withFunction(function))); + setInput(new Input().withFrom(new InputFrom().withObject((function)))); return (SELF) this; } @SuppressWarnings("unchecked") default SELF inputFrom(ContextFunction function, Class argClass) { - setInput(new Input().withFrom(new InputFromFunction().withFunction(function, argClass))); + setInput( + new Input() + .withFrom(new InputFrom().withObject(new TypedContextFunction<>(function, argClass)))); return (SELF) this; } @@ -73,37 +79,42 @@ default SELF inputFrom(String jqExpression) { @SuppressWarnings("unchecked") default SELF outputAs(Function function) { - setOutput(new Output().withAs(new OutputAsFunction().withFunction(function))); + setOutput(new Output().withAs(new OutputAs().withObject(function))); return (SELF) this; } @SuppressWarnings("unchecked") default SELF outputAs(Function function, Class argClass) { - setOutput(new Output().withAs(new OutputAsFunction().withFunction(function, argClass))); + setOutput( + new Output().withAs(new OutputAs().withObject(new TypedFunction<>(function, argClass)))); return (SELF) this; } @SuppressWarnings("unchecked") default SELF outputAs(FilterFunction function) { - setOutput(new Output().withAs(new OutputAsFunction().withFunction(function))); + setOutput(new Output().withAs(new OutputAs().withObject(function))); return (SELF) this; } @SuppressWarnings("unchecked") default SELF outputAs(FilterFunction function, Class argClass) { - setOutput(new Output().withAs(new OutputAsFunction().withFunction(function, argClass))); + setOutput( + new Output() + .withAs(new OutputAs().withObject(new TypedFilterFunction<>(function, argClass)))); return (SELF) this; } @SuppressWarnings("unchecked") default SELF outputAs(ContextFunction function) { - setOutput(new Output().withAs(new OutputAsFunction().withFunction(function))); + setOutput(new Output().withAs(new OutputAs().withObject(function))); return (SELF) this; } @SuppressWarnings("unchecked") default SELF outputAs(ContextFunction function, Class argClass) { - setOutput(new Output().withAs(new OutputAsFunction().withFunction(function, argClass))); + setOutput( + new Output() + .withAs(new OutputAs().withObject(new TypedContextFunction<>(function, argClass)))); return (SELF) this; } diff --git a/experimental/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/FuncDSLTaskNameTest.java b/experimental/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/FuncDSLTaskNameTest.java index 90af3b688..e2ca64d92 100644 --- a/experimental/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/FuncDSLTaskNameTest.java +++ b/experimental/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/FuncDSLTaskNameTest.java @@ -22,7 +22,6 @@ import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.switchWhen; import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.switchWhenOrElse; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -32,7 +31,6 @@ import io.serverlessworkflow.api.types.TaskItem; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.api.types.func.LoopFunction; -import io.serverlessworkflow.api.types.func.SwitchCasePredicate; import io.serverlessworkflow.fluent.func.configurers.FuncTaskConfigurer; import java.util.List; import org.junit.jupiter.api.DisplayName; @@ -61,7 +59,6 @@ void namedPredicateOverload() { buildItems(switchWhen("checkSign", (Integer v) -> v > 0, "positive", Integer.class)); assertEquals("checkSign", items.get(0).getName()); SwitchCase sc = items.get(0).getTask().getSwitchTask().getSwitch().get(0).getSwitchCase(); - assertInstanceOf(SwitchCasePredicate.class, sc); assertEquals("positive", sc.getThen().getString()); } @@ -104,7 +101,6 @@ void namedPredicateDirective() { assertEquals("scoreGate", items.get(0).getName()); var cases = items.get(0).getTask().getSwitchTask().getSwitch(); assertEquals(2, cases.size()); - assertInstanceOf(SwitchCasePredicate.class, cases.get(0).getSwitchCase()); assertEquals("pass", cases.get(0).getSwitchCase().getThen().getString()); assertEquals( FlowDirectiveEnum.END, cases.get(1).getSwitchCase().getThen().getFlowDirectiveEnum()); diff --git a/experimental/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/FuncDSLTest.java b/experimental/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/FuncDSLTest.java index 1f518e99c..b4d89fa28 100644 --- a/experimental/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/FuncDSLTest.java +++ b/experimental/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/FuncDSLTest.java @@ -32,6 +32,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import io.serverlessworkflow.api.types.CallFunction; import io.serverlessworkflow.api.types.CallGRPC; import io.serverlessworkflow.api.types.CallHTTP; import io.serverlessworkflow.api.types.Export; @@ -41,7 +42,6 @@ import io.serverlessworkflow.api.types.Task; import io.serverlessworkflow.api.types.TaskItem; import io.serverlessworkflow.api.types.Workflow; -import io.serverlessworkflow.api.types.func.CallJava; import io.serverlessworkflow.api.types.func.FilterFunction; import io.serverlessworkflow.fluent.func.dsl.FuncDSL; import java.net.URI; @@ -68,7 +68,7 @@ void function_step_exportAs_function_sets_export() { Task t = items.get(0).getTask(); assertNotNull(t.getCallTask(), "CallTask expected"); - Export ex = ((CallJava) t.getCallTask().get()).getExport(); + Export ex = ((CallFunction) t.getCallTask().get()).getExport(); assertNotNull(ex, "Export should be set via Step.exportAs(Function)"); assertNotNull(ex.getAs(), "'as' should be populated"); // functional export should not produce a literal string @@ -174,7 +174,7 @@ void mixed_chaining_order_and_exports() { assertNotNull(t2.getListenTask()); assertNotNull( - ((CallJava) t0.getCallTask().get()).getExport(), "function step should carry export"); + ((CallFunction) t0.getCallTask().get()).getExport(), "function step should carry export"); assertNotNull(t2.getListenTask().getExport(), "listen step should carry export"); } @@ -465,7 +465,7 @@ void function_step_then_task_name_sets_flow_directive() { Task t = items.get(0).getTask(); assertNotNull(t.getCallTask(), "CallTask expected"); - CallJava callJava = (CallJava) t.getCallTask().get(); + CallFunction callJava = (CallFunction) t.getCallTask().get(); assertNotNull(callJava.getThen(), "then() should be set on the task"); assertEquals("otherTask", callJava.getThen().getString(), "then() should point to 'otherTask'"); } @@ -484,7 +484,7 @@ void function_step_then_flow_directive_enum_sets_end() { Task t = items.get(0).getTask(); assertNotNull(t.getCallTask(), "CallTask expected"); - CallJava callJava = (CallJava) t.getCallTask().get(); + CallFunction callJava = (CallFunction) t.getCallTask().get(); assertNotNull(callJava.getThen(), "then() should be set on the task"); assertEquals( FlowDirectiveEnum.END, @@ -510,7 +510,7 @@ void consume_step_then_task_name_sets_flow_directive() { Task t = items.get(0).getTask(); assertNotNull(t.getCallTask(), "CallTask expected for consume step"); - CallJava callJava = (CallJava) t.getCallTask().get(); + CallFunction callJava = (CallFunction) t.getCallTask().get(); assertNotNull(callJava.getThen(), "then() should be set on the consume task"); assertEquals("otherTask", callJava.getThen().getString(), "then() should point to 'otherTask'"); } @@ -532,7 +532,7 @@ void consume_step_then_flow_directive_enum_sets_end() { Task t = items.get(0).getTask(); assertNotNull(t.getCallTask(), "CallTask expected for consume step"); - CallJava callJava = (CallJava) t.getCallTask().get(); + CallFunction callJava = (CallFunction) t.getCallTask().get(); assertNotNull(callJava.getThen(), "then() should be set on the consume task"); assertEquals( FlowDirectiveEnum.END, diff --git a/experimental/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/FuncDSLUniqueIdTest.java b/experimental/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/FuncDSLUniqueIdTest.java index b3ae573c3..cfac3f7d2 100644 --- a/experimental/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/FuncDSLUniqueIdTest.java +++ b/experimental/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/FuncDSLUniqueIdTest.java @@ -23,12 +23,13 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import io.serverlessworkflow.api.reflection.func.UniqueIdBiFunction; +import io.serverlessworkflow.api.types.CallFunction; import io.serverlessworkflow.api.types.Task; import io.serverlessworkflow.api.types.TaskItem; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.api.types.func.CallJava; import io.serverlessworkflow.api.types.func.FilterFunction; +import io.serverlessworkflow.api.types.func.UniqueIdBiFunction; import io.serverlessworkflow.impl.TaskContextData; import io.serverlessworkflow.impl.WorkflowContextData; import io.serverlessworkflow.impl.WorkflowInstanceData; @@ -45,9 +46,10 @@ class FuncDSLUniqueIdTest { @SuppressWarnings("unchecked") - private static FilterFunction extractFilterFunction(CallJava callJava) { - if (callJava instanceof CallJava.CallJavaFilterFunction f) { - return (FilterFunction) f.function(); + private static FilterFunction extractFilterFunction(CallFunction callJava) { + if (callJava.getWith().getAdditionalProperties().get(CallJava.FUNCTION_NAME_KEY) + instanceof FilterFunction f) { + return f; } fail("CallTask is not a CallJavaFilterFunction; DSL contract may have changed."); return null; // unreachable @@ -77,7 +79,7 @@ void withUniqueId_uses_json_pointer_for_unique_id() { Task t = items.get(0).getTask(); assertNotNull(t.getCallTask(), "CallTask expected"); - CallJava cj = (CallJava) t.getCallTask().get(); + CallFunction cj = (CallFunction) t.getCallTask().get(); var jff = extractFilterFunction(cj); assertNotNull(jff, "JavaFilterFunction must be present for withUniqueId"); @@ -125,7 +127,7 @@ void agent_uses_json_pointer_for_unique_id() { Task t = items.get(0).getTask(); assertNotNull(t.getCallTask(), "CallTask expected"); - CallJava cj = (CallJava) t.getCallTask().get(); + CallFunction cj = (CallFunction) t.getCallTask().get(); var jff = extractFilterFunction(cj); assertNotNull(jff, "JavaFilterFunction must be present for agent/withUniqueId"); diff --git a/experimental/fluent/jackson/pom.xml b/experimental/fluent/jackson/pom.xml new file mode 100644 index 000000000..10b764f89 --- /dev/null +++ b/experimental/fluent/jackson/pom.xml @@ -0,0 +1,36 @@ + + 4.0.0 + + io.serverlessworkflow + serverlessworkflow-experimental-fluent + 8.0.0-SNAPSHOT + + serverlessworkflow-experimental-fluent-serialization-jackson + Serverless Workflow :: Experimental :: Fluent :: Serialization:: Jackson + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + com.fasterxml.jackson.datatype + jackson-datatype-jdk8 + + + io.serverlessworkflow + serverlessworkflow-experimental-types + + + io.serverlessworkflow + serverlessworkflow-serialization + + + io.serverlessworkflow + serverlessworkflow-impl-json + + + io.serverlessworkflow + serverlessworkflow-api + + + \ No newline at end of file diff --git a/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncExportAsDeserializer.java b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncExportAsDeserializer.java new file mode 100644 index 000000000..b9e65b05f --- /dev/null +++ b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncExportAsDeserializer.java @@ -0,0 +1,33 @@ +/* + * 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.serialization.jackson; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import io.serverlessworkflow.api.types.ExportAs; +import io.serverlessworkflow.api.types.jackson.ExportAsDeserializer; +import java.io.IOException; + +public class FuncExportAsDeserializer extends ExportAsDeserializer { + + @Override + public ExportAs deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException, JacksonException { + return SerializationUtils.deserializeFilterClass( + p, ctxt, f -> new ExportAs().withObject(f), ExportAs.class); + } +} diff --git a/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncExportAsMixIn.java b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncExportAsMixIn.java new file mode 100644 index 000000000..944edad50 --- /dev/null +++ b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncExportAsMixIn.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.fluent.func.serialization.jackson; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.serverlessworkflow.api.types.jackson.OutputAsMixIn; + +@JsonSerialize(using = FuncExportAsSerializer.class) +@JsonDeserialize(using = FuncExportAsDeserializer.class) +public class FuncExportAsMixIn extends OutputAsMixIn {} diff --git a/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncExportAsSerializer.java b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncExportAsSerializer.java new file mode 100644 index 000000000..3821bfac2 --- /dev/null +++ b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncExportAsSerializer.java @@ -0,0 +1,34 @@ +/* + * 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.serialization.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import io.serverlessworkflow.api.types.ExportAs; +import io.serverlessworkflow.api.types.jackson.ExportAsSerializer; +import java.io.IOException; + +public class FuncExportAsSerializer extends ExportAsSerializer { + + public void serialize(ExportAs value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + if (SerializationUtils.isFilterSerializable(value.getObject())) { + SerializationUtils.serializeObjectWithType(gen, value.getObject()); + } else { + super.serialize(value, gen, serializers); + } + } +} diff --git a/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncInputFromDeserializer.java b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncInputFromDeserializer.java new file mode 100644 index 000000000..6b0c5ab6c --- /dev/null +++ b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncInputFromDeserializer.java @@ -0,0 +1,33 @@ +/* + * 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.serialization.jackson; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import io.serverlessworkflow.api.types.InputFrom; +import io.serverlessworkflow.api.types.jackson.InputFromDeserializer; +import java.io.IOException; + +public class FuncInputFromDeserializer extends InputFromDeserializer { + + @Override + public InputFrom deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException, JacksonException { + return SerializationUtils.deserializeFilterClass( + p, ctxt, f -> new InputFrom().withObject(f), InputFrom.class); + } +} diff --git a/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncInputFromMixIn.java b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncInputFromMixIn.java new file mode 100644 index 000000000..1e4e91c3c --- /dev/null +++ b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncInputFromMixIn.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.fluent.func.serialization.jackson; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.serverlessworkflow.api.types.jackson.OutputAsMixIn; + +@JsonSerialize(using = FuncInputFromSerializer.class) +@JsonDeserialize(using = FuncInputFromDeserializer.class) +public class FuncInputFromMixIn extends OutputAsMixIn {} diff --git a/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncInputFromSerializer.java b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncInputFromSerializer.java new file mode 100644 index 000000000..277f04730 --- /dev/null +++ b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncInputFromSerializer.java @@ -0,0 +1,34 @@ +/* + * 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.serialization.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import io.serverlessworkflow.api.types.InputFrom; +import io.serverlessworkflow.api.types.jackson.InputFromSerializer; +import java.io.IOException; + +public class FuncInputFromSerializer extends InputFromSerializer { + + public void serialize(InputFrom value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + if (SerializationUtils.isFilterSerializable(value.getObject())) { + SerializationUtils.serializeObjectWithType(gen, value.getObject()); + } else { + super.serialize(value, gen, serializers); + } + } +} diff --git a/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncJacksonModule.java b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncJacksonModule.java new file mode 100644 index 000000000..7068d77aa --- /dev/null +++ b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncJacksonModule.java @@ -0,0 +1,104 @@ +/* + * 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.serialization.jackson; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; +import io.serverlessworkflow.api.types.ExportAs; +import io.serverlessworkflow.api.types.FunctionArguments; +import io.serverlessworkflow.api.types.InputFrom; +import io.serverlessworkflow.api.types.OutputAs; +import io.serverlessworkflow.api.types.TaskMetadata; +import io.serverlessworkflow.api.types.func.ContextFunction; +import io.serverlessworkflow.api.types.func.FilterFunction; +import io.serverlessworkflow.api.types.func.LoopFunction; +import io.serverlessworkflow.api.types.func.LoopFunctionIndex; +import io.serverlessworkflow.api.types.func.LoopPredicate; +import io.serverlessworkflow.api.types.func.LoopPredicateIndex; +import io.serverlessworkflow.api.types.func.LoopPredicateIndexContext; +import io.serverlessworkflow.api.types.func.LoopPredicateIndexFilter; +import io.serverlessworkflow.api.types.func.SerializableConsumer; +import io.serverlessworkflow.api.types.func.SerializableFunction; +import io.serverlessworkflow.api.types.func.SerializablePredicate; +import java.lang.invoke.SerializedLambda; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +public class FuncJacksonModule extends SimpleModule { + + private static final long serialVersionUID = 1L; + + public void setupModule(com.fasterxml.jackson.databind.Module.SetupContext context) { + SerializableFunctionSerializer serializer = new SerializableFunctionSerializer(); + super.addSerializer(SerializableFunction.class, serializer); + super.addSerializer(SerializablePredicate.class, serializer); + super.addSerializer(SerializableConsumer.class, serializer); + super.addSerializer(ContextFunction.class, serializer); + super.addSerializer(FilterFunction.class, serializer); + super.addSerializer(LoopFunction.class, serializer); + super.addSerializer(LoopFunctionIndex.class, serializer); + super.addSerializer(LoopPredicate.class, serializer); + super.addSerializer(LoopPredicateIndex.class, serializer); + super.addSerializer(LoopPredicateIndexContext.class, serializer); + super.addSerializer(LoopPredicateIndexFilter.class, serializer); + + super.addSerializer(TaskMetadata.class, new TaskMetadataSerializer()); + super.addDeserializer(TaskMetadata.class, new TaskMetadataDeserializer()); + super.addSerializer(FunctionArguments.class, new FunctionArgumentsSerializer()); + super.addDeserializer(FunctionArguments.class, new FunctionArgumentsDeserializer()); + super.addDeserializer(Function.class, new FunctionDeserializer(Function.class)); + super.addDeserializer(Predicate.class, new FunctionDeserializer(Predicate.class)); + super.addDeserializer(Consumer.class, new FunctionDeserializer(Consumer.class)); + super.addDeserializer(ContextFunction.class, new FunctionDeserializer(ContextFunction.class)); + super.addDeserializer(FilterFunction.class, new FunctionDeserializer(FilterFunction.class)); + super.addDeserializer(LoopFunction.class, new FunctionDeserializer(LoopFunction.class)); + super.addDeserializer( + LoopFunctionIndex.class, new FunctionDeserializer(LoopFunctionIndex.class)); + super.addDeserializer(LoopPredicate.class, new FunctionDeserializer(LoopPredicate.class)); + super.addDeserializer( + LoopPredicateIndex.class, new FunctionDeserializer(LoopPredicateIndex.class)); + super.addDeserializer( + LoopPredicateIndexContext.class, new FunctionDeserializer(LoopPredicateIndexContext.class)); + super.addDeserializer( + LoopPredicateIndexFilter.class, new FunctionDeserializer(LoopPredicateIndexFilter.class)); + + super.setSerializerModifier( + new BeanSerializerModifier() { + @Override + public List changeProperties( + SerializationConfig config, + BeanDescription beanDesc, + List beanProperties) { + if (beanDesc.getBeanClass().equals(SerializedLambda.class)) { + beanProperties.add(new SerializedLambdaWriter(beanProperties.get(0))); + } + return beanProperties; + } + }); + super.addDeserializer(SerializedLambda.class, new SerializedLambdaDeserializer()); + + super.setMixInAnnotation(OutputAs.class, FuncOutputAsMixIn.class); + super.setMixInAnnotation(ExportAs.class, FuncExportAsMixIn.class); + super.setMixInAnnotation(InputFrom.class, FuncInputFromMixIn.class); + + super.setupModule(context); + } +} diff --git a/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncOutputAsDeserializer.java b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncOutputAsDeserializer.java new file mode 100644 index 000000000..ffa17fd41 --- /dev/null +++ b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncOutputAsDeserializer.java @@ -0,0 +1,33 @@ +/* + * 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.serialization.jackson; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import io.serverlessworkflow.api.types.OutputAs; +import io.serverlessworkflow.api.types.jackson.OutputAsDeserializer; +import java.io.IOException; + +public class FuncOutputAsDeserializer extends OutputAsDeserializer { + + @Override + public OutputAs deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException, JacksonException { + return SerializationUtils.deserializeFilterClass( + p, ctxt, f -> new OutputAs().withObject(f), OutputAs.class); + } +} diff --git a/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncOutputAsMixIn.java b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncOutputAsMixIn.java new file mode 100644 index 000000000..dfb846705 --- /dev/null +++ b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncOutputAsMixIn.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.fluent.func.serialization.jackson; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.serverlessworkflow.api.types.jackson.OutputAsMixIn; + +@JsonSerialize(using = FuncOutputAsSerializer.class) +@JsonDeserialize(using = FuncOutputAsDeserializer.class) +public class FuncOutputAsMixIn extends OutputAsMixIn {} diff --git a/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncOutputAsSerializer.java b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncOutputAsSerializer.java new file mode 100644 index 000000000..e1bc3e0e7 --- /dev/null +++ b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FuncOutputAsSerializer.java @@ -0,0 +1,34 @@ +/* + * 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.serialization.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import io.serverlessworkflow.api.types.OutputAs; +import io.serverlessworkflow.api.types.jackson.OutputAsSerializer; +import java.io.IOException; + +public class FuncOutputAsSerializer extends OutputAsSerializer { + + public void serialize(OutputAs value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + if (SerializationUtils.isFilterSerializable(value.getObject())) { + SerializationUtils.serializeObjectWithType(gen, value.getObject()); + } else { + super.serialize(value, gen, serializers); + } + } +} diff --git a/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FunctionArgumentsDeserializer.java b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FunctionArgumentsDeserializer.java new file mode 100644 index 000000000..5911314bc --- /dev/null +++ b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FunctionArgumentsDeserializer.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.serialization.jackson; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import io.serverlessworkflow.api.types.FunctionArguments; +import java.io.IOException; + +public class FunctionArgumentsDeserializer extends JsonDeserializer { + + @Override + public FunctionArguments deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException { + try { + FunctionArguments result = new FunctionArguments(); + SerializationUtils.deserializeMap(p, ctxt, result.getAdditionalProperties()); + return result; + } catch (ReflectiveOperationException e) { + throw new IOException(e); + } + } +} diff --git a/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FunctionArgumentsSerializer.java b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FunctionArgumentsSerializer.java new file mode 100644 index 000000000..bb411443a --- /dev/null +++ b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FunctionArgumentsSerializer.java @@ -0,0 +1,31 @@ +/* + * 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.serialization.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import io.serverlessworkflow.api.types.FunctionArguments; +import java.io.IOException; + +public class FunctionArgumentsSerializer extends JsonSerializer { + + @Override + public void serialize(FunctionArguments value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + SerializationUtils.serializeMap(gen, value.getAdditionalProperties()); + } +} diff --git a/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FunctionDeserializer.java b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FunctionDeserializer.java new file mode 100644 index 000000000..44f2648b7 --- /dev/null +++ b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/FunctionDeserializer.java @@ -0,0 +1,42 @@ +/* + * 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.serialization.jackson; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import io.serverlessworkflow.api.types.utils.ReflectionUtils; +import java.io.IOException; +import java.lang.invoke.SerializedLambda; + +public class FunctionDeserializer extends JsonDeserializer { + + private final Class objectClass; + + public FunctionDeserializer(Class objectClass) { + this.objectClass = objectClass; + } + + @Override + public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + try { + return objectClass.cast( + ReflectionUtils.functionFromSerialized(p.readValueAs(SerializedLambda.class))); + } catch (ReflectiveOperationException e) { + throw new IOException(e); + } + } +} diff --git a/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/SerializableFunctionSerializer.java b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/SerializableFunctionSerializer.java new file mode 100644 index 000000000..4dd61c2aa --- /dev/null +++ b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/SerializableFunctionSerializer.java @@ -0,0 +1,38 @@ +/* + * 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.serialization.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import io.serverlessworkflow.api.types.utils.ReflectionUtils; +import java.io.IOException; +import java.lang.invoke.SerializedLambda; +import java.util.Optional; + +public class SerializableFunctionSerializer extends JsonSerializer { + + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + Optional serializedLambda = ReflectionUtils.serializedFromFunction(value); + if (serializedLambda.isPresent()) { + gen.writeObject(serializedLambda.orElseThrow()); + } else { + gen.writeString(value.toString()); + } + } +} diff --git a/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/SerializationUtils.java b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/SerializationUtils.java new file mode 100644 index 000000000..3da29d808 --- /dev/null +++ b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/SerializationUtils.java @@ -0,0 +1,145 @@ +/* + * 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.serialization.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.TreeNode; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.serverlessworkflow.api.types.func.FilterSerializable; +import io.serverlessworkflow.api.types.func.FunctionObject; +import io.serverlessworkflow.api.types.utils.ReflectionUtils; +import io.serverlessworkflow.serialization.DeserializeHelper; +import java.io.IOException; +import java.lang.invoke.SerializedLambda; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.function.Function; + +class SerializationUtils { + + private SerializationUtils() {} + + private static final String TYPE = "type"; + private static final String VALUE = "value"; + private static final String NULL = "null"; + + public static void serializeObjectWithType(JsonGenerator gen, Object value) throws IOException { + gen.writeStartObject(); + if (value == null) { + gen.writeStringField(TYPE, NULL); + } else { + if (value instanceof FunctionObject) { + gen.writeStringField(TYPE, SerializedLambda.class.getName()); + try { + gen.writeObjectField(VALUE, ReflectionUtils.serializedLambda(value)); + } catch (ReflectiveOperationException e) { + throw new IOException(e); + } + } else { + gen.writeStringField(TYPE, value.getClass().getName()); + if (value instanceof Optional optional) { + writeOptionalWithType(gen, optional); + } else { + gen.writeObjectField(VALUE, value); + } + } + } + gen.writeEndObject(); + } + + public static Object deserializeObjectWithType(DeserializationContext ctxt, JsonNode objectNode) + throws IOException, ReflectiveOperationException { + String className = objectNode.get(TYPE).asText(); + if (NULL.equals(className)) { + return null; + } + Class clazz = ReflectionUtils.loadClass(className); + if (clazz.equals(Optional.class)) { + return readOptionalWithType(ctxt, objectNode.get(VALUE)); + } else { + Object value = ctxt.readTreeAsValue(objectNode.get(VALUE), clazz); + return value instanceof SerializedLambda sl + ? ReflectionUtils.functionFromSerialized(sl) + : value; + } + } + + public static void writeOptionalWithType(JsonGenerator gen, Optional optional) + throws IOException { + if (!optional.isEmpty()) { + gen.writeFieldName(VALUE); + serializeObjectWithType(gen, optional.orElseThrow()); + } + } + + public static Optional readOptionalWithType(DeserializationContext ctxt, JsonNode objectNode) + throws IOException, ReflectiveOperationException { + return objectNode == null + ? Optional.empty() + : Optional.of(deserializeObjectWithType(ctxt, objectNode)); + } + + public static void serializeMap(JsonGenerator gen, Map map) throws IOException { + gen.writeStartObject(); + for (Map.Entry entry : map.entrySet()) { + gen.writeFieldName(entry.getKey()); + SerializationUtils.serializeObjectWithType(gen, entry.getValue()); + } + gen.writeEndObject(); + } + + public static void deserializeMap( + JsonParser p, DeserializationContext ctxt, Map map) + throws IOException, ReflectiveOperationException { + ObjectNode node = (ObjectNode) ctxt.readTree(p); + for (Entry item : node.properties()) { + map.put(item.getKey(), deserializeObjectWithType(ctxt, item.getValue())); + } + } + + public static T deserializeFilterClass( + JsonParser p, + DeserializationContext ctxt, + Function setter, + Class objectClass) + throws IOException { + TreeNode treeNode = p.readValueAsTree(); + if (treeNode instanceof ObjectNode node && SerializationUtils.hasType(node)) { + try { + return setter.apply( + (FilterSerializable) SerializationUtils.deserializeObjectWithType(ctxt, node)); + } catch (ReflectiveOperationException e) { + throw new IOException(e); + } + } else { + return DeserializeHelper.deserializeOneOf( + treeNode, p, objectClass, List.of(String.class, Object.class)); + } + } + + public static boolean isFilterSerializable(Object object) { + return object instanceof FilterSerializable; + } + + public static boolean hasType(ObjectNode node) { + return node.has(TYPE); + } +} diff --git a/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/SerializedLambdaDeserializer.java b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/SerializedLambdaDeserializer.java new file mode 100644 index 000000000..87c6c8397 --- /dev/null +++ b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/SerializedLambdaDeserializer.java @@ -0,0 +1,82 @@ +/* + * 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.serialization.jackson; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.TreeNode; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.serverlessworkflow.api.types.utils.ReflectionUtils; +import java.io.IOException; +import java.lang.invoke.SerializedLambda; + +public class SerializedLambdaDeserializer extends JsonDeserializer { + + static final String CAPTURING_CLASS = "capturingClass"; + static final String FUNCTIONAL_CLASS = "functionalInterfaceClass"; + static final String FUNCTIONAL_METHOD_NAME = "functionalInterfaceMethodName"; + static final String FUNCTIONAL_METHOD_SIGNATURE = "functionalInterfaceMethodSignature"; + static final String METHOD_KIND = "implMethodKind"; + static final String METHOD_CLASS = "implClass"; + static final String METHOD_NAME = "implMethodName"; + static final String METHOD_SIGNATURE = "implMethodSignature"; + static final String METHOD_TYPE = "instantiatedMethodType"; + static final String CAPTURED_ARGS = "capturedArgs"; + + @Override + public SerializedLambda deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException { + TreeNode tree = p.readValueAsTree(); + + if (tree instanceof ObjectNode node) { + try { + return new SerializedLambda( + ReflectionUtils.loadCapturingClass(node.get(CAPTURING_CLASS).asText()), + node.get(FUNCTIONAL_CLASS).asText(), + node.get(FUNCTIONAL_METHOD_NAME).asText(), + node.get(FUNCTIONAL_METHOD_SIGNATURE).asText(), + node.get(METHOD_KIND).asInt(), + node.get(METHOD_CLASS).asText(), + node.get(METHOD_NAME).asText(), + node.get(METHOD_SIGNATURE).asText(), + node.get(METHOD_TYPE).asText(), + fromArray(ctxt, (ArrayNode) node.get(CAPTURED_ARGS))); + } catch (ReflectiveOperationException ex) { + throw new IOException("Error unmarshalling SerializedLambda " + node, ex); + } + } else { + throw new IOException( + "Node " + + tree + + " is not an object and therefore cannot be converted into SerializedLambda"); + } + } + + private Object[] fromArray(DeserializationContext ctxt, ArrayNode node) + throws IOException, ReflectiveOperationException { + if (node == null) { + return new Object[0]; + } else { + Object[] result = new Object[node.size()]; + for (int i = 0; i < result.length; i++) { + result[i] = SerializationUtils.deserializeObjectWithType(ctxt, node.get(i)); + } + return result; + } + } +} diff --git a/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/SerializedLambdaWriter.java b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/SerializedLambdaWriter.java new file mode 100644 index 000000000..49162c11d --- /dev/null +++ b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/SerializedLambdaWriter.java @@ -0,0 +1,45 @@ +/* + * 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.serialization.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import java.io.IOException; +import java.lang.invoke.SerializedLambda; + +public class SerializedLambdaWriter extends BeanPropertyWriter { + + private static final long serialVersionUID = 1L; + + public SerializedLambdaWriter(BeanPropertyWriter base) { + super(base); + } + + @Override + public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) + throws IOException { + SerializedLambda sl = (SerializedLambda) bean; + int size = sl.getCapturedArgCount(); + if (size > 0) { + gen.writeArrayFieldStart(SerializedLambdaDeserializer.CAPTURED_ARGS); + for (int i = 0; i < size; i++) { + SerializationUtils.serializeObjectWithType(gen, sl.getCapturedArg(i)); + } + gen.writeEndArray(); + } + } +} diff --git a/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/TaskMetadataDeserializer.java b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/TaskMetadataDeserializer.java new file mode 100644 index 000000000..50a30d9c4 --- /dev/null +++ b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/TaskMetadataDeserializer.java @@ -0,0 +1,36 @@ +/* + * 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.serialization.jackson; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import io.serverlessworkflow.api.types.TaskMetadata; +import java.io.IOException; + +public class TaskMetadataDeserializer extends JsonDeserializer { + + @Override + public TaskMetadata deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + try { + TaskMetadata result = new TaskMetadata(); + SerializationUtils.deserializeMap(p, ctxt, result.getAdditionalProperties()); + return result; + } catch (ReflectiveOperationException e) { + throw new IOException(e); + } + } +} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallAbstractJavaFunction.java b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/TaskMetadataSerializer.java similarity index 52% rename from experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallAbstractJavaFunction.java rename to experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/TaskMetadataSerializer.java index 0a8af1201..5c39de2af 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallAbstractJavaFunction.java +++ b/experimental/fluent/jackson/src/main/java/io/serverlessworkflow/fluent/func/serialization/jackson/TaskMetadataSerializer.java @@ -13,27 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.api.types.func; +package io.serverlessworkflow.fluent.func.serialization.jackson; -import java.util.Optional; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import io.serverlessworkflow.api.types.TaskMetadata; +import java.io.IOException; -public abstract class CallAbstractJavaFunction extends CallJava { - - private static final long serialVersionUID = 1L; - - private final Optional> outputClass; - - protected CallAbstractJavaFunction() { - this(Optional.empty(), Optional.empty()); - } - - protected CallAbstractJavaFunction( - Optional> inputClass, Optional> outputClass) { - super(inputClass); - this.outputClass = outputClass; - } - - public Optional> outputClass() { - return outputClass; +public class TaskMetadataSerializer extends JsonSerializer { + @Override + public void serialize(TaskMetadata value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + SerializationUtils.serializeMap(gen, value.getAdditionalProperties()); } } diff --git a/experimental/fluent/jackson/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module b/experimental/fluent/jackson/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module new file mode 100644 index 000000000..3ae3675c6 --- /dev/null +++ b/experimental/fluent/jackson/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module @@ -0,0 +1 @@ +io.serverlessworkflow.fluent.func.serialization.jackson.FuncJacksonModule \ No newline at end of file diff --git a/experimental/fluent/pom.xml b/experimental/fluent/pom.xml index 56bd2f5aa..15f289354 100644 --- a/experimental/fluent/pom.xml +++ b/experimental/fluent/pom.xml @@ -60,5 +60,6 @@ func + jackson \ No newline at end of file diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/AbstractJavaCallExecutor.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/AbstractJavaCallExecutor.java index 466a9c277..c723fc87c 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/AbstractJavaCallExecutor.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/AbstractJavaCallExecutor.java @@ -31,10 +31,6 @@ public abstract class AbstractJavaCallExecutor implements CallableTask { private final boolean directCompletable; private final boolean convertedCompletable; - protected AbstractJavaCallExecutor() { - this(Optional.empty(), Optional.empty()); - } - protected AbstractJavaCallExecutor( Optional> inputClass, Optional> outputClass) { this.inputClass = inputClass; diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaCallFunctionBuilder.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaCallFunctionBuilder.java new file mode 100644 index 000000000..c9a20266f --- /dev/null +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaCallFunctionBuilder.java @@ -0,0 +1,93 @@ +/* + * 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.impl.executors.func; + +import io.serverlessworkflow.api.types.CallFunction; +import io.serverlessworkflow.api.types.func.CallJava; +import io.serverlessworkflow.api.types.func.ContextFunction; +import io.serverlessworkflow.api.types.func.FilterFunction; +import io.serverlessworkflow.api.types.func.LoopFunction; +import io.serverlessworkflow.api.types.func.LoopFunctionIndex; +import io.serverlessworkflow.impl.WorkflowDefinition; +import io.serverlessworkflow.impl.WorkflowMutablePosition; +import io.serverlessworkflow.impl.executors.CallFunctionExecutorBuilder; +import io.serverlessworkflow.impl.executors.CallableTaskFactory; +import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JavaCallFunctionBuilder extends CallFunctionExecutorBuilder { + + private static final Logger logger = LoggerFactory.getLogger(JavaCallFunctionBuilder.class); + + @Override + public int priority() { + return DEFAULT_PRIORITY - 10; + } + + @Override + public CallableTaskFactory init( + CallFunction task, WorkflowDefinition definition, WorkflowMutablePosition position) { + if (CallJava.JAVA_CALL_KEY.equals(task.getCall())) { + if (task.getWith() == null) { + throw new IllegalArgumentException( + "At least one key " + + CallJava.FUNCTION_NAME_KEY + + " is expected as Java function argument"); + } + Map props = task.getWith().getAdditionalProperties(); + Object obj = props.get(CallJava.FUNCTION_NAME_KEY); + if (obj == null) { + throw new IllegalArgumentException( + "Missing required Java function argument '" + CallJava.FUNCTION_NAME_KEY + "'"); + } + Optional> input = + (Optional>) props.getOrDefault(CallJava.INPUT_CLASS_KEY, Optional.empty()); + Optional> output = + (Optional>) props.getOrDefault(CallJava.OUTPUT_CLASS_KEY, Optional.empty()); + if (obj instanceof ContextFunction fn) { + return () -> new JavaContextFunctionCallExecutor(input, output, fn); + } else if (obj instanceof FilterFunction fn) { + return () -> new JavaFilterFunctionCallExecutor(input, output, fn); + } else if (obj instanceof LoopFunction loop) { + return () -> + new JavaLoopFunctionCallExecutor( + loop, (String) props.get(CallJava.VAR_NAME_KEY), input, output); + } else if (obj instanceof LoopFunctionIndex loop) { + return () -> + new JavaLoopFunctionIndexCallExecutor( + loop, + (String) props.get(CallJava.VAR_NAME_KEY), + (String) props.get(CallJava.INDEX_NAME_KEY), + input, + output); + + } else if (obj instanceof Function fn) { + return () -> new JavaFunctionCallExecutor(input, output, fn); + } else if (obj instanceof Consumer consumer) { + return () -> new JavaConsumerCallExecutor(input, consumer); + } else { + throw new UnsupportedOperationException("Unrecognized function " + obj); + } + } else { + logger.info("Calling regular function handler for task call {}", task.getCall()); + return super.init(task, definition, position); + } + } +} diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaConsumerCallExecutorBuilder.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaConsumerCallExecutorBuilder.java deleted file mode 100644 index 555d331d5..000000000 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaConsumerCallExecutorBuilder.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.impl.executors.func; - -import io.serverlessworkflow.api.types.TaskBase; -import io.serverlessworkflow.api.types.func.CallJava; -import io.serverlessworkflow.impl.WorkflowDefinition; -import io.serverlessworkflow.impl.WorkflowMutablePosition; -import io.serverlessworkflow.impl.executors.CallableTaskBuilder; -import io.serverlessworkflow.impl.executors.CallableTaskFactory; - -public class JavaConsumerCallExecutorBuilder - implements CallableTaskBuilder> { - - public CallableTaskFactory init( - CallJava.CallJavaConsumer task, - WorkflowDefinition definition, - WorkflowMutablePosition position) { - return () -> new JavaConsumerCallExecutor(task.inputClass(), task.consumer()); - } - - @Override - public boolean accept(Class clazz) { - return CallJava.CallJavaConsumer.class.isAssignableFrom(clazz); - } -} diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaContextFunctionCallExecutorBuilder.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaContextFunctionCallExecutorBuilder.java deleted file mode 100644 index 1c6126b7e..000000000 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaContextFunctionCallExecutorBuilder.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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.impl.executors.func; - -import io.serverlessworkflow.api.types.TaskBase; -import io.serverlessworkflow.api.types.func.CallJava; -import io.serverlessworkflow.api.types.func.CallJava.CallJavaContextFunction; -import io.serverlessworkflow.impl.WorkflowDefinition; -import io.serverlessworkflow.impl.WorkflowMutablePosition; -import io.serverlessworkflow.impl.executors.CallableTaskBuilder; -import io.serverlessworkflow.impl.executors.CallableTaskFactory; - -public class JavaContextFunctionCallExecutorBuilder - implements CallableTaskBuilder> { - - @Override - public boolean accept(Class clazz) { - return CallJava.CallJavaContextFunction.class.isAssignableFrom(clazz); - } - - @Override - public CallableTaskFactory init( - CallJavaContextFunction task, - WorkflowDefinition definition, - WorkflowMutablePosition position) { - return () -> - new JavaContextFunctionCallExecutor( - task.inputClass(), task.outputClass(), task.function()); - } -} diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaFilterFunctionCallExecutorBuilder.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaFilterFunctionCallExecutorBuilder.java deleted file mode 100644 index 6178ada8c..000000000 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaFilterFunctionCallExecutorBuilder.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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.impl.executors.func; - -import io.serverlessworkflow.api.types.TaskBase; -import io.serverlessworkflow.api.types.func.CallJava; -import io.serverlessworkflow.api.types.func.CallJava.CallJavaFilterFunction; -import io.serverlessworkflow.impl.WorkflowDefinition; -import io.serverlessworkflow.impl.WorkflowMutablePosition; -import io.serverlessworkflow.impl.executors.CallableTaskBuilder; -import io.serverlessworkflow.impl.executors.CallableTaskFactory; - -public class JavaFilterFunctionCallExecutorBuilder - implements CallableTaskBuilder> { - - @Override - public boolean accept(Class clazz) { - return CallJava.CallJavaFilterFunction.class.isAssignableFrom(clazz); - } - - @Override - public CallableTaskFactory init( - CallJavaFilterFunction task, - WorkflowDefinition definition, - WorkflowMutablePosition position) { - return () -> - new JavaFilterFunctionCallExecutor<>( - task.inputClass(), task.outputClass(), task.function()); - } -} 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 8b1c23b3c..0be8770bd 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 @@ -18,9 +18,12 @@ import static io.serverlessworkflow.impl.executors.func.JavaFuncUtils.safeObject; import io.serverlessworkflow.api.types.ForTask; -import io.serverlessworkflow.api.types.func.ForTaskFunction; +import io.serverlessworkflow.api.types.func.LoopPredicate; +import io.serverlessworkflow.api.types.func.LoopPredicateIndex; +import io.serverlessworkflow.api.types.func.LoopPredicateIndexContext; import io.serverlessworkflow.api.types.func.LoopPredicateIndexFilter; import io.serverlessworkflow.api.types.func.TypedFunction; +import io.serverlessworkflow.api.types.utils.ForTaskFunction; import io.serverlessworkflow.impl.WorkflowDefinition; import io.serverlessworkflow.impl.WorkflowMutablePosition; import io.serverlessworkflow.impl.WorkflowPredicate; @@ -39,44 +42,67 @@ protected JavaForExecutorBuilder( @Override protected Optional buildWhileFilter() { - if (task instanceof ForTaskFunction taskFunctions) { - final LoopPredicateIndexFilter whilePred = taskFunctions.getWhilePredicate(); - Optional> whileClass = taskFunctions.getWhileClass(); - String varName = task.getFor().getEach(); - String indexName = task.getFor().getAt(); - if (whilePred != null) { - return Optional.of( - (w, t, n) -> { - Object item = safeObject(t.variables().get(varName)); - return whilePred.test( - JavaFuncUtils.convert(n, whileClass), - item, - (Integer) safeObject(t.variables().get(indexName)), - w, - t); - }); - } + final Object whilePred = ForTaskFunction.getWhilePredicate(task); + Optional> whileClass = ForTaskFunction.getWhileClass(task); + String varName = task.getFor().getEach(); + String indexName = task.getFor().getAt(); + if (whilePred instanceof LoopPredicateIndexFilter pred) { + return Optional.of( + (w, t, n) -> { + Object item = safeObject(t.variables().get(varName)); + return pred.test( + JavaFuncUtils.convert(n, whileClass), + item, + (Integer) safeObject(t.variables().get(indexName)), + w, + t); + }); + } else if (whilePred instanceof LoopPredicate pred) { + return Optional.of( + (w, t, n) -> { + Object item = safeObject(t.variables().get(varName)); + return pred.test(JavaFuncUtils.convert(n, whileClass), item); + }); + } else if (whilePred instanceof LoopPredicateIndexContext pred) { + return Optional.of( + (w, t, n) -> { + Object item = safeObject(t.variables().get(varName)); + return pred.test( + JavaFuncUtils.convert(n, whileClass), + item, + (Integer) safeObject(t.variables().get(indexName)), + w); + }); + } else if (whilePred instanceof LoopPredicateIndex pred) { + return Optional.of( + (w, t, n) -> { + Object item = safeObject(t.variables().get(varName)); + return pred.test( + JavaFuncUtils.convert(n, whileClass), + item, + (Integer) safeObject(t.variables().get(indexName))); + }); } return super.buildWhileFilter(); } protected WorkflowValueResolver> buildCollectionFilter() { - return task instanceof ForTaskFunction taskFunctions + Object inCollection = collectionFilterObject(); + return inCollection != null ? application .expressionFactory() - .resolveCollection(ExpressionDescriptor.object(collectionFilterObject(taskFunctions))) + .resolveCollection(ExpressionDescriptor.object(inCollection)) : super.buildCollectionFilter(); } - private Object collectionFilterObject(ForTaskFunction taskFunctions) { - return taskFunctions - .getForClass() - .map(forClass -> typedCollectionFunction(taskFunctions, forClass)) - .orElse(taskFunctions.getCollection()); + private Object collectionFilterObject() { + return ForTaskFunction.getForClass(task) + .map(forClass -> typedCollectionFunction(forClass)) + .orElse(ForTaskFunction.getInCollection(task)); } @SuppressWarnings({"rawtypes", "unchecked"}) - private Object typedCollectionFunction(ForTaskFunction taskFunctions, Class forClass) { - return new TypedFunction(taskFunctions.getCollection(), forClass); + private Object typedCollectionFunction(Class forClass) { + return new TypedFunction(ForTaskFunction.getInCollection(task), forClass); } } diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaFuncUtils.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaFuncUtils.java index 8056be447..7f677d1b4 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaFuncUtils.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaFuncUtils.java @@ -15,7 +15,6 @@ */ package io.serverlessworkflow.impl.executors.func; -import io.serverlessworkflow.api.types.func.PredicateContainer; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowPredicate; @@ -28,11 +27,8 @@ static Object safeObject(Object obj) { return obj instanceof WorkflowModel model ? model.asJavaObject() : obj; } - static WorkflowPredicate from(WorkflowApplication application, PredicateContainer source) { - assert (source.predicate() != null); - return application - .expressionFactory() - .buildPredicate(ExpressionDescriptor.object(source.predicate())); + static WorkflowPredicate from(WorkflowApplication application, Object predicate) { + return application.expressionFactory().buildPredicate(ExpressionDescriptor.object(predicate)); } static T convertT(WorkflowModel model, Optional> inputClass) { diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaFunctionCallExecutorBuilder.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaFunctionCallExecutorBuilder.java deleted file mode 100644 index 57058bbb3..000000000 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaFunctionCallExecutorBuilder.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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.impl.executors.func; - -import io.serverlessworkflow.api.types.TaskBase; -import io.serverlessworkflow.api.types.func.CallJava; -import io.serverlessworkflow.api.types.func.CallJava.CallJavaFunction; -import io.serverlessworkflow.impl.WorkflowDefinition; -import io.serverlessworkflow.impl.WorkflowMutablePosition; -import io.serverlessworkflow.impl.executors.CallableTaskBuilder; -import io.serverlessworkflow.impl.executors.CallableTaskFactory; - -public class JavaFunctionCallExecutorBuilder - implements CallableTaskBuilder> { - - @Override - public boolean accept(Class clazz) { - return CallJava.CallJavaFunction.class.isAssignableFrom(clazz); - } - - @Override - public CallableTaskFactory init( - CallJavaFunction task, - WorkflowDefinition definition, - WorkflowMutablePosition position) { - return () -> - new JavaFunctionCallExecutor<>(task.inputClass(), task.outputClass(), task.function()); - } -} diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaListenExecutorBuilder.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaListenExecutorBuilder.java index 394423c48..a528c7d16 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaListenExecutorBuilder.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaListenExecutorBuilder.java @@ -17,7 +17,8 @@ import io.serverlessworkflow.api.types.ListenTask; import io.serverlessworkflow.api.types.Until; -import io.serverlessworkflow.api.types.func.UntilPredicate; +import io.serverlessworkflow.api.types.utils.TaskPredicate; +import io.serverlessworkflow.api.types.utils.TypesUtils; import io.serverlessworkflow.impl.WorkflowDefinition; import io.serverlessworkflow.impl.WorkflowMutablePosition; import io.serverlessworkflow.impl.WorkflowPredicate; @@ -32,8 +33,9 @@ protected JavaListenExecutorBuilder( @Override protected WorkflowPredicate buildUntilPredicate(Until until) { - return until instanceof UntilPredicate untilPred && untilPred.predicate() != null - ? JavaFuncUtils.from(application, untilPred) + Object predicate = TaskPredicate.predicate(task, TypesUtils.UNTIL_PRED_NAME); + return predicate != null + ? JavaFuncUtils.from(application, predicate) : super.buildUntilPredicate(until); } } diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaLoopFunctionCallExecutor.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaLoopFunctionCallExecutor.java index 840f3a201..f22af220a 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaLoopFunctionCallExecutor.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaLoopFunctionCallExecutor.java @@ -20,13 +20,19 @@ import io.serverlessworkflow.api.types.func.LoopFunction; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowContext; +import java.util.Optional; public class JavaLoopFunctionCallExecutor extends AbstractJavaCallExecutor { private final LoopFunction function; private final String varName; - public JavaLoopFunctionCallExecutor(LoopFunction function, String varName) { + public JavaLoopFunctionCallExecutor( + LoopFunction function, + String varName, + Optional> inputClass, + Optional> outputClass) { + super(inputClass, outputClass); this.function = function; this.varName = varName; } diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaLoopFunctionCallExecutorBuilder.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaLoopFunctionCallExecutorBuilder.java deleted file mode 100644 index 5c95ec611..000000000 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaLoopFunctionCallExecutorBuilder.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.impl.executors.func; - -import io.serverlessworkflow.api.types.TaskBase; -import io.serverlessworkflow.api.types.func.CallJava; -import io.serverlessworkflow.api.types.func.CallJava.CallJavaLoopFunction; -import io.serverlessworkflow.impl.WorkflowDefinition; -import io.serverlessworkflow.impl.WorkflowMutablePosition; -import io.serverlessworkflow.impl.executors.CallableTaskBuilder; -import io.serverlessworkflow.impl.executors.CallableTaskFactory; - -public class JavaLoopFunctionCallExecutorBuilder - implements CallableTaskBuilder { - - @Override - public boolean accept(Class clazz) { - return CallJava.CallJavaLoopFunction.class.isAssignableFrom(clazz); - } - - @Override - public CallableTaskFactory init( - CallJavaLoopFunction task, WorkflowDefinition definition, WorkflowMutablePosition position) { - return () -> new JavaLoopFunctionCallExecutor<>(task.function(), task.varName()); - } -} diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaLoopFunctionIndexCallExecutor.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaLoopFunctionIndexCallExecutor.java index 5e4837d52..360c65174 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaLoopFunctionIndexCallExecutor.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaLoopFunctionIndexCallExecutor.java @@ -20,6 +20,7 @@ import io.serverlessworkflow.api.types.func.LoopFunctionIndex; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowContext; +import java.util.Optional; public class JavaLoopFunctionIndexCallExecutor extends AbstractJavaCallExecutor { @@ -28,7 +29,12 @@ public class JavaLoopFunctionIndexCallExecutor extends AbstractJavaCall private final String indexName; public JavaLoopFunctionIndexCallExecutor( - LoopFunctionIndex function, String varName, String indexName) { + LoopFunctionIndex function, + String varName, + String indexName, + Optional> inputClass, + Optional> outputClass) { + super(inputClass, outputClass); this.function = function; this.varName = varName; this.indexName = indexName; diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaLoopFunctionIndexCallExecutorBuilder.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaLoopFunctionIndexCallExecutorBuilder.java deleted file mode 100644 index 7e6f0c9d9..000000000 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaLoopFunctionIndexCallExecutorBuilder.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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.impl.executors.func; - -import io.serverlessworkflow.api.types.TaskBase; -import io.serverlessworkflow.api.types.func.CallJava; -import io.serverlessworkflow.api.types.func.CallJava.CallJavaLoopFunctionIndex; -import io.serverlessworkflow.impl.WorkflowDefinition; -import io.serverlessworkflow.impl.WorkflowMutablePosition; -import io.serverlessworkflow.impl.executors.CallableTaskBuilder; -import io.serverlessworkflow.impl.executors.CallableTaskFactory; - -public class JavaLoopFunctionIndexCallExecutorBuilder - implements CallableTaskBuilder { - - @Override - public boolean accept(Class clazz) { - return CallJava.CallJavaLoopFunctionIndex.class.isAssignableFrom(clazz); - } - - @Override - public CallableTaskFactory init( - CallJavaLoopFunctionIndex task, - WorkflowDefinition definition, - WorkflowMutablePosition position) { - return () -> - new JavaLoopFunctionIndexCallExecutor<>(task.function(), task.varName(), task.indexName()); - } -} diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaSwitchExecutorBuilder.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaSwitchExecutorBuilder.java index 1676c57b2..0f63a7375 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaSwitchExecutorBuilder.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaSwitchExecutorBuilder.java @@ -15,9 +15,9 @@ */ package io.serverlessworkflow.impl.executors.func; -import io.serverlessworkflow.api.types.SwitchCase; +import io.serverlessworkflow.api.types.SwitchItem; import io.serverlessworkflow.api.types.SwitchTask; -import io.serverlessworkflow.api.types.func.SwitchCasePredicate; +import io.serverlessworkflow.api.types.utils.TaskPredicate; import io.serverlessworkflow.impl.WorkflowDefinition; import io.serverlessworkflow.impl.WorkflowMutablePosition; import io.serverlessworkflow.impl.WorkflowPredicate; @@ -32,9 +32,10 @@ protected JavaSwitchExecutorBuilder( } @Override - protected Optional buildFilter(SwitchCase switchCase) { - return switchCase instanceof SwitchCasePredicate predicate && predicate.predicate() != null + protected Optional buildFilter(SwitchItem item) { + Object predicate = TaskPredicate.predicate(task, item.getName()); + return predicate != null ? Optional.of(JavaFuncUtils.from(application, predicate)) - : super.buildFilter(switchCase); + : super.buildFilter(item); } } 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 058fb0550..9f9d53fed 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 @@ -22,13 +22,13 @@ import io.serverlessworkflow.api.types.func.ContextPredicate; import io.serverlessworkflow.api.types.func.FilterFunction; import io.serverlessworkflow.api.types.func.FilterPredicate; -import io.serverlessworkflow.api.types.func.TaskMetadataKeys; import io.serverlessworkflow.api.types.func.TypedContextFunction; import io.serverlessworkflow.api.types.func.TypedContextPredicate; import io.serverlessworkflow.api.types.func.TypedFilterFunction; import io.serverlessworkflow.api.types.func.TypedFilterPredicate; import io.serverlessworkflow.api.types.func.TypedFunction; import io.serverlessworkflow.api.types.func.TypedPredicate; +import io.serverlessworkflow.api.types.utils.TypesUtils; import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowPredicate; import io.serverlessworkflow.impl.expressions.AbstractExpressionFactory; @@ -128,7 +128,7 @@ private WorkflowPredicate fromPredicate(TypedFilterPredicate pred) { public Optional buildIfFilter(TaskBase task) { TaskMetadata metadata = task.getMetadata(); if (metadata != null) { - Object obj = metadata.getAdditionalProperties().get(TaskMetadataKeys.IF_PREDICATE); + Object obj = metadata.getAdditionalProperties().get(TypesUtils.IF_PREDICATE); if (obj instanceof Predicate pred) { return Optional.of(fromPredicate(pred)); } else if (obj instanceof TypedPredicate pred) { diff --git a/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.CallableTaskBuilder b/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.CallableTaskBuilder index 01f6607e8..e6c043b77 100644 --- a/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.CallableTaskBuilder +++ b/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.CallableTaskBuilder @@ -1,6 +1,2 @@ -io.serverlessworkflow.impl.executors.func.JavaLoopFunctionIndexCallExecutorBuilder -io.serverlessworkflow.impl.executors.func.JavaLoopFunctionCallExecutorBuilder -io.serverlessworkflow.impl.executors.func.JavaFunctionCallExecutorBuilder -io.serverlessworkflow.impl.executors.func.JavaConsumerCallExecutorBuilder -io.serverlessworkflow.impl.executors.func.JavaContextFunctionCallExecutorBuilder -io.serverlessworkflow.impl.executors.func.JavaFilterFunctionCallExecutorBuilder +io.serverlessworkflow.impl.executors.func.JavaCallFunctionBuilder + diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/CallJavaContextFunctionTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/CallJavaContextFunctionTest.java index 3a90da348..a7d46b94a 100644 --- a/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/CallJavaContextFunctionTest.java +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/CallJavaContextFunctionTest.java @@ -17,12 +17,12 @@ import static org.assertj.core.api.Assertions.assertThat; +import io.serverlessworkflow.api.types.CallTask; import io.serverlessworkflow.api.types.Document; import io.serverlessworkflow.api.types.Task; import io.serverlessworkflow.api.types.TaskItem; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.api.types.func.CallJava; -import io.serverlessworkflow.api.types.func.CallTaskJava; import io.serverlessworkflow.api.types.func.ContextFunction; import io.serverlessworkflow.impl.WorkflowApplication; import java.util.List; @@ -57,7 +57,8 @@ void testJavaContextFunction_simple() throws InterruptedException, ExecutionExce "javaContextCall", new Task() .withCallTask( - new CallTaskJava(CallJava.function(ctxFn, Person.class)))))); + new CallTask() + .withCallFunction(CallJava.function(ctxFn, Person.class)))))); var out = app.workflowDefinition(workflow) diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/CallTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/CallTest.java index 5427b5c28..063d9fd1b 100644 --- a/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/CallTest.java +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/CallTest.java @@ -17,10 +17,14 @@ import static org.assertj.core.api.Assertions.assertThat; +import io.serverlessworkflow.api.types.CallFunction; +import io.serverlessworkflow.api.types.CallTask; import io.serverlessworkflow.api.types.Document; import io.serverlessworkflow.api.types.FlowDirective; import io.serverlessworkflow.api.types.FlowDirectiveEnum; +import io.serverlessworkflow.api.types.ForTask; import io.serverlessworkflow.api.types.ForTaskConfiguration; +import io.serverlessworkflow.api.types.SwitchCase; import io.serverlessworkflow.api.types.SwitchItem; import io.serverlessworkflow.api.types.SwitchTask; import io.serverlessworkflow.api.types.Task; @@ -28,10 +32,9 @@ 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; -import io.serverlessworkflow.api.types.func.ForTaskFunction; -import io.serverlessworkflow.api.types.func.SwitchCasePredicate; -import io.serverlessworkflow.api.types.func.TaskMetadataKeys; +import io.serverlessworkflow.api.types.utils.ForTaskFunction; +import io.serverlessworkflow.api.types.utils.TaskPredicate; +import io.serverlessworkflow.api.types.utils.TypesUtils; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowDefinition; import io.serverlessworkflow.impl.WorkflowModel; @@ -72,7 +75,8 @@ private void internalJavaFunctionTest(Function function, Class c "javaCall", new Task() .withCallTask( - new CallTaskJava(CallJava.function(function, clazz)))))); + new CallTask() + .withCallFunction(CallJava.function(function, clazz)))))); assertThat( app.workflowDefinition(workflow) @@ -89,30 +93,26 @@ private void internalJavaFunctionTest(Function function, Class c void testForLoop() throws InterruptedException, ExecutionException { try (WorkflowApplication app = WorkflowApplication.builder().build()) { ForTaskConfiguration forConfig = new ForTaskConfiguration(); - Workflow workflow = - new Workflow() - .withDocument( - new Document().withNamespace("test").withName("testLoop").withVersion("1.0")) + ForTask forTask = + new ForTask() + .withFor(forConfig) .withDo( List.of( new TaskItem( - "forLoop", + "javaCall", new Task() - .withForTask( - new ForTaskFunction() - .withWhile(CallTest::isEven) - .withCollection(v -> v, Collection.class) - .withFor(forConfig) - .withDo( - List.of( - new TaskItem( - "javaCall", - new Task() - .withCallTask( - new CallTaskJava( - CallJava.loopFunction( - CallTest::sum, - forConfig.getEach())))))))))); + .withCallTask( + new CallTask() + .withCallFunction( + CallJava.loopFunction( + CallTest::sum, forConfig.getEach())))))); + ForTaskFunction.withCollection(forTask, v -> v, Collection.class); + ForTaskFunction.withWhile(forTask, CallTest::isEven); + Workflow workflow = + new Workflow() + .withDocument( + new Document().withNamespace("test").withName("testLoop").withVersion("1.0")) + .withDo(List.of(new TaskItem("forLoop", new Task().withForTask(forTask)))); assertThat( app.workflowDefinition(workflow) @@ -138,21 +138,26 @@ void testSwitch() throws InterruptedException, ExecutionException { "switch", new Task() .withSwitchTask( - new SwitchTask() - .withSwitch( - List.of( - new SwitchItem( - "odd", - new SwitchCasePredicate() - .withPredicate(CallTest::isOdd, Integer.class) - .withThen( - new FlowDirective() - .withFlowDirectiveEnum( - FlowDirectiveEnum.END))))))), + TaskPredicate.withPredicate( + new SwitchTask() + .withSwitch( + List.of( + new SwitchItem( + "odd", + new SwitchCase() + .withThen( + new FlowDirective() + .withFlowDirectiveEnum( + FlowDirectiveEnum.END))))), + "odd", + CallTest::isOdd, + Integer.class))), new TaskItem( "java", new Task() - .withCallTask(new CallTaskJava(CallJava.function(CallTest::zero)))))); + .withCallTask( + new CallTask() + .withCallFunction(CallJava.function(CallTest::zero)))))); WorkflowDefinition definition = app.workflowDefinition(workflow); assertThat(definition.instance(3).start().get().asNumber().orElseThrow()).isEqualTo(3); @@ -173,9 +178,11 @@ void testIf() throws InterruptedException, ExecutionException { "java", new Task() .withCallTask( - new CallTaskJava( - withPredicate( - CallJava.function(CallTest::zero), CallTest::isOdd)))))); + new CallTask() + .withCallFunction( + 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); @@ -195,21 +202,21 @@ void testIfWithModel() throws InterruptedException, ExecutionException { "java", new Task() .withCallTask( - new CallTaskJava( - withPredicate( - CallJava.function( - CallTest::zeroWithModel, WorkflowModel.class), - CallTest::isOdd)))))); + new CallTask() + .withCallFunction( + withPredicate( + CallJava.function( + CallTest::zeroWithModel, WorkflowModel.class), + 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(TaskMetadataKeys.IF_PREDICATE, pred)); + private CallFunction withPredicate(CallFunction call, Predicate pred) { + return call.withMetadata( + new TaskMetadata().withAdditionalProperty(TypesUtils.IF_PREDICATE, pred)); } public static boolean isEven(Object model, Integer number) { diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/ForTaskFunctionRegressionTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/ForTaskFunctionRegressionTest.java index e000f4987..c7b4fdeaa 100644 --- a/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/ForTaskFunctionRegressionTest.java +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/ForTaskFunctionRegressionTest.java @@ -17,20 +17,16 @@ import static org.assertj.core.api.Assertions.assertThat; +import io.serverlessworkflow.api.types.CallTask; import io.serverlessworkflow.api.types.Document; +import io.serverlessworkflow.api.types.ForTask; import io.serverlessworkflow.api.types.ForTaskConfiguration; import io.serverlessworkflow.api.types.Task; import io.serverlessworkflow.api.types.TaskItem; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.api.types.func.CallJava; -import io.serverlessworkflow.api.types.func.CallTaskJava; -import io.serverlessworkflow.api.types.func.ForTaskFunction; +import io.serverlessworkflow.api.types.utils.ForTaskFunction; import io.serverlessworkflow.impl.WorkflowApplication; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.lang.reflect.Field; import java.util.Collection; import java.util.List; import java.util.concurrent.ExecutionException; @@ -38,35 +34,25 @@ class ForTaskFunctionRegressionTest { - @Test - void initializesOptionalFieldsAsEmpty() { - ForTaskFunction taskFunction = new ForTaskFunction(); - - assertThat(taskFunction.getWhileClass()).isNotNull().isEmpty(); - assertThat(taskFunction.getItemClass()).isNotNull().isEmpty(); - assertThat(taskFunction.getForClass()).isNotNull().isEmpty(); - } - - @Test - void optionalFieldsSurviveJavaSerializationRoundTrip() throws Exception { - ForTaskFunction taskFunction = new ForTaskFunction(); - clearField(taskFunction, "whileClass"); - clearField(taskFunction, "itemClass"); - clearField(taskFunction, "forClass"); - clearField(taskFunction, "collection"); - - ForTaskFunction copy = roundTrip(taskFunction); - - assertThat(copy.getWhileClass()).isNotNull().isEmpty(); - assertThat(copy.getItemClass()).isNotNull().isEmpty(); - assertThat(copy.getForClass()).isNotNull().isEmpty(); - } - @Test void forLoopWithExplicitCollectionClassExecutesSuccessfully() throws InterruptedException, ExecutionException { try (WorkflowApplication app = WorkflowApplication.builder().build()) { ForTaskConfiguration forConfig = new ForTaskConfiguration(); + ForTask forTask = + new ForTask() + .withDo( + List.of( + new TaskItem( + "javaCall", + new Task() + .withCallTask( + new CallTask() + .withCallFunction( + CallJava.loopFunction( + CallTest::sum, forConfig.getEach())))))); + ForTaskFunction.withWhile(forTask, CallTest::isEven); + ForTaskFunction.withCollection(forTask, v -> v, Collection.class); Workflow workflow = new Workflow() .withDocument( @@ -76,47 +62,11 @@ void forLoopWithExplicitCollectionClassExecutesSuccessfully() .withVersion("1.0")) .withDo( List.of( - new TaskItem( - "forLoop", - new Task() - .withForTask( - new ForTaskFunction() - .withWhile(CallTest::isEven) - .withCollection(v -> v, Collection.class) - .withFor(forConfig) - .withDo( - List.of( - new TaskItem( - "javaCall", - new Task() - .withCallTask( - new CallTaskJava( - CallJava.loopFunction( - CallTest::sum, - forConfig.getEach())))))))))); + new TaskItem("forLoop", new Task().withForTask(forTask.withFor(forConfig))))); var result = app.workflowDefinition(workflow).instance(List.of(2, 4, 6)).start().get(); assertThat(result.asNumber().orElseThrow()).isEqualTo(12); } } - - private static ForTaskFunction roundTrip(ForTaskFunction taskFunction) throws Exception { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - try (ObjectOutputStream oos = new ObjectOutputStream(output)) { - oos.writeObject(taskFunction); - } - - try (ObjectInputStream ois = - new ObjectInputStream(new ByteArrayInputStream(output.toByteArray()))) { - return (ForTaskFunction) ois.readObject(); - } - } - - private static void clearField(Object target, String fieldName) - throws ReflectiveOperationException { - Field field = target.getClass().getDeclaredField(fieldName); - field.setAccessible(true); - field.set(target, null); - } } diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/ModelTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/ModelTest.java index aef280b93..7e922981a 100644 --- a/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/ModelTest.java +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/executors/func/ModelTest.java @@ -20,6 +20,7 @@ import io.serverlessworkflow.api.types.Document; import io.serverlessworkflow.api.types.DurationInline; import io.serverlessworkflow.api.types.Output; +import io.serverlessworkflow.api.types.OutputAs; import io.serverlessworkflow.api.types.Set; import io.serverlessworkflow.api.types.SetTask; import io.serverlessworkflow.api.types.Task; @@ -27,13 +28,15 @@ import io.serverlessworkflow.api.types.TimeoutAfter; import io.serverlessworkflow.api.types.WaitTask; import io.serverlessworkflow.api.types.Workflow; -import io.serverlessworkflow.api.types.func.MapSetTaskConfiguration; -import io.serverlessworkflow.api.types.func.OutputAsFunction; +import io.serverlessworkflow.api.types.func.ContextFunction; +import io.serverlessworkflow.api.types.func.FilterFunction; +import io.serverlessworkflow.api.types.utils.MapSetTaskConfiguration; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowInstance; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; +import java.util.function.Function; import org.junit.jupiter.api.Test; class ModelTest { @@ -59,7 +62,9 @@ void testStringExpression() throws InterruptedException, ExecutionException { .withOutput( new Output() .withAs( - new OutputAsFunction().withFunction(JavaFunctions::addJavieritoString))); + new OutputAs() + .withObject( + (Function) JavaFunctions::addJavieritoString))); assertThat( app.workflowDefinition(workflow) @@ -89,14 +94,15 @@ void testMapExpression() throws InterruptedException, ExecutionException { .withSet( new Set() .withSetTaskConfiguration( - new MapSetTaskConfiguration( + MapSetTaskConfiguration.map( Map.of("name", "Francisco")))) .withOutput( new Output() .withAs( - new OutputAsFunction() - .withFunction( - JavaFunctions::addJavierito))))))); + new OutputAs() + .withObject( + (Function) + JavaFunctions::addJavierito))))))); assertThat( app.workflowDefinition(workflow) .instance(Map.of()) @@ -129,7 +135,9 @@ void testStringPOJOExpression() throws InterruptedException, ExecutionException new DurationInline().withMilliseconds(10))))))) .withOutput( new Output() - .withAs(new OutputAsFunction().withFunction(JavaFunctions::personPojo))); + .withAs( + new OutputAs() + .withObject((Function) JavaFunctions::personPojo))); assertThat( app.workflowDefinition(workflow) @@ -162,7 +170,10 @@ void testPOJOStringExpression() throws InterruptedException, ExecutionException .withDurationInline( new DurationInline().withMilliseconds(10))))))) .withOutput( - new Output().withAs(new OutputAsFunction().withFunction(JavaFunctions::getName))); + new Output() + .withAs( + new OutputAs() + .withObject((Function) JavaFunctions::getName))); assertThat( app.workflowDefinition(workflow) @@ -195,7 +206,11 @@ void testPOJOStringExpressionWithContext() throws InterruptedException, Executio new DurationInline().withMilliseconds(10))))))) .withOutput( new Output() - .withAs(new OutputAsFunction().withFunction(JavaFunctions::getContextName))); + .withAs( + new OutputAs() + .withObject( + (ContextFunction) + JavaFunctions::getContextName))); WorkflowInstance instance = app.workflowDefinition(workflow).instance(new Person("Francisco", 33)); assertThat(instance.start().get().asText().orElseThrow()) @@ -220,8 +235,10 @@ void testPOJOStringExpressionWithFilter() throws InterruptedException, Execution .withOutput( new Output() .withAs( - new OutputAsFunction() - .withFunction(JavaFunctions::getFilterName))) + new OutputAs() + .withObject( + (FilterFunction) + JavaFunctions::getFilterName))) .withWait( new TimeoutAfter() .withDurationInline( diff --git a/experimental/pom.xml b/experimental/pom.xml index 54dd0e206..1fda5a72a 100644 --- a/experimental/pom.xml +++ b/experimental/pom.xml @@ -41,6 +41,11 @@ serverlessworkflow-experimental-fluent-func ${project.version} + + io.serverlessworkflow + serverlessworkflow-experimental-fluent-serialization-jackson + ${project.version} + diff --git a/experimental/test/pom.xml b/experimental/test/pom.xml index 85f3d96c1..bb09d60cc 100644 --- a/experimental/test/pom.xml +++ b/experimental/test/pom.xml @@ -97,5 +97,20 @@ ${version.org.glassfish.jersey} test + + io.serverlessworkflow + serverlessworkflow-api + test + + + org.junit.jupiter + junit-jupiter-params + test + + + io.serverlessworkflow + serverlessworkflow-experimental-fluent-serialization-jackson + test + \ No newline at end of file diff --git a/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/ForEachFuncTest.java b/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/ForEachFuncTest.java index 01737f035..cea746e57 100644 --- a/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/ForEachFuncTest.java +++ b/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/ForEachFuncTest.java @@ -16,6 +16,7 @@ package io.serverlessworkflow.fluent.test; import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.*; +import static io.serverlessworkflow.fluent.test.TestSerializationUtils.writeAndReadInMemory; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; @@ -26,6 +27,7 @@ import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.lifecycle.TraceExecutionListener; +import java.io.IOException; import java.time.Duration; import java.util.Collection; import java.util.List; @@ -44,12 +46,13 @@ private record OrdersPayload(List orders) {} private record OrderName(String id, String name) {} @Test - void testForEachIteration() { + void testForEachIteration() throws IOException { Workflow workflow = - FuncWorkflowBuilder.workflow("foreach-workflow") - .tasks(forEachItem(OrdersPayload::orders, ForEachFuncTest::enhace)) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow("foreach-workflow") + .tasks(forEachItem(OrdersPayload::orders, ForEachFuncTest::enhace)) + .build()); try (WorkflowApplication app = WorkflowApplication.builder().build()) { OrdersPayload input = diff --git a/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/ForkFuncTest.java b/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/ForkFuncTest.java index a70ef4d5b..c63f8ed72 100644 --- a/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/ForkFuncTest.java +++ b/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/ForkFuncTest.java @@ -21,12 +21,19 @@ import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.fluent.func.FuncWorkflowBuilder; import io.serverlessworkflow.impl.WorkflowApplication; +import java.io.IOException; import org.junit.jupiter.api.Test; public class ForkFuncTest { + private int value = 2; + + public int getValue() { + return value; + } + @Test - void testForkVerbose() { + void testForkVerbose() throws IOException { testIt( FuncWorkflowBuilder.workflow("parallel-execution-workflow") .tasks( @@ -35,35 +42,36 @@ void testForkVerbose() { funcForkTaskBuilder -> funcForkTaskBuilder.branches( inner -> { - inner.function(f -> f.function(this::doubleIt, int.class)); - inner.function(f -> f.function(this::halfIt, int.class)); + inner.function(f -> f.function(this::doubleIt)); + inner.function(f -> f.function(this::halfIt)); }))) .build()); } @Test - void testForkSyntaxSugar() { + void testForkSyntaxSugar() throws IOException { testIt( FuncWorkflowBuilder.workflow("parallel-execution-workflow") .tasks(fork(function(this::doubleIt), function(this::halfIt))) .build()); } - private void testIt(Workflow workflow) { + private void testIt(Workflow workflow) throws IOException { + workflow = TestSerializationUtils.writeAndReadInMemory(workflow); try (WorkflowApplication app = WorkflowApplication.builder().build()) { assertThat( app.workflowDefinition(workflow).instance(8).start().join().asCollection().stream() .flatMap(m -> m.asMap().orElseThrow().values().stream()) .toList()) - .containsExactlyInAnyOrder(4, 16); + .containsExactlyInAnyOrder(2, 18); } } private int doubleIt(int number) { - return number << 1; + return (number << 1) + value; } private int halfIt(int number) { - return number >> 1; + return (number >> 1) - value; } } diff --git a/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncCallAsyncTest.java b/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncCallAsyncTest.java index 1aead510e..d4188a6c1 100644 --- a/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncCallAsyncTest.java +++ b/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncCallAsyncTest.java @@ -25,6 +25,7 @@ import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.lifecycle.WorkflowExecutionListener; import io.serverlessworkflow.impl.lifecycle.WorkflowStartedEvent; +import java.io.IOException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicLong; import org.junit.jupiter.api.Test; @@ -53,17 +54,17 @@ private Integer waitSync(Integer waitTime) { } @Test - void testCompletableCall() { + void testCompletableCall() throws IOException { runIt(FuncWorkflowBuilder.workflow("waitCompletable").tasks(function(this::waitAsync)).build()); } @Test - void testReferencedFunctionCall() { + void testReferencedFunctionCall() throws IOException { runIt(FuncWorkflowBuilder.workflow("waitReference").tasks(function(this::waitSync)).build()); } @Test - void testLambdaCall() { + void testLambdaCall() throws IOException { runIt(FuncWorkflowBuilder.workflow("waitLambda").tasks(function(v -> 1)).build()); } @@ -77,11 +78,13 @@ public void onWorkflowStarted(WorkflowStartedEvent ev) { } } - private void runIt(Workflow workflow) { + private void runIt(Workflow workflow) throws IOException { TimeListener listener = new TimeListener(); try (WorkflowApplication app = WorkflowApplication.builder().withListener(listener).build()) { final long waitTime = 200; - WorkflowInstance instance = app.workflowDefinition(workflow).instance(waitTime); + WorkflowInstance instance = + app.workflowDefinition(TestSerializationUtils.writeAndReadInMemory(workflow)) + .instance(waitTime); CompletableFuture future = instance.start(); assertThat(System.currentTimeMillis() - listener.startTime.get()).isLessThan(waitTime); assertThat(future.join().asNumber().map(Number::intValue).orElseThrow()).isEqualTo(1); diff --git a/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncDSLSerializationTest.java b/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncDSLSerializationTest.java new file mode 100644 index 000000000..c8d753f49 --- /dev/null +++ b/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncDSLSerializationTest.java @@ -0,0 +1,107 @@ +/* + * 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.test; + +import static io.serverlessworkflow.api.WorkflowWriter.workflowAsBytes; +import static io.serverlessworkflow.api.WorkflowWriter.workflowAsString; +import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.function; +import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.withContext; +import static io.serverlessworkflow.fluent.test.TestSerializationUtils.writeAndReadInMemory; +import static org.assertj.core.api.Assertions.assertThat; + +import io.serverlessworkflow.api.WorkflowFormat; +import io.serverlessworkflow.api.types.Workflow; +import io.serverlessworkflow.fluent.func.FuncWorkflowBuilder; +import io.serverlessworkflow.impl.WorkflowApplication; +import io.serverlessworkflow.impl.WorkflowContextData; +import io.serverlessworkflow.impl.WorkflowDefinition; +import java.io.IOException; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class FuncDSLSerializationTest { + + @ParameterizedTest + @MethodSource("workflows") + public void testSpecFeaturesParsing(Workflow workflow, Consumer assertion) + throws IOException { + Workflow otherWorkflow = writeAndReadInMemory(workflow); + assertWorkflowEquals(workflow, otherWorkflow); + try (WorkflowApplication application = WorkflowApplication.builder().build()) { + assertion.accept(application.workflowDefinition(otherWorkflow)); + } + } + + static Stream workflows() { + final int QUANTITY = 3; + return Stream.of( + Arguments.of( + FuncWorkflowBuilder.workflow("hello") + .tasks(t -> t.set("sayHelloWorld", b -> b.expr(Map.of("result", "hello world!")))) + .build(), + new CheckResult(Map.of(), Map.of("result", "hello world!"))), + Arguments.of( + FuncWorkflowBuilder.workflow("inc") + .tasks(function(FuncDSLSerializationTest::inc)) + .build(), + new CheckResult(1, 2)), + Arguments.of( + FuncWorkflowBuilder.workflow("incContext") + .tasks(withContext(FuncDSLSerializationTest::incContext)) + .build(), + new CheckResult(1, 3)), + Arguments.of( + FuncWorkflowBuilder.workflow("incLambda") + .tasks(function((Integer number) -> number + QUANTITY)) + .build(), + new CheckResult(1, 4))); + } + + private static class CheckResult implements Consumer { + + private final Object input; + private final Object output; + + public CheckResult(Object input, Object output) { + this.input = input; + this.output = output; + } + + @Override + public void accept(WorkflowDefinition t) { + assertThat(t.instance(this.input).start().join().asJavaObject()).isEqualTo(this.output); + } + } + + private static void assertWorkflowEquals(Workflow workflow, Workflow other) throws IOException { + assertThat(workflowAsString(workflow, WorkflowFormat.YAML)) + .isEqualTo(workflowAsString(other, WorkflowFormat.YAML)); + assertThat(workflowAsBytes(workflow, WorkflowFormat.JSON)) + .isEqualTo(workflowAsBytes(other, WorkflowFormat.JSON)); + } + + private static Integer inc(Integer quantity) { + return quantity + 1; + } + + private static Integer incContext(Integer quantity, WorkflowContextData workflowContext) { + return quantity + 2; + } +} diff --git a/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncDoTaskTest.java b/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncDoTaskTest.java index d73142ab5..28a2dc22f 100644 --- a/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncDoTaskTest.java +++ b/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncDoTaskTest.java @@ -22,6 +22,7 @@ import io.serverlessworkflow.impl.WorkflowInstance; import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowStatus; +import java.io.IOException; import java.net.URI; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -30,7 +31,7 @@ public class FuncDoTaskTest { @Test - void testDoTaskRaiseAndTryCatch() throws Exception { + void testDoTaskRaiseAndTryCatch() throws IOException { try (WorkflowApplication app = WorkflowApplication.builder().build()) { var workflow = @@ -67,7 +68,9 @@ void testDoTaskRaiseAndTryCatch() throws Exception { Map.of("handled", true))))))) .build(); - WorkflowInstance instance = app.workflowDefinition(workflow).instance(Map.of()); + WorkflowInstance instance = + app.workflowDefinition(TestSerializationUtils.writeAndReadInMemory(workflow)) + .instance(Map.of()); CompletableFuture future = instance.start(); WorkflowModel result = future.join(); diff --git a/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncEventFilterTest.java b/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncEventFilterTest.java index dbac8eda7..645373c05 100644 --- a/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncEventFilterTest.java +++ b/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncEventFilterTest.java @@ -23,6 +23,7 @@ import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.switchWhenOrElse; import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.to; import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.toOne; +import static io.serverlessworkflow.fluent.test.TestSerializationUtils.writeAndReadInMemory; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; @@ -42,6 +43,7 @@ import io.serverlessworkflow.impl.WorkflowModelCollection; import io.serverlessworkflow.impl.WorkflowStatus; import io.serverlessworkflow.impl.events.EventPublisher; +import java.io.IOException; import java.net.URI; import java.nio.charset.StandardCharsets; import java.time.Duration; @@ -60,7 +62,7 @@ class FuncEventFilterTest { @Test - void testListenToOneCollection() { + void testListenToOneCollection() throws IOException { runIt( FuncWorkflowBuilder.workflow("listenToOneReviewCol") .tasks( @@ -70,7 +72,7 @@ void testListenToOneCollection() { } @Test - void testListenToOneArrayNode() { + void testListenToOneArrayNode() throws IOException { runIt( FuncWorkflowBuilder.workflow("listenToOneReviewNode") .tasks( @@ -80,7 +82,7 @@ void testListenToOneArrayNode() { } @Test - void testListenToOneList() { + void testListenToOneList() throws IOException { runIt( FuncWorkflowBuilder.workflow("listenToOneReviewList") .tasks( @@ -90,7 +92,7 @@ void testListenToOneList() { } @Test - void testListenToOneSet() { + void testListenToOneSet() throws IOException { runIt( FuncWorkflowBuilder.workflow("listenToOneReviewSet") .tasks( @@ -100,7 +102,7 @@ void testListenToOneSet() { } @Test - void testListenToOneArray() { + void testListenToOneArray() throws IOException { runIt( FuncWorkflowBuilder.workflow("listenToOneReviewArray") .tasks( @@ -145,13 +147,13 @@ private Workflow reviewEmitter() { .build(); } - private void runIt(Workflow listen) { + private void runIt(Workflow listen) throws IOException { Review review = new Review("Torrente", "espectacular", 5); try (WorkflowApplication app = WorkflowApplication.builder().build()) { CompletableFuture waiting = - app.workflowDefinition(listen).instance(Map.of()).start(); + app.workflowDefinition(writeAndReadInMemory(listen)).instance(Map.of()).start(); app.workflowDefinition(reviewEmitter()).instance(review).start().join(); diff --git a/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncHttpTest.java b/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncHttpTest.java index 724b4fa8e..d6ff8766a 100644 --- a/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncHttpTest.java +++ b/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncHttpTest.java @@ -16,6 +16,7 @@ package io.serverlessworkflow.fluent.test; import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.http; +import static io.serverlessworkflow.fluent.test.TestSerializationUtils.writeAndReadInMemory; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.SoftAssertions.assertSoftly; @@ -65,13 +66,14 @@ private RecordedRequest takeRequestOrFail() throws Exception { @DisplayName("Query method with single key-value pair") void test_query_with_single_key_value() throws Exception { var workflow = - FuncWorkflowBuilder.workflow("test-query-single") - .tasks( - http("callHttp") - .GET() - .uri(mockServer.url("/api/endpoint").toString()) - .query("param1", "value1")) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow("test-query-single") + .tasks( + http("callHttp") + .GET() + .uri(mockServer.url("/api/endpoint").toString()) + .query("param1", "value1")) + .build()); WorkflowInstance instance = app.workflowDefinition(workflow).instance(Map.of()); @@ -90,15 +92,16 @@ void test_query_with_single_key_value() throws Exception { @DisplayName("Query method with multiple single key-value pairs (individually tested)") void test_query_with_multiple_single_values() throws Exception { var workflow = - FuncWorkflowBuilder.workflow("test-query-single-multi") - .tasks( - http("callHttp") - .GET() - .uri(mockServer.url("/api/endpoint").toString()) - .query("param1", "value1") - .query("param2", "value2") - .query("param3", "value3")) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow("test-query-single-multi") + .tasks( + http("callHttp") + .GET() + .uri(mockServer.url("/api/endpoint").toString()) + .query("param1", "value1") + .query("param2", "value2") + .query("param3", "value3")) + .build()); WorkflowInstance instance = app.workflowDefinition(workflow).instance(Map.of()); instance.start().join(); @@ -119,13 +122,14 @@ void test_query_with_multiple_single_values() throws Exception { void test_query_with_map() throws Exception { var workflow = - FuncWorkflowBuilder.workflow("test-query-map") - .tasks( - http("callHttp") - .GET() - .uri(mockServer.url("/api/endpoint").toString()) - .query(Map.of("userId", "123", "userName", "john", "status", "active"))) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow("test-query-map") + .tasks( + http("callHttp") + .GET() + .uri(mockServer.url("/api/endpoint").toString()) + .query(Map.of("userId", "123", "userName", "john", "status", "active"))) + .build()); WorkflowInstance instance = app.workflowDefinition(workflow).instance(Map.of()); instance.start().join(); @@ -145,13 +149,14 @@ void test_query_with_map() throws Exception { @DisplayName("Query method with expression string") void test_query_with_expression() throws Exception { var workflow = - FuncWorkflowBuilder.workflow("test-query-expression") - .tasks( - http("callHttp") - .GET() - .uri(mockServer.url("/api/endpoint").toString()) - .query("enabled", "${ .enabled }")) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow("test-query-expression") + .tasks( + http("callHttp") + .GET() + .uri(mockServer.url("/api/endpoint").toString()) + .query("enabled", "${ .enabled }")) + .build()); WorkflowInstance instance = app.workflowDefinition(workflow).instance(Map.of("enabled", true)); instance.start().join(); @@ -165,13 +170,14 @@ void test_query_with_expression() throws Exception { @DisplayName("Query method with empty Map") void test_query_with_empty_map() throws Exception { var workflow = - FuncWorkflowBuilder.workflow("test-query-empty-map") - .tasks( - http("callHttp") - .GET() - .uri(mockServer.url("/api/endpoint").toString()) - .query(Map.of())) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow("test-query-empty-map") + .tasks( + http("callHttp") + .GET() + .uri(mockServer.url("/api/endpoint").toString()) + .query(Map.of())) + .build()); WorkflowInstance instance = app.workflowDefinition(workflow).instance(Map.of()); instance.start().join(); @@ -189,13 +195,14 @@ void test_query_with_empty_map() throws Exception { @DisplayName("Query method with special characters in values") void test_query_with_special_characters() throws Exception { var workflow = - FuncWorkflowBuilder.workflow("test-query-special-chars") - .tasks( - http("callHttp") - .GET() - .uri(mockServer.url("/api/endpoint").toString()) - .query("email", "user@example.com")) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow("test-query-special-chars") + .tasks( + http("callHttp") + .GET() + .uri(mockServer.url("/api/endpoint").toString()) + .query("email", "user@example.com")) + .build()); WorkflowInstance instance = app.workflowDefinition(workflow).instance(Map.of()); instance.start().join(); @@ -213,13 +220,14 @@ void test_query_with_special_characters() throws Exception { @DisplayName("Query method overload - Map with multiple values") void test_query_map_multiple_values() throws Exception { var workflow = - FuncWorkflowBuilder.workflow("test-query-map-multi") - .tasks( - http("callHttp") - .GET() - .uri(mockServer.url("/api/endpoint").toString()) - .query(Map.of("limit", "50", "offset", "0", "sort", "name"))) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow("test-query-map-multi") + .tasks( + http("callHttp") + .GET() + .uri(mockServer.url("/api/endpoint").toString()) + .query(Map.of("limit", "50", "offset", "0", "sort", "name"))) + .build()); WorkflowInstance instance = app.workflowDefinition(workflow).instance(Map.of()); instance.start().join(); @@ -239,15 +247,16 @@ void test_query_map_multiple_values() throws Exception { @DisplayName("Query method with headers and query parameters") void test_query_with_headers_and_query() throws Exception { var workflow = - FuncWorkflowBuilder.workflow("test-query-with-headers") - .tasks( - http("callHttp") - .GET() - .uri(mockServer.url("/api/endpoint").toString()) - .header("Authorization", "Bearer token123") - .header("Accept", "application/json") - .query("userId", "123")) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow("test-query-with-headers") + .tasks( + http("callHttp") + .GET() + .uri(mockServer.url("/api/endpoint").toString()) + .header("Authorization", "Bearer token123") + .header("Accept", "application/json") + .query("userId", "123")) + .build()); WorkflowInstance instance = app.workflowDefinition(workflow).instance(Map.of()); instance.start().join(); diff --git a/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncTryCatchTest.java b/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncTryCatchTest.java index db33b5a53..a97a6755c 100644 --- a/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncTryCatchTest.java +++ b/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/FuncTryCatchTest.java @@ -33,6 +33,7 @@ import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.function; import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.tryCatch; +import static io.serverlessworkflow.fluent.test.TestSerializationUtils.writeAndReadInMemory; import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import io.serverlessworkflow.api.types.Workflow; @@ -42,6 +43,7 @@ import io.serverlessworkflow.impl.WorkflowError; import io.serverlessworkflow.impl.WorkflowException; import io.serverlessworkflow.impl.WorkflowModel; +import java.io.IOException; import java.util.List; import org.junit.jupiter.api.Test; import org.slf4j.Logger; @@ -60,34 +62,37 @@ public class FuncTryCatchTest { private static final String ORDER_003 = "ORDER#003"; @Test - void booking_compensation_dsl() { + void booking_compensation_dsl() throws IOException { Workflow workflow = - FuncWorkflowBuilder.workflow() - .tasks( - tryCatch( - "tryStockReservation", - t -> - t.tryCatch(function("stockReservation", this::reserveStock)) - .catchError( - err -> err.type(STOCK_ORDER_ERROR), - function("cancelStockReservation", this::cancelReservation) - .then("endFlow"))), - tryCatch( - "tryPaymentProcessing", - t -> - t.tryCatch(function("paymentProcessing", this::processPayment)) - .catchWhen( - "${ .status == 503 }", - function("cancelPayment", this::cancelPayment).then("endFlow"))), - tryCatch( - "tryShipping", - t -> - t.tryCatch(function("scheduleShipping", this::scheduleShipping)) - .catchType( - SHIPPING_ERROR, function("cancelPayment", this::cancelShipping))), - function("endFlow", this::endFlow)) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow() + .tasks( + tryCatch( + "tryStockReservation", + t -> + t.tryCatch(function("stockReservation", this::reserveStock)) + .catchError( + err -> err.type(STOCK_ORDER_ERROR), + function("cancelStockReservation", this::cancelReservation) + .then("endFlow"))), + tryCatch( + "tryPaymentProcessing", + t -> + t.tryCatch(function("paymentProcessing", this::processPayment)) + .catchWhen( + "${ .status == 503 }", + function("cancelPayment", this::cancelPayment) + .then("endFlow"))), + tryCatch( + "tryShipping", + t -> + t.tryCatch(function("scheduleShipping", this::scheduleShipping)) + .catchType( + SHIPPING_ERROR, + function("cancelPayment", this::cancelShipping))), + function("endFlow", this::endFlow)) + .build()); try (WorkflowApplication application = WorkflowApplication.builder().build()) { WorkflowDefinition workflowDefinition = application.workflowDefinition(workflow); @@ -100,22 +105,23 @@ SHIPPING_ERROR, function("cancelPayment", this::cancelShipping))), } @Test - void testStockReservationError_CatchByType() { + void testStockReservationError_CatchByType() throws IOException { log.info("Testing stock reservation error with catch by type"); Workflow workflow = - FuncWorkflowBuilder.workflow() - .tasks( - tryCatch( - "tryStockReservation", - t -> - t.tryCatch(function("stockReservation", this::reserveStock)) - .catchError( - err -> err.type(STOCK_ORDER_ERROR), - function("cancelStockReservation", this::cancelReservation) - .then("endFlow"))), - function("endFlow", this::endFlow)) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow() + .tasks( + tryCatch( + "tryStockReservation", + t -> + t.tryCatch(function("stockReservation", this::reserveStock)) + .catchError( + err -> err.type(STOCK_ORDER_ERROR), + function("cancelStockReservation", this::cancelReservation) + .then("endFlow"))), + function("endFlow", this::endFlow)) + .build()); try (WorkflowApplication application = WorkflowApplication.builder().build()) { WorkflowDefinition workflowDefinition = application.workflowDefinition(workflow); @@ -128,22 +134,23 @@ void testStockReservationError_CatchByType() { } @Test - void testStockReservationError_CatchByStatus() { + void testStockReservationError_CatchByStatus() throws IOException { log.info("Testing stock reservation error with catch by status code"); Workflow workflow = - FuncWorkflowBuilder.workflow() - .tasks( - tryCatch( - "tryStockReservation", - t -> - t.tryCatch(function("stockReservation", this::reserveStock)) - .catchWhen( - "${ .status == 409 }", - function("cancelStockReservation", this::cancelReservation) - .then("endFlow"))), - function("endFlow", this::endFlow)) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow() + .tasks( + tryCatch( + "tryStockReservation", + t -> + t.tryCatch(function("stockReservation", this::reserveStock)) + .catchWhen( + "${ .status == 409 }", + function("cancelStockReservation", this::cancelReservation) + .then("endFlow"))), + function("endFlow", this::endFlow)) + .build()); try (WorkflowApplication application = WorkflowApplication.builder().build()) { WorkflowDefinition workflowDefinition = application.workflowDefinition(workflow); @@ -156,22 +163,23 @@ void testStockReservationError_CatchByStatus() { } @Test - void testStockReservationError_CatchType() { + void testStockReservationError_CatchType() throws IOException { log.info("Testing stock reservation error with catchType"); Workflow workflow = - FuncWorkflowBuilder.workflow() - .tasks( - tryCatch( - "tryStockReservation", - t -> - t.tryCatch(function("stockReservation", this::reserveStock)) - .catchType( - STOCK_ORDER_ERROR, - function("cancelStockReservation", this::cancelReservation) - .then("endFlow"))), - function("endFlow", this::endFlow)) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow() + .tasks( + tryCatch( + "tryStockReservation", + t -> + t.tryCatch(function("stockReservation", this::reserveStock)) + .catchType( + STOCK_ORDER_ERROR, + function("cancelStockReservation", this::cancelReservation) + .then("endFlow"))), + function("endFlow", this::endFlow)) + .build()); try (WorkflowApplication application = WorkflowApplication.builder().build()) { WorkflowDefinition workflowDefinition = application.workflowDefinition(workflow); @@ -184,21 +192,23 @@ void testStockReservationError_CatchType() { } @Test - void testPaymentProcessingError_CatchByType() { + void testPaymentProcessingError_CatchByType() throws IOException { log.info("Testing payment processing error with catch by type"); Workflow workflow = - FuncWorkflowBuilder.workflow() - .tasks( - tryCatch( - "tryPaymentProcessing", - t -> - t.tryCatch(function("paymentProcessing", this::processPayment)) - .catchError( - err -> err.type(PAYMENT_PROCESSING_ERROR), - function("cancelPayment", this::cancelPayment).then("endFlow"))), - function("endFlow", this::endFlow)) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow() + .tasks( + tryCatch( + "tryPaymentProcessing", + t -> + t.tryCatch(function("paymentProcessing", this::processPayment)) + .catchError( + err -> err.type(PAYMENT_PROCESSING_ERROR), + function("cancelPayment", this::cancelPayment) + .then("endFlow"))), + function("endFlow", this::endFlow)) + .build()); try (WorkflowApplication application = WorkflowApplication.builder().build()) { WorkflowDefinition workflowDefinition = application.workflowDefinition(workflow); @@ -211,21 +221,23 @@ void testPaymentProcessingError_CatchByType() { } @Test - void testPaymentProcessingError_CatchByStatus() { + void testPaymentProcessingError_CatchByStatus() throws IOException { log.info("Testing payment processing error with catch by status code"); Workflow workflow = - FuncWorkflowBuilder.workflow() - .tasks( - tryCatch( - "tryPaymentProcessing", - t -> - t.tryCatch(function("paymentProcessing", this::processPayment)) - .catchWhen( - "${ .status == 503 }", - function("cancelPayment", this::cancelPayment).then("endFlow"))), - function("endFlow", this::endFlow)) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow() + .tasks( + tryCatch( + "tryPaymentProcessing", + t -> + t.tryCatch(function("paymentProcessing", this::processPayment)) + .catchWhen( + "${ .status == 503 }", + function("cancelPayment", this::cancelPayment) + .then("endFlow"))), + function("endFlow", this::endFlow)) + .build()); try (WorkflowApplication application = WorkflowApplication.builder().build()) { WorkflowDefinition workflowDefinition = application.workflowDefinition(workflow); @@ -238,21 +250,23 @@ void testPaymentProcessingError_CatchByStatus() { } @Test - void testPaymentProcessingError_CatchType() { + void testPaymentProcessingError_CatchType() throws IOException { log.info("Testing payment processing error with catchType"); Workflow workflow = - FuncWorkflowBuilder.workflow() - .tasks( - tryCatch( - "tryPaymentProcessing", - t -> - t.tryCatch(function("paymentProcessing", this::processPayment)) - .catchType( - PAYMENT_PROCESSING_ERROR, - function("cancelPayment", this::cancelPayment).then("endFlow"))), - function("endFlow", this::endFlow)) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow() + .tasks( + tryCatch( + "tryPaymentProcessing", + t -> + t.tryCatch(function("paymentProcessing", this::processPayment)) + .catchType( + PAYMENT_PROCESSING_ERROR, + function("cancelPayment", this::cancelPayment) + .then("endFlow"))), + function("endFlow", this::endFlow)) + .build()); try (WorkflowApplication application = WorkflowApplication.builder().build()) { WorkflowDefinition workflowDefinition = application.workflowDefinition(workflow); @@ -265,21 +279,23 @@ void testPaymentProcessingError_CatchType() { } @Test - void testShippingError_CatchByType() { + void testShippingError_CatchByType() throws IOException { log.info("Testing shipping error with catch by type"); Workflow workflow = - FuncWorkflowBuilder.workflow() - .tasks( - tryCatch( - "tryShipping", - t -> - t.tryCatch(function("scheduleShipping", this::scheduleShipping)) - .catchError( - err -> err.type(SHIPPING_ERROR), - function("cancelShipping", this::cancelShipping).then("endFlow"))), - function("endFlow", this::endFlow)) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow() + .tasks( + tryCatch( + "tryShipping", + t -> + t.tryCatch(function("scheduleShipping", this::scheduleShipping)) + .catchError( + err -> err.type(SHIPPING_ERROR), + function("cancelShipping", this::cancelShipping) + .then("endFlow"))), + function("endFlow", this::endFlow)) + .build()); try (WorkflowApplication application = WorkflowApplication.builder().build()) { WorkflowDefinition workflowDefinition = application.workflowDefinition(workflow); @@ -292,21 +308,23 @@ void testShippingError_CatchByType() { } @Test - void testShippingError_CatchByStatus() { + void testShippingError_CatchByStatus() throws IOException { log.info("Testing shipping error with catch by status code"); Workflow workflow = - FuncWorkflowBuilder.workflow() - .tasks( - tryCatch( - "tryShipping", - t -> - t.tryCatch(function("scheduleShipping", this::scheduleShipping)) - .catchWhen( - "${ .status == 500 }", - function("cancelShipping", this::cancelShipping).then("endFlow"))), - function("endFlow", this::endFlow)) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow() + .tasks( + tryCatch( + "tryShipping", + t -> + t.tryCatch(function("scheduleShipping", this::scheduleShipping)) + .catchWhen( + "${ .status == 500 }", + function("cancelShipping", this::cancelShipping) + .then("endFlow"))), + function("endFlow", this::endFlow)) + .build()); try (WorkflowApplication application = WorkflowApplication.builder().build()) { WorkflowDefinition workflowDefinition = application.workflowDefinition(workflow); @@ -319,21 +337,23 @@ void testShippingError_CatchByStatus() { } @Test - void testShippingError_CatchType() { + void testShippingError_CatchType() throws IOException { log.info("Testing shipping error with catchType"); Workflow workflow = - FuncWorkflowBuilder.workflow() - .tasks( - tryCatch( - "tryShipping", - t -> - t.tryCatch(function("scheduleShipping", this::scheduleShipping)) - .catchType( - SHIPPING_ERROR, - function("cancelShipping", this::cancelShipping).then("endFlow"))), - function("endFlow", this::endFlow)) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow() + .tasks( + tryCatch( + "tryShipping", + t -> + t.tryCatch(function("scheduleShipping", this::scheduleShipping)) + .catchType( + SHIPPING_ERROR, + function("cancelShipping", this::cancelShipping) + .then("endFlow"))), + function("endFlow", this::endFlow)) + .build()); try (WorkflowApplication application = WorkflowApplication.builder().build()) { WorkflowDefinition workflowDefinition = application.workflowDefinition(workflow); @@ -346,35 +366,38 @@ void testShippingError_CatchType() { } @Test - void testSuccessfulFlow_NoErrors() { + void testSuccessfulFlow_NoErrors() throws IOException { log.info("Testing successful flow without any errors"); Workflow workflow = - FuncWorkflowBuilder.workflow() - .tasks( - tryCatch( - "tryStockReservation", - t -> - t.tryCatch(function("stockReservation", this::reserveStock)) - .catchError( - err -> err.type(STOCK_ORDER_ERROR), - function("cancelStockReservation", this::cancelReservation) - .then("endFlow"))), - tryCatch( - "tryPaymentProcessing", - t -> - t.tryCatch(function("paymentProcessing", this::processPayment)) - .catchWhen( - "${ .status == 503 }", - function("cancelPayment", this::cancelPayment).then("endFlow"))), - tryCatch( - "tryShipping", - t -> - t.tryCatch(function("scheduleShipping", this::scheduleShipping)) - .catchType( - SHIPPING_ERROR, function("cancelShipping", this::cancelShipping))), - function("endFlow", this::endFlow)) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow() + .tasks( + tryCatch( + "tryStockReservation", + t -> + t.tryCatch(function("stockReservation", this::reserveStock)) + .catchError( + err -> err.type(STOCK_ORDER_ERROR), + function("cancelStockReservation", this::cancelReservation) + .then("endFlow"))), + tryCatch( + "tryPaymentProcessing", + t -> + t.tryCatch(function("paymentProcessing", this::processPayment)) + .catchWhen( + "${ .status == 503 }", + function("cancelPayment", this::cancelPayment) + .then("endFlow"))), + tryCatch( + "tryShipping", + t -> + t.tryCatch(function("scheduleShipping", this::scheduleShipping)) + .catchType( + SHIPPING_ERROR, + function("cancelShipping", this::cancelShipping))), + function("endFlow", this::endFlow)) + .build()); try (WorkflowApplication application = WorkflowApplication.builder().build()) { WorkflowDefinition workflowDefinition = application.workflowDefinition(workflow); @@ -388,26 +411,27 @@ SHIPPING_ERROR, function("cancelShipping", this::cancelShipping))), } @Test - void testMultipleCatchHandlers_FirstMatches() { + void testMultipleCatchHandlers_FirstMatches() throws IOException { log.info("Testing multiple catch handlers where first one matches"); Workflow workflow = - FuncWorkflowBuilder.workflow() - .tasks( - tryCatch( - "tryStockReservation", - t -> - t.tryCatch(function("stockReservation", this::reserveStock)) - .catchError( - err -> err.type(STOCK_ORDER_ERROR), - function("cancelStockReservation", this::cancelReservation) - .then("endFlow")) - .catchWhen( - "${ .status == 409 }", - function("alternativeCancellation", this::cancelReservation) - .then("endFlow"))), - function("endFlow", this::endFlow)) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow() + .tasks( + tryCatch( + "tryStockReservation", + t -> + t.tryCatch(function("stockReservation", this::reserveStock)) + .catchError( + err -> err.type(STOCK_ORDER_ERROR), + function("cancelStockReservation", this::cancelReservation) + .then("endFlow")) + .catchWhen( + "${ .status == 409 }", + function("alternativeCancellation", this::cancelReservation) + .then("endFlow"))), + function("endFlow", this::endFlow)) + .build()); try (WorkflowApplication application = WorkflowApplication.builder().build()) { WorkflowDefinition workflowDefinition = application.workflowDefinition(workflow); @@ -420,22 +444,23 @@ void testMultipleCatchHandlers_FirstMatches() { } @Test - void testCatchAll_WithAnyError() { + void testCatchAll_WithAnyError() throws IOException { log.info("Testing catch-all handler for any error"); Workflow workflow = - FuncWorkflowBuilder.workflow() - .tasks( - tryCatch( - "tryStockReservation", - t -> - t.tryCatch(function("stockReservation", this::reserveStock)) - .catchWhen( - "${ true }", - function("genericErrorHandler", this::cancelReservation) - .then("endFlow"))), - function("endFlow", this::endFlow)) - .build(); + writeAndReadInMemory( + FuncWorkflowBuilder.workflow() + .tasks( + tryCatch( + "tryStockReservation", + t -> + t.tryCatch(function("stockReservation", this::reserveStock)) + .catchWhen( + "${ true }", + function("genericErrorHandler", this::cancelReservation) + .then("endFlow"))), + function("endFlow", this::endFlow)) + .build()); try (WorkflowApplication application = WorkflowApplication.builder().build()) { WorkflowDefinition workflowDefinition = application.workflowDefinition(workflow); diff --git a/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/TestSerializationUtils.java b/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/TestSerializationUtils.java new file mode 100644 index 000000000..b0dc16b0b --- /dev/null +++ b/experimental/test/src/test/java/io/serverlessworkflow/fluent/test/TestSerializationUtils.java @@ -0,0 +1,48 @@ +/* + * 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.test; + +import static io.serverlessworkflow.api.WorkflowReader.readWorkflow; +import static io.serverlessworkflow.api.WorkflowWriter.writeWorkflow; + +import io.serverlessworkflow.api.WorkflowFormat; +import io.serverlessworkflow.api.types.Workflow; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class TestSerializationUtils { + + private static final Logger logger = LoggerFactory.getLogger(TestSerializationUtils.class); + + private TestSerializationUtils() {} + + static Workflow writeAndReadInMemory(Workflow workflow) throws IOException { + byte[] bytes; + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + writeWorkflow(out, workflow, WorkflowFormat.YAML); + bytes = out.toByteArray(); + } + if (logger.isDebugEnabled()) { + logger.debug("Workflow string representation is {}", new String(bytes)); + } + try (ByteArrayInputStream in = new ByteArrayInputStream(bytes)) { + return readWorkflow(in, WorkflowFormat.YAML); + } + } +} diff --git a/experimental/test/src/test/resources/logback.xml b/experimental/test/src/test/resources/logback.xml new file mode 100644 index 000000000..355a1df3e --- /dev/null +++ b/experimental/test/src/test/resources/logback.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + \ No newline at end of file diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/ReflectionUtils.java b/experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/ReflectionUtils.java deleted file mode 100644 index 6b30fcdb2..000000000 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/ReflectionUtils.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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.reflection.func; - -import io.serverlessworkflow.api.types.func.ContextFunction; -import io.serverlessworkflow.api.types.func.FilterFunction; -import java.lang.invoke.MethodType; -import java.lang.invoke.SerializedLambda; -import java.lang.reflect.Method; -import java.util.function.Function; - -/** - * Specially used by {@link Function} parameters in the Java Function. - * - * @see Serialize a Lambda in Java - */ -public final class ReflectionUtils { - - private ReflectionUtils() {} - - @SuppressWarnings("unchecked") - public static Class inferInputType(ContextFunction fn) { - return (Class) inferInputTypeFromAny(fn, 0); - } - - @SuppressWarnings("unchecked") - public static Class inferInputType(FilterFunction fn) { - return (Class) inferInputTypeFromAny(fn, 0); - } - - @SuppressWarnings("unchecked") - public static Class inferInputType(SerializableFunction fn) { - return (Class) inferInputTypeFromAny(fn, 0); - } - - @SuppressWarnings("unchecked") - public static Class inferInputType(SerializablePredicate fn) { - return (Class) inferInputTypeFromAny(fn, 0); - } - - @SuppressWarnings("unchecked") - public static Class inferInputType(InstanceIdFunction fn) { - return (Class) inferInputTypeFromAny(fn, 1); - } - - @SuppressWarnings("unchecked") - public static Class inferInputType(UniqueIdBiFunction fn) { - return (Class) inferInputTypeFromAny(fn, 1); - } - - @SuppressWarnings("unchecked") - public static Class inferInputType(SerializableConsumer fn) { - return (Class) inferInputTypeFromAny(fn, 0); - } - - @SuppressWarnings("unchecked") - public static Class inferResultType(Object fn) { - return (Class) inferMethodType(fn).returnType(); - } - - /** - * Extracts the input type using the resolved interface signature. * @param fn The serializable - * lambda - * - * @param lambdaParamIndex The index of the payload parameter in the interface's apply method - */ - public static Class inferInputTypeFromAny(Object fn, int lambdaParamIndex) { - return inferMethodType(fn).parameterArray()[lambdaParamIndex]; - } - - private static MethodType inferMethodType(Object fn) { - try { - Method m = fn.getClass().getDeclaredMethod("writeReplace"); - m.setAccessible(true); - SerializedLambda sl = (SerializedLambda) m.invoke(fn); - - ClassLoader cl = fn.getClass().getClassLoader(); - - // getInstantiatedMethodType() provides the exact generic signature resolved - // by the compiler, completely bypassing captured variables and method kind switches! - - return MethodType.fromMethodDescriptorString(sl.getInstantiatedMethodType(), cl); - } catch (ReflectiveOperationException ex) { - throw new IllegalStateException( - "Cannot infer type from lambda. Pass Class or use a method reference.", ex); - } - } -} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallJava.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallJava.java index e441ecfb8..8e6f88a55 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallJava.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallJava.java @@ -15,192 +15,115 @@ */ package io.serverlessworkflow.api.types.func; -import io.serverlessworkflow.api.types.TaskBase; +import io.serverlessworkflow.api.types.CallFunction; +import io.serverlessworkflow.api.types.FunctionArguments; +import io.serverlessworkflow.api.types.utils.ReflectionUtils; +import java.lang.invoke.MethodType; import java.util.Optional; import java.util.function.Consumer; import java.util.function.Function; -public abstract class CallJava extends TaskBase { +public abstract class CallJava { - private static final long serialVersionUID = 1L; + private CallJava() {} - private final Optional> inputClass; + public static final String JAVA_CALL_KEY = "Java"; + public static final String FUNCTION_NAME_KEY = "function"; + public static final String INPUT_CLASS_KEY = "inputClass"; + public static final String OUTPUT_CLASS_KEY = "outputClass"; + public static final String VAR_NAME_KEY = "varName"; + public static final String INDEX_NAME_KEY = "index"; - protected CallJava() { - this(Optional.empty()); + private static CallFunction buildFunction( + Object function, Optional> inputClass, Optional> outputClass) { + CallFunction result = new CallFunction(); + result.setCall(JAVA_CALL_KEY); + result.withWith( + new FunctionArguments() + .withAdditionalProperty(FUNCTION_NAME_KEY, function) + .withAdditionalProperty(INPUT_CLASS_KEY, inputClass) + .withAdditionalProperty(OUTPUT_CLASS_KEY, outputClass)); + return result; } - protected CallJava(Optional> inputClass) { - this.inputClass = inputClass; + public static CallFunction consumer(Consumer consumer) { + return buildFunction( + consumer, + ReflectionUtils.methodType(consumer).map(m -> m.parameterType(0)), + Optional.empty()); } - public Optional> inputClass() { - return inputClass; + public static CallFunction consumer(Consumer consumer, Class inputClass) { + return buildFunction(consumer, Optional.ofNullable(inputClass), Optional.empty()); } - public static CallJava consumer(Consumer consumer) { - return new CallJavaConsumer<>(consumer, Optional.empty()); + public static CallFunction function(Function function) { + return buildFunction(function, Optional.empty(), Optional.empty()); } - public static CallJava consumer(Consumer consumer, Class inputClass) { - return new CallJavaConsumer<>(consumer, Optional.ofNullable(inputClass)); + public static CallFunction function(Function function, Class inputClass) { + return buildFunction( + function, + Optional.ofNullable(inputClass), + ReflectionUtils.methodType(function).map(MethodType::returnType)); } - public static CallJavaFunction function(Function function) { - return new CallJavaFunction<>(function, Optional.empty(), Optional.empty()); - } - - public static CallJavaFunction function( - Function function, Class inputClass) { - return new CallJavaFunction<>(function, Optional.ofNullable(inputClass), Optional.empty()); - } - - public static CallJavaFunction function( + public static CallFunction function( Function function, Class inputClass, Class outputClass) { - return new CallJavaFunction<>( + return buildFunction( function, Optional.ofNullable(inputClass), Optional.ofNullable(outputClass)); } - public static CallJava loopFunction( + public static CallFunction loopFunction( LoopFunctionIndex function, String varName, String indexName) { - return new CallJavaLoopFunctionIndex<>(function, varName, indexName); - } - - public static CallJava loopFunction(LoopFunction function, String varName) { - return new CallJavaLoopFunction<>(function, varName); - } - - public static CallJava function(ContextFunction function, Class inputClass) { - return new CallJavaContextFunction<>( - function, Optional.ofNullable(inputClass), Optional.empty()); - } - - public static CallJava function( + Optional methodType = ReflectionUtils.methodType(function); + CallFunction result = + buildFunction( + function, + methodType.map(m -> m.parameterType(0)), + methodType.map(MethodType::returnType)); + result + .getWith() + .withAdditionalProperty(VAR_NAME_KEY, varName) + .withAdditionalProperty(INDEX_NAME_KEY, indexName); + return result; + } + + public static CallFunction loopFunction( + LoopFunction function, String varName) { + Optional methodType = ReflectionUtils.methodType(function); + CallFunction result = + buildFunction( + function, + methodType.map(m -> m.parameterType(0)), + methodType.map(MethodType::returnType)); + result.getWith().withAdditionalProperty(VAR_NAME_KEY, varName); + return result; + } + + public static CallFunction function(ContextFunction function, Class inputClass) { + return buildFunction( + function, + Optional.ofNullable(inputClass), + ReflectionUtils.methodType(function).map(MethodType::returnType)); + } + + public static CallFunction function( ContextFunction function, Class inputClass, Class outputClass) { - return new CallJavaContextFunction<>( + return buildFunction( function, Optional.ofNullable(inputClass), Optional.ofNullable(outputClass)); } - public static CallJava function(FilterFunction function, Class inputClass) { - return new CallJavaFilterFunction<>( - function, Optional.ofNullable(inputClass), Optional.empty()); + public static CallFunction function(FilterFunction function, Class inputClass) { + return buildFunction( + function, + Optional.ofNullable(inputClass), + ReflectionUtils.methodType(function).map(MethodType::returnType)); } - public static CallJava function( + public static CallFunction function( FilterFunction function, Class inputClass, Class outputClass) { - return new CallJavaFilterFunction<>( + return buildFunction( function, Optional.ofNullable(inputClass), Optional.ofNullable(outputClass)); } - - public static class CallJavaConsumer extends CallJava { - private static final long serialVersionUID = 1L; - private final Consumer consumer; - - public CallJavaConsumer(Consumer consumer, Optional> inputClass) { - super(inputClass); - this.consumer = consumer; - } - - public Consumer consumer() { - return consumer; - } - } - - public static class CallJavaFunction extends CallAbstractJavaFunction { - - private static final long serialVersionUID = 1L; - private final Function function; - - public CallJavaFunction( - Function function, Optional> inputClass, Optional> outputClass) { - super(inputClass, outputClass); - this.function = function; - } - - public Function function() { - return function; - } - } - - public static class CallJavaContextFunction extends CallAbstractJavaFunction { - private static final long serialVersionUID = 1L; - private final ContextFunction function; - - public CallJavaContextFunction( - ContextFunction function, - Optional> inputClass, - Optional> outputClass) { - super(inputClass, outputClass); - this.function = function; - } - - public ContextFunction function() { - return function; - } - } - - public static class CallJavaFilterFunction extends CallAbstractJavaFunction { - private static final long serialVersionUID = 1L; - private final FilterFunction function; - - public CallJavaFilterFunction( - FilterFunction function, - Optional> inputClass, - Optional> outputClass) { - super(inputClass, outputClass); - this.function = function; - } - - public FilterFunction function() { - return function; - } - } - - public static class CallJavaLoopFunction extends CallAbstractJavaFunction { - - private static final long serialVersionUID = 1L; - private LoopFunction function; - private String varName; - - public CallJavaLoopFunction(LoopFunction function, String varName) { - - this.function = function; - this.varName = varName; - } - - public LoopFunction function() { - return function; - } - - public String varName() { - return varName; - } - } - - public static class CallJavaLoopFunctionIndex extends CallAbstractJavaFunction { - - private static final long serialVersionUID = 1L; - private final LoopFunctionIndex function; - private final String varName; - private final String indexName; - - public CallJavaLoopFunctionIndex( - LoopFunctionIndex function, String varName, String indexName) { - this.function = function; - this.varName = varName; - this.indexName = indexName; - } - - public LoopFunctionIndex function() { - return function; - } - - public String varName() { - return varName; - } - - public String indexName() { - return indexName; - } - } } diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ContextFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ContextFunction.java index 197764a70..c2f87ebda 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ContextFunction.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ContextFunction.java @@ -16,9 +16,8 @@ package io.serverlessworkflow.api.types.func; import io.serverlessworkflow.impl.WorkflowContextData; -import java.io.Serializable; @FunctionalInterface -public interface ContextFunction extends Serializable { +public interface ContextFunction extends FunctionObject { R apply(T object, WorkflowContextData workflowContext); } diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ContextPredicate.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ContextPredicate.java index f1072c135..0dd0c7dc9 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ContextPredicate.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ContextPredicate.java @@ -18,6 +18,6 @@ import io.serverlessworkflow.impl.WorkflowContextData; @FunctionalInterface -public interface ContextPredicate { +public interface ContextPredicate extends FunctionObject { boolean test(T value, WorkflowContextData context); } 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 deleted file mode 100644 index b18f8d886..000000000 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ExportAsFunction.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.api.types.ExportAs; -import java.util.Objects; -import java.util.function.Function; - -public class ExportAsFunction extends ExportAs { - - public ExportAs withFunction(Function value) { - setObject(value); - return this; - } - - public ExportAs withFunction(Function value, Class argClass) { - Objects.requireNonNull(argClass); - setObject(new TypedFunction<>(value, argClass)); - return this; - } - - public ExportAs withFunction(FilterFunction value) { - setObject(value); - return this; - } - - public ExportAs withFunction(FilterFunction value, Class argClass) { - setObject(new TypedFilterFunction<>(value, argClass)); - return this; - } - - public ExportAs withFunction(ContextFunction value) { - setObject(value); - return this; - } - - public ExportAs withFunction(ContextFunction value, Class argClass) { - setObject(new TypedContextFunction<>(value, argClass)); - return this; - } -} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/FilterFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/FilterFunction.java index ccf138ca7..916ae9eed 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/FilterFunction.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/FilterFunction.java @@ -17,9 +17,8 @@ import io.serverlessworkflow.impl.TaskContextData; import io.serverlessworkflow.impl.WorkflowContextData; -import java.io.Serializable; @FunctionalInterface -public interface FilterFunction extends Serializable { +public interface FilterFunction extends FunctionObject { R apply(T object, WorkflowContextData workflowContext, TaskContextData taskContext); } diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/FilterPredicate.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/FilterPredicate.java index d81b3ac48..e09b98b06 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/FilterPredicate.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/FilterPredicate.java @@ -19,6 +19,6 @@ import io.serverlessworkflow.impl.WorkflowContextData; @FunctionalInterface -public interface FilterPredicate { +public interface FilterPredicate extends FunctionObject { boolean test(T value, WorkflowContextData workflow, TaskContextData task); } diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TaskMetadataKeys.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/FilterSerializable.java similarity index 79% rename from experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TaskMetadataKeys.java rename to experimental/types/src/main/java/io/serverlessworkflow/api/types/func/FilterSerializable.java index c0ea43cdf..cdec9f30a 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TaskMetadataKeys.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/FilterSerializable.java @@ -15,8 +15,5 @@ */ package io.serverlessworkflow.api.types.func; -public final class TaskMetadataKeys { - - /** Metadata entry name for the DSL’s “when”/“if” predicate. */ - public static final String IF_PREDICATE = "if_predicate"; -} +/** Marker interfaces for types that might be serialized with ExportAs, OutputAs and InputFrom */ +public interface FilterSerializable {} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ForTaskFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ForTaskFunction.java deleted file mode 100644 index d6932f2e3..000000000 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ForTaskFunction.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * 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.api.types.ForTask; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.util.Collection; -import java.util.Optional; -import java.util.function.Function; - -public class ForTaskFunction extends ForTask { - - private static final long serialVersionUID = 1L; - private LoopPredicateIndexFilter whilePredicate; - private Optional> whileClass = Optional.empty(); - private Optional> itemClass = Optional.empty(); - private Optional> forClass = Optional.empty(); - private Function collection; - - public ForTaskFunction withWhile(LoopPredicate whilePredicate) { - return withWhile(toPredicate(whilePredicate)); - } - - public ForTaskFunction withWhile(LoopPredicate whilePredicate, Class modelClass) { - return withWhile(toPredicate(whilePredicate), modelClass); - } - - public ForTaskFunction withWhile( - LoopPredicate whilePredicate, Class modelClass, Class itemClass) { - return withWhile(toPredicate(whilePredicate), modelClass, itemClass); - } - - public ForTaskFunction withWhile(LoopPredicateIndex whilePredicate) { - return withWhile(toPredicate(whilePredicate), Optional.empty(), Optional.empty()); - } - - public ForTaskFunction withWhile( - LoopPredicateIndex whilePredicate, Class modelClass) { - return withWhile( - toPredicate(whilePredicate), Optional.ofNullable(modelClass), Optional.empty()); - } - - public ForTaskFunction withWhile( - LoopPredicateIndex whilePredicate, Class modelClass, Class itemClass) { - return withWhile( - toPredicate(whilePredicate), - Optional.ofNullable(modelClass), - Optional.ofNullable(itemClass)); - } - - public ForTaskFunction withWhile(LoopPredicateIndexContext whilePredicate) { - return withWhile(toPredicate(whilePredicate), Optional.empty(), Optional.empty()); - } - - public ForTaskFunction withWhile( - LoopPredicateIndexContext whilePredicate, Class modelClass) { - return withWhile( - toPredicate(whilePredicate), Optional.ofNullable(modelClass), Optional.empty()); - } - - public ForTaskFunction withWhile( - LoopPredicateIndexContext whilePredicate, Class modelClass, Class itemClass) { - return withWhile( - toPredicate(whilePredicate), - Optional.ofNullable(modelClass), - Optional.ofNullable(itemClass)); - } - - public ForTaskFunction withWhile(LoopPredicateIndexFilter whilePredicate) { - return withWhile(whilePredicate, Optional.empty(), Optional.empty()); - } - - public ForTaskFunction withWhile( - LoopPredicateIndexFilter whilePredicate, Class modelClass) { - return withWhile(whilePredicate, Optional.ofNullable(modelClass), Optional.empty()); - } - - public ForTaskFunction withWhile( - LoopPredicateIndexFilter whilePredicate, Class modelClass, Class itemClass) { - return withWhile( - whilePredicate, Optional.ofNullable(modelClass), Optional.ofNullable(itemClass)); - } - - private LoopPredicateIndexFilter toPredicate(LoopPredicate whilePredicate) { - return (model, item, index, w, t) -> whilePredicate.test(model, item); - } - - private LoopPredicateIndexFilter toPredicate( - LoopPredicateIndex whilePredicate) { - return (model, item, index, w, t) -> whilePredicate.test(model, item, index); - } - - private LoopPredicateIndexFilter toPredicate( - LoopPredicateIndexContext whilePredicate) { - return (model, item, index, w, t) -> whilePredicate.test(model, item, index, w); - } - - private ForTaskFunction withWhile( - LoopPredicateIndexFilter whilePredicate, - Optional> modelClass, - Optional> itemClass) { - this.whilePredicate = whilePredicate; - this.whileClass = modelClass; - this.itemClass = itemClass; - return this; - } - - public ForTaskFunction withCollection(Function> collection) { - return withCollection(collection, null); - } - - public ForTaskFunction withCollection( - Function> collection, Class colArgClass) { - this.collection = collection; - this.forClass = Optional.ofNullable(colArgClass); - return this; - } - - public LoopPredicateIndexFilter getWhilePredicate() { - return whilePredicate; - } - - public Optional> getWhileClass() { - return whileClass; - } - - public Optional> getForClass() { - return forClass; - } - - public Optional> getItemClass() { - return itemClass; - } - - public Function> getCollection() { - return collection; - } - - private void normalizeOptionalFields() { - if (whileClass == null) { - whileClass = Optional.empty(); - } - if (itemClass == null) { - itemClass = Optional.empty(); - } - if (forClass == null) { - forClass = Optional.empty(); - } - } - - private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException { - input.defaultReadObject(); - // Preserve compatibility with older serialized instances that may have null optionals. - normalizeOptionalFields(); - } -} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallTaskJava.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/FunctionObject.java similarity index 65% rename from experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallTaskJava.java rename to experimental/types/src/main/java/io/serverlessworkflow/api/types/func/FunctionObject.java index cde6281f2..f6647a2f8 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallTaskJava.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/FunctionObject.java @@ -15,22 +15,10 @@ */ package io.serverlessworkflow.api.types.func; -import io.serverlessworkflow.api.types.CallTask; +import java.io.Serializable; -public class CallTaskJava extends CallTask { - - private CallJava callJava; - - public CallTaskJava(CallJava callJava) { - this.callJava = callJava; - } - - public CallJava getCallJava() { - return callJava; - } - - @Override - public Object get() { - return callJava != null ? callJava : super.get(); - } -} +/** + * Marked interface for objects that represent a function, predicate or consumer invoked by the + * runtime + */ +public interface FunctionObject extends Serializable, FilterSerializable {} 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 deleted file mode 100644 index bfa5bf12f..000000000 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/InputFromFunction.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.api.types.InputFrom; -import java.util.Objects; -import java.util.function.Function; - -public class InputFromFunction extends InputFrom { - - public InputFrom withFunction(Function value) { - setObject(value); - return this; - } - - public InputFrom withFunction(Function value, Class argClass) { - Objects.requireNonNull(argClass); - setObject(new TypedFunction<>(value, argClass)); - return this; - } - - public InputFrom withFunction(FilterFunction value) { - setObject(value); - return this; - } - - public InputFrom withFunction(FilterFunction value, Class argClass) { - setObject(new TypedFilterFunction<>(value, argClass)); - return this; - } - - public InputFrom withFunction(ContextFunction value) { - setObject(value); - return this; - } - - public InputFrom withFunction(ContextFunction value, Class argClass) { - setObject(new TypedContextFunction<>(value, argClass)); - return this; - } -} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/InstanceIdFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/InstanceIdFunction.java similarity index 85% rename from experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/InstanceIdFunction.java rename to experimental/types/src/main/java/io/serverlessworkflow/api/types/func/InstanceIdFunction.java index 083456d4f..9af0d1d12 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/InstanceIdFunction.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/InstanceIdFunction.java @@ -13,9 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.api.reflection.func; - -import java.io.Serializable; +package io.serverlessworkflow.api.types.func; /** * Functions that expect a workflow instance ID injection in runtime @@ -24,6 +22,6 @@ * @param The task result output */ @FunctionalInterface -public interface InstanceIdFunction extends Serializable { +public interface InstanceIdFunction extends FunctionObject { R apply(String instanceId, T payload); } diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopFunction.java index 094cff1c1..a7cac96be 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopFunction.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopFunction.java @@ -18,4 +18,4 @@ import java.util.function.BiFunction; @FunctionalInterface -public interface LoopFunction extends BiFunction {} +public interface LoopFunction extends FunctionObject, BiFunction {} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopFunctionIndex.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopFunctionIndex.java index b5831d098..38ec773cb 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopFunctionIndex.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopFunctionIndex.java @@ -16,6 +16,6 @@ package io.serverlessworkflow.api.types.func; @FunctionalInterface -public interface LoopFunctionIndex { +public interface LoopFunctionIndex extends FunctionObject { R apply(T model, V item, Integer index); } diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopPredicate.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopPredicate.java index 38b6b3041..0007ce1b2 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopPredicate.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopPredicate.java @@ -18,4 +18,4 @@ import java.util.function.BiPredicate; @FunctionalInterface -public interface LoopPredicate extends BiPredicate {} +public interface LoopPredicate extends FunctionObject, BiPredicate {} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopPredicateIndex.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopPredicateIndex.java index 0e0a39a0d..d4904721a 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopPredicateIndex.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopPredicateIndex.java @@ -16,6 +16,6 @@ package io.serverlessworkflow.api.types.func; @FunctionalInterface -public interface LoopPredicateIndex { +public interface LoopPredicateIndex extends FunctionObject { boolean test(T model, V item, Integer index); } diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopPredicateIndexContext.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopPredicateIndexContext.java index 470bfbca5..6c17fe98c 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopPredicateIndexContext.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopPredicateIndexContext.java @@ -18,6 +18,6 @@ import io.serverlessworkflow.impl.WorkflowContextData; @FunctionalInterface -public interface LoopPredicateIndexContext { +public interface LoopPredicateIndexContext extends FunctionObject { boolean test(T model, V item, Integer index, WorkflowContextData context); } diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopPredicateIndexFilter.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopPredicateIndexFilter.java index 808719c50..b25b00537 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopPredicateIndexFilter.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/LoopPredicateIndexFilter.java @@ -19,6 +19,6 @@ import io.serverlessworkflow.impl.WorkflowContextData; @FunctionalInterface -public interface LoopPredicateIndexFilter { +public interface LoopPredicateIndexFilter extends FunctionObject { boolean test(T model, V item, Integer index, WorkflowContextData workflow, TaskContextData task); } 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 deleted file mode 100644 index 19d61335b..000000000 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/OutputAsFunction.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.api.types.OutputAs; -import java.util.Objects; -import java.util.function.Function; - -public class OutputAsFunction extends OutputAs { - - public OutputAs withFunction(Function value) { - setObject(value); - return this; - } - - public OutputAs withFunction(Function value, Class argClass) { - Objects.requireNonNull(argClass); - setObject(new TypedFunction<>(value, argClass)); - return this; - } - - public OutputAs withFunction(FilterFunction value) { - setObject(value); - return this; - } - - public OutputAs withFunction(FilterFunction value, Class argClass) { - setObject(new TypedFilterFunction<>(value, argClass)); - return this; - } - - public OutputAs withFunction(ContextFunction value) { - setObject(value); - return this; - } - - public OutputAs withFunction(ContextFunction value, Class argClass) { - setObject(new TypedContextFunction<>(value, argClass)); - return this; - } -} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/SerializableConsumer.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/SerializableConsumer.java similarity index 81% rename from experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/SerializableConsumer.java rename to experimental/types/src/main/java/io/serverlessworkflow/api/types/func/SerializableConsumer.java index 60df61d0c..430cdaf75 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/SerializableConsumer.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/SerializableConsumer.java @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.api.reflection.func; +package io.serverlessworkflow.api.types.func; -import java.io.Serializable; import java.util.function.Consumer; @FunctionalInterface -public interface SerializableConsumer extends Consumer, Serializable {} +public interface SerializableConsumer extends Consumer, FunctionObject {} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/SerializableFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/SerializableFunction.java similarity index 90% rename from experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/SerializableFunction.java rename to experimental/types/src/main/java/io/serverlessworkflow/api/types/func/SerializableFunction.java index c4b2eee08..9daf11216 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/SerializableFunction.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/SerializableFunction.java @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.api.reflection.func; +package io.serverlessworkflow.api.types.func; -import java.io.Serializable; import java.util.function.Function; /** @@ -26,4 +25,4 @@ * @param */ @FunctionalInterface -public interface SerializableFunction extends Function, Serializable {} +public interface SerializableFunction extends Function, FunctionObject {} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/SerializablePredicate.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/SerializablePredicate.java similarity index 88% rename from experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/SerializablePredicate.java rename to experimental/types/src/main/java/io/serverlessworkflow/api/types/func/SerializablePredicate.java index a960b8db8..82f2159b9 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/SerializablePredicate.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/SerializablePredicate.java @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.api.reflection.func; +package io.serverlessworkflow.api.types.func; -import java.io.Serializable; import java.util.function.Predicate; @FunctionalInterface -public interface SerializablePredicate extends Predicate, Serializable {} +public interface SerializablePredicate extends Predicate, FunctionObject {} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/SwitchCasePredicate.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/SwitchCasePredicate.java deleted file mode 100644 index 4b67be8ca..000000000 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/SwitchCasePredicate.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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.api.types.SwitchCase; -import java.util.function.Predicate; - -public class SwitchCasePredicate extends SwitchCase implements PredicateContainer { - - private static final long serialVersionUID = 1L; - private Object predicate; - - public SwitchCasePredicate withPredicate(Predicate predicate) { - this.predicate = predicate; - return this; - } - - public SwitchCasePredicate withPredicate(Predicate predicate, Class predicateClass) { - this.predicate = - predicateClass == null ? predicate : new TypedPredicate<>(predicate, predicateClass); - return this; - } - - public SwitchCasePredicate withPredicate(ContextPredicate predicate) { - this.predicate = predicate; - return this; - } - - public SwitchCasePredicate withPredicate( - ContextPredicate predicate, Class predicateClass) { - this.predicate = - predicateClass == null ? predicate : new TypedContextPredicate<>(predicate, predicateClass); - return this; - } - - public SwitchCasePredicate withPredicate(FilterPredicate predicate) { - this.predicate = predicate; - return this; - } - - public SwitchCasePredicate withPredicate( - FilterPredicate predicate, Class predicateClass) { - this.predicate = - predicateClass == null ? predicate : new TypedFilterPredicate<>(predicate, predicateClass); - return this; - } - - public Object predicate() { - return predicate; - } -} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedContextFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedContextFunction.java index 6a83f65f9..fd8d10bd3 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedContextFunction.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedContextFunction.java @@ -15,4 +15,5 @@ */ package io.serverlessworkflow.api.types.func; -public record TypedContextFunction(ContextFunction function, Class argClass) {} +public record TypedContextFunction(ContextFunction function, Class argClass) + implements FilterSerializable {} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedContextPredicate.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedContextPredicate.java index e6a522336..4b9142588 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedContextPredicate.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedContextPredicate.java @@ -15,4 +15,5 @@ */ package io.serverlessworkflow.api.types.func; -public record TypedContextPredicate(ContextPredicate predicate, Class argClass) {} +public record TypedContextPredicate(ContextPredicate predicate, Class argClass) + implements FilterSerializable {} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedFilterFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedFilterFunction.java index 2589af952..71ee2d2c4 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedFilterFunction.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedFilterFunction.java @@ -15,4 +15,5 @@ */ package io.serverlessworkflow.api.types.func; -public record TypedFilterFunction(FilterFunction function, Class argClass) {} +public record TypedFilterFunction(FilterFunction function, Class argClass) + implements FilterSerializable {} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedFilterPredicate.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedFilterPredicate.java index aaef7f98f..462952dc7 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedFilterPredicate.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedFilterPredicate.java @@ -15,4 +15,5 @@ */ package io.serverlessworkflow.api.types.func; -public record TypedFilterPredicate(FilterPredicate predicate, Class argClass) {} +public record TypedFilterPredicate(FilterPredicate predicate, Class argClass) + implements FilterSerializable {} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedFunction.java index c38bbb92f..a17267c16 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedFunction.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedFunction.java @@ -17,4 +17,5 @@ import java.util.function.Function; -public record TypedFunction(Function function, Class argClass) {} +public record TypedFunction(Function function, Class argClass) + implements FilterSerializable {} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedPredicate.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedPredicate.java index 26c0893ed..762e9d3d7 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedPredicate.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/TypedPredicate.java @@ -17,4 +17,5 @@ import java.util.function.Predicate; -public record TypedPredicate(Predicate pred, Class argClass) {} +public record TypedPredicate(Predicate pred, Class argClass) + implements FilterSerializable {} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/UniqueIdBiFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/UniqueIdBiFunction.java similarity index 90% rename from experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/UniqueIdBiFunction.java rename to experimental/types/src/main/java/io/serverlessworkflow/api/types/func/UniqueIdBiFunction.java index 4340cc3f8..94c1347ce 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/UniqueIdBiFunction.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/UniqueIdBiFunction.java @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.api.reflection.func; +package io.serverlessworkflow.api.types.func; -import java.io.Serializable; import java.util.function.BiFunction; /** @@ -26,6 +25,6 @@ * @param The task result output */ @FunctionalInterface -public interface UniqueIdBiFunction extends BiFunction, Serializable { +public interface UniqueIdBiFunction extends BiFunction, FunctionObject { R apply(String uniqueId, T object); } diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/UntilPredicate.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/UntilPredicate.java deleted file mode 100644 index e09c471b9..000000000 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/UntilPredicate.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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.api.types.Until; -import java.util.function.Predicate; - -public class UntilPredicate extends Until implements PredicateContainer { - - private Object predicate; - - public UntilPredicate withPredicate(Predicate predicate) { - this.predicate = predicate; - return this; - } - - public UntilPredicate withPredicate(Predicate predicate, Class predicateClass) { - this.predicate = - predicateClass == null ? predicate : new TypedPredicate<>(predicate, predicateClass); - return this; - } - - public UntilPredicate withPredicate(ContextPredicate predicate) { - this.predicate = predicate; - return this; - } - - public UntilPredicate withPredicate(ContextPredicate predicate, Class predicateClass) { - this.predicate = - predicateClass == null ? predicate : new TypedContextPredicate<>(predicate, predicateClass); - return this; - } - - public UntilPredicate withPredicate(FilterPredicate predicate) { - this.predicate = predicate; - return this; - } - - public UntilPredicate withPredicate(FilterPredicate predicate, Class predicateClass) { - this.predicate = - predicateClass == null ? predicate : new TypedFilterPredicate<>(predicate, predicateClass); - return this; - } - - public Object predicate() { - return predicate; - } -} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/utils/ForTaskFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/utils/ForTaskFunction.java new file mode 100644 index 000000000..4c185f655 --- /dev/null +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/utils/ForTaskFunction.java @@ -0,0 +1,196 @@ +/* + * 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.utils; + +import io.serverlessworkflow.api.types.ForTask; +import io.serverlessworkflow.api.types.ForTaskConfiguration; +import io.serverlessworkflow.api.types.TaskMetadata; +import io.serverlessworkflow.api.types.func.LoopPredicate; +import io.serverlessworkflow.api.types.func.LoopPredicateIndex; +import io.serverlessworkflow.api.types.func.LoopPredicateIndexContext; +import io.serverlessworkflow.api.types.func.LoopPredicateIndexFilter; +import java.lang.invoke.MethodType; +import java.util.Collection; +import java.util.Optional; +import java.util.function.Function; + +public class ForTaskFunction { + + private ForTaskFunction() {} + + public static final String WHILE_PREDICATE = "whilePredicate"; + public static final String WHILE_CLASS = "whileClass"; + public static final String ITEM_CLASS = "itemClass"; + public static final String FOR_CLASS = "forClass"; + public static final String COLLECTION = "inCollection"; + + public static ForTask withWhile(ForTask task, LoopPredicate whilePredicate) { + return withWhile(task, whilePredicate, Optional.empty(), Optional.empty()); + } + + public static ForTask withWhile( + ForTask task, LoopPredicate whilePredicate, Class modelClass) { + return withWhile(task, whilePredicate, Optional.ofNullable(modelClass), Optional.empty()); + } + + public static ForTask withWhile( + ForTask task, LoopPredicate whilePredicate, Class modelClass, Class itemClass) { + return withWhile( + task, whilePredicate, Optional.ofNullable(modelClass), Optional.ofNullable(itemClass)); + } + + public static ForTask withWhile(ForTask task, LoopPredicateIndex whilePredicate) { + return withWhile(task, whilePredicate, Optional.empty(), Optional.empty()); + } + + public static ForTask withWhile( + ForTask task, LoopPredicateIndex whilePredicate, Class modelClass) { + return withWhile(task, whilePredicate, Optional.ofNullable(modelClass), Optional.empty()); + } + + public static ForTask withWhile( + ForTask task, + LoopPredicateIndex whilePredicate, + Class modelClass, + Class itemClass) { + return withWhile( + task, whilePredicate, Optional.ofNullable(modelClass), Optional.ofNullable(itemClass)); + } + + public static ForTask withWhile( + ForTask task, LoopPredicateIndexContext whilePredicate) { + Optional methodType = ReflectionUtils.methodType(whilePredicate); + return withWhile( + task, + whilePredicate, + methodType.map(m -> m.parameterType(0)), + methodType.map(m -> m.parameterType(1))); + } + + public static ForTask withWhile( + ForTask task, LoopPredicateIndexContext whilePredicate, Class modelClass) { + return withWhile( + task, + whilePredicate, + Optional.ofNullable(modelClass), + ReflectionUtils.methodType(whilePredicate).map(m -> m.parameterType(1))); + } + + public static ForTask withWhile( + ForTask task, + LoopPredicateIndexContext whilePredicate, + Class modelClass, + Class itemClass) { + return withWhile( + task, whilePredicate, Optional.ofNullable(modelClass), Optional.ofNullable(itemClass)); + } + + public static ForTask withWhile( + ForTask task, LoopPredicateIndexFilter whilePredicate) { + Optional methodType = ReflectionUtils.methodType(whilePredicate); + return withWhile( + task, + whilePredicate, + methodType.map(m -> m.parameterType(0)), + methodType.map(m -> m.parameterType(1))); + } + + public static ForTask withWhile( + ForTask task, LoopPredicateIndexFilter whilePredicate, Class modelClass) { + return withWhile( + task, + whilePredicate, + Optional.ofNullable(modelClass), + ReflectionUtils.methodType(whilePredicate).map(m -> m.parameterType(1))); + } + + public static ForTask withWhile( + ForTask task, + LoopPredicateIndexFilter whilePredicate, + Class modelClass, + Class itemClass) { + return withWhile( + task, whilePredicate, Optional.ofNullable(modelClass), Optional.ofNullable(itemClass)); + } + + private static ForTask withWhile( + ForTask forTask, + Object whilePredicate, + Optional> modelClass, + Optional> itemClass) { + TypesUtils.initMetadata(forTask) + .withAdditionalProperty(WHILE_PREDICATE, whilePredicate) + .withAdditionalProperty(WHILE_CLASS, modelClass) + .withAdditionalProperty(ITEM_CLASS, itemClass); + return forTask; + } + + public static ForTask withCollection( + ForTask forTask, Function> collection) { + return withCollection(forTask, collection, null); + } + + public static ForTask withCollection( + ForTask forTask, Function> collection, Class colArgClass) { + TaskMetadata metadata = TypesUtils.initMetadata(forTask); + metadata.withAdditionalProperty(COLLECTION, collection); + metadata.withAdditionalProperty(FOR_CLASS, Optional.ofNullable(colArgClass)); + ForTaskConfiguration forConfig = forTask.getFor(); + if (forConfig == null) { + forConfig = new ForTaskConfiguration(); + forTask.setFor(forConfig); + } + if (forConfig.getIn() == null) { + forConfig.setIn("Handling item collection with metadata key " + ForTaskFunction.COLLECTION); + } + return forTask; + } + + public static Object getWhilePredicate(ForTask task) { + return task.getMetadata() == null + ? null + : task.getMetadata().getAdditionalProperties().get(WHILE_PREDICATE); + } + + public static Optional> getWhileClass(ForTask task) { + return task.getMetadata() == null + ? Optional.empty() + : (Optional>) + task.getMetadata() + .getAdditionalProperties() + .getOrDefault(WHILE_CLASS, Optional.empty()); + } + + public static Optional> getForClass(ForTask task) { + return task.getMetadata() == null + ? Optional.empty() + : (Optional>) + task.getMetadata().getAdditionalProperties().getOrDefault(FOR_CLASS, Optional.empty()); + } + + public static Optional> getItemClass(ForTask task) { + return task.getMetadata() == null + ? Optional.empty() + : (Optional>) + task.getMetadata().getAdditionalProperties().getOrDefault(ITEM_CLASS, Optional.empty()); + } + + public static Function> getInCollection(ForTask task) { + return task.getMetadata() == null + ? null + : (Function>) task.getMetadata().getAdditionalProperties().get(COLLECTION); + } +} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/MapSetTaskConfiguration.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/utils/MapSetTaskConfiguration.java similarity index 63% rename from experimental/types/src/main/java/io/serverlessworkflow/api/types/func/MapSetTaskConfiguration.java rename to experimental/types/src/main/java/io/serverlessworkflow/api/types/utils/MapSetTaskConfiguration.java index a68fb7805..4baa13286 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/MapSetTaskConfiguration.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/utils/MapSetTaskConfiguration.java @@ -13,20 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.api.types.func; +package io.serverlessworkflow.api.types.utils; import io.serverlessworkflow.api.types.SetTaskConfiguration; import java.util.Map; -public class MapSetTaskConfiguration extends SetTaskConfiguration { +public class MapSetTaskConfiguration { - private static final long serialVersionUID = 1L; + private MapSetTaskConfiguration() {} - public MapSetTaskConfiguration(Map map) { - map.forEach(this::processItem); - } - - private void processItem(String key, Object value) { - withAdditionalProperty(key, value); + public static SetTaskConfiguration map(Map map) { + SetTaskConfiguration config = new SetTaskConfiguration(); + for (Map.Entry entry : map.entrySet()) { + config.withAdditionalProperty(entry.getKey(), entry.getValue()); + } + return config; } } diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/utils/ReflectionUtils.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/utils/ReflectionUtils.java new file mode 100644 index 000000000..536f9b3bb --- /dev/null +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/utils/ReflectionUtils.java @@ -0,0 +1,157 @@ +/* + * 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.utils; + +import io.serverlessworkflow.api.types.func.ContextFunction; +import io.serverlessworkflow.api.types.func.FilterFunction; +import io.serverlessworkflow.api.types.func.InstanceIdFunction; +import io.serverlessworkflow.api.types.func.SerializableConsumer; +import io.serverlessworkflow.api.types.func.SerializableFunction; +import io.serverlessworkflow.api.types.func.SerializablePredicate; +import io.serverlessworkflow.api.types.func.UniqueIdBiFunction; +import java.lang.invoke.MethodType; +import java.lang.invoke.SerializedLambda; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Optional; +import java.util.function.Function; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Specially used by {@link Function} parameters in the Java Function. + * + * @see Serialize a Lambda in Java + */ +public final class ReflectionUtils { + + private static final Logger logger = LoggerFactory.getLogger(ReflectionUtils.class); + + private ReflectionUtils() {} + + @SuppressWarnings("unchecked") + public static Class inferInputType(ContextFunction fn) { + return (Class) inferInputTypeFromAny(fn, 0); + } + + @SuppressWarnings("unchecked") + public static Class inferInputType(FilterFunction fn) { + return (Class) inferInputTypeFromAny(fn, 0); + } + + @SuppressWarnings("unchecked") + public static Class inferInputType(SerializableFunction fn) { + return (Class) inferInputTypeFromAny(fn, 0); + } + + @SuppressWarnings("unchecked") + public static Class inferInputType(SerializablePredicate fn) { + return (Class) inferInputTypeFromAny(fn, 0); + } + + @SuppressWarnings("unchecked") + public static Class inferInputType(InstanceIdFunction fn) { + return (Class) inferInputTypeFromAny(fn, 1); + } + + @SuppressWarnings("unchecked") + public static Class inferInputType(UniqueIdBiFunction fn) { + return (Class) inferInputTypeFromAny(fn, 1); + } + + @SuppressWarnings("unchecked") + public static Class inferInputType(SerializableConsumer fn) { + return (Class) inferInputTypeFromAny(fn, 0); + } + + @SuppressWarnings("unchecked") + public static Class inferResultType(Object fn) { + return (Class) inferOutputType(inferMethodType(fn)); + } + + public static Class inferInputTypeFromAny(Object fn, int lambdaParamIndex) { + return inferInputType(inferMethodType(fn), lambdaParamIndex); + } + + public static Optional serializedFromFunction(Object fn) { + try { + return Optional.of(serializedLambda(fn)); + } catch (ReflectiveOperationException ex) { + logger.debug("Error resolving serialized lambda for {}", fn, ex); + return Optional.empty(); + } + } + + public static Class loadCapturingClass(String capturingClass) throws ClassNotFoundException { + return loadClass(capturingClass.replace('/', '.')); + } + + public static Class loadClass(String className) throws ClassNotFoundException { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if (cl == null) { + cl = ReflectionUtils.class.getClassLoader(); + if (cl == null) { + cl = ClassLoader.getSystemClassLoader(); + } + } + return cl.loadClass(className); + } + + public static Object functionFromSerialized(SerializedLambda sl) + throws ReflectiveOperationException { + Method deserializeMethod = + loadCapturingClass(sl.getCapturingClass()) + .getDeclaredMethod("$deserializeLambda$", SerializedLambda.class); + deserializeMethod.setAccessible(true); + return deserializeMethod.invoke(null, sl); + } + + public static Optional methodType(Object fn) { + return serializedFromFunction(fn).map(ReflectionUtils::inferMethodType); + } + + public static MethodType inferMethodType(SerializedLambda sl) { + // getInstantiatedMethodType() provides the exact generic signature resolved + // by the compiler, completely bypassing captured variables and method kind switches! + return MethodType.fromMethodDescriptorString( + sl.getInstantiatedMethodType(), sl.getClass().getClassLoader()); + } + + public static SerializedLambda serializedLambda(Object fn) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + Method m = fn.getClass().getDeclaredMethod("writeReplace"); + m.setAccessible(true); + return (SerializedLambda) m.invoke(fn); + } + + private static Class inferInputType(MethodType type, int index) { + return type.parameterType(index); + } + + private static Class inferOutputType(MethodType type) { + return type.returnType(); + } + + private static MethodType inferMethodType(Object fn) { + try { + SerializedLambda sl = serializedLambda(fn); + return inferMethodType(sl); + } catch (ReflectiveOperationException ex) { + throw new IllegalStateException( + "Cannot infer type from lambda. Pass Class or use a method reference.", ex); + } + } +} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/PredicateContainer.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/utils/TaskMetadataKeys.java similarity index 85% rename from experimental/types/src/main/java/io/serverlessworkflow/api/types/func/PredicateContainer.java rename to experimental/types/src/main/java/io/serverlessworkflow/api/types/utils/TaskMetadataKeys.java index 480d08cbd..b0a4c3a16 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/PredicateContainer.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/utils/TaskMetadataKeys.java @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.api.types.func; +package io.serverlessworkflow.api.types.utils; -public interface PredicateContainer { - Object predicate(); -} +public final class TaskMetadataKeys {} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/utils/TaskPredicate.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/utils/TaskPredicate.java new file mode 100644 index 000000000..0d3062c95 --- /dev/null +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/utils/TaskPredicate.java @@ -0,0 +1,90 @@ +/* + * 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.utils; + +import io.serverlessworkflow.api.types.TaskBase; +import io.serverlessworkflow.api.types.TaskMetadata; +import io.serverlessworkflow.api.types.func.ContextPredicate; +import io.serverlessworkflow.api.types.func.FilterPredicate; +import io.serverlessworkflow.api.types.func.TypedContextPredicate; +import io.serverlessworkflow.api.types.func.TypedFilterPredicate; +import io.serverlessworkflow.api.types.func.TypedPredicate; +import java.util.function.Predicate; + +public class TaskPredicate { + + private static final String PREDICATE_KEY_PREFIX = "predicate-"; + + private TaskPredicate() {} + + public static TaskMetadata withPredicate( + V task, String name, Predicate predicate) { + return TypesUtils.initMetadata(task).withAdditionalProperty(concat(name), predicate); + } + + public static V withPredicate( + V task, String name, Predicate predicate, Class predicateClass) { + TypesUtils.initMetadata(task) + .withAdditionalProperty( + concat(name), + predicateClass == null ? predicate : new TypedPredicate<>(predicate, predicateClass)); + return task; + } + + public static V withPredicate( + V task, String name, ContextPredicate predicate) { + TypesUtils.initMetadata(task).withAdditionalProperty(concat(name), predicate); + return task; + } + + public static V withPredicate( + V task, String name, ContextPredicate predicate, Class predicateClass) { + TypesUtils.initMetadata(task) + .withAdditionalProperty( + concat(name), + predicateClass == null + ? predicate + : new TypedContextPredicate<>(predicate, predicateClass)); + return task; + } + + public static V withPredicate( + V task, String name, FilterPredicate predicate) { + TypesUtils.initMetadata(task).withAdditionalProperty(concat(name), predicate); + return task; + } + + public static V withPredicate( + V task, String name, FilterPredicate predicate, Class predicateClass) { + TypesUtils.initMetadata(task) + .withAdditionalProperty( + concat(name), + predicateClass == null + ? predicate + : new TypedFilterPredicate<>(predicate, predicateClass)); + return task; + } + + public static Object predicate(TaskBase task, String name) { + return task.getMetadata() != null + ? task.getMetadata().getAdditionalProperties().get(concat(name)) + : null; + } + + private static String concat(String name) { + return PREDICATE_KEY_PREFIX + name; + } +} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/utils/TypesUtils.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/utils/TypesUtils.java new file mode 100644 index 000000000..825c21762 --- /dev/null +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/utils/TypesUtils.java @@ -0,0 +1,39 @@ +/* + * 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.utils; + +import io.serverlessworkflow.api.types.TaskBase; +import io.serverlessworkflow.api.types.TaskMetadata; + +public class TypesUtils { + + private TypesUtils() {} + + /** Metadata entry name for the DSL’s “when”/“if” predicate. */ + public static final String IF_PREDICATE = "if_predicate"; + + /** Metadata entry name for event until predicate */ + public static final String UNTIL_PRED_NAME = "until"; + + public static TaskMetadata initMetadata(TaskBase task) { + TaskMetadata metadata = task.getMetadata(); + if (metadata == null) { + metadata = new TaskMetadata(); + task.setMetadata(metadata); + } + return metadata; + } +} diff --git a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseWorkflowBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseWorkflowBuilder.java index 0d305d8a8..329e486e7 100644 --- a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseWorkflowBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseWorkflowBuilder.java @@ -15,6 +15,8 @@ */ package io.serverlessworkflow.fluent.spec; +import static io.serverlessworkflow.types.Defaults.DSL; + import io.serverlessworkflow.api.types.DoTimeout; import io.serverlessworkflow.api.types.Document; import io.serverlessworkflow.api.types.Input; @@ -35,10 +37,6 @@ public abstract class BaseWorkflowBuilder< IListBuilder extends BaseTaskItemListBuilder> implements TransformationHandlers { - public static final String DSL = "1.0.0"; - public static final String DEFAULT_VERSION = "0.0.1"; - public static final String DEFAULT_NAMESPACE = "org.acme"; - protected final Workflow workflow; private final Document document; 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 fd74a2834..e8151a093 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 @@ -15,6 +15,9 @@ */ package io.serverlessworkflow.fluent.spec; +import static io.serverlessworkflow.types.Defaults.DEFAULT_NAMESPACE; +import static io.serverlessworkflow.types.Defaults.DEFAULT_VERSION; + import java.util.UUID; public class WorkflowBuilder 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 b316de7fe..0ab726740 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 @@ -76,7 +76,7 @@ void testWorkflowDocumentDefaults() { assertNotNull(wf, "Workflow should not be null"); Document doc = wf.getDocument(); assertNotNull(doc, "Document should not be null"); - assertEquals("org.acme", doc.getNamespace(), "Default namespace should be org.acme"); + assertEquals("org-acme", doc.getNamespace(), "Default namespace should be org-acme"); assertEquals("0.0.1", doc.getVersion(), "Default version should be 0.0.1"); assertEquals("1.0.0", doc.getDsl(), "DSL version should be set to 1.0.0"); assertNotNull(doc.getName(), "Name should be auto-generated"); diff --git a/generators/jackson/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java b/generators/jackson/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java index 7cdae4621..aedea124a 100644 --- a/generators/jackson/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java +++ b/generators/jackson/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java @@ -165,7 +165,8 @@ private void processAnnotatedClasses( private JExpression processAnnotatedClass(ClassInfo classInfo, AnnotationProcessor processor) throws JClassAlreadyExistsException { - JDefinedClass result = rootPackage._class(JMod.ABSTRACT, classInfo.getSimpleName() + "MixIn"); + JDefinedClass result = + rootPackage._class(JMod.ABSTRACT | JMod.PUBLIC, classInfo.getSimpleName() + "MixIn"); nativeHandler.addClass(result); processor.accept(classInfo, result); return JExpr.dotclass(result); diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowDefinitionId.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowDefinitionId.java index 1a2bfef1c..2edbad5ed 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowDefinitionId.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowDefinitionId.java @@ -17,18 +17,19 @@ import io.serverlessworkflow.api.types.Document; import io.serverlessworkflow.api.types.Workflow; +import io.serverlessworkflow.types.Defaults; public record WorkflowDefinitionId(String namespace, String name, String version) { - public static final String DEFAULT_VERSION = "0.0.1"; - public static final String DEFAULT_NAMESPACE = "org.acme"; - public static WorkflowDefinitionId of(Workflow workflow) { Document document = workflow.getDocument(); return new WorkflowDefinitionId( document.getNamespace(), document.getName(), document.getVersion()); } + public static final String DEFAULT_NAMESPACE = Defaults.DEFAULT_NAMESPACE; + public static final String DEFAULT_VERSION = Defaults.DEFAULT_VERSION; + public static WorkflowDefinitionId fromName(String name) { return new WorkflowDefinitionId(DEFAULT_NAMESPACE, name, DEFAULT_VERSION); } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/events/EventRegistrationBuilderInfo.java b/impl/core/src/main/java/io/serverlessworkflow/impl/events/EventRegistrationBuilderInfo.java index 77a413967..550af4313 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/events/EventRegistrationBuilderInfo.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/events/EventRegistrationBuilderInfo.java @@ -47,21 +47,15 @@ public static EventRegistrationBuilderInfo from( AnyEventConsumptionStrategy any = to.getAnyEventConsumptionStrategy(); registrations = anyEvents(any, application); Until untilDesc = any.getUntil(); - if (untilDesc != null) { - until = predBuilder.apply(untilDesc); - if (until == null) { - if (untilDesc.getAnyEventUntilConsumed() != null) { - EventConsumptionStrategy strategy = untilDesc.getAnyEventUntilConsumed(); - if (strategy.getAllEventConsumptionStrategy() != null) { - untilRegistrations = - allEvents(strategy.getAllEventConsumptionStrategy(), application); - } else if (strategy.getAnyEventConsumptionStrategy() != null) { - untilRegistrations = - anyEvents(strategy.getAnyEventConsumptionStrategy(), application); - } else if (strategy.getOneEventConsumptionStrategy() != null) { - untilRegistrations = oneEvent(strategy.getOneEventConsumptionStrategy(), application); - } - } + until = predBuilder.apply(untilDesc); + if (until == null && untilDesc != null && untilDesc.getAnyEventUntilConsumed() != null) { + EventConsumptionStrategy strategy = untilDesc.getAnyEventUntilConsumed(); + if (strategy.getAllEventConsumptionStrategy() != null) { + untilRegistrations = allEvents(strategy.getAllEventConsumptionStrategy(), application); + } else if (strategy.getAnyEventConsumptionStrategy() != null) { + untilRegistrations = anyEvents(strategy.getAnyEventConsumptionStrategy(), application); + } else if (strategy.getOneEventConsumptionStrategy() != null) { + untilRegistrations = oneEvent(strategy.getOneEventConsumptionStrategy(), application); } } } else { diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/DefaultTaskExecutorFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/DefaultTaskExecutorFactory.java index dd41652bc..55363ac99 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/DefaultTaskExecutorFactory.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/DefaultTaskExecutorFactory.java @@ -47,7 +47,7 @@ public static TaskExecutorFactory get() { protected DefaultTaskExecutorFactory() {} private Collection callTasks = - ServiceLoader.load(CallableTaskBuilder.class).stream().map(Provider::get).toList(); + ServiceLoader.load(CallableTaskBuilder.class).stream().map(Provider::get).sorted().toList(); @Override public TaskExecutorBuilder getTaskExecutor( @@ -90,7 +90,7 @@ private CallableTaskBuilder findCallTask(Class clazz) return (CallableTaskBuilder) callTasks.stream() .filter(s -> s.accept(clazz)) - .findAny() + .findFirst() .orElseThrow( () -> new UnsupportedOperationException( diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/ForExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/ForExecutor.java index b4c024bee..249ee0397 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/ForExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/ForExecutor.java @@ -37,15 +37,11 @@ public class ForExecutor extends RegularTaskExecutor { private final TaskExecutor taskExecutor; public static class ForExecutorBuilder extends RegularTaskExecutorBuilder { - private WorkflowValueResolver> collectionExpr; - private Optional whileExpr; private TaskExecutor taskExecutor; protected ForExecutorBuilder( WorkflowMutablePosition position, ForTask task, WorkflowDefinition definition) { super(position, task, definition); - this.collectionExpr = buildCollectionFilter(); - this.whileExpr = buildWhileFilter(); this.taskExecutor = TaskExecutorHelper.createExecutorList(position, task.getDo(), definition); } @@ -67,8 +63,8 @@ public ForExecutor buildInstance() { protected ForExecutor(ForExecutorBuilder builder) { super(builder); - this.collectionExpr = builder.collectionExpr; - this.whileExpr = builder.whileExpr; + this.collectionExpr = builder.buildCollectionFilter(); + this.whileExpr = builder.buildWhileFilter(); this.taskExecutor = builder.taskExecutor; } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/ListenExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/ListenExecutor.java index f09e5d10f..5896e8a6f 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/ListenExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/ListenExecutor.java @@ -71,7 +71,7 @@ protected ListenExecutorBuilder( } protected WorkflowPredicate buildUntilPredicate(Until until) { - return until.getAnyEventUntilCondition() != null + return until != null && until.getAnyEventUntilCondition() != null ? WorkflowUtils.buildPredicate(application, until.getAnyEventUntilCondition()) : null; } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/SwitchExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/SwitchExecutor.java index 0e118c2c4..238c4dbab 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/SwitchExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/SwitchExecutor.java @@ -48,15 +48,15 @@ public SwitchExecutorBuilder( WorkflowMutablePosition position, SwitchTask task, WorkflowDefinition definition) { super(position, task, definition); for (SwitchItem item : task.getSwitch()) { - SwitchCase switchCase = item.getSwitchCase(); - buildFilter(switchCase) + buildFilter(item) .ifPresentOrElse( - f -> workflowFilters.put(switchCase, f), - () -> defaultDirective = switchCase.getThen()); + f -> workflowFilters.put(item.getSwitchCase(), f), + () -> defaultDirective = item.getSwitchCase().getThen()); } } - protected Optional buildFilter(SwitchCase switchCase) { + protected Optional buildFilter(SwitchItem item) { + SwitchCase switchCase = item.getSwitchCase(); return switchCase.getWhen() != null ? Optional.of(WorkflowUtils.buildPredicate(application, switchCase.getWhen())) : Optional.empty(); diff --git a/impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/ObjectMapperFactoryProvider.java b/impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/ObjectMapperFactoryProvider.java index ce9b518a4..4e7cb1c60 100644 --- a/impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/ObjectMapperFactoryProvider.java +++ b/impl/json-utils/src/main/java/io/serverlessworkflow/impl/jackson/ObjectMapperFactoryProvider.java @@ -63,6 +63,7 @@ private static class DefaultObjectMapperFactory implements ObjectMapperFactory { new ObjectMapper() .findAndRegisterModules() .registerModule(JsonFormat.getCloudEventJacksonModule()) + .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false) .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE); } diff --git a/serialization/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java b/serialization/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java index 041474d91..4291ca5b8 100644 --- a/serialization/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java +++ b/serialization/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java @@ -31,7 +31,12 @@ public class DeserializeHelper { public static T deserializeOneOf( JsonParser p, Class targetClass, Collection> oneOfTypes) throws IOException { - TreeNode node = p.readValueAsTree(); + return deserializeOneOf(p.readValueAsTree(), p, targetClass, oneOfTypes); + } + + public static T deserializeOneOf( + TreeNode node, JsonParser p, Class targetClass, Collection> oneOfTypes) + throws IOException { try { T result = targetClass.getDeclaredConstructor().newInstance(); Collection exceptions = new ArrayList<>(); diff --git a/types/src/main/java/io/serverlessworkflow/types/Defaults.java b/types/src/main/java/io/serverlessworkflow/types/Defaults.java new file mode 100644 index 000000000..d7668ef83 --- /dev/null +++ b/types/src/main/java/io/serverlessworkflow/types/Defaults.java @@ -0,0 +1,25 @@ +/* + * 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.types; + +public class Defaults { + + private Defaults() {} + + public static final String DSL = "1.0.0"; + public static final String DEFAULT_VERSION = "0.0.1"; + public static final String DEFAULT_NAMESPACE = "org-acme"; +}