diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncCallHttpSpec.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncCallHttpSpec.java deleted file mode 100644 index 58259b80..00000000 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncCallHttpSpec.java +++ /dev/null @@ -1,34 +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.fluent.func.dsl; - -import io.serverlessworkflow.fluent.func.FuncCallHttpTaskBuilder; -import io.serverlessworkflow.fluent.func.configurers.FuncCallHttpConfigurer; -import io.serverlessworkflow.fluent.spec.dsl.BaseCallHttpSpec; - -public class FuncCallHttpSpec extends BaseCallHttpSpec - implements FuncCallHttpConfigurer { - - @Override - protected FuncCallHttpSpec self() { - return this; - } - - @Override - public void accept(FuncCallHttpTaskBuilder funcCallHttpTaskBuilder) { - super.accept(funcCallHttpTaskBuilder); - } -} diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncCallHttpStep.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncCallHttpStep.java new file mode 100644 index 00000000..5e724628 --- /dev/null +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncCallHttpStep.java @@ -0,0 +1,57 @@ +/* + * 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.dsl; + +import io.serverlessworkflow.fluent.func.FuncCallHttpTaskBuilder; +import io.serverlessworkflow.fluent.func.FuncTaskItemListBuilder; +import io.serverlessworkflow.fluent.spec.dsl.BaseCallHttpSpec; +import io.serverlessworkflow.fluent.spec.spi.CallHttpTaskFluent; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class FuncCallHttpStep extends Step + implements BaseCallHttpSpec { + + private final List>> steps = new ArrayList<>(); + + private String name; + + public FuncCallHttpStep(String name) { + this.name = name; + } + + public FuncCallHttpStep() {} + + @Override + public FuncCallHttpStep self() { + return this; + } + + protected void configure(FuncTaskItemListBuilder list, Consumer post) { + list.http( + name, + builder -> { + this.accept(builder); + post.accept(builder); + }); + } + + @Override + public List>> steps() { + return steps; + } +} diff --git a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncCallOpenAPISpec.java b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncCallOpenAPIStep.java similarity index 62% rename from experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncCallOpenAPISpec.java rename to experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncCallOpenAPIStep.java index 5915920b..b6530cbf 100644 --- a/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncCallOpenAPISpec.java +++ b/experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncCallOpenAPIStep.java @@ -17,7 +17,7 @@ import io.serverlessworkflow.api.types.OpenAPIArguments; import io.serverlessworkflow.fluent.func.FuncCallOpenAPITaskBuilder; -import io.serverlessworkflow.fluent.func.configurers.FuncCallOpenAPIConfigurer; +import io.serverlessworkflow.fluent.func.FuncTaskItemListBuilder; import io.serverlessworkflow.fluent.spec.configurers.AuthenticationConfigurer; import io.serverlessworkflow.fluent.spec.spi.CallOpenAPITaskFluent; import java.net.URI; @@ -26,65 +26,83 @@ import java.util.Map; import java.util.function.Consumer; -public class FuncCallOpenAPISpec implements FuncCallOpenAPIConfigurer { +public class FuncCallOpenAPIStep extends Step { private final List>> steps = new ArrayList<>(); - public FuncCallOpenAPISpec document(String uri) { + private String name; + + public FuncCallOpenAPIStep(String name) { + this.name = name; + } + + public FuncCallOpenAPIStep() {} + + public void setName(String name) { + this.name = name; + } + + public FuncCallOpenAPIStep document(String uri) { steps.add(b -> b.document(uri)); return this; } - public FuncCallOpenAPISpec document( + public FuncCallOpenAPIStep document( String uri, AuthenticationConfigurer authenticationConfigurer) { steps.add(b -> b.document(uri, authenticationConfigurer)); return this; } - public FuncCallOpenAPISpec document(URI uri) { + public FuncCallOpenAPIStep document(URI uri) { steps.add(b -> b.document(uri)); return this; } - public FuncCallOpenAPISpec document(URI uri, AuthenticationConfigurer authenticationConfigurer) { + public FuncCallOpenAPIStep document(URI uri, AuthenticationConfigurer authenticationConfigurer) { steps.add(b -> b.document(uri, authenticationConfigurer)); return this; } - public FuncCallOpenAPISpec operation(String operationId) { + public FuncCallOpenAPIStep operation(String operationId) { steps.add(b -> b.operation(operationId)); return this; } - public FuncCallOpenAPISpec parameters(Map params) { + public FuncCallOpenAPIStep parameters(Map params) { steps.add(b -> b.parameters(params)); return this; } - public FuncCallOpenAPISpec parameter(String name, String value) { + public FuncCallOpenAPIStep parameter(String name, String value) { steps.add(b -> b.parameter(name, value)); return this; } - public FuncCallOpenAPISpec redirect(boolean redirect) { + public FuncCallOpenAPIStep redirect(boolean redirect) { steps.add(b -> b.redirect(redirect)); return this; } - public FuncCallOpenAPISpec authentication(AuthenticationConfigurer authenticationConfigurer) { + public FuncCallOpenAPIStep authentication(AuthenticationConfigurer authenticationConfigurer) { steps.add(b -> b.authentication(authenticationConfigurer)); return this; } - public FuncCallOpenAPISpec output(OpenAPIArguments.WithOpenAPIOutput output) { + public FuncCallOpenAPIStep output(OpenAPIArguments.WithOpenAPIOutput output) { steps.add(b -> b.output(output)); return this; } @Override - public void accept(FuncCallOpenAPITaskBuilder builder) { - for (var s : steps) { - s.accept(builder); - } + protected void configure( + FuncTaskItemListBuilder list, Consumer post) { + list.openapi( + name, + builder -> { + for (Consumer> c : steps) { + c.accept(builder); + } + post.accept(builder); + }); } } 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 a98153fc..ff2db37e 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 @@ -24,6 +24,7 @@ import io.serverlessworkflow.fluent.func.FuncSwitchTaskBuilder; import io.serverlessworkflow.fluent.func.FuncTaskItemListBuilder; import io.serverlessworkflow.fluent.func.configurers.FuncCallHttpConfigurer; +import io.serverlessworkflow.fluent.func.configurers.FuncCallOpenAPIConfigurer; import io.serverlessworkflow.fluent.func.configurers.FuncPredicateEventConfigurer; import io.serverlessworkflow.fluent.func.configurers.FuncTaskConfigurer; import io.serverlessworkflow.fluent.func.configurers.SwitchCaseConfigurer; @@ -47,22 +48,23 @@ * *
    *
  • Infer input types for Java lambdas where possible. - *
  • Expose chainable steps (e.g., {@code emit(...).exportAs(...).when(...)}) via {@code Step}s. + *
  • Expose chainable steps (e.g., {@code emit(...).exportAs(...).when(...)}) via {@link Step} + * subclasses. *
  • Provide opinionated helpers for CloudEvents (JSON/bytes) and listen strategies. *
  • Offer context-aware function variants ({@code withContext}, {@code withInstanceId}, {@code - * agent}). + * withUniqueId}, {@code agent}). *
* *

Typical usage: * *

{@code
- * Workflow wf = FuncWorkflowBuilder.workflow()
+ * Workflow wf = FuncWorkflowBuilder.workflow("example")
  *   .tasks(
  *     FuncDSL.function(String::trim, String.class),
  *     FuncDSL.emitJson("org.acme.started", MyPayload.class),
  *     FuncDSL.listen(FuncDSL.toAny("type.one", "type.two"))
  *       .outputAs(map -> map.get("value")),
- *     FuncDSL.switchWhenOrElse((Integer v) -> v > 0, "positive", FlowDirectiveEnum.END)
+ *     FuncDSL.switchWhenOrElse((Integer v) -> v > 0, "positive", FlowDirectiveEnum.END, Integer.class)
  *   ).build();
  * }
*/ @@ -297,7 +299,7 @@ public static FuncCallStep function(Function fn, Class cla * *

Signature expected: {@code (ctx, payload) -> result} * - * @param fn context-aware bi-function + * @param fn context-aware function * @param in payload input class * @param input type * @param result type @@ -313,7 +315,7 @@ public static FuncCallStep withContext(JavaContextFunction fn * *

Signature expected: {@code (instanceId, payload) -> result} * - * @param fn instance-id-aware bi-function + * @param fn instance-id-aware function * @param in payload input class * @param input type * @param result type @@ -328,7 +330,7 @@ public static FuncCallStep withInstanceId( * Named variant of {@link #withContext(JavaContextFunction, Class)}. * * @param name task name - * @param fn context-aware bi-function + * @param fn context-aware function * @param in payload input class * @param input type * @param result type @@ -341,12 +343,12 @@ public static FuncCallStep withContext( /** * Build a call step for functions that need {@link WorkflowContextData} and {@link - * io.serverlessworkflow.impl.TaskContextData} as the first and second parameter. The DSL wraps it - * as a {@link JavaFilterFunction} and injects the runtime context. + * TaskContextData} as the first and second parameter. The DSL wraps it as a {@link + * JavaFilterFunction} and injects the runtime context. * - *

Signature expected: {@code (wctx, tctx, payload) -> result} + *

Signature expected: {@code (payload, wctx, tctx) -> result} * - * @param fn context-aware bi-function + * @param fn context-aware filter function * @param in payload input class * @param input type * @param result type @@ -360,7 +362,7 @@ public static FuncCallStep withFilter(JavaFilterFunction fn, * Named variant of {@link #withFilter(JavaFilterFunction, Class)}. * * @param name task name - * @param fn context-aware bi-function + * @param fn context-aware filter function * @param in payload input class * @param input type * @param result type @@ -375,7 +377,7 @@ public static FuncCallStep withFilter( * Named variant of {@link #withInstanceId(InstanceIdBiFunction, Class)}. * * @param name task name - * @param fn instance-id-aware bi-function + * @param fn instance-id-aware function * @param in payload input class * @param input type * @param result type @@ -389,20 +391,25 @@ public static FuncCallStep withInstanceId( /** * Builds a composition of the current workflow instance id and the definition of the task - * position as a JSON pointer. + * position as a JSON pointer, used as a stable "unique id" for the task. + * + * @param wctx workflow context + * @param tctx task context + * @return a unique id in the form {@code "-"} */ static String defaultUniqueId(WorkflowContextData wctx, TaskContextData tctx) { return String.format("%s-%s", wctx.instanceData().id(), tctx.position().jsonPointer()); } /** - * Build a call step for functions that expect a composition with the workflow instance id and the - * task position as the first parameter. The instance ID is extracted from the runtime context, - * the task position from the definition. + * Build a call step for functions that expect a composite "unique id" as the first parameter. + * This id is derived from the workflow instance id and the task definition position, encoded as a + * JSON pointer. * *

Signature expected: {@code (uniqueId, payload) -> result} * - * @param fn unique-id-aware bi-function + * @param name task name (or {@code null} for an anonymous task) + * @param fn unique-id-aware function * @param in payload input class * @param input type * @param result type @@ -415,6 +422,16 @@ public static FuncCallStep withUniqueId( return new FuncCallStep<>(name, jff, in); } + /** + * Variant of {@link #withUniqueId(String, UniqueIdBiFunction, Class)} without an explicit task + * name. + * + * @param fn unique-id-aware function + * @param in payload input class + * @param input type + * @param result type + * @return a call step + */ public static FuncCallStep withUniqueId(UniqueIdBiFunction fn, Class in) { return withUniqueId(null, fn, in); } @@ -425,7 +442,8 @@ public static FuncCallStep withUniqueId(UniqueIdBiFunction fn * @param consumer side-effect function * @param clazz expected input class for conversion * @param input type - * @return a consume step + * @return a {@link ConsumeStep} which can be chained and added via {@link + * #tasks(FuncTaskConfigurer...)} */ public static ConsumeStep consume(Consumer consumer, Class clazz) { return new ConsumeStep<>(consumer, clazz); @@ -438,19 +456,20 @@ public static ConsumeStep consume(Consumer consumer, Class clazz) { * @param consumer side-effect function * @param clazz expected input class * @param input type - * @return a consume step + * @return a named {@link ConsumeStep} */ public static ConsumeStep consume(String name, Consumer consumer, Class clazz) { return new ConsumeStep<>(name, consumer, clazz); } /** - * Agent-style sugar for methods that receive a "memory id" as first parameter. We reuse the - * workflow instance id for that purpose. + * Agent-style sugar for methods that receive a "memory id" as first parameter. The DSL uses a + * derived unique id composed of the workflow instance id and the task position (JSON pointer), + * via {@link #withUniqueId(UniqueIdBiFunction, Class)}. * - *

Equivalent to {@link #withInstanceId(InstanceIdBiFunction, Class)}. + *

Signature expected: {@code (uniqueId, payload) -> result} * - * @param fn (instanceId, payload) -> result + * @param fn (uniqueId, payload) -> result * @param in payload input class * @param input type * @param result type @@ -463,8 +482,10 @@ public static FuncCallStep agent(UniqueIdBiFunction fn, Class /** * Named agent-style sugar. See {@link #agent(UniqueIdBiFunction, Class)}. * + *

Signature expected: {@code (uniqueId, payload) -> result} + * * @param name task name - * @param fn (instanceId, payload) -> result + * @param fn (uniqueId, payload) -> result * @param in payload input class * @param input type * @param result type @@ -548,7 +569,7 @@ public static EmitStep emit(Consumer cfg) { * * @param name task name * @param cfg emit builder configurer - * @return a named emit step + * @return a named {@link EmitStep} */ public static EmitStep emit(String name, Consumer cfg) { return new EmitStep(name, cfg); @@ -594,7 +615,15 @@ public static EmitStep emit( return new EmitStep(name, eventBytes(type, serializer, clazz)); } - /** Unnamed variant of {@link #emit(String, String, Function, Class)}. */ + /** + * Unnamed variant of {@link #emit(String, String, Function, Class)}. + * + * @param type CloudEvent type + * @param serializer function producing bytes + * @param clazz expected input class + * @param input type + * @return an {@link EmitStep} + */ public static EmitStep emit(String type, Function serializer, Class clazz) { return new EmitStep(null, eventBytes(type, serializer, clazz)); } @@ -658,7 +687,12 @@ public static FuncTaskConfigurer switchCase( return list -> list.switchCase(taskName, switchCase); } - /** Variant of {@link #switchCase(String, Consumer)} without a name. */ + /** + * Variant of {@link #switchCase(String, Consumer)} without a name. + * + * @param switchCase consumer to configure the {@link FuncSwitchTaskBuilder} + * @return a list configurer + */ public static FuncTaskConfigurer switchCase(Consumer switchCase) { return list -> list.switchCase(switchCase); } @@ -762,6 +796,11 @@ public static FuncTaskConfigurer switchWhenOrElse( * * *

The JQ expression is evaluated against the task input at runtime. + * + * @param jqExpression JQ expression evaluated against the current task input + * @param thenTask task to jump to if the expression evaluates truthy + * @param otherwise default flow directive when the expression is falsy + * @return list configurer */ public static FuncTaskConfigurer switchWhenOrElse( String jqExpression, String thenTask, FlowDirectiveEnum otherwise) { @@ -783,6 +822,11 @@ public static FuncTaskConfigurer switchWhenOrElse( * * *

The JQ expression is evaluated against the task input at runtime. + * + * @param jqExpression JQ expression evaluated against the current task input + * @param thenTask task name when truthy + * @param otherwiseTask task name when falsy + * @return list configurer */ public static FuncTaskConfigurer switchWhenOrElse( String jqExpression, String thenTask, String otherwiseTask) { @@ -837,7 +881,7 @@ public static FuncTaskConfigurer forEach( } /** - * Set a raw JQ-like expression on the current list (advanced). + * Set a raw expression on the current list (advanced). * * @param expr expression string * @return list configurer @@ -847,7 +891,7 @@ public static FuncTaskConfigurer set(String expr) { } /** - * Set a map-based expression on the current list (advanced). + * Set values on the current list from a map (advanced). * * @param map map of values to set * @return list configurer @@ -856,6 +900,10 @@ public static FuncTaskConfigurer set(Map map) { return list -> list.set(s -> s.expr(map)); } + // --------------------------------------------------------------------------- + // HTTP / OpenAPI + // --------------------------------------------------------------------------- + /** * Low-level HTTP call entrypoint using a {@link FuncCallHttpConfigurer}. * @@ -883,7 +931,7 @@ public static FuncTaskConfigurer call(String name, FuncCallHttpConfigurer config } /** - * HTTP call using a fluent {@link FuncCallHttpSpec}. + * HTTP call using a fluent {@link FuncCallHttpStep}. * *

This overload creates an unnamed HTTP task. * @@ -900,12 +948,12 @@ public static FuncTaskConfigurer call(String name, FuncCallHttpConfigurer config * @param spec fluent HTTP spec built via {@link #http()} * @return a {@link FuncTaskConfigurer} that adds an HTTP task */ - public static FuncTaskConfigurer call(FuncCallHttpSpec spec) { + public static FuncTaskConfigurer call(FuncCallHttpStep spec) { return call(null, spec); } /** - * HTTP call using a fluent {@link FuncCallHttpSpec} with explicit task name. + * HTTP call using a fluent {@link FuncCallHttpStep} with explicit task name. * *

{@code
    * tasks(
@@ -921,13 +969,13 @@ public static FuncTaskConfigurer call(FuncCallHttpSpec spec) {
    * @param spec fluent HTTP spec built via {@link #http()}
    * @return a {@link FuncTaskConfigurer} that adds an HTTP task
    */
-  public static FuncTaskConfigurer call(String name, FuncCallHttpSpec spec) {
+  public static FuncTaskConfigurer call(String name, FuncCallHttpStep spec) {
     Objects.requireNonNull(spec, "spec");
-    return call(name, spec::accept);
+    return call(name, (FuncCallHttpConfigurer) spec::accept);
   }
 
   /**
-   * OpenAPI call using a fluent {@link FuncCallOpenAPISpec}.
+   * OpenAPI call using a fluent {@link FuncCallOpenAPIStep}.
    *
    * 

This overload creates an unnamed OpenAPI call task. * @@ -946,12 +994,12 @@ public static FuncTaskConfigurer call(String name, FuncCallHttpSpec spec) { * @param spec fluent OpenAPI spec built via {@link #openapi()} * @return a {@link FuncTaskConfigurer} that adds an OpenAPI call task to the workflow */ - public static FuncTaskConfigurer call(FuncCallOpenAPISpec spec) { + public static FuncTaskConfigurer call(FuncCallOpenAPIStep spec) { return call(null, spec); } /** - * OpenAPI call using a fluent {@link FuncCallOpenAPISpec} with an explicit task name. + * OpenAPI call using a fluent {@link FuncCallOpenAPIStep} with an explicit task name. * *

Example: * @@ -973,13 +1021,40 @@ public static FuncTaskConfigurer call(FuncCallOpenAPISpec spec) { * @param spec fluent OpenAPI spec built via {@link #openapi()} * @return a {@link FuncTaskConfigurer} that adds a named OpenAPI call task */ - public static FuncTaskConfigurer call(String name, FuncCallOpenAPISpec spec) { + public static FuncTaskConfigurer call(String name, FuncCallOpenAPIStep spec) { Objects.requireNonNull(spec, "spec"); - return list -> list.openapi(name, spec); + spec.setName(name); + return spec; } /** - * Create a new OpenAPI specification to be used with {@link #call(FuncCallOpenAPISpec)}. + * Low-level OpenAPI call entrypoint using a {@link FuncCallOpenAPIConfigurer}. + * + *

This overload creates an unnamed OpenAPI call task. + * + * @param configurer configurer that mutates the underlying OpenAPI call builder + * @return a {@link FuncTaskConfigurer} that adds an OpenAPI call task + */ + public static FuncTaskConfigurer call(FuncCallOpenAPIConfigurer configurer) { + return call(null, configurer); + } + + /** + * Low-level OpenAPI call entrypoint using a {@link FuncCallOpenAPIConfigurer}. + * + *

This overload allows assigning an explicit task name. + * + * @param name task name, or {@code null} for an anonymous task + * @param configurer configurer that mutates the underlying OpenAPI call builder + * @return a {@link FuncTaskConfigurer} that adds an OpenAPI call task + */ + public static FuncTaskConfigurer call(String name, FuncCallOpenAPIConfigurer configurer) { + Objects.requireNonNull(configurer, "configurer"); + return list -> list.openapi(name, configurer); + } + + /** + * Create a new OpenAPI specification to be used with {@link #call(FuncCallOpenAPIStep)}. * *

Typical usage: * @@ -996,14 +1071,25 @@ public static FuncTaskConfigurer call(String name, FuncCallOpenAPISpec spec) { * parameters, authentication, etc.) and applies them to the underlying OpenAPI call task at build * time. * - * @return a new {@link FuncCallOpenAPISpec} + * @return a new {@link FuncCallOpenAPIStep} */ - public static FuncCallOpenAPISpec openapi() { - return new FuncCallOpenAPISpec(); + public static FuncCallOpenAPIStep openapi() { + return new FuncCallOpenAPIStep(); } /** - * Create a new, empty HTTP specification to be used with {@link #call(FuncCallHttpSpec)}. + * Named variant of {@link #openapi()}. + * + * @param name task name to be used when the spec is attached via {@link + * #call(FuncCallOpenAPIStep)} + * @return a new named {@link FuncCallOpenAPIStep} + */ + public static FuncCallOpenAPIStep openapi(String name) { + return new FuncCallOpenAPIStep(name); + } + + /** + * Create a new, empty HTTP specification to be used with {@link #call(FuncCallHttpStep)}. * *

Typical usage: * @@ -1016,10 +1102,20 @@ public static FuncCallOpenAPISpec openapi() { * ); * }

* - * @return a new {@link FuncCallHttpSpec} + * @return a new {@link FuncCallHttpStep} + */ + public static FuncCallHttpStep http() { + return new FuncCallHttpStep(); + } + + /** + * Named variant of {@link #http()}. + * + * @param name task name to be used when the spec is attached via {@link #call(FuncCallHttpStep)} + * @return a new named {@link FuncCallHttpStep} */ - public static FuncCallHttpSpec http() { - return new FuncCallHttpSpec(); + public static FuncCallHttpStep http(String name) { + return new FuncCallHttpStep(name); } /** @@ -1034,10 +1130,10 @@ public static FuncCallHttpSpec http() { * * @param urlExpr expression or literal string for the endpoint URL * @param auth authentication configurer (e.g. {@code auth -> auth.use("my-auth")}) - * @return a {@link FuncCallHttpSpec} preconfigured with endpoint + auth + * @return a {@link FuncCallHttpStep} preconfigured with endpoint + auth */ - public static FuncCallHttpSpec http(String urlExpr, AuthenticationConfigurer auth) { - return new FuncCallHttpSpec().endpoint(urlExpr, auth); + public static FuncCallHttpStep http(String urlExpr, AuthenticationConfigurer auth) { + return new FuncCallHttpStep().endpoint(urlExpr, auth); } /** @@ -1045,176 +1141,185 @@ public static FuncCallHttpSpec http(String urlExpr, AuthenticationConfigurer aut * * @param url concrete URI to call * @param auth authentication configurer - * @return a {@link FuncCallHttpSpec} preconfigured with URI + auth + * @return a {@link FuncCallHttpStep} preconfigured with URI + auth */ - public static FuncCallHttpSpec http(URI url, AuthenticationConfigurer auth) { - return new FuncCallHttpSpec().uri(url, auth); + public static FuncCallHttpStep http(URI url, AuthenticationConfigurer auth) { + return new FuncCallHttpStep().uri(url, auth); } /** - * Convenience for adding an unnamed {@code GET} HTTP task using a string endpoint. + * Convenience for building an unnamed {@code GET} HTTP step using a string endpoint. * *
{@code
    * tasks(
-   *   FuncDSL.get("http://service/health")
+   *   FuncDSL.call(
+   *     FuncDSL.get("http://service/health")
+   *   )
    * );
    * }
* * @param endpoint literal or expression for the endpoint URL - * @return a {@link FuncTaskConfigurer} adding a {@code GET} HTTP task + * @return a {@link FuncCallHttpStep} that can be chained and later passed to {@link + * #call(FuncCallHttpStep)} */ - public static FuncTaskConfigurer get(String endpoint) { + public static FuncCallHttpStep get(String endpoint) { return get(null, endpoint); } /** - * Convenience for adding a named {@code GET} HTTP task using a string endpoint. + * Convenience for building a named {@code GET} HTTP step using a string endpoint. * *
{@code
    * tasks(
-   *   FuncDSL.get("checkHealth", "http://service/health")
+   *   FuncDSL.call(
+   *     FuncDSL.get("checkHealth", "http://service/health")
+   *   )
    * );
    * }
* * @param name task name * @param endpoint literal or expression for the endpoint URL - * @return a {@link FuncTaskConfigurer} adding a {@code GET} HTTP task + * @return a named {@link FuncCallHttpStep} that can be passed to {@link #call(FuncCallHttpStep)} */ - public static FuncTaskConfigurer get(String name, String endpoint) { - return call(name, http().GET().endpoint(endpoint)); + public static FuncCallHttpStep get(String name, String endpoint) { + return http(name).GET().endpoint(endpoint); } /** - * Convenience for adding an unnamed authenticated {@code GET} HTTP task using a string endpoint. + * Convenience for building an unnamed authenticated {@code GET} HTTP step using a string + * endpoint. * *
{@code
    * tasks(
-   *   FuncDSL.get("http://service/api/users", auth -> auth.use("user-service-auth"))
+   *   FuncDSL.call(
+   *     FuncDSL.get("http://service/api/users", auth -> auth.use("user-service-auth"))
+   *   )
    * );
    * }
* * @param endpoint literal or expression for the endpoint URL * @param auth authentication configurer - * @return a {@link FuncTaskConfigurer} adding a {@code GET} HTTP task + * @return a {@link FuncCallHttpStep} that can be passed to {@link #call(FuncCallHttpStep)} */ - public static FuncTaskConfigurer get(String endpoint, AuthenticationConfigurer auth) { + public static FuncCallHttpStep get(String endpoint, AuthenticationConfigurer auth) { return get(null, endpoint, auth); } /** - * Convenience for adding a named authenticated {@code GET} HTTP task using a string endpoint. + * Convenience for building a named authenticated {@code GET} HTTP step using a string endpoint. * * @param name task name * @param endpoint literal or expression for the endpoint URL * @param auth authentication configurer - * @return a {@link FuncTaskConfigurer} adding a {@code GET} HTTP task + * @return a named {@link FuncCallHttpStep} that can be passed to {@link #call(FuncCallHttpStep)} */ - public static FuncTaskConfigurer get( - String name, String endpoint, AuthenticationConfigurer auth) { - return call(name, http().GET().endpoint(endpoint, auth)); + public static FuncCallHttpStep get(String name, String endpoint, AuthenticationConfigurer auth) { + return http(name).GET().endpoint(endpoint, auth); } /** - * Convenience for adding an unnamed {@code GET} HTTP task using a {@link URI}. + * Convenience for building an unnamed {@code GET} HTTP step using a {@link URI}. * * @param endpoint concrete URI to call - * @return a {@link FuncTaskConfigurer} adding a {@code GET} HTTP task + * @return a {@link FuncCallHttpStep} that can be passed to {@link #call(FuncCallHttpStep)} */ - public static FuncTaskConfigurer get(URI endpoint) { + public static FuncCallHttpStep get(URI endpoint) { return get(null, endpoint); } /** - * Convenience for adding a named {@code GET} HTTP task using a {@link URI}. + * Convenience for building a named {@code GET} HTTP step using a {@link URI}. * * @param name task name * @param endpoint concrete URI to call - * @return a {@link FuncTaskConfigurer} adding a {@code GET} HTTP task + * @return a named {@link FuncCallHttpStep} that can be passed to {@link #call(FuncCallHttpStep)} */ - public static FuncTaskConfigurer get(String name, URI endpoint) { - return call(name, http().GET().uri(endpoint)); + public static FuncCallHttpStep get(String name, URI endpoint) { + return http(name).GET().uri(endpoint); } /** - * Convenience for adding an unnamed authenticated {@code GET} HTTP task using a {@link URI}. + * Convenience for building an unnamed authenticated {@code GET} HTTP step using a {@link URI}. * * @param endpoint concrete URI to call * @param auth authentication configurer - * @return a {@link FuncTaskConfigurer} adding a {@code GET} HTTP task + * @return a {@link FuncCallHttpStep} that can be passed to {@link #call(FuncCallHttpStep)} */ - public static FuncTaskConfigurer get(URI endpoint, AuthenticationConfigurer auth) { + public static FuncCallHttpStep get(URI endpoint, AuthenticationConfigurer auth) { return get(null, endpoint, auth); } /** - * Convenience for adding a named authenticated {@code GET} HTTP task using a {@link URI}. + * Convenience for building a named authenticated {@code GET} HTTP step using a {@link URI}. * * @param name task name * @param endpoint concrete URI to call * @param auth authentication configurer - * @return a {@link FuncTaskConfigurer} adding a {@code GET} HTTP task + * @return a named {@link FuncCallHttpStep} that can be passed to {@link #call(FuncCallHttpStep)} */ - public static FuncTaskConfigurer get(String name, URI endpoint, AuthenticationConfigurer auth) { - return call(name, http().GET().uri(endpoint, auth)); + public static FuncCallHttpStep get(String name, URI endpoint, AuthenticationConfigurer auth) { + return http(name).GET().uri(endpoint, auth); } /** - * Convenience for adding an unnamed {@code POST} HTTP task with a body and string endpoint. + * Convenience for building an unnamed {@code POST} HTTP step with a body and string endpoint. * *
{@code
    * tasks(
-   *   FuncDSL.post(
-   *     Map.of("name", "Ricardo"),
-   *     "http://service/api/users"
+   *   FuncDSL.call(
+   *     FuncDSL.post(
+   *       Map.of("name", "Ricardo"),
+   *       "http://service/api/users"
+   *     )
    *   )
    * );
    * }
* * @param body HTTP request body (literal value or expression-compatible object) * @param endpointExpr literal or expression for the endpoint URL - * @return a {@link FuncTaskConfigurer} adding a {@code POST} HTTP task + * @return a {@link FuncCallHttpStep} that can be passed to {@link #call(FuncCallHttpStep)} */ - public static FuncTaskConfigurer post(Object body, String endpointExpr) { + public static FuncCallHttpStep post(Object body, String endpointExpr) { return post(null, body, endpointExpr); } /** - * Convenience for adding a named {@code POST} HTTP task with a body and string endpoint. + * Convenience for building a named {@code POST} HTTP step with a body and string endpoint. * * @param name task name * @param body HTTP request body (literal value or expression-compatible object) * @param endpoint literal or expression for the endpoint URL - * @return a {@link FuncTaskConfigurer} adding a {@code POST} HTTP task + * @return a named {@link FuncCallHttpStep} that can be passed to {@link #call(FuncCallHttpStep)} */ - public static FuncTaskConfigurer post(String name, Object body, String endpoint) { - return call(name, http().POST().endpoint(endpoint).body(body)); + public static FuncCallHttpStep post(String name, Object body, String endpoint) { + return http(name).POST().endpoint(endpoint).body(body); } /** - * Convenience for adding an unnamed authenticated {@code POST} HTTP task with body and endpoint. + * Convenience for building an unnamed authenticated {@code POST} HTTP step with body and + * endpoint. * * @param body HTTP request body * @param endpoint literal or expression for the endpoint URL * @param auth authentication configurer - * @return a {@link FuncTaskConfigurer} adding an authenticated {@code POST} HTTP task + * @return a {@link FuncCallHttpStep} that can be passed to {@link #call(FuncCallHttpStep)} */ - public static FuncTaskConfigurer post( - Object body, String endpoint, AuthenticationConfigurer auth) { + public static FuncCallHttpStep post(Object body, String endpoint, AuthenticationConfigurer auth) { return post(null, body, endpoint, auth); } /** - * Convenience for adding a named authenticated {@code POST} HTTP task with body and endpoint. + * Convenience for building a named authenticated {@code POST} HTTP step with body and endpoint. * * @param name task name * @param body HTTP request body * @param endpoint literal or expression for the endpoint URL * @param auth authentication configurer - * @return a {@link FuncTaskConfigurer} adding an authenticated {@code POST} HTTP task + * @return a named {@link FuncCallHttpStep} that can be passed to {@link #call(FuncCallHttpStep)} */ - public static FuncTaskConfigurer post( + public static FuncCallHttpStep post( String name, Object body, String endpoint, AuthenticationConfigurer auth) { - return call(name, http().POST().endpoint(endpoint, auth).body(body)); + return http(name).POST().endpoint(endpoint, auth).body(body); } } 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 9ce49b97..6e302126 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 @@ -37,7 +37,7 @@ abstract class Step, B> implements FuncTaskConfigurer private final List> postConfigurers = new ArrayList<>(); @SuppressWarnings("unchecked") - protected final SELF self() { + protected SELF self() { return (SELF) this; } 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 2425eb6d..c89ad019 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 @@ -15,10 +15,12 @@ */ package io.serverlessworkflow.fluent.func; +import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.call; import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.emit; import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.event; import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.function; import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.get; +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.toOne; import static io.serverlessworkflow.fluent.spec.dsl.DSL.auth; @@ -213,8 +215,7 @@ void switchWhenOrElse_jq_to_directive() { void http_spec_via_call_builds_call_http_task() { Workflow wf = FuncWorkflowBuilder.workflow("http-call-spec") - .tasks( - FuncDSL.call("checkHealth", FuncDSL.http().GET().endpoint("http://service/health"))) + .tasks(call("checkHealth", http().GET().endpoint("http://service/health"))) .build(); List items = wf.getDo(); @@ -401,9 +402,9 @@ void call_with_preconfigured_http_spec() { Workflow wf = FuncWorkflowBuilder.workflow("http-call-preconfigured") .tasks( - FuncDSL.call( + call( "preconfigured", - FuncDSL.http("http://service/api", auth -> auth.use("svc-auth")) + http("http://service/api", auth -> auth.use("svc-auth")) .POST() .body(Map.of("foo", "bar")))) .build(); diff --git a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/dsl/BaseCallHttpSpec.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/dsl/BaseCallHttpSpec.java index 1348a0a3..ece52bca 100644 --- a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/dsl/BaseCallHttpSpec.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/dsl/BaseCallHttpSpec.java @@ -18,92 +18,98 @@ import io.serverlessworkflow.fluent.spec.configurers.AuthenticationConfigurer; import io.serverlessworkflow.fluent.spec.spi.CallHttpTaskFluent; import java.net.URI; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Consumer; -public abstract class BaseCallHttpSpec> { +public interface BaseCallHttpSpec> { - private final List>> steps = new ArrayList<>(); + /** Implementors must return the concrete SELF instance (usually {@code return this;}). */ + SELF self(); - protected abstract SELF self(); + /** + * Internal list of configuration steps that will be replayed against the underlying {@link + * CallHttpTaskFluent}. + */ + List>> steps(); - public SELF GET() { - steps.add(c -> c.method("GET")); + default SELF GET() { + steps().add(c -> c.method("GET")); return self(); } - public SELF POST() { - steps.add(c -> c.method("POST")); + default SELF POST() { + steps().add(c -> c.method("POST")); return self(); } - public SELF acceptJSON() { + default SELF acceptJSON() { return header("Accept", "application/json"); } - public SELF endpoint(String urlExpr) { - steps.add(b -> b.endpoint(urlExpr)); + default SELF endpoint(String urlExpr) { + steps().add(b -> b.endpoint(urlExpr)); return self(); } - public SELF endpoint(String urlExpr, AuthenticationConfigurer auth) { - steps.add(b -> b.endpoint(urlExpr, auth)); + default SELF endpoint(String urlExpr, AuthenticationConfigurer auth) { + steps().add(b -> b.endpoint(urlExpr, auth)); return self(); } - public SELF uri(String url) { - steps.add(b -> b.endpoint(URI.create(url))); + default SELF uri(String url) { + steps().add(b -> b.endpoint(URI.create(url))); return self(); } - public SELF uri(String url, AuthenticationConfigurer auth) { - steps.add(b -> b.endpoint(URI.create(url), auth)); + default SELF uri(String url, AuthenticationConfigurer auth) { + steps().add(b -> b.endpoint(URI.create(url), auth)); return self(); } - public SELF uri(URI uri) { - steps.add(b -> b.endpoint(uri)); + default SELF uri(URI uri) { + steps().add(b -> b.endpoint(uri)); return self(); } - public SELF uri(URI uri, AuthenticationConfigurer auth) { - steps.add(b -> b.endpoint(uri, auth)); + default SELF uri(URI uri, AuthenticationConfigurer auth) { + steps().add(b -> b.endpoint(uri, auth)); return self(); } - public SELF body(String bodyExpr) { - steps.add(c -> c.body(bodyExpr)); + default SELF body(String bodyExpr) { + steps().add(c -> c.body(bodyExpr)); return self(); } - public SELF body(Map body) { - steps.add(c -> c.body(body)); + default SELF body(Map body) { + steps().add(c -> c.body(body)); return self(); } - public SELF body(Object bodyExpr) { - steps.add(c -> c.body(bodyExpr)); + default SELF body(Object bodyExpr) { + steps().add(c -> c.body(bodyExpr)); return self(); } - public SELF method(String method) { - steps.add(b -> b.method(method)); + default SELF method(String method) { + steps().add(b -> b.method(method)); return self(); } - public SELF header(String name, String value) { - steps.add(c -> c.headers(h -> h.header(name, value))); + default SELF header(String name, String value) { + steps().add(c -> c.headers(h -> h.header(name, value))); return self(); } - public SELF headers(Map headers) { - steps.add(b -> b.headers(headers)); + default SELF headers(Map headers) { + steps().add(b -> b.headers(headers)); return self(); } - public void accept(CallHttpTaskFluent b) { - for (var s : steps) s.accept(b); + default void accept(CallHttpTaskFluent b) { + for (var s : steps()) { + s.accept(b); + } } } diff --git a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/dsl/CallHttpSpec.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/dsl/CallHttpSpec.java index 4c9b85d3..0cc53fa2 100644 --- a/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/dsl/CallHttpSpec.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/dsl/CallHttpSpec.java @@ -17,17 +17,29 @@ import io.serverlessworkflow.fluent.spec.CallHttpTaskBuilder; import io.serverlessworkflow.fluent.spec.configurers.CallHttpConfigurer; +import io.serverlessworkflow.fluent.spec.spi.CallHttpTaskFluent; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; -public final class CallHttpSpec extends BaseCallHttpSpec - implements CallHttpConfigurer { +public final class CallHttpSpec implements BaseCallHttpSpec, CallHttpConfigurer { + + private final List>> steps = new ArrayList<>(); + + public CallHttpSpec() {} @Override - protected CallHttpSpec self() { + public CallHttpSpec self() { return this; } @Override - public void accept(CallHttpTaskBuilder callHttpTaskBuilder) { - super.accept(callHttpTaskBuilder); + public List>> steps() { + return steps; + } + + @Override + public void accept(CallHttpTaskBuilder builder) { + BaseCallHttpSpec.super.accept(builder); } }