diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncDoTaskBuilder.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncDoTaskBuilder.java index 0e01dd60f..49dbb2b5a 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncDoTaskBuilder.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncDoTaskBuilder.java @@ -47,6 +47,12 @@ public FuncDoTaskBuilder listen(String name, Consumer ite return this; } + @Override + public FuncDoTaskBuilder raise(String name, Consumer itemsConfigurer) { + this.listBuilder().raise(name, itemsConfigurer); + return this; + } + @Override public FuncDoTaskBuilder forEach(String name, Consumer itemsConfigurer) { this.listBuilder().forEach(name, itemsConfigurer); @@ -96,4 +102,10 @@ public FuncDoTaskBuilder openapi( this.listBuilder().openapi(name, itemsConfigurer); return this; } + + @Override + public FuncDoTaskBuilder tryCatch(String name, Consumer itemsConfigurer) { + this.listBuilder().tryCatch(name, itemsConfigurer); + return this; + } } diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncRaiseTaskBuilder.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncRaiseTaskBuilder.java new file mode 100644 index 000000000..c37d718e1 --- /dev/null +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncRaiseTaskBuilder.java @@ -0,0 +1,102 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.fluent.func; + +import io.serverlessworkflow.api.types.ErrorDetails; +import io.serverlessworkflow.api.types.ErrorTitle; +import io.serverlessworkflow.api.types.ErrorType; +import io.serverlessworkflow.api.types.RaiseTask; +import io.serverlessworkflow.api.types.RaiseTaskConfiguration; +import io.serverlessworkflow.api.types.RaiseTaskError; +import io.serverlessworkflow.api.types.UriTemplate; +import io.serverlessworkflow.fluent.func.spi.ConditionalTaskBuilder; +import io.serverlessworkflow.fluent.func.spi.FuncTaskTransformations; +import io.serverlessworkflow.fluent.spec.TaskBaseBuilder; +import java.net.URI; +import java.util.function.Consumer; + +public class FuncRaiseTaskBuilder extends TaskBaseBuilder + implements FuncTaskTransformations, + ConditionalTaskBuilder { + + private final RaiseTask raiseTask; + + FuncRaiseTaskBuilder() { + this.raiseTask = new RaiseTask(); + setTask(this.raiseTask); + } + + @Override + protected FuncRaiseTaskBuilder self() { + return this; + } + + public FuncRaiseTaskBuilder error(Consumer consumer) { + final RaiseTaskErrorBuilder raiseTaskErrorBuilder = new RaiseTaskErrorBuilder(); + consumer.accept(raiseTaskErrorBuilder); + this.raiseTask.setRaise(new RaiseTaskConfiguration().withError(raiseTaskErrorBuilder.build())); + return this; + } + + public FuncRaiseTaskBuilder error(String errorReference) { + this.raiseTask.setRaise( + new RaiseTaskConfiguration() + .withError(new RaiseTaskError().withRaiseErrorReference(errorReference))); + return this; + } + + public RaiseTask build() { + return this.raiseTask; + } + + public static final class RaiseTaskErrorBuilder { + private final io.serverlessworkflow.api.types.Error error; + + private RaiseTaskErrorBuilder() { + this.error = new io.serverlessworkflow.api.types.Error(); + } + + public RaiseTaskErrorBuilder type(String expression) { + this.error.setType(new ErrorType().withExpressionErrorType(expression)); + return this; + } + + public RaiseTaskErrorBuilder type(URI errorType) { + this.error.setType( + new ErrorType().withLiteralErrorType(new UriTemplate().withLiteralUri(errorType))); + return this; + } + + public RaiseTaskErrorBuilder status(int status) { + this.error.setStatus(status); + return this; + } + + public RaiseTaskErrorBuilder title(String expression) { + this.error.setTitle(new ErrorTitle().withExpressionErrorTitle(expression)); + return this; + } + + public RaiseTaskErrorBuilder detail(String expression) { + this.error.setDetail(new ErrorDetails().withExpressionErrorDetails(expression)); + return this; + } + + public RaiseTaskError build() { + return new RaiseTaskError().withRaiseErrorDefinition(this.error); + } + } +} 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 da32d61dc..123fd073f 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 @@ -93,6 +93,16 @@ public FuncTaskItemListBuilder listen( new TaskItem(name, new Task().withListenTask(listenTaskJavaBuilder.build()))); } + @Override + public FuncTaskItemListBuilder raise( + String name, Consumer itemsConfigurer) { + name = this.defaultNameAndRequireConfig(name, itemsConfigurer, TYPE_RAISE); + final FuncRaiseTaskBuilder raiseTaskJavaBuilder = new FuncRaiseTaskBuilder(); + itemsConfigurer.accept(raiseTaskJavaBuilder); + return this.addTaskItem( + new TaskItem(name, new Task().withRaiseTask(raiseTaskJavaBuilder.build()))); + } + @Override public FuncTaskItemListBuilder forEach( String name, Consumer itemsConfigurer) { @@ -154,4 +164,13 @@ public FuncTaskItemListBuilder openapi( return this.addTaskItem(new TaskItem(name, task)); } + + @Override + public FuncTaskItemListBuilder tryCatch( + String name, Consumer itemsConfigurer) { + name = this.defaultNameAndRequireConfig(name, itemsConfigurer, TYPE_TRY); + final FuncTryTaskBuilder tryTaskBuilder = new FuncTryTaskBuilder(); + itemsConfigurer.accept(tryTaskBuilder); + return this.addTaskItem(new TaskItem(name, new Task().withTryTask(tryTaskBuilder.build()))); + } } diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTryTaskBuilder.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTryTaskBuilder.java new file mode 100644 index 000000000..8a5629501 --- /dev/null +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTryTaskBuilder.java @@ -0,0 +1,411 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.fluent.func; + +import io.serverlessworkflow.api.types.CatchErrors; +import io.serverlessworkflow.api.types.Constant; +import io.serverlessworkflow.api.types.ConstantBackoff; +import io.serverlessworkflow.api.types.ErrorFilter; +import io.serverlessworkflow.api.types.Exponential; +import io.serverlessworkflow.api.types.ExponentialBackOff; +import io.serverlessworkflow.api.types.Linear; +import io.serverlessworkflow.api.types.LinearBackoff; +import io.serverlessworkflow.api.types.Retry; +import io.serverlessworkflow.api.types.RetryBackoff; +import io.serverlessworkflow.api.types.RetryLimit; +import io.serverlessworkflow.api.types.RetryLimitAttempt; +import io.serverlessworkflow.api.types.RetryPolicy; +import io.serverlessworkflow.api.types.RetryPolicyJitter; +import io.serverlessworkflow.api.types.TaskItem; +import io.serverlessworkflow.api.types.TimeoutAfter; +import io.serverlessworkflow.api.types.TryTask; +import io.serverlessworkflow.api.types.TryTaskCatch; +import io.serverlessworkflow.fluent.spec.TaskBaseBuilder; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class FuncTryTaskBuilder extends TaskBaseBuilder { + + private final TryTask tryTask; + + FuncTryTaskBuilder() { + this.tryTask = new TryTask(); + } + + @Override + protected FuncTryTaskBuilder self() { + return this; + } + + public FuncTryTaskBuilder tryHandler(Consumer consumer) { + List existingTasks = this.tryTask.getTry(); + int currentOffset = (existingTasks == null) ? 0 : existingTasks.size(); + + final FuncTaskItemListBuilder taskItemListBuilder = new FuncTaskItemListBuilder(currentOffset); + consumer.accept(taskItemListBuilder); + + List newTasks = taskItemListBuilder.build(); + if (existingTasks == null || existingTasks.isEmpty()) { + this.tryTask.setTry(newTasks); + } else { + List merged = new ArrayList<>(existingTasks); + merged.addAll(newTasks); + this.tryTask.setTry(merged); + } + + return this; + } + + public FuncTryTaskBuilder catchHandler(Consumer consumer) { + final TryTaskCatchBuilder catchBuilder = new TryTaskCatchBuilder(); + consumer.accept(catchBuilder); + this.tryTask.setCatch(catchBuilder.build()); + return this; + } + + public TryTask build() { + return tryTask; + } + + public static final class TryTaskCatchBuilder { + private final TryTaskCatch tryTaskCatch; + + TryTaskCatchBuilder() { + this.tryTaskCatch = new TryTaskCatch(); + } + + public TryTaskCatchBuilder as(final String as) { + this.tryTaskCatch.setAs(as); + return this; + } + + public TryTaskCatchBuilder when(final String when) { + this.tryTaskCatch.setWhen(when); + return this; + } + + public TryTaskCatchBuilder exceptWhen(final String exceptWhen) { + this.tryTaskCatch.setExceptWhen(exceptWhen); + return this; + } + + public TryTaskCatchBuilder retry(Consumer consumer) { + final RetryPolicyBuilder retryPolicyBuilder = new RetryPolicyBuilder(); + consumer.accept(retryPolicyBuilder); + this.tryTaskCatch.setRetry(new Retry().withRetryPolicyDefinition(retryPolicyBuilder.build())); + return this; + } + + public TryTaskCatchBuilder errorsWith(Consumer consumer) { + final CatchErrorsBuilder catchErrorsBuilder = new CatchErrorsBuilder(); + consumer.accept(catchErrorsBuilder); + this.tryTaskCatch.setErrors(catchErrorsBuilder.build()); + return this; + } + + public TryTaskCatchBuilder doTasks(Consumer consumer) { + List existingTasks = this.tryTaskCatch.getDo(); + int currentOffset = (existingTasks == null) ? 0 : existingTasks.size(); + + final FuncTaskItemListBuilder taskItemListBuilder = + new FuncTaskItemListBuilder(currentOffset); + consumer.accept(taskItemListBuilder); + + List newTasks = taskItemListBuilder.build(); + if (existingTasks == null || existingTasks.isEmpty()) { + this.tryTaskCatch.setDo(newTasks); + } else { + List merged = new ArrayList<>(existingTasks); + merged.addAll(newTasks); + this.tryTaskCatch.setDo(merged); + } + + return this; + } + + public TryTaskCatch build() { + return tryTaskCatch; + } + } + + public static final class CatchErrorsBuilder { + private final ErrorFilter errorFilter; + + CatchErrorsBuilder() { + this.errorFilter = new ErrorFilter(); + } + + public CatchErrorsBuilder type(final String type) { + this.errorFilter.setType(type); + return this; + } + + public CatchErrorsBuilder status(final int status) { + this.errorFilter.setStatus(status); + return this; + } + + public CatchErrorsBuilder instance(final String instance) { + this.errorFilter.setInstance(instance); + return this; + } + + public CatchErrorsBuilder title(final String title) { + this.errorFilter.setTitle(title); + return this; + } + + public CatchErrorsBuilder details(final String details) { + this.errorFilter.setDetails(details); + return this; + } + + public CatchErrors build() { + return new CatchErrors().withWith(this.errorFilter); + } + } + + public static final class RetryPolicyJitterBuilder { + private final RetryPolicyJitter retryPolicyJitter; + + RetryPolicyJitterBuilder() { + this.retryPolicyJitter = new RetryPolicyJitter(); + } + + public RetryPolicyJitterBuilder to(Consumer consumer) { + final DurationInlineBuilder durationInlineBuilder = new DurationInlineBuilder(); + consumer.accept(durationInlineBuilder); + this.retryPolicyJitter.setTo( + new TimeoutAfter().withDurationInline(durationInlineBuilder.build())); + return this; + } + + public RetryPolicyJitterBuilder to(String expression) { + this.retryPolicyJitter.setTo(new TimeoutAfter().withDurationExpression(expression)); + return this; + } + + public RetryPolicyJitterBuilder from(Consumer consumer) { + final DurationInlineBuilder durationInlineBuilder = new DurationInlineBuilder(); + consumer.accept(durationInlineBuilder); + this.retryPolicyJitter.setFrom( + new TimeoutAfter().withDurationInline(durationInlineBuilder.build())); + return this; + } + + public RetryPolicyJitterBuilder from(String expression) { + this.retryPolicyJitter.setFrom(new TimeoutAfter().withDurationExpression(expression)); + return this; + } + + public RetryPolicyJitter build() { + return retryPolicyJitter; + } + } + + public static final class RetryPolicyBuilder { + private final RetryPolicy retryPolicy; + + RetryPolicyBuilder() { + this.retryPolicy = new RetryPolicy(); + } + + public RetryPolicyBuilder when(final String when) { + this.retryPolicy.setWhen(when); + return this; + } + + public RetryPolicyBuilder exceptWhen(final String exceptWhen) { + this.retryPolicy.setExceptWhen(exceptWhen); + return this; + } + + public RetryPolicyBuilder backoff(Consumer consumer) { + final BackoffBuilder backoffBuilder = new BackoffBuilder(); + consumer.accept(backoffBuilder); + this.retryPolicy.setBackoff(backoffBuilder.build()); + return this; + } + + public RetryPolicyBuilder delay(Consumer consumer) { + final DurationInlineBuilder builder = new DurationInlineBuilder(); + consumer.accept(builder); + this.retryPolicy.setDelay(new TimeoutAfter().withDurationInline(builder.build())); + return this; + } + + public RetryPolicyBuilder delay(String expression) { + this.retryPolicy.setDelay(new TimeoutAfter().withDurationExpression(expression)); + return this; + } + + public RetryPolicyBuilder limit(Consumer consumer) { + final RetryLimitBuilder limitBuilder = new RetryLimitBuilder(); + consumer.accept(limitBuilder); + this.retryPolicy.setLimit(limitBuilder.build()); + return this; + } + + public RetryPolicyBuilder jitter(Consumer consumer) { + final RetryPolicyJitterBuilder jitterBuilder = new RetryPolicyJitterBuilder(); + consumer.accept(jitterBuilder); + this.retryPolicy.setJitter(jitterBuilder.build()); + return this; + } + + public RetryPolicy build() { + return this.retryPolicy; + } + } + + public static final class RetryLimitBuilder { + private final RetryLimit retryLimit; + + RetryLimitBuilder() { + this.retryLimit = new RetryLimit(); + } + + public RetryLimitBuilder duration(Consumer consumer) { + final DurationInlineBuilder builder = new DurationInlineBuilder(); + consumer.accept(builder); + this.retryLimit.setDuration(new TimeoutAfter().withDurationInline(builder.build())); + return this; + } + + public RetryLimitBuilder duration(String expression) { + this.retryLimit.setDuration(new TimeoutAfter().withDurationExpression(expression)); + return this; + } + + public RetryLimitBuilder attempt(Consumer consumer) { + final RetryLimitAttemptBuilder retryLimitAttemptBuilder = new RetryLimitAttemptBuilder(); + consumer.accept(retryLimitAttemptBuilder); + this.retryLimit.setAttempt(retryLimitAttemptBuilder.build()); + return this; + } + + public RetryLimit build() { + return this.retryLimit; + } + } + + public static final class RetryLimitAttemptBuilder { + private final RetryLimitAttempt retryLimitAttempt; + + RetryLimitAttemptBuilder() { + this.retryLimitAttempt = new RetryLimitAttempt(); + } + + public RetryLimitAttemptBuilder count(int count) { + this.retryLimitAttempt.setCount(count); + return this; + } + + public RetryLimitAttemptBuilder duration(Consumer consumer) { + final DurationInlineBuilder builder = new DurationInlineBuilder(); + consumer.accept(builder); + this.retryLimitAttempt.setDuration(new TimeoutAfter().withDurationInline(builder.build())); + return this; + } + + public RetryLimitAttemptBuilder duration(String expression) { + this.retryLimitAttempt.setDuration(new TimeoutAfter().withDurationExpression(expression)); + return this; + } + + public RetryLimitAttempt build() { + return this.retryLimitAttempt; + } + } + + public static final class BackoffBuilder { + private final RetryBackoff retryBackoff; + private final ConstantBackoff constantBackoff; + private final ExponentialBackOff exponentialBackOff; + private final LinearBackoff linearBackoff; + + BackoffBuilder() { + this.retryBackoff = new RetryBackoff(); + + this.constantBackoff = new ConstantBackoff(); + this.constantBackoff.setConstant(new Constant()); + this.exponentialBackOff = new ExponentialBackOff(); + this.exponentialBackOff.setExponential(new Exponential()); + this.linearBackoff = new LinearBackoff(); + this.linearBackoff.setLinear(new Linear()); + } + + public BackoffBuilder constant(String key, String value) { + this.constantBackoff.getConstant().withAdditionalProperty(key, value); + return this; + } + + public BackoffBuilder exponential(String key, String value) { + this.exponentialBackOff.getExponential().withAdditionalProperty(key, value); + return this; + } + + public BackoffBuilder linear(String key, String value) { + this.linearBackoff.getLinear().withAdditionalProperty(key, value); + return this; + } + + public RetryBackoff build() { + this.retryBackoff.setConstantBackoff(constantBackoff); + this.retryBackoff.setExponentialBackOff(exponentialBackOff); + this.retryBackoff.setLinearBackoff(linearBackoff); + return this.retryBackoff; + } + } + + public static final class DurationInlineBuilder { + private final io.serverlessworkflow.api.types.DurationInline durationInline; + + DurationInlineBuilder() { + this.durationInline = new io.serverlessworkflow.api.types.DurationInline(); + } + + public DurationInlineBuilder seconds(int seconds) { + this.durationInline.setSeconds(seconds); + return this; + } + + public DurationInlineBuilder milliseconds(int milliseconds) { + this.durationInline.setMilliseconds(milliseconds); + return this; + } + + public DurationInlineBuilder minutes(int minutes) { + this.durationInline.setMinutes(minutes); + return this; + } + + public DurationInlineBuilder hours(int hours) { + this.durationInline.setHours(hours); + return this; + } + + public DurationInlineBuilder days(int days) { + this.durationInline.setDays(days); + return this; + } + + public io.serverlessworkflow.api.types.DurationInline build() { + return this.durationInline; + } + } +} 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 746d0ce1f..f37c5332c 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 @@ -28,8 +28,10 @@ import io.serverlessworkflow.api.types.func.FilterFunction; import io.serverlessworkflow.fluent.func.FuncCallTaskBuilder; import io.serverlessworkflow.fluent.func.FuncEmitTaskBuilder; +import io.serverlessworkflow.fluent.func.FuncRaiseTaskBuilder; import io.serverlessworkflow.fluent.func.FuncSwitchTaskBuilder; import io.serverlessworkflow.fluent.func.FuncTaskItemListBuilder; +import io.serverlessworkflow.fluent.func.FuncTryTaskBuilder; import io.serverlessworkflow.fluent.func.configurers.FuncCallHttpConfigurer; import io.serverlessworkflow.fluent.func.configurers.FuncCallOpenAPIConfigurer; import io.serverlessworkflow.fluent.func.configurers.FuncTaskConfigurer; @@ -883,6 +885,48 @@ public static FuncTaskConfigurer switchCase(String taskName, SwitchCaseConfigure return list -> list.switchCase(taskName, s -> snapshot.forEach(s::onPredicate)); } + /** + * Create a {@link FuncTaskConfigurer} that adds a {@code raise} task. + * + * @param configurer raise task builder configurer + * @return list configurer + */ + public static FuncTaskConfigurer raise(Consumer configurer) { + return list -> list.raise(configurer); + } + + /** + * Create a {@link FuncTaskConfigurer} that adds a named {@code raise} task. + * + * @param name task name + * @param configurer raise task builder configurer + * @return list configurer + */ + public static FuncTaskConfigurer raise(String name, Consumer configurer) { + return list -> list.raise(name, configurer); + } + + /** + * Create a {@link FuncTaskConfigurer} that adds a {@code try/catch} task. + * + * @param configurer try task builder configurer + * @return list configurer + */ + public static FuncTaskConfigurer tryCatch(Consumer configurer) { + return list -> list.tryCatch(configurer); + } + + /** + * Create a {@link FuncTaskConfigurer} that adds a named {@code try/catch} task. + * + * @param name task name + * @param configurer try task builder configurer + * @return list configurer + */ + public static FuncTaskConfigurer tryCatch(String name, Consumer configurer) { + return list -> list.tryCatch(name, configurer); + } + /** * Sugar for a single-case switch: if predicate matches, jump to {@code thenTask}. * diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/spi/FuncDoFluent.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/spi/FuncDoFluent.java index ee9f857e0..e1aa5e203 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/spi/FuncDoFluent.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/spi/FuncDoFluent.java @@ -22,16 +22,20 @@ import io.serverlessworkflow.fluent.func.FuncForTaskBuilder; import io.serverlessworkflow.fluent.func.FuncForkTaskBuilder; import io.serverlessworkflow.fluent.func.FuncListenTaskBuilder; +import io.serverlessworkflow.fluent.func.FuncRaiseTaskBuilder; import io.serverlessworkflow.fluent.func.FuncSetTaskBuilder; import io.serverlessworkflow.fluent.func.FuncSwitchTaskBuilder; +import io.serverlessworkflow.fluent.func.FuncTryTaskBuilder; import io.serverlessworkflow.fluent.spec.spi.CallHttpFluent; import io.serverlessworkflow.fluent.spec.spi.CallOpenAPIFluent; import io.serverlessworkflow.fluent.spec.spi.EmitFluent; import io.serverlessworkflow.fluent.spec.spi.ForEachFluent; import io.serverlessworkflow.fluent.spec.spi.ForkFluent; import io.serverlessworkflow.fluent.spec.spi.ListenFluent; +import io.serverlessworkflow.fluent.spec.spi.RaiseFluent; import io.serverlessworkflow.fluent.spec.spi.SetFluent; import io.serverlessworkflow.fluent.spec.spi.SwitchFluent; +import io.serverlessworkflow.fluent.spec.spi.TryCatchFluent; public interface FuncDoFluent> extends SetFluent, @@ -40,6 +44,8 @@ public interface FuncDoFluent> SwitchFluent, ForkFluent, ListenFluent, + RaiseFluent, + TryCatchFluent, CallFnFluent, CallHttpFluent, CallOpenAPIFluent {} 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 a11d2c753..5376103e2 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 @@ -23,8 +23,10 @@ import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.http; import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.listen; import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.produced; +import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.raise; import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.switchWhenOrElse; import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.toOne; +import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.tryCatch; import static io.serverlessworkflow.fluent.spec.dsl.DSL.use; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; @@ -174,6 +176,68 @@ void mixed_chaining_order_and_exports() { assertNotNull(t2.getListenTask().getExport(), "listen step should carry export"); } + @Test + void raise_and_tryCatch_build_through_func_workflow_builder() { + Workflow wf = + FuncWorkflowBuilder.workflow("raise-try") + .tasks( + FuncDSL.tasks( + raise("boom", r -> r.error(e -> e.type("org.acme.Boom").status(409))), + tryCatch( + "guarded", + t -> + t.tryHandler( + tb -> + tb.function( + cb -> + cb.function((String s) -> s.trim(), String.class))) + .catchHandler( + c -> + c.when("$.errorType == 'TEMP'") + .doTasks( + d -> + d.raise( + "handled", + r -> + r.error( + e -> + e.type("org.acme.Handled") + .status(500)))))))) + .build(); + + List items = wf.getDo(); + assertEquals(2, items.size()); + + Task raiseTask = items.get(0).getTask(); + assertNotNull(raiseTask.getRaiseTask(), "RaiseTask expected"); + assertEquals( + "org.acme.Boom", + raiseTask + .getRaiseTask() + .getRaise() + .getError() + .getRaiseErrorDefinition() + .getType() + .getExpressionErrorType()); + assertEquals( + 409, raiseTask.getRaiseTask().getRaise().getError().getRaiseErrorDefinition().getStatus()); + + Task tryTask = items.get(1).getTask(); + assertNotNull(tryTask.getTryTask(), "TryTask expected"); + assertEquals(1, tryTask.getTryTask().getTry().size(), "Try block should contain one task"); + assertNotNull( + tryTask.getTryTask().getTry().get(0).getTask().getCallTask(), + "Function task should compile inside try"); + + assertNotNull(tryTask.getTryTask().getCatch(), "Catch block expected"); + assertEquals("$.errorType == 'TEMP'", tryTask.getTryTask().getCatch().getWhen()); + assertEquals( + 1, tryTask.getTryTask().getCatch().getDo().size(), "Catch block should contain one task"); + assertNotNull( + tryTask.getTryTask().getCatch().getDo().get(0).getTask().getRaiseTask(), + "Raise task should compile inside catch"); + } + @Test void switchWhenOrElse_jq_to_taskName() { Workflow wf =