From 21478f9a47dc57cda9de2874925fa987b95a0fdd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 15:27:16 -0400 Subject: [PATCH 01/30] Bump com.networknt:json-schema-validator from 1.5.7 to 1.5.8 (#623) Bumps [com.networknt:json-schema-validator](https://github.com/networknt/json-schema-validator) from 1.5.7 to 1.5.8. - [Release notes](https://github.com/networknt/json-schema-validator/releases) - [Changelog](https://github.com/networknt/json-schema-validator/blob/master/CHANGELOG.md) - [Commits](https://github.com/networknt/json-schema-validator/compare/1.5.7...1.5.8) --- updated-dependencies: - dependency-name: com.networknt:json-schema-validator dependency-version: 1.5.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bf470fa0..1f1e72c1 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ 1.5.18 2.19.1 - 1.5.7 + 1.5.8 3.1.1 1.5.2 3.27.3 From 70a46af33a71f87ac37015dfe6a346ae9f98d6d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 16:15:00 +0000 Subject: [PATCH 02/30] Bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.7 to 3.2.8 Bumps [org.apache.maven.plugins:maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 3.2.7 to 3.2.8. - [Release notes](https://github.com/apache/maven-gpg-plugin/releases) - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.2.7...maven-gpg-plugin-3.2.8) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-version: 3.2.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1f1e72c1..d86d9b6d 100644 --- a/pom.xml +++ b/pom.xml @@ -57,7 +57,7 @@ 3.5.0 3.5.3 2.27 - 3.2.7 + 3.2.8 3.4.2 ${java.version} 1.2.2 From 7f1475e211fc3568aeb3e110e6c8a83788b53543 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 15:49:45 +0000 Subject: [PATCH 03/30] Bump org.apache.maven.plugins:maven-enforcer-plugin from 3.5.0 to 3.6.0 Bumps [org.apache.maven.plugins:maven-enforcer-plugin](https://github.com/apache/maven-enforcer) from 3.5.0 to 3.6.0. - [Release notes](https://github.com/apache/maven-enforcer/releases) - [Commits](https://github.com/apache/maven-enforcer/compare/enforcer-3.5.0...enforcer-3.6.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-enforcer-plugin dependency-version: 3.6.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d86d9b6d..5d42e813 100644 --- a/pom.xml +++ b/pom.xml @@ -54,7 +54,7 @@ 3.6.0 3.14.0 3.1.4 - 3.5.0 + 3.6.0 3.5.3 2.27 3.2.8 From 9d635fa9fd9a065187138bbd76724a77ae90a927 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 16:25:07 +0000 Subject: [PATCH 04/30] Bump version.org.junit.jupiter from 5.13.2 to 5.13.3 Bumps `version.org.junit.jupiter` from 5.13.2 to 5.13.3. Updates `org.junit.jupiter:junit-jupiter-api` from 5.13.2 to 5.13.3 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r5.13.2...r5.13.3) Updates `org.junit.jupiter:junit-jupiter-engine` from 5.13.2 to 5.13.3 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r5.13.2...r5.13.3) Updates `org.junit.jupiter:junit-jupiter-params` from 5.13.2 to 5.13.3 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r5.13.2...r5.13.3) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-version: 5.13.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-version: 5.13.3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.junit.jupiter:junit-jupiter-params dependency-version: 5.13.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5d42e813..a1d961ad 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ 3.1.1 1.5.2 3.27.3 - 5.13.2 + 5.13.3 5.18.0 2.0.17 9.0.1.Final From a1a86f622c1b9d161ad52f5d6a02c676c7724b95 Mon Sep 17 00:00:00 2001 From: fjtirado Date: Mon, 7 Jul 2025 21:11:31 +0200 Subject: [PATCH 05/30] Add javadoc to serverlessworkflow api Signed-off-by: fjtirado --- .../api/WorkflowFormat.java | 32 +++++ .../api/WorkflowReader.java | 115 ++++++++++++++++++ .../api/WorkflowWriter.java | 56 +++++++++ 3 files changed, 203 insertions(+) diff --git a/api/src/main/java/io/serverlessworkflow/api/WorkflowFormat.java b/api/src/main/java/io/serverlessworkflow/api/WorkflowFormat.java index d0cdfd95..7ca2cc27 100644 --- a/api/src/main/java/io/serverlessworkflow/api/WorkflowFormat.java +++ b/api/src/main/java/io/serverlessworkflow/api/WorkflowFormat.java @@ -18,24 +18,56 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.nio.file.Path; +/** + * Enum representing the supported formats for Serverless Workflow definitions. + * + *

Provides utility methods to determine the format based on file name or path, and to access the + * corresponding {@link ObjectMapper} for serialization and deserialization. + */ public enum WorkflowFormat { + /** JSON format for workflow definitions. */ JSON(ObjectMapperFactory.jsonMapper()), + + /** YAML format for workflow definitions. */ YAML(ObjectMapperFactory.yamlMapper()); private final ObjectMapper mapper; + /** + * Determines the {@link WorkflowFormat} from a file path by inspecting its file extension. + * + * @param path the file path to inspect + * @return the corresponding {@link WorkflowFormat} + */ public static WorkflowFormat fromPath(Path path) { return fromFileName(path.getFileName().toString()); } + /** + * Determines the {@link WorkflowFormat} from a file name by inspecting its extension. Returns + * {@code JSON} if the file name ends with ".json", otherwise returns {@code YAML}. + * + * @param fileName the file name to inspect + * @return the corresponding {@link WorkflowFormat} + */ public static WorkflowFormat fromFileName(String fileName) { return fileName.endsWith(".json") ? JSON : YAML; } + /** + * Constructs a {@link WorkflowFormat} with the specified {@link ObjectMapper}. + * + * @param mapper the object mapper for this format + */ private WorkflowFormat(ObjectMapper mapper) { this.mapper = mapper; } + /** + * Returns the {@link ObjectMapper} associated with this workflow format. + * + * @return the object mapper for this format + */ public ObjectMapper mapper() { return mapper; } diff --git a/api/src/main/java/io/serverlessworkflow/api/WorkflowReader.java b/api/src/main/java/io/serverlessworkflow/api/WorkflowReader.java index 6868a6dc..b4401af0 100644 --- a/api/src/main/java/io/serverlessworkflow/api/WorkflowReader.java +++ b/api/src/main/java/io/serverlessworkflow/api/WorkflowReader.java @@ -23,52 +23,141 @@ import java.nio.file.Files; import java.nio.file.Path; +/** Utility class for reading and parsing Serverless Workflow definitions from various sources. */ public class WorkflowReader { + /** + * Reads a workflow from an {@link InputStream} using the specified format. + * + * @param input the input stream containing the workflow definition + * @param format the workflow format + * @return the parsed {@link Workflow} + * @throws IOException if an I/O error occurs + */ public static Workflow readWorkflow(InputStream input, WorkflowFormat format) throws IOException { return defaultReader().read(input, format); } + /** + * Reads a workflow from a {@link Reader} using the specified format. + * + * @param input the reader containing the workflow definition + * @param format the workflow format + * @return the parsed {@link Workflow} + * @throws IOException if an I/O error occurs + */ public static Workflow readWorkflow(Reader input, WorkflowFormat format) throws IOException { return defaultReader().read(input, format); } + /** + * Reads a workflow from a byte array using the specified format. + * + * @param input the byte array containing the workflow definition + * @param format the workflow format + * @return the parsed {@link Workflow} + * @throws IOException if an I/O error occurs + */ public static Workflow readWorkflow(byte[] input, WorkflowFormat format) throws IOException { return defaultReader().read(input, format); } + /** + * Reads a workflow from a file path, inferring the format from the file extension. + * + * @param path the path to the workflow file + * @return the parsed {@link Workflow} + * @throws IOException if an I/O error occurs + */ public static Workflow readWorkflow(Path path) throws IOException { return readWorkflow(path, WorkflowFormat.fromPath(path), defaultReader()); } + /** + * Reads a workflow from a file path using the specified format. + * + * @param path the path to the workflow file + * @param format the workflow format + * @return the parsed {@link Workflow} + * @throws IOException if an I/O error occurs + */ public static Workflow readWorkflow(Path path, WorkflowFormat format) throws IOException { return readWorkflow(path, format, defaultReader()); } + /** + * Reads a workflow from a string using the specified format. + * + * @param input the string containing the workflow definition + * @param format the workflow format + * @return the parsed {@link Workflow} + * @throws IOException if an I/O error occurs + */ public static Workflow readWorkflowFromString(String input, WorkflowFormat format) throws IOException { return defaultReader().read(input, format); } + /** + * Reads a workflow from the classpath, inferring the format from the file name. + * + * @param classpath the classpath location of the workflow file + * @return the parsed {@link Workflow} + * @throws IOException if an I/O error occurs + */ public static Workflow readWorkflowFromClasspath(String classpath) throws IOException { return readWorkflowFromClasspath(classpath, defaultReader()); } + /** + * Reads a workflow from the classpath using the specified class loader and format. + * + * @param classpath the classpath location of the workflow file + * @param cl the class loader to use + * @param format the workflow format + * @return the parsed {@link Workflow} + * @throws IOException if an I/O error occurs + */ public static Workflow readWorkflowFromClasspath( String classpath, ClassLoader cl, WorkflowFormat format) throws IOException { return readWorkflowFromClasspath(classpath, defaultReader()); } + /** + * Reads a workflow from a file path using a custom reader. + * + * @param path the path to the workflow file + * @param reader the custom {@link WorkflowReaderOperations} + * @return the parsed {@link Workflow} + * @throws IOException if an I/O error occurs + */ public static Workflow readWorkflow(Path path, WorkflowReaderOperations reader) throws IOException { return readWorkflow(path, WorkflowFormat.fromPath(path), reader); } + /** + * Reads a workflow from a file path using the specified format and custom reader. + * + * @param path the path to the workflow file + * @param format the workflow format + * @param reader the custom {@link WorkflowReaderOperations} + * @return the parsed {@link Workflow} + * @throws IOException if an I/O error occurs + */ public static Workflow readWorkflow( Path path, WorkflowFormat format, WorkflowReaderOperations reader) throws IOException { return reader.read(Files.readAllBytes(path), format); } + /** + * Reads a workflow from the classpath using a custom reader. + * + * @param classpath the classpath location of the workflow file + * @param reader the custom {@link WorkflowReaderOperations} + * @return the parsed {@link Workflow} + * @throws IOException if an I/O error occurs + */ public static Workflow readWorkflowFromClasspath( String classpath, WorkflowReaderOperations reader) throws IOException { return readWorkflowFromClasspath( @@ -78,6 +167,17 @@ public static Workflow readWorkflowFromClasspath( reader); } + /** + * Reads a workflow from the classpath using the specified class loader, format, and custom + * reader. + * + * @param classpath the classpath location of the workflow file + * @param cl the class loader to use + * @param format the workflow format + * @param reader the custom {@link WorkflowReaderOperations} + * @return the parsed {@link Workflow} + * @throws IOException if an I/O error occurs or the resource is not found + */ public static Workflow readWorkflowFromClasspath( String classpath, ClassLoader cl, WorkflowFormat format, WorkflowReaderOperations reader) throws IOException { @@ -89,10 +189,20 @@ public static Workflow readWorkflowFromClasspath( } } + /** + * Returns a {@link WorkflowReaderOperations} instance that performs no validation. + * + * @return a no-validation reader + */ public static WorkflowReaderOperations noValidation() { return NoValidationHolder.instance; } + /** + * Returns a {@link WorkflowReaderOperations} instance that performs validation. + * + * @return a validation reader + */ public static WorkflowReaderOperations validation() { return ValidationHolder.instance; } @@ -105,6 +215,11 @@ private static class ValidationHolder { private static final WorkflowReaderOperations instance = new ValidationReader(); } + /** + * Returns the default {@link WorkflowReaderOperations} instance (no validation). + * + * @return the default reader + */ private static WorkflowReaderOperations defaultReader() { return NoValidationHolder.instance; } diff --git a/api/src/main/java/io/serverlessworkflow/api/WorkflowWriter.java b/api/src/main/java/io/serverlessworkflow/api/WorkflowWriter.java index 5980dee6..3285cff4 100644 --- a/api/src/main/java/io/serverlessworkflow/api/WorkflowWriter.java +++ b/api/src/main/java/io/serverlessworkflow/api/WorkflowWriter.java @@ -23,22 +23,61 @@ import java.nio.file.Files; import java.nio.file.Path; +/** + * Utility class for writing Serverless Workflow definitions to various outputs and formats. + * + *

This class provides static methods to serialize {@link Workflow} objects to files, streams, + * writers, byte arrays, or strings in either JSON or YAML format. The format is determined by the + * {@link WorkflowFormat} parameter or inferred from file extensions. + */ public class WorkflowWriter { + /** + * Writes a {@link Workflow} to the given {@link OutputStream} in the specified format. + * + * @param output the output stream to write the workflow to + * @param workflow the workflow object to serialize + * @param format the format to use (JSON or YAML) + * @throws IOException if an I/O error occurs during writing + */ public static void writeWorkflow(OutputStream output, Workflow workflow, WorkflowFormat format) throws IOException { format.mapper().writeValue(output, workflow); } + /** + * Writes a {@link Workflow} to the given {@link Writer} in the specified format. + * + * @param output the writer to write the workflow to + * @param workflow the workflow object to serialize + * @param format the format to use (JSON or YAML) + * @throws IOException if an I/O error occurs during writing + */ public static void writeWorkflow(Writer output, Workflow workflow, WorkflowFormat format) throws IOException { format.mapper().writeValue(output, workflow); } + /** + * Writes a {@link Workflow} to the specified file path, inferring the format from the file + * extension. + * + * @param output the file path to write the workflow to + * @param workflow the workflow object to serialize + * @throws IOException if an I/O error occurs during writing + */ public static void writeWorkflow(Path output, Workflow workflow) throws IOException { writeWorkflow(output, workflow, WorkflowFormat.fromPath(output)); } + /** + * Writes a {@link Workflow} to the specified file path in the given format. + * + * @param output the file path to write the workflow to + * @param workflow the workflow object to serialize + * @param format the format to use (JSON or YAML) + * @throws IOException if an I/O error occurs during writing + */ public static void writeWorkflow(Path output, Workflow workflow, WorkflowFormat format) throws IOException { try (OutputStream out = Files.newOutputStream(output)) { @@ -46,11 +85,27 @@ public static void writeWorkflow(Path output, Workflow workflow, WorkflowFormat } } + /** + * Serializes a {@link Workflow} to a string in the specified format. + * + * @param workflow the workflow object to serialize + * @param format the format to use (JSON or YAML) + * @return the serialized workflow as a string + * @throws IOException if an error occurs during serialization + */ public static String workflowAsString(Workflow workflow, WorkflowFormat format) throws IOException { return format.mapper().writeValueAsString(workflow); } + /** + * Serializes a {@link Workflow} to a byte array in the specified format. + * + * @param workflow the workflow object to serialize + * @param format the format to use (JSON or YAML) + * @return the serialized workflow as a byte array + * @throws IOException if an error occurs during serialization + */ public static byte[] workflowAsBytes(Workflow workflow, WorkflowFormat format) throws IOException { try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { @@ -59,5 +114,6 @@ public static byte[] workflowAsBytes(Workflow workflow, WorkflowFormat format) } } + // Private constructor to prevent instantiation private WorkflowWriter() {} } From 6dc4a724084510cabc074f3b9ed113a2f75c3f91 Mon Sep 17 00:00:00 2001 From: fjtirado Date: Thu, 10 Jul 2025 18:31:14 +0200 Subject: [PATCH 06/30] [Fix #634] Refactoring modules Signed-off-by: fjtirado --- api/pom.xml | 70 ++----------------- custom-generator/pom.xml | 6 ++ .../generator/AllAnyOneOfSchemaRule.java | 17 +++-- .../generator/GeneratorUtils.java | 9 --- .../generator/UnevaluatedPropertiesRule.java | 3 +- pom.xml | 2 + serverlessworkflow-annotations/pom.xml | 20 ++++++ .../annotations}/OneOfSetter.java | 2 +- .../annotations}/OneOfValueProvider.java | 2 +- .../serialization/DeserializeHelper.java | 1 + .../serialization/SerializeHelper.java | 4 +- serverlessworkflow-types/pom.xml | 66 +++++++++++++++++ .../src/main/resources/schema/workflow.yaml | 0 13 files changed, 116 insertions(+), 86 deletions(-) create mode 100644 serverlessworkflow-annotations/pom.xml rename {api/src/main/java/io/serverlessworkflow/serialization => serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations}/OneOfSetter.java (95%) rename {api/src/main/java/io/serverlessworkflow/api => serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations}/OneOfValueProvider.java (94%) rename {api => serverlessworkflow-annotations}/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java (98%) rename {api => serverlessworkflow-annotations}/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java (91%) create mode 100644 serverlessworkflow-types/pom.xml rename {api => serverlessworkflow-types}/src/main/resources/schema/workflow.yaml (100%) diff --git a/api/pom.xml b/api/pom.xml index 2b08c827..e2bf61fa 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -14,29 +14,23 @@ - org.slf4j - slf4j-api + io.serverlessworkflow + serverlessworkflow-types + ${project.version} - com.fasterxml.jackson.core - jackson-core + org.slf4j + slf4j-api com.networknt json-schema-validator - - com.fasterxml.jackson.core - jackson-databind - com.fasterxml.jackson.dataformat jackson-dataformat-yaml - - jakarta.validation - jakarta.validation-api - + org.hibernate.validator hibernate-validator @@ -79,54 +73,4 @@ test - - - - - - org.jsonschema2pojo - jsonschema2pojo-maven-plugin - - ${basedir}/src/main/resources/schema - - - yamlschema - io.serverlessworkflow.api.types - ${project.build.directory}/generated-sources/src/main/java - true - true - true - true - false - false - true - true - true - true - ${java.version} - true - true - io.serverlessworkflow.generator.UnreferencedFactory - io.serverlessworkflow.generator.ConstAnnotator - - - - io.serverlessworkflow - serverless-workflow-custom-generator - ${project.version} - - - - - - generate - - generate-sources - - - - - - + diff --git a/custom-generator/pom.xml b/custom-generator/pom.xml index 40bf6b11..55b2215b 100644 --- a/custom-generator/pom.xml +++ b/custom-generator/pom.xml @@ -12,6 +12,12 @@ org.jsonschema2pojo jsonschema2pojo-core + + io.serverlessworkflow + serverlessworkflow-annotations + ${project.version} + + diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java index 28a611cb..6221a560 100644 --- a/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java @@ -33,6 +33,10 @@ import com.sun.codemodel.JPackage; import com.sun.codemodel.JType; import com.sun.codemodel.JVar; +import io.serverlessworkflow.annotations.OneOfSetter; +import io.serverlessworkflow.annotations.OneOfValueProvider; +import io.serverlessworkflow.serialization.DeserializeHelper; +import io.serverlessworkflow.serialization.SerializeHelper; import jakarta.validation.ConstraintViolationException; import java.io.UnsupportedEncodingException; import java.net.URI; @@ -318,10 +322,7 @@ private JDefinedClass populateOneOf( null); definedClass._implements( - definedClass - .owner() - .ref(GeneratorUtils.ONE_OF_VALUE_PROVIDER_INTERFACE_NAME) - .narrow(valueField.type())); + definedClass.owner().ref(OneOfValueProvider.class).narrow(valueField.type())); GeneratorUtils.implementInterface(definedClass, valueField); try { JDefinedClass serializer = generateSerializer(definedClass); @@ -397,9 +398,7 @@ private JDefinedClass generateSerializer(JDefinedClass relatedClass) (method, valueParam, genParam) -> method .body() - .staticInvoke( - definedClass.owner().ref(GeneratorUtils.SERIALIZE_HELPER_NAME), - "serializeOneOf") + .staticInvoke(definedClass.owner().ref(SerializeHelper.class), "serializeOneOf") .arg(genParam) .arg(valueParam)); return definedClass; @@ -418,7 +417,7 @@ private JDefinedClass generateDeserializer( body._return( definedClass .owner() - .ref(GeneratorUtils.DESERIALIZE_HELPER_NAME) + .ref(DeserializeHelper.class) .staticInvoke(methodName) .arg(parserParam) .arg(relatedClass.dotclass()) @@ -460,7 +459,7 @@ private JVar setupMethod( v -> { method.body().assign(JExpr._this().ref(v), methodParam); method - .annotate(definedClass.owner().ref(GeneratorUtils.SETTER_ANNOTATION_NAME)) + .annotate(definedClass.owner().ref(OneOfSetter.class)) .param("value", instanceField.type()); }); return methodParam; diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java index e7af60d5..b98bd7be 100644 --- a/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java @@ -32,15 +32,6 @@ public class GeneratorUtils { - public static final String SERIALIZE_HELPER_NAME = - "io.serverlessworkflow.serialization.SerializeHelper"; - public static final String DESERIALIZE_HELPER_NAME = - "io.serverlessworkflow.serialization.DeserializeHelper"; - public static final String ONE_OF_VALUE_PROVIDER_INTERFACE_NAME = - "io.serverlessworkflow.api.OneOfValueProvider"; - public static final String SETTER_ANNOTATION_NAME = - "io.serverlessworkflow.serialization.OneOfSetter"; - @FunctionalInterface public interface SerializerFiller { void accept(JMethod method, JVar valueParam, JVar genParam); diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java index 18bdbba6..533db957 100644 --- a/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java @@ -27,6 +27,7 @@ import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; import com.sun.codemodel.JType; +import io.serverlessworkflow.serialization.DeserializeHelper; import org.jsonschema2pojo.Schema; import org.jsonschema2pojo.rules.AdditionalPropertiesRule; import org.jsonschema2pojo.rules.Rule; @@ -125,7 +126,7 @@ private JDefinedClass generateDeserializer(JDefinedClass relatedClass, JType pro ._return( definedClass .owner() - .ref(GeneratorUtils.DESERIALIZE_HELPER_NAME) + .ref(DeserializeHelper.class) .staticInvoke("deserializeItem") .arg(parserParam) .arg(relatedClass.dotclass()) diff --git a/pom.xml b/pom.xml index a1d961ad..271e10da 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,8 @@ api custom-generator impl + serverlessworkflow-types + serverlessworkflow-annotations diff --git a/serverlessworkflow-annotations/pom.xml b/serverlessworkflow-annotations/pom.xml new file mode 100644 index 00000000..e37aa671 --- /dev/null +++ b/serverlessworkflow-annotations/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + io.serverlessworkflow + serverlessworkflow-parent + 8.0.0-SNAPSHOT + + Serverless Workflow :: Annotations + serverlessworkflow-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + jakarta.validation + jakarta.validation-api + + + \ No newline at end of file diff --git a/api/src/main/java/io/serverlessworkflow/serialization/OneOfSetter.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/OneOfSetter.java similarity index 95% rename from api/src/main/java/io/serverlessworkflow/serialization/OneOfSetter.java rename to serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/OneOfSetter.java index 098df425..d67f0292 100644 --- a/api/src/main/java/io/serverlessworkflow/serialization/OneOfSetter.java +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/OneOfSetter.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.serialization; +package io.serverlessworkflow.annotations; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/api/src/main/java/io/serverlessworkflow/api/OneOfValueProvider.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/OneOfValueProvider.java similarity index 94% rename from api/src/main/java/io/serverlessworkflow/api/OneOfValueProvider.java rename to serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/OneOfValueProvider.java index 9d17b872..d275ff9d 100644 --- a/api/src/main/java/io/serverlessworkflow/api/OneOfValueProvider.java +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/OneOfValueProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.api; +package io.serverlessworkflow.annotations; public interface OneOfValueProvider { T get(); diff --git a/api/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java similarity index 98% rename from api/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java rename to serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java index cfbd54ca..041474d9 100644 --- a/api/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.TreeNode; import com.fasterxml.jackson.databind.JsonMappingException; +import io.serverlessworkflow.annotations.OneOfSetter; import jakarta.validation.ConstraintViolationException; import java.io.IOException; import java.lang.reflect.InvocationTargetException; diff --git a/api/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java similarity index 91% rename from api/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java rename to serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java index e074a656..47ab1a5e 100644 --- a/api/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java @@ -16,11 +16,11 @@ package io.serverlessworkflow.serialization; import com.fasterxml.jackson.core.JsonGenerator; -import io.serverlessworkflow.api.OneOfValueProvider; +import io.serverlessworkflow.annotations.OneOfValueProvider; import java.io.IOException; public class SerializeHelper { - public static void serializeOneOf(JsonGenerator jgen, OneOfValueProvider item) + public static void serializeOneOf(JsonGenerator jgen, OneOfValueProvider item) throws IOException { jgen.writeObject(item.get()); } diff --git a/serverlessworkflow-types/pom.xml b/serverlessworkflow-types/pom.xml new file mode 100644 index 00000000..4fe9f9d2 --- /dev/null +++ b/serverlessworkflow-types/pom.xml @@ -0,0 +1,66 @@ + + 4.0.0 + + io.serverlessworkflow + serverlessworkflow-parent + 8.0.0-SNAPSHOT + + Serverless Workflow :: Types + serverlessworkflow-types + + + io.serverlessworkflow + serverlessworkflow-annotations + ${project.version} + + + + + + org.jsonschema2pojo + jsonschema2pojo-maven-plugin + + ${basedir}/src/main/resources/schema + + + yamlschema + io.serverlessworkflow.api.types + ${project.build.directory}/generated-sources/src/main/java + true + true + true + true + false + false + true + true + true + true + ${java.version} + true + jackson2 + true + io.serverlessworkflow.generator.UnreferencedFactory + io.serverlessworkflow.generator.ConstAnnotator + + + + io.serverlessworkflow + serverless-workflow-custom-generator + ${project.version} + + + + + + generate + + generate-sources + + + + + + \ No newline at end of file diff --git a/api/src/main/resources/schema/workflow.yaml b/serverlessworkflow-types/src/main/resources/schema/workflow.yaml similarity index 100% rename from api/src/main/resources/schema/workflow.yaml rename to serverlessworkflow-types/src/main/resources/schema/workflow.yaml From 97d2b8927dbfe41d2b20d500def25ea4b85a0cdc Mon Sep 17 00:00:00 2001 From: fjtirado Date: Thu, 10 Jul 2025 20:01:27 +0200 Subject: [PATCH 07/30] [Fix #634] Setting up maven plugin Signed-off-by: fjtirado --- api/pom.xml | 47 ++++ .../api/ObjectMapperFactory.java | 6 +- .../generator/AllAnyOneOfSchemaRule.java | 64 +---- ...nstAnnotator.java => CustomAnnotator.java} | 10 +- .../generator/GeneratorUtils.java | 61 +---- .../generator/UnevaluatedPropertiesRule.java | 56 +--- jackson-generator/pom.xml | 80 ++++++ .../generator/jackson/GeneratorUtils.java | 172 +++++++++++++ .../generator/jackson/JacksonMixInPojo.java | 243 ++++++++++++++++++ pom.xml | 2 + serverlessworkflow-annotations/pom.xml | 10 - .../annotations/AdditionalProperties.java | 26 ++ .../annotations/GetterMethod.java | 26 ++ .../serverlessworkflow/annotations/Item.java | 26 ++ .../annotations/ItemKey.java | 26 ++ .../annotations/ItemValue.java | 26 ++ .../serverlessworkflow/annotations/Union.java | 26 ++ serverlessworkflow-serialization/pom.xml | 24 ++ .../serialization/DeserializeHelper.java | 0 .../serialization/SerializeHelper.java | 0 serverlessworkflow-types/pom.xml | 8 +- 21 files changed, 754 insertions(+), 185 deletions(-) rename custom-generator/src/main/java/io/serverlessworkflow/generator/{ConstAnnotator.java => CustomAnnotator.java} (78%) create mode 100644 jackson-generator/pom.xml create mode 100644 jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/GeneratorUtils.java create mode 100644 jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java create mode 100644 serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/AdditionalProperties.java create mode 100644 serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/GetterMethod.java create mode 100644 serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Item.java create mode 100644 serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemKey.java create mode 100644 serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemValue.java create mode 100644 serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Union.java create mode 100644 serverlessworkflow-serialization/pom.xml rename {serverlessworkflow-annotations => serverlessworkflow-serialization}/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java (100%) rename {serverlessworkflow-annotations => serverlessworkflow-serialization}/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java (100%) diff --git a/api/pom.xml b/api/pom.xml index e2bf61fa..19414b85 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -18,6 +18,11 @@ serverlessworkflow-types ${project.version} + + io.serverlessworkflow + serverlessworkflow-serialization + ${project.version} + org.slf4j slf4j-api @@ -73,4 +78,46 @@ test + + + + + io.serverlessworkflow + jackson-generator + ${project.version} + + + io.serverlessworkflow.api.types + + + + + generate + + generate-sources + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.3.0 + + + add-mixin + generate-sources + + add-source + + + + ${project.build.directory}/generated-sources/jacksonmixinpojo + + + + + + + diff --git a/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java b/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java index c8211586..78b1e24e 100644 --- a/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java +++ b/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java @@ -15,11 +15,13 @@ */ package io.serverlessworkflow.api; +import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator.Feature; +import io.serverlessworkflow.api.types.JacksonMixInModule; import io.serverlessworkflow.serialization.BeanDeserializerModifierWithValidation; import io.serverlessworkflow.serialization.URIDeserializer; import io.serverlessworkflow.serialization.URISerializer; @@ -47,10 +49,12 @@ private static ObjectMapper configure(ObjectMapper mapper) { validationModule.setDeserializerModifier(new BeanDeserializerModifierWithValidation()); return mapper + .setSerializationInclusion(Include.NON_NULL) .configure(SerializationFeature.INDENT_OUTPUT, true) .configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false) .configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false) - .registerModule(validationModule); + .registerModule(validationModule) + .registerModule(new JacksonMixInModule()); } private ObjectMapperFactory() {} diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java index 6221a560..32dca0cd 100644 --- a/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java @@ -16,8 +16,6 @@ package io.serverlessworkflow.generator; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.node.ArrayNode; import com.sun.codemodel.JBlock; import com.sun.codemodel.JClass; @@ -35,8 +33,7 @@ import com.sun.codemodel.JVar; import io.serverlessworkflow.annotations.OneOfSetter; import io.serverlessworkflow.annotations.OneOfValueProvider; -import io.serverlessworkflow.serialization.DeserializeHelper; -import io.serverlessworkflow.serialization.SerializeHelper; +import io.serverlessworkflow.annotations.Union; import jakarta.validation.ConstraintViolationException; import java.io.UnsupportedEncodingException; import java.net.URI; @@ -324,21 +321,7 @@ private JDefinedClass populateOneOf( definedClass._implements( definedClass.owner().ref(OneOfValueProvider.class).narrow(valueField.type())); GeneratorUtils.implementInterface(definedClass, valueField); - try { - JDefinedClass serializer = generateSerializer(definedClass); - definedClass.annotate(JsonSerialize.class).param("using", serializer); - } catch (JClassAlreadyExistsException ex) { - // already serialized aware - } - - try { - JDefinedClass deserializer = - generateDeserializer(definedClass, oneOfTypes, "deserializeOneOf"); - definedClass.annotate(JsonDeserialize.class).param("using", deserializer); - } catch (JClassAlreadyExistsException ex) { - // already deserialized aware - } - + definedClass.annotate(Union.class); return wrapAll(parentSchema, definedClass, commonType, oneOfTypes, Optional.of(valueField)); } @@ -389,49 +372,6 @@ private static boolean isStringType(JType type) { return type.name().equals("String"); } - private JDefinedClass generateSerializer(JDefinedClass relatedClass) - throws JClassAlreadyExistsException { - JDefinedClass definedClass = GeneratorUtils.serializerClass(relatedClass); - GeneratorUtils.fillSerializer( - definedClass, - relatedClass, - (method, valueParam, genParam) -> - method - .body() - .staticInvoke(definedClass.owner().ref(SerializeHelper.class), "serializeOneOf") - .arg(genParam) - .arg(valueParam)); - return definedClass; - } - - private JDefinedClass generateDeserializer( - JDefinedClass relatedClass, Collection oneOfTypes, String methodName) - throws JClassAlreadyExistsException { - JDefinedClass definedClass = GeneratorUtils.deserializerClass(relatedClass); - GeneratorUtils.fillDeserializer( - definedClass, - relatedClass, - (method, parserParam) -> { - JBlock body = method.body(); - - body._return( - definedClass - .owner() - .ref(DeserializeHelper.class) - .staticInvoke(methodName) - .arg(parserParam) - .arg(relatedClass.dotclass()) - .arg(list(definedClass, oneOfTypes))); - }); - return definedClass; - } - - private JInvocation list(JDefinedClass definedClass, Collection list) { - JInvocation result = definedClass.owner().ref(List.class).staticInvoke("of"); - list.forEach(c -> result.arg(((JClass) c.getType()).dotclass())); - return result; - } - private void wrapIt( Schema parentSchema, JDefinedClass definedClass, diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/ConstAnnotator.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/CustomAnnotator.java similarity index 78% rename from custom-generator/src/main/java/io/serverlessworkflow/generator/ConstAnnotator.java rename to custom-generator/src/main/java/io/serverlessworkflow/generator/CustomAnnotator.java index a893cfb5..657ba3ce 100644 --- a/custom-generator/src/main/java/io/serverlessworkflow/generator/ConstAnnotator.java +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/CustomAnnotator.java @@ -18,18 +18,24 @@ import com.fasterxml.jackson.databind.JsonNode; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JFieldVar; +import io.serverlessworkflow.annotations.AdditionalProperties; import jakarta.validation.constraints.Pattern; import org.jsonschema2pojo.AbstractAnnotator; import org.jsonschema2pojo.GenerationConfig; -public class ConstAnnotator extends AbstractAnnotator { +public class CustomAnnotator extends AbstractAnnotator { private static final String CONST = "const"; - public ConstAnnotator(GenerationConfig generationConfig) { + public CustomAnnotator(GenerationConfig generationConfig) { super(generationConfig); } + @Override + public void additionalPropertiesField(JFieldVar field, JDefinedClass clazz, String propertyName) { + clazz.annotate(AdditionalProperties.class); + } + @Override public void propertyField( JFieldVar field, JDefinedClass clazz, String propertyName, JsonNode propertyNode) { diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java index b98bd7be..d330321b 100644 --- a/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java @@ -15,43 +15,15 @@ */ package io.serverlessworkflow.generator; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.sun.codemodel.JClassAlreadyExistsException; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JFieldVar; import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; -import com.sun.codemodel.JVar; -import java.io.IOException; +import io.serverlessworkflow.annotations.GetterMethod; import org.jsonschema2pojo.util.NameHelper; public class GeneratorUtils { - @FunctionalInterface - public interface SerializerFiller { - void accept(JMethod method, JVar valueParam, JVar genParam); - } - - @FunctionalInterface - public interface DeserializerFiller { - void accept(JMethod method, JVar parserParam); - } - - public static JDefinedClass serializerClass(JDefinedClass relatedClass) - throws JClassAlreadyExistsException { - return createClass(relatedClass, JsonSerializer.class, "Serializer"); - } - - public static JDefinedClass deserializerClass(JDefinedClass relatedClass) - throws JClassAlreadyExistsException { - return createClass(relatedClass, JsonDeserializer.class, "Deserializer"); - } - public static JMethod implementInterface(JDefinedClass definedClass, JFieldVar valueField) { JMethod method = definedClass.method(JMod.PUBLIC, valueField.type(), "get"); method.annotate(Override.class); @@ -67,38 +39,9 @@ public static JMethod getterMethod( instanceField.type(), nameHelper.getGetterName(name, instanceField.type(), null)); method.body()._return(instanceField); + method.annotate(GetterMethod.class); return method; } - public static void fillSerializer( - JDefinedClass definedClass, JDefinedClass relatedClass, SerializerFiller filler) { - JMethod method = definedClass.method(JMod.PUBLIC, void.class, "serialize"); - method.annotate(Override.class); - method._throws(IOException.class); - JVar valueParam = method.param(relatedClass, "value"); - JVar genParam = method.param(JsonGenerator.class, "gen"); - method.param(SerializerProvider.class, "serializers"); - filler.accept(method, valueParam, genParam); - } - - public static void fillDeserializer( - JDefinedClass definedClass, JDefinedClass relatedClass, DeserializerFiller filler) { - JMethod method = definedClass.method(JMod.PUBLIC, relatedClass, "deserialize"); - method.annotate(Override.class); - method._throws(IOException.class); - JVar parserParam = method.param(JsonParser.class, "parser"); - method.param(DeserializationContext.class, "dctx"); - filler.accept(method, parserParam); - } - - private static JDefinedClass createClass( - JDefinedClass relatedClass, Class serializerClass, String suffix) - throws JClassAlreadyExistsException { - JDefinedClass definedClass = - relatedClass._package()._class(JMod.NONE, relatedClass.name() + suffix); - definedClass._extends(definedClass.owner().ref(serializerClass).narrow(relatedClass)); - return definedClass; - } - private GeneratorUtils() {} } diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java index 533db957..5388a503 100644 --- a/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java @@ -16,10 +16,6 @@ package io.serverlessworkflow.generator; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.sun.codemodel.JBlock; -import com.sun.codemodel.JClass; import com.sun.codemodel.JClassAlreadyExistsException; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JExpr; @@ -27,7 +23,9 @@ import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; import com.sun.codemodel.JType; -import io.serverlessworkflow.serialization.DeserializeHelper; +import io.serverlessworkflow.annotations.Item; +import io.serverlessworkflow.annotations.ItemKey; +import io.serverlessworkflow.annotations.ItemValue; import org.jsonschema2pojo.Schema; import org.jsonschema2pojo.rules.AdditionalPropertiesRule; import org.jsonschema2pojo.rules.Rule; @@ -100,12 +98,10 @@ private JDefinedClass addKeyValueFields( JMod.PRIVATE, propertyType, nameHelper.getPropertyName(propertyType.name(), null)); JMethod valueMethod = GeneratorUtils.getterMethod(jclass, valueField, nameHelper, propertyType.name()); - jclass - .annotate(JsonSerialize.class) - .param("using", generateSerializer(jclass, nameMethod, valueMethod)); - jclass - .annotate(JsonDeserialize.class) - .param("using", generateDeserializer(jclass, propertyType)); + + jclass.annotate(Item.class); + nameMethod.annotate(ItemKey.class); + valueMethod.annotate(ItemValue.class); JMethod constructor = jclass.constructor(JMod.PUBLIC); constructor .body() @@ -114,44 +110,6 @@ private JDefinedClass addKeyValueFields( return jclass; } - private JDefinedClass generateDeserializer(JDefinedClass relatedClass, JType propertyType) - throws JClassAlreadyExistsException { - JDefinedClass definedClass = GeneratorUtils.deserializerClass(relatedClass); - GeneratorUtils.fillDeserializer( - definedClass, - relatedClass, - (method, parserParam) -> - method - .body() - ._return( - definedClass - .owner() - .ref(DeserializeHelper.class) - .staticInvoke("deserializeItem") - .arg(parserParam) - .arg(relatedClass.dotclass()) - .arg(((JClass) propertyType).dotclass()))); - return definedClass; - } - - private JDefinedClass generateSerializer( - JDefinedClass relatedClass, JMethod nameMethod, JMethod valueMethod) - throws JClassAlreadyExistsException { - JDefinedClass definedClass = GeneratorUtils.serializerClass(relatedClass); - GeneratorUtils.fillSerializer( - definedClass, - relatedClass, - (method, valueParam, genParam) -> { - JBlock body = method.body(); - body.invoke(genParam, "writeStartObject"); - body.invoke(genParam, "writeObjectField") - .arg(valueParam.invoke(nameMethod)) - .arg(valueParam.invoke(valueMethod)); - body.invoke(genParam, "writeEndObject"); - }); - return definedClass; - } - private boolean checkIntValue(JsonNode node, String propName, int value) { return node.has(propName) && node.get(propName).asInt() == value; } diff --git a/jackson-generator/pom.xml b/jackson-generator/pom.xml new file mode 100644 index 00000000..55d2e03c --- /dev/null +++ b/jackson-generator/pom.xml @@ -0,0 +1,80 @@ + + 4.0.0 + maven-plugin + + io.serverlessworkflow + serverlessworkflow-parent + 8.0.0-SNAPSHOT + + jackson-generator + + 3.15.1 + + + + + + org.apache.maven.plugins + maven-plugin-plugin + ${maven-plugin-tools.version} + + jackson-generator + + + + help-mojo + + + helpmojo + + + + + + + + + + + + org.apache.maven.plugins + maven-plugin-report-plugin + ${maven-plugin-tools.version} + + + + + + org.apache.maven + maven-plugin-api + ${version.maven} + provided + + + com.sun.codemodel + codemodel + 2.6 + + + io.github.classgraph + classgraph + 4.8.180 + + + org.apache.maven.plugin-tools + maven-plugin-annotations + ${maven-plugin-tools.version} + provided + + + io.serverlessworkflow + serverlessworkflow-types + ${project.version} + + + io.serverlessworkflow + serverlessworkflow-serialization + ${project.version} + + + \ No newline at end of file diff --git a/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/GeneratorUtils.java b/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/GeneratorUtils.java new file mode 100644 index 00000000..9463106f --- /dev/null +++ b/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/GeneratorUtils.java @@ -0,0 +1,172 @@ +/* + * 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.generator.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.sun.codemodel.JBlock; +import com.sun.codemodel.JClass; +import com.sun.codemodel.JClassAlreadyExistsException; +import com.sun.codemodel.JDefinedClass; +import com.sun.codemodel.JInvocation; +import com.sun.codemodel.JMethod; +import com.sun.codemodel.JMod; +import com.sun.codemodel.JType; +import com.sun.codemodel.JVar; +import io.serverlessworkflow.serialization.DeserializeHelper; +import io.serverlessworkflow.serialization.SerializeHelper; +import java.io.IOException; +import java.util.Collection; +import java.util.List; + +public class GeneratorUtils { + + @FunctionalInterface + public interface SerializerFiller { + void accept(JMethod method, JVar valueParam, JVar genParam); + } + + @FunctionalInterface + public interface DeserializerFiller { + void accept(JMethod method, JVar parserParam); + } + + public static JDefinedClass serializerClass(JClass relatedClass) + throws JClassAlreadyExistsException { + return createClass(relatedClass, JsonSerializer.class, "Serializer"); + } + + public static JDefinedClass deserializerClass(JClass relatedClass) + throws JClassAlreadyExistsException { + return createClass(relatedClass, JsonDeserializer.class, "Deserializer"); + } + + public static void fillSerializer( + JDefinedClass definedClass, JClass relatedClass, SerializerFiller filler) { + JMethod method = definedClass.method(JMod.PUBLIC, void.class, "serialize"); + method.annotate(Override.class); + method._throws(IOException.class); + JVar valueParam = method.param(relatedClass, "value"); + JVar genParam = method.param(JsonGenerator.class, "gen"); + method.param(SerializerProvider.class, "serializers"); + filler.accept(method, valueParam, genParam); + } + + public static void fillDeserializer( + JDefinedClass definedClass, JClass relatedClass, DeserializerFiller filler) { + JMethod method = definedClass.method(JMod.PUBLIC, relatedClass, "deserialize"); + method.annotate(Override.class); + method._throws(IOException.class); + JVar parserParam = method.param(JsonParser.class, "parser"); + method.param(DeserializationContext.class, "dctx"); + filler.accept(method, parserParam); + } + + private static JDefinedClass createClass( + JClass relatedClass, Class serializerClass, String suffix) + throws JClassAlreadyExistsException { + JDefinedClass definedClass = + relatedClass._package()._class(JMod.NONE, relatedClass.name() + suffix); + definedClass._extends(definedClass.owner().ref(serializerClass).narrow(relatedClass)); + return definedClass; + } + + public static JDefinedClass generateSerializer(JClass relatedClass) + throws JClassAlreadyExistsException { + JDefinedClass definedClass = GeneratorUtils.serializerClass(relatedClass); + GeneratorUtils.fillSerializer( + definedClass, + relatedClass, + (method, valueParam, genParam) -> + method + .body() + .staticInvoke(definedClass.owner().ref(SerializeHelper.class), "serializeOneOf") + .arg(genParam) + .arg(valueParam)); + return definedClass; + } + + public static JDefinedClass generateDeserializer( + JClass relatedClass, Collection oneOfTypes) throws JClassAlreadyExistsException { + JDefinedClass definedClass = GeneratorUtils.deserializerClass(relatedClass); + GeneratorUtils.fillDeserializer( + definedClass, + relatedClass, + (method, parserParam) -> { + JBlock body = method.body(); + + body._return( + definedClass + .owner() + .ref(DeserializeHelper.class) + .staticInvoke("deserializeOneOf") + .arg(parserParam) + .arg(relatedClass.dotclass()) + .arg(list(definedClass, oneOfTypes))); + }); + return definedClass; + } + + public static JDefinedClass generateDeserializer(JClass relatedClass, JType propertyType) + throws JClassAlreadyExistsException { + JDefinedClass definedClass = GeneratorUtils.deserializerClass(relatedClass); + GeneratorUtils.fillDeserializer( + definedClass, + relatedClass, + (method, parserParam) -> + method + .body() + ._return( + definedClass + .owner() + .ref(DeserializeHelper.class) + .staticInvoke("deserializeItem") + .arg(parserParam) + .arg(relatedClass.dotclass()) + .arg(((JClass) propertyType).dotclass()))); + return definedClass; + } + + public static JDefinedClass generateSerializer( + JClass relatedClass, String keyMethod, String valueMethod) + throws JClassAlreadyExistsException { + JDefinedClass definedClass = GeneratorUtils.serializerClass(relatedClass); + GeneratorUtils.fillSerializer( + definedClass, + relatedClass, + (method, valueParam, genParam) -> { + JBlock body = method.body(); + body.invoke(genParam, "writeStartObject"); + body.invoke(genParam, "writeObjectField") + .arg(valueParam.invoke(keyMethod)) + .arg(valueParam.invoke(valueMethod)); + body.invoke(genParam, "writeEndObject"); + }); + return definedClass; + } + + private static JInvocation list(JDefinedClass definedClass, Collection list) { + JInvocation result = definedClass.owner().ref(List.class).staticInvoke("of"); + list.forEach(c -> result.arg(c.dotclass())); + return result; + } + + private GeneratorUtils() {} +} diff --git a/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java b/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java new file mode 100644 index 00000000..16df2a6a --- /dev/null +++ b/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java @@ -0,0 +1,243 @@ +/* + * 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.generator.jackson; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.databind.Module.SetupContext; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.sun.codemodel.JClass; +import com.sun.codemodel.JClassAlreadyExistsException; +import com.sun.codemodel.JCodeModel; +import com.sun.codemodel.JDefinedClass; +import com.sun.codemodel.JExpr; +import com.sun.codemodel.JExpression; +import com.sun.codemodel.JMethod; +import com.sun.codemodel.JMod; +import com.sun.codemodel.JPackage; +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ClassRefTypeSignature; +import io.github.classgraph.MethodInfo; +import io.github.classgraph.ScanResult; +import io.github.classgraph.TypeArgument; +import io.github.classgraph.TypeSignature; +import io.serverlessworkflow.annotations.AdditionalProperties; +import io.serverlessworkflow.annotations.GetterMethod; +import io.serverlessworkflow.annotations.Item; +import io.serverlessworkflow.annotations.ItemKey; +import io.serverlessworkflow.annotations.ItemValue; +import io.serverlessworkflow.annotations.Union; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.annotation.Annotation; +import java.nio.file.Files; +import java.util.Collection; +import java.util.LinkedHashSet; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; + +@Mojo( + name = "generate", + defaultPhase = LifecyclePhase.GENERATE_SOURCES, + requiresDependencyResolution = ResolutionScope.COMPILE, + threadSafe = true) +public class JacksonMixInPojo extends AbstractMojo { + + @Parameter( + property = "jacksonmixinpojo.outputDirectory", + defaultValue = "${project.build.directory}/generated-sources/jacksonmixinpojo") + private File outputDirectory; + + /** + * Package name used for generated Java classes (for types where a fully qualified name has not + * been supplied in the schema using the 'javaType' property). + * + * @since 0.1.0 + */ + @Parameter(property = "jsonschema2pojo.targetPackage") + private String targetPackage = ""; + + private static final String MIXIN_METHOD = "setMixInAnnotation"; + private static final String ADD_PROPERTIES_METHOD = "getAdditionalProperties"; + private static final String SETUP_METHOD = "setupModule"; + private JCodeModel codeModel; + private JPackage rootPackage; + private JMethod setupMethod; + + @FunctionalInterface + interface AnnotationProcessor { + void accept(ClassInfo classInfo, JDefinedClass definedClass) + throws JClassAlreadyExistsException; + } + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + + try (ScanResult result = + new ClassGraph() + .enableAnnotationInfo() + .enableMethodInfo() + .acceptPackages(targetPackage) + .scan()) { + codeModel = new JCodeModel(); + rootPackage = codeModel._package(targetPackage); + setupMethod = + rootPackage + ._class("JacksonMixInModule") + ._extends(SimpleModule.class) + .method(JMod.PUBLIC, codeModel.VOID, SETUP_METHOD); + processAnnotatedClasses(result, Union.class, this::buildUnionMixIn); + processAnnotatedClasses(result, AdditionalProperties.class, this::buildAdditionalPropsMixIn); + processAnnotatedClasses(result, Item.class, this::buildItemMixIn); + processAnnotatedClasses(result.getAllEnums(), this::buildEnumMixIn); + setupMethod + .body() + .invoke(JExpr._super(), SETUP_METHOD) + .arg(setupMethod.param(SetupContext.class, "context")); + Files.createDirectories(outputDirectory.toPath()); + codeModel.build(outputDirectory, (PrintStream) null); + } catch (JClassAlreadyExistsException | IOException e) { + getLog().error(e); + } + } + + private void processAnnotatedClasses( + Iterable classesInfo, AnnotationProcessor processor) + throws JClassAlreadyExistsException { + for (ClassInfo classInfo : classesInfo) { + setupMethod + .body() + .invoke(JExpr._super(), MIXIN_METHOD) + .arg(JExpr.dotclass(codeModel.ref(classInfo.getName()))) + .arg(processAnnotatedClass(classInfo, processor)); + } + } + + private void processAnnotatedClasses( + ScanResult result, Class annotation, AnnotationProcessor processor) + throws JClassAlreadyExistsException { + processAnnotatedClasses(result.getClassesWithAnnotation(annotation), processor); + } + + private JExpression processAnnotatedClass(ClassInfo classInfo, AnnotationProcessor processor) + throws JClassAlreadyExistsException { + JDefinedClass result = createMixInClass(classInfo); + processor.accept(classInfo, result); + return JExpr.dotclass(result); + } + + private void buildAdditionalPropsMixIn( + ClassInfo addPropsClassInfo, JDefinedClass addPropsMixClass) { + JClass mapStringObject = + getReturnType(addPropsClassInfo.getMethodInfo().getSingleMethod(ADD_PROPERTIES_METHOD)); + JClass objectType = mapStringObject.getTypeParameters().get(1); + addPropsMixClass + .method(JMod.ABSTRACT, mapStringObject, ADD_PROPERTIES_METHOD) + .annotate(JsonAnyGetter.class); + addPropsMixClass + .field(JMod.NONE, mapStringObject, "additionalProperties") + .annotate(JsonIgnore.class); + JMethod setter = + addPropsMixClass.method(JMod.ABSTRACT, codeModel.VOID, "setAdditionalProperty"); + setter.param(String.class, "name"); + setter.param(objectType, "value"); + setter.annotate(JsonAnySetter.class); + } + + private void buildItemMixIn(ClassInfo classInfo, JDefinedClass mixClass) + throws JClassAlreadyExistsException { + JClass relClass = codeModel.ref(classInfo.getName()); + MethodInfo keyMethod = + classInfo.getMethodInfo().filter(m -> m.hasAnnotation(ItemKey.class)).get(0); + MethodInfo valueMethod = + classInfo.getMethodInfo().filter(m -> m.hasAnnotation(ItemValue.class)).get(0); + mixClass + .annotate(JsonSerialize.class) + .param( + "using", + GeneratorUtils.generateSerializer( + relClass, keyMethod.getName(), valueMethod.getName())); + mixClass + .annotate(JsonDeserialize.class) + .param("using", GeneratorUtils.generateDeserializer(relClass, getReturnType(valueMethod))); + } + + private void buildUnionMixIn(ClassInfo unionClassInfo, JDefinedClass unionMixClass) + throws JClassAlreadyExistsException { + JClass unionClass = codeModel.ref(unionClassInfo.getName()); + unionMixClass + .annotate(JsonSerialize.class) + .param("using", GeneratorUtils.generateSerializer(unionClass)); + unionMixClass + .annotate(JsonDeserialize.class) + .param( + "using", + GeneratorUtils.generateDeserializer(unionClass, getUnionClasses(unionClassInfo))); + } + + private void buildEnumMixIn(ClassInfo classInfo, JDefinedClass mixClass) + throws JClassAlreadyExistsException { + mixClass.method(JMod.ABSTRACT, String.class, "value").annotate(JsonValue.class); + + JMethod staticMethod = + mixClass.method(JMod.STATIC, codeModel.ref(classInfo.getName()), "fromValue"); + staticMethod.param(String.class, "value"); + staticMethod.annotate(JsonCreator.class); + staticMethod.body()._return(JExpr._null()); + } + + private JDefinedClass createMixInClass(ClassInfo classInfo) throws JClassAlreadyExistsException { + return rootPackage._class(JMod.ABSTRACT, classInfo.getSimpleName() + "MixIn"); + } + + private Collection getUnionClasses(ClassInfo unionClassInfo) { + Collection result = new LinkedHashSet(); + unionClassInfo + .getMethodInfo() + .filter(f -> f.hasAnnotation(GetterMethod.class)) + .forEach(m -> result.add(getReturnType(m))); + return result; + } + + private JClass getReturnType(MethodInfo info) { + return getReturnType(info.getTypeSignatureOrTypeDescriptor().getResultType()); + } + + private JClass getReturnType(ClassRefTypeSignature refTypeSignature) { + JClass result = codeModel.ref(refTypeSignature.getFullyQualifiedClassName()); + for (TypeArgument t : refTypeSignature.getTypeArguments()) + result = result.narrow(getReturnType(t.getTypeSignature())); + return result; + } + + private JClass getReturnType(TypeSignature t) { + return t instanceof ClassRefTypeSignature refTypeSignature + ? getReturnType(refTypeSignature) + : codeModel.ref(t.toString()); + } +} diff --git a/pom.xml b/pom.xml index 271e10da..a187fbfe 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,8 @@ impl serverlessworkflow-types serverlessworkflow-annotations + jackson-generator + serverlessworkflow-serialization diff --git a/serverlessworkflow-annotations/pom.xml b/serverlessworkflow-annotations/pom.xml index e37aa671..6e583ab9 100644 --- a/serverlessworkflow-annotations/pom.xml +++ b/serverlessworkflow-annotations/pom.xml @@ -7,14 +7,4 @@ Serverless Workflow :: Annotations serverlessworkflow-annotations - - - com.fasterxml.jackson.core - jackson-databind - - - jakarta.validation - jakarta.validation-api - - \ No newline at end of file diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/AdditionalProperties.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/AdditionalProperties.java new file mode 100644 index 00000000..69a3b023 --- /dev/null +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/AdditionalProperties.java @@ -0,0 +1,26 @@ +/* + * 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.annotations; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target(TYPE) +public @interface AdditionalProperties {} diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/GetterMethod.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/GetterMethod.java new file mode 100644 index 00000000..1906f20b --- /dev/null +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/GetterMethod.java @@ -0,0 +1,26 @@ +/* + * 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.annotations; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target(METHOD) +public @interface GetterMethod {} diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Item.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Item.java new file mode 100644 index 00000000..f42a86d5 --- /dev/null +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Item.java @@ -0,0 +1,26 @@ +/* + * 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.annotations; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target(TYPE) +public @interface Item {} diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemKey.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemKey.java new file mode 100644 index 00000000..ea75094d --- /dev/null +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemKey.java @@ -0,0 +1,26 @@ +/* + * 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.annotations; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target(METHOD) +public @interface ItemKey {} diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemValue.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemValue.java new file mode 100644 index 00000000..2aaf09e6 --- /dev/null +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemValue.java @@ -0,0 +1,26 @@ +/* + * 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.annotations; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target(METHOD) +public @interface ItemValue {} diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Union.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Union.java new file mode 100644 index 00000000..e9d3f2a8 --- /dev/null +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Union.java @@ -0,0 +1,26 @@ +/* + * 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.annotations; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target(ElementType.TYPE) +public @interface Union {} diff --git a/serverlessworkflow-serialization/pom.xml b/serverlessworkflow-serialization/pom.xml new file mode 100644 index 00000000..55787c00 --- /dev/null +++ b/serverlessworkflow-serialization/pom.xml @@ -0,0 +1,24 @@ + + 4.0.0 + + io.serverlessworkflow + serverlessworkflow-parent + 8.0.0-SNAPSHOT + + serverlessworkflow-serialization + + + io.serverlessworkflow + serverlessworkflow-annotations + ${project.version} + + + com.fasterxml.jackson.core + jackson-databind + + + jakarta.validation + jakarta.validation-api + + + \ No newline at end of file diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java b/serverlessworkflow-serialization/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java similarity index 100% rename from serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java rename to serverlessworkflow-serialization/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java b/serverlessworkflow-serialization/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java similarity index 100% rename from serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java rename to serverlessworkflow-serialization/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java diff --git a/serverlessworkflow-types/pom.xml b/serverlessworkflow-types/pom.xml index 4fe9f9d2..90aa8996 100644 --- a/serverlessworkflow-types/pom.xml +++ b/serverlessworkflow-types/pom.xml @@ -13,6 +13,10 @@ serverlessworkflow-annotations ${project.version} + + jakarta.validation + jakarta.validation-api + @@ -40,10 +44,10 @@ true ${java.version} true - jackson2 + none true io.serverlessworkflow.generator.UnreferencedFactory - io.serverlessworkflow.generator.ConstAnnotator + io.serverlessworkflow.generator.CustomAnnotator From 2e4a5500a70895847b984e0c9892a1c9c26a6ce7 Mon Sep 17 00:00:00 2001 From: fjtirado Date: Mon, 14 Jul 2025 11:26:54 +0200 Subject: [PATCH 08/30] [Fix #634] Preserving union class order Signed-off-by: fjtirado --- .../generator/AllAnyOneOfSchemaRule.java | 5 ++-- .../generator/GeneratorUtils.java | 2 -- .../generator/jackson/JacksonMixInPojo.java | 18 +++++++------ .../annotations/GetterMethod.java | 26 ------------------- .../serverlessworkflow/annotations/Union.java | 4 ++- 5 files changed, 16 insertions(+), 39 deletions(-) delete mode 100644 serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/GetterMethod.java diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java index 32dca0cd..a9823ce6 100644 --- a/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java @@ -17,6 +17,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; +import com.sun.codemodel.JAnnotationArrayMember; import com.sun.codemodel.JBlock; import com.sun.codemodel.JClass; import com.sun.codemodel.JClassAlreadyExistsException; @@ -317,11 +318,11 @@ private JDefinedClass populateOneOf( commonType.orElse(definedClass.owner().ref(Object.class)), ruleFactory.getNameHelper().getPropertyName("value", null), null); - definedClass._implements( definedClass.owner().ref(OneOfValueProvider.class).narrow(valueField.type())); GeneratorUtils.implementInterface(definedClass, valueField); - definedClass.annotate(Union.class); + JAnnotationArrayMember unionAnnotation = definedClass.annotate(Union.class).paramArray("value"); + oneOfTypes.forEach(t -> unionAnnotation.param(t.getType())); return wrapAll(parentSchema, definedClass, commonType, oneOfTypes, Optional.of(valueField)); } diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java index d330321b..abcf40eb 100644 --- a/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java @@ -19,7 +19,6 @@ import com.sun.codemodel.JFieldVar; import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; -import io.serverlessworkflow.annotations.GetterMethod; import org.jsonschema2pojo.util.NameHelper; public class GeneratorUtils { @@ -39,7 +38,6 @@ public static JMethod getterMethod( instanceField.type(), nameHelper.getGetterName(name, instanceField.type(), null)); method.body()._return(instanceField); - method.annotate(GetterMethod.class); return method; } diff --git a/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java b/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java index 16df2a6a..b80238ed 100644 --- a/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java +++ b/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java @@ -33,6 +33,8 @@ import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; import com.sun.codemodel.JPackage; +import io.github.classgraph.AnnotationClassRef; +import io.github.classgraph.AnnotationInfo; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; import io.github.classgraph.ClassRefTypeSignature; @@ -41,7 +43,6 @@ import io.github.classgraph.TypeArgument; import io.github.classgraph.TypeSignature; import io.serverlessworkflow.annotations.AdditionalProperties; -import io.serverlessworkflow.annotations.GetterMethod; import io.serverlessworkflow.annotations.Item; import io.serverlessworkflow.annotations.ItemKey; import io.serverlessworkflow.annotations.ItemValue; @@ -52,7 +53,8 @@ import java.lang.annotation.Annotation; import java.nio.file.Files; import java.util.Collection; -import java.util.LinkedHashSet; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; @@ -216,12 +218,12 @@ private JDefinedClass createMixInClass(ClassInfo classInfo) throws JClassAlready } private Collection getUnionClasses(ClassInfo unionClassInfo) { - Collection result = new LinkedHashSet(); - unionClassInfo - .getMethodInfo() - .filter(f -> f.hasAnnotation(GetterMethod.class)) - .forEach(m -> result.add(getReturnType(m))); - return result; + AnnotationInfo info = unionClassInfo.getAnnotationInfoRepeatable(Union.class).get(0); + Object[] unionClasses = (Object[]) info.getParameterValues().getValue("value"); + return Stream.of(unionClasses) + .map(AnnotationClassRef.class::cast) + .map(ref -> codeModel.ref(ref.getName())) + .collect(Collectors.toList()); } private JClass getReturnType(MethodInfo info) { diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/GetterMethod.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/GetterMethod.java deleted file mode 100644 index 1906f20b..00000000 --- a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/GetterMethod.java +++ /dev/null @@ -1,26 +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.annotations; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -@Retention(RUNTIME) -@Target(METHOD) -public @interface GetterMethod {} diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Union.java b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Union.java index e9d3f2a8..e6cc4ecb 100644 --- a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Union.java +++ b/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Union.java @@ -23,4 +23,6 @@ @Retention(RUNTIME) @Target(ElementType.TYPE) -public @interface Union {} +public @interface Union { + Class[] value(); +} From 58077404398ad38ab3a6e0467173c0a651454b10 Mon Sep 17 00:00:00 2001 From: fjtirado Date: Mon, 14 Jul 2025 13:13:35 +0200 Subject: [PATCH 09/30] Reorganizing directories Signed-off-by: fjtirado --- .../pom.xml | 0 .../annotations/AdditionalProperties.java | 0 .../io/serverlessworkflow/annotations/Item.java | 0 .../serverlessworkflow/annotations/ItemKey.java | 0 .../serverlessworkflow/annotations/ItemValue.java | 0 .../annotations/OneOfSetter.java | 0 .../annotations/OneOfValueProvider.java | 0 .../io/serverlessworkflow/annotations/Union.java | 0 api/pom.xml | 2 +- {jackson-generator => generators/jackson}/pom.xml | 5 +++-- .../generator/jackson/GeneratorUtils.java | 0 .../generator/jackson/JacksonMixInPojo.java | 0 generators/pom.xml | 15 +++++++++++++++ {custom-generator => generators/types}/pom.xml | 6 +++--- .../generator/AllAnyOneOfSchemaRule.java | 0 .../generator/CustomAnnotator.java | 0 .../generator/EmptyObjectTypeRule.java | 0 .../generator/GeneratorUtils.java | 0 .../generator/RefNameHelper.java | 0 .../generator/UnevaluatedPropertiesRule.java | 0 .../generator/UnreferencedFactory.java | 0 pom.xml | 9 ++++----- .../pom.xml | 1 + .../serialization/DeserializeHelper.java | 0 .../serialization/SerializeHelper.java | 0 {serverlessworkflow-types => types}/pom.xml | 2 +- .../src/main/resources/schema/workflow.yaml | 0 27 files changed, 28 insertions(+), 12 deletions(-) rename {serverlessworkflow-annotations => annotations}/pom.xml (100%) rename {serverlessworkflow-annotations => annotations}/src/main/java/io/serverlessworkflow/annotations/AdditionalProperties.java (100%) rename {serverlessworkflow-annotations => annotations}/src/main/java/io/serverlessworkflow/annotations/Item.java (100%) rename {serverlessworkflow-annotations => annotations}/src/main/java/io/serverlessworkflow/annotations/ItemKey.java (100%) rename {serverlessworkflow-annotations => annotations}/src/main/java/io/serverlessworkflow/annotations/ItemValue.java (100%) rename {serverlessworkflow-annotations => annotations}/src/main/java/io/serverlessworkflow/annotations/OneOfSetter.java (100%) rename {serverlessworkflow-annotations => annotations}/src/main/java/io/serverlessworkflow/annotations/OneOfValueProvider.java (100%) rename {serverlessworkflow-annotations => annotations}/src/main/java/io/serverlessworkflow/annotations/Union.java (100%) rename {jackson-generator => generators/jackson}/pom.xml (93%) rename {jackson-generator => generators/jackson}/src/main/java/io/serverlessworkflow/generator/jackson/GeneratorUtils.java (100%) rename {jackson-generator => generators/jackson}/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java (100%) create mode 100644 generators/pom.xml rename {custom-generator => generators/types}/pom.xml (90%) rename {custom-generator => generators/types}/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java (100%) rename {custom-generator => generators/types}/src/main/java/io/serverlessworkflow/generator/CustomAnnotator.java (100%) rename {custom-generator => generators/types}/src/main/java/io/serverlessworkflow/generator/EmptyObjectTypeRule.java (100%) rename {custom-generator => generators/types}/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java (100%) rename {custom-generator => generators/types}/src/main/java/io/serverlessworkflow/generator/RefNameHelper.java (100%) rename {custom-generator => generators/types}/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java (100%) rename {custom-generator => generators/types}/src/main/java/io/serverlessworkflow/generator/UnreferencedFactory.java (100%) rename {serverlessworkflow-serialization => serialization}/pom.xml (94%) rename {serverlessworkflow-serialization => serialization}/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java (100%) rename {serverlessworkflow-serialization => serialization}/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java (100%) rename {serverlessworkflow-types => types}/pom.xml (97%) rename {serverlessworkflow-types => types}/src/main/resources/schema/workflow.yaml (100%) diff --git a/serverlessworkflow-annotations/pom.xml b/annotations/pom.xml similarity index 100% rename from serverlessworkflow-annotations/pom.xml rename to annotations/pom.xml diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/AdditionalProperties.java b/annotations/src/main/java/io/serverlessworkflow/annotations/AdditionalProperties.java similarity index 100% rename from serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/AdditionalProperties.java rename to annotations/src/main/java/io/serverlessworkflow/annotations/AdditionalProperties.java diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Item.java b/annotations/src/main/java/io/serverlessworkflow/annotations/Item.java similarity index 100% rename from serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Item.java rename to annotations/src/main/java/io/serverlessworkflow/annotations/Item.java diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemKey.java b/annotations/src/main/java/io/serverlessworkflow/annotations/ItemKey.java similarity index 100% rename from serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemKey.java rename to annotations/src/main/java/io/serverlessworkflow/annotations/ItemKey.java diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemValue.java b/annotations/src/main/java/io/serverlessworkflow/annotations/ItemValue.java similarity index 100% rename from serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/ItemValue.java rename to annotations/src/main/java/io/serverlessworkflow/annotations/ItemValue.java diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/OneOfSetter.java b/annotations/src/main/java/io/serverlessworkflow/annotations/OneOfSetter.java similarity index 100% rename from serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/OneOfSetter.java rename to annotations/src/main/java/io/serverlessworkflow/annotations/OneOfSetter.java diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/OneOfValueProvider.java b/annotations/src/main/java/io/serverlessworkflow/annotations/OneOfValueProvider.java similarity index 100% rename from serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/OneOfValueProvider.java rename to annotations/src/main/java/io/serverlessworkflow/annotations/OneOfValueProvider.java diff --git a/serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Union.java b/annotations/src/main/java/io/serverlessworkflow/annotations/Union.java similarity index 100% rename from serverlessworkflow-annotations/src/main/java/io/serverlessworkflow/annotations/Union.java rename to annotations/src/main/java/io/serverlessworkflow/annotations/Union.java diff --git a/api/pom.xml b/api/pom.xml index 19414b85..f69dfd99 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -83,7 +83,7 @@ io.serverlessworkflow - jackson-generator + serverless-workflow-jackson-generator ${project.version} diff --git a/jackson-generator/pom.xml b/generators/jackson/pom.xml similarity index 93% rename from jackson-generator/pom.xml rename to generators/jackson/pom.xml index 55d2e03c..81d4c254 100644 --- a/jackson-generator/pom.xml +++ b/generators/jackson/pom.xml @@ -3,10 +3,11 @@ maven-plugin io.serverlessworkflow - serverlessworkflow-parent + serverlessworkflow-generators 8.0.0-SNAPSHOT - jackson-generator + serverless-workflow-jackson-generator + Serverless Workflow :: Generator:: Jackson 3.15.1 diff --git a/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/GeneratorUtils.java b/generators/jackson/src/main/java/io/serverlessworkflow/generator/jackson/GeneratorUtils.java similarity index 100% rename from jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/GeneratorUtils.java rename to generators/jackson/src/main/java/io/serverlessworkflow/generator/jackson/GeneratorUtils.java diff --git a/jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java b/generators/jackson/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java similarity index 100% rename from jackson-generator/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java rename to generators/jackson/src/main/java/io/serverlessworkflow/generator/jackson/JacksonMixInPojo.java diff --git a/generators/pom.xml b/generators/pom.xml new file mode 100644 index 00000000..9ad258c4 --- /dev/null +++ b/generators/pom.xml @@ -0,0 +1,15 @@ + + 4.0.0 + + io.serverlessworkflow + serverlessworkflow-parent + 8.0.0-SNAPSHOT + + serverlessworkflow-generators + Serverless Workflow :: Generators + pom + + jackson + types + + \ No newline at end of file diff --git a/custom-generator/pom.xml b/generators/types/pom.xml similarity index 90% rename from custom-generator/pom.xml rename to generators/types/pom.xml index 55b2215b..232009ea 100644 --- a/custom-generator/pom.xml +++ b/generators/types/pom.xml @@ -2,11 +2,11 @@ 4.0.0 io.serverlessworkflow - serverlessworkflow-parent + serverlessworkflow-generators 8.0.0-SNAPSHOT - serverless-workflow-custom-generator - Serverless Workflow :: Custom Generator + serverless-workflow-types-generator + Serverless Workflow :: Generator:: Types org.jsonschema2pojo diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java b/generators/types/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java similarity index 100% rename from custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java rename to generators/types/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/CustomAnnotator.java b/generators/types/src/main/java/io/serverlessworkflow/generator/CustomAnnotator.java similarity index 100% rename from custom-generator/src/main/java/io/serverlessworkflow/generator/CustomAnnotator.java rename to generators/types/src/main/java/io/serverlessworkflow/generator/CustomAnnotator.java diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/EmptyObjectTypeRule.java b/generators/types/src/main/java/io/serverlessworkflow/generator/EmptyObjectTypeRule.java similarity index 100% rename from custom-generator/src/main/java/io/serverlessworkflow/generator/EmptyObjectTypeRule.java rename to generators/types/src/main/java/io/serverlessworkflow/generator/EmptyObjectTypeRule.java diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java b/generators/types/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java similarity index 100% rename from custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java rename to generators/types/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/RefNameHelper.java b/generators/types/src/main/java/io/serverlessworkflow/generator/RefNameHelper.java similarity index 100% rename from custom-generator/src/main/java/io/serverlessworkflow/generator/RefNameHelper.java rename to generators/types/src/main/java/io/serverlessworkflow/generator/RefNameHelper.java diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java b/generators/types/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java similarity index 100% rename from custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java rename to generators/types/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/UnreferencedFactory.java b/generators/types/src/main/java/io/serverlessworkflow/generator/UnreferencedFactory.java similarity index 100% rename from custom-generator/src/main/java/io/serverlessworkflow/generator/UnreferencedFactory.java rename to generators/types/src/main/java/io/serverlessworkflow/generator/UnreferencedFactory.java diff --git a/pom.xml b/pom.xml index a187fbfe..0f420cee 100644 --- a/pom.xml +++ b/pom.xml @@ -38,12 +38,11 @@ api - custom-generator impl - serverlessworkflow-types - serverlessworkflow-annotations - jackson-generator - serverlessworkflow-serialization + types + annotations + generators + serialization diff --git a/serverlessworkflow-serialization/pom.xml b/serialization/pom.xml similarity index 94% rename from serverlessworkflow-serialization/pom.xml rename to serialization/pom.xml index 55787c00..6e8411f0 100644 --- a/serverlessworkflow-serialization/pom.xml +++ b/serialization/pom.xml @@ -6,6 +6,7 @@ 8.0.0-SNAPSHOT serverlessworkflow-serialization + Serverless Workflow :: Serialization io.serverlessworkflow diff --git a/serverlessworkflow-serialization/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java b/serialization/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java similarity index 100% rename from serverlessworkflow-serialization/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java rename to serialization/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java diff --git a/serverlessworkflow-serialization/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java b/serialization/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java similarity index 100% rename from serverlessworkflow-serialization/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java rename to serialization/src/main/java/io/serverlessworkflow/serialization/SerializeHelper.java diff --git a/serverlessworkflow-types/pom.xml b/types/pom.xml similarity index 97% rename from serverlessworkflow-types/pom.xml rename to types/pom.xml index 90aa8996..20b4d552 100644 --- a/serverlessworkflow-types/pom.xml +++ b/types/pom.xml @@ -52,7 +52,7 @@ io.serverlessworkflow - serverless-workflow-custom-generator + serverless-workflow-types-generator ${project.version} diff --git a/serverlessworkflow-types/src/main/resources/schema/workflow.yaml b/types/src/main/resources/schema/workflow.yaml similarity index 100% rename from serverlessworkflow-types/src/main/resources/schema/workflow.yaml rename to types/src/main/resources/schema/workflow.yaml From df20c94521d9d29edb19b8bba98cd4204c04e27b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 13:16:59 -0400 Subject: [PATCH 10/30] Bump org.apache.maven:maven-plugin-api from 3.9.7 to 3.9.10 (#640) Bumps [org.apache.maven:maven-plugin-api](https://github.com/apache/maven) from 3.9.7 to 3.9.10. - [Release notes](https://github.com/apache/maven/releases) - [Commits](https://github.com/apache/maven/compare/maven-3.9.7...maven-3.9.10) --- updated-dependencies: - dependency-name: org.apache.maven:maven-plugin-api dependency-version: 3.9.10 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0f420cee..b17bda17 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ ${java.version} ${java.version} UTF-8 - 3.9.7 + 3.9.10 3.2.1 From 9d4ac192d2acdeb617d3c7691016c8286f36e6e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 13:19:35 -0400 Subject: [PATCH 11/30] Bump org.codehaus.mojo:build-helper-maven-plugin from 3.3.0 to 3.6.1 (#639) Bumps [org.codehaus.mojo:build-helper-maven-plugin](https://github.com/mojohaus/build-helper-maven-plugin) from 3.3.0 to 3.6.1. - [Release notes](https://github.com/mojohaus/build-helper-maven-plugin/releases) - [Commits](https://github.com/mojohaus/build-helper-maven-plugin/compare/build-helper-maven-plugin-3.3.0...3.6.1) --- updated-dependencies: - dependency-name: org.codehaus.mojo:build-helper-maven-plugin dependency-version: 3.6.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/pom.xml b/api/pom.xml index f69dfd99..662d25c2 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -102,7 +102,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.3.0 + 3.6.1 add-mixin From 2e9e85eab934b94d947c976d17d41d2437954075 Mon Sep 17 00:00:00 2001 From: fjtirado Date: Tue, 15 Jul 2025 13:25:41 +0200 Subject: [PATCH 12/30] [Fix #636] Initial refactor to separate Jackson/JQ Signed-off-by: fjtirado --- impl/core/pom.xml | 10 +- .../serverlessworkflow/impl/TaskContext.java | 33 ++-- .../impl/WorkflowApplication.java | 4 + .../impl/WorkflowContext.java | 8 +- .../impl/WorkflowDefinition.java | 7 +- .../impl/WorkflowFilter.java | 4 +- .../impl/WorkflowInstance.java | 29 ++-- .../impl/WorkflowModel.java | 50 ++++++ .../impl/WorkflowModelCollection.java | 61 +++++++ .../impl/WorkflowModelFactory.java | 67 ++++++++ .../impl/WorkflowUtils.java | 113 ++++--------- .../impl/events/AbstractTypeConsumer.java | 2 +- .../impl/events/CloudEventUtils.java | 13 -- .../events/DefaultCloudEventPredicate.java | 86 ++++++---- .../impl/events/InMemoryEvents.java | 2 +- .../impl/executors/AbstractTaskExecutor.java | 15 +- .../impl/executors/CallTaskExecutor.java | 7 +- .../impl/executors/CallableTask.java | 6 +- .../impl/executors/DoExecutor.java | 4 +- .../impl/executors/EmitExecutor.java | 78 +++++---- .../impl/executors/ForExecutor.java | 23 ++- .../impl/executors/ForkExecutor.java | 19 ++- .../impl/executors/ListenExecutor.java | 56 +++---- .../impl/executors/RaiseExecutor.java | 24 ++- .../impl/executors/RegularTaskExecutor.java | 4 +- .../impl/executors/SetExecutor.java | 6 +- .../impl/executors/SwitchExecutor.java | 10 +- .../impl/executors/TaskExecutor.java | 4 +- .../impl/executors/TaskExecutorHelper.java | 6 +- .../impl/executors/TryExecutor.java | 20 ++- .../impl/executors/WaitExecutor.java | 7 +- .../impl/expressions/Expression.java | 4 +- .../impl/expressions/ExpressionFactory.java | 9 +- .../impl/expressions/ExpressionUtils.java | 24 +-- .../impl/expressions/JQExpression.java | 20 ++- .../impl/expressions/JQExpressionFactory.java | 15 +- .../impl/expressions/JacksonModel.java | 125 ++++++++++++++ .../expressions/JacksonModelCollection.java | 157 ++++++++++++++++++ .../impl/expressions/JacksonModelFactory.java | 125 ++++++++++++++ .../expressions/JacksonModelSerializer.java | 36 ++++ .../expressions/ObjectExpressionFactory.java | 37 +++++ .../impl/expressions/TaskDescriptor.java | 6 +- .../impl/expressions/WorkflowDescriptor.java | 4 +- .../impl/json/JsonUtils.java | 18 ++ .../jsonschema/DefaultSchemaValidator.java | 11 +- .../DefaultSchemaValidatorFactory.java | 23 ++- .../impl/jsonschema/SchemaValidator.java | 4 +- .../jsonschema/SchemaValidatorFactory.java | 7 +- .../impl/EventDefinitionTest.java | 8 +- .../impl/WorkflowDefinitionTest.java | 14 +- .../impl/executors/HttpExecutor.java | 97 ++++++----- .../impl/executors/HttpModelConverter.java} | 18 +- .../impl/HTTPWorkflowDefinitionTest.java | 17 +- 53 files changed, 1135 insertions(+), 422 deletions(-) create mode 100644 impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModel.java create mode 100644 impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModelCollection.java create mode 100644 impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModelFactory.java create mode 100644 impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModel.java create mode 100644 impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelCollection.java create mode 100644 impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelFactory.java create mode 100644 impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelSerializer.java create mode 100644 impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ObjectExpressionFactory.java rename impl/{core/src/main/java/io/serverlessworkflow/impl/LongFilter.java => http/src/main/java/io/serverlessworkflow/impl/executors/HttpModelConverter.java} (59%) diff --git a/impl/core/pom.xml b/impl/core/pom.xml index 844cf2a4..4140e747 100644 --- a/impl/core/pom.xml +++ b/impl/core/pom.xml @@ -8,9 +8,9 @@ serverlessworkflow-impl-core Serverless Workflow :: Impl :: Core - + io.serverlessworkflow - serverlessworkflow-api + serverlessworkflow-types ${project.version} @@ -58,5 +58,11 @@ logback-classic test + + io.serverlessworkflow + serverlessworkflow-api + ${project.version} + + diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/TaskContext.java b/impl/core/src/main/java/io/serverlessworkflow/impl/TaskContext.java index 4fc3d1f4..91c4abab 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/TaskContext.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/TaskContext.java @@ -15,7 +15,6 @@ */ package io.serverlessworkflow.impl; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.api.types.TaskBase; import io.serverlessworkflow.impl.executors.TransitionInfo; import java.time.Instant; @@ -25,7 +24,7 @@ public class TaskContext { - private final JsonNode rawInput; + private final WorkflowModel rawInput; private final TaskBase task; private final WorkflowPosition position; private final Instant startedAt; @@ -33,14 +32,14 @@ public class TaskContext { private final Map contextVariables; private final Optional parentContext; - private JsonNode input; - private JsonNode output; - private JsonNode rawOutput; + private WorkflowModel input; + private WorkflowModel output; + private WorkflowModel rawOutput; private Instant completedAt; private TransitionInfo transition; public TaskContext( - JsonNode input, + WorkflowModel input, WorkflowPosition position, Optional parentContext, String taskName, @@ -49,15 +48,15 @@ public TaskContext( } private TaskContext( - JsonNode rawInput, + WorkflowModel rawInput, Optional parentContext, String taskName, TaskBase task, WorkflowPosition position, Instant startedAt, - JsonNode input, - JsonNode output, - JsonNode rawOutput) { + WorkflowModel input, + WorkflowModel output, + WorkflowModel rawOutput) { this.rawInput = rawInput; this.parentContext = parentContext; this.taskName = taskName; @@ -76,17 +75,17 @@ public TaskContext copy() { rawInput, parentContext, taskName, task, position, startedAt, input, output, rawOutput); } - public void input(JsonNode input) { + public void input(WorkflowModel input) { this.input = input; this.rawOutput = input; this.output = input; } - public JsonNode input() { + public WorkflowModel input() { return input; } - public JsonNode rawInput() { + public WorkflowModel rawInput() { return rawInput; } @@ -94,22 +93,22 @@ public TaskBase task() { return task; } - public TaskContext rawOutput(JsonNode output) { + public TaskContext rawOutput(WorkflowModel output) { this.rawOutput = output; this.output = output; return this; } - public JsonNode rawOutput() { + public WorkflowModel rawOutput() { return rawOutput; } - public TaskContext output(JsonNode output) { + public TaskContext output(WorkflowModel output) { this.output = output; return this; } - public JsonNode output() { + public WorkflowModel output() { return output; } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java index b998c57d..ab09dac7 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java @@ -202,6 +202,10 @@ public WorkflowPositionFactory positionFactory() { return positionFactory; } + public WorkflowModelFactory modelFactory() { + return exprFactory.modelFactory(); + } + public RuntimeDescriptorFactory runtimeDescriptorFactory() { return runtimeDescriptorFactory; } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowContext.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowContext.java index 96890c8b..6960ca66 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowContext.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowContext.java @@ -15,12 +15,10 @@ */ package io.serverlessworkflow.impl; -import com.fasterxml.jackson.databind.JsonNode; - public class WorkflowContext { private final WorkflowDefinition definition; private final WorkflowInstance instance; - private JsonNode context; + private WorkflowModel context; WorkflowContext(WorkflowDefinition definition, WorkflowInstance instance) { this.definition = definition; @@ -31,11 +29,11 @@ public WorkflowInstance instance() { return instance; } - public JsonNode context() { + public WorkflowModel context() { return context; } - public void context(JsonNode context) { + public void context(WorkflowModel context) { this.context = context; } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowDefinition.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowDefinition.java index 1a789616..639f368d 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowDefinition.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowDefinition.java @@ -22,7 +22,6 @@ import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.impl.executors.TaskExecutor; import io.serverlessworkflow.impl.executors.TaskExecutorHelper; -import io.serverlessworkflow.impl.json.JsonUtils; import io.serverlessworkflow.impl.jsonschema.SchemaValidator; import io.serverlessworkflow.impl.resources.ResourceLoader; import java.nio.file.Path; @@ -47,13 +46,13 @@ private WorkflowDefinition( Input input = workflow.getInput(); this.inputSchemaValidator = getSchemaValidator(application.validatorFactory(), resourceLoader, input.getSchema()); - this.inputFilter = buildWorkflowFilter(application.expressionFactory(), input.getFrom()); + this.inputFilter = buildWorkflowFilter(application, input.getFrom()); } if (workflow.getOutput() != null) { Output output = workflow.getOutput(); this.outputSchemaValidator = getSchemaValidator(application.validatorFactory(), resourceLoader, output.getSchema()); - this.outputFilter = buildWorkflowFilter(application.expressionFactory(), output.getAs()); + this.outputFilter = buildWorkflowFilter(application, output.getAs()); } this.taskExecutor = TaskExecutorHelper.createExecutorList( @@ -74,7 +73,7 @@ static WorkflowDefinition of(WorkflowApplication application, Workflow workflow, } public WorkflowInstance instance(Object input) { - return new WorkflowInstance(this, JsonUtils.fromValue(input)); + return new WorkflowInstance(this, application.modelFactory().fromAny(input)); } Optional inputSchemaValidator() { diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowFilter.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowFilter.java index 4475cacd..04c34d29 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowFilter.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowFilter.java @@ -15,9 +15,7 @@ */ package io.serverlessworkflow.impl; -import com.fasterxml.jackson.databind.JsonNode; - @FunctionalInterface public interface WorkflowFilter { - JsonNode apply(WorkflowContext workflow, TaskContext task, JsonNode node); + WorkflowModel apply(WorkflowContext workflow, TaskContext task, WorkflowModel node); } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowInstance.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowInstance.java index 2e55c484..88269082 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowInstance.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowInstance.java @@ -15,9 +15,7 @@ */ package io.serverlessworkflow.impl; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.impl.executors.TaskExecutorHelper; -import io.serverlessworkflow.impl.json.JsonUtils; import java.time.Instant; import java.util.Optional; import java.util.concurrent.CompletableFuture; @@ -26,16 +24,16 @@ public class WorkflowInstance { private final AtomicReference status; private final String id; - private final JsonNode input; + private final WorkflowModel input; private WorkflowContext workflowContext; private WorkflowDefinition definition; private Instant startedAt; private Instant completedAt; - private volatile JsonNode output; - private CompletableFuture completableFuture; + private volatile WorkflowModel output; + private CompletableFuture completableFuture; - WorkflowInstance(WorkflowDefinition definition, JsonNode input) { + WorkflowInstance(WorkflowDefinition definition, WorkflowModel input) { this.id = definition.application().idFactory().get(); this.input = input; this.definition = definition; @@ -43,7 +41,7 @@ public class WorkflowInstance { definition.inputSchemaValidator().ifPresent(v -> v.validate(input)); } - public CompletableFuture start() { + public CompletableFuture start() { this.startedAt = Instant.now(); this.workflowContext = new WorkflowContext(definition, this); this.status.set(WorkflowStatus.RUNNING); @@ -60,7 +58,7 @@ public CompletableFuture start() { return completableFuture; } - private JsonNode whenCompleted(JsonNode node) { + private WorkflowModel whenCompleted(WorkflowModel node) { output = workflowContext .definition() @@ -85,7 +83,7 @@ public Instant completedAt() { return completedAt; } - public JsonNode input() { + public WorkflowModel input() { return input; } @@ -97,11 +95,16 @@ public void status(WorkflowStatus state) { this.status.set(state); } - public Object output() { - return JsonUtils.toJavaValue(outputAsJsonNode()); + public WorkflowModel output() { + return output; } - public JsonNode outputAsJsonNode() { - return output; + public T outputAs(Class clazz) { + return output + .as(clazz) + .orElseThrow( + () -> + new IllegalArgumentException( + "Output " + output + " cannot be converted to class " + clazz)); } } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModel.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModel.java new file mode 100644 index 00000000..dc45896f --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModel.java @@ -0,0 +1,50 @@ +/* + * 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; + +import io.cloudevents.CloudEventData; +import java.time.OffsetDateTime; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiConsumer; + +public interface WorkflowModel { + + void forEach(BiConsumer consumer); + + Optional asBoolean(); + + Collection asCollection(); + + Optional asText(); + + Optional asDate(); + + Optional asNumber(); + + Optional asCloudEventData(); + + Optional> asMap(); + + Object asJavaObject(); + + Object asIs(); + + Class objectClass(); + + Optional as(Class clazz); +} diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModelCollection.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModelCollection.java new file mode 100644 index 00000000..09f1dd75 --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModelCollection.java @@ -0,0 +1,61 @@ +/* + * 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; + +import io.cloudevents.CloudEventData; +import java.time.OffsetDateTime; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiConsumer; + +public interface WorkflowModelCollection extends WorkflowModel, Collection { + + default void forEach(BiConsumer consumer) {} + + @Override + default Collection asCollection() { + return this; + } + + @Override + default Optional asBoolean() { + return Optional.empty(); + } + + @Override + default Optional asText() { + return Optional.empty(); + } + + @Override + default Optional asNumber() { + return Optional.empty(); + } + + @Override + public default Optional asDate() { + return Optional.empty(); + } + + default Optional asCloudEventData() { + return Optional.empty(); + } + + default Optional> asMap() { + return Optional.empty(); + } +} diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModelFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModelFactory.java new file mode 100644 index 00000000..f8cf7278 --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModelFactory.java @@ -0,0 +1,67 @@ +/* + * 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; + +import io.cloudevents.CloudEvent; +import io.cloudevents.CloudEventData; +import java.time.OffsetDateTime; +import java.util.Map; + +public interface WorkflowModelFactory { + + WorkflowModel combine(Map workflowVariables); + + WorkflowModelCollection createCollection(); + + WorkflowModel from(boolean value); + + WorkflowModel from(Number value); + + WorkflowModel from(String value); + + WorkflowModel from(CloudEvent ce); + + WorkflowModel from(CloudEventData ce); + + WorkflowModel from(OffsetDateTime value); + + WorkflowModel from(Map map); + + WorkflowModel fromNull(); + + default WorkflowModel fromAny(Object obj) { + if (obj == null) { + return fromNull(); + } else if (obj instanceof Boolean value) { + return from(value); + } else if (obj instanceof Number value) { + return from(value); + } else if (obj instanceof String value) { + return from(value); + } else if (obj instanceof CloudEvent value) { + return from(value); + } else if (obj instanceof CloudEventData value) { + return from(value); + } else if (obj instanceof OffsetDateTime value) { + return from(value); + } else if (obj instanceof Map) { + return from((Map) obj); + } else { + throw new IllegalArgumentException( + "Unsopported conversion for object " + obj + " of type" + obj.getClass()); + } + } +} diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java index 5feaf04e..069986df 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java @@ -15,29 +15,16 @@ */ package io.serverlessworkflow.impl; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.serverlessworkflow.api.WorkflowFormat; import io.serverlessworkflow.api.types.ExportAs; import io.serverlessworkflow.api.types.InputFrom; import io.serverlessworkflow.api.types.OutputAs; -import io.serverlessworkflow.api.types.SchemaExternal; -import io.serverlessworkflow.api.types.SchemaInline; import io.serverlessworkflow.api.types.SchemaUnion; import io.serverlessworkflow.api.types.UriTemplate; -import io.serverlessworkflow.impl.expressions.Expression; -import io.serverlessworkflow.impl.expressions.ExpressionFactory; import io.serverlessworkflow.impl.expressions.ExpressionUtils; -import io.serverlessworkflow.impl.json.JsonUtils; import io.serverlessworkflow.impl.jsonschema.SchemaValidator; import io.serverlessworkflow.impl.jsonschema.SchemaValidatorFactory; import io.serverlessworkflow.impl.resources.ResourceLoader; -import io.serverlessworkflow.impl.resources.StaticResource; -import java.io.IOException; -import java.io.InputStream; -import java.io.UncheckedIOException; import java.net.URI; -import java.util.Map; import java.util.Optional; import java.util.function.Function; @@ -47,55 +34,41 @@ private WorkflowUtils() {} public static Optional getSchemaValidator( SchemaValidatorFactory validatorFactory, ResourceLoader resourceLoader, SchemaUnion schema) { - return schemaToNode(resourceLoader, schema).map(n -> validatorFactory.getValidator(n)); - } - - private static Optional schemaToNode( - ResourceLoader resourceLoader, SchemaUnion schema) { if (schema != null) { + if (schema.getSchemaInline() != null) { - SchemaInline inline = schema.getSchemaInline(); - return Optional.of(JsonUtils.mapper().convertValue(inline.getDocument(), JsonNode.class)); + return Optional.of(validatorFactory.getValidator(schema.getSchemaInline())); } else if (schema.getSchemaExternal() != null) { - SchemaExternal external = schema.getSchemaExternal(); - StaticResource resource = resourceLoader.loadStatic(external.getResource()); - ObjectMapper mapper = WorkflowFormat.fromFileName(resource.name()).mapper(); - try (InputStream in = resource.open()) { - return Optional.of(mapper.readTree(in)); - } catch (IOException io) { - throw new UncheckedIOException(io); - } + return Optional.of( + validatorFactory.getValidator( + resourceLoader.loadStatic(schema.getSchemaExternal().getResource()))); } } return Optional.empty(); } public static Optional buildWorkflowFilter( - ExpressionFactory exprFactory, InputFrom from) { + WorkflowApplication app, InputFrom from) { return from != null - ? Optional.of(buildWorkflowFilter(exprFactory, from.getString(), from.getObject())) + ? Optional.of(buildWorkflowFilter(app, from.getString(), from.getObject())) : Optional.empty(); } - public static Optional buildWorkflowFilter( - ExpressionFactory exprFactory, OutputAs as) { + public static Optional buildWorkflowFilter(WorkflowApplication app, OutputAs as) { return as != null - ? Optional.of(buildWorkflowFilter(exprFactory, as.getString(), as.getObject())) + ? Optional.of(buildWorkflowFilter(app, as.getString(), as.getObject())) : Optional.empty(); } public static ExpressionHolder buildExpressionHolder( - ExpressionFactory exprFactory, - String expression, - T literal, - Function converter) { + WorkflowApplication app, String expression, T literal, Function converter) { return expression != null - ? buildExpressionHolder(buildWorkflowFilter(exprFactory, expression), converter) + ? buildExpressionHolder(buildWorkflowFilter(app, expression), converter) : buildExpressionHolder(literal); } private static ExpressionHolder buildExpressionHolder( - WorkflowFilter filter, Function converter) { + WorkflowFilter filter, Function converter) { return (w, t) -> converter.apply(filter.apply(w, t, t.input())); } @@ -103,28 +76,27 @@ private static ExpressionHolder buildExpressionHolder(T literal) { return (w, t) -> literal; } - public static Optional buildWorkflowFilter( - ExpressionFactory exprFactory, ExportAs as) { + public static Optional buildWorkflowFilter(WorkflowApplication app, ExportAs as) { return as != null - ? Optional.of(buildWorkflowFilter(exprFactory, as.getString(), as.getObject())) + ? Optional.of(buildWorkflowFilter(app, as.getString(), as.getObject())) : Optional.empty(); } public static StringFilter buildStringFilter( - ExpressionFactory exprFactory, String expression, String literal) { - return expression != null - ? toString(buildWorkflowFilter(exprFactory, expression)) - : toString(literal); + WorkflowApplication app, String expression, String literal) { + return expression != null ? toString(buildWorkflowFilter(app, expression)) : toString(literal); } - public static StringFilter buildStringFilter(ExpressionFactory exprFactory, String str) { - return ExpressionUtils.isExpr(str) - ? toString(buildWorkflowFilter(exprFactory, str)) - : toString(str); + public static StringFilter buildStringFilter(WorkflowApplication app, String str) { + return ExpressionUtils.isExpr(str) ? toString(buildWorkflowFilter(app, str)) : toString(str); } private static StringFilter toString(WorkflowFilter filter) { - return (w, t) -> filter.apply(w, t, t.input()).asText(); + return (w, t) -> + filter + .apply(w, t, t.input()) + .asText() + .orElseThrow(() -> new IllegalArgumentException("Result is not an string")); } private static StringFilter toString(String literal) { @@ -132,43 +104,16 @@ private static StringFilter toString(String literal) { } public static WorkflowFilter buildWorkflowFilter( - ExpressionFactory exprFactory, String str, Object object) { - if (str != null) { - return buildWorkflowFilter(exprFactory, str); - } else if (object != null) { - Object exprObj = ExpressionUtils.buildExpressionObject(object, exprFactory); - return exprObj instanceof Map - ? (w, t, n) -> - JsonUtils.fromValue( - ExpressionUtils.evaluateExpressionMap((Map) exprObj, w, t, n)) - : (w, t, n) -> JsonUtils.fromValue(object); - } - throw new IllegalStateException("Both object and str are null"); - } - - public static LongFilter buildLongFilter( - ExpressionFactory exprFactory, String expression, Long literal) { - return expression != null - ? toLong(buildWorkflowFilter(exprFactory, expression)) - : toLong(literal); - } - - private static LongFilter toLong(WorkflowFilter filter) { - return (w, t) -> filter.apply(w, t, t.input()).asLong(); - } - - private static LongFilter toLong(Long literal) { - return (w, t) -> literal; + WorkflowApplication app, String str, Object object) { + return app.expressionFactory().buildFilter(str, object); } - public static WorkflowFilter buildWorkflowFilter(ExpressionFactory exprFactory, String str) { - assert str != null; - Expression expression = exprFactory.getExpression(str); - return expression::eval; + public static WorkflowFilter buildWorkflowFilter(WorkflowApplication app, String str) { + return app.expressionFactory().buildFilter(str, null); } - public static Optional optionalFilter(ExpressionFactory exprFactory, String str) { - return str != null ? Optional.of(buildWorkflowFilter(exprFactory, str)) : Optional.empty(); + public static Optional optionalFilter(WorkflowApplication app, String str) { + return str != null ? Optional.of(buildWorkflowFilter(app, str)) : Optional.empty(); } public static String toString(UriTemplate template) { diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/events/AbstractTypeConsumer.java b/impl/core/src/main/java/io/serverlessworkflow/impl/events/AbstractTypeConsumer.java index a3222342..d4a613d2 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/events/AbstractTypeConsumer.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/events/AbstractTypeConsumer.java @@ -51,7 +51,7 @@ public TypeEventRegistrationBuilder listen( EventProperties properties = register.getWith(); String type = properties.getType(); return new TypeEventRegistrationBuilder( - type, new DefaultCloudEventPredicate(properties, application.expressionFactory())); + type, new DefaultCloudEventPredicate(properties, application)); } @Override diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/events/CloudEventUtils.java b/impl/core/src/main/java/io/serverlessworkflow/impl/events/CloudEventUtils.java index 1b2709b8..a5a5e04c 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/events/CloudEventUtils.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/events/CloudEventUtils.java @@ -20,7 +20,6 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import io.cloudevents.CloudEvent; import io.cloudevents.CloudEventData; -import io.cloudevents.core.builder.CloudEventBuilder; import io.cloudevents.jackson.JsonCloudEventData; import io.serverlessworkflow.impl.json.JsonUtils; import java.io.IOException; @@ -64,18 +63,6 @@ public static OffsetDateTime toOffset(Date date) { return date.toInstant().atOffset(ZoneOffset.UTC); } - public static CloudEventBuilder addExtension( - CloudEventBuilder builder, String name, JsonNode value) { - if (value.isTextual()) { - builder.withExtension(name, value.asText()); - } else if (value.isBoolean()) { - builder.withExtension(name, value.isBoolean()); - } else if (value.isNumber()) { - builder.withExtension(name, value.numberValue()); - } - return builder; - } - public static JsonNode toJsonNode(CloudEventData data) { if (data == null) { return NullNode.instance; diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/events/DefaultCloudEventPredicate.java b/impl/core/src/main/java/io/serverlessworkflow/impl/events/DefaultCloudEventPredicate.java index 6eb35995..ee319727 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/events/DefaultCloudEventPredicate.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/events/DefaultCloudEventPredicate.java @@ -15,19 +15,19 @@ */ package io.serverlessworkflow.impl.events; -import com.fasterxml.jackson.databind.JsonNode; import io.cloudevents.CloudEvent; +import io.cloudevents.CloudEventData; import io.serverlessworkflow.api.types.EventData; import io.serverlessworkflow.api.types.EventDataschema; import io.serverlessworkflow.api.types.EventProperties; import io.serverlessworkflow.api.types.EventSource; import io.serverlessworkflow.api.types.EventTime; import io.serverlessworkflow.api.types.UriTemplate; +import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowFilter; +import io.serverlessworkflow.impl.WorkflowModelFactory; import io.serverlessworkflow.impl.WorkflowUtils; import io.serverlessworkflow.impl.expressions.Expression; -import io.serverlessworkflow.impl.expressions.ExpressionFactory; -import io.serverlessworkflow.impl.json.JsonUtils; import java.net.URI; import java.time.OffsetDateTime; import java.util.Map; @@ -42,51 +42,59 @@ public class DefaultCloudEventPredicate implements CloudEventPredicate { private final CloudEventAttrPredicate typeFilter; private final CloudEventAttrPredicate dataSchemaFilter; private final CloudEventAttrPredicate timeFilter; - private final CloudEventAttrPredicate dataFilter; - private final CloudEventAttrPredicate additionalFilter; + private final CloudEventAttrPredicate dataFilter; + private final CloudEventAttrPredicate> additionalFilter; private static final CloudEventAttrPredicate isTrue() { return x -> true; } - public DefaultCloudEventPredicate(EventProperties properties, ExpressionFactory exprFactory) { + public DefaultCloudEventPredicate(EventProperties properties, WorkflowApplication app) { idFilter = stringFilter(properties.getId()); subjectFilter = stringFilter(properties.getSubject()); typeFilter = stringFilter(properties.getType()); contentTypeFilter = stringFilter(properties.getDatacontenttype()); - sourceFilter = sourceFilter(properties.getSource(), exprFactory); - dataSchemaFilter = dataSchemaFilter(properties.getDataschema(), exprFactory); - timeFilter = offsetTimeFilter(properties.getTime(), exprFactory); - dataFilter = dataFilter(properties.getData(), exprFactory); - additionalFilter = additionalFilter(properties.getAdditionalProperties(), exprFactory); + sourceFilter = sourceFilter(properties.getSource(), app); + dataSchemaFilter = dataSchemaFilter(properties.getDataschema(), app); + timeFilter = offsetTimeFilter(properties.getTime(), app); + dataFilter = dataFilter(properties.getData(), app); + additionalFilter = additionalFilter(properties.getAdditionalProperties(), app); } - private CloudEventAttrPredicate additionalFilter( - Map additionalProperties, ExpressionFactory exprFactory) { + private CloudEventAttrPredicate> additionalFilter( + Map additionalProperties, WorkflowApplication app) { return additionalProperties != null && !additionalProperties.isEmpty() - ? from(WorkflowUtils.buildWorkflowFilter(exprFactory, null, additionalProperties)) + ? fromMap( + app.modelFactory(), WorkflowUtils.buildWorkflowFilter(app, null, additionalProperties)) : isTrue(); } - private CloudEventAttrPredicate from(WorkflowFilter filter) { - return d -> filter.apply(null, null, d).asBoolean(); + private CloudEventAttrPredicate fromCloudEvent( + WorkflowModelFactory workflowModelFactory, WorkflowFilter filter) { + return d -> filter.apply(null, null, workflowModelFactory.from(d)).asBoolean().orElse(false); } - private CloudEventAttrPredicate dataFilter( - EventData data, ExpressionFactory exprFactory) { + private CloudEventAttrPredicate> fromMap( + WorkflowModelFactory workflowModelFactory, WorkflowFilter filter) { + return d -> filter.apply(null, null, workflowModelFactory.from(d)).asBoolean().orElse(false); + } + + private CloudEventAttrPredicate dataFilter( + EventData data, WorkflowApplication app) { return data != null - ? from( - WorkflowUtils.buildWorkflowFilter( - exprFactory, data.getRuntimeExpression(), data.getObject())) + ? fromCloudEvent( + app.modelFactory(), + WorkflowUtils.buildWorkflowFilter(app, data.getRuntimeExpression(), data.getObject())) : isTrue(); } private CloudEventAttrPredicate offsetTimeFilter( - EventTime time, ExpressionFactory exprFactory) { + EventTime time, WorkflowApplication app) { if (time != null) { if (time.getRuntimeExpression() != null) { - final Expression expr = exprFactory.getExpression(time.getRuntimeExpression()); - return s -> evalExpr(expr, toString(s)); + final Expression expr = + app.expressionFactory().buildExpression(time.getRuntimeExpression()); + return s -> evalExpr(app.modelFactory(), expr, s); } else if (time.getLiteralTime() != null) { return s -> Objects.equals(s, CloudEventUtils.toOffset(time.getLiteralTime())); } @@ -95,11 +103,12 @@ private CloudEventAttrPredicate offsetTimeFilter( } private CloudEventAttrPredicate dataSchemaFilter( - EventDataschema dataSchema, ExpressionFactory exprFactory) { + EventDataschema dataSchema, WorkflowApplication app) { if (dataSchema != null) { if (dataSchema.getExpressionDataSchema() != null) { - final Expression expr = exprFactory.getExpression(dataSchema.getExpressionDataSchema()); - return s -> evalExpr(expr, toString(s)); + final Expression expr = + app.expressionFactory().buildExpression(dataSchema.getExpressionDataSchema()); + return s -> evalExpr(app.modelFactory(), expr, toString(s)); } else if (dataSchema.getLiteralDataSchema() != null) { return templateFilter(dataSchema.getLiteralDataSchema()); } @@ -111,12 +120,12 @@ private CloudEventAttrPredicate stringFilter(String str) { return str == null ? isTrue() : x -> x.equals(str); } - private CloudEventAttrPredicate sourceFilter( - EventSource source, ExpressionFactory exprFactory) { + private CloudEventAttrPredicate sourceFilter(EventSource source, WorkflowApplication app) { if (source != null) { if (source.getRuntimeExpression() != null) { - final Expression expr = exprFactory.getExpression(source.getRuntimeExpression()); - return s -> evalExpr(expr, toString(s)); + final Expression expr = + app.expressionFactory().buildExpression(source.getRuntimeExpression()); + return s -> evalExpr(app.modelFactory(), expr, toString(s)); } else if (source.getUriTemplate() != null) { return templateFilter(source.getUriTemplate()); } @@ -128,15 +137,20 @@ private CloudEventAttrPredicate templateFilter(UriTemplate template) { if (template.getLiteralUri() != null) { return u -> Objects.equals(u, template.getLiteralUri()); } - throw new UnsupportedOperationException("Template not supporte here yet"); + throw new UnsupportedOperationException("Template not supported here yet"); } private String toString(T uri) { return uri != null ? uri.toString() : null; } - private boolean evalExpr(Expression expr, T value) { - return expr.eval(null, null, JsonUtils.fromValue(value)).asBoolean(); + private boolean evalExpr(WorkflowModelFactory modelFactory, Expression expr, String value) { + return expr.eval(null, null, modelFactory.from(value)).asBoolean().orElse(false); + } + + private boolean evalExpr( + WorkflowModelFactory modelFactory, Expression expr, OffsetDateTime value) { + return expr.eval(null, null, modelFactory.from(value)).asBoolean().orElse(false); } @Override @@ -148,7 +162,7 @@ public boolean test(CloudEvent event) { && typeFilter.test(event.getType()) && dataSchemaFilter.test(event.getDataSchema()) && timeFilter.test(event.getTime()) - && dataFilter.test(CloudEventUtils.toJsonNode(event.getData())) - && additionalFilter.test(JsonUtils.fromValue(CloudEventUtils.extensions(event))); + && dataFilter.test(event.getData()) + && additionalFilter.test(CloudEventUtils.extensions(event)); } } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/events/InMemoryEvents.java b/impl/core/src/main/java/io/serverlessworkflow/impl/events/InMemoryEvents.java index 714d89d0..edd2dad7 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/events/InMemoryEvents.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/events/InMemoryEvents.java @@ -25,7 +25,7 @@ /* * Straightforward implementation of in memory event broker. - * User might invoke notifyCE to simulate event reception. + * User might invoke publish to simulate event reception. */ public class InMemoryEvents extends AbstractTypeConsumer implements EventPublisher { diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java index 23ca9a22..18d23d85 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java @@ -17,7 +17,6 @@ import static io.serverlessworkflow.impl.WorkflowUtils.*; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.api.types.Export; import io.serverlessworkflow.api.types.FlowDirective; import io.serverlessworkflow.api.types.Input; @@ -28,6 +27,7 @@ import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowFilter; +import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowPosition; import io.serverlessworkflow.impl.WorkflowStatus; import io.serverlessworkflow.impl.jsonschema.SchemaValidator; @@ -83,26 +83,25 @@ protected AbstractTaskExecutorBuilder( this.resourceLoader = resourceLoader; if (task.getInput() != null) { Input input = task.getInput(); - this.inputProcessor = buildWorkflowFilter(application.expressionFactory(), input.getFrom()); + this.inputProcessor = buildWorkflowFilter(application, input.getFrom()); this.inputSchemaValidator = getSchemaValidator(application.validatorFactory(), resourceLoader, input.getSchema()); } if (task.getOutput() != null) { Output output = task.getOutput(); - this.outputProcessor = buildWorkflowFilter(application.expressionFactory(), output.getAs()); + this.outputProcessor = buildWorkflowFilter(application, output.getAs()); this.outputSchemaValidator = getSchemaValidator(application.validatorFactory(), resourceLoader, output.getSchema()); } if (task.getExport() != null) { Export export = task.getExport(); if (export.getAs() != null) { - this.contextProcessor = - buildWorkflowFilter(application.expressionFactory(), export.getAs()); + this.contextProcessor = buildWorkflowFilter(application, export.getAs()); } this.contextSchemaValidator = getSchemaValidator(application.validatorFactory(), resourceLoader, export.getSchema()); } - this.ifFilter = optionalFilter(application.expressionFactory(), task.getIf()); + this.ifFilter = optionalFilter(application, task.getIf()); } protected final TransitionInfoBuilder next( @@ -175,14 +174,14 @@ protected final CompletableFuture executeNext( @Override public CompletableFuture apply( - WorkflowContext workflowContext, Optional parentContext, JsonNode input) { + WorkflowContext workflowContext, Optional parentContext, WorkflowModel input) { TaskContext taskContext = new TaskContext(input, position, parentContext, taskName, task); CompletableFuture completable = CompletableFuture.completedFuture(taskContext); if (!TaskExecutorHelper.isActive(workflowContext)) { return completable; } if (ifFilter - .map(f -> f.apply(workflowContext, taskContext, input).asBoolean(true)) + .flatMap(f -> f.apply(workflowContext, taskContext, input).asBoolean()) .orElse(true)) { return executeNext( completable diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/CallTaskExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/CallTaskExecutor.java index 2a3d1ae9..56545b68 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/CallTaskExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/CallTaskExecutor.java @@ -15,14 +15,13 @@ */ package io.serverlessworkflow.impl.executors; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.api.types.TaskBase; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowContext; +import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowPosition; -import io.serverlessworkflow.impl.executors.RegularTaskExecutor.RegularTaskExecutorBuilder; import io.serverlessworkflow.impl.resources.ResourceLoader; import java.util.concurrent.CompletableFuture; @@ -48,7 +47,7 @@ protected CallTaskExecutorBuilder( @Override public TaskExecutor buildInstance() { - return new CallTaskExecutor(this); + return new CallTaskExecutor<>(this); } } @@ -58,7 +57,7 @@ protected CallTaskExecutor(CallTaskExecutorBuilder builder) { } @Override - protected CompletableFuture internalExecute( + protected CompletableFuture internalExecute( WorkflowContext workflow, TaskContext taskContext) { return callable.apply(workflow, taskContext, taskContext.input()); } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/CallableTask.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/CallableTask.java index ecff0662..e391dae6 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/CallableTask.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/CallableTask.java @@ -15,19 +15,19 @@ */ package io.serverlessworkflow.impl.executors; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.api.types.TaskBase; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowContext; +import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.resources.ResourceLoader; import java.util.concurrent.CompletableFuture; public interface CallableTask { void init(T task, WorkflowApplication application, ResourceLoader loader); - CompletableFuture apply( - WorkflowContext workflowContext, TaskContext taskContext, JsonNode input); + CompletableFuture apply( + WorkflowContext workflowContext, TaskContext taskContext, WorkflowModel input); boolean accept(Class clazz); } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/DoExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/DoExecutor.java index a35e4a87..65e3469b 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/DoExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/DoExecutor.java @@ -15,12 +15,12 @@ */ package io.serverlessworkflow.impl.executors; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.api.types.DoTask; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowContext; +import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowPosition; import io.serverlessworkflow.impl.resources.ResourceLoader; import java.util.Optional; @@ -57,7 +57,7 @@ private DoExecutor(DoExecutorBuilder builder) { } @Override - protected CompletableFuture internalExecute( + protected CompletableFuture internalExecute( WorkflowContext workflow, TaskContext taskContext) { return TaskExecutorHelper.processTaskList( taskExecutor, workflow, Optional.of(taskContext), taskContext.input()); diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/EmitExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/EmitExecutor.java index 7a8eb09d..1c7f99df 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/EmitExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/EmitExecutor.java @@ -15,10 +15,8 @@ */ package io.serverlessworkflow.impl.executors; -import com.fasterxml.jackson.databind.JsonNode; import io.cloudevents.CloudEvent; import io.cloudevents.core.builder.CloudEventBuilder; -import io.cloudevents.jackson.JsonCloudEventData; import io.serverlessworkflow.api.types.EmitTask; import io.serverlessworkflow.api.types.EventData; import io.serverlessworkflow.api.types.EventDataschema; @@ -32,11 +30,10 @@ import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowFilter; +import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowPosition; import io.serverlessworkflow.impl.WorkflowUtils; import io.serverlessworkflow.impl.events.CloudEventUtils; -import io.serverlessworkflow.impl.expressions.ExpressionFactory; -import io.serverlessworkflow.impl.json.JsonUtils; import io.serverlessworkflow.impl.resources.ResourceLoader; import java.net.URI; import java.time.OffsetDateTime; @@ -61,8 +58,7 @@ protected EmitExecutorBuilder( ResourceLoader resourceLoader) { super(position, task, workflow, application, resourceLoader); this.eventBuilder = - EventPropertiesBuilder.build( - task.getEmit().getEvent().getWith(), application.expressionFactory()); + EventPropertiesBuilder.build(task.getEmit().getEvent().getWith(), application); } @Override @@ -77,7 +73,7 @@ private EmitExecutor(EmitExecutorBuilder builder) { } @Override - protected CompletableFuture internalExecute( + protected CompletableFuture internalExecute( WorkflowContext workflow, TaskContext taskContext) { return workflow .definition() @@ -125,19 +121,41 @@ private CloudEvent buildCloudEvent(WorkflowContext workflow, TaskContext taskCon props .dataFilter() .map(filter -> filter.apply(workflow, taskContext, taskContext.input())) - .ifPresent(value -> ceBuilder.withData(JsonCloudEventData.wrap(value))); + .ifPresent( + value -> + ceBuilder.withData( + value + .asCloudEventData() + .orElseThrow( + () -> + new IllegalArgumentException( + "Workflow model " + + value + + " cannot be converted to CloudEvent")))); + // TODO JsonCloudEventData.wrap(value) props .additionalFilter() .map(filter -> filter.apply(workflow, taskContext, taskContext.input())) - .ifPresent( - value -> - value - .fields() - .forEachRemaining( - e -> CloudEventUtils.addExtension(ceBuilder, e.getKey(), e.getValue()))); + .ifPresent(value -> value.forEach((k, v) -> addExtension(ceBuilder, k, v))); + return ceBuilder.build(); } + private static CloudEventBuilder addExtension( + CloudEventBuilder builder, String name, WorkflowModel value) { + value + .asText() + .ifPresentOrElse( + v -> builder.withExtension(name, v), + () -> + value + .asBoolean() + .ifPresentOrElse( + v -> builder.withExtension(name, v), + () -> value.asNumber().ifPresent(v -> builder.withExtension(name, v)))); + return builder; + } + private static record EventPropertiesBuilder( Optional idFilter, Optional sourceFilter, @@ -150,28 +168,27 @@ private static record EventPropertiesBuilder( Optional additionalFilter) { public static EventPropertiesBuilder build( - EventProperties properties, ExpressionFactory exprFactory) { - Optional idFilter = buildFilter(exprFactory, properties.getId()); + EventProperties properties, WorkflowApplication app) { + Optional idFilter = buildFilter(app, properties.getId()); EventSource source = properties.getSource(); Optional sourceFilter = source == null ? Optional.empty() : Optional.of( WorkflowUtils.buildStringFilter( - exprFactory, + app, source.getRuntimeExpression(), WorkflowUtils.toString(source.getUriTemplate()))); - Optional subjectFilter = buildFilter(exprFactory, properties.getSubject()); - Optional contentTypeFilter = - buildFilter(exprFactory, properties.getDatacontenttype()); - Optional typeFilter = buildFilter(exprFactory, properties.getType()); + Optional subjectFilter = buildFilter(app, properties.getSubject()); + Optional contentTypeFilter = buildFilter(app, properties.getDatacontenttype()); + Optional typeFilter = buildFilter(app, properties.getType()); EventDataschema dataSchema = properties.getDataschema(); Optional dataSchemaFilter = dataSchema == null ? Optional.empty() : Optional.of( WorkflowUtils.buildStringFilter( - exprFactory, + app, dataSchema.getExpressionDataSchema(), WorkflowUtils.toString(dataSchema.getLiteralDataSchema()))); EventTime time = properties.getTime(); @@ -180,22 +197,27 @@ public static EventPropertiesBuilder build( ? Optional.empty() : Optional.of( WorkflowUtils.buildExpressionHolder( - exprFactory, + app, time.getRuntimeExpression(), CloudEventUtils.toOffset(time.getLiteralTime()), - JsonUtils::toOffsetDateTime)); + v -> + v.asDate() + .orElseThrow( + () -> + new IllegalArgumentException( + "Expression does not generate a valid date")))); EventData data = properties.getData(); Optional dataFilter = properties.getData() == null ? Optional.empty() : Optional.of( WorkflowUtils.buildWorkflowFilter( - exprFactory, data.getRuntimeExpression(), data.getObject())); + app, data.getRuntimeExpression(), data.getObject())); Map ceAttrs = properties.getAdditionalProperties(); Optional additionalFilter = ceAttrs == null || ceAttrs.isEmpty() ? Optional.empty() - : Optional.of(WorkflowUtils.buildWorkflowFilter(exprFactory, null, ceAttrs)); + : Optional.of(WorkflowUtils.buildWorkflowFilter(app, null, ceAttrs)); return new EventPropertiesBuilder( idFilter, sourceFilter, @@ -208,10 +230,10 @@ public static EventPropertiesBuilder build( additionalFilter); } - private static Optional buildFilter(ExpressionFactory exprFactory, String str) { + private static Optional buildFilter(WorkflowApplication appl, String str) { return str == null ? Optional.empty() - : Optional.of(WorkflowUtils.buildStringFilter(exprFactory, str)); + : Optional.of(WorkflowUtils.buildStringFilter(appl, str)); } } } 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 8f7e04f1..15b5e744 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 @@ -15,7 +15,6 @@ */ package io.serverlessworkflow.impl.executors; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.api.types.ForTask; import io.serverlessworkflow.api.types.ForTaskConfiguration; import io.serverlessworkflow.api.types.Workflow; @@ -23,6 +22,7 @@ import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowFilter; +import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowPosition; import io.serverlessworkflow.impl.WorkflowUtils; import io.serverlessworkflow.impl.executors.RegularTaskExecutor.RegularTaskExecutorBuilder; @@ -50,10 +50,8 @@ protected ForExecutorBuilder( ResourceLoader resourceLoader) { super(position, task, workflow, application, resourceLoader); ForTaskConfiguration forConfig = task.getFor(); - this.collectionExpr = - WorkflowUtils.buildWorkflowFilter(application.expressionFactory(), forConfig.getIn()); - this.whileExpr = - WorkflowUtils.optionalFilter(application.expressionFactory(), task.getWhile()); + this.collectionExpr = WorkflowUtils.buildWorkflowFilter(application, forConfig.getIn()); + this.whileExpr = WorkflowUtils.optionalFilter(application, task.getWhile()); this.taskExecutor = TaskExecutorHelper.createExecutorList( position, task.getDo(), workflow, application, resourceLoader); @@ -73,18 +71,19 @@ protected ForExecutor(ForExecutorBuilder builder) { } @Override - protected CompletableFuture internalExecute( + protected CompletableFuture internalExecute( WorkflowContext workflow, TaskContext taskContext) { - Iterator iter = - collectionExpr.apply(workflow, taskContext, taskContext.input()).iterator(); + Iterator iter = + collectionExpr.apply(workflow, taskContext, taskContext.input()).asCollection().iterator(); int i = 0; - CompletableFuture future = CompletableFuture.completedFuture(taskContext.input()); + CompletableFuture future = + CompletableFuture.completedFuture(taskContext.input()); while (iter.hasNext() && whileExpr - .map(w -> w.apply(workflow, taskContext, taskContext.rawOutput())) - .map(n -> n.asBoolean(true)) + .map(w -> w.apply(workflow, taskContext, taskContext.rawOutput())) + .map(n -> n.asBoolean().orElse(true)) .orElse(true)) { - JsonNode item = iter.next(); + WorkflowModel item = iter.next(); taskContext.variables().put(task.getFor().getEach(), item); taskContext.variables().put(task.getFor().getAt(), i++); future = diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/ForkExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/ForkExecutor.java index 85bd3f22..d92eb1a6 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/ForkExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/ForkExecutor.java @@ -15,16 +15,15 @@ */ package io.serverlessworkflow.impl.executors; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.api.types.ForkTask; import io.serverlessworkflow.api.types.ForkTaskConfiguration; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowContext; +import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowPosition; import io.serverlessworkflow.impl.executors.RegularTaskExecutor.RegularTaskExecutorBuilder; -import io.serverlessworkflow.impl.json.JsonUtils; import io.serverlessworkflow.impl.resources.ResourceLoader; import java.util.HashMap; import java.util.Map; @@ -39,6 +38,7 @@ public class ForkExecutor extends RegularTaskExecutor { private final ExecutorService service; private final Map> taskExecutors; + private final boolean compete; public static class ForkExecutorBuilder extends RegularTaskExecutorBuilder { @@ -74,7 +74,7 @@ protected ForkExecutor(ForkExecutorBuilder builder) { } @Override - protected CompletableFuture internalExecute( + protected CompletableFuture internalExecute( WorkflowContext workflow, TaskContext taskContext) { Map> futures = new HashMap<>(); CompletableFuture initial = CompletableFuture.completedFuture(taskContext); @@ -89,11 +89,12 @@ protected CompletableFuture internalExecute( .thenApply( i -> combine( + workflow, futures.entrySet().stream() .collect(Collectors.toMap(Entry::getKey, e -> e.getValue().join())))); } - private JsonNode combine(Map futures) { + private WorkflowModel combine(WorkflowContext context, Map futures) { Stream> sortedStream = futures.entrySet().stream() @@ -102,9 +103,11 @@ private JsonNode combine(Map futures) { arg1.getValue().completedAt().compareTo(arg2.getValue().completedAt())); return compete ? sortedStream.map(e -> e.getValue().output()).findFirst().orElseThrow() - : sortedStream - .map( - e -> JsonUtils.mapper().createObjectNode().set(e.getKey(), e.getValue().output())) - .collect(JsonUtils.arrayNodeCollector()); + : context + .definition() + .application() + .modelFactory() + .combine( + sortedStream.collect(Collectors.toMap(Entry::getKey, e -> e.getValue().output()))); } } 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 e351bae2..ebede8c1 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 @@ -15,8 +15,6 @@ */ package io.serverlessworkflow.impl.executors; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; import io.cloudevents.CloudEvent; import io.serverlessworkflow.api.types.AllEventConsumptionStrategy; import io.serverlessworkflow.api.types.AnyEventConsumptionStrategy; @@ -34,14 +32,14 @@ import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowFilter; +import io.serverlessworkflow.impl.WorkflowModel; +import io.serverlessworkflow.impl.WorkflowModelCollection; import io.serverlessworkflow.impl.WorkflowPosition; import io.serverlessworkflow.impl.WorkflowStatus; import io.serverlessworkflow.impl.WorkflowUtils; -import io.serverlessworkflow.impl.events.CloudEventUtils; import io.serverlessworkflow.impl.events.EventConsumer; import io.serverlessworkflow.impl.events.EventRegistration; import io.serverlessworkflow.impl.events.EventRegistrationBuilder; -import io.serverlessworkflow.impl.json.JsonUtils; import io.serverlessworkflow.impl.resources.ResourceLoader; import java.util.ArrayList; import java.util.Collection; @@ -56,7 +54,7 @@ public abstract class ListenExecutor extends RegularTaskExecutor { protected final EventRegistrationBuilderCollection regBuilders; protected final Optional> loop; - protected final Function converter; + protected final Function converter; protected final EventConsumer eventConsumer; private static record EventRegistrationBuilderCollection( @@ -68,7 +66,8 @@ public static class ListenExecutorBuilder extends RegularTaskExecutorBuilder loop; - private Function converter = this::defaultCEConverter; + private Function converter = + ce -> application.modelFactory().from(ce.getData()); private EventRegistrationBuilderCollection allEvents(AllEventConsumptionStrategy allStrategy) { return new EventRegistrationBuilderCollection(from(allStrategy.getAll()), true); @@ -103,7 +102,7 @@ protected ListenExecutorBuilder( if (untilDesc.getAnyEventUntilCondition() != null) { until = WorkflowUtils.buildWorkflowFilter( - application.expressionFactory(), untilDesc.getAnyEventUntilCondition()); + application, untilDesc.getAnyEventUntilCondition()); } else if (untilDesc.getAnyEventUntilConsumed() != null) { EventConsumptionStrategy strategy = untilDesc.getAnyEventUntilConsumed(); if (strategy.getAllEventConsumptionStrategy() != null) { @@ -128,10 +127,10 @@ protected ListenExecutorBuilder( if (readAs != null) { switch (readAs) { case ENVELOPE: - converter = CloudEventUtils::toJsonNode; + converter = ce -> application.modelFactory().from(ce); default: case DATA: - converter = this::defaultCEConverter; + converter = ce -> application.modelFactory().from(ce.getData()); break; } } @@ -141,10 +140,6 @@ private Collection registerToAll() { return application.eventConsumer().listenToAll(application); } - private JsonNode defaultCEConverter(CloudEvent ce) { - return CloudEventUtils.toJsonNode(ce.getData()); - } - private Collection from(List filters) { return filters.stream().map(this::from).collect(Collectors.toList()); } @@ -166,11 +161,11 @@ public AndListenExecutor(ListenExecutorBuilder builder) { } protected void internalProcessCe( - JsonNode node, - ArrayNode arrayNode, + WorkflowModel node, + WorkflowModelCollection arrayNode, WorkflowContext workflow, TaskContext taskContext, - CompletableFuture future) { + CompletableFuture future) { arrayNode.add(node); future.complete(node); } @@ -208,16 +203,14 @@ protected CompletableFuture buildFuture( } protected void internalProcessCe( - JsonNode node, - ArrayNode arrayNode, + WorkflowModel node, + WorkflowModelCollection arrayNode, WorkflowContext workflow, TaskContext taskContext, - CompletableFuture future) { + CompletableFuture future) { arrayNode.add(node); if ((until.isEmpty() - || until - .filter(u -> u.apply(workflow, taskContext, arrayNode).asBoolean()) - .isPresent()) + || until.map(u -> u.apply(workflow, taskContext, arrayNode).asBoolean()).isPresent()) && untilRegBuilders == null) { future.complete(node); } @@ -225,22 +218,23 @@ protected void internalProcessCe( } protected abstract void internalProcessCe( - JsonNode node, - ArrayNode arrayNode, + WorkflowModel node, + WorkflowModelCollection arrayNode, WorkflowContext workflow, TaskContext taskContext, - CompletableFuture future); + CompletableFuture future); @Override - protected CompletableFuture internalExecute( + protected CompletableFuture internalExecute( WorkflowContext workflow, TaskContext taskContext) { - ArrayNode output = JsonUtils.mapper().createArrayNode(); + WorkflowModelCollection output = + workflow.definition().application().modelFactory().createCollection(); Collection registrations = new ArrayList<>(); workflow.instance().status(WorkflowStatus.WAITING); return buildFuture( regBuilders, registrations, - (BiConsumer>) + (BiConsumer>) ((ce, future) -> processCe(converter.apply(ce), output, workflow, taskContext, future))) .thenApply( @@ -282,11 +276,11 @@ private CompletableFuture toCompletable( } private void processCe( - JsonNode node, - ArrayNode arrayNode, + WorkflowModel node, + WorkflowModelCollection arrayNode, WorkflowContext workflow, TaskContext taskContext, - CompletableFuture future) { + CompletableFuture future) { loop.ifPresentOrElse( t -> { SubscriptionIterator forEach = task.getForeach(); diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RaiseExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RaiseExecutor.java index 7a2c4025..27c9018f 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RaiseExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RaiseExecutor.java @@ -15,7 +15,6 @@ */ package io.serverlessworkflow.impl.executors; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.api.types.Error; import io.serverlessworkflow.api.types.ErrorInstance; import io.serverlessworkflow.api.types.ErrorType; @@ -28,9 +27,9 @@ import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowError; import io.serverlessworkflow.impl.WorkflowException; +import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowPosition; import io.serverlessworkflow.impl.WorkflowUtils; -import io.serverlessworkflow.impl.expressions.ExpressionFactory; import io.serverlessworkflow.impl.resources.ResourceLoader; import java.util.Map; import java.util.Optional; @@ -61,17 +60,16 @@ protected RaiseExecutorBuilder( raiseError.getRaiseErrorDefinition() != null ? raiseError.getRaiseErrorDefinition() : findError(raiseError.getRaiseErrorReference()); - this.typeFilter = getTypeFunction(application.expressionFactory(), error.getType()); - this.instanceFilter = - getInstanceFunction(application.expressionFactory(), error.getInstance()); + this.typeFilter = getTypeFunction(application, error.getType()); + this.instanceFilter = getInstanceFunction(application, error.getInstance()); this.titleFilter = WorkflowUtils.buildStringFilter( - application.expressionFactory(), + application, error.getTitle().getExpressionErrorTitle(), error.getTitle().getLiteralErrorTitle()); this.detailFilter = WorkflowUtils.buildStringFilter( - application.expressionFactory(), + application, error.getDetail().getExpressionErrorDetails(), error.getTitle().getExpressionErrorTitle()); this.errorBuilder = (w, t) -> buildError(error, w, t); @@ -90,21 +88,19 @@ private WorkflowError buildError( } private Optional getInstanceFunction( - ExpressionFactory expressionFactory, ErrorInstance errorInstance) { + WorkflowApplication app, ErrorInstance errorInstance) { return errorInstance != null ? Optional.of( WorkflowUtils.buildStringFilter( - expressionFactory, + app, errorInstance.getExpressionErrorInstance(), errorInstance.getLiteralErrorInstance())) : Optional.empty(); } - private StringFilter getTypeFunction(ExpressionFactory expressionFactory, ErrorType type) { + private StringFilter getTypeFunction(WorkflowApplication app, ErrorType type) { return WorkflowUtils.buildStringFilter( - expressionFactory, - type.getExpressionErrorType(), - type.getLiteralErrorType().get().toString()); + app, type.getExpressionErrorType(), type.getLiteralErrorType().get().toString()); } private Error findError(String raiseErrorReference) { @@ -128,7 +124,7 @@ protected RaiseExecutor(RaiseExecutorBuilder builder) { } @Override - protected CompletableFuture internalExecute( + protected CompletableFuture internalExecute( WorkflowContext workflow, TaskContext taskContext) { throw new WorkflowException(errorBuilder.apply(workflow, taskContext)); } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RegularTaskExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RegularTaskExecutor.java index 7cac9a8e..c4a716c9 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RegularTaskExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RegularTaskExecutor.java @@ -15,12 +15,12 @@ */ package io.serverlessworkflow.impl.executors; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.api.types.TaskBase; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowContext; +import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowPosition; import io.serverlessworkflow.impl.resources.ResourceLoader; import java.util.Map; @@ -67,6 +67,6 @@ protected CompletableFuture execute( return future; } - protected abstract CompletableFuture internalExecute( + protected abstract CompletableFuture internalExecute( WorkflowContext workflow, TaskContext task); } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/SetExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/SetExecutor.java index f8373d39..9dc8c0a5 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/SetExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/SetExecutor.java @@ -15,7 +15,6 @@ */ package io.serverlessworkflow.impl.executors; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.api.types.Set; import io.serverlessworkflow.api.types.SetTask; import io.serverlessworkflow.api.types.SetTaskConfiguration; @@ -24,6 +23,7 @@ import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowFilter; +import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowPosition; import io.serverlessworkflow.impl.WorkflowUtils; import io.serverlessworkflow.impl.resources.ResourceLoader; @@ -48,7 +48,7 @@ protected SetExecutorBuilder( SetTaskConfiguration setConfig = setInfo.getSetTaskConfiguration(); this.setFilter = WorkflowUtils.buildWorkflowFilter( - application.expressionFactory(), + application, setInfo.getString(), setConfig != null ? setConfig.getAdditionalProperties() : null); } @@ -65,7 +65,7 @@ private SetExecutor(SetExecutorBuilder builder) { } @Override - protected CompletableFuture internalExecute( + protected CompletableFuture internalExecute( WorkflowContext workflow, TaskContext taskContext) { return CompletableFuture.completedFuture( setFilter.apply(workflow, taskContext, taskContext.input())); 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 9bd3a74a..424d4c97 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 @@ -55,9 +55,7 @@ public SwitchExecutorBuilder( SwitchCase switchCase = item.getSwitchCase(); if (switchCase.getWhen() != null) { workflowFilters.put( - switchCase, - WorkflowUtils.buildWorkflowFilter( - application.expressionFactory(), switchCase.getWhen())); + switchCase, WorkflowUtils.buildWorkflowFilter(application, switchCase.getWhen())); } else { defaultDirective = switchCase.getThen(); } @@ -97,7 +95,11 @@ protected CompletableFuture execute( WorkflowContext workflow, TaskContext taskContext) { CompletableFuture future = CompletableFuture.completedFuture(taskContext); for (Entry entry : workflowFilters.entrySet()) { - if (entry.getKey().apply(workflow, taskContext, taskContext.input()).asBoolean()) { + if (entry + .getKey() + .apply(workflow, taskContext, taskContext.input()) + .asBoolean() + .orElse(false)) { return future.thenApply(t -> t.transition(entry.getValue())); } } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TaskExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TaskExecutor.java index b77398c3..ebf492f3 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TaskExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TaskExecutor.java @@ -15,15 +15,15 @@ */ package io.serverlessworkflow.impl.executors; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.api.types.TaskBase; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowContext; +import io.serverlessworkflow.impl.WorkflowModel; import java.util.Optional; import java.util.concurrent.CompletableFuture; @FunctionalInterface public interface TaskExecutor { CompletableFuture apply( - WorkflowContext workflowContext, Optional parentContext, JsonNode input); + WorkflowContext workflowContext, Optional parentContext, WorkflowModel input); } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TaskExecutorHelper.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TaskExecutorHelper.java index 3fa77f5f..646a16f4 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TaskExecutorHelper.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TaskExecutorHelper.java @@ -15,12 +15,12 @@ */ package io.serverlessworkflow.impl.executors; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.api.types.TaskItem; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowContext; +import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowPosition; import io.serverlessworkflow.impl.WorkflowStatus; import io.serverlessworkflow.impl.resources.ResourceLoader; @@ -35,11 +35,11 @@ public class TaskExecutorHelper { private TaskExecutorHelper() {} - public static CompletableFuture processTaskList( + public static CompletableFuture processTaskList( TaskExecutor taskExecutor, WorkflowContext context, Optional parentTask, - JsonNode input) { + WorkflowModel input) { return taskExecutor .apply(context, parentTask, input) .thenApply( diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TryExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TryExecutor.java index a4442bf2..b3efca9e 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TryExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TryExecutor.java @@ -15,7 +15,6 @@ */ package io.serverlessworkflow.impl.executors; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.api.types.CatchErrors; import io.serverlessworkflow.api.types.ErrorFilter; import io.serverlessworkflow.api.types.TaskItem; @@ -28,6 +27,7 @@ import io.serverlessworkflow.impl.WorkflowError; import io.serverlessworkflow.impl.WorkflowException; import io.serverlessworkflow.impl.WorkflowFilter; +import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowPosition; import io.serverlessworkflow.impl.WorkflowUtils; import io.serverlessworkflow.impl.resources.ResourceLoader; @@ -62,10 +62,8 @@ protected TryExecutorBuilder( super(position, task, workflow, application, resourceLoader); TryTaskCatch catchInfo = task.getCatch(); this.errorFilter = buildErrorFilter(catchInfo.getErrors()); - this.whenFilter = - WorkflowUtils.optionalFilter(application.expressionFactory(), catchInfo.getWhen()); - this.exceptFilter = - WorkflowUtils.optionalFilter(application.expressionFactory(), catchInfo.getExceptWhen()); + this.whenFilter = WorkflowUtils.optionalFilter(application, catchInfo.getWhen()); + this.exceptFilter = WorkflowUtils.optionalFilter(application, catchInfo.getExceptWhen()); this.taskExecutor = TaskExecutorHelper.createExecutorList( position, task.getTry(), workflow, application, resourceLoader); @@ -94,14 +92,14 @@ protected TryExecutor(TryExecutorBuilder builder) { } @Override - protected CompletableFuture internalExecute( + protected CompletableFuture internalExecute( WorkflowContext workflow, TaskContext taskContext) { return TaskExecutorHelper.processTaskList( taskExecutor, workflow, Optional.of(taskContext), taskContext.input()) .exceptionallyCompose(e -> handleException(e, workflow, taskContext)); } - private CompletableFuture handleException( + private CompletableFuture handleException( Throwable e, WorkflowContext workflow, TaskContext taskContext) { if (e instanceof CompletionException) { return handleException(e.getCause(), workflow, taskContext); @@ -110,10 +108,14 @@ private CompletableFuture handleException( WorkflowException exception = (WorkflowException) e; if (errorFilter.map(f -> f.test(exception.getWorflowError())).orElse(true) && whenFilter - .map(w -> w.apply(workflow, taskContext, taskContext.input()).asBoolean()) + .flatMap(w -> w.apply(workflow, taskContext, taskContext.input()).asBoolean()) .orElse(true) && exceptFilter - .map(w -> !w.apply(workflow, taskContext, taskContext.input()).asBoolean()) + .map( + w -> + !w.apply(workflow, taskContext, taskContext.input()) + .asBoolean() + .orElse(false)) .orElse(true)) { if (catchTaskExecutor.isPresent()) { return TaskExecutorHelper.processTaskList( diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/WaitExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/WaitExecutor.java index 42e648aa..64ecde23 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/WaitExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/WaitExecutor.java @@ -15,16 +15,15 @@ */ package io.serverlessworkflow.impl.executors; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.api.types.DurationInline; import io.serverlessworkflow.api.types.WaitTask; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowContext; +import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowPosition; import io.serverlessworkflow.impl.WorkflowStatus; -import io.serverlessworkflow.impl.executors.RegularTaskExecutor.RegularTaskExecutorBuilder; import io.serverlessworkflow.impl.resources.ResourceLoader; import java.time.Duration; import java.util.concurrent.CompletableFuture; @@ -71,10 +70,10 @@ protected WaitExecutor(WaitExecutorBuilder builder) { } @Override - protected CompletableFuture internalExecute( + protected CompletableFuture internalExecute( WorkflowContext workflow, TaskContext taskContext) { workflow.instance().status(WorkflowStatus.WAITING); - return new CompletableFuture() + return new CompletableFuture() .completeOnTimeout(taskContext.output(), millisToWait.toMillis(), TimeUnit.MILLISECONDS) .thenApply( node -> { diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/Expression.java b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/Expression.java index 122fc6d8..f2e91ace 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/Expression.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/Expression.java @@ -15,10 +15,10 @@ */ package io.serverlessworkflow.impl.expressions; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowContext; +import io.serverlessworkflow.impl.WorkflowModel; public interface Expression { - JsonNode eval(WorkflowContext workflowContext, TaskContext context, JsonNode node); + WorkflowModel eval(WorkflowContext workflowContext, TaskContext context, WorkflowModel model); } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionFactory.java index 4d07d5af..696e4fda 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionFactory.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionFactory.java @@ -15,11 +15,18 @@ */ package io.serverlessworkflow.impl.expressions; +import io.serverlessworkflow.impl.WorkflowFilter; +import io.serverlessworkflow.impl.WorkflowModelFactory; + public interface ExpressionFactory { /** * @throws ExpressionValidationException * @param expression * @return */ - Expression getExpression(String expression); + Expression buildExpression(String expression); + + WorkflowFilter buildFilter(String expr, Object value); + + WorkflowModelFactory modelFactory(); } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionUtils.java b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionUtils.java index c91ef3a2..83f6fe1c 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionUtils.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionUtils.java @@ -15,10 +15,9 @@ */ package io.serverlessworkflow.impl.expressions; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowContext; -import io.serverlessworkflow.impl.json.JsonUtils; +import io.serverlessworkflow.impl.WorkflowModel; import java.util.Map; public class ExpressionUtils { @@ -30,30 +29,17 @@ private ExpressionUtils() {} public static Map buildExpressionMap( Map origMap, ExpressionFactory factory) { - return new ProxyMap(origMap, o -> isExpr(o) ? factory.getExpression(o.toString()) : o); + return new ProxyMap(origMap, o -> isExpr(o) ? factory.buildExpression(o.toString()) : o); } public static Map evaluateExpressionMap( - Map origMap, WorkflowContext workflow, TaskContext task, JsonNode n) { + Map origMap, WorkflowContext workflow, TaskContext task, WorkflowModel n) { return new ProxyMap( - origMap, - o -> - o instanceof Expression - ? JsonUtils.toJavaValue(((Expression) o).eval(workflow, task, n)) - : o); + origMap, o -> o instanceof Expression ? ((Expression) o).eval(workflow, task, n) : o); } public static Object buildExpressionObject(Object obj, ExpressionFactory factory) { - return obj instanceof Map - ? ExpressionUtils.buildExpressionMap((Map) obj, factory) - : obj; - } - - public static Object evaluateExpressionObject( - Object obj, WorkflowContext workflow, TaskContext task, JsonNode node) { - return obj instanceof Map - ? ExpressionUtils.evaluateExpressionMap((Map) obj, workflow, task, node) - : obj; + return obj instanceof Map map ? ExpressionUtils.buildExpressionMap(map, factory) : obj; } public static boolean isExpr(Object expr) { diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JQExpression.java b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JQExpression.java index 6041515a..e55f9877 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JQExpression.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JQExpression.java @@ -15,10 +15,14 @@ */ package io.serverlessworkflow.impl.expressions; +import static io.serverlessworkflow.impl.json.JsonUtils.modelToJson; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowContext; +import io.serverlessworkflow.impl.WorkflowModel; +import io.serverlessworkflow.impl.WorkflowModelFactory; import io.serverlessworkflow.impl.json.JsonUtils; import java.util.function.Supplier; import net.thisptr.jackson.jq.Output; @@ -32,20 +36,24 @@ public class JQExpression implements Expression { private final Supplier scope; private final String expr; private final net.thisptr.jackson.jq.Expression internalExpr; + private final WorkflowModelFactory modelFactory; - public JQExpression(Supplier scope, String expr, Version version) + public JQExpression( + Supplier scope, String expr, Version version, WorkflowModelFactory modelFactory) throws JsonQueryException { this.expr = expr; this.scope = scope; this.internalExpr = ExpressionParser.compile(expr, version); + this.modelFactory = modelFactory; } @Override - public JsonNode eval(WorkflowContext workflow, TaskContext task, JsonNode node) { + public WorkflowModel eval(WorkflowContext workflow, TaskContext task, WorkflowModel model) { JsonNodeOutput output = new JsonNodeOutput(); + JsonNode node = modelToJson(model); try { internalExpr.apply(createScope(workflow, task), node, output); - return output.getResult(); + return modelFactory.fromAny(output.getResult()); } catch (JsonQueryException e) { throw new IllegalArgumentException( "Unable to evaluate content " + node + " using expr " + expr, e); @@ -78,13 +86,13 @@ public JsonNode getResult() { private Scope createScope(WorkflowContext workflow, TaskContext task) { Scope childScope = Scope.newChildScope(scope.get()); if (task != null) { - childScope.setValue("input", task.input()); - childScope.setValue("output", task.output()); + childScope.setValue("input", modelToJson(task.input())); + childScope.setValue("output", modelToJson(task.output())); childScope.setValue("task", () -> JsonUtils.fromValue(TaskDescriptor.of(task))); task.variables().forEach((k, v) -> childScope.setValue(k, JsonUtils.fromValue(v))); } if (workflow != null) { - childScope.setValue("context", workflow.context()); + childScope.setValue("context", modelToJson(workflow.context())); childScope.setValue( "runtime", () -> diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JQExpressionFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JQExpressionFactory.java index 0375224a..5d552934 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JQExpressionFactory.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JQExpressionFactory.java @@ -15,18 +15,21 @@ */ package io.serverlessworkflow.impl.expressions; +import io.serverlessworkflow.impl.WorkflowModelFactory; import java.util.function.Supplier; import net.thisptr.jackson.jq.BuiltinFunctionLoader; import net.thisptr.jackson.jq.Scope; import net.thisptr.jackson.jq.Versions; import net.thisptr.jackson.jq.exception.JsonQueryException; -public class JQExpressionFactory implements ExpressionFactory { +public class JQExpressionFactory extends ObjectExpressionFactory { private JQExpressionFactory() {} private static final JQExpressionFactory instance = new JQExpressionFactory(); + private WorkflowModelFactory modelFactory = new JacksonModelFactory(); + public static JQExpressionFactory get() { return instance; } @@ -50,11 +53,17 @@ public Scope get() { } @Override - public Expression getExpression(String expression) { + public Expression buildExpression(String expression) { try { - return new JQExpression(scopeSupplier, ExpressionUtils.trimExpr(expression), Versions.JQ_1_6); + return new JQExpression( + scopeSupplier, ExpressionUtils.trimExpr(expression), Versions.JQ_1_6, modelFactory); } catch (JsonQueryException e) { throw new IllegalArgumentException(e); } } + + @Override + public WorkflowModelFactory modelFactory() { + return modelFactory; + } } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModel.java b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModel.java new file mode 100644 index 00000000..1deb520e --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModel.java @@ -0,0 +1,125 @@ +/* + * 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.expressions; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.BooleanNode; +import com.fasterxml.jackson.databind.node.NullNode; +import io.cloudevents.CloudEventData; +import io.cloudevents.jackson.JsonCloudEventData; +import io.serverlessworkflow.impl.WorkflowModel; +import io.serverlessworkflow.impl.json.JsonUtils; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiConsumer; + +@JsonSerialize(using = JacksonModelSerializer.class) +public class JacksonModel implements WorkflowModel { + + protected JsonNode node; + + public static final JacksonModel TRUE = new JacksonModel(BooleanNode.TRUE); + public static final JacksonModel FALSE = new JacksonModel(BooleanNode.FALSE); + public static final JacksonModel NULL = new JacksonModel(NullNode.instance); + + JacksonModel(JsonNode node) { + this.node = node; + } + + @Override + public void forEach(BiConsumer consumer) { + node.forEachEntry((k, v) -> consumer.accept(k, new JacksonModel(v))); + } + + @Override + public Optional asBoolean() { + return node.isBoolean() ? Optional.of(node.asBoolean()) : Optional.empty(); + } + + @Override + public Collection asCollection() { + return node.isArray() ? new JacksonModelCollection((ArrayNode) node) : Collections.emptyList(); + } + + @Override + public Optional asText() { + return node.isTextual() ? Optional.of(node.asText()) : Optional.empty(); + } + + @Override + public Optional asDate() { + if (node.isTextual()) { + return Optional.of(OffsetDateTime.parse(node.asText())); + } else if (node.isNumber()) { + return Optional.of( + OffsetDateTime.ofInstant(Instant.ofEpochMilli(node.asLong()), ZoneOffset.UTC)); + } else { + return Optional.empty(); + } + } + + @Override + public Optional asNumber() { + return node.isNumber() ? Optional.of(node.asLong()) : Optional.empty(); + } + + @Override + public Optional asCloudEventData() { + return node.isObject() ? Optional.of(JsonCloudEventData.wrap(node)) : Optional.empty(); + } + + @Override + public Optional as(Class clazz) { + return clazz.isAssignableFrom(node.getClass()) + ? Optional.of(clazz.cast(node)) + : Optional.of(JsonUtils.convertValue(node, clazz)); + } + + @Override + public String toString() { + return node.toPrettyString(); + } + + @Override + public Optional> asMap() { + // TODO use generic to avoid warning + return node.isObject() + ? Optional.of(JsonUtils.convertValue(node, Map.class)) + : Optional.empty(); + } + + @Override + public Object asJavaObject() { + return JsonUtils.toJavaValue(node); + } + + @Override + public Object asIs() { + return node; + } + + @Override + public Class objectClass() { + return node.getClass(); + } +} diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelCollection.java b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelCollection.java new file mode 100644 index 00000000..43da790a --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelCollection.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.impl.expressions; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import io.serverlessworkflow.impl.WorkflowModel; +import io.serverlessworkflow.impl.WorkflowModelCollection; +import io.serverlessworkflow.impl.json.JsonUtils; +import java.util.Collection; +import java.util.Iterator; +import java.util.Optional; + +public class JacksonModelCollection implements WorkflowModelCollection { + + private ArrayNode node; + + JacksonModelCollection() { + this.node = JsonUtils.array(); + } + + JacksonModelCollection(ArrayNode node) { + this.node = node; + } + + @Override + public Optional as(Class clazz) { + return clazz.isAssignableFrom(ArrayNode.class) + ? Optional.of(clazz.cast(node)) + : Optional.empty(); + } + + @Override + public int size() { + return node.size(); + } + + @Override + public boolean isEmpty() { + return node.isEmpty(); + } + + @Override + public boolean contains(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public Iterator iterator() { + return new WrapperIterator(node.iterator()); + } + + private class WrapperIterator implements Iterator { + + private Iterator iterator; + + public WrapperIterator(Iterator iterator) { + this.iterator = iterator; + } + + @Override + public boolean hasNext() { + + return iterator.hasNext(); + } + + @Override + public WorkflowModel next() { + return new JacksonModel(iterator.next()); + } + } + + @Override + public Object[] toArray() { + throw new UnsupportedOperationException(); + } + + @Override + public T[] toArray(T[] a) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean add(WorkflowModel e) { + node.add( + e.as(JsonNode.class).orElseThrow(() -> new IllegalArgumentException("Not a json node"))); + return true; + } + + @Override + public boolean remove(Object o) { + int size = node.size(); + node.removeIf(i -> i.equals(o)); + return node.size() < size; + } + + @Override + public boolean containsAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection c) { + c.forEach(this::add); + return true; + } + + @Override + public boolean removeAll(Collection c) { + int size = node.size(); + c.forEach(o -> node.removeIf(i -> i.equals(o))); + return node.size() < size; + } + + @Override + public boolean retainAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + node.removeAll(); + } + + @Override + public String toString() { + return node.toPrettyString(); + } + + @Override + public Object asJavaObject() { + return JsonUtils.toJavaValue(node); + } + + @Override + public Object asIs() { + return node; + } + + @Override + public Class objectClass() { + return ArrayNode.class; + } +} diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelFactory.java new file mode 100644 index 00000000..2c488707 --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelFactory.java @@ -0,0 +1,125 @@ +/* + * 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.expressions; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.DoubleNode; +import com.fasterxml.jackson.databind.node.FloatNode; +import com.fasterxml.jackson.databind.node.IntNode; +import com.fasterxml.jackson.databind.node.LongNode; +import com.fasterxml.jackson.databind.node.ShortNode; +import com.fasterxml.jackson.databind.node.TextNode; +import io.cloudevents.CloudEvent; +import io.cloudevents.CloudEventData; +import io.serverlessworkflow.impl.WorkflowModel; +import io.serverlessworkflow.impl.WorkflowModelCollection; +import io.serverlessworkflow.impl.WorkflowModelFactory; +import io.serverlessworkflow.impl.events.CloudEventUtils; +import io.serverlessworkflow.impl.json.JsonUtils; +import java.math.BigDecimal; +import java.time.OffsetDateTime; +import java.util.Map; + +public class JacksonModelFactory implements WorkflowModelFactory { + + @Override + public WorkflowModel combine(Map workflowVariables) { + return new JacksonModelCollection( + workflowVariables.entrySet().stream() + .map( + e -> + JsonUtils.object() + .set( + e.getKey(), + e.getValue() + .as(JsonNode.class) + .orElseThrow( + () -> + new IllegalArgumentException( + "Model cannot be converted ")))) + .collect(JsonUtils.arrayNodeCollector())); + } + + @Override + public WorkflowModelCollection createCollection() { + return new JacksonModelCollection(); + } + + @Override + public WorkflowModel from(boolean value) { + return value ? JacksonModel.TRUE : JacksonModel.FALSE; + } + + @Override + public WorkflowModel from(Number number) { + if (number instanceof Double value) { + return new JacksonModel(new DoubleNode(value)); + } else if (number instanceof BigDecimal) { + return new JacksonModel(new DoubleNode(number.doubleValue())); + } else if (number instanceof Float value) { + return new JacksonModel(new FloatNode(value)); + } else if (number instanceof Long value) { + return new JacksonModel(new LongNode(value)); + } else if (number instanceof Integer value) { + return new JacksonModel(new IntNode(value)); + } else if (number instanceof Short value) { + return new JacksonModel(new ShortNode(value)); + } else if (number instanceof Byte value) { + return new JacksonModel(new ShortNode(value)); + } else { + return new JacksonModel(new LongNode(number.longValue())); + } + } + + @Override + public WorkflowModel from(String value) { + return new JacksonModel(new TextNode(value)); + } + + @Override + public WorkflowModel from(CloudEvent ce) { + return new JacksonModel(CloudEventUtils.toJsonNode(ce)); + } + + @Override + public WorkflowModel from(CloudEventData ce) { + return new JacksonModel(CloudEventUtils.toJsonNode(ce)); + } + + @Override + public WorkflowModel from(OffsetDateTime value) { + return new JacksonModel(JsonUtils.fromValue(new TextNode(value.toString()))); + } + + @Override + public WorkflowModel from(Map map) { + return new JacksonModel(JsonUtils.fromValue(map)); + } + + @Override + public WorkflowModel fromAny(Object obj) { + if (obj instanceof JsonNode node) { + return new JacksonModel(node); + } else { + return WorkflowModelFactory.super.fromAny(obj); + } + } + + @Override + public WorkflowModel fromNull() { + return JacksonModel.NULL; + } +} diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelSerializer.java b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelSerializer.java new file mode 100644 index 00000000..874bd674 --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelSerializer.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.impl.expressions; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; + +public class JacksonModelSerializer extends StdSerializer { + + private static final long serialVersionUID = 1L; + + protected JacksonModelSerializer() { + super(JacksonModel.class); + } + + @Override + public void serialize(JacksonModel value, JsonGenerator gen, SerializerProvider provider) + throws IOException { + gen.writeTree(value.node); + } +} diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ObjectExpressionFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ObjectExpressionFactory.java new file mode 100644 index 00000000..f96894cc --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ObjectExpressionFactory.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.impl.expressions; + +import io.serverlessworkflow.impl.WorkflowFilter; +import java.util.Map; + +public abstract class ObjectExpressionFactory implements ExpressionFactory { + + @Override + public WorkflowFilter buildFilter(String str, Object object) { + if (str != null) { + assert str != null; + Expression expression = buildExpression(str); + return expression::eval; + } else if (object != null) { + Object exprObj = ExpressionUtils.buildExpressionObject(object, this); + return exprObj instanceof Map map + ? (w, t, n) -> modelFactory().from(ExpressionUtils.evaluateExpressionMap(map, w, t, n)) + : (w, t, n) -> modelFactory().fromAny(object); + } + throw new IllegalArgumentException("Both object and str are null"); + } +} diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/TaskDescriptor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/TaskDescriptor.java index f1e04cba..a4919002 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/TaskDescriptor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/TaskDescriptor.java @@ -15,16 +15,16 @@ */ package io.serverlessworkflow.impl.expressions; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.api.types.TaskBase; import io.serverlessworkflow.impl.TaskContext; +import io.serverlessworkflow.impl.WorkflowModel; public record TaskDescriptor( String name, String reference, TaskBase definition, - JsonNode rawInput, - JsonNode rawOutput, + WorkflowModel rawInput, + WorkflowModel rawOutput, DateTimeDescriptor startedAt) { public static TaskDescriptor of(TaskContext context) { diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/WorkflowDescriptor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/WorkflowDescriptor.java index f6b906fb..cf87cfbb 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/WorkflowDescriptor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/WorkflowDescriptor.java @@ -15,13 +15,13 @@ */ package io.serverlessworkflow.impl.expressions; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.api.types.TaskBase; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.impl.WorkflowContext; +import io.serverlessworkflow.impl.WorkflowModel; public record WorkflowDescriptor( - String id, Workflow definition, JsonNode input, DateTimeDescriptor startedAt) { + String id, Workflow definition, WorkflowModel input, DateTimeDescriptor startedAt) { public static WorkflowDescriptor of(WorkflowContext context) { return new WorkflowDescriptor( diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/json/JsonUtils.java b/impl/core/src/main/java/io/serverlessworkflow/impl/json/JsonUtils.java index 37d5c668..42a32b7d 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/json/JsonUtils.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/json/JsonUtils.java @@ -31,6 +31,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ShortNode; import com.fasterxml.jackson.databind.node.TextNode; +import io.serverlessworkflow.impl.WorkflowModel; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; @@ -130,11 +131,24 @@ public static JsonNode fromValue(Object value) { return mapToArray((Collection) value); } else if (value instanceof Map) { return mapToNode((Map) value); + } else if (value instanceof WorkflowModel model) { + return modelToJson(model); } else { return mapper.convertValue(value, JsonNode.class); } } + public static JsonNode modelToJson(WorkflowModel model) { + return model == null + ? NullNode.instance + : model + .as(JsonNode.class) + .orElseThrow( + () -> + new IllegalArgumentException( + "Unable to convert model " + model + " to JsonNode")); + } + public static Object toJavaValue(Object object) { return object instanceof JsonNode ? toJavaValue((JsonNode) object) : object; } @@ -257,5 +271,9 @@ public static ObjectNode object() { return mapper.createObjectNode(); } + public static ArrayNode array() { + return mapper.createArrayNode(); + } + private JsonUtils() {} } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/DefaultSchemaValidator.java b/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/DefaultSchemaValidator.java index 8982908f..1530757c 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/DefaultSchemaValidator.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/DefaultSchemaValidator.java @@ -20,6 +20,7 @@ import com.networknt.schema.JsonSchemaFactory; import com.networknt.schema.SpecVersion.VersionFlag; import com.networknt.schema.ValidationMessage; +import io.serverlessworkflow.impl.WorkflowModel; import java.util.Set; public class DefaultSchemaValidator implements SchemaValidator { @@ -31,8 +32,14 @@ public DefaultSchemaValidator(JsonNode jsonNode) { } @Override - public void validate(JsonNode node) { - Set report = schemaObject.validate(node); + public void validate(WorkflowModel node) { + Set report = + schemaObject.validate( + node.as(JsonNode.class) + .orElseThrow( + () -> + new IllegalArgumentException( + "Default schema validator requires WorkflowModel to support conversion to json node"))); if (!report.isEmpty()) { StringBuilder sb = new StringBuilder("There are JsonSchema validation errors:"); report.forEach(m -> sb.append(System.lineSeparator()).append(m.getMessage())); diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/DefaultSchemaValidatorFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/DefaultSchemaValidatorFactory.java index 0f74e433..cc14b265 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/DefaultSchemaValidatorFactory.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/DefaultSchemaValidatorFactory.java @@ -15,7 +15,14 @@ */ package io.serverlessworkflow.impl.jsonschema; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.serverlessworkflow.api.WorkflowFormat; +import io.serverlessworkflow.api.types.SchemaInline; +import io.serverlessworkflow.impl.json.JsonUtils; +import io.serverlessworkflow.impl.resources.StaticResource; +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; public class DefaultSchemaValidatorFactory implements SchemaValidatorFactory { @@ -28,7 +35,17 @@ public static DefaultSchemaValidatorFactory get() { } @Override - public SchemaValidator getValidator(JsonNode node) { - return new DefaultSchemaValidator(node); + public SchemaValidator getValidator(SchemaInline inline) { + return new DefaultSchemaValidator(JsonUtils.fromValue(inline.getDocument())); + } + + @Override + public SchemaValidator getValidator(StaticResource resource) { + ObjectMapper mapper = WorkflowFormat.fromFileName(resource.name()).mapper(); + try (InputStream in = resource.open()) { + return new DefaultSchemaValidator(mapper.readTree(in)); + } catch (IOException io) { + throw new UncheckedIOException(io); + } } } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/SchemaValidator.java b/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/SchemaValidator.java index d86a582f..9cce324e 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/SchemaValidator.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/SchemaValidator.java @@ -15,8 +15,8 @@ */ package io.serverlessworkflow.impl.jsonschema; -import com.fasterxml.jackson.databind.JsonNode; +import io.serverlessworkflow.impl.WorkflowModel; public interface SchemaValidator { - void validate(JsonNode node); + void validate(WorkflowModel node); } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/SchemaValidatorFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/SchemaValidatorFactory.java index 52c29584..1581ecf3 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/SchemaValidatorFactory.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/SchemaValidatorFactory.java @@ -15,8 +15,11 @@ */ package io.serverlessworkflow.impl.jsonschema; -import com.fasterxml.jackson.databind.JsonNode; +import io.serverlessworkflow.api.types.SchemaInline; +import io.serverlessworkflow.impl.resources.StaticResource; public interface SchemaValidatorFactory { - SchemaValidator getValidator(JsonNode node); + SchemaValidator getValidator(SchemaInline inline); + + SchemaValidator getValidator(StaticResource resource); } diff --git a/impl/core/src/test/java/io/serverlessworkflow/impl/EventDefinitionTest.java b/impl/core/src/test/java/io/serverlessworkflow/impl/EventDefinitionTest.java index 981b149d..f76dfd30 100644 --- a/impl/core/src/test/java/io/serverlessworkflow/impl/EventDefinitionTest.java +++ b/impl/core/src/test/java/io/serverlessworkflow/impl/EventDefinitionTest.java @@ -50,12 +50,12 @@ void testEventListened(String listen, String emit, JsonNode expectedResult, Obje WorkflowDefinition emitDefinition = appl.workflowDefinition(WorkflowReader.readWorkflowFromClasspath(emit)); WorkflowInstance waitingInstance = listenDefinition.instance(Map.of()); - CompletableFuture future = waitingInstance.start(); + CompletableFuture future = waitingInstance.start(); assertThat(waitingInstance.status()).isEqualTo(WorkflowStatus.WAITING); emitDefinition.instance(emitInput).start().join(); assertThat(future).isCompleted(); assertThat(waitingInstance.status()).isEqualTo(WorkflowStatus.COMPLETED); - assertThat(waitingInstance.outputAsJsonNode()).isEqualTo(expectedResult); + assertThat(waitingInstance.outputAs(JsonNode.class)).isEqualTo(expectedResult); } @ParameterizedTest @@ -69,7 +69,7 @@ void testEventsListened(String listen, String emit1, String emit2, JsonNode expe WorkflowDefinition emitOutDefinition = appl.workflowDefinition(WorkflowReader.readWorkflowFromClasspath(emit2)); WorkflowInstance waitingInstance = listenDefinition.instance(Map.of()); - CompletableFuture future = waitingInstance.start(); + CompletableFuture future = waitingInstance.start(); assertThat(waitingInstance.status()).isEqualTo(WorkflowStatus.WAITING); emitDoctorDefinition.instance(Map.of("temperature", 35)).start().join(); assertThat(waitingInstance.status()).isEqualTo(WorkflowStatus.WAITING); @@ -78,7 +78,7 @@ void testEventsListened(String listen, String emit1, String emit2, JsonNode expe emitOutDefinition.instance(Map.of()).start().join(); assertThat(future).isCompleted(); assertThat(waitingInstance.status()).isEqualTo(WorkflowStatus.COMPLETED); - assertThat(waitingInstance.outputAsJsonNode()).isEqualTo(expectedResult); + assertThat(waitingInstance.outputAs(JsonNode.class)).isEqualTo(expectedResult); } private static Stream eventListenerParameters() { diff --git a/impl/core/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java b/impl/core/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java index 4a0a073f..bb600990 100644 --- a/impl/core/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java +++ b/impl/core/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java @@ -111,11 +111,14 @@ private static Arguments args( (Consumer) d -> instance.accept( - d.instance(input).start().thenApply(JsonUtils::toJavaValue).join())); + d.instance(input) + .start() + .thenApply(model -> JsonUtils.toJavaValue(JsonUtils.modelToJson(model))) + .join())); } private static Arguments argsJson( - String fileName, Map input, Consumer instance) { + String fileName, Map input, Consumer instance) { return Arguments.of( fileName, (Consumer) d -> instance.accept(d.instance(input).start().join())); @@ -140,7 +143,12 @@ private static void checkWorkflowException( consumer.accept(clazz.cast(ex.getCause())); } - private static void checkNotCompeteOuput(JsonNode out) { + private static void checkNotCompeteOuput(WorkflowModel model) { + JsonNode out = + model + .as(JsonNode.class) + .orElseThrow( + () -> new IllegalArgumentException("Model cannot be converted to json node")); assertThat(out).isInstanceOf(ArrayNode.class); assertThat(out).hasSize(2); ArrayNode array = (ArrayNode) out; diff --git a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/HttpExecutor.java b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/HttpExecutor.java index d60c4655..cede1880 100644 --- a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/HttpExecutor.java +++ b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/HttpExecutor.java @@ -15,8 +15,6 @@ */ package io.serverlessworkflow.impl.executors; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.api.types.CallHTTP; import io.serverlessworkflow.api.types.Endpoint; import io.serverlessworkflow.api.types.EndpointUri; @@ -29,24 +27,20 @@ import io.serverlessworkflow.impl.WorkflowError; import io.serverlessworkflow.impl.WorkflowException; import io.serverlessworkflow.impl.WorkflowFilter; +import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowUtils; import io.serverlessworkflow.impl.expressions.Expression; import io.serverlessworkflow.impl.expressions.ExpressionFactory; -import io.serverlessworkflow.impl.expressions.ExpressionUtils; -import io.serverlessworkflow.impl.json.JsonUtils; import io.serverlessworkflow.impl.resources.ResourceLoader; import jakarta.ws.rs.HttpMethod; import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.client.Client; import jakarta.ws.rs.client.ClientBuilder; -import jakarta.ws.rs.client.Entity; import jakarta.ws.rs.client.Invocation.Builder; import jakarta.ws.rs.client.WebTarget; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; public class HttpExecutor implements CallableTask { @@ -56,15 +50,17 @@ public class HttpExecutor implements CallableTask { private Optional headersMap; private Optional queryMap; private RequestSupplier requestFunction; + private static HttpModelConverter converter = new HttpModelConverter() {}; @FunctionalInterface private interface TargetSupplier { - WebTarget apply(WorkflowContext workflow, TaskContext task, JsonNode node); + WebTarget apply(WorkflowContext workflow, TaskContext task, WorkflowModel node); } @FunctionalInterface private interface RequestSupplier { - JsonNode apply(Builder request, WorkflowContext workflow, TaskContext task, JsonNode node); + WorkflowModel apply( + Builder request, WorkflowContext workflow, TaskContext task, WorkflowModel node); } @Override @@ -76,7 +72,7 @@ public void init(CallHTTP task, WorkflowApplication application, ResourceLoader httpArgs.getHeaders() != null ? Optional.of( WorkflowUtils.buildWorkflowFilter( - application.expressionFactory(), + application, httpArgs.getHeaders().getRuntimeExpression(), httpArgs.getHeaders().getHTTPHeaders() != null ? httpArgs.getHeaders().getHTTPHeaders().getAdditionalProperties() @@ -86,7 +82,7 @@ public void init(CallHTTP task, WorkflowApplication application, ResourceLoader httpArgs.getQuery() != null ? Optional.of( WorkflowUtils.buildWorkflowFilter( - application.expressionFactory(), + application, httpArgs.getQuery().getRuntimeExpression(), httpArgs.getQuery().getHTTPQuery() != null ? httpArgs.getQuery().getHTTPQuery().getAdditionalProperties() @@ -94,42 +90,55 @@ public void init(CallHTTP task, WorkflowApplication application, ResourceLoader : Optional.empty(); switch (httpArgs.getMethod().toUpperCase()) { case HttpMethod.POST: - Object body = - ExpressionUtils.buildExpressionObject( - httpArgs.getBody(), application.expressionFactory()); + WorkflowFilter bodyFilter = + WorkflowUtils.buildWorkflowFilter(application, null, httpArgs.getBody()); this.requestFunction = (request, workflow, context, node) -> - request.post( - Entity.json( - ExpressionUtils.evaluateExpressionObject(body, workflow, context, node)), - JsonNode.class); + converter.toModel( + application.modelFactory(), + request.post( + converter.toEntity(bodyFilter.apply(workflow, context, node)), + node.objectClass())); break; case HttpMethod.GET: default: - this.requestFunction = (request, w, t, n) -> request.get(JsonNode.class); + this.requestFunction = + (request, w, t, n) -> + converter.toModel(application.modelFactory(), request.get(n.objectClass())); } } - @Override - public CompletableFuture apply( - WorkflowContext workflow, TaskContext taskContext, JsonNode input) { - WebTarget target = targetSupplier.apply(workflow, taskContext, input); - Optional queryJson = queryMap.map(q -> q.apply(workflow, taskContext, input)); - if (queryJson.isPresent()) { - Iterator> iter = queryJson.orElseThrow().fields(); - while (iter.hasNext()) { - Entry item = iter.next(); - target = target.queryParam(item.getKey(), JsonUtils.toJavaValue(item.getValue())); - } + private static class TargetQuerySupplier implements Supplier { + + private WebTarget target; + + public TargetQuerySupplier(WebTarget original) { + this.target = original; } - Builder request = target.request(); + public void addQuery(String key, Object value) { + target = target.queryParam(key, value); + } + + public WebTarget get() { + return target; + } + } + + @Override + public CompletableFuture apply( + WorkflowContext workflow, TaskContext taskContext, WorkflowModel input) { + TargetQuerySupplier supplier = + new TargetQuerySupplier(targetSupplier.apply(workflow, taskContext, input)); + queryMap.ifPresent( + q -> + q.apply(workflow, taskContext, input) + .forEach((k, v) -> supplier.addQuery(k, v.asJavaObject()))); + Builder request = supplier.get().request(); headersMap.ifPresent( h -> h.apply(workflow, taskContext, input) - .fields() - .forEachRemaining( - e -> request.header(e.getKey(), JsonUtils.toJavaValue(e.getValue())))); + .forEach((k, v) -> request.header(k, v.asJavaObject()))); return CompletableFuture.supplyAsync( () -> { try { @@ -157,11 +166,11 @@ private static TargetSupplier getTargetSupplier( return getURISupplier(uri.getLiteralEndpointURI()); } else if (uri.getExpressionEndpointURI() != null) { return new ExpressionURISupplier( - expressionFactory.getExpression(uri.getExpressionEndpointURI())); + expressionFactory.buildExpression(uri.getExpressionEndpointURI())); } } else if (endpoint.getRuntimeExpression() != null) { return new ExpressionURISupplier( - expressionFactory.getExpression(endpoint.getRuntimeExpression())); + expressionFactory.buildExpression(endpoint.getRuntimeExpression())); } else if (endpoint.getUriTemplate() != null) { return getURISupplier(endpoint.getUriTemplate()); } @@ -173,10 +182,7 @@ private static TargetSupplier getURISupplier(UriTemplate template) { return (w, t, n) -> client.target(template.getLiteralUri()); } else if (template.getLiteralUriTemplate() != null) { return (w, t, n) -> - client - .target(template.getLiteralUriTemplate()) - .resolveTemplates( - JsonUtils.mapper().convertValue(n, new TypeReference>() {})); + client.target(template.getLiteralUriTemplate()).resolveTemplates(n.asMap().orElseThrow()); } throw new IllegalArgumentException("Invalid uritemplate definition " + template); } @@ -189,8 +195,13 @@ public ExpressionURISupplier(Expression expr) { } @Override - public WebTarget apply(WorkflowContext workflow, TaskContext task, JsonNode node) { - return client.target(expr.eval(workflow, task, node).asText()); + public WebTarget apply(WorkflowContext workflow, TaskContext task, WorkflowModel node) { + return client.target( + expr.eval(workflow, task, node) + .asText() + .orElseThrow( + () -> + new IllegalArgumentException("Target expression requires a string result"))); } } } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/LongFilter.java b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/HttpModelConverter.java similarity index 59% rename from impl/core/src/main/java/io/serverlessworkflow/impl/LongFilter.java rename to impl/http/src/main/java/io/serverlessworkflow/impl/executors/HttpModelConverter.java index cf5598e7..a8c264dd 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/LongFilter.java +++ b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/HttpModelConverter.java @@ -13,7 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl; +package io.serverlessworkflow.impl.executors; -@FunctionalInterface -public interface LongFilter extends ExpressionHolder {} +import io.serverlessworkflow.impl.WorkflowModel; +import io.serverlessworkflow.impl.WorkflowModelFactory; +import jakarta.ws.rs.client.Entity; + +public interface HttpModelConverter { + + default WorkflowModel toModel(WorkflowModelFactory factory, Object entity) { + return factory.fromAny(entity); + } + + default Entity toEntity(WorkflowModel model) { + return Entity.json(model.asIs()); + } +} diff --git a/impl/http/src/test/java/io/serverlessworkflow/impl/HTTPWorkflowDefinitionTest.java b/impl/http/src/test/java/io/serverlessworkflow/impl/HTTPWorkflowDefinitionTest.java index badb6403..d490859f 100644 --- a/impl/http/src/test/java/io/serverlessworkflow/impl/HTTPWorkflowDefinitionTest.java +++ b/impl/http/src/test/java/io/serverlessworkflow/impl/HTTPWorkflowDefinitionTest.java @@ -68,20 +68,20 @@ void testWrongSchema(String fileName) { .hasMessageContaining("There are JsonSchema validation errors"); } - private static boolean httpCondition(Object obj) { - Map map = (Map) obj; + private static boolean httpCondition(WorkflowModel obj) { + Map map = obj.asMap().orElseThrow(); return map.containsKey("photoUrls") || map.containsKey("petId"); } private static Stream provideParameters() { Map petInput = Map.of("petId", 10); Map starTrekInput = Map.of("uid", "MOMA0000092393"); - Condition petCondition = + Condition petCondition = new Condition<>(HTTPWorkflowDefinitionTest::httpCondition, "callHttpCondition"); - Condition starTrekCondition = + Condition starTrekCondition = new Condition<>( o -> - ((Map) ((Map) o).get("movie")) + ((Map) o.asMap().orElseThrow().get("movie")) .get("title") .equals("Star Trek"), "StartTrek"); @@ -90,8 +90,8 @@ private static Stream provideParameters() { Arguments.of( "callGetHttp.yaml", Map.of("petId", "-1"), - new Condition<>( - o -> ((Map) o).containsKey("petId"), "notFoundCondition")), + new Condition( + o -> o.asMap().orElseThrow().containsKey("petId"), "notFoundCondition")), Arguments.of("call-http-endpoint-interpolation.yaml", petInput, petCondition), Arguments.of("call-http-query-parameters.yaml", starTrekInput, starTrekCondition), Arguments.of( @@ -99,6 +99,7 @@ private static Stream provideParameters() { Arguments.of( "callPostHttp.yaml", Map.of("name", "Javierito", "surname", "Unknown"), - new Condition<>(o -> o.equals("Javierito"), "CallHttpPostCondition"))); + new Condition( + o -> o.asText().orElseThrow().equals("Javierito"), "CallHttpPostCondition"))); } } From 492a14b7e8664f31c5c5cd0d03d216bc1d5fd4ae Mon Sep 17 00:00:00 2001 From: fjtirado Date: Thu, 17 Jul 2025 11:50:53 +0200 Subject: [PATCH 13/30] [Fix #636] Relocating dependencies Signed-off-by: fjtirado --- examples/events/pom.xml | 2 +- examples/pom.xml | 5 ++ examples/simpleGet/pom.xml | 2 +- impl/core/pom.xml | 28 ++----- .../impl/WorkflowApplication.java | 44 +++++++++-- .../impl/WorkflowDefinition.java | 2 +- .../impl/WorkflowUtils.java | 4 +- .../impl/events/CloudEventUtils.java | 50 +----------- .../impl/executors/AbstractTaskExecutor.java | 2 +- .../impl/expressions/DateTimeDescriptor.java | 7 +- .../impl/resources/DynamicResource.java | 4 +- .../SchemaValidator.java | 2 +- .../SchemaValidatorFactory.java | 2 +- impl/http/pom.xml | 11 +++ .../impl/HTTPWorkflowDefinitionTest.java | 2 - impl/jackson/pom.xml | 57 ++++++++++++++ .../impl/events/JacksonCloudEventUtils.java | 78 +++++++++++++++++++ .../impl/expressions/JQExpression.java | 0 .../impl/expressions/JQExpressionFactory.java | 8 -- .../impl/expressions/JacksonModel.java | 0 .../expressions/JacksonModelCollection.java | 0 .../impl/expressions/JacksonModelFactory.java | 6 +- .../expressions/JacksonModelSerializer.java | 0 .../impl/json/JsonUtils.java | 0 .../impl/json/MergeUtils.java | 0 .../impl/schema/JsonSchemaValidator.java} | 6 +- .../schema/JsonSchemaValidatorFactory.java} | 16 +--- ...orkflow.impl.expressions.ExpressionFactory | 1 + ...orkflow.impl.schema.SchemaValidatorFactory | 1 + .../impl/EventDefinitionTest.java | 0 .../impl/WorkflowDefinitionTest.java | 0 .../src/test/resources/conditional-set.yaml | 0 .../src/test/resources/emit-doctor.yaml | 0 .../src/test/resources/emit-out.yaml | 0 .../src/test/resources/emit.yaml | 0 .../src/test/resources/for-collect.yaml | 0 .../src/test/resources/for-sum.yaml | 0 .../src/test/resources/fork-no-compete.yaml | 0 .../src/test/resources/fork.yaml | 0 .../src/test/resources/listen-to-all.yaml | 0 .../test/resources/listen-to-any-filter.yaml | 0 .../listen-to-any-until-consumed.yaml | 0 .../src/test/resources/listen-to-any.yaml | 0 .../src/test/resources/raise-inline copy.yaml | 0 .../src/test/resources/raise-reusable.yaml | 0 .../src/test/resources/simple-expression.yaml | 0 .../test/resources/switch-then-string.yaml | 0 impl/pom.xml | 8 +- pom.xml | 7 +- 49 files changed, 235 insertions(+), 120 deletions(-) rename impl/core/src/main/java/io/serverlessworkflow/impl/{jsonschema => schema}/SchemaValidator.java (94%) rename impl/core/src/main/java/io/serverlessworkflow/impl/{jsonschema => schema}/SchemaValidatorFactory.java (95%) create mode 100644 impl/jackson/pom.xml create mode 100644 impl/jackson/src/main/java/io/serverlessworkflow/impl/events/JacksonCloudEventUtils.java rename impl/{core => jackson}/src/main/java/io/serverlessworkflow/impl/expressions/JQExpression.java (100%) rename impl/{core => jackson}/src/main/java/io/serverlessworkflow/impl/expressions/JQExpressionFactory.java (91%) rename impl/{core => jackson}/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModel.java (100%) rename impl/{core => jackson}/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelCollection.java (100%) rename impl/{core => jackson}/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelFactory.java (95%) rename impl/{core => jackson}/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelSerializer.java (100%) rename impl/{core => jackson}/src/main/java/io/serverlessworkflow/impl/json/JsonUtils.java (100%) rename impl/{core => jackson}/src/main/java/io/serverlessworkflow/impl/json/MergeUtils.java (100%) rename impl/{core/src/main/java/io/serverlessworkflow/impl/jsonschema/DefaultSchemaValidator.java => jackson/src/main/java/io/serverlessworkflow/impl/schema/JsonSchemaValidator.java} (91%) rename impl/{core/src/main/java/io/serverlessworkflow/impl/jsonschema/DefaultSchemaValidatorFactory.java => jackson/src/main/java/io/serverlessworkflow/impl/schema/JsonSchemaValidatorFactory.java} (73%) create mode 100644 impl/jackson/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory create mode 100644 impl/jackson/src/main/resources/META-INF/services/io.serverlessworkflow.impl.schema.SchemaValidatorFactory rename impl/{core => jackson}/src/test/java/io/serverlessworkflow/impl/EventDefinitionTest.java (100%) rename impl/{core => jackson}/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java (100%) rename impl/{core => jackson}/src/test/resources/conditional-set.yaml (100%) rename impl/{core => jackson}/src/test/resources/emit-doctor.yaml (100%) rename impl/{core => jackson}/src/test/resources/emit-out.yaml (100%) rename impl/{core => jackson}/src/test/resources/emit.yaml (100%) rename impl/{core => jackson}/src/test/resources/for-collect.yaml (100%) rename impl/{core => jackson}/src/test/resources/for-sum.yaml (100%) rename impl/{core => jackson}/src/test/resources/fork-no-compete.yaml (100%) rename impl/{core => jackson}/src/test/resources/fork.yaml (100%) rename impl/{core => jackson}/src/test/resources/listen-to-all.yaml (100%) rename impl/{core => jackson}/src/test/resources/listen-to-any-filter.yaml (100%) rename impl/{core => jackson}/src/test/resources/listen-to-any-until-consumed.yaml (100%) rename impl/{core => jackson}/src/test/resources/listen-to-any.yaml (100%) rename impl/{core => jackson}/src/test/resources/raise-inline copy.yaml (100%) rename impl/{core => jackson}/src/test/resources/raise-reusable.yaml (100%) rename impl/{core => jackson}/src/test/resources/simple-expression.yaml (100%) rename impl/{core => jackson}/src/test/resources/switch-then-string.yaml (100%) diff --git a/examples/events/pom.xml b/examples/events/pom.xml index 143a7967..439b3a11 100644 --- a/examples/events/pom.xml +++ b/examples/events/pom.xml @@ -11,7 +11,7 @@ io.serverlessworkflow - serverlessworkflow-impl-core + serverlessworkflow-impl-jackson org.slf4j diff --git a/examples/pom.xml b/examples/pom.xml index 238ee4b1..e393cb8d 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -21,6 +21,11 @@ serverlessworkflow-impl-http ${project.version} + + io.serverlessworkflow + serverlessworkflow-impl-jackson + ${project.version} + org.slf4j slf4j-simple diff --git a/examples/simpleGet/pom.xml b/examples/simpleGet/pom.xml index 923001ae..4d07f168 100644 --- a/examples/simpleGet/pom.xml +++ b/examples/simpleGet/pom.xml @@ -11,7 +11,7 @@ io.serverlessworkflow - serverlessworkflow-impl-core + serverlessworkflow-impl-jackson io.serverlessworkflow diff --git a/impl/core/pom.xml b/impl/core/pom.xml index 4140e747..2ea1eb9b 100644 --- a/impl/core/pom.xml +++ b/impl/core/pom.xml @@ -8,31 +8,23 @@ serverlessworkflow-impl-core Serverless Workflow :: Impl :: Core - - io.serverlessworkflow - serverlessworkflow-types - ${project.version} + + io.serverlessworkflow + serverlessworkflow-types + ${project.version} io.cloudevents - cloudevents-api + cloudevents-core - io.cloudevents - cloudevents-json-jackson + org.slf4j + slf4j-api com.github.f4b6a3 ulid-creator - - com.networknt - json-schema-validator - - - net.thisptr - jackson-jq - org.junit.jupiter junit-jupiter-api @@ -58,11 +50,5 @@ logback-classic test - - io.serverlessworkflow - serverlessworkflow-api - ${project.version} - - diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java index ab09dac7..0477ccf1 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java @@ -17,6 +17,7 @@ import com.github.f4b6a3.ulid.UlidCreator; import io.serverlessworkflow.api.types.Document; +import io.serverlessworkflow.api.types.SchemaInline; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.impl.events.EventConsumer; import io.serverlessworkflow.impl.events.EventPublisher; @@ -24,16 +25,17 @@ import io.serverlessworkflow.impl.executors.DefaultTaskExecutorFactory; import io.serverlessworkflow.impl.executors.TaskExecutorFactory; import io.serverlessworkflow.impl.expressions.ExpressionFactory; -import io.serverlessworkflow.impl.expressions.JQExpressionFactory; import io.serverlessworkflow.impl.expressions.RuntimeDescriptor; -import io.serverlessworkflow.impl.jsonschema.DefaultSchemaValidatorFactory; -import io.serverlessworkflow.impl.jsonschema.SchemaValidatorFactory; import io.serverlessworkflow.impl.resources.DefaultResourceLoaderFactory; import io.serverlessworkflow.impl.resources.ResourceLoaderFactory; +import io.serverlessworkflow.impl.resources.StaticResource; +import io.serverlessworkflow.impl.schema.SchemaValidator; +import io.serverlessworkflow.impl.schema.SchemaValidatorFactory; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Map; +import java.util.ServiceLoader; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -101,11 +103,31 @@ public WorkflowIdFactory idFactory() { } public static class Builder { + private static final SchemaValidatorFactory EMPTY_SCHEMA_VALIDATOR = + new SchemaValidatorFactory() { + + private final SchemaValidator NoValidation = + new SchemaValidator() { + @Override + public void validate(WorkflowModel node) {} + }; + + @Override + public SchemaValidator getValidator(StaticResource resource) { + + return NoValidation; + } + + @Override + public SchemaValidator getValidator(SchemaInline inline) { + return NoValidation; + } + }; private TaskExecutorFactory taskFactory = DefaultTaskExecutorFactory.get(); - private ExpressionFactory exprFactory = JQExpressionFactory.get(); + private ExpressionFactory exprFactory; private Collection listeners; private ResourceLoaderFactory resourceLoaderFactory = DefaultResourceLoaderFactory.get(); - private SchemaValidatorFactory schemaValidatorFactory = DefaultSchemaValidatorFactory.get(); + private SchemaValidatorFactory schemaValidatorFactory; private WorkflowPositionFactory positionFactory = () -> new QueueWorkflowPosition(); private WorkflowIdFactory idFactory = () -> UlidCreator.getMonotonicUlid().toString(); private ExecutorServiceFactory executorFactory = () -> Executors.newCachedThreadPool(); @@ -175,6 +197,18 @@ public Builder withEventPublisher(EventPublisher eventPublisher) { } public WorkflowApplication build() { + if (exprFactory == null) { + exprFactory = + ServiceLoader.load(ExpressionFactory.class) + .findFirst() + .orElseThrow(() -> new IllegalStateException("Expression factory is required")); + } + if (schemaValidatorFactory == null) { + schemaValidatorFactory = + ServiceLoader.load(SchemaValidatorFactory.class) + .findFirst() + .orElse(EMPTY_SCHEMA_VALIDATOR); + } return new WorkflowApplication(this); } } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowDefinition.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowDefinition.java index 639f368d..404ecf07 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowDefinition.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowDefinition.java @@ -22,8 +22,8 @@ import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.impl.executors.TaskExecutor; import io.serverlessworkflow.impl.executors.TaskExecutorHelper; -import io.serverlessworkflow.impl.jsonschema.SchemaValidator; import io.serverlessworkflow.impl.resources.ResourceLoader; +import io.serverlessworkflow.impl.schema.SchemaValidator; import java.nio.file.Path; import java.util.Collection; import java.util.Optional; diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java index 069986df..d9bf2824 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java @@ -21,9 +21,9 @@ import io.serverlessworkflow.api.types.SchemaUnion; import io.serverlessworkflow.api.types.UriTemplate; import io.serverlessworkflow.impl.expressions.ExpressionUtils; -import io.serverlessworkflow.impl.jsonschema.SchemaValidator; -import io.serverlessworkflow.impl.jsonschema.SchemaValidatorFactory; import io.serverlessworkflow.impl.resources.ResourceLoader; +import io.serverlessworkflow.impl.schema.SchemaValidator; +import io.serverlessworkflow.impl.schema.SchemaValidatorFactory; import java.net.URI; import java.util.Optional; import java.util.function.Function; diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/events/CloudEventUtils.java b/impl/core/src/main/java/io/serverlessworkflow/impl/events/CloudEventUtils.java index a5a5e04c..f2857ab0 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/events/CloudEventUtils.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/events/CloudEventUtils.java @@ -15,15 +15,7 @@ */ package io.serverlessworkflow.impl.events; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.NullNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import io.cloudevents.CloudEvent; -import io.cloudevents.CloudEventData; -import io.cloudevents.jackson.JsonCloudEventData; -import io.serverlessworkflow.impl.json.JsonUtils; -import java.io.IOException; -import java.io.UncheckedIOException; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.Date; @@ -32,50 +24,12 @@ public class CloudEventUtils { - public static JsonNode toJsonNode(CloudEvent event) { - ObjectNode result = JsonUtils.mapper().createObjectNode(); - if (event.getData() != null) { - result.set("data", toJsonNode(event.getData())); - } - if (event.getSubject() != null) { - result.put("subject", event.getSubject()); - } - if (event.getDataContentType() != null) { - result.put("datacontenttype", event.getDataContentType()); - } - result.put("id", event.getId()); - result.put("source", event.getSource().toString()); - result.put("type", event.getType()); - result.put("specversion", event.getSpecVersion().toString()); - if (event.getDataSchema() != null) { - result.put("dataschema", event.getDataSchema().toString()); - } - if (event.getTime() != null) { - result.put("time", event.getTime().toString()); - } - event - .getExtensionNames() - .forEach(n -> result.set(n, JsonUtils.fromValue(event.getExtension(n)))); - return result; - } + private CloudEventUtils() {} public static OffsetDateTime toOffset(Date date) { return date.toInstant().atOffset(ZoneOffset.UTC); } - public static JsonNode toJsonNode(CloudEventData data) { - if (data == null) { - return NullNode.instance; - } - try { - return data instanceof JsonCloudEventData - ? ((JsonCloudEventData) data).getNode() - : JsonUtils.mapper().readTree(data.toBytes()); - } catch (IOException io) { - throw new UncheckedIOException(io); - } - } - public static Map extensions(CloudEvent event) { Map result = new LinkedHashMap<>(); for (String name : event.getExtensionNames()) { @@ -83,6 +37,4 @@ public static Map extensions(CloudEvent event) { } return result; } - - private CloudEventUtils() {} } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java index 18d23d85..414c82c6 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java @@ -30,8 +30,8 @@ import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowPosition; import io.serverlessworkflow.impl.WorkflowStatus; -import io.serverlessworkflow.impl.jsonschema.SchemaValidator; import io.serverlessworkflow.impl.resources.ResourceLoader; +import io.serverlessworkflow.impl.schema.SchemaValidator; import java.time.Instant; import java.util.Iterator; import java.util.Map; diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/DateTimeDescriptor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/DateTimeDescriptor.java index 7936763f..898476f8 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/DateTimeDescriptor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/DateTimeDescriptor.java @@ -15,7 +15,6 @@ */ package io.serverlessworkflow.impl.expressions; -import com.fasterxml.jackson.annotation.JsonProperty; import java.time.Instant; public class DateTimeDescriptor { @@ -30,13 +29,11 @@ private DateTimeDescriptor(Instant instant) { this.instant = instant; } - @JsonProperty("iso8601") - public String iso8601() { + public String getIso8601() { return instant.toString(); } - @JsonProperty("epoch") - public Epoch epoch() { + public Epoch getEpoch() { return Epoch.of(instant); } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/resources/DynamicResource.java b/impl/core/src/main/java/io/serverlessworkflow/impl/resources/DynamicResource.java index accac01e..cd8b1780 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/resources/DynamicResource.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/resources/DynamicResource.java @@ -15,12 +15,12 @@ */ package io.serverlessworkflow.impl.resources; -import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowContext; +import io.serverlessworkflow.impl.WorkflowModel; import java.io.InputStream; import java.util.Optional; public interface DynamicResource { - InputStream open(WorkflowContext workflow, Optional task, JsonNode input); + InputStream open(WorkflowContext workflow, Optional task, WorkflowModel input); } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/SchemaValidator.java b/impl/core/src/main/java/io/serverlessworkflow/impl/schema/SchemaValidator.java similarity index 94% rename from impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/SchemaValidator.java rename to impl/core/src/main/java/io/serverlessworkflow/impl/schema/SchemaValidator.java index 9cce324e..fa66676b 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/SchemaValidator.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/schema/SchemaValidator.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.jsonschema; +package io.serverlessworkflow.impl.schema; import io.serverlessworkflow.impl.WorkflowModel; diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/SchemaValidatorFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/schema/SchemaValidatorFactory.java similarity index 95% rename from impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/SchemaValidatorFactory.java rename to impl/core/src/main/java/io/serverlessworkflow/impl/schema/SchemaValidatorFactory.java index 1581ecf3..56b4b079 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/SchemaValidatorFactory.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/schema/SchemaValidatorFactory.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.jsonschema; +package io.serverlessworkflow.impl.schema; import io.serverlessworkflow.api.types.SchemaInline; import io.serverlessworkflow.impl.resources.StaticResource; diff --git a/impl/http/pom.xml b/impl/http/pom.xml index 04f6f625..65c48aac 100644 --- a/impl/http/pom.xml +++ b/impl/http/pom.xml @@ -15,11 +15,22 @@ org.glassfish.jersey.media jersey-media-json-jackson + runtime io.serverlessworkflow serverlessworkflow-impl-core + + io.serverlessworkflow + serverlessworkflow-api + test + + + io.serverlessworkflow + serverlessworkflow-impl-jackson + test + org.junit.jupiter junit-jupiter-api diff --git a/impl/http/src/test/java/io/serverlessworkflow/impl/HTTPWorkflowDefinitionTest.java b/impl/http/src/test/java/io/serverlessworkflow/impl/HTTPWorkflowDefinitionTest.java index d490859f..fd1c575b 100644 --- a/impl/http/src/test/java/io/serverlessworkflow/impl/HTTPWorkflowDefinitionTest.java +++ b/impl/http/src/test/java/io/serverlessworkflow/impl/HTTPWorkflowDefinitionTest.java @@ -19,7 +19,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowableOfType; -import io.serverlessworkflow.impl.json.JsonUtils; import java.io.IOException; import java.util.Map; import java.util.stream.Stream; @@ -47,7 +46,6 @@ void testWorkflowExecution(String fileName, Object input, Condition cond appl.workflowDefinition(readWorkflowFromClasspath(fileName)) .instance(input) .start() - .thenApply(JsonUtils::toJavaValue) .join()) .is(condition); } diff --git a/impl/jackson/pom.xml b/impl/jackson/pom.xml new file mode 100644 index 00000000..babc6904 --- /dev/null +++ b/impl/jackson/pom.xml @@ -0,0 +1,57 @@ + + 4.0.0 + + io.serverlessworkflow + serverlessworkflow-impl + 8.0.0-SNAPSHOT + + serverlessworkflow-impl-jackson + Serverless Workflow :: Impl :: HTTP + + + io.serverlessworkflow + serverlessworkflow-impl-core + + + io.serverlessworkflow + serverlessworkflow-api + + + io.cloudevents + cloudevents-json-jackson + + + com.networknt + json-schema-validator + + + net.thisptr + jackson-jq + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.assertj + assertj-core + test + + + ch.qos.logback + logback-classic + test + + + \ No newline at end of file diff --git a/impl/jackson/src/main/java/io/serverlessworkflow/impl/events/JacksonCloudEventUtils.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/events/JacksonCloudEventUtils.java new file mode 100644 index 00000000..f51f4547 --- /dev/null +++ b/impl/jackson/src/main/java/io/serverlessworkflow/impl/events/JacksonCloudEventUtils.java @@ -0,0 +1,78 @@ +/* + * 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.events; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.NullNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.cloudevents.CloudEvent; +import io.cloudevents.CloudEventData; +import io.cloudevents.jackson.JsonCloudEventData; +import io.serverlessworkflow.impl.json.JsonUtils; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Date; + +public class JacksonCloudEventUtils { + + public static JsonNode toJsonNode(CloudEvent event) { + ObjectNode result = JsonUtils.mapper().createObjectNode(); + if (event.getData() != null) { + result.set("data", toJsonNode(event.getData())); + } + if (event.getSubject() != null) { + result.put("subject", event.getSubject()); + } + if (event.getDataContentType() != null) { + result.put("datacontenttype", event.getDataContentType()); + } + result.put("id", event.getId()); + result.put("source", event.getSource().toString()); + result.put("type", event.getType()); + result.put("specversion", event.getSpecVersion().toString()); + if (event.getDataSchema() != null) { + result.put("dataschema", event.getDataSchema().toString()); + } + if (event.getTime() != null) { + result.put("time", event.getTime().toString()); + } + event + .getExtensionNames() + .forEach(n -> result.set(n, JsonUtils.fromValue(event.getExtension(n)))); + return result; + } + + public static OffsetDateTime toOffset(Date date) { + return date.toInstant().atOffset(ZoneOffset.UTC); + } + + public static JsonNode toJsonNode(CloudEventData data) { + if (data == null) { + return NullNode.instance; + } + try { + return data instanceof JsonCloudEventData + ? ((JsonCloudEventData) data).getNode() + : JsonUtils.mapper().readTree(data.toBytes()); + } catch (IOException io) { + throw new UncheckedIOException(io); + } + } + + private JacksonCloudEventUtils() {} +} diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JQExpression.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JQExpression.java similarity index 100% rename from impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JQExpression.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JQExpression.java diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JQExpressionFactory.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JQExpressionFactory.java similarity index 91% rename from impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JQExpressionFactory.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JQExpressionFactory.java index 5d552934..e5e9c481 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JQExpressionFactory.java +++ b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JQExpressionFactory.java @@ -24,16 +24,8 @@ public class JQExpressionFactory extends ObjectExpressionFactory { - private JQExpressionFactory() {} - - private static final JQExpressionFactory instance = new JQExpressionFactory(); - private WorkflowModelFactory modelFactory = new JacksonModelFactory(); - public static JQExpressionFactory get() { - return instance; - } - private static Supplier scopeSupplier = new DefaultScopeSupplier(); private static class DefaultScopeSupplier implements Supplier { diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModel.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModel.java similarity index 100% rename from impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModel.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModel.java diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelCollection.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelCollection.java similarity index 100% rename from impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelCollection.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelCollection.java diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelFactory.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelFactory.java similarity index 95% rename from impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelFactory.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelFactory.java index 2c488707..00906165 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelFactory.java +++ b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelFactory.java @@ -27,7 +27,7 @@ import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowModelCollection; import io.serverlessworkflow.impl.WorkflowModelFactory; -import io.serverlessworkflow.impl.events.CloudEventUtils; +import io.serverlessworkflow.impl.events.JacksonCloudEventUtils; import io.serverlessworkflow.impl.json.JsonUtils; import java.math.BigDecimal; import java.time.OffsetDateTime; @@ -91,12 +91,12 @@ public WorkflowModel from(String value) { @Override public WorkflowModel from(CloudEvent ce) { - return new JacksonModel(CloudEventUtils.toJsonNode(ce)); + return new JacksonModel(JacksonCloudEventUtils.toJsonNode(ce)); } @Override public WorkflowModel from(CloudEventData ce) { - return new JacksonModel(CloudEventUtils.toJsonNode(ce)); + return new JacksonModel(JacksonCloudEventUtils.toJsonNode(ce)); } @Override diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelSerializer.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelSerializer.java similarity index 100% rename from impl/core/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelSerializer.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelSerializer.java diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/json/JsonUtils.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/json/JsonUtils.java similarity index 100% rename from impl/core/src/main/java/io/serverlessworkflow/impl/json/JsonUtils.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/json/JsonUtils.java diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/json/MergeUtils.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/json/MergeUtils.java similarity index 100% rename from impl/core/src/main/java/io/serverlessworkflow/impl/json/MergeUtils.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/json/MergeUtils.java diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/DefaultSchemaValidator.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/JsonSchemaValidator.java similarity index 91% rename from impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/DefaultSchemaValidator.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/JsonSchemaValidator.java index 1530757c..142faf73 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/DefaultSchemaValidator.java +++ b/impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/JsonSchemaValidator.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.jsonschema; +package io.serverlessworkflow.impl.schema; import com.fasterxml.jackson.databind.JsonNode; import com.networknt.schema.JsonSchema; @@ -23,11 +23,11 @@ import io.serverlessworkflow.impl.WorkflowModel; import java.util.Set; -public class DefaultSchemaValidator implements SchemaValidator { +public class JsonSchemaValidator implements SchemaValidator { private final JsonSchema schemaObject; - public DefaultSchemaValidator(JsonNode jsonNode) { + public JsonSchemaValidator(JsonNode jsonNode) { this.schemaObject = JsonSchemaFactory.getInstance(VersionFlag.V7).getSchema(jsonNode); } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/DefaultSchemaValidatorFactory.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/JsonSchemaValidatorFactory.java similarity index 73% rename from impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/DefaultSchemaValidatorFactory.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/JsonSchemaValidatorFactory.java index cc14b265..269bebb4 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/jsonschema/DefaultSchemaValidatorFactory.java +++ b/impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/JsonSchemaValidatorFactory.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.jsonschema; +package io.serverlessworkflow.impl.schema; import com.fasterxml.jackson.databind.ObjectMapper; import io.serverlessworkflow.api.WorkflowFormat; @@ -24,26 +24,18 @@ import java.io.InputStream; import java.io.UncheckedIOException; -public class DefaultSchemaValidatorFactory implements SchemaValidatorFactory { - - private DefaultSchemaValidatorFactory() {} - - private static final DefaultSchemaValidatorFactory instance = new DefaultSchemaValidatorFactory(); - - public static DefaultSchemaValidatorFactory get() { - return instance; - } +public class JsonSchemaValidatorFactory implements SchemaValidatorFactory { @Override public SchemaValidator getValidator(SchemaInline inline) { - return new DefaultSchemaValidator(JsonUtils.fromValue(inline.getDocument())); + return new JsonSchemaValidator(JsonUtils.fromValue(inline.getDocument())); } @Override public SchemaValidator getValidator(StaticResource resource) { ObjectMapper mapper = WorkflowFormat.fromFileName(resource.name()).mapper(); try (InputStream in = resource.open()) { - return new DefaultSchemaValidator(mapper.readTree(in)); + return new JsonSchemaValidator(mapper.readTree(in)); } catch (IOException io) { throw new UncheckedIOException(io); } diff --git a/impl/jackson/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory b/impl/jackson/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory new file mode 100644 index 00000000..1853d536 --- /dev/null +++ b/impl/jackson/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory @@ -0,0 +1 @@ +io.serverlessworkflow.impl.expressions.JQExpressionFactory \ No newline at end of file diff --git a/impl/jackson/src/main/resources/META-INF/services/io.serverlessworkflow.impl.schema.SchemaValidatorFactory b/impl/jackson/src/main/resources/META-INF/services/io.serverlessworkflow.impl.schema.SchemaValidatorFactory new file mode 100644 index 00000000..b4bc2dd0 --- /dev/null +++ b/impl/jackson/src/main/resources/META-INF/services/io.serverlessworkflow.impl.schema.SchemaValidatorFactory @@ -0,0 +1 @@ +io.serverlessworkflow.impl.schema.JsonSchemaValidatorFactory \ No newline at end of file diff --git a/impl/core/src/test/java/io/serverlessworkflow/impl/EventDefinitionTest.java b/impl/jackson/src/test/java/io/serverlessworkflow/impl/EventDefinitionTest.java similarity index 100% rename from impl/core/src/test/java/io/serverlessworkflow/impl/EventDefinitionTest.java rename to impl/jackson/src/test/java/io/serverlessworkflow/impl/EventDefinitionTest.java diff --git a/impl/core/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java b/impl/jackson/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java similarity index 100% rename from impl/core/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java rename to impl/jackson/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java diff --git a/impl/core/src/test/resources/conditional-set.yaml b/impl/jackson/src/test/resources/conditional-set.yaml similarity index 100% rename from impl/core/src/test/resources/conditional-set.yaml rename to impl/jackson/src/test/resources/conditional-set.yaml diff --git a/impl/core/src/test/resources/emit-doctor.yaml b/impl/jackson/src/test/resources/emit-doctor.yaml similarity index 100% rename from impl/core/src/test/resources/emit-doctor.yaml rename to impl/jackson/src/test/resources/emit-doctor.yaml diff --git a/impl/core/src/test/resources/emit-out.yaml b/impl/jackson/src/test/resources/emit-out.yaml similarity index 100% rename from impl/core/src/test/resources/emit-out.yaml rename to impl/jackson/src/test/resources/emit-out.yaml diff --git a/impl/core/src/test/resources/emit.yaml b/impl/jackson/src/test/resources/emit.yaml similarity index 100% rename from impl/core/src/test/resources/emit.yaml rename to impl/jackson/src/test/resources/emit.yaml diff --git a/impl/core/src/test/resources/for-collect.yaml b/impl/jackson/src/test/resources/for-collect.yaml similarity index 100% rename from impl/core/src/test/resources/for-collect.yaml rename to impl/jackson/src/test/resources/for-collect.yaml diff --git a/impl/core/src/test/resources/for-sum.yaml b/impl/jackson/src/test/resources/for-sum.yaml similarity index 100% rename from impl/core/src/test/resources/for-sum.yaml rename to impl/jackson/src/test/resources/for-sum.yaml diff --git a/impl/core/src/test/resources/fork-no-compete.yaml b/impl/jackson/src/test/resources/fork-no-compete.yaml similarity index 100% rename from impl/core/src/test/resources/fork-no-compete.yaml rename to impl/jackson/src/test/resources/fork-no-compete.yaml diff --git a/impl/core/src/test/resources/fork.yaml b/impl/jackson/src/test/resources/fork.yaml similarity index 100% rename from impl/core/src/test/resources/fork.yaml rename to impl/jackson/src/test/resources/fork.yaml diff --git a/impl/core/src/test/resources/listen-to-all.yaml b/impl/jackson/src/test/resources/listen-to-all.yaml similarity index 100% rename from impl/core/src/test/resources/listen-to-all.yaml rename to impl/jackson/src/test/resources/listen-to-all.yaml diff --git a/impl/core/src/test/resources/listen-to-any-filter.yaml b/impl/jackson/src/test/resources/listen-to-any-filter.yaml similarity index 100% rename from impl/core/src/test/resources/listen-to-any-filter.yaml rename to impl/jackson/src/test/resources/listen-to-any-filter.yaml diff --git a/impl/core/src/test/resources/listen-to-any-until-consumed.yaml b/impl/jackson/src/test/resources/listen-to-any-until-consumed.yaml similarity index 100% rename from impl/core/src/test/resources/listen-to-any-until-consumed.yaml rename to impl/jackson/src/test/resources/listen-to-any-until-consumed.yaml diff --git a/impl/core/src/test/resources/listen-to-any.yaml b/impl/jackson/src/test/resources/listen-to-any.yaml similarity index 100% rename from impl/core/src/test/resources/listen-to-any.yaml rename to impl/jackson/src/test/resources/listen-to-any.yaml diff --git a/impl/core/src/test/resources/raise-inline copy.yaml b/impl/jackson/src/test/resources/raise-inline copy.yaml similarity index 100% rename from impl/core/src/test/resources/raise-inline copy.yaml rename to impl/jackson/src/test/resources/raise-inline copy.yaml diff --git a/impl/core/src/test/resources/raise-reusable.yaml b/impl/jackson/src/test/resources/raise-reusable.yaml similarity index 100% rename from impl/core/src/test/resources/raise-reusable.yaml rename to impl/jackson/src/test/resources/raise-reusable.yaml diff --git a/impl/core/src/test/resources/simple-expression.yaml b/impl/jackson/src/test/resources/simple-expression.yaml similarity index 100% rename from impl/core/src/test/resources/simple-expression.yaml rename to impl/jackson/src/test/resources/simple-expression.yaml diff --git a/impl/core/src/test/resources/switch-then-string.yaml b/impl/jackson/src/test/resources/switch-then-string.yaml similarity index 100% rename from impl/core/src/test/resources/switch-then-string.yaml rename to impl/jackson/src/test/resources/switch-then-string.yaml diff --git a/impl/pom.xml b/impl/pom.xml index a82d3348..7e76bbf4 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -26,6 +26,11 @@ serverlessworkflow-impl-http ${project.version} + + io.serverlessworkflow + serverlessworkflow-impl-jackson + ${project.version} + org.glassfish.jersey.core jersey-client @@ -38,7 +43,7 @@ io.cloudevents - cloudevents-api + cloudevents-core ${version.io.cloudevents} @@ -61,5 +66,6 @@ http core + jackson \ No newline at end of file diff --git a/pom.xml b/pom.xml index b17bda17..732e84b0 100644 --- a/pom.xml +++ b/pom.xml @@ -43,6 +43,7 @@ annotations generators serialization + examples @@ -142,12 +143,16 @@ jackson-annotations ${version.com.fasterxml.jackson} - org.slf4j slf4j-api ${version.org.slf4j} + + io.serverlessworkflow + serverlessworkflow-api + ${project.version} + com.networknt json-schema-validator From a3d9f58753926a8bb822ff429e916b2a35fc317d Mon Sep 17 00:00:00 2001 From: fjtirado Date: Thu, 17 Jul 2025 13:05:12 +0200 Subject: [PATCH 14/30] Minor refinements Signed-off-by: fjtirado --- .../impl/WorkflowApplication.java | 44 ++++++++++--------- .../impl/DateTimeDescriptorTest.java | 36 +++++++++++++++ 2 files changed, 59 insertions(+), 21 deletions(-) create mode 100644 impl/jackson/src/test/java/io/serverlessworkflow/impl/DateTimeDescriptorTest.java diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java index 0477ccf1..ab23f2c5 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java @@ -103,26 +103,28 @@ public WorkflowIdFactory idFactory() { } public static class Builder { - private static final SchemaValidatorFactory EMPTY_SCHEMA_VALIDATOR = - new SchemaValidatorFactory() { - - private final SchemaValidator NoValidation = - new SchemaValidator() { - @Override - public void validate(WorkflowModel node) {} - }; - - @Override - public SchemaValidator getValidator(StaticResource resource) { - - return NoValidation; - } - - @Override - public SchemaValidator getValidator(SchemaInline inline) { - return NoValidation; - } - }; + + private static final class EmptySchemaValidatorHolder { + private static final SchemaValidatorFactory instance = + new SchemaValidatorFactory() { + private final SchemaValidator NoValidation = + new SchemaValidator() { + @Override + public void validate(WorkflowModel node) {} + }; + + @Override + public SchemaValidator getValidator(StaticResource resource) { + return NoValidation; + } + + @Override + public SchemaValidator getValidator(SchemaInline inline) { + return NoValidation; + } + }; + } + private TaskExecutorFactory taskFactory = DefaultTaskExecutorFactory.get(); private ExpressionFactory exprFactory; private Collection listeners; @@ -207,7 +209,7 @@ public WorkflowApplication build() { schemaValidatorFactory = ServiceLoader.load(SchemaValidatorFactory.class) .findFirst() - .orElse(EMPTY_SCHEMA_VALIDATOR); + .orElseGet(() -> EmptySchemaValidatorHolder.instance); } return new WorkflowApplication(this); } diff --git a/impl/jackson/src/test/java/io/serverlessworkflow/impl/DateTimeDescriptorTest.java b/impl/jackson/src/test/java/io/serverlessworkflow/impl/DateTimeDescriptorTest.java new file mode 100644 index 00000000..46937cbc --- /dev/null +++ b/impl/jackson/src/test/java/io/serverlessworkflow/impl/DateTimeDescriptorTest.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.impl; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.fasterxml.jackson.databind.JsonNode; +import io.serverlessworkflow.impl.expressions.DateTimeDescriptor; +import io.serverlessworkflow.impl.json.JsonUtils; +import java.time.Instant; +import org.junit.jupiter.api.Test; + +class DateTimeDescriptorTest { + + @Test + void serializeDate() { + DateTimeDescriptor descriptor = DateTimeDescriptor.from(Instant.now()); + + JsonNode node = JsonUtils.fromValue(descriptor); + assertThat(node.get("iso8601").isTextual()).isTrue(); + assertThat(node.get("epoch").isObject()).isTrue(); + } +} From ead26ba5e72d8767f58b5aa1f1cdf29853df3ffe Mon Sep 17 00:00:00 2001 From: fjtirado Date: Thu, 17 Jul 2025 19:16:36 +0200 Subject: [PATCH 15/30] Adding java lambda support Signed-off-by: fjtirado --- experimental/lambda/pom.xml | 45 +++++ .../impl/executors/JavaCallExecutor.java | 71 +++++++ .../executors/JavaForExecutorBuilder.java | 73 ++++++++ .../executors/JavaSwitchExecutorBuilder.java | 48 +++++ .../executors/JavaTaskExecutorFactory.java | 43 +++++ .../expressions/JavaExpressionFactory.java | 66 +++++++ .../impl/expressions/JavaModel.java | 108 +++++++++++ .../impl/expressions/JavaModelCollection.java | 147 +++++++++++++++ .../impl/expressions/JavaModelFactory.java | 82 ++++++++ ...erlessworkflow.impl.executors.CallableTask | 1 + ...orkflow.impl.executors.TaskExecutorFactory | 1 + ...orkflow.impl.expressions.ExpressionFactory | 1 + .../io/serverless/workflow/impl/CallTest.java | 158 ++++++++++++++++ .../workflow/impl/JavaFunctions.java | 37 ++++ .../serverless/workflow/impl/ModelTest.java | 176 ++++++++++++++++++ .../io/serverless/workflow/impl/Person.java | 18 ++ experimental/pom.xml | 39 ++++ experimental/types/pom.xml | 16 ++ .../api/types/CallJava.java | 118 ++++++++++++ .../api/types/CallTaskJava.java | 34 ++++ .../api/types/ExportAsFunction.java | 26 +++ .../api/types/ForTaskFunction.java | 55 ++++++ .../api/types/InputFromFunction.java | 26 +++ .../api/types/OutputAsFunction.java | 26 +++ .../api/types/SwitchCaseFunction.java | 33 ++++ .../impl/expressions/LoopFunction.java | 21 +++ .../impl/expressions/LoopFunctionIndex.java | 21 +++ .../impl/expressions/LoopPredicate.java | 21 +++ .../impl/expressions/LoopPredicateIndex.java | 21 +++ impl/core/pom.xml | 25 --- .../impl/WorkflowApplication.java | 8 +- .../impl/WorkflowModelFactory.java | 2 + .../executors/DefaultTaskExecutorFactory.java | 46 +---- .../impl/executors/ForExecutor.java | 15 +- .../impl/executors/ForkExecutor.java | 8 +- .../impl/executors/SwitchExecutor.java | 17 +- pom.xml | 1 + 37 files changed, 1575 insertions(+), 79 deletions(-) create mode 100644 experimental/lambda/pom.xml create mode 100644 experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaCallExecutor.java create mode 100644 experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaForExecutorBuilder.java create mode 100644 experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaSwitchExecutorBuilder.java create mode 100644 experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaTaskExecutorFactory.java create mode 100644 experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaExpressionFactory.java create mode 100644 experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModel.java create mode 100644 experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelCollection.java create mode 100644 experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelFactory.java create mode 100644 experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.CallableTask create mode 100644 experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.TaskExecutorFactory create mode 100644 experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory create mode 100644 experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java create mode 100644 experimental/lambda/src/test/java/io/serverless/workflow/impl/JavaFunctions.java create mode 100644 experimental/lambda/src/test/java/io/serverless/workflow/impl/ModelTest.java create mode 100644 experimental/lambda/src/test/java/io/serverless/workflow/impl/Person.java create mode 100644 experimental/pom.xml create mode 100644 experimental/types/pom.xml create mode 100644 experimental/types/src/main/java/io/serverlessworkflow/api/types/CallJava.java create mode 100644 experimental/types/src/main/java/io/serverlessworkflow/api/types/CallTaskJava.java create mode 100644 experimental/types/src/main/java/io/serverlessworkflow/api/types/ExportAsFunction.java create mode 100644 experimental/types/src/main/java/io/serverlessworkflow/api/types/ForTaskFunction.java create mode 100644 experimental/types/src/main/java/io/serverlessworkflow/api/types/InputFromFunction.java create mode 100644 experimental/types/src/main/java/io/serverlessworkflow/api/types/OutputAsFunction.java create mode 100644 experimental/types/src/main/java/io/serverlessworkflow/api/types/SwitchCaseFunction.java create mode 100644 experimental/types/src/main/java/io/serverlessworkflow/impl/expressions/LoopFunction.java create mode 100644 experimental/types/src/main/java/io/serverlessworkflow/impl/expressions/LoopFunctionIndex.java create mode 100644 experimental/types/src/main/java/io/serverlessworkflow/impl/expressions/LoopPredicate.java create mode 100644 experimental/types/src/main/java/io/serverlessworkflow/impl/expressions/LoopPredicateIndex.java diff --git a/experimental/lambda/pom.xml b/experimental/lambda/pom.xml new file mode 100644 index 00000000..72e4c0b2 --- /dev/null +++ b/experimental/lambda/pom.xml @@ -0,0 +1,45 @@ + + 4.0.0 + + io.serverlessworkflow + serverlessworkflow-experimental + 8.0.0-SNAPSHOT + + serverlessworkflow-experimental-lambda + ServelessWorkflow:: Experimental:: lambda + + + io.serverlessworkflow + serverlessworkflow-experimental-types + + + io.serverlessworkflow + serverlessworkflow-impl-core + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.assertj + assertj-core + test + + + ch.qos.logback + logback-classic + test + + + \ No newline at end of file diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaCallExecutor.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaCallExecutor.java new file mode 100644 index 00000000..8d166986 --- /dev/null +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaCallExecutor.java @@ -0,0 +1,71 @@ +/* + * 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; + +import io.serverlessworkflow.api.types.CallJava; +import io.serverlessworkflow.api.types.TaskBase; +import io.serverlessworkflow.impl.TaskContext; +import io.serverlessworkflow.impl.WorkflowApplication; +import io.serverlessworkflow.impl.WorkflowContext; +import io.serverlessworkflow.impl.WorkflowModel; +import io.serverlessworkflow.impl.WorkflowModelFactory; +import io.serverlessworkflow.impl.resources.ResourceLoader; +import java.util.concurrent.CompletableFuture; + +public class JavaCallExecutor implements CallableTask { + + @Override + public void init(CallJava task, WorkflowApplication application, ResourceLoader loader) {} + + @Override + public CompletableFuture apply( + WorkflowContext workflowContext, TaskContext taskContext, WorkflowModel input) { + WorkflowModelFactory modelFactory = workflowContext.definition().application().modelFactory(); + if (taskContext.task() instanceof CallJava.CallJavaFunction function) { + return CompletableFuture.completedFuture( + modelFactory.fromAny(function.function().apply(input.asJavaObject()))); + } else if (taskContext.task() instanceof CallJava.CallJavaLoopFunction function) { + return CompletableFuture.completedFuture( + modelFactory.fromAny( + function + .function() + .apply( + input.asJavaObject(), + safeObject(taskContext.variables().get(function.varName()))))); + } else if (taskContext.task() instanceof CallJava.CallJavaLoopFunctionIndex function) { + return CompletableFuture.completedFuture( + modelFactory.fromAny( + function + .function() + .apply( + input.asJavaObject(), + safeObject(taskContext.variables().get(function.varName())), + (Integer) safeObject(taskContext.variables().get(function.indexName()))))); + } else if (taskContext.task() instanceof CallJava.CallJavaConsumer consumer) { + consumer.consumer().accept(input.asJavaObject()); + } + return CompletableFuture.completedFuture(input); + } + + @Override + public boolean accept(Class clazz) { + return CallJava.class.isAssignableFrom(clazz); + } + + static Object safeObject(Object obj) { + return obj instanceof WorkflowModel model ? model.asJavaObject() : obj; + } +} diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaForExecutorBuilder.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaForExecutorBuilder.java new file mode 100644 index 00000000..faa1942c --- /dev/null +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaForExecutorBuilder.java @@ -0,0 +1,73 @@ +/* + * 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; + +import static io.serverlessworkflow.impl.executors.JavaCallExecutor.safeObject; + +import io.serverlessworkflow.api.types.ForTask; +import io.serverlessworkflow.api.types.ForTaskFunction; +import io.serverlessworkflow.api.types.Workflow; +import io.serverlessworkflow.impl.WorkflowApplication; +import io.serverlessworkflow.impl.WorkflowFilter; +import io.serverlessworkflow.impl.WorkflowPosition; +import io.serverlessworkflow.impl.WorkflowUtils; +import io.serverlessworkflow.impl.executors.ForExecutor.ForExecutorBuilder; +import io.serverlessworkflow.impl.expressions.LoopPredicateIndex; +import io.serverlessworkflow.impl.resources.ResourceLoader; +import java.util.Optional; + +public class JavaForExecutorBuilder extends ForExecutorBuilder { + + protected JavaForExecutorBuilder( + WorkflowPosition position, + ForTask task, + Workflow workflow, + WorkflowApplication application, + ResourceLoader resourceLoader) { + super(position, task, workflow, application, resourceLoader); + if (task instanceof ForTaskFunction taskFunctions) {} + } + + protected Optional buildWhileFilter() { + if (task instanceof ForTaskFunction taskFunctions) { + LoopPredicateIndex whilePred = taskFunctions.getWhilePredicate(); + 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 application + .modelFactory() + .from( + item == null + || whilePred.test( + n.asJavaObject(), + item, + (Integer) safeObject(t.variables().get(indexName)))); + }); + } + } + return super.buildWhileFilter(); + } + + protected WorkflowFilter buildCollectionFilter() { + return task instanceof ForTaskFunction taskFunctions + ? WorkflowUtils.buildWorkflowFilter(application, null, taskFunctions.getCollection()) + : super.buildCollectionFilter(); + } +} diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaSwitchExecutorBuilder.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaSwitchExecutorBuilder.java new file mode 100644 index 00000000..3b42825d --- /dev/null +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaSwitchExecutorBuilder.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.impl.executors; + +import io.serverlessworkflow.api.types.SwitchCase; +import io.serverlessworkflow.api.types.SwitchCaseFunction; +import io.serverlessworkflow.api.types.SwitchTask; +import io.serverlessworkflow.api.types.Workflow; +import io.serverlessworkflow.impl.WorkflowApplication; +import io.serverlessworkflow.impl.WorkflowFilter; +import io.serverlessworkflow.impl.WorkflowPosition; +import io.serverlessworkflow.impl.WorkflowUtils; +import io.serverlessworkflow.impl.executors.SwitchExecutor.SwitchExecutorBuilder; +import io.serverlessworkflow.impl.resources.ResourceLoader; +import java.util.Optional; + +public class JavaSwitchExecutorBuilder extends SwitchExecutorBuilder { + + protected JavaSwitchExecutorBuilder( + WorkflowPosition position, + SwitchTask task, + Workflow workflow, + WorkflowApplication application, + ResourceLoader resourceLoader) { + super(position, task, workflow, application, resourceLoader); + } + + @Override + protected Optional buildFilter(SwitchCase switchCase) { + return switchCase instanceof SwitchCaseFunction function + ? Optional.of(WorkflowUtils.buildWorkflowFilter(application, null, function.predicate())) + : super.buildFilter(switchCase); + } +} diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaTaskExecutorFactory.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaTaskExecutorFactory.java new file mode 100644 index 00000000..26177287 --- /dev/null +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaTaskExecutorFactory.java @@ -0,0 +1,43 @@ +/* + * 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; + +import io.serverlessworkflow.api.types.Task; +import io.serverlessworkflow.api.types.TaskBase; +import io.serverlessworkflow.api.types.Workflow; +import io.serverlessworkflow.impl.WorkflowApplication; +import io.serverlessworkflow.impl.WorkflowPosition; +import io.serverlessworkflow.impl.resources.ResourceLoader; + +public class JavaTaskExecutorFactory extends DefaultTaskExecutorFactory { + + public TaskExecutorBuilder getTaskExecutor( + WorkflowPosition position, + Task task, + Workflow workflow, + WorkflowApplication application, + ResourceLoader resourceLoader) { + if (task.getForTask() != null) { + return new JavaForExecutorBuilder( + position, task.getForTask(), workflow, application, resourceLoader); + } else if (task.getSwitchTask() != null) { + return new JavaSwitchExecutorBuilder( + position, task.getSwitchTask(), workflow, application, resourceLoader); + } else { + return super.getTaskExecutor(position, task, workflow, application, resourceLoader); + } + } +} diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaExpressionFactory.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaExpressionFactory.java new file mode 100644 index 00000000..a6e89ae8 --- /dev/null +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaExpressionFactory.java @@ -0,0 +1,66 @@ +/* + * 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.expressions; + +import io.serverlessworkflow.impl.TaskContext; +import io.serverlessworkflow.impl.WorkflowContext; +import io.serverlessworkflow.impl.WorkflowFilter; +import io.serverlessworkflow.impl.WorkflowModel; +import io.serverlessworkflow.impl.WorkflowModelFactory; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.Function; +import java.util.function.Predicate; + +public class JavaExpressionFactory implements ExpressionFactory { + + private final WorkflowModelFactory modelFactory = new JavaModelFactory(); + private final Expression dummyExpression = + new Expression() { + @Override + public WorkflowModel eval( + WorkflowContext workflowContext, TaskContext context, WorkflowModel model) { + return model; + } + }; + + @Override + public Expression buildExpression(String expression) { + return dummyExpression; + } + + @Override + public WorkflowFilter buildFilter(String expr, Object value) { + if (value instanceof Function func) { + return (w, t, n) -> modelFactory.fromAny(func.apply(n.asJavaObject())); + } else if (value instanceof Predicate pred) { + return (w, t, n) -> modelFactory.from(pred.test(n.asJavaObject())); + } else if (value instanceof BiPredicate pred) { + return (w, t, n) -> modelFactory.from(pred.test(w, t)); + } else if (value instanceof BiFunction func) { + return (w, t, n) -> modelFactory.fromAny(func.apply(w, t)); + } else if (value instanceof WorkflowFilter filter) { + return filter; + } else { + return (w, t, n) -> modelFactory.fromAny(value); + } + } + + @Override + public WorkflowModelFactory modelFactory() { + return modelFactory; + } +} diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModel.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModel.java new file mode 100644 index 00000000..0e4b4df1 --- /dev/null +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModel.java @@ -0,0 +1,108 @@ +/* + * 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.expressions; + +import io.cloudevents.CloudEventData; +import io.serverlessworkflow.impl.WorkflowModel; +import java.time.OffsetDateTime; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiConsumer; + +public class JavaModel implements WorkflowModel { + + private Object object; + + static final JavaModel TrueModel = new JavaModel(Boolean.TRUE); + static final JavaModel FalseModel = new JavaModel(Boolean.FALSE); + static final JavaModel NullModel = new JavaModel(null); + + JavaModel(Object object) { + this.object = object; + } + + @Override + public void forEach(BiConsumer consumer) { + asMap() + .ifPresent( + m -> + m.forEach( + (k, v) -> + consumer.accept( + k, v instanceof WorkflowModel model ? model : new JavaModel(v)))); + } + + @Override + public Optional asBoolean() { + return object instanceof Boolean value ? Optional.of(value) : Optional.empty(); + } + + @Override + public Collection asCollection() { + return object instanceof Collection value + ? new JavaModelCollection(value) + : Collections.emptyList(); + } + + @Override + public Optional asText() { + return object instanceof String value ? Optional.of(value) : Optional.empty(); + } + + @Override + public Optional asDate() { + return object instanceof OffsetDateTime value ? Optional.of(value) : Optional.empty(); + } + + @Override + public Optional asNumber() { + return object instanceof Number value ? Optional.of(value) : Optional.empty(); + } + + @Override + public Optional asCloudEventData() { + return object instanceof CloudEventData value ? Optional.of(value) : Optional.empty(); + } + + @Override + public Optional> asMap() { + return object instanceof Map ? Optional.of((Map) object) : Optional.empty(); + } + + @Override + public Object asJavaObject() { + return object; + } + + @Override + public Object asIs() { + return object; + } + + @Override + public Class objectClass() { + return object != null ? object.getClass() : Object.class; + } + + @Override + public Optional as(Class clazz) { + return object != null && object.getClass().isAssignableFrom(clazz) + ? Optional.of(clazz.cast(object)) + : Optional.empty(); + } +} diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelCollection.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelCollection.java new file mode 100644 index 00000000..065d8832 --- /dev/null +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelCollection.java @@ -0,0 +1,147 @@ +/* + * 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.expressions; + +import io.serverlessworkflow.impl.WorkflowModel; +import io.serverlessworkflow.impl.WorkflowModelCollection; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Optional; + +public class JavaModelCollection implements Collection, WorkflowModelCollection { + + private final Collection object; + + JavaModelCollection() { + this.object = new ArrayList<>(); + } + + JavaModelCollection(Collection object) { + this.object = object; + } + + @Override + public int size() { + return object.size(); + } + + @Override + public boolean isEmpty() { + return object.isEmpty(); + } + + @Override + public boolean contains(Object o) { + throw new UnsupportedOperationException(); + } + + private class ModelIterator implements Iterator { + + private Iterator wrapped; + + public ModelIterator(Iterator wrapped) { + this.wrapped = wrapped; + } + + @Override + public boolean hasNext() { + return wrapped.hasNext(); + } + + @Override + public WorkflowModel next() { + Object obj = wrapped.next(); + return obj instanceof WorkflowModel value ? value : new JavaModel(obj); + } + } + + @Override + public Iterator iterator() { + return new ModelIterator(object.iterator()); + } + + @Override + public Object[] toArray() { + throw new UnsupportedOperationException(); + } + + @Override + public T[] toArray(T[] a) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean add(WorkflowModel e) { + return object.add(e.asIs()); + } + + @Override + public boolean remove(Object o) { + return object.remove(((WorkflowModel) o).asIs()); + } + + @Override + public boolean containsAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection c) { + int size = size(); + c.forEach(this::add); + return size() > size; + } + + @Override + public boolean removeAll(Collection c) { + int size = size(); + c.forEach(this::remove); + return size() < size; + } + + @Override + public boolean retainAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + object.clear(); + } + + @Override + public Object asJavaObject() { + return object; + } + + @Override + public Object asIs() { + return object; + } + + @Override + public Class objectClass() { + return object.getClass(); + } + + @Override + public Optional as(Class clazz) { + return object.getClass().isAssignableFrom(clazz) + ? Optional.of(clazz.cast(object)) + : Optional.empty(); + } +} diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelFactory.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelFactory.java new file mode 100644 index 00000000..6034d33a --- /dev/null +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelFactory.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.impl.expressions; + +import io.cloudevents.CloudEvent; +import io.cloudevents.CloudEventData; +import io.serverlessworkflow.impl.WorkflowModel; +import io.serverlessworkflow.impl.WorkflowModelCollection; +import io.serverlessworkflow.impl.WorkflowModelFactory; +import java.time.OffsetDateTime; +import java.util.Map; + +public class JavaModelFactory implements WorkflowModelFactory { + + @Override + public WorkflowModel combine(Map workflowVariables) { + return new JavaModel(workflowVariables); + } + + @Override + public WorkflowModelCollection createCollection() { + return new JavaModelCollection(); + } + + @Override + public WorkflowModel from(boolean value) { + return value ? JavaModel.TrueModel : JavaModel.FalseModel; + } + + @Override + public WorkflowModel from(Number value) { + return new JavaModel(value); + } + + @Override + public WorkflowModel from(String value) { + return new JavaModel(value); + } + + @Override + public WorkflowModel from(CloudEvent ce) { + return new JavaModel(ce); + } + + @Override + public WorkflowModel from(CloudEventData ce) { + return new JavaModel(ce); + } + + @Override + public WorkflowModel from(OffsetDateTime value) { + return new JavaModel(value); + } + + @Override + public WorkflowModel from(Map map) { + return new JavaModel(map); + } + + @Override + public WorkflowModel fromNull() { + return JavaModel.NullModel; + } + + @Override + public WorkflowModel fromAny(Object obj) { + return new JavaModel(obj); + } +} diff --git a/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.CallableTask b/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.CallableTask new file mode 100644 index 00000000..e413059c --- /dev/null +++ b/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.CallableTask @@ -0,0 +1 @@ +io.serverlessworkflow.impl.executors.JavaCallExecutor \ No newline at end of file diff --git a/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.TaskExecutorFactory b/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.TaskExecutorFactory new file mode 100644 index 00000000..6fd5dc15 --- /dev/null +++ b/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.TaskExecutorFactory @@ -0,0 +1 @@ +io.serverlessworkflow.impl.executors.JavaTaskExecutorFactory \ No newline at end of file diff --git a/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory b/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory new file mode 100644 index 00000000..171ce036 --- /dev/null +++ b/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory @@ -0,0 +1 @@ +io.serverlessworkflow.impl.expressions.JavaExpressionFactory \ No newline at end of file diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java new file mode 100644 index 00000000..9118ec06 --- /dev/null +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java @@ -0,0 +1,158 @@ +/* + * 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.serverless.workflow.impl; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.serverlessworkflow.api.types.CallJava; +import io.serverlessworkflow.api.types.CallTaskJava; +import io.serverlessworkflow.api.types.Document; +import io.serverlessworkflow.api.types.FlowDirective; +import io.serverlessworkflow.api.types.FlowDirectiveEnum; +import io.serverlessworkflow.api.types.ForTaskConfiguration; +import io.serverlessworkflow.api.types.ForTaskFunction; +import io.serverlessworkflow.api.types.SwitchCaseFunction; +import io.serverlessworkflow.api.types.SwitchItem; +import io.serverlessworkflow.api.types.SwitchTask; +import io.serverlessworkflow.api.types.Task; +import io.serverlessworkflow.api.types.TaskItem; +import io.serverlessworkflow.api.types.Workflow; +import io.serverlessworkflow.impl.WorkflowApplication; +import io.serverlessworkflow.impl.WorkflowDefinition; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ExecutionException; +import org.junit.jupiter.api.Test; + +class CallTest { + + @Test + void testJavaFunction() throws InterruptedException, ExecutionException { + try (WorkflowApplication app = WorkflowApplication.builder().build()) { + Workflow workflow = + new Workflow() + .withDocument( + new Document().withNamespace("test").withName("testJavaCall").withVersion("1.0")) + .withDo( + List.of( + new TaskItem( + "javaCall", + new Task() + .withCallTask( + new CallTaskJava(CallJava.function(JavaFunctions::getName)))))); + + assertThat( + app.workflowDefinition(workflow) + .instance(new Person("Francisco", 33)) + .start() + .get() + .asText() + .orElseThrow()) + .isEqualTo("Francisco Javierito"); + } + } + + @Test + 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")) + .withDo( + List.of( + new TaskItem( + "forLoop", + new Task() + .withForTask( + new ForTaskFunction() + .withWhile(this::isEven) + .withCollection(v -> (Collection) v) + .withFor(forConfig) + .withDo( + List.of( + new TaskItem( + "javaCall", + new Task() + .withCallTask( + new CallTaskJava( + CallJava.loopFunction( + this::sum, + forConfig.getEach())))))))))); + + assertThat( + app.workflowDefinition(workflow) + .instance(List.of(2, 4, 6)) + .start() + .get() + .asNumber() + .orElseThrow()) + .isEqualTo(12); + } + } + + @Test + void testSwitch() throws InterruptedException, ExecutionException { + try (WorkflowApplication app = WorkflowApplication.builder().build()) { + Workflow workflow = + new Workflow() + .withDocument( + new Document().withNamespace("test").withName("testSwith").withVersion("1.0")) + .withDo( + List.of( + new TaskItem( + "switch", + new Task() + .withSwitchTask( + new SwitchTask() + .withSwitch( + List.of( + new SwitchItem( + "odd", + new SwitchCaseFunction() + .withPredicate(this::isOdd) + .withThen( + new FlowDirective() + .withFlowDirectiveEnum( + FlowDirectiveEnum.END))))))), + new TaskItem( + "java", + new Task() + .withCallTask(new CallTaskJava(CallJava.function(this::zero)))))); + + WorkflowDefinition definition = app.workflowDefinition(workflow); + assertThat(definition.instance(3).start().get().asNumber().orElseThrow()).isEqualTo(3); + assertThat(definition.instance(4).start().get().asNumber().orElseThrow()).isEqualTo(0); + } + } + + private boolean isEven(Object model, Integer number) { + return !isOdd(number); + } + + private boolean isOdd(Integer number) { + return number % 2 != 0; + } + + private int zero(Integer value) { + return 0; + } + + private Integer sum(Object model, Integer item) { + return model instanceof Collection ? item : (Integer) model + item; + } +} diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/JavaFunctions.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/JavaFunctions.java new file mode 100644 index 00000000..f24766aa --- /dev/null +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/JavaFunctions.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.serverless.workflow.impl; + +import java.util.Map; + +public class JavaFunctions { + + static Person personPojo(String name) { + return new Person(name + " Javierito", 23); + } + + static String getName(Person person) { + return person.name() + " Javierito"; + } + + static Map addJavierito(Map map) { + return Map.of("name", map.get("name") + " Javierito"); + } + + static String addJavieritoString(String value) { + return value + " Javierito"; + } +} diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/ModelTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/ModelTest.java new file mode 100644 index 00000000..7702fffe --- /dev/null +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/ModelTest.java @@ -0,0 +1,176 @@ +/* + * 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.serverless.workflow.impl; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.serverlessworkflow.api.types.Document; +import io.serverlessworkflow.api.types.DurationInline; +import io.serverlessworkflow.api.types.Output; +import io.serverlessworkflow.api.types.OutputAsFunction; +import io.serverlessworkflow.api.types.Set; +import io.serverlessworkflow.api.types.SetTask; +import io.serverlessworkflow.api.types.SetTaskConfiguration; +import io.serverlessworkflow.api.types.Task; +import io.serverlessworkflow.api.types.TaskItem; +import io.serverlessworkflow.api.types.TimeoutAfter; +import io.serverlessworkflow.api.types.WaitTask; +import io.serverlessworkflow.api.types.Workflow; +import io.serverlessworkflow.impl.WorkflowApplication; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import org.junit.jupiter.api.Test; + +class ModelTest { + + @Test + void testStringExpression() throws InterruptedException, ExecutionException { + try (WorkflowApplication app = WorkflowApplication.builder().build()) { + Workflow workflow = + new Workflow() + .withDocument( + new Document().withNamespace("test").withName("testString").withVersion("1.0")) + .withDo( + List.of( + new TaskItem( + "doNothing", + new Task() + .withWaitTask( + new WaitTask() + .withWait( + new TimeoutAfter() + .withDurationInline( + new DurationInline().withMilliseconds(10))))))) + .withOutput( + new Output() + .withAs( + new OutputAsFunction().withFunction(JavaFunctions::addJavieritoString))); + + assertThat( + app.workflowDefinition(workflow) + .instance("Francisco") + .start() + .get() + .asText() + .orElseThrow()) + .isEqualTo("Francisco Javierito"); + } + } + + @Test + void testMapExpression() throws InterruptedException, ExecutionException { + try (WorkflowApplication app = WorkflowApplication.builder().build()) { + Workflow workflow = + new Workflow() + .withDocument( + new Document().withNamespace("test").withName("testMap").withVersion("1.0")) + .withDo( + List.of( + new TaskItem( + "javierito", + new Task() + .withSetTask( + new SetTask() + .withSet( + new Set() + .withSetTaskConfiguration( + new SetTaskConfiguration() + .withAdditionalProperty("name", "Francisco"))) + .withOutput( + new Output() + .withAs( + new OutputAsFunction() + .withFunction( + JavaFunctions::addJavierito))))))); + assertThat( + app.workflowDefinition(workflow) + .instance(Map.of()) + .start() + .get() + .asMap() + .map(m -> m.get("name")) + .orElseThrow()) + .isEqualTo("Francisco Javierito"); + } + } + + @Test + void testStringPOJOExpression() throws InterruptedException, ExecutionException { + try (WorkflowApplication app = WorkflowApplication.builder().build()) { + Workflow workflow = + new Workflow() + .withDocument( + new Document().withNamespace("test").withName("testPojo").withVersion("1.0")) + .withDo( + List.of( + new TaskItem( + "doNothing", + new Task() + .withWaitTask( + new WaitTask() + .withWait( + new TimeoutAfter() + .withDurationInline( + new DurationInline().withMilliseconds(10))))))) + .withOutput( + new Output() + .withAs(new OutputAsFunction().withFunction(JavaFunctions::personPojo))); + + assertThat( + app.workflowDefinition(workflow) + .instance("Francisco") + .start() + .get() + .as(Person.class) + .orElseThrow() + .name()) + .isEqualTo("Francisco Javierito"); + } + } + + @Test + void testPOJOStringExpression() throws InterruptedException, ExecutionException { + try (WorkflowApplication app = WorkflowApplication.builder().build()) { + Workflow workflow = + new Workflow() + .withDocument( + new Document().withNamespace("test").withName("testPojo").withVersion("1.0")) + .withDo( + List.of( + new TaskItem( + "doNothing", + new Task() + .withWaitTask( + new WaitTask() + .withWait( + new TimeoutAfter() + .withDurationInline( + new DurationInline().withMilliseconds(10))))))) + .withOutput( + new Output().withAs(new OutputAsFunction().withFunction(JavaFunctions::getName))); + + assertThat( + app.workflowDefinition(workflow) + .instance(new Person("Francisco", 33)) + .start() + .get() + .asText() + .orElseThrow()) + .isEqualTo("Francisco Javierito"); + } + } +} diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/Person.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/Person.java new file mode 100644 index 00000000..9594c285 --- /dev/null +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/Person.java @@ -0,0 +1,18 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverless.workflow.impl; + +record Person(String name, int age) {} diff --git a/experimental/pom.xml b/experimental/pom.xml new file mode 100644 index 00000000..409e68c2 --- /dev/null +++ b/experimental/pom.xml @@ -0,0 +1,39 @@ + + 4.0.0 + + io.serverlessworkflow + serverlessworkflow-parent + 8.0.0-SNAPSHOT + + serverlessworkflow-experimental + pom + ServerlessWorkflow:: Experimental + + + + io.serverlessworkflow + serverlessworkflow-impl-core + ${project.version} + + + io.serverlessworkflow + serverlessworkflow-types + ${project.version} + + + io.serverlessworkflow + serverlessworkflow-experimental-lambda + ${project.version} + + + io.serverlessworkflow + serverlessworkflow-experimental-types + ${project.version} + + + + + types + lambda + + \ No newline at end of file diff --git a/experimental/types/pom.xml b/experimental/types/pom.xml new file mode 100644 index 00000000..dea3931d --- /dev/null +++ b/experimental/types/pom.xml @@ -0,0 +1,16 @@ + + 4.0.0 + + io.serverlessworkflow + serverlessworkflow-experimental + 8.0.0-SNAPSHOT + + serverlessworkflow-experimental-types + ServelessWorkflow:: Experimental:: Types + + + io.serverlessworkflow + serverlessworkflow-types + + + \ No newline at end of file diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/CallJava.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/CallJava.java new file mode 100644 index 00000000..c3115de2 --- /dev/null +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/CallJava.java @@ -0,0 +1,118 @@ +/* + * 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; + +import io.serverlessworkflow.impl.expressions.LoopFunction; +import io.serverlessworkflow.impl.expressions.LoopFunctionIndex; +import java.util.function.Consumer; +import java.util.function.Function; + +public abstract class CallJava extends TaskBase { + + private static final long serialVersionUID = 1L; + + public static CallJava consumer(Consumer consumer) { + return new CallJavaConsumer<>(consumer); + } + + public static CallJava function(Function function) { + return new CallJavaFunction<>(function); + } + + public static CallJava 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 class CallJavaConsumer extends CallJava { + + private static final long serialVersionUID = 1L; + private Consumer consumer; + + public CallJavaConsumer(Consumer consumer) { + this.consumer = consumer; + } + + public Consumer consumer() { + return consumer; + } + } + + public static class CallJavaFunction extends CallJava { + + private static final long serialVersionUID = 1L; + private Function function; + + public CallJavaFunction(Function function) { + this.function = function; + } + + public Function function() { + return function; + } + } + + public static class CallJavaLoopFunction extends CallJava { + + 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 CallJava { + + 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/CallTaskJava.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/CallTaskJava.java new file mode 100644 index 00000000..e1b406ec --- /dev/null +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/CallTaskJava.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.api.types; + +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(); + } +} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/ExportAsFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/ExportAsFunction.java new file mode 100644 index 00000000..fd279cd2 --- /dev/null +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/ExportAsFunction.java @@ -0,0 +1,26 @@ +/* + * 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; + +import java.util.function.Function; + +public class ExportAsFunction extends ExportAs { + + public ExportAs withFunction(Function value) { + setObject(value); + return this; + } +} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/ForTaskFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/ForTaskFunction.java new file mode 100644 index 00000000..00e29614 --- /dev/null +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/ForTaskFunction.java @@ -0,0 +1,55 @@ +/* + * 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; + +import io.serverlessworkflow.impl.expressions.LoopPredicate; +import io.serverlessworkflow.impl.expressions.LoopPredicateIndex; +import java.util.Collection; +import java.util.function.Function; + +public class ForTaskFunction extends ForTask { + + private static final long serialVersionUID = 1L; + private LoopPredicateIndex whilePredicate; + private Function> collection; + + public ForTaskFunction withWhile(LoopPredicate whilePredicate) { + this.whilePredicate = toPredicateIndex(whilePredicate); + return this; + } + + private LoopPredicateIndex toPredicateIndex(LoopPredicate whilePredicate) { + return (model, item, index) -> whilePredicate.test(model, item); + } + + public ForTaskFunction withWhile(LoopPredicateIndex whilePredicate) { + this.whilePredicate = whilePredicate; + return this; + } + + public ForTaskFunction withCollection(Function> collection) { + this.collection = collection; + return this; + } + + public LoopPredicateIndex getWhilePredicate() { + return whilePredicate; + } + + public Function> getCollection() { + return collection; + } +} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/InputFromFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/InputFromFunction.java new file mode 100644 index 00000000..abea6daa --- /dev/null +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/InputFromFunction.java @@ -0,0 +1,26 @@ +/* + * 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; + +import java.util.function.Function; + +public class InputFromFunction extends InputFrom { + + public InputFrom withFunction(Function value) { + setObject(value); + return this; + } +} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/OutputAsFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/OutputAsFunction.java new file mode 100644 index 00000000..7ae183a4 --- /dev/null +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/OutputAsFunction.java @@ -0,0 +1,26 @@ +/* + * 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; + +import java.util.function.Function; + +public class OutputAsFunction extends OutputAs { + + public OutputAs withFunction(Function value) { + setObject(value); + return this; + } +} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/SwitchCaseFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/SwitchCaseFunction.java new file mode 100644 index 00000000..027ab178 --- /dev/null +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/SwitchCaseFunction.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.api.types; + +import java.util.function.Predicate; + +public class SwitchCaseFunction extends SwitchCase { + + private static final long serialVersionUID = 1L; + private Predicate predicate; + + public SwitchCaseFunction withPredicate(Predicate predicate) { + this.predicate = predicate; + return this; + } + + public Predicate predicate() { + return predicate; + } +} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/impl/expressions/LoopFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/impl/expressions/LoopFunction.java new file mode 100644 index 00000000..6e23b97b --- /dev/null +++ b/experimental/types/src/main/java/io/serverlessworkflow/impl/expressions/LoopFunction.java @@ -0,0 +1,21 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.impl.expressions; + +import java.util.function.BiFunction; + +@FunctionalInterface +public interface LoopFunction extends BiFunction {} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/impl/expressions/LoopFunctionIndex.java b/experimental/types/src/main/java/io/serverlessworkflow/impl/expressions/LoopFunctionIndex.java new file mode 100644 index 00000000..783092dd --- /dev/null +++ b/experimental/types/src/main/java/io/serverlessworkflow/impl/expressions/LoopFunctionIndex.java @@ -0,0 +1,21 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.impl.expressions; + +@FunctionalInterface +public interface LoopFunctionIndex { + R apply(T model, V item, Integer index); +} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/impl/expressions/LoopPredicate.java b/experimental/types/src/main/java/io/serverlessworkflow/impl/expressions/LoopPredicate.java new file mode 100644 index 00000000..ecbeda77 --- /dev/null +++ b/experimental/types/src/main/java/io/serverlessworkflow/impl/expressions/LoopPredicate.java @@ -0,0 +1,21 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.impl.expressions; + +import java.util.function.BiPredicate; + +@FunctionalInterface +public interface LoopPredicate extends BiPredicate {} diff --git a/experimental/types/src/main/java/io/serverlessworkflow/impl/expressions/LoopPredicateIndex.java b/experimental/types/src/main/java/io/serverlessworkflow/impl/expressions/LoopPredicateIndex.java new file mode 100644 index 00000000..dc897683 --- /dev/null +++ b/experimental/types/src/main/java/io/serverlessworkflow/impl/expressions/LoopPredicateIndex.java @@ -0,0 +1,21 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.impl.expressions; + +@FunctionalInterface +public interface LoopPredicateIndex { + boolean test(T model, V item, Integer index); +} diff --git a/impl/core/pom.xml b/impl/core/pom.xml index 2ea1eb9b..6cc2beed 100644 --- a/impl/core/pom.xml +++ b/impl/core/pom.xml @@ -25,30 +25,5 @@ com.github.f4b6a3 ulid-creator - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.junit.jupiter - junit-jupiter-params - test - - - org.assertj - assertj-core - test - - - ch.qos.logback - logback-classic - test - diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java index ab23f2c5..16063c60 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java @@ -125,7 +125,7 @@ public SchemaValidator getValidator(SchemaInline inline) { }; } - private TaskExecutorFactory taskFactory = DefaultTaskExecutorFactory.get(); + private TaskExecutorFactory taskFactory; private ExpressionFactory exprFactory; private Collection listeners; private ResourceLoaderFactory resourceLoaderFactory = DefaultResourceLoaderFactory.get(); @@ -211,6 +211,12 @@ public WorkflowApplication build() { .findFirst() .orElseGet(() -> EmptySchemaValidatorHolder.instance); } + if (taskFactory == null) { + taskFactory = + ServiceLoader.load(TaskExecutorFactory.class) + .findFirst() + .orElseGet(() -> DefaultTaskExecutorFactory.get()); + } return new WorkflowApplication(this); } } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModelFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModelFactory.java index f8cf7278..4c55723a 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModelFactory.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowModelFactory.java @@ -59,6 +59,8 @@ default WorkflowModel fromAny(Object obj) { return from(value); } else if (obj instanceof Map) { return from((Map) obj); + } else if (obj instanceof WorkflowModel model) { + return model; } else { throw new IllegalArgumentException( "Unsopported conversion for object " + obj + " of type" + obj.getClass()); 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 0499fced..bf63b5ed 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 @@ -15,11 +15,6 @@ */ package io.serverlessworkflow.impl.executors; -import io.serverlessworkflow.api.types.CallAsyncAPI; -import io.serverlessworkflow.api.types.CallFunction; -import io.serverlessworkflow.api.types.CallGRPC; -import io.serverlessworkflow.api.types.CallHTTP; -import io.serverlessworkflow.api.types.CallOpenAPI; import io.serverlessworkflow.api.types.CallTask; import io.serverlessworkflow.api.types.Task; import io.serverlessworkflow.api.types.TaskBase; @@ -62,46 +57,15 @@ public TaskExecutorBuilder getTaskExecutor( ResourceLoader resourceLoader) { if (task.getCallTask() != null) { CallTask callTask = task.getCallTask(); - if (callTask.getCallHTTP() != null) { - return new CallTaskExecutorBuilder<>( + TaskBase taskBase = (TaskBase) callTask.get(); + if (taskBase != null) { + return new CallTaskExecutorBuilder( position, - callTask.getCallHTTP(), + taskBase, workflow, application, resourceLoader, - findCallTask(CallHTTP.class)); - } else if (callTask.getCallAsyncAPI() != null) { - return new CallTaskExecutorBuilder<>( - position, - callTask.getCallAsyncAPI(), - workflow, - application, - resourceLoader, - findCallTask(CallAsyncAPI.class)); - } else if (callTask.getCallGRPC() != null) { - return new CallTaskExecutorBuilder<>( - position, - callTask.getCallGRPC(), - workflow, - application, - resourceLoader, - findCallTask(CallGRPC.class)); - } else if (callTask.getCallOpenAPI() != null) { - return new CallTaskExecutorBuilder<>( - position, - callTask.getCallOpenAPI(), - workflow, - application, - resourceLoader, - findCallTask(CallOpenAPI.class)); - } else if (callTask.getCallFunction() != null) { - return new CallTaskExecutorBuilder<>( - position, - callTask.getCallFunction(), - workflow, - application, - resourceLoader, - findCallTask(CallFunction.class)); + findCallTask(taskBase.getClass())); } } else if (task.getSwitchTask() != null) { return new SwitchExecutorBuilder( 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 15b5e744..e0aa8d29 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 @@ -16,7 +16,6 @@ package io.serverlessworkflow.impl.executors; import io.serverlessworkflow.api.types.ForTask; -import io.serverlessworkflow.api.types.ForTaskConfiguration; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowApplication; @@ -25,7 +24,6 @@ import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowPosition; import io.serverlessworkflow.impl.WorkflowUtils; -import io.serverlessworkflow.impl.executors.RegularTaskExecutor.RegularTaskExecutorBuilder; import io.serverlessworkflow.impl.resources.ResourceLoader; import java.util.Iterator; import java.util.Optional; @@ -49,14 +47,21 @@ protected ForExecutorBuilder( WorkflowApplication application, ResourceLoader resourceLoader) { super(position, task, workflow, application, resourceLoader); - ForTaskConfiguration forConfig = task.getFor(); - this.collectionExpr = WorkflowUtils.buildWorkflowFilter(application, forConfig.getIn()); - this.whileExpr = WorkflowUtils.optionalFilter(application, task.getWhile()); + this.collectionExpr = buildCollectionFilter(); + this.whileExpr = buildWhileFilter(); this.taskExecutor = TaskExecutorHelper.createExecutorList( position, task.getDo(), workflow, application, resourceLoader); } + protected Optional buildWhileFilter() { + return WorkflowUtils.optionalFilter(application, task.getWhile()); + } + + protected WorkflowFilter buildCollectionFilter() { + return WorkflowUtils.buildWorkflowFilter(application, task.getFor().getIn()); + } + @Override public TaskExecutor buildInstance() { return new ForExecutor(this); diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/ForkExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/ForkExecutor.java index d92eb1a6..1353db89 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/ForkExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/ForkExecutor.java @@ -26,6 +26,7 @@ import io.serverlessworkflow.impl.executors.RegularTaskExecutor.RegularTaskExecutorBuilder; import io.serverlessworkflow.impl.resources.ResourceLoader; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; @@ -108,6 +109,11 @@ private WorkflowModel combine(WorkflowContext context, Map .application() .modelFactory() .combine( - sortedStream.collect(Collectors.toMap(Entry::getKey, e -> e.getValue().output()))); + sortedStream.collect( + Collectors.toMap( + Entry::getKey, + e -> e.getValue().output(), + (x, y) -> y, + LinkedHashMap::new))); } } 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 424d4c97..19a69568 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 @@ -30,6 +30,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @@ -53,15 +54,19 @@ public SwitchExecutorBuilder( super(position, task, workflow, application, resourceLoader); for (SwitchItem item : task.getSwitch()) { SwitchCase switchCase = item.getSwitchCase(); - if (switchCase.getWhen() != null) { - workflowFilters.put( - switchCase, WorkflowUtils.buildWorkflowFilter(application, switchCase.getWhen())); - } else { - defaultDirective = switchCase.getThen(); - } + buildFilter(switchCase) + .ifPresentOrElse( + f -> workflowFilters.put(switchCase, f), + () -> defaultDirective = switchCase.getThen()); } } + protected Optional buildFilter(SwitchCase switchCase) { + return switchCase.getWhen() != null + ? Optional.of(WorkflowUtils.buildWorkflowFilter(application, switchCase.getWhen())) + : Optional.empty(); + } + @Override public void connect(Map> connections) { this.switchFilters = diff --git a/pom.xml b/pom.xml index 732e84b0..b0a57369 100644 --- a/pom.xml +++ b/pom.xml @@ -44,6 +44,7 @@ generators serialization examples + experimental From a799b4aa14a884bafcd5006dbfb72512fbd385c5 Mon Sep 17 00:00:00 2001 From: Ricardo Zanini <1538000+ricardozanini@users.noreply.github.com> Date: Fri, 18 Jul 2025 12:06:37 -0400 Subject: [PATCH 16/30] Fix #368 - Introduce Standard Fluent DSL (#645) * Fix #368 - Introduce Standard Fluent DSL Signed-off-by: Ricardo Zanini * Add input/output to workflow/tasks Signed-off-by: Ricardo Zanini * Add callHttp task Signed-off-by: Ricardo Zanini --------- Signed-off-by: Ricardo Zanini --- fluent/pom.xml | 35 ++ fluent/standard/pom.xml | 32 ++ .../AuthenticationPolicyUnionBuilder.java | 80 +++ .../BasicAuthenticationPolicyBuilder.java | 46 ++ .../BearerAuthenticationPolicyBuilder.java | 40 ++ .../fluent/standard/CallHTTPTaskBuilder.java | 157 ++++++ .../DigestAuthenticationPolicyBuilder.java | 45 ++ .../fluent/standard/DoTaskBuilder.java | 156 ++++++ .../fluent/standard/DocumentBuilder.java | 108 ++++ .../standard/DurationInlineBuilder.java | 56 ++ .../fluent/standard/EmitTaskBuilder.java | 49 ++ .../standard/EventPropertiesBuilder.java | 78 +++ .../fluent/standard/ForTaskBuilder.java | 69 +++ .../fluent/standard/ForkTaskBuilder.java | 53 ++ .../fluent/standard/InputBuilder.java | 67 +++ .../fluent/standard/ListenTaskBuilder.java | 149 +++++ .../OAuth2AuthenticationPolicyBuilder.java | 57 ++ .../fluent/standard/OIDCBuilder.java | 190 +++++++ ...nIdConnectAuthenticationPolicyBuilder.java | 36 ++ .../fluent/standard/OutputBuilder.java | 67 +++ .../fluent/standard/RaiseTaskBuilder.java | 101 ++++ .../fluent/standard/SetTaskBuilder.java | 55 ++ .../fluent/standard/SwitchTaskBuilder.java | 76 +++ .../fluent/standard/TaskBaseBuilder.java | 120 ++++ .../fluent/standard/TryTaskBuilder.java | 338 ++++++++++++ .../fluent/standard/UriTemplateBuilder.java | 31 ++ .../standard/UseAuthenticationsBuilder.java | 40 ++ .../fluent/standard/UseBuilder.java | 49 ++ .../fluent/standard/WorkflowBuilder.java | 96 ++++ .../standard/WorkflowBuilderConsumers.java | 37 ++ .../fluent/standard/WorkflowBuilderTest.java | 518 ++++++++++++++++++ pom.xml | 1 + 32 files changed, 3032 insertions(+) create mode 100644 fluent/pom.xml create mode 100644 fluent/standard/pom.xml create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/AuthenticationPolicyUnionBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BasicAuthenticationPolicyBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BearerAuthenticationPolicyBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/CallHTTPTaskBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DigestAuthenticationPolicyBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DoTaskBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DocumentBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DurationInlineBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/EmitTaskBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/EventPropertiesBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForTaskBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForkTaskBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/InputBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ListenTaskBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OAuth2AuthenticationPolicyBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OIDCBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OpenIdConnectAuthenticationPolicyBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OutputBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/RaiseTaskBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SetTaskBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SwitchTaskBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskBaseBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TryTaskBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UriTemplateBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UseAuthenticationsBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UseBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderConsumers.java create mode 100644 fluent/standard/src/test/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderTest.java diff --git a/fluent/pom.xml b/fluent/pom.xml new file mode 100644 index 00000000..71d54a64 --- /dev/null +++ b/fluent/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + io.serverlessworkflow + serverlessworkflow-parent + 8.0.0-SNAPSHOT + + Serverless Workflow :: Fluent + serverlessworkflow-fluent + pom + + + 17 + 17 + UTF-8 + + + + + + io.serverlessworkflow + serverlessworkflow-types + ${project.version} + + + + + + standard + + + \ No newline at end of file diff --git a/fluent/standard/pom.xml b/fluent/standard/pom.xml new file mode 100644 index 00000000..570d9665 --- /dev/null +++ b/fluent/standard/pom.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + io.serverlessworkflow + serverlessworkflow-fluent + 8.0.0-SNAPSHOT + + Serverless Workflow :: Fluent :: Standard + serverlessworkflow-fluent-standard + + + 17 + 17 + UTF-8 + + + + + io.serverlessworkflow + serverlessworkflow-types + + + org.junit.jupiter + junit-jupiter-api + test + + + + \ No newline at end of file diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/AuthenticationPolicyUnionBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/AuthenticationPolicyUnionBuilder.java new file mode 100644 index 00000000..2699c809 --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/AuthenticationPolicyUnionBuilder.java @@ -0,0 +1,80 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.AuthenticationPolicyUnion; +import java.util.function.Consumer; + +public class AuthenticationPolicyUnionBuilder { + final AuthenticationPolicyUnion authenticationPolicy; + + AuthenticationPolicyUnionBuilder() { + this.authenticationPolicy = new AuthenticationPolicyUnion(); + } + + public AuthenticationPolicyUnionBuilder basic( + Consumer basicConsumer) { + final BasicAuthenticationPolicyBuilder basicAuthenticationPolicyBuilder = + new BasicAuthenticationPolicyBuilder(); + basicConsumer.accept(basicAuthenticationPolicyBuilder); + this.authenticationPolicy.setBasicAuthenticationPolicy( + basicAuthenticationPolicyBuilder.build()); + return this; + } + + public AuthenticationPolicyUnionBuilder bearer( + Consumer bearerConsumer) { + final BearerAuthenticationPolicyBuilder bearerAuthenticationPolicyBuilder = + new BearerAuthenticationPolicyBuilder(); + bearerConsumer.accept(bearerAuthenticationPolicyBuilder); + this.authenticationPolicy.setBearerAuthenticationPolicy( + bearerAuthenticationPolicyBuilder.build()); + return this; + } + + public AuthenticationPolicyUnionBuilder digest( + Consumer digestConsumer) { + final DigestAuthenticationPolicyBuilder digestAuthenticationPolicyBuilder = + new DigestAuthenticationPolicyBuilder(); + digestConsumer.accept(digestAuthenticationPolicyBuilder); + this.authenticationPolicy.setDigestAuthenticationPolicy( + digestAuthenticationPolicyBuilder.build()); + return this; + } + + public AuthenticationPolicyUnionBuilder oauth2( + Consumer oauth2Consumer) { + final OAuth2AuthenticationPolicyBuilder oauth2AuthenticationPolicyBuilder = + new OAuth2AuthenticationPolicyBuilder(); + oauth2Consumer.accept(oauth2AuthenticationPolicyBuilder); + this.authenticationPolicy.setOAuth2AuthenticationPolicy( + oauth2AuthenticationPolicyBuilder.build()); + return this; + } + + public AuthenticationPolicyUnionBuilder openIDConnect( + Consumer openIdConnectConsumer) { + final OpenIdConnectAuthenticationPolicyBuilder builder = + new OpenIdConnectAuthenticationPolicyBuilder(); + openIdConnectConsumer.accept(builder); + this.authenticationPolicy.setOpenIdConnectAuthenticationPolicy(builder.build()); + return this; + } + + public AuthenticationPolicyUnion build() { + return authenticationPolicy; + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BasicAuthenticationPolicyBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BasicAuthenticationPolicyBuilder.java new file mode 100644 index 00000000..c121f18f --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BasicAuthenticationPolicyBuilder.java @@ -0,0 +1,46 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.BasicAuthenticationPolicy; +import io.serverlessworkflow.api.types.BasicAuthenticationPolicyConfiguration; +import io.serverlessworkflow.api.types.BasicAuthenticationProperties; + +public final class BasicAuthenticationPolicyBuilder { + + private final BasicAuthenticationProperties basicAuthenticationProperties; + + BasicAuthenticationPolicyBuilder() { + this.basicAuthenticationProperties = new BasicAuthenticationProperties(); + } + + public BasicAuthenticationPolicyBuilder username(String username) { + this.basicAuthenticationProperties.setUsername(username); + return this; + } + + public BasicAuthenticationPolicyBuilder password(String password) { + this.basicAuthenticationProperties.setPassword(password); + return this; + } + + public BasicAuthenticationPolicy build() { + final BasicAuthenticationPolicyConfiguration configuration = + new BasicAuthenticationPolicyConfiguration(); + configuration.setBasicAuthenticationProperties(basicAuthenticationProperties); + return new BasicAuthenticationPolicy(configuration); + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BearerAuthenticationPolicyBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BearerAuthenticationPolicyBuilder.java new file mode 100644 index 00000000..08e52522 --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BearerAuthenticationPolicyBuilder.java @@ -0,0 +1,40 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.BearerAuthenticationPolicy; +import io.serverlessworkflow.api.types.BearerAuthenticationPolicyConfiguration; +import io.serverlessworkflow.api.types.BearerAuthenticationProperties; + +public final class BearerAuthenticationPolicyBuilder { + private final BearerAuthenticationProperties bearerAuthenticationProperties; + + BearerAuthenticationPolicyBuilder() { + this.bearerAuthenticationProperties = new BearerAuthenticationProperties(); + } + + public BearerAuthenticationPolicyBuilder token(final String token) { + this.bearerAuthenticationProperties.setToken(token); + return this; + } + + public BearerAuthenticationPolicy build() { + final BearerAuthenticationPolicyConfiguration configuration = + new BearerAuthenticationPolicyConfiguration(); + configuration.setBearerAuthenticationProperties(bearerAuthenticationProperties); + return new BearerAuthenticationPolicy(configuration); + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/CallHTTPTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/CallHTTPTaskBuilder.java new file mode 100644 index 00000000..f2603903 --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/CallHTTPTaskBuilder.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.fluent.standard; + +import io.serverlessworkflow.api.types.CallHTTP; +import io.serverlessworkflow.api.types.Endpoint; +import io.serverlessworkflow.api.types.HTTPArguments; +import io.serverlessworkflow.api.types.HTTPHeaders; +import io.serverlessworkflow.api.types.HTTPQuery; +import io.serverlessworkflow.api.types.Headers; +import io.serverlessworkflow.api.types.Query; +import io.serverlessworkflow.api.types.UriTemplate; +import java.net.URI; +import java.util.Map; +import java.util.function.Consumer; + +public class CallHTTPTaskBuilder extends TaskBaseBuilder { + + private final CallHTTP callHTTP; + + CallHTTPTaskBuilder() { + callHTTP = new CallHTTP(); + callHTTP.setWith(new HTTPArguments()); + callHTTP.getWith().setOutput(HTTPArguments.HTTPOutput.CONTENT); + super.setTask(this.callHTTP); + } + + @Override + protected CallHTTPTaskBuilder self() { + return this; + } + + public CallHTTPTaskBuilder method(String method) { + this.callHTTP.getWith().setMethod(method); + return this; + } + + public CallHTTPTaskBuilder endpoint(URI endpoint) { + this.callHTTP + .getWith() + .setEndpoint(new Endpoint().withUriTemplate(new UriTemplate().withLiteralUri(endpoint))); + return this; + } + + public CallHTTPTaskBuilder endpoint(String expr) { + this.callHTTP.getWith().setEndpoint(new Endpoint().withRuntimeExpression(expr)); + return this; + } + + // TODO: add endpoint configuration to support authentication + + public CallHTTPTaskBuilder headers(String expr) { + this.callHTTP.getWith().setHeaders(new Headers().withRuntimeExpression(expr)); + return this; + } + + public CallHTTPTaskBuilder headers(Consumer consumer) { + HTTPHeadersBuilder hb = new HTTPHeadersBuilder(); + consumer.accept(hb); + callHTTP.getWith().setHeaders(hb.build()); + return this; + } + + public CallHTTPTaskBuilder headers(Map headers) { + HTTPHeadersBuilder hb = new HTTPHeadersBuilder(); + hb.headers(headers); + callHTTP.getWith().setHeaders(hb.build()); + return this; + } + + public CallHTTPTaskBuilder body(Object body) { + this.callHTTP.getWith().setBody(body); + return this; + } + + public CallHTTPTaskBuilder query(String expr) { + this.callHTTP.getWith().setQuery(new Query().withRuntimeExpression(expr)); + return this; + } + + public CallHTTPTaskBuilder query(Consumer consumer) { + HTTPQueryBuilder queryBuilder = new HTTPQueryBuilder(); + consumer.accept(queryBuilder); + callHTTP.getWith().setQuery(queryBuilder.build()); + return this; + } + + public CallHTTPTaskBuilder query(Map query) { + HTTPQueryBuilder httpQueryBuilder = new HTTPQueryBuilder(); + httpQueryBuilder.queries(query); + callHTTP.getWith().setQuery(httpQueryBuilder.build()); + return this; + } + + public CallHTTPTaskBuilder redirect(boolean redirect) { + callHTTP.getWith().setRedirect(redirect); + return this; + } + + public CallHTTPTaskBuilder output(HTTPArguments.HTTPOutput output) { + callHTTP.getWith().setOutput(output); + return this; + } + + public CallHTTP build() { + return callHTTP; + } + + public static class HTTPQueryBuilder { + private final HTTPQuery httpQuery = new HTTPQuery(); + + public HTTPQueryBuilder query(String name, String value) { + httpQuery.setAdditionalProperty(name, value); + return this; + } + + public HTTPQueryBuilder queries(Map headers) { + headers.forEach(httpQuery::setAdditionalProperty); + return this; + } + + public Query build() { + return new Query().withHTTPQuery(httpQuery); + } + } + + public static class HTTPHeadersBuilder { + private final HTTPHeaders httpHeaders = new HTTPHeaders(); + + public HTTPHeadersBuilder header(String name, String value) { + httpHeaders.setAdditionalProperty(name, value); + return this; + } + + public HTTPHeadersBuilder headers(Map headers) { + headers.forEach(httpHeaders::setAdditionalProperty); + return this; + } + + public Headers build() { + return new Headers().withHTTPHeaders(httpHeaders); + } + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DigestAuthenticationPolicyBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DigestAuthenticationPolicyBuilder.java new file mode 100644 index 00000000..8405a48b --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DigestAuthenticationPolicyBuilder.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.standard; + +import io.serverlessworkflow.api.types.DigestAuthenticationPolicy; +import io.serverlessworkflow.api.types.DigestAuthenticationPolicyConfiguration; +import io.serverlessworkflow.api.types.DigestAuthenticationProperties; + +public final class DigestAuthenticationPolicyBuilder { + private final DigestAuthenticationProperties digestAuthenticationProperties; + + DigestAuthenticationPolicyBuilder() { + this.digestAuthenticationProperties = new DigestAuthenticationProperties(); + } + + public DigestAuthenticationPolicyBuilder username(String username) { + this.digestAuthenticationProperties.setUsername(username); + return this; + } + + public DigestAuthenticationPolicyBuilder password(String password) { + this.digestAuthenticationProperties.setPassword(password); + return this; + } + + public DigestAuthenticationPolicy build() { + final DigestAuthenticationPolicyConfiguration configuration = + new DigestAuthenticationPolicyConfiguration(); + configuration.setDigestAuthenticationProperties(digestAuthenticationProperties); + return new DigestAuthenticationPolicy(configuration); + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DoTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DoTaskBuilder.java new file mode 100644 index 00000000..3de5cfe7 --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DoTaskBuilder.java @@ -0,0 +1,156 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.CallTask; +import io.serverlessworkflow.api.types.DoTask; +import io.serverlessworkflow.api.types.Task; +import io.serverlessworkflow.api.types.TaskItem; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; + +public class DoTaskBuilder extends TaskBaseBuilder { + + private final DoTask doTask; + private final List list; + + DoTaskBuilder() { + this.doTask = new DoTask(); + this.list = new ArrayList<>(); + this.setTask(doTask); + } + + @Override + protected DoTaskBuilder self() { + return this; + } + + public DoTaskBuilder set(String name, Consumer itemsConfigurer) { + final SetTaskBuilder setBuilder = new SetTaskBuilder(); + itemsConfigurer.accept(setBuilder); + this.list.add(new TaskItem(name, new Task().withSetTask(setBuilder.build()))); + return this; + } + + public DoTaskBuilder set(Consumer itemsConfigurer) { + return this.set(UUID.randomUUID().toString(), itemsConfigurer); + } + + public DoTaskBuilder set(String name, final String expr) { + return this.set(name, s -> s.expr(expr)); + } + + public DoTaskBuilder set(final String expr) { + return this.set(UUID.randomUUID().toString(), s -> s.expr(expr)); + } + + public DoTaskBuilder forEach(String name, Consumer itemsConfigurer) { + final ForTaskBuilder forBuilder = new ForTaskBuilder(); + itemsConfigurer.accept(forBuilder); + this.list.add(new TaskItem(name, new Task().withForTask(forBuilder.build()))); + return this; + } + + public DoTaskBuilder forEach(Consumer itemsConfigurer) { + return this.forEach(UUID.randomUUID().toString(), itemsConfigurer); + } + + public DoTaskBuilder switchTask(String name, Consumer itemsConfigurer) { + final SwitchTaskBuilder switchBuilder = new SwitchTaskBuilder(); + itemsConfigurer.accept(switchBuilder); + this.list.add(new TaskItem(name, new Task().withSwitchTask(switchBuilder.build()))); + return this; + } + + public DoTaskBuilder switchTask(Consumer itemsConfigurer) { + return this.switchTask(UUID.randomUUID().toString(), itemsConfigurer); + } + + public DoTaskBuilder raise(String name, Consumer itemsConfigurer) { + final RaiseTaskBuilder raiseBuilder = new RaiseTaskBuilder(); + itemsConfigurer.accept(raiseBuilder); + this.list.add(new TaskItem(name, new Task().withRaiseTask(raiseBuilder.build()))); + return this; + } + + public DoTaskBuilder raise(Consumer itemsConfigurer) { + return this.raise(UUID.randomUUID().toString(), itemsConfigurer); + } + + public DoTaskBuilder fork(String name, Consumer itemsConfigurer) { + final ForkTaskBuilder forkBuilder = new ForkTaskBuilder(); + itemsConfigurer.accept(forkBuilder); + this.list.add(new TaskItem(name, new Task().withForkTask(forkBuilder.build()))); + return this; + } + + public DoTaskBuilder fork(Consumer itemsConfigurer) { + return this.fork(UUID.randomUUID().toString(), itemsConfigurer); + } + + public DoTaskBuilder listen(String name, Consumer itemsConfigurer) { + final ListenTaskBuilder listenBuilder = new ListenTaskBuilder(); + itemsConfigurer.accept(listenBuilder); + this.list.add(new TaskItem(name, new Task().withListenTask(listenBuilder.build()))); + return this; + } + + public DoTaskBuilder listen(Consumer itemsConfigurer) { + return this.listen(UUID.randomUUID().toString(), itemsConfigurer); + } + + public DoTaskBuilder emit(String name, Consumer itemsConfigurer) { + final EmitTaskBuilder emitBuilder = new EmitTaskBuilder(); + itemsConfigurer.accept(emitBuilder); + this.list.add(new TaskItem(name, new Task().withEmitTask(emitBuilder.build()))); + return this; + } + + public DoTaskBuilder emit(Consumer itemsConfigurer) { + return this.emit(UUID.randomUUID().toString(), itemsConfigurer); + } + + public DoTaskBuilder tryTask(String name, Consumer itemsConfigurer) { + final TryTaskBuilder tryBuilder = new TryTaskBuilder(); + itemsConfigurer.accept(tryBuilder); + this.list.add(new TaskItem(name, new Task().withTryTask(tryBuilder.build()))); + return this; + } + + public DoTaskBuilder tryTask(Consumer itemsConfigurer) { + return this.tryTask(UUID.randomUUID().toString(), itemsConfigurer); + } + + public DoTaskBuilder callHTTP(String name, Consumer itemsConfigurer) { + final CallHTTPTaskBuilder callHTTPBuilder = new CallHTTPTaskBuilder(); + itemsConfigurer.accept(callHTTPBuilder); + this.list.add( + new TaskItem( + name, new Task().withCallTask(new CallTask().withCallHTTP(callHTTPBuilder.build())))); + return this; + } + + public DoTaskBuilder callHTTP(Consumer itemsConfigurer) { + return this.callHTTP(UUID.randomUUID().toString(), itemsConfigurer); + } + + public DoTask build() { + this.doTask.setDo(this.list); + return this.doTask; + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DocumentBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DocumentBuilder.java new file mode 100644 index 00000000..de6d9ee3 --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DocumentBuilder.java @@ -0,0 +1,108 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.Document; +import io.serverlessworkflow.api.types.WorkflowMetadata; +import io.serverlessworkflow.api.types.WorkflowTags; +import java.util.function.Consumer; + +public class DocumentBuilder { + + private final Document document; + + DocumentBuilder(final Document document) { + this.document = document; + } + + public DocumentBuilder dsl(final String dsl) { + this.document.setDsl(dsl); + return this; + } + + public DocumentBuilder name(final String name) { + this.document.setName(name); + return this; + } + + public DocumentBuilder namespace(final String namespace) { + this.document.setNamespace(namespace); + return this; + } + + public DocumentBuilder version(final String version) { + this.document.setVersion(version); + return this; + } + + public DocumentBuilder title(final String title) { + this.document.setTitle(title); + return this; + } + + public DocumentBuilder summary(final String summary) { + this.document.setSummary(summary); + return this; + } + + public DocumentBuilder tags(Consumer tagsBuilderConsumer) { + final WorkflowTagsBuilder tagsBuilder = new WorkflowTagsBuilder(); + tagsBuilderConsumer.accept(tagsBuilder); + this.document.setTags(tagsBuilder.build()); + return this; + } + + public DocumentBuilder metadata(Consumer metadataBuilderConsumer) { + final WorkflowMetadataBuilder metadataBuilder = new WorkflowMetadataBuilder(); + metadataBuilderConsumer.accept(metadataBuilder); + this.document.setMetadata(metadataBuilder.build()); + return this; + } + + public static final class WorkflowTagsBuilder { + private final WorkflowTags tags; + + WorkflowTagsBuilder() { + this.tags = new WorkflowTags(); + } + + public WorkflowTagsBuilder tag(final String key, final String value) { + this.tags.withAdditionalProperty(key, value); + return this; + } + + public WorkflowTags build() { + return this.tags; + } + } + + public static final class WorkflowMetadataBuilder { + private final WorkflowMetadata workflowMetadata; + + WorkflowMetadataBuilder() { + this.workflowMetadata = new WorkflowMetadata(); + } + + public WorkflowMetadataBuilder metadata(final String key, final Object value) { + this.workflowMetadata.withAdditionalProperty(key, value); + return this; + } + + public WorkflowMetadata build() { + return this.workflowMetadata; + } + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DurationInlineBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DurationInlineBuilder.java new file mode 100644 index 00000000..23730731 --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DurationInlineBuilder.java @@ -0,0 +1,56 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.DurationInline; + +public class DurationInlineBuilder { + + private final DurationInline duration; + + DurationInlineBuilder() { + duration = new DurationInline(); + } + + public DurationInlineBuilder days(int days) { + duration.setDays(days); + return this; + } + + public DurationInlineBuilder hours(int hours) { + duration.setHours(hours); + return this; + } + + public DurationInlineBuilder minutes(int minutes) { + duration.setMinutes(minutes); + return this; + } + + public DurationInlineBuilder seconds(int seconds) { + duration.setSeconds(seconds); + return this; + } + + public DurationInlineBuilder milliseconds(int milliseconds) { + duration.setMilliseconds(milliseconds); + return this; + } + + public DurationInline build() { + return duration; + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/EmitTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/EmitTaskBuilder.java new file mode 100644 index 00000000..77ea9d98 --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/EmitTaskBuilder.java @@ -0,0 +1,49 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.EmitEventDefinition; +import io.serverlessworkflow.api.types.EmitTask; +import io.serverlessworkflow.api.types.EmitTaskConfiguration; +import java.util.function.Consumer; + +public class EmitTaskBuilder extends TaskBaseBuilder { + + private final EmitTask emitTask; + + EmitTaskBuilder() { + this.emitTask = new EmitTask(); + super.setTask(emitTask); + } + + public EmitTaskBuilder event(Consumer consumer) { + final EventPropertiesBuilder eventPropertiesBuilder = new EventPropertiesBuilder(); + consumer.accept(eventPropertiesBuilder); + this.emitTask.setEmit( + new EmitTaskConfiguration() + .withEvent(new EmitEventDefinition().withWith(eventPropertiesBuilder.build()))); + return this; + } + + public EmitTask build() { + return emitTask; + } + + @Override + protected EmitTaskBuilder self() { + return this; + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/EventPropertiesBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/EventPropertiesBuilder.java new file mode 100644 index 00000000..86863804 --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/EventPropertiesBuilder.java @@ -0,0 +1,78 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.EventData; +import io.serverlessworkflow.api.types.EventProperties; +import io.serverlessworkflow.api.types.EventSource; +import io.serverlessworkflow.api.types.EventTime; +import io.serverlessworkflow.api.types.UriTemplate; +import java.net.URI; +import java.util.Date; + +public final class EventPropertiesBuilder { + private final EventProperties properties = new EventProperties(); + + public EventPropertiesBuilder id(String id) { + properties.setId(id); + return this; + } + + public EventPropertiesBuilder source(String expr) { + + properties.setSource(new EventSource().withRuntimeExpression(expr)); + return this; + } + + public EventPropertiesBuilder source(URI uri) { + properties.setSource(new EventSource().withUriTemplate(new UriTemplate().withLiteralUri(uri))); + return this; + } + + public EventPropertiesBuilder type(String type) { + properties.setType(type); + return this; + } + + public EventPropertiesBuilder time(Date time) { + properties.setTime(new EventTime().withLiteralTime(time)); + return this; + } + + public EventPropertiesBuilder subject(String subject) { + properties.setSubject(subject); + return this; + } + + public EventPropertiesBuilder dataContentType(String ct) { + properties.setDatacontenttype(ct); + return this; + } + + public EventPropertiesBuilder data(String expr) { + properties.setData(new EventData().withRuntimeExpression(expr)); + return this; + } + + public EventPropertiesBuilder data(Object obj) { + properties.setData(new EventData().withObject(obj)); + return this; + } + + public EventProperties build() { + return properties; + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForTaskBuilder.java new file mode 100644 index 00000000..e755eebd --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForTaskBuilder.java @@ -0,0 +1,69 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.ForTask; +import io.serverlessworkflow.api.types.ForTaskConfiguration; +import java.util.function.Consumer; + +public class ForTaskBuilder extends TaskBaseBuilder { + + private final ForTask forTask; + private final ForTaskConfiguration forTaskConfiguration; + + ForTaskBuilder() { + super(); + forTask = new ForTask(); + forTaskConfiguration = new ForTaskConfiguration(); + super.setTask(forTask); + } + + protected ForTaskBuilder self() { + return this; + } + + public ForTaskBuilder each(String each) { + forTaskConfiguration.setEach(each); + return this; + } + + public ForTaskBuilder in(String in) { + this.forTaskConfiguration.setIn(in); + return this; + } + + public ForTaskBuilder at(String at) { + this.forTaskConfiguration.setAt(at); + return this; + } + + public ForTaskBuilder whileCondition(final String expression) { + this.forTask.setWhile(expression); + return this; + } + + public ForTaskBuilder doTasks(Consumer doBuilderConsumer) { + final DoTaskBuilder doTaskBuilder = new DoTaskBuilder(); + doBuilderConsumer.accept(doTaskBuilder); + this.forTask.setDo(doTaskBuilder.build().getDo()); + return this; + } + + public ForTask build() { + this.forTask.setFor(this.forTaskConfiguration); + return this.forTask; + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForkTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForkTaskBuilder.java new file mode 100644 index 00000000..59754ed8 --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForkTaskBuilder.java @@ -0,0 +1,53 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.ForkTask; +import io.serverlessworkflow.api.types.ForkTaskConfiguration; +import java.util.function.Consumer; + +public class ForkTaskBuilder extends TaskBaseBuilder { + + private final ForkTask forkTask; + private final ForkTaskConfiguration forkTaskConfiguration; + + @Override + protected ForkTaskBuilder self() { + return this; + } + + ForkTaskBuilder() { + this.forkTask = new ForkTask(); + this.forkTaskConfiguration = new ForkTaskConfiguration(); + super.setTask(this.forkTask); + } + + public ForkTaskBuilder compete(final Boolean compete) { + this.forkTaskConfiguration.setCompete(compete); + return this; + } + + public ForkTaskBuilder branches(Consumer branchesConsumer) { + final DoTaskBuilder doTaskBuilder = new DoTaskBuilder(); + branchesConsumer.accept(doTaskBuilder); + this.forkTaskConfiguration.setBranches(doTaskBuilder.build().getDo()); + return this; + } + + public ForkTask build() { + return this.forkTask.withFork(this.forkTaskConfiguration); + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/InputBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/InputBuilder.java new file mode 100644 index 00000000..81ebfcc0 --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/InputBuilder.java @@ -0,0 +1,67 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.Endpoint; +import io.serverlessworkflow.api.types.ExternalResource; +import io.serverlessworkflow.api.types.Input; +import io.serverlessworkflow.api.types.InputFrom; +import io.serverlessworkflow.api.types.SchemaExternal; +import io.serverlessworkflow.api.types.SchemaInline; +import io.serverlessworkflow.api.types.SchemaUnion; + +public class InputBuilder { + + private final Input input; + + InputBuilder() { + this.input = new Input(); + this.input.setFrom(new InputFrom()); + this.input.setSchema(new SchemaUnion()); + } + + public InputBuilder from(String expr) { + this.input.getFrom().setString(expr); + return this; + } + + public InputBuilder from(Object object) { + this.input.getFrom().setObject(object); + return this; + } + + public InputBuilder schema(Object schema) { + this.input.getSchema().setSchemaInline(new SchemaInline(schema)); + return this; + } + + public InputBuilder schema(String schema) { + this.input + .getSchema() + .setSchemaExternal( + new SchemaExternal() + .withResource( + new ExternalResource() + .withEndpoint( + new Endpoint() + .withUriTemplate(UriTemplateBuilder.newUriTemplate(schema))))); + return this; + } + + public Input build() { + return this.input; + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ListenTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ListenTaskBuilder.java new file mode 100644 index 00000000..16791cab --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ListenTaskBuilder.java @@ -0,0 +1,149 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.AllEventConsumptionStrategy; +import io.serverlessworkflow.api.types.AnyEventConsumptionStrategy; +import io.serverlessworkflow.api.types.CorrelateProperty; +import io.serverlessworkflow.api.types.EventFilter; +import io.serverlessworkflow.api.types.EventFilterCorrelate; +import io.serverlessworkflow.api.types.ListenTask; +import io.serverlessworkflow.api.types.ListenTaskConfiguration; +import io.serverlessworkflow.api.types.ListenTo; +import io.serverlessworkflow.api.types.OneEventConsumptionStrategy; +import java.util.List; +import java.util.function.Consumer; + +/** + * Fluent builder for a "listen" task in a Serverless Workflow. Enforces exactly one consumption + * strategy: one, all, or any. + */ +public class ListenTaskBuilder extends TaskBaseBuilder { + + private final ListenTask listenTask; + private final ListenTaskConfiguration config; + private boolean oneSet, allSet, anySet; + + public ListenTaskBuilder() { + super(); + this.listenTask = new ListenTask(); + this.config = new ListenTaskConfiguration(); + this.config.setTo(new ListenTo()); + this.listenTask.setListen(config); + super.setTask(listenTask); + } + + @Override + protected ListenTaskBuilder self() { + return this; + } + + /** Consume exactly one matching event. */ + public ListenTaskBuilder one(Consumer c) { + ensureNoneSet(); + oneSet = true; + EventFilterBuilder fb = new EventFilterBuilder(); + c.accept(fb); + OneEventConsumptionStrategy strat = new OneEventConsumptionStrategy(); + strat.setOne(fb.build()); + config.getTo().withOneEventConsumptionStrategy(strat); + return this; + } + + /** Consume events only when *all* filters match. */ + public ListenTaskBuilder all(Consumer c) { + ensureNoneSet(); + allSet = true; + EventFilterBuilder fb = new EventFilterBuilder(); + c.accept(fb); + AllEventConsumptionStrategy strat = new AllEventConsumptionStrategy(); + strat.setAll(List.of(fb.build())); + config.getTo().withAllEventConsumptionStrategy(strat); + return this; + } + + /** Consume events when *any* filter matches. */ + public ListenTaskBuilder any(Consumer c) { + ensureNoneSet(); + anySet = true; + EventFilterBuilder fb = new EventFilterBuilder(); + c.accept(fb); + AnyEventConsumptionStrategy strat = new AnyEventConsumptionStrategy(); + strat.setAny(List.of(fb.build())); + config.getTo().withAnyEventConsumptionStrategy(strat); + return this; + } + + private void ensureNoneSet() { + if (oneSet || allSet || anySet) { + throw new IllegalStateException("Only one consumption strategy can be configured"); + } + } + + /** Validate and return the built ListenTask. */ + public ListenTask build() { + if (!(oneSet || allSet || anySet)) { + throw new IllegalStateException( + "A consumption strategy (one, all, or any) must be configured"); + } + return listenTask; + } + + /** Builder for event filters used in consumption strategies. */ + public static final class EventFilterBuilder { + private final EventFilter filter = new EventFilter(); + private final EventFilterCorrelate correlate = new EventFilterCorrelate(); + + /** Predicate to match event properties. */ + public EventFilterBuilder with(Consumer c) { + EventPropertiesBuilder pb = new EventPropertiesBuilder(); + c.accept(pb); + filter.setWith(pb.build()); + return this; + } + + /** Correlation property for the filter. */ + public EventFilterBuilder correlate(String key, Consumer c) { + CorrelatePropertyBuilder cpb = new CorrelatePropertyBuilder(); + c.accept(cpb); + correlate.withAdditionalProperty(key, cpb.build()); + return this; + } + + public EventFilter build() { + filter.setCorrelate(correlate); + return filter; + } + } + + public static final class CorrelatePropertyBuilder { + private final CorrelateProperty prop = new CorrelateProperty(); + + public CorrelatePropertyBuilder from(String expr) { + prop.setFrom(expr); + return this; + } + + public CorrelatePropertyBuilder expect(String val) { + prop.setExpect(val); + return this; + } + + public CorrelateProperty build() { + return prop; + } + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OAuth2AuthenticationPolicyBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OAuth2AuthenticationPolicyBuilder.java new file mode 100644 index 00000000..7eac31b4 --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OAuth2AuthenticationPolicyBuilder.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.standard; + +import io.serverlessworkflow.api.types.OAuth2AuthenticationPolicy; +import io.serverlessworkflow.api.types.OAuth2AuthenticationPolicyConfiguration; +import io.serverlessworkflow.api.types.OAuth2ConnectAuthenticationProperties; +import io.serverlessworkflow.api.types.Oauth2; +import java.util.function.Consumer; + +public final class OAuth2AuthenticationPolicyBuilder + extends OIDCBuilder { + + private final OAuth2ConnectAuthenticationProperties properties; + + OAuth2AuthenticationPolicyBuilder() { + super(); + this.properties = new OAuth2ConnectAuthenticationProperties(); + } + + public OAuth2AuthenticationPolicyBuilder endpoints( + Consumer endpointsConsumer) { + final OAuth2AuthenticationPropertiesEndpointsBuilder builder = + new OAuth2AuthenticationPropertiesEndpointsBuilder(); + endpointsConsumer.accept(builder); + this.properties.setEndpoints(builder.build()); + return this; + } + + public OAuth2AuthenticationPolicy build() { + final OAuth2AuthenticationPolicyConfiguration configuration = + new OAuth2AuthenticationPolicyConfiguration(); + configuration.setOAuth2AutenthicationData(this.getAuthenticationData()); + configuration.setOAuth2ConnectAuthenticationProperties(this.properties); + + final Oauth2 oauth2 = new Oauth2(); + oauth2.setOAuth2ConnectAuthenticationProperties(configuration); + + final OAuth2AuthenticationPolicy policy = new OAuth2AuthenticationPolicy(); + policy.setOauth2(oauth2); + + return policy; + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OIDCBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OIDCBuilder.java new file mode 100644 index 00000000..e757de8f --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OIDCBuilder.java @@ -0,0 +1,190 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.AuthenticationPolicy; +import io.serverlessworkflow.api.types.OAuth2AutenthicationData; +import io.serverlessworkflow.api.types.OAuth2AutenthicationDataClient; +import io.serverlessworkflow.api.types.OAuth2AuthenticationPropertiesEndpoints; +import io.serverlessworkflow.api.types.OAuth2TokenDefinition; +import io.serverlessworkflow.api.types.OAuth2TokenRequest; +import java.util.List; +import java.util.function.Consumer; + +public abstract class OIDCBuilder { + private final OAuth2AutenthicationData authenticationData; + + OIDCBuilder() { + this.authenticationData = new OAuth2AutenthicationData(); + this.authenticationData.setRequest(new OAuth2TokenRequest()); + } + + public OIDCBuilder authority(String authority) { + this.authenticationData.setAuthority(UriTemplateBuilder.newUriTemplate(authority)); + return this; + } + + public OIDCBuilder grant(OAuth2AutenthicationData.OAuth2AutenthicationDataGrant grant) { + this.authenticationData.setGrant(grant); + return this; + } + + public OIDCBuilder issuers(String... issuers) { + if (issuers != null) { + this.authenticationData.setIssuers(List.of(issuers)); + } + return this; + } + + public OIDCBuilder scopes(String... scopes) { + if (scopes != null) { + this.authenticationData.setScopes(List.of(scopes)); + } + return this; + } + + public OIDCBuilder audiences(String... audiences) { + if (audiences != null) { + this.authenticationData.setAudiences(List.of(audiences)); + } + return this; + } + + public OIDCBuilder username(String username) { + this.authenticationData.setUsername(username); + return this; + } + + public OIDCBuilder password(String password) { + this.authenticationData.setPassword(password); + return this; + } + + public OIDCBuilder requestEncoding(OAuth2TokenRequest.Oauth2TokenRequestEncoding encoding) { + this.authenticationData.setRequest(new OAuth2TokenRequest().withEncoding(encoding)); + return this; + } + + public OIDCBuilder subject(Consumer subjectConsumer) { + final OAuth2TokenDefinitionBuilder builder = new OAuth2TokenDefinitionBuilder(); + subjectConsumer.accept(builder); + this.authenticationData.setSubject(builder.build()); + return this; + } + + public OIDCBuilder actor(Consumer actorConsumer) { + final OAuth2TokenDefinitionBuilder builder = new OAuth2TokenDefinitionBuilder(); + actorConsumer.accept(builder); + this.authenticationData.setActor(builder.build()); + return this; + } + + public OIDCBuilder client(Consumer clientConsumer) { + final OAuth2AuthenticationDataClientBuilder builder = + new OAuth2AuthenticationDataClientBuilder(); + clientConsumer.accept(builder); + this.authenticationData.setClient(builder.build()); + return this; + } + + protected final OAuth2AutenthicationData getAuthenticationData() { + return authenticationData; + } + + public abstract T build(); + + public static final class OAuth2TokenDefinitionBuilder { + private final OAuth2TokenDefinition oauth2TokenDefinition; + + OAuth2TokenDefinitionBuilder() { + this.oauth2TokenDefinition = new OAuth2TokenDefinition(); + } + + public OAuth2TokenDefinitionBuilder token(String token) { + this.oauth2TokenDefinition.setToken(token); + return this; + } + + public OAuth2TokenDefinitionBuilder type(String type) { + this.oauth2TokenDefinition.setType(type); + return this; + } + + public OAuth2TokenDefinition build() { + return this.oauth2TokenDefinition; + } + } + + public static final class OAuth2AuthenticationDataClientBuilder { + private final OAuth2AutenthicationDataClient client; + + OAuth2AuthenticationDataClientBuilder() { + this.client = new OAuth2AutenthicationDataClient(); + } + + public OAuth2AuthenticationDataClientBuilder id(String id) { + this.client.setId(id); + return this; + } + + public OAuth2AuthenticationDataClientBuilder secret(String secret) { + this.client.setSecret(secret); + return this; + } + + public OAuth2AuthenticationDataClientBuilder assertion(String assertion) { + this.client.setAssertion(assertion); + return this; + } + + public OAuth2AuthenticationDataClientBuilder authentication( + OAuth2AutenthicationDataClient.ClientAuthentication authentication) { + this.client.setAuthentication(authentication); + return this; + } + + public OAuth2AutenthicationDataClient build() { + return this.client; + } + } + + public static final class OAuth2AuthenticationPropertiesEndpointsBuilder { + private final OAuth2AuthenticationPropertiesEndpoints endpoints; + + OAuth2AuthenticationPropertiesEndpointsBuilder() { + endpoints = new OAuth2AuthenticationPropertiesEndpoints(); + } + + public OAuth2AuthenticationPropertiesEndpointsBuilder token(String token) { + this.endpoints.setToken(token); + return this; + } + + public OAuth2AuthenticationPropertiesEndpointsBuilder revocation(String revocation) { + this.endpoints.setRevocation(revocation); + return this; + } + + public OAuth2AuthenticationPropertiesEndpointsBuilder introspection(String introspection) { + this.endpoints.setIntrospection(introspection); + return this; + } + + public OAuth2AuthenticationPropertiesEndpoints build() { + return this.endpoints; + } + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OpenIdConnectAuthenticationPolicyBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OpenIdConnectAuthenticationPolicyBuilder.java new file mode 100644 index 00000000..98b9152b --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OpenIdConnectAuthenticationPolicyBuilder.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.standard; + +import io.serverlessworkflow.api.types.OpenIdConnectAuthenticationPolicy; +import io.serverlessworkflow.api.types.OpenIdConnectAuthenticationPolicyConfiguration; + +public final class OpenIdConnectAuthenticationPolicyBuilder + extends OIDCBuilder { + + OpenIdConnectAuthenticationPolicyBuilder() { + super(); + } + + public OpenIdConnectAuthenticationPolicy build() { + final OpenIdConnectAuthenticationPolicyConfiguration configuration = + new OpenIdConnectAuthenticationPolicyConfiguration(); + configuration.setOpenIdConnectAuthenticationProperties(this.getAuthenticationData()); + final OpenIdConnectAuthenticationPolicy policy = new OpenIdConnectAuthenticationPolicy(); + policy.setOidc(configuration); + return policy; + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OutputBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OutputBuilder.java new file mode 100644 index 00000000..797f8872 --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OutputBuilder.java @@ -0,0 +1,67 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.Endpoint; +import io.serverlessworkflow.api.types.ExternalResource; +import io.serverlessworkflow.api.types.Output; +import io.serverlessworkflow.api.types.OutputAs; +import io.serverlessworkflow.api.types.SchemaExternal; +import io.serverlessworkflow.api.types.SchemaInline; +import io.serverlessworkflow.api.types.SchemaUnion; + +public class OutputBuilder { + + private final Output output; + + OutputBuilder() { + this.output = new Output(); + this.output.setAs(new OutputAs()); + this.output.setSchema(new SchemaUnion()); + } + + public OutputBuilder as(final String expr) { + this.output.getAs().setString(expr); + return this; + } + + public OutputBuilder as(final Object object) { + this.output.getAs().setObject(object); + return this; + } + + public OutputBuilder schema(final String schema) { + this.output + .getSchema() + .setSchemaExternal( + new SchemaExternal() + .withResource( + new ExternalResource() + .withEndpoint( + new Endpoint() + .withUriTemplate(UriTemplateBuilder.newUriTemplate(schema))))); + return this; + } + + public OutputBuilder schema(final Object schema) { + this.output.getSchema().setSchemaInline(new SchemaInline(schema)); + return this; + } + + public Output build() { + return this.output; + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/RaiseTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/RaiseTaskBuilder.java new file mode 100644 index 00000000..f2d370e9 --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/RaiseTaskBuilder.java @@ -0,0 +1,101 @@ +/* + * 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.standard; + +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 java.net.URI; +import java.util.function.Consumer; + +public class RaiseTaskBuilder extends TaskBaseBuilder { + + private final RaiseTask raiseTask; + + RaiseTaskBuilder() { + this.raiseTask = new RaiseTask(); + setTask(raiseTask); + } + + @Override + protected RaiseTaskBuilder self() { + return this; + } + + public RaiseTaskBuilder error(Consumer consumer) { + final RaiseTaskErrorBuilder raiseTaskErrorBuilder = new RaiseTaskErrorBuilder(); + consumer.accept(raiseTaskErrorBuilder); + this.raiseTask.setRaise(new RaiseTaskConfiguration().withError(raiseTaskErrorBuilder.build())); + return this; + } + + // TODO: validation, one or the other + + public RaiseTaskBuilder 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; + } + + // TODO: change signature to Expression interface since literal and expressions are String + + 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/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SetTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SetTaskBuilder.java new file mode 100644 index 00000000..6c43ddfc --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SetTaskBuilder.java @@ -0,0 +1,55 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.Set; +import io.serverlessworkflow.api.types.SetTask; +import io.serverlessworkflow.api.types.SetTaskConfiguration; + +public class SetTaskBuilder extends TaskBaseBuilder { + + private final SetTask setTask; + private final SetTaskConfiguration setTaskConfiguration; + + SetTaskBuilder() { + this.setTask = new SetTask(); + this.setTaskConfiguration = new SetTaskConfiguration(); + this.setTask(setTask); + } + + @Override + protected SetTaskBuilder self() { + return this; + } + + public SetTaskBuilder expr(String expression) { + this.setTask.setSet(new Set().withString(expression)); + return this; + } + + public SetTaskBuilder put(String key, String value) { + setTaskConfiguration.withAdditionalProperty(key, value); + return this; + } + + public SetTask build() { + if (this.setTask.getSet() == null) { + this.setTask.setSet(new Set().withSetTaskConfiguration(setTaskConfiguration)); + } + + return setTask; + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SwitchTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SwitchTaskBuilder.java new file mode 100644 index 00000000..12e027ee --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SwitchTaskBuilder.java @@ -0,0 +1,76 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.SwitchCase; +import io.serverlessworkflow.api.types.SwitchItem; +import io.serverlessworkflow.api.types.SwitchTask; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class SwitchTaskBuilder extends TaskBaseBuilder { + + private final SwitchTask switchTask; + private final List switchItems; + + SwitchTaskBuilder() { + super(); + this.switchTask = new SwitchTask(); + this.switchItems = new ArrayList<>(); + this.setTask(switchTask); + } + + @Override + protected SwitchTaskBuilder self() { + return this; + } + + public SwitchTaskBuilder switchTask( + final String name, Consumer switchCaseConsumer) { + final SwitchCaseBuilder switchCaseBuilder = new SwitchCaseBuilder(); + switchCaseConsumer.accept(switchCaseBuilder); + this.switchItems.add(new SwitchItem(name, switchCaseBuilder.build())); + return this; + } + + public SwitchTask build() { + this.switchTask.setSwitch(this.switchItems); + return this.switchTask; + } + + public static final class SwitchCaseBuilder { + private final SwitchCase switchCase; + + SwitchCaseBuilder() { + this.switchCase = new SwitchCase(); + } + + public SwitchCaseBuilder when(String when) { + this.switchCase.setWhen(when); + return this; + } + + public SwitchCaseBuilder then(String then) { + this.switchCase.setWhen(then); + return this; + } + + public SwitchCase build() { + return this.switchCase; + } + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskBaseBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskBaseBuilder.java new file mode 100644 index 00000000..31d30b3f --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskBaseBuilder.java @@ -0,0 +1,120 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.Endpoint; +import io.serverlessworkflow.api.types.Export; +import io.serverlessworkflow.api.types.ExportAs; +import io.serverlessworkflow.api.types.ExternalResource; +import io.serverlessworkflow.api.types.FlowDirective; +import io.serverlessworkflow.api.types.FlowDirectiveEnum; +import io.serverlessworkflow.api.types.SchemaExternal; +import io.serverlessworkflow.api.types.SchemaInline; +import io.serverlessworkflow.api.types.SchemaUnion; +import io.serverlessworkflow.api.types.TaskBase; +import java.util.function.Consumer; + +public abstract class TaskBaseBuilder> { + protected abstract T self(); + + private TaskBase task; + + protected TaskBaseBuilder() {} + + protected void setTask(TaskBase task) { + this.task = task; + } + + public T _if(String id) { + this.task.setIf(id); + return self(); + } + + public T then(FlowDirectiveEnum then) { + this.task.setThen(new FlowDirective().withFlowDirectiveEnum(then)); + return self(); + } + + public T exportAs(Object exportAs) { + this.task.setExport(new ExportBuilder().as(exportAs).build()); + return self(); + } + + public T export(Consumer exportConsumer) { + final ExportBuilder exportBuilder = new ExportBuilder(); + exportConsumer.accept(exportBuilder); + this.task.setExport(exportBuilder.build()); + return self(); + } + + public T input(Consumer inputConsumer) { + final InputBuilder inputBuilder = new InputBuilder(); + inputConsumer.accept(inputBuilder); + this.task.setInput(inputBuilder.build()); + return self(); + } + + public T output(Consumer outputConsumer) { + final OutputBuilder outputBuilder = new OutputBuilder(); + outputConsumer.accept(outputBuilder); + this.task.setOutput(outputBuilder.build()); + return self(); + } + + // TODO: add timeout, metadata + + public static final class ExportBuilder { + private final Export export; + + public ExportBuilder() { + this.export = new Export(); + this.export.setAs(new ExportAs()); + this.export.setSchema(new SchemaUnion()); + } + + public ExportBuilder as(Object as) { + this.export.getAs().withObject(as); + return this; + } + + public ExportBuilder as(String as) { + this.export.getAs().withString(as); + return this; + } + + public ExportBuilder schema(String schema) { + this.export + .getSchema() + .setSchemaExternal( + new SchemaExternal() + .withResource( + new ExternalResource() + .withEndpoint( + new Endpoint() + .withUriTemplate(UriTemplateBuilder.newUriTemplate(schema))))); + return this; + } + + public ExportBuilder schema(Object schema) { + this.export.getSchema().setSchemaInline(new SchemaInline(schema)); + return this; + } + + public Export build() { + return this.export; + } + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TryTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TryTaskBuilder.java new file mode 100644 index 00000000..2e022503 --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TryTaskBuilder.java @@ -0,0 +1,338 @@ +/* + * 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.standard; + +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.TimeoutAfter; +import io.serverlessworkflow.api.types.TryTask; +import io.serverlessworkflow.api.types.TryTaskCatch; +import java.util.function.Consumer; + +public class TryTaskBuilder extends TaskBaseBuilder { + + private final TryTask tryTask; + + TryTaskBuilder() { + this.tryTask = new TryTask(); + } + + @Override + protected TryTaskBuilder self() { + return this; + } + + public TryTaskBuilder tryHandler(Consumer consumer) { + final DoTaskBuilder doTaskBuilder = new DoTaskBuilder(); + consumer.accept(doTaskBuilder); + this.tryTask.setTry(doTaskBuilder.build().getDo()); + return this; + } + + public TryTaskBuilder 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 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 RetryPolicyJitter to(Consumer consumer) { + final DurationInlineBuilder durationInlineBuilder = new DurationInlineBuilder(); + consumer.accept(durationInlineBuilder); + this.retryPolicyJitter.setTo( + new TimeoutAfter().withDurationInline(durationInlineBuilder.build())); + return retryPolicyJitter; + } + + public RetryPolicyJitter to(String expression) { + this.retryPolicyJitter.setTo(new TimeoutAfter().withDurationExpression(expression)); + return retryPolicyJitter; + } + + public RetryPolicyJitter from(Consumer consumer) { + final DurationInlineBuilder durationInlineBuilder = new DurationInlineBuilder(); + consumer.accept(durationInlineBuilder); + this.retryPolicyJitter.setFrom( + new TimeoutAfter().withDurationInline(durationInlineBuilder.build())); + return retryPolicyJitter; + } + + public RetryPolicyJitter from(String expression) { + this.retryPolicyJitter.setFrom(new TimeoutAfter().withDurationExpression(expression)); + return retryPolicyJitter; + } + + 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; + } + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UriTemplateBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UriTemplateBuilder.java new file mode 100644 index 00000000..da95f6b7 --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UriTemplateBuilder.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.standard; + +import io.serverlessworkflow.api.types.UriTemplate; +import java.net.URI; +import java.net.URISyntaxException; + +public final class UriTemplateBuilder { + + public static UriTemplate newUriTemplate(String uri) { + try { + return new UriTemplate().withLiteralUri(new URI(uri)); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UseAuthenticationsBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UseAuthenticationsBuilder.java new file mode 100644 index 00000000..f24fec2f --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UseAuthenticationsBuilder.java @@ -0,0 +1,40 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.UseAuthentications; +import java.util.function.Consumer; + +public class UseAuthenticationsBuilder { + + private final UseAuthentications authentication; + + UseAuthenticationsBuilder() { + this.authentication = new UseAuthentications(); + } + + public UseAuthenticationsBuilder authentication( + String name, Consumer authenticationConsumer) { + final AuthenticationPolicyUnionBuilder builder = new AuthenticationPolicyUnionBuilder(); + authenticationConsumer.accept(builder); + this.authentication.setAdditionalProperty(name, builder.build()); + return this; + } + + public UseAuthentications build() { + return authentication; + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UseBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UseBuilder.java new file mode 100644 index 00000000..5b4dd836 --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UseBuilder.java @@ -0,0 +1,49 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.Use; +import java.util.List; +import java.util.function.Consumer; + +public class UseBuilder { + + private final Use use; + + UseBuilder() { + this.use = new Use(); + } + + public UseBuilder secrets(final String... secrets) { + if (secrets != null) { + this.use.setSecrets(List.of(secrets)); + } + return this; + } + + public UseBuilder authentications(Consumer authenticationsConsumer) { + final UseAuthenticationsBuilder builder = new UseAuthenticationsBuilder(); + authenticationsConsumer.accept(builder); + this.use.setAuthentications(builder.build()); + return this; + } + + // TODO: implement the remaining `use` attributes + + public Use build() { + return use; + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilder.java new file mode 100644 index 00000000..742b5761 --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilder.java @@ -0,0 +1,96 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.Document; +import io.serverlessworkflow.api.types.Workflow; +import java.util.UUID; +import java.util.function.Consumer; + +public class WorkflowBuilder { + + private static final String DSL = "1.0.0"; + private static final String DEFAULT_VERSION = "0.0.1"; + private static final String DEFAULT_NAMESPACE = "org.acme"; + + private final Workflow workflow; + private final Document document; + + private WorkflowBuilder(final String name, final String namespace, final String version) { + this.document = new Document(); + this.document.setName(name); + this.document.setNamespace(namespace); + this.document.setVersion(version); + this.document.setDsl(DSL); + this.workflow = new Workflow(); + this.workflow.setDocument(this.document); + } + + public static WorkflowBuilder workflow( + final String name, final String namespace, final String version) { + return new WorkflowBuilder(name, namespace, version); + } + + public static WorkflowBuilder workflow(final String name, final String namespace) { + return new WorkflowBuilder(name, namespace, DEFAULT_VERSION); + } + + public static WorkflowBuilder workflow(final String name) { + return new WorkflowBuilder(name, DEFAULT_NAMESPACE, DEFAULT_VERSION); + } + + public static WorkflowBuilder workflow() { + return new WorkflowBuilder(UUID.randomUUID().toString(), DEFAULT_NAMESPACE, DEFAULT_VERSION); + } + + public WorkflowBuilder document(Consumer documentBuilderConsumer) { + final DocumentBuilder documentBuilder = new DocumentBuilder(this.document); + documentBuilderConsumer.accept(documentBuilder); + return this; + } + + public WorkflowBuilder use(Consumer useBuilderConsumer) { + final UseBuilder builder = new UseBuilder(); + useBuilderConsumer.accept(builder); + this.workflow.setUse(builder.build()); + return this; + } + + public WorkflowBuilder doTasks(Consumer doTaskConsumer) { + final DoTaskBuilder doTaskBuilder = new DoTaskBuilder(); + doTaskConsumer.accept(doTaskBuilder); + this.workflow.setDo(doTaskBuilder.build().getDo()); + return this; + } + + public WorkflowBuilder input(Consumer inputBuilderConsumer) { + final InputBuilder inputBuilder = new InputBuilder(); + inputBuilderConsumer.accept(inputBuilder); + this.workflow.setInput(inputBuilder.build()); + return this; + } + + public WorkflowBuilder output(Consumer outputBuilderConsumer) { + final OutputBuilder outputBuilder = new OutputBuilder(); + outputBuilderConsumer.accept(outputBuilder); + this.workflow.setOutput(outputBuilder.build()); + return this; + } + + public Workflow build() { + return this.workflow; + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderConsumers.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderConsumers.java new file mode 100644 index 00000000..28652c5f --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderConsumers.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.standard; + +import java.util.function.Consumer; + +public final class WorkflowBuilderConsumers { + + private WorkflowBuilderConsumers() {} + + public static Consumer authBasic( + final String username, final String password) { + return auth -> auth.basic(b -> b.username(username).password(password)); + } + + public static Consumer authBearer(final String token) { + return auth -> auth.bearer(b -> b.token(token)); + } + + public static Consumer authDigest( + final String username, final String password) { + return auth -> auth.digest(d -> d.username(username).password(password)); + } +} diff --git a/fluent/standard/src/test/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderTest.java b/fluent/standard/src/test/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderTest.java new file mode 100644 index 00000000..e29a27b9 --- /dev/null +++ b/fluent/standard/src/test/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderTest.java @@ -0,0 +1,518 @@ +/* + * 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.standard; + +import static io.serverlessworkflow.fluent.standard.WorkflowBuilderConsumers.authBasic; +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.assertTrue; + +import io.serverlessworkflow.api.types.AuthenticationPolicyUnion; +import io.serverlessworkflow.api.types.CallHTTP; +import io.serverlessworkflow.api.types.CatchErrors; +import io.serverlessworkflow.api.types.Document; +import io.serverlessworkflow.api.types.ErrorFilter; +import io.serverlessworkflow.api.types.EventFilter; +import io.serverlessworkflow.api.types.HTTPArguments; +import io.serverlessworkflow.api.types.HTTPHeaders; +import io.serverlessworkflow.api.types.HTTPQuery; +import io.serverlessworkflow.api.types.ListenTask; +import io.serverlessworkflow.api.types.OneEventConsumptionStrategy; +import io.serverlessworkflow.api.types.RetryLimitAttempt; +import io.serverlessworkflow.api.types.RetryPolicy; +import io.serverlessworkflow.api.types.SetTask; +import io.serverlessworkflow.api.types.TaskItem; +import io.serverlessworkflow.api.types.TryTask; +import io.serverlessworkflow.api.types.TryTaskCatch; +import io.serverlessworkflow.api.types.Use; +import io.serverlessworkflow.api.types.UseAuthentications; +import io.serverlessworkflow.api.types.Workflow; +import java.net.URI; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; + +/** Unit tests for the fluent WorkflowBuilder API (using static consumers). */ +public class WorkflowBuilderTest { + + @Test + void testWorkflowDocumentDefaults() { + // Use default name, namespace, version + Workflow wf = WorkflowBuilder.workflow().build(); + 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("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"); + } + + @Test + void testWorkflowDocumentExplicit() { + Workflow wf = + WorkflowBuilder.workflow("myFlow", "myNs", "1.2.3") + .document(d -> d.dsl("1.0.0").namespace("myNs").name("myFlow").version("1.2.3")) + .build(); + + Document doc = wf.getDocument(); + assertEquals("1.0.0", doc.getDsl()); + assertEquals("myNs", doc.getNamespace()); + assertEquals("myFlow", doc.getName()); + assertEquals("1.2.3", doc.getVersion()); + } + + @Test + void testUseAuthenticationsBasic() { + Workflow wf = + WorkflowBuilder.workflow("flowAuth") + .use( + u -> + u.authentications( + a -> a.authentication("basicAuth", authBasic("admin", "pass")))) + .build(); + + Use use = wf.getUse(); + assertNotNull(use, "Use must not be null"); + UseAuthentications auths = use.getAuthentications(); + assertNotNull(auths, "Authentications map must not be null"); + AuthenticationPolicyUnion union = auths.getAdditionalProperties().get("basicAuth"); + assertNotNull(union, "basicAuth policy should be present"); + assertNotNull(union.getBasicAuthenticationPolicy(), "BasicAuthenticationPolicy should be set"); + } + + @Test + void testDoTaskSetAndForEach() { + Workflow wf = + WorkflowBuilder.workflow("flowDo") + .doTasks( + d -> + d.set("initCtx", "$.foo = 'bar'") + .forEach("item", f -> f.each("item").at("$.list"))) + .build(); + + List items = wf.getDo(); + assertNotNull(items, "Do list must not be null"); + assertEquals(2, items.size(), "There should be two tasks"); + + TaskItem setItem = items.get(0); + assertEquals("initCtx", setItem.getName()); + SetTask st = setItem.getTask().getSetTask(); + assertNotNull(st, "SetTask should be present"); + assertEquals("$.foo = 'bar'", st.getSet().getString()); + + TaskItem forItem = items.get(1); + assertEquals("item", forItem.getName()); + assertNotNull(forItem.getTask().getForTask(), "ForTask should be present"); + } + + @Test + void testDoTaskMultipleTypes() { + Workflow wf = + WorkflowBuilder.workflow("flowMixed") + .doTasks( + d -> + d.set("init", s -> s.expr("$.init = true")) + .forEach("items", f -> f.each("item").in("$.list")) + .switchTask( + "choice", + sw -> { + // no-op configuration + }) + .raise( + "alert", + r -> { + // no-op configuration + }) + .fork( + "parallel", + f -> { + // no-op configuration + })) + .build(); + + List items = wf.getDo(); + assertNotNull(items, "Do list must not be null"); + assertEquals(5, items.size(), "There should be five tasks"); + + // set task + TaskItem setItem = items.get(0); + assertEquals("init", setItem.getName()); + assertNotNull(setItem.getTask().getSetTask(), "SetTask should be present"); + + // forEach task + TaskItem forItem = items.get(1); + assertEquals("items", forItem.getName()); + assertNotNull(forItem.getTask().getForTask(), "ForTask should be present"); + + // switchTask + TaskItem switchItem = items.get(2); + assertEquals("choice", switchItem.getName()); + assertNotNull(switchItem.getTask().getSwitchTask(), "SwitchTask should be present"); + + // raise task + TaskItem raiseItem = items.get(3); + assertEquals("alert", raiseItem.getName()); + assertNotNull(raiseItem.getTask().getRaiseTask(), "RaiseTask should be present"); + + // fork task + TaskItem forkItem = items.get(4); + assertEquals("parallel", forkItem.getName()); + assertNotNull(forkItem.getTask().getForkTask(), "ForkTask should be present"); + } + + @Test + void testDoTaskListenOne() { + Workflow wf = + WorkflowBuilder.workflow("flowListen") + .doTasks( + d -> + d.listen( + "waitCheck", + l -> l.one(f -> f.with(p -> p.type("com.fake.pet").source("mySource"))))) + .build(); + + List items = wf.getDo(); + assertNotNull(items, "Do list must not be null"); + assertEquals(1, items.size(), "There should be one task"); + + TaskItem item = items.get(0); + assertEquals("waitCheck", item.getName()); + ListenTask lt = item.getTask().getListenTask(); + assertNotNull(lt, "ListenTask should be present"); + OneEventConsumptionStrategy one = lt.getListen().getTo().getOneEventConsumptionStrategy(); + assertNotNull(one, "One consumption strategy should be set"); + EventFilter filter = one.getOne(); + assertNotNull(filter, "EventFilter should be present"); + assertEquals("com.fake.pet", filter.getWith().getType(), "Filter type should match"); + } + + @Test + void testDoTaskEmitEvent() { + Workflow wf = + WorkflowBuilder.workflow("flowEmit") + .doTasks( + d -> + d.emit( + "emitEvent", + e -> + e.event( + p -> + p.source(URI.create("https://petstore.com")) + .type("com.petstore.order.placed.v1") + .data( + Map.of( + "client", + Map.of( + "firstName", "Cruella", "lastName", "de Vil"), + "items", + List.of( + Map.of( + "breed", "dalmatian", "quantity", 101))))))) + .build(); + + List items = wf.getDo(); + assertNotNull(items, "Do list must not be null"); + assertEquals(1, items.size(), "There should be one emit task"); + + TaskItem item = items.get(0); + assertEquals("emitEvent", item.getName(), "TaskItem name should match"); + io.serverlessworkflow.api.types.EmitTask et = item.getTask().getEmitTask(); + assertNotNull(et, "EmitTask should be present"); + + io.serverlessworkflow.api.types.EmitEventDefinition ed = et.getEmit().getEvent(); + assertNotNull(ed, "EmitEventDefinition should be present"); + io.serverlessworkflow.api.types.EventProperties props = ed.getWith(); + assertEquals( + "https://petstore.com", + props.getSource().getUriTemplate().getLiteralUri().toString(), + "Source URI should match"); + assertEquals("com.petstore.order.placed.v1", props.getType(), "Event type should match"); + + Object dataObj = props.getData().getObject(); + assertNotNull(dataObj, "Data object should be present"); + assertInstanceOf(Map.class, dataObj, "Data should be a Map"); + @SuppressWarnings("unchecked") + Map dataMap = (Map) dataObj; + assertTrue(dataMap.containsKey("client"), "Data should contain 'client'"); + assertTrue(dataMap.containsKey("items"), "Data should contain 'items'"); + } + + @Test + void testDoTaskTryCatchWithRetry() { + Workflow wf = + WorkflowBuilder.workflow("flowTry") + .doTasks( + d -> + d.tryTask( + "tryBlock", + t -> + t.tryHandler(tb -> tb.set("init", s -> s.expr("$.start = true"))) + .catchHandler( + c -> + c.when("$.errorType == 'TEMP' ") + .retry( + r -> + r.when("$.retryCount < 3") + .limit( + l -> l.attempt(at -> at.count(3))))))) + .build(); + + List items = wf.getDo(); + assertEquals(1, items.size(), "There should be one try task"); + TaskItem item = items.get(0); + assertEquals("tryBlock", item.getName()); + + // Verify TryTask + TryTask tryTask = item.getTask().getTryTask(); + assertNotNull(tryTask, "TryTask should be present"); + + // Verify try handler tasks + List tryItems = tryTask.getTry(); + assertEquals(1, tryItems.size(), "Try handler should contain one task"); + TaskItem initItem = tryItems.get(0); + assertEquals("init", initItem.getName()); + assertNotNull(initItem.getTask().getSetTask(), "SetTask in try handler should be present"); + + // Verify catch configuration + TryTaskCatch catchCfg = tryTask.getCatch(); + assertNotNull(catchCfg, "Catch configuration should be present"); + assertEquals("$.errorType == 'TEMP' ", catchCfg.getWhen()); + + RetryPolicy retry = catchCfg.getRetry().getRetryPolicyDefinition(); + assertNotNull(retry, "RetryPolicy should be defined"); + assertEquals("$.retryCount < 3", retry.getWhen()); + RetryLimitAttempt attempt = retry.getLimit().getAttempt(); + assertEquals(3, attempt.getCount()); + } + + @Test + void testDoTaskTryCatchErrorsFiltering() { + Workflow wf = + WorkflowBuilder.workflow("flowCatch") + .doTasks( + d -> + d.tryTask( + "tryBlock", + t -> + t.tryHandler(tb -> tb.set("foo", s -> s.expr("$.foo = 'bar'"))) + .catchHandler( + c -> + c.exceptWhen("$.status == 500") + .errorsWith( + eb -> + eb.type("ServerError") + .status(500) + .instance("http://errors/5xx"))))) + .build(); + + TaskItem item = wf.getDo().get(0); + TryTask tryTask = item.getTask().getTryTask(); + TryTaskCatch catchCfg = tryTask.getCatch(); + + // exceptWhen should match + assertEquals("$.status == 500", catchCfg.getExceptWhen()); + + CatchErrors errors = catchCfg.getErrors(); + assertNotNull(errors, "CatchErrors should be present"); + ErrorFilter filter = errors.getWith(); + assertEquals("ServerError", filter.getType()); + assertEquals(500, filter.getStatus()); + assertEquals("http://errors/5xx", filter.getInstance()); + } + + @Test + void testWorkflowInputExternalSchema() { + String uri = "http://example.com/schema"; + Workflow wf = + WorkflowBuilder.workflow("wfInput").input(i -> i.from("$.data").schema(uri)).build(); + + assertNotNull(wf.getInput(), "Input must be set"); + assertEquals("$.data", wf.getInput().getFrom().getString()); + assertNotNull(wf.getInput().getSchema().getSchemaExternal(), "External schema must be set"); + String resolved = + wf.getInput() + .getSchema() + .getSchemaExternal() + .getResource() + .getEndpoint() + .getUriTemplate() + .getLiteralUri() + .toString(); + assertEquals(uri, resolved, "Schema URI should match"); + } + + @Test + void testWorkflowOutputExternalSchemaAndAs() { + String uri = "http://example.org/output-schema"; + Workflow wf = + WorkflowBuilder.workflow("wfOutput").output(o -> o.as("$.result").schema(uri)).build(); + + assertNotNull(wf.getOutput(), "Output must be set"); + assertEquals("$.result", wf.getOutput().getAs().getString()); + assertNotNull(wf.getOutput().getSchema().getSchemaExternal(), "External schema must be set"); + String resolved = + wf.getOutput() + .getSchema() + .getSchemaExternal() + .getResource() + .getEndpoint() + .getUriTemplate() + .getLiteralUri() + .toString(); + assertEquals(uri, resolved, "Schema URI should match"); + } + + @Test + void testWorkflowOutputInlineSchemaAndAsObject() { + Map inline = Map.of("foo", "bar"); + Workflow wf = + WorkflowBuilder.workflow().output(o -> o.as(Map.of("ok", true)).schema(inline)).build(); + + assertNotNull(wf.getOutput(), "Output must be set"); + assertInstanceOf(Map.class, wf.getOutput().getAs().getObject(), "As object must be a Map"); + assertNotNull(wf.getOutput().getSchema().getSchemaInline(), "Inline schema must be set"); + } + + @Test + void testWorkflowInputInlineSchemaAndFromObject() { + Map inline = Map.of("nested", List.of(1, 2, 3)); + Workflow wf = WorkflowBuilder.workflow().input(i -> i.from(inline).schema(inline)).build(); + + assertNotNull(wf.getInput(), "Input must be set"); + assertInstanceOf(Map.class, wf.getInput().getFrom().getObject(), "From object must be a Map"); + assertNotNull(wf.getInput().getSchema().getSchemaInline(), "Inline schema must be set"); + } + + @Test + void testDoTaskCallHTTPBasic() { + Workflow wf = + WorkflowBuilder.workflow("flowCallBasic") + .doTasks( + d -> + d.callHTTP( + "basicCall", + c -> + c.method("POST") + .endpoint(URI.create("http://example.com/api")) + .body(Map.of("foo", "bar")))) + .build(); + List items = wf.getDo(); + assertEquals(1, items.size(), "Should have one HTTP call task"); + TaskItem ti = items.get(0); + assertEquals("basicCall", ti.getName()); + CallHTTP call = ti.getTask().getCallTask().getCallHTTP(); + assertNotNull(call, "CallHTTP should be present"); + assertEquals("POST", call.getWith().getMethod()); + assertEquals( + URI.create("http://example.com/api"), + call.getWith().getEndpoint().getUriTemplate().getLiteralUri()); + assertInstanceOf(Map.class, call.getWith().getBody(), "Body should be the Map provided"); + } + + @Test + void testDoTaskCallHTTPHeadersConsumerAndMap() { + Workflow wf = + WorkflowBuilder.workflow("flowCallHeaders") + .doTasks( + d -> + d.callHTTP( + "hdrCall", + c -> + c.method("GET") + .endpoint("${uriExpr}") + .headers(h -> h.header("A", "1").header("B", "2")))) + .build(); + CallHTTP call = wf.getDo().get(0).getTask().getCallTask().getCallHTTP(); + HTTPHeaders hh = call.getWith().getHeaders().getHTTPHeaders(); + assertEquals("1", hh.getAdditionalProperties().get("A")); + assertEquals("2", hh.getAdditionalProperties().get("B")); + + Workflow wf2 = + WorkflowBuilder.workflow() + .doTasks( + d -> + d.callHTTP( + c -> + c.method("GET").endpoint("expr").headers(Map.of("X", "10", "Y", "20")))) + .build(); + CallHTTP call2 = wf2.getDo().get(0).getTask().getCallTask().getCallHTTP(); + HTTPHeaders hh2 = call2.getWith().getHeaders().getHTTPHeaders(); + assertEquals("10", hh2.getAdditionalProperties().get("X")); + assertEquals("20", hh2.getAdditionalProperties().get("Y")); + } + + @Test + void testDoTaskCallHTTPQueryConsumerAndMap() { + Workflow wf = + WorkflowBuilder.workflow("flowCallQuery") + .doTasks( + d -> + d.callHTTP( + "qryCall", + c -> + c.method("GET") + .endpoint("exprUri") + .query(q -> q.query("k1", "v1").query("k2", "v2")))) + .build(); + HTTPQuery hq = + wf.getDo().get(0).getTask().getCallTask().getCallHTTP().getWith().getQuery().getHTTPQuery(); + assertEquals("v1", hq.getAdditionalProperties().get("k1")); + assertEquals("v2", hq.getAdditionalProperties().get("k2")); + + Workflow wf2 = + WorkflowBuilder.workflow() + .doTasks( + d -> + d.callHTTP( + c -> c.method("GET").endpoint("uri").query(Map.of("q1", "x", "q2", "y")))) + .build(); + HTTPQuery hq2 = + wf2.getDo() + .get(0) + .getTask() + .getCallTask() + .getCallHTTP() + .getWith() + .getQuery() + .getHTTPQuery(); + assertEquals("x", hq2.getAdditionalProperties().get("q1")); + assertEquals("y", hq2.getAdditionalProperties().get("q2")); + } + + @Test + void testDoTaskCallHTTPRedirectAndOutput() { + Workflow wf = + WorkflowBuilder.workflow("flowCallOpts") + .doTasks( + d -> + d.callHTTP( + "optCall", + c -> + c.method("DELETE") + .endpoint("expr") + .redirect(true) + .output(HTTPArguments.HTTPOutput.RESPONSE))) + .build(); + CallHTTP call = wf.getDo().get(0).getTask().getCallTask().getCallHTTP(); + assertTrue(call.getWith().isRedirect(), "Redirect should be true"); + assertEquals( + HTTPArguments.HTTPOutput.RESPONSE, + call.getWith().getOutput(), + "Output should be overridden"); + } +} diff --git a/pom.xml b/pom.xml index b0a57369..f6d140e8 100644 --- a/pom.xml +++ b/pom.xml @@ -45,6 +45,7 @@ serialization examples experimental + fluent From dd30ee0037945ee1881e859b9f1f52818008fbd7 Mon Sep 17 00:00:00 2001 From: fjtirado Date: Sat, 19 Jul 2025 10:19:53 +0200 Subject: [PATCH 17/30] Ensure not workflowmodel is returned within a Map or Collection Signed-off-by: fjtirado --- .../impl/expressions/JavaModel.java | 20 +++++++++++++++++-- .../impl/expressions/JavaModelCollection.java | 8 ++++---- .../impl/expressions/JavaModelFactory.java | 2 +- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModel.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModel.java index 0e4b4df1..00481d54 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModel.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModel.java @@ -21,10 +21,12 @@ import java.util.Collection; import java.util.Collections; import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; import java.util.function.BiConsumer; +import java.util.stream.Collectors; -public class JavaModel implements WorkflowModel { +class JavaModel implements WorkflowModel { private Object object; @@ -33,7 +35,7 @@ public class JavaModel implements WorkflowModel { static final JavaModel NullModel = new JavaModel(null); JavaModel(Object object) { - this.object = object; + this.object = asJavaObject(object); } @Override @@ -89,6 +91,20 @@ public Object asJavaObject() { return object; } + static Object asJavaObject(Object object) { + if (object instanceof WorkflowModel model) { + return model.asJavaObject(); + } else if (object instanceof Map map) { + return ((Map) map) + .entrySet().stream() + .collect(Collectors.toMap(Entry::getKey, e -> asJavaObject(e.getValue()))); + } else if (object instanceof Collection col) { + return col.stream().map(JavaModel::asJavaObject).collect(Collectors.toList()); + } else { + return object; + } + } + @Override public Object asIs() { return object; diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelCollection.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelCollection.java index 065d8832..501cc287 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelCollection.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelCollection.java @@ -22,7 +22,7 @@ import java.util.Iterator; import java.util.Optional; -public class JavaModelCollection implements Collection, WorkflowModelCollection { +class JavaModelCollection implements Collection, WorkflowModelCollection { private final Collection object; @@ -31,7 +31,7 @@ public class JavaModelCollection implements Collection, WorkflowM } JavaModelCollection(Collection object) { - this.object = object; + this.object = (Collection) JavaModel.asJavaObject(object); } @Override @@ -86,12 +86,12 @@ public T[] toArray(T[] a) { @Override public boolean add(WorkflowModel e) { - return object.add(e.asIs()); + return object.add(e.asJavaObject()); } @Override public boolean remove(Object o) { - return object.remove(((WorkflowModel) o).asIs()); + return object.remove(((WorkflowModel) o).asJavaObject()); } @Override diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelFactory.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelFactory.java index 6034d33a..6ca4cc06 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelFactory.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelFactory.java @@ -23,7 +23,7 @@ import java.time.OffsetDateTime; import java.util.Map; -public class JavaModelFactory implements WorkflowModelFactory { +class JavaModelFactory implements WorkflowModelFactory { @Override public WorkflowModel combine(Map workflowVariables) { From fc800d5d1b25b3232d441b89aa24bc5cdd4f85fa Mon Sep 17 00:00:00 2001 From: Ricardo Zanini <1538000+ricardozanini@users.noreply.github.com> Date: Mon, 21 Jul 2025 15:17:02 -0400 Subject: [PATCH 18/30] Introduce Java Fluent DSL (#646) * Introduce Java Fluent DSL Signed-off-by: Ricardo Zanini * Fix taskItems builder - refactor Signed-off-by: Ricardo Zanini * Add tests to the lambda module, integrate the DSL Signed-off-by: Ricardo Zanini --------- Signed-off-by: Ricardo Zanini --- experimental/lambda/pom.xml | 45 +-- .../io/serverless/workflow/impl/CallTest.java | 16 +- .../workflow/impl/FluentDSLCallTest.java | 96 ++++++ experimental/pom.xml | 5 + .../api/types/SwitchCaseFunction.java | 4 + fluent/java/pom.xml | 42 +++ .../fluent/java/CallTaskJavaBuilder.java | 47 +++ .../fluent/java/DoTaskJavaBuilder.java | 62 ++++ .../fluent/java/ForTaskJavaBuilder.java | 95 ++++++ .../java/JavaTransformationHandlers.java | 44 +++ .../fluent/java/JavaWorkflowBuilder.java | 51 ++++ .../fluent/java/SwitchTaskJavaBuilder.java | 90 ++++++ .../fluent/java/TaskItemListJavaBuilder.java | 73 +++++ .../fluent/java/JavaWorkflowBuilderTest.java | 274 ++++++++++++++++++ fluent/pom.xml | 11 + .../fluent/standard/BaseDoTaskBuilder.java | 143 +++++++++ .../standard/BaseTaskItemListBuilder.java | 176 +++++++++++ .../fluent/standard/BaseWorkflowBuilder.java | 106 +++++++ .../fluent/standard/DoTaskBuilder.java | 132 +-------- .../fluent/standard/ForTaskBuilder.java | 25 +- .../fluent/standard/SwitchTaskBuilder.java | 18 +- .../fluent/standard/TaskBaseBuilder.java | 26 +- .../fluent/standard/TaskItemListBuilder.java | 33 +++ .../standard/TransformationHandlers.java | 29 ++ .../fluent/standard/TryTaskBuilder.java | 19 +- .../fluent/standard/WorkflowBuilder.java | 63 +--- .../fluent/standard/WorkflowBuilderTest.java | 32 +- 27 files changed, 1504 insertions(+), 253 deletions(-) create mode 100644 experimental/lambda/src/test/java/io/serverless/workflow/impl/FluentDSLCallTest.java create mode 100644 fluent/java/pom.xml create mode 100644 fluent/java/src/main/java/io/serverlessworkflow/fluent/java/CallTaskJavaBuilder.java create mode 100644 fluent/java/src/main/java/io/serverlessworkflow/fluent/java/DoTaskJavaBuilder.java create mode 100644 fluent/java/src/main/java/io/serverlessworkflow/fluent/java/ForTaskJavaBuilder.java create mode 100644 fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaTransformationHandlers.java create mode 100644 fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilder.java create mode 100644 fluent/java/src/main/java/io/serverlessworkflow/fluent/java/SwitchTaskJavaBuilder.java create mode 100644 fluent/java/src/main/java/io/serverlessworkflow/fluent/java/TaskItemListJavaBuilder.java create mode 100644 fluent/java/src/test/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilderTest.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseDoTaskBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseTaskItemListBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseWorkflowBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskItemListBuilder.java create mode 100644 fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TransformationHandlers.java diff --git a/experimental/lambda/pom.xml b/experimental/lambda/pom.xml index 72e4c0b2..5c20338c 100644 --- a/experimental/lambda/pom.xml +++ b/experimental/lambda/pom.xml @@ -1,22 +1,27 @@ - - 4.0.0 - - io.serverlessworkflow - serverlessworkflow-experimental - 8.0.0-SNAPSHOT - - serverlessworkflow-experimental-lambda - ServelessWorkflow:: Experimental:: lambda - - - io.serverlessworkflow - serverlessworkflow-experimental-types - - - io.serverlessworkflow - serverlessworkflow-impl-core - - + + 4.0.0 + + io.serverlessworkflow + serverlessworkflow-experimental + 8.0.0-SNAPSHOT + + serverlessworkflow-experimental-lambda + ServelessWorkflow:: Experimental:: lambda + + + io.serverlessworkflow + serverlessworkflow-experimental-types + + + io.serverlessworkflow + serverlessworkflow-impl-core + + + io.serverlessworkflow + serverlessworkflow-fluent-java + + org.junit.jupiter junit-jupiter-api test @@ -41,5 +46,5 @@ logback-classic test - + \ No newline at end of file diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java index 9118ec06..7d95410b 100644 --- a/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java @@ -80,7 +80,7 @@ void testForLoop() throws InterruptedException, ExecutionException { new Task() .withForTask( new ForTaskFunction() - .withWhile(this::isEven) + .withWhile(CallTest::isEven) .withCollection(v -> (Collection) v) .withFor(forConfig) .withDo( @@ -91,7 +91,7 @@ void testForLoop() throws InterruptedException, ExecutionException { .withCallTask( new CallTaskJava( CallJava.loopFunction( - this::sum, + CallTest::sum, forConfig.getEach())))))))))); assertThat( @@ -124,7 +124,7 @@ void testSwitch() throws InterruptedException, ExecutionException { new SwitchItem( "odd", new SwitchCaseFunction() - .withPredicate(this::isOdd) + .withPredicate(CallTest::isOdd) .withThen( new FlowDirective() .withFlowDirectiveEnum( @@ -132,7 +132,7 @@ void testSwitch() throws InterruptedException, ExecutionException { new TaskItem( "java", new Task() - .withCallTask(new CallTaskJava(CallJava.function(this::zero)))))); + .withCallTask(new CallTaskJava(CallJava.function(CallTest::zero)))))); WorkflowDefinition definition = app.workflowDefinition(workflow); assertThat(definition.instance(3).start().get().asNumber().orElseThrow()).isEqualTo(3); @@ -140,19 +140,19 @@ void testSwitch() throws InterruptedException, ExecutionException { } } - private boolean isEven(Object model, Integer number) { + public static boolean isEven(Object model, Integer number) { return !isOdd(number); } - private boolean isOdd(Integer number) { + public static boolean isOdd(Integer number) { return number % 2 != 0; } - private int zero(Integer value) { + public static int zero(Integer value) { return 0; } - private Integer sum(Object model, Integer item) { + public static Integer sum(Object model, Integer item) { return model instanceof Collection ? item : (Integer) model + item; } } diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/FluentDSLCallTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/FluentDSLCallTest.java new file mode 100644 index 00000000..dd4f9a29 --- /dev/null +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/FluentDSLCallTest.java @@ -0,0 +1,96 @@ +/* + * 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.serverless.workflow.impl; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.serverlessworkflow.api.types.FlowDirectiveEnum; +import io.serverlessworkflow.api.types.Workflow; +import io.serverlessworkflow.fluent.java.JavaWorkflowBuilder; +import io.serverlessworkflow.impl.WorkflowApplication; +import io.serverlessworkflow.impl.WorkflowDefinition; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ExecutionException; +import org.junit.jupiter.api.Test; + +public class FluentDSLCallTest { + + @Test + void testJavaFunction() throws InterruptedException, ExecutionException { + try (WorkflowApplication app = WorkflowApplication.builder().build()) { + final Workflow workflow = + JavaWorkflowBuilder.workflow("testJavaCall") + .tasks(tasks -> tasks.callFn(f -> f.fn(JavaFunctions::getName))) + .build(); + assertThat( + app.workflowDefinition(workflow) + .instance(new Person("Francisco", 33)) + .start() + .get() + .asText() + .orElseThrow()) + .isEqualTo("Francisco Javierito"); + } + } + + @Test + void testForLoop() throws InterruptedException, ExecutionException { + try (WorkflowApplication app = WorkflowApplication.builder().build()) { + Workflow workflow = + JavaWorkflowBuilder.workflow() + .tasks( + t -> + t.forFn( + f -> + f.whileC(CallTest::isEven) + .collection(v -> (Collection) v) + .tasks(CallTest::sum))) + .build(); + + assertThat( + app.workflowDefinition(workflow) + .instance(List.of(2, 4, 6)) + .start() + .get() + .asNumber() + .orElseThrow()) + .isEqualTo(12); + } + } + + @Test + void testSwitch() throws InterruptedException, ExecutionException { + try (WorkflowApplication app = WorkflowApplication.builder().build()) { + Workflow workflow = + JavaWorkflowBuilder.workflow() + .tasks( + tasks -> + tasks + .switchFn( + switchOdd -> + switchOdd.items( + item -> + item.when(CallTest::isOdd).then(FlowDirectiveEnum.END))) + .callFn(callJava -> callJava.fn(CallTest::zero))) + .build(); + + WorkflowDefinition definition = app.workflowDefinition(workflow); + assertThat(definition.instance(3).start().get().asNumber().orElseThrow()).isEqualTo(3); + assertThat(definition.instance(4).start().get().asNumber().orElseThrow()).isEqualTo(0); + } + } +} diff --git a/experimental/pom.xml b/experimental/pom.xml index 409e68c2..e269b7b0 100644 --- a/experimental/pom.xml +++ b/experimental/pom.xml @@ -30,6 +30,11 @@ serverlessworkflow-experimental-types ${project.version} + + io.serverlessworkflow + serverlessworkflow-fluent-java + ${project.version} + diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/SwitchCaseFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/SwitchCaseFunction.java index 027ab178..51b4175a 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/SwitchCaseFunction.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/SwitchCaseFunction.java @@ -27,6 +27,10 @@ public SwitchCaseFunction withPredicate(Predicate predicate) { return this; } + public void setPredicate(Predicate predicate) { + this.predicate = predicate; + } + public Predicate predicate() { return predicate; } diff --git a/fluent/java/pom.xml b/fluent/java/pom.xml new file mode 100644 index 00000000..aa12ec97 --- /dev/null +++ b/fluent/java/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + io.serverlessworkflow + serverlessworkflow-fluent + 8.0.0-SNAPSHOT + + + Serverless Workflow :: Fluent :: Java + serverlessworkflow-fluent-java + + + 17 + 17 + UTF-8 + + + + + io.serverlessworkflow + serverlessworkflow-experimental-types + + + io.serverlessworkflow + serverlessworkflow-types + + + io.serverlessworkflow + serverlessworkflow-fluent-standard + + + + org.junit.jupiter + junit-jupiter-api + test + + + + \ No newline at end of file diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/CallTaskJavaBuilder.java b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/CallTaskJavaBuilder.java new file mode 100644 index 00000000..8149ebba --- /dev/null +++ b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/CallTaskJavaBuilder.java @@ -0,0 +1,47 @@ +/* + * 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.java; + +import io.serverlessworkflow.api.types.CallJava; +import io.serverlessworkflow.api.types.CallTaskJava; +import io.serverlessworkflow.fluent.standard.TaskBaseBuilder; +import java.util.function.Function; + +public class CallTaskJavaBuilder extends TaskBaseBuilder + implements JavaTransformationHandlers { + + private CallTaskJava callTaskJava; + + CallTaskJavaBuilder() { + callTaskJava = new CallTaskJava(new CallJava() {}); + super.setTask(callTaskJava.getCallJava()); + } + + @Override + protected CallTaskJavaBuilder self() { + return this; + } + + public CallTaskJavaBuilder fn(Function function) { + this.callTaskJava = new CallTaskJava(CallJava.function(function)); + super.setTask(this.callTaskJava.getCallJava()); + return this; + } + + public CallTaskJava build() { + return this.callTaskJava; + } +} diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/DoTaskJavaBuilder.java b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/DoTaskJavaBuilder.java new file mode 100644 index 00000000..b17cf643 --- /dev/null +++ b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/DoTaskJavaBuilder.java @@ -0,0 +1,62 @@ +/* + * 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.java; + +import io.serverlessworkflow.fluent.standard.BaseDoTaskBuilder; +import java.util.function.Consumer; + +public class DoTaskJavaBuilder extends BaseDoTaskBuilder + implements JavaTransformationHandlers { + + DoTaskJavaBuilder() { + super(new TaskItemListJavaBuilder()); + } + + @Override + protected DoTaskJavaBuilder self() { + return this; + } + + public DoTaskJavaBuilder callFn(String name, Consumer consumer) { + this.innerListBuilder().callJava(name, consumer); + return this; + } + + public DoTaskJavaBuilder callFn(Consumer consumer) { + this.innerListBuilder().callJava(consumer); + return this; + } + + public DoTaskJavaBuilder forFn(String name, Consumer consumer) { + this.innerListBuilder().forFn(name, consumer); + return this; + } + + public DoTaskJavaBuilder forFn(Consumer consumer) { + this.innerListBuilder().forFn(consumer); + return this; + } + + public DoTaskJavaBuilder switchFn(String name, Consumer consumer) { + this.innerListBuilder().switchFn(name, consumer); + return this; + } + + public DoTaskJavaBuilder switchFn(Consumer consumer) { + this.innerListBuilder().switchFn(consumer); + return this; + } +} diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/ForTaskJavaBuilder.java b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/ForTaskJavaBuilder.java new file mode 100644 index 00000000..241272f0 --- /dev/null +++ b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/ForTaskJavaBuilder.java @@ -0,0 +1,95 @@ +/* + * 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.java; + +import io.serverlessworkflow.api.types.CallJava; +import io.serverlessworkflow.api.types.CallTaskJava; +import io.serverlessworkflow.api.types.ForTaskConfiguration; +import io.serverlessworkflow.api.types.ForTaskFunction; +import io.serverlessworkflow.api.types.Task; +import io.serverlessworkflow.api.types.TaskItem; +import io.serverlessworkflow.fluent.standard.TaskBaseBuilder; +import io.serverlessworkflow.impl.expressions.LoopFunction; +import io.serverlessworkflow.impl.expressions.LoopPredicate; +import io.serverlessworkflow.impl.expressions.LoopPredicateIndex; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.function.Function; + +public class ForTaskJavaBuilder extends TaskBaseBuilder + implements JavaTransformationHandlers { + + private final ForTaskFunction forTaskFunction; + private final List items; + + ForTaskJavaBuilder() { + this.forTaskFunction = new ForTaskFunction(); + this.forTaskFunction.withFor(new ForTaskConfiguration()); + this.items = new ArrayList<>(); + super.setTask(forTaskFunction); + } + + @Override + protected ForTaskJavaBuilder self() { + return this; + } + + public ForTaskJavaBuilder whileC(LoopPredicate predicate) { + this.forTaskFunction.withWhile(predicate); + return this; + } + + public ForTaskJavaBuilder whileC(LoopPredicateIndex predicate) { + this.forTaskFunction.withWhile(predicate); + return this; + } + + public ForTaskJavaBuilder collection(Function> collectionF) { + this.forTaskFunction.withCollection(collectionF); + return this; + } + + public ForTaskJavaBuilder tasks(String name, LoopFunction function) { + this.items.add( + new TaskItem( + name, + new Task() + .withCallTask( + new CallTaskJava( + CallJava.loopFunction( + function, this.forTaskFunction.getFor().getEach()))))); + return this; + } + + public ForTaskJavaBuilder tasks(LoopFunction function) { + return this.tasks(UUID.randomUUID().toString(), function); + } + + public ForTaskJavaBuilder tasks(Consumer consumer) { + final TaskItemListJavaBuilder builder = new TaskItemListJavaBuilder(); + consumer.accept(builder); + this.items.addAll(builder.build()); + return this; + } + + public ForTaskFunction build() { + this.forTaskFunction.setDo(this.items); + return this.forTaskFunction; + } +} diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaTransformationHandlers.java b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaTransformationHandlers.java new file mode 100644 index 00000000..c8651b83 --- /dev/null +++ b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaTransformationHandlers.java @@ -0,0 +1,44 @@ +/* + * 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.java; + +import io.serverlessworkflow.api.types.Export; +import io.serverlessworkflow.api.types.ExportAsFunction; +import io.serverlessworkflow.api.types.Input; +import io.serverlessworkflow.api.types.InputFromFunction; +import io.serverlessworkflow.api.types.Output; +import io.serverlessworkflow.api.types.OutputAsFunction; +import io.serverlessworkflow.fluent.standard.TransformationHandlers; +import java.util.function.Function; + +public interface JavaTransformationHandlers> + extends TransformationHandlers { + + default B exportAsFn(Function function) { + setExport(new Export().withAs(new ExportAsFunction().withFunction(function))); + return (B) this; + } + + default B inputFrom(Function function) { + setInput(new Input().withFrom(new InputFromFunction().withFunction(function))); + return (B) this; + } + + default B outputAs(Function function) { + setOutput(new Output().withAs(new OutputAsFunction().withFunction(function))); + return (B) this; + } +} diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilder.java b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilder.java new file mode 100644 index 00000000..5d90b598 --- /dev/null +++ b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilder.java @@ -0,0 +1,51 @@ +/* + * 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.java; + +import io.serverlessworkflow.fluent.standard.BaseWorkflowBuilder; +import java.util.UUID; + +public class JavaWorkflowBuilder + extends BaseWorkflowBuilder + implements JavaTransformationHandlers { + + private JavaWorkflowBuilder(final String name, final String namespace, final String version) { + super(name, namespace, version); + } + + public static JavaWorkflowBuilder workflow(final String name, final String namespace) { + return new JavaWorkflowBuilder(name, namespace, DEFAULT_VERSION); + } + + public static JavaWorkflowBuilder workflow(final String name) { + return new JavaWorkflowBuilder(name, DEFAULT_NAMESPACE, DEFAULT_VERSION); + } + + public static JavaWorkflowBuilder workflow() { + return new JavaWorkflowBuilder( + UUID.randomUUID().toString(), DEFAULT_NAMESPACE, DEFAULT_VERSION); + } + + @Override + protected DoTaskJavaBuilder newDo() { + return new DoTaskJavaBuilder(); + } + + @Override + protected JavaWorkflowBuilder self() { + return this; + } +} diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/SwitchTaskJavaBuilder.java b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/SwitchTaskJavaBuilder.java new file mode 100644 index 00000000..8b9d98e6 --- /dev/null +++ b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/SwitchTaskJavaBuilder.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.fluent.java; + +import io.serverlessworkflow.api.types.FlowDirective; +import io.serverlessworkflow.api.types.FlowDirectiveEnum; +import io.serverlessworkflow.api.types.SwitchCase; +import io.serverlessworkflow.api.types.SwitchCaseFunction; +import io.serverlessworkflow.api.types.SwitchItem; +import io.serverlessworkflow.api.types.SwitchTask; +import io.serverlessworkflow.fluent.standard.TaskBaseBuilder; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.function.Predicate; + +public class SwitchTaskJavaBuilder extends TaskBaseBuilder + implements JavaTransformationHandlers { + + private final SwitchTask switchTask; + private final List switchItems; + + SwitchTaskJavaBuilder() { + this.switchTask = new SwitchTask(); + this.switchItems = new ArrayList<>(); + super.setTask(switchTask); + } + + @Override + protected SwitchTaskJavaBuilder self() { + return this; + } + + public SwitchTaskJavaBuilder items(Consumer consumer) { + return this.items(UUID.randomUUID().toString(), consumer); + } + + public SwitchTaskJavaBuilder items(String name, Consumer consumer) { + final SwitchCaseJavaBuilder switchCase = new SwitchCaseJavaBuilder(); + consumer.accept(switchCase); + this.switchItems.add(new SwitchItem(name, switchCase.build())); + return this; + } + + public SwitchTask build() { + this.switchTask.setSwitch(this.switchItems); + return switchTask; + } + + public static final class SwitchCaseJavaBuilder { + private final SwitchCaseFunction switchCase; + + SwitchCaseJavaBuilder() { + this.switchCase = new SwitchCaseFunction(); + } + + public SwitchCaseJavaBuilder when(Predicate when) { + this.switchCase.setPredicate(when); + return this; + } + + public SwitchCaseJavaBuilder then(FlowDirective then) { + this.switchCase.setThen(then); + return this; + } + + public SwitchCaseJavaBuilder then(FlowDirectiveEnum then) { + this.switchCase.setThen(new FlowDirective().withFlowDirectiveEnum(then)); + return this; + } + + public SwitchCase build() { + return this.switchCase; + } + } +} diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/TaskItemListJavaBuilder.java b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/TaskItemListJavaBuilder.java new file mode 100644 index 00000000..40fa250b --- /dev/null +++ b/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/TaskItemListJavaBuilder.java @@ -0,0 +1,73 @@ +/* + * 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.java; + +import io.serverlessworkflow.api.types.Task; +import io.serverlessworkflow.api.types.TaskItem; +import io.serverlessworkflow.fluent.standard.BaseTaskItemListBuilder; +import java.util.UUID; +import java.util.function.Consumer; + +public class TaskItemListJavaBuilder extends BaseTaskItemListBuilder { + + TaskItemListJavaBuilder() { + super(); + } + + @Override + protected TaskItemListJavaBuilder self() { + return this; + } + + @Override + protected TaskItemListJavaBuilder newItemListBuilder() { + return new TaskItemListJavaBuilder(); + } + + public TaskItemListJavaBuilder callJava(String name, Consumer consumer) { + this.requireNameAndConfig(name, consumer); + final CallTaskJavaBuilder callTaskJavaBuilder = new CallTaskJavaBuilder(); + consumer.accept(callTaskJavaBuilder); + return addTaskItem(new TaskItem(name, new Task().withCallTask(callTaskJavaBuilder.build()))); + } + + public TaskItemListJavaBuilder callJava(Consumer consumer) { + return this.callJava(UUID.randomUUID().toString(), consumer); + } + + public TaskItemListJavaBuilder forFn(String name, Consumer consumer) { + this.requireNameAndConfig(name, consumer); + final ForTaskJavaBuilder forTaskJavaBuilder = new ForTaskJavaBuilder(); + consumer.accept(forTaskJavaBuilder); + return this.addTaskItem(new TaskItem(name, new Task().withForTask(forTaskJavaBuilder.build()))); + } + + public TaskItemListJavaBuilder forFn(Consumer consumer) { + return this.forFn(UUID.randomUUID().toString(), consumer); + } + + public TaskItemListJavaBuilder switchFn(String name, Consumer consumer) { + this.requireNameAndConfig(name, consumer); + final SwitchTaskJavaBuilder switchTaskJavaBuilder = new SwitchTaskJavaBuilder(); + consumer.accept(switchTaskJavaBuilder); + return this.addTaskItem( + new TaskItem(name, new Task().withSwitchTask(switchTaskJavaBuilder.build()))); + } + + public TaskItemListJavaBuilder switchFn(Consumer consumer) { + return this.switchFn(UUID.randomUUID().toString(), consumer); + } +} diff --git a/fluent/java/src/test/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilderTest.java b/fluent/java/src/test/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilderTest.java new file mode 100644 index 00000000..ef037cad --- /dev/null +++ b/fluent/java/src/test/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilderTest.java @@ -0,0 +1,274 @@ +/* + * 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.java; + +import static org.junit.jupiter.api.Assertions.*; + +import io.serverlessworkflow.api.types.*; +import io.serverlessworkflow.fluent.standard.BaseWorkflowBuilder; +// if you reuse anything +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** Tests for JavaWorkflowBuilder + Java DSL extensions. */ +class JavaWorkflowBuilderTest { + + @Test + @DisplayName("Default Java workflow has auto-generated name and default namespace/version") + void testDefaults() { + Workflow wf = JavaWorkflowBuilder.workflow().build(); + assertNotNull(wf); + Document doc = wf.getDocument(); + assertNotNull(doc); + assertEquals(BaseWorkflowBuilder.DEFAULT_NAMESPACE, doc.getNamespace()); + assertEquals(BaseWorkflowBuilder.DEFAULT_VERSION, doc.getVersion()); + assertEquals(BaseWorkflowBuilder.DSL, doc.getDsl()); + assertNotNull(doc.getName()); + } + + @Test + @DisplayName("Spec style forE still works inside Java workflow") + void testSpecForEachInJavaWorkflow() { + Workflow wf = + JavaWorkflowBuilder.workflow("specLoopFlow") + .tasks( + d -> + d.forEach(f -> f.each("pet").in("$.pets")) + .set("markDone", s -> s.expr("$.done = true"))) + .build(); + + List items = wf.getDo(); + assertEquals(2, items.size()); + + TaskItem loopItem = items.get(0); + assertNotNull(loopItem.getTask().getForTask(), "Spec ForTask should be present"); + + TaskItem setItem = items.get(1); + assertNotNull(setItem.getTask().getSetTask()); + SetTask st = setItem.getTask().getSetTask(); + assertEquals("$.done = true", st.getSet().getString()); + } + + @Test + @DisplayName("Java style forE with collection + whileC builds ForTaskFunction") + void testJavaForEach() { + Workflow wf = + JavaWorkflowBuilder.workflow("javaLoopFlow") + .tasks( + d -> + d.forFn( + j -> + j.collection(ctx -> List.of("a", "b", "c")) + .whileC((String val, Object ctx) -> !val.equals("c")) + .tasks( + inner -> inner.set("loopFlag", s -> s.expr("$.flag = true"))))) + .build(); + + List items = wf.getDo(); + assertEquals(1, items.size()); + + TaskItem loopItem = items.get(0); + Task task = loopItem.getTask(); + + assertNotNull(task.getForTask(), "Java ForTaskFunction should be present"); + + // Basic structural checks on nested do inside the function loop + ForTaskFunction fn = (ForTaskFunction) task.getForTask(); + assertNotNull(fn.getDo(), "Nested 'do' list inside ForTaskFunction should be populated"); + assertEquals(1, fn.getDo().size()); + Task nested = fn.getDo().get(0).getTask(); + assertNotNull(nested.getSetTask()); + } + + @Test + @DisplayName("Mixed spec and Java loops in one workflow") + void testMixedLoops() { + Workflow wf = + JavaWorkflowBuilder.workflow("mixed") + .tasks( + d -> + d.forEach(f -> f.each("item").in("$.array")) // spec + .forFn(j -> j.collection(ctx -> List.of(1, 2, 3))) // java + ) + .build(); + + List items = wf.getDo(); + assertEquals(2, items.size()); + + Task specLoop = items.get(0).getTask(); + Task javaLoop = items.get(1).getTask(); + + assertNotNull(specLoop.getForTask()); + assertNotNull(javaLoop.getForTask()); + } + + @Test + @DisplayName("Java functional exportAsFn/inputFrom/outputAs set function wrappers (not literals)") + void testJavaFunctionalIO() { + AtomicBoolean exportCalled = new AtomicBoolean(false); + AtomicBoolean inputCalled = new AtomicBoolean(false); + AtomicBoolean outputCalled = new AtomicBoolean(false); + + Workflow wf = + JavaWorkflowBuilder.workflow("fnIO") + .tasks( + d -> + d.set("init", s -> s.expr("$.x = 1")) + .forFn( + j -> + j.collection( + ctx -> { + inputCalled.set(true); + return List.of("x", "y"); + }) + .tasks(inner -> inner.set("calc", s -> s.expr("$.y = $.x + 1"))) + .exportAsFn( + item -> { + exportCalled.set(true); + return Map.of("computed", 42); + }) + .outputAs( + item -> { + outputCalled.set(true); + return Map.of("out", true); + }))) + .build(); + + // Top-level 'do' structure + assertEquals(2, wf.getDo().size()); + + // Find nested forTaskFunction + Task forTaskFnHolder = wf.getDo().get(1).getTask(); + ForTaskFunction fn = (ForTaskFunction) forTaskFnHolder.getForTask(); + assertNotNull(fn); + + // Inspect nested tasks inside the function loop + List nested = fn.getDo(); + assertEquals(1, nested.size()); + TaskBase nestedTask = nested.get(0).getTask().getSetTask(); + assertNotNull(nestedTask); + + // Because functions are likely stored as opaque objects, we check that + // export / output structures exist and are not expression-based. + Export export = fn.getExport(); + assertNotNull(export, "Export should be set via functional variant"); + assertNull( + export.getAs() != null ? export.getAs().getString() : null, + "Export 'as' should not be a plain string when using function variant"); + + Output out = fn.getOutput(); + // If functional output maps to an OutputAsFunction wrapper, adapt the checks: + if (out != null && out.getAs() != null) { + // Expect no literal string if function used + assertNull(out.getAs().getString(), "Output 'as' should not be a literal string"); + } + + // We can't *invoke* lambdas here (unless your runtime exposes them), + // but we verified structural placement. Flipping AtomicBooleans in creation lambdas + // (collection) at least shows one function executed during build (if it is executed now; + // if they are deferred, remove those assertions.) + } + + @Test + @DisplayName("callJava task added and retains name + CallTask union") + void testCallJavaTask() { + Workflow wf = + JavaWorkflowBuilder.workflow("callJavaFlow") + .tasks( + d -> + d.callFn( + "invokeHandler", + cj -> { + // configure your CallTaskJavaBuilder here + // e.g., cj.className("com.acme.Handler").arg("key", "value"); + })) + .build(); + + List items = wf.getDo(); + assertEquals(1, items.size()); + TaskItem ti = items.get(0); + + assertEquals("invokeHandler", ti.getName()); + Task task = ti.getTask(); + assertNotNull(task.getCallTask(), "CallTask should be present for callJava"); + // Additional assertions if CallTaskJavaBuilder populates fields + // e.g., assertEquals("com.acme.Handler", task.getCallTask().getCallJava().getClassName()); + } + + @Test + @DisplayName("switchCaseFn (Java variant) coexists with spec tasks") + void testSwitchCaseJava() { + Workflow wf = + JavaWorkflowBuilder.workflow("switchJava") + .tasks( + d -> + d.set("prepare", s -> s.expr("$.ready = true")) + .switchC( + sw -> { + // configure Java switch builder (cases / predicates) + })) + .build(); + + List items = wf.getDo(); + assertEquals(2, items.size()); + + Task specSet = items.get(0).getTask(); + Task switchTask = items.get(1).getTask(); + + assertNotNull(specSet.getSetTask()); + assertNotNull(switchTask.getSwitchTask(), "SwitchTask union should be present"); + } + + @Test + @DisplayName("Combined: spec set + java forE + callJava inside nested do") + void testCompositeScenario() { + Workflow wf = + JavaWorkflowBuilder.workflow("composite") + .tasks( + d -> + d.set("init", s -> s.expr("$.val = 0")) + .forFn( + j -> + j.collection(ctx -> List.of("a", "b")) + .tasks( + inner -> + inner + .callJava( + cj -> { + // customizing Java call + }) + .set("flag", s -> s.expr("$.flag = true"))))) + .build(); + + assertEquals(2, wf.getDo().size()); + + Task loopHolder = wf.getDo().get(1).getTask(); + ForTaskFunction fn = (ForTaskFunction) loopHolder.getForTask(); + assertNotNull(fn); + + List nested = fn.getDo(); + assertEquals(2, nested.size()); + + Task nestedCall = nested.get(0).getTask(); + Task nestedSet = nested.get(1).getTask(); + + assertNotNull(nestedCall.getCallTask()); + assertNotNull(nestedSet.getSetTask()); + } +} diff --git a/fluent/pom.xml b/fluent/pom.xml index 71d54a64..1af30562 100644 --- a/fluent/pom.xml +++ b/fluent/pom.xml @@ -25,11 +25,22 @@ serverlessworkflow-types ${project.version} + + io.serverlessworkflow + serverlessworkflow-experimental-types + ${project.version} + + + io.serverlessworkflow + serverlessworkflow-fluent-standard + ${project.version} + standard + java \ No newline at end of file diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseDoTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseDoTaskBuilder.java new file mode 100644 index 00000000..fab86cdd --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseDoTaskBuilder.java @@ -0,0 +1,143 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.DoTask; +import java.util.function.Consumer; + +public abstract class BaseDoTaskBuilder< + TASK extends TaskBaseBuilder, LIST extends BaseTaskItemListBuilder> + extends TaskBaseBuilder { + private final DoTask doTask; + private final BaseTaskItemListBuilder taskItemListBuilder; + + protected BaseDoTaskBuilder(BaseTaskItemListBuilder taskItemListBuilder) { + this.doTask = new DoTask(); + this.taskItemListBuilder = taskItemListBuilder; + this.setTask(doTask); + } + + protected abstract TASK self(); + + protected LIST innerListBuilder() { + return (LIST) taskItemListBuilder; + } + + public TASK set(String name, Consumer itemsConfigurer) { + taskItemListBuilder.set(name, itemsConfigurer); + return self(); + } + + public TASK set(Consumer itemsConfigurer) { + taskItemListBuilder.set(itemsConfigurer); + return self(); + } + + public TASK set(String name, final String expr) { + taskItemListBuilder.set(name, s -> s.expr(expr)); + return self(); + } + + public TASK set(final String expr) { + taskItemListBuilder.set(expr); + return self(); + } + + public TASK forEach(String name, Consumer> itemsConfigurer) { + taskItemListBuilder.forEach(name, itemsConfigurer); + return self(); + } + + public TASK forEach(Consumer> itemsConfigurer) { + taskItemListBuilder.forEach(itemsConfigurer); + return self(); + } + + public TASK switchC(String name, Consumer itemsConfigurer) { + taskItemListBuilder.switchC(name, itemsConfigurer); + return self(); + } + + public TASK switchC(Consumer itemsConfigurer) { + taskItemListBuilder.switchC(itemsConfigurer); + return self(); + } + + public TASK raise(String name, Consumer itemsConfigurer) { + taskItemListBuilder.raise(name, itemsConfigurer); + return self(); + } + + public TASK raise(Consumer itemsConfigurer) { + taskItemListBuilder.raise(itemsConfigurer); + return self(); + } + + public TASK fork(String name, Consumer itemsConfigurer) { + taskItemListBuilder.fork(name, itemsConfigurer); + return self(); + } + + public TASK fork(Consumer itemsConfigurer) { + taskItemListBuilder.fork(itemsConfigurer); + return self(); + } + + public TASK listen(String name, Consumer itemsConfigurer) { + taskItemListBuilder.listen(name, itemsConfigurer); + return self(); + } + + public TASK listen(Consumer itemsConfigurer) { + taskItemListBuilder.listen(itemsConfigurer); + return self(); + } + + public TASK emit(String name, Consumer itemsConfigurer) { + taskItemListBuilder.emit(name, itemsConfigurer); + return self(); + } + + public TASK emit(Consumer itemsConfigurer) { + taskItemListBuilder.emit(itemsConfigurer); + return self(); + } + + public TASK tryC(String name, Consumer> itemsConfigurer) { + taskItemListBuilder.tryC(name, itemsConfigurer); + return self(); + } + + public TASK tryC(Consumer> itemsConfigurer) { + taskItemListBuilder.tryC(itemsConfigurer); + return self(); + } + + public TASK callHTTP(String name, Consumer itemsConfigurer) { + taskItemListBuilder.callHTTP(name, itemsConfigurer); + return self(); + } + + public TASK callHTTP(Consumer itemsConfigurer) { + taskItemListBuilder.callHTTP(itemsConfigurer); + return self(); + } + + public DoTask build() { + this.doTask.setDo(this.taskItemListBuilder.build()); + return this.doTask; + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseTaskItemListBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseTaskItemListBuilder.java new file mode 100644 index 00000000..566b493a --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseTaskItemListBuilder.java @@ -0,0 +1,176 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.CallTask; +import io.serverlessworkflow.api.types.Task; +import io.serverlessworkflow.api.types.TaskBase; +import io.serverlessworkflow.api.types.TaskItem; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.function.Consumer; + +/** + * A builder for an ordered {@link TaskItem} list. + * + *

This builder only knows how to append new TaskItems of various flavors, but does NOT expose + * {@link TaskBase}‑level methods like export(), input(), etc. Those belong on {@link + * TaskBaseBuilder} subclasses. + * + * @param the concrete builder type + */ +public abstract class BaseTaskItemListBuilder> { + + private final List list; + + public BaseTaskItemListBuilder() { + this.list = new ArrayList<>(); + } + + protected abstract SELF self(); + + protected abstract SELF newItemListBuilder(); + + protected SELF addTaskItem(TaskItem taskItem) { + Objects.requireNonNull(taskItem, "taskItem must not be null"); + list.add(taskItem); + return self(); + } + + protected void requireNameAndConfig(String name, Consumer cfg) { + Objects.requireNonNull(name, "Task name must not be null"); + Objects.requireNonNull(cfg, "Configurer must not be null"); + } + + public SELF set(String name, Consumer itemsConfigurer) { + requireNameAndConfig(name, itemsConfigurer); + final SetTaskBuilder setBuilder = new SetTaskBuilder(); + itemsConfigurer.accept(setBuilder); + return addTaskItem(new TaskItem(name, new Task().withSetTask(setBuilder.build()))); + } + + public SELF set(Consumer itemsConfigurer) { + return this.set(UUID.randomUUID().toString(), itemsConfigurer); + } + + public SELF set(String name, final String expr) { + return this.set(name, s -> s.expr(expr)); + } + + public SELF set(final String expr) { + return this.set(UUID.randomUUID().toString(), s -> s.expr(expr)); + } + + public SELF forEach(String name, Consumer> itemsConfigurer) { + requireNameAndConfig(name, itemsConfigurer); + final ForTaskBuilder forBuilder = new ForTaskBuilder<>(newItemListBuilder()); + itemsConfigurer.accept(forBuilder); + return addTaskItem(new TaskItem(name, new Task().withForTask(forBuilder.build()))); + } + + public SELF forEach(Consumer> itemsConfigurer) { + return this.forEach(UUID.randomUUID().toString(), itemsConfigurer); + } + + public SELF switchC(String name, Consumer itemsConfigurer) { + requireNameAndConfig(name, itemsConfigurer); + final SwitchTaskBuilder switchBuilder = new SwitchTaskBuilder(); + itemsConfigurer.accept(switchBuilder); + return addTaskItem(new TaskItem(name, new Task().withSwitchTask(switchBuilder.build()))); + } + + public SELF switchC(Consumer itemsConfigurer) { + return this.switchC(UUID.randomUUID().toString(), itemsConfigurer); + } + + public SELF raise(String name, Consumer itemsConfigurer) { + requireNameAndConfig(name, itemsConfigurer); + final RaiseTaskBuilder raiseBuilder = new RaiseTaskBuilder(); + itemsConfigurer.accept(raiseBuilder); + return addTaskItem(new TaskItem(name, new Task().withRaiseTask(raiseBuilder.build()))); + } + + public SELF raise(Consumer itemsConfigurer) { + return this.raise(UUID.randomUUID().toString(), itemsConfigurer); + } + + public SELF fork(String name, Consumer itemsConfigurer) { + requireNameAndConfig(name, itemsConfigurer); + final ForkTaskBuilder forkBuilder = new ForkTaskBuilder(); + itemsConfigurer.accept(forkBuilder); + return addTaskItem(new TaskItem(name, new Task().withForkTask(forkBuilder.build()))); + } + + public SELF fork(Consumer itemsConfigurer) { + return this.fork(UUID.randomUUID().toString(), itemsConfigurer); + } + + public SELF listen(String name, Consumer itemsConfigurer) { + requireNameAndConfig(name, itemsConfigurer); + final ListenTaskBuilder listenBuilder = new ListenTaskBuilder(); + itemsConfigurer.accept(listenBuilder); + return addTaskItem(new TaskItem(name, new Task().withListenTask(listenBuilder.build()))); + } + + public SELF listen(Consumer itemsConfigurer) { + return this.listen(UUID.randomUUID().toString(), itemsConfigurer); + } + + public SELF emit(String name, Consumer itemsConfigurer) { + requireNameAndConfig(name, itemsConfigurer); + final EmitTaskBuilder emitBuilder = new EmitTaskBuilder(); + itemsConfigurer.accept(emitBuilder); + return addTaskItem(new TaskItem(name, new Task().withEmitTask(emitBuilder.build()))); + } + + public SELF emit(Consumer itemsConfigurer) { + return this.emit(UUID.randomUUID().toString(), itemsConfigurer); + } + + public SELF tryC(String name, Consumer> itemsConfigurer) { + requireNameAndConfig(name, itemsConfigurer); + final TryTaskBuilder tryBuilder = new TryTaskBuilder<>(this.newItemListBuilder()); + itemsConfigurer.accept(tryBuilder); + return addTaskItem(new TaskItem(name, new Task().withTryTask(tryBuilder.build()))); + } + + public SELF tryC(Consumer> itemsConfigurer) { + return this.tryC(UUID.randomUUID().toString(), itemsConfigurer); + } + + public SELF callHTTP(String name, Consumer itemsConfigurer) { + requireNameAndConfig(name, itemsConfigurer); + final CallHTTPTaskBuilder callHTTPBuilder = new CallHTTPTaskBuilder(); + itemsConfigurer.accept(callHTTPBuilder); + return addTaskItem( + new TaskItem( + name, new Task().withCallTask(new CallTask().withCallHTTP(callHTTPBuilder.build())))); + } + + public SELF callHTTP(Consumer itemsConfigurer) { + return this.callHTTP(UUID.randomUUID().toString(), itemsConfigurer); + } + + /** + * @return an immutable snapshot of all {@link TaskItem}s added so far + */ + public List build() { + return Collections.unmodifiableList(list); + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseWorkflowBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseWorkflowBuilder.java new file mode 100644 index 00000000..b93bdda0 --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseWorkflowBuilder.java @@ -0,0 +1,106 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.Document; +import io.serverlessworkflow.api.types.Export; +import io.serverlessworkflow.api.types.Input; +import io.serverlessworkflow.api.types.Output; +import io.serverlessworkflow.api.types.Workflow; +import java.util.function.Consumer; + +public abstract class BaseWorkflowBuilder< + SELF extends BaseWorkflowBuilder, + DO extends BaseDoTaskBuilder, + LIST 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"; + + private final Workflow workflow; + private final Document document; + + protected BaseWorkflowBuilder(final String name, final String namespace, final String version) { + this.document = new Document(); + this.document.setName(name); + this.document.setNamespace(namespace); + this.document.setVersion(version); + this.document.setDsl(DSL); + this.workflow = new Workflow(); + this.workflow.setDocument(this.document); + } + + protected abstract DO newDo(); + + protected abstract SELF self(); + + @Override + public void setOutput(Output output) { + this.workflow.setOutput(output); + } + + @Override + public void setExport(Export export) { + // TODO: build another interface with only Output and Input + throw new UnsupportedOperationException( + "export() is not supported on the workflow root; only tasks may export"); + } + + @Override + public void setInput(Input input) { + this.workflow.setInput(input); + } + + public SELF document(Consumer documentBuilderConsumer) { + final DocumentBuilder documentBuilder = new DocumentBuilder(this.document); + documentBuilderConsumer.accept(documentBuilder); + return self(); + } + + public SELF use(Consumer useBuilderConsumer) { + final UseBuilder builder = new UseBuilder(); + useBuilderConsumer.accept(builder); + this.workflow.setUse(builder.build()); + return self(); + } + + public SELF tasks(Consumer doTaskConsumer) { + final DO doTaskBuilder = newDo(); + doTaskConsumer.accept(doTaskBuilder); + this.workflow.setDo(doTaskBuilder.build().getDo()); + return self(); + } + + public SELF input(Consumer inputBuilderConsumer) { + final InputBuilder inputBuilder = new InputBuilder(); + inputBuilderConsumer.accept(inputBuilder); + this.workflow.setInput(inputBuilder.build()); + return self(); + } + + public SELF output(Consumer outputBuilderConsumer) { + final OutputBuilder outputBuilder = new OutputBuilder(); + outputBuilderConsumer.accept(outputBuilder); + this.workflow.setOutput(outputBuilder.build()); + return self(); + } + + public Workflow build() { + return this.workflow; + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DoTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DoTaskBuilder.java index 3de5cfe7..dee523b3 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DoTaskBuilder.java +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DoTaskBuilder.java @@ -15,142 +15,14 @@ */ package io.serverlessworkflow.fluent.standard; -import io.serverlessworkflow.api.types.CallTask; -import io.serverlessworkflow.api.types.DoTask; -import io.serverlessworkflow.api.types.Task; -import io.serverlessworkflow.api.types.TaskItem; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.function.Consumer; - -public class DoTaskBuilder extends TaskBaseBuilder { - - private final DoTask doTask; - private final List list; +public class DoTaskBuilder extends BaseDoTaskBuilder { DoTaskBuilder() { - this.doTask = new DoTask(); - this.list = new ArrayList<>(); - this.setTask(doTask); + super(new TaskItemListBuilder()); } @Override protected DoTaskBuilder self() { return this; } - - public DoTaskBuilder set(String name, Consumer itemsConfigurer) { - final SetTaskBuilder setBuilder = new SetTaskBuilder(); - itemsConfigurer.accept(setBuilder); - this.list.add(new TaskItem(name, new Task().withSetTask(setBuilder.build()))); - return this; - } - - public DoTaskBuilder set(Consumer itemsConfigurer) { - return this.set(UUID.randomUUID().toString(), itemsConfigurer); - } - - public DoTaskBuilder set(String name, final String expr) { - return this.set(name, s -> s.expr(expr)); - } - - public DoTaskBuilder set(final String expr) { - return this.set(UUID.randomUUID().toString(), s -> s.expr(expr)); - } - - public DoTaskBuilder forEach(String name, Consumer itemsConfigurer) { - final ForTaskBuilder forBuilder = new ForTaskBuilder(); - itemsConfigurer.accept(forBuilder); - this.list.add(new TaskItem(name, new Task().withForTask(forBuilder.build()))); - return this; - } - - public DoTaskBuilder forEach(Consumer itemsConfigurer) { - return this.forEach(UUID.randomUUID().toString(), itemsConfigurer); - } - - public DoTaskBuilder switchTask(String name, Consumer itemsConfigurer) { - final SwitchTaskBuilder switchBuilder = new SwitchTaskBuilder(); - itemsConfigurer.accept(switchBuilder); - this.list.add(new TaskItem(name, new Task().withSwitchTask(switchBuilder.build()))); - return this; - } - - public DoTaskBuilder switchTask(Consumer itemsConfigurer) { - return this.switchTask(UUID.randomUUID().toString(), itemsConfigurer); - } - - public DoTaskBuilder raise(String name, Consumer itemsConfigurer) { - final RaiseTaskBuilder raiseBuilder = new RaiseTaskBuilder(); - itemsConfigurer.accept(raiseBuilder); - this.list.add(new TaskItem(name, new Task().withRaiseTask(raiseBuilder.build()))); - return this; - } - - public DoTaskBuilder raise(Consumer itemsConfigurer) { - return this.raise(UUID.randomUUID().toString(), itemsConfigurer); - } - - public DoTaskBuilder fork(String name, Consumer itemsConfigurer) { - final ForkTaskBuilder forkBuilder = new ForkTaskBuilder(); - itemsConfigurer.accept(forkBuilder); - this.list.add(new TaskItem(name, new Task().withForkTask(forkBuilder.build()))); - return this; - } - - public DoTaskBuilder fork(Consumer itemsConfigurer) { - return this.fork(UUID.randomUUID().toString(), itemsConfigurer); - } - - public DoTaskBuilder listen(String name, Consumer itemsConfigurer) { - final ListenTaskBuilder listenBuilder = new ListenTaskBuilder(); - itemsConfigurer.accept(listenBuilder); - this.list.add(new TaskItem(name, new Task().withListenTask(listenBuilder.build()))); - return this; - } - - public DoTaskBuilder listen(Consumer itemsConfigurer) { - return this.listen(UUID.randomUUID().toString(), itemsConfigurer); - } - - public DoTaskBuilder emit(String name, Consumer itemsConfigurer) { - final EmitTaskBuilder emitBuilder = new EmitTaskBuilder(); - itemsConfigurer.accept(emitBuilder); - this.list.add(new TaskItem(name, new Task().withEmitTask(emitBuilder.build()))); - return this; - } - - public DoTaskBuilder emit(Consumer itemsConfigurer) { - return this.emit(UUID.randomUUID().toString(), itemsConfigurer); - } - - public DoTaskBuilder tryTask(String name, Consumer itemsConfigurer) { - final TryTaskBuilder tryBuilder = new TryTaskBuilder(); - itemsConfigurer.accept(tryBuilder); - this.list.add(new TaskItem(name, new Task().withTryTask(tryBuilder.build()))); - return this; - } - - public DoTaskBuilder tryTask(Consumer itemsConfigurer) { - return this.tryTask(UUID.randomUUID().toString(), itemsConfigurer); - } - - public DoTaskBuilder callHTTP(String name, Consumer itemsConfigurer) { - final CallHTTPTaskBuilder callHTTPBuilder = new CallHTTPTaskBuilder(); - itemsConfigurer.accept(callHTTPBuilder); - this.list.add( - new TaskItem( - name, new Task().withCallTask(new CallTask().withCallHTTP(callHTTPBuilder.build())))); - return this; - } - - public DoTaskBuilder callHTTP(Consumer itemsConfigurer) { - return this.callHTTP(UUID.randomUUID().toString(), itemsConfigurer); - } - - public DoTask build() { - this.doTask.setDo(this.list); - return this.doTask; - } } diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForTaskBuilder.java index e755eebd..ba0cec7c 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForTaskBuilder.java +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForTaskBuilder.java @@ -19,46 +19,49 @@ import io.serverlessworkflow.api.types.ForTaskConfiguration; import java.util.function.Consumer; -public class ForTaskBuilder extends TaskBaseBuilder { +public class ForTaskBuilder> + extends TaskBaseBuilder> { private final ForTask forTask; private final ForTaskConfiguration forTaskConfiguration; + private final T taskItemListBuilder; - ForTaskBuilder() { + ForTaskBuilder(T taskItemListBuilder) { super(); forTask = new ForTask(); forTaskConfiguration = new ForTaskConfiguration(); + this.taskItemListBuilder = taskItemListBuilder; super.setTask(forTask); } - protected ForTaskBuilder self() { + protected ForTaskBuilder self() { return this; } - public ForTaskBuilder each(String each) { + public ForTaskBuilder each(String each) { forTaskConfiguration.setEach(each); return this; } - public ForTaskBuilder in(String in) { + public ForTaskBuilder in(String in) { this.forTaskConfiguration.setIn(in); return this; } - public ForTaskBuilder at(String at) { + public ForTaskBuilder at(String at) { this.forTaskConfiguration.setAt(at); return this; } - public ForTaskBuilder whileCondition(final String expression) { + public ForTaskBuilder whileC(final String expression) { this.forTask.setWhile(expression); return this; } - public ForTaskBuilder doTasks(Consumer doBuilderConsumer) { - final DoTaskBuilder doTaskBuilder = new DoTaskBuilder(); - doBuilderConsumer.accept(doTaskBuilder); - this.forTask.setDo(doTaskBuilder.build().getDo()); + public ForTaskBuilder tasks(Consumer doBuilderConsumer) { + final T taskItemListBuilder = this.taskItemListBuilder.newItemListBuilder(); + doBuilderConsumer.accept(taskItemListBuilder); + this.forTask.setDo(taskItemListBuilder.build()); return this; } diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SwitchTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SwitchTaskBuilder.java index 12e027ee..fca04cdf 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SwitchTaskBuilder.java +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SwitchTaskBuilder.java @@ -15,11 +15,14 @@ */ package io.serverlessworkflow.fluent.standard; +import io.serverlessworkflow.api.types.FlowDirective; +import io.serverlessworkflow.api.types.FlowDirectiveEnum; import io.serverlessworkflow.api.types.SwitchCase; import io.serverlessworkflow.api.types.SwitchItem; import io.serverlessworkflow.api.types.SwitchTask; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import java.util.function.Consumer; public class SwitchTaskBuilder extends TaskBaseBuilder { @@ -39,7 +42,11 @@ protected SwitchTaskBuilder self() { return this; } - public SwitchTaskBuilder switchTask( + public SwitchTaskBuilder items(Consumer switchCaseConsumer) { + return this.items(UUID.randomUUID().toString(), switchCaseConsumer); + } + + public SwitchTaskBuilder items( final String name, Consumer switchCaseConsumer) { final SwitchCaseBuilder switchCaseBuilder = new SwitchCaseBuilder(); switchCaseConsumer.accept(switchCaseBuilder); @@ -64,8 +71,13 @@ public SwitchCaseBuilder when(String when) { return this; } - public SwitchCaseBuilder then(String then) { - this.switchCase.setWhen(then); + public SwitchCaseBuilder then(FlowDirective then) { + this.switchCase.setThen(then); + return this; + } + + public SwitchCaseBuilder then(FlowDirectiveEnum then) { + this.switchCase.setThen(new FlowDirective().withFlowDirectiveEnum(then)); return this; } diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskBaseBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskBaseBuilder.java index 31d30b3f..e4b9a623 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskBaseBuilder.java +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskBaseBuilder.java @@ -21,24 +21,42 @@ import io.serverlessworkflow.api.types.ExternalResource; import io.serverlessworkflow.api.types.FlowDirective; import io.serverlessworkflow.api.types.FlowDirectiveEnum; +import io.serverlessworkflow.api.types.Input; +import io.serverlessworkflow.api.types.Output; import io.serverlessworkflow.api.types.SchemaExternal; import io.serverlessworkflow.api.types.SchemaInline; import io.serverlessworkflow.api.types.SchemaUnion; import io.serverlessworkflow.api.types.TaskBase; import java.util.function.Consumer; -public abstract class TaskBaseBuilder> { - protected abstract T self(); - +public abstract class TaskBaseBuilder> + implements TransformationHandlers { private TaskBase task; protected TaskBaseBuilder() {} + protected abstract T self(); + protected void setTask(TaskBase task) { this.task = task; } - public T _if(String id) { + @Override + public void setInput(Input input) { + this.task.setInput(input); + } + + @Override + public void setExport(Export export) { + this.task.setExport(export); + } + + @Override + public void setOutput(Output output) { + this.task.setOutput(output); + } + + public T ifClause(String id) { this.task.setIf(id); return self(); } diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskItemListBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskItemListBuilder.java new file mode 100644 index 00000000..ebc3353c --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskItemListBuilder.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.standard; + +public class TaskItemListBuilder extends BaseTaskItemListBuilder { + + TaskItemListBuilder() { + super(); + } + + @Override + protected TaskItemListBuilder self() { + return this; + } + + @Override + protected TaskItemListBuilder newItemListBuilder() { + return new TaskItemListBuilder(); + } +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TransformationHandlers.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TransformationHandlers.java new file mode 100644 index 00000000..298f2fbe --- /dev/null +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TransformationHandlers.java @@ -0,0 +1,29 @@ +/* + * 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.standard; + +import io.serverlessworkflow.api.types.Export; +import io.serverlessworkflow.api.types.Input; +import io.serverlessworkflow.api.types.Output; + +public interface TransformationHandlers { + + void setOutput(final Output output); + + void setExport(final Export export); + + void setInput(final Input input); +} diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TryTaskBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TryTaskBuilder.java index 2e022503..677a36f0 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TryTaskBuilder.java +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TryTaskBuilder.java @@ -34,27 +34,30 @@ import io.serverlessworkflow.api.types.TryTaskCatch; import java.util.function.Consumer; -public class TryTaskBuilder extends TaskBaseBuilder { +public class TryTaskBuilder> + extends TaskBaseBuilder> { private final TryTask tryTask; + private final T doTaskBuilderFactory; - TryTaskBuilder() { + TryTaskBuilder(T doTaskBuilderFactory) { this.tryTask = new TryTask(); + this.doTaskBuilderFactory = doTaskBuilderFactory; } @Override - protected TryTaskBuilder self() { + protected TryTaskBuilder self() { return this; } - public TryTaskBuilder tryHandler(Consumer consumer) { - final DoTaskBuilder doTaskBuilder = new DoTaskBuilder(); - consumer.accept(doTaskBuilder); - this.tryTask.setTry(doTaskBuilder.build().getDo()); + public TryTaskBuilder tryHandler(Consumer consumer) { + final T taskItemListBuilder = this.doTaskBuilderFactory.newItemListBuilder(); + consumer.accept(taskItemListBuilder); + this.tryTask.setTry(taskItemListBuilder.build()); return this; } - public TryTaskBuilder catchHandler(Consumer consumer) { + public TryTaskBuilder catchHandler(Consumer consumer) { final TryTaskCatchBuilder catchBuilder = new TryTaskCatchBuilder(); consumer.accept(catchBuilder); this.tryTask.setCatch(catchBuilder.build()); diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilder.java b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilder.java index 742b5761..278e1df8 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilder.java +++ b/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilder.java @@ -15,28 +15,18 @@ */ package io.serverlessworkflow.fluent.standard; -import io.serverlessworkflow.api.types.Document; -import io.serverlessworkflow.api.types.Workflow; import java.util.UUID; -import java.util.function.Consumer; -public class WorkflowBuilder { - - private static final String DSL = "1.0.0"; - private static final String DEFAULT_VERSION = "0.0.1"; - private static final String DEFAULT_NAMESPACE = "org.acme"; - - private final Workflow workflow; - private final Document document; +public class WorkflowBuilder + extends BaseWorkflowBuilder { private WorkflowBuilder(final String name, final String namespace, final String version) { - this.document = new Document(); - this.document.setName(name); - this.document.setNamespace(namespace); - this.document.setVersion(version); - this.document.setDsl(DSL); - this.workflow = new Workflow(); - this.workflow.setDocument(this.document); + super(name, namespace, version); + } + + @Override + protected DoTaskBuilder newDo() { + return new DoTaskBuilder(); } public static WorkflowBuilder workflow( @@ -56,41 +46,8 @@ public static WorkflowBuilder workflow() { return new WorkflowBuilder(UUID.randomUUID().toString(), DEFAULT_NAMESPACE, DEFAULT_VERSION); } - public WorkflowBuilder document(Consumer documentBuilderConsumer) { - final DocumentBuilder documentBuilder = new DocumentBuilder(this.document); - documentBuilderConsumer.accept(documentBuilder); - return this; - } - - public WorkflowBuilder use(Consumer useBuilderConsumer) { - final UseBuilder builder = new UseBuilder(); - useBuilderConsumer.accept(builder); - this.workflow.setUse(builder.build()); - return this; - } - - public WorkflowBuilder doTasks(Consumer doTaskConsumer) { - final DoTaskBuilder doTaskBuilder = new DoTaskBuilder(); - doTaskConsumer.accept(doTaskBuilder); - this.workflow.setDo(doTaskBuilder.build().getDo()); + @Override + protected WorkflowBuilder self() { return this; } - - public WorkflowBuilder input(Consumer inputBuilderConsumer) { - final InputBuilder inputBuilder = new InputBuilder(); - inputBuilderConsumer.accept(inputBuilder); - this.workflow.setInput(inputBuilder.build()); - return this; - } - - public WorkflowBuilder output(Consumer outputBuilderConsumer) { - final OutputBuilder outputBuilder = new OutputBuilder(); - outputBuilderConsumer.accept(outputBuilder); - this.workflow.setOutput(outputBuilder.build()); - return this; - } - - public Workflow build() { - return this.workflow; - } } diff --git a/fluent/standard/src/test/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderTest.java b/fluent/standard/src/test/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderTest.java index e29a27b9..cdf1c3ba 100644 --- a/fluent/standard/src/test/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderTest.java +++ b/fluent/standard/src/test/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderTest.java @@ -99,7 +99,7 @@ void testUseAuthenticationsBasic() { void testDoTaskSetAndForEach() { Workflow wf = WorkflowBuilder.workflow("flowDo") - .doTasks( + .tasks( d -> d.set("initCtx", "$.foo = 'bar'") .forEach("item", f -> f.each("item").at("$.list"))) @@ -124,11 +124,11 @@ void testDoTaskSetAndForEach() { void testDoTaskMultipleTypes() { Workflow wf = WorkflowBuilder.workflow("flowMixed") - .doTasks( + .tasks( d -> d.set("init", s -> s.expr("$.init = true")) .forEach("items", f -> f.each("item").in("$.list")) - .switchTask( + .switchC( "choice", sw -> { // no-op configuration @@ -154,7 +154,7 @@ void testDoTaskMultipleTypes() { assertEquals("init", setItem.getName()); assertNotNull(setItem.getTask().getSetTask(), "SetTask should be present"); - // forEach task + // forE task TaskItem forItem = items.get(1); assertEquals("items", forItem.getName()); assertNotNull(forItem.getTask().getForTask(), "ForTask should be present"); @@ -179,7 +179,7 @@ void testDoTaskMultipleTypes() { void testDoTaskListenOne() { Workflow wf = WorkflowBuilder.workflow("flowListen") - .doTasks( + .tasks( d -> d.listen( "waitCheck", @@ -205,7 +205,7 @@ void testDoTaskListenOne() { void testDoTaskEmitEvent() { Workflow wf = WorkflowBuilder.workflow("flowEmit") - .doTasks( + .tasks( d -> d.emit( "emitEvent", @@ -256,9 +256,9 @@ void testDoTaskEmitEvent() { void testDoTaskTryCatchWithRetry() { Workflow wf = WorkflowBuilder.workflow("flowTry") - .doTasks( + .tasks( d -> - d.tryTask( + d.tryC( "tryBlock", t -> t.tryHandler(tb -> tb.set("init", s -> s.expr("$.start = true"))) @@ -304,9 +304,9 @@ void testDoTaskTryCatchWithRetry() { void testDoTaskTryCatchErrorsFiltering() { Workflow wf = WorkflowBuilder.workflow("flowCatch") - .doTasks( + .tasks( d -> - d.tryTask( + d.tryC( "tryBlock", t -> t.tryHandler(tb -> tb.set("foo", s -> s.expr("$.foo = 'bar'"))) @@ -402,7 +402,7 @@ void testWorkflowInputInlineSchemaAndFromObject() { void testDoTaskCallHTTPBasic() { Workflow wf = WorkflowBuilder.workflow("flowCallBasic") - .doTasks( + .tasks( d -> d.callHTTP( "basicCall", @@ -428,7 +428,7 @@ void testDoTaskCallHTTPBasic() { void testDoTaskCallHTTPHeadersConsumerAndMap() { Workflow wf = WorkflowBuilder.workflow("flowCallHeaders") - .doTasks( + .tasks( d -> d.callHTTP( "hdrCall", @@ -444,7 +444,7 @@ void testDoTaskCallHTTPHeadersConsumerAndMap() { Workflow wf2 = WorkflowBuilder.workflow() - .doTasks( + .tasks( d -> d.callHTTP( c -> @@ -460,7 +460,7 @@ void testDoTaskCallHTTPHeadersConsumerAndMap() { void testDoTaskCallHTTPQueryConsumerAndMap() { Workflow wf = WorkflowBuilder.workflow("flowCallQuery") - .doTasks( + .tasks( d -> d.callHTTP( "qryCall", @@ -476,7 +476,7 @@ void testDoTaskCallHTTPQueryConsumerAndMap() { Workflow wf2 = WorkflowBuilder.workflow() - .doTasks( + .tasks( d -> d.callHTTP( c -> c.method("GET").endpoint("uri").query(Map.of("q1", "x", "q2", "y")))) @@ -498,7 +498,7 @@ void testDoTaskCallHTTPQueryConsumerAndMap() { void testDoTaskCallHTTPRedirectAndOutput() { Workflow wf = WorkflowBuilder.workflow("flowCallOpts") - .doTasks( + .tasks( d -> d.callHTTP( "optCall", From e87a84986ccab50ac7118c4da20ea76dbc096fe6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Jul 2025 15:17:42 -0400 Subject: [PATCH 19/30] Bump io.github.classgraph:classgraph from 4.8.180 to 4.8.181 (#656) Bumps [io.github.classgraph:classgraph](https://github.com/classgraph/classgraph) from 4.8.180 to 4.8.181. - [Release notes](https://github.com/classgraph/classgraph/releases) - [Commits](https://github.com/classgraph/classgraph/compare/classgraph-4.8.180...classgraph-4.8.181) --- updated-dependencies: - dependency-name: io.github.classgraph:classgraph dependency-version: 4.8.181 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- generators/jackson/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generators/jackson/pom.xml b/generators/jackson/pom.xml index 81d4c254..8bddaa5e 100644 --- a/generators/jackson/pom.xml +++ b/generators/jackson/pom.xml @@ -59,7 +59,7 @@ io.github.classgraph classgraph - 4.8.180 + 4.8.181 org.apache.maven.plugin-tools From a786b9ad4c935e31b8d4087ddc151dd9cb23ad64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Jul 2025 15:17:52 -0400 Subject: [PATCH 20/30] Bump version.com.fasterxml.jackson from 2.19.1 to 2.19.2 (#655) Bumps `version.com.fasterxml.jackson` from 2.19.1 to 2.19.2. Updates `com.fasterxml.jackson:jackson-bom` from 2.19.1 to 2.19.2 - [Commits](https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.19.1...jackson-bom-2.19.2) Updates `com.fasterxml.jackson.core:jackson-core` from 2.19.1 to 2.19.2 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.19.1...jackson-core-2.19.2) Updates `com.fasterxml.jackson.core:jackson-databind` from 2.19.1 to 2.19.2 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.dataformat:jackson-dataformat-yaml` from 2.19.1 to 2.19.2 - [Commits](https://github.com/FasterXML/jackson-dataformats-text/compare/jackson-dataformats-text-2.19.1...jackson-dataformats-text-2.19.2) Updates `com.fasterxml.jackson.core:jackson-annotations` from 2.19.1 to 2.19.2 - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson:jackson-bom dependency-version: 2.19.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-version: 2.19.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-version: 2.19.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.fasterxml.jackson.dataformat:jackson-dataformat-yaml dependency-version: 2.19.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.fasterxml.jackson.core:jackson-annotations dependency-version: 2.19.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f6d140e8..e626ca23 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ 1.5.18 - 2.19.1 + 2.19.2 1.5.8 3.1.1 1.5.2 From 86679fcbc61afe88e6f1765ccffd11adee9b04aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Jul 2025 15:18:02 -0400 Subject: [PATCH 21/30] Bump org.apache.maven.plugins:maven-enforcer-plugin from 3.6.0 to 3.6.1 (#654) Bumps [org.apache.maven.plugins:maven-enforcer-plugin](https://github.com/apache/maven-enforcer) from 3.6.0 to 3.6.1. - [Release notes](https://github.com/apache/maven-enforcer/releases) - [Commits](https://github.com/apache/maven-enforcer/compare/enforcer-3.6.0...enforcer-3.6.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-enforcer-plugin dependency-version: 3.6.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e626ca23..041261ce 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ 3.6.0 3.14.0 3.1.4 - 3.6.0 + 3.6.1 3.5.3 2.27 3.2.8 From f164fa9067ec3baeafbdf936ade2b01405aba5ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Jul 2025 15:18:11 -0400 Subject: [PATCH 22/30] Bump org.apache.maven:maven-plugin-api from 3.9.10 to 3.9.11 (#653) Bumps [org.apache.maven:maven-plugin-api](https://github.com/apache/maven) from 3.9.10 to 3.9.11. - [Release notes](https://github.com/apache/maven/releases) - [Commits](https://github.com/apache/maven/compare/maven-3.9.10...maven-3.9.11) --- updated-dependencies: - dependency-name: org.apache.maven:maven-plugin-api dependency-version: 3.9.11 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 041261ce..1ac5d99c 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ ${java.version} ${java.version} UTF-8 - 3.9.10 + 3.9.11 3.2.1 From c39f66515f39de7921e09fc2e5c861a7a64e66dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Jul 2025 15:18:21 -0400 Subject: [PATCH 23/30] Bump version.org.junit.jupiter from 5.13.3 to 5.13.4 (#652) Bumps `version.org.junit.jupiter` from 5.13.3 to 5.13.4. Updates `org.junit.jupiter:junit-jupiter-api` from 5.13.3 to 5.13.4 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r5.13.3...r5.13.4) Updates `org.junit.jupiter:junit-jupiter-engine` from 5.13.3 to 5.13.4 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r5.13.3...r5.13.4) Updates `org.junit.jupiter:junit-jupiter-params` from 5.13.3 to 5.13.4 - [Release notes](https://github.com/junit-team/junit-framework/releases) - [Commits](https://github.com/junit-team/junit-framework/compare/r5.13.3...r5.13.4) --- updated-dependencies: - dependency-name: org.junit.jupiter:junit-jupiter-api dependency-version: 5.13.4 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.junit.jupiter:junit-jupiter-engine dependency-version: 5.13.4 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.junit.jupiter:junit-jupiter-params dependency-version: 5.13.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1ac5d99c..b456e6a4 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ 3.1.1 1.5.2 3.27.3 - 5.13.3 + 5.13.4 5.18.0 2.0.17 9.0.1.Final From b107003110b38bffa4a642c8baa219d83cf38f5f Mon Sep 17 00:00:00 2001 From: Ricardo Zanini <1538000+ricardozanini@users.noreply.github.com> Date: Mon, 21 Jul 2025 17:42:55 -0400 Subject: [PATCH 24/30] Rename java -> func / standard -> spec (#660) Signed-off-by: Ricardo Zanini --- experimental/lambda/pom.xml | 2 +- .../workflow/impl/FluentDSLCallTest.java | 8 ++--- experimental/pom.xml | 2 +- fluent/{java => func}/pom.xml | 6 ++-- .../fluent/func/FuncCallTaskBuilder.java} | 14 ++++---- .../fluent/func/FuncDoTaskBuilder.java} | 26 +++++++------- .../fluent/func/FuncForTaskBuilder.java} | 26 +++++++------- .../fluent/func/FuncSwitchTaskBuilder.java} | 16 ++++----- .../fluent/func/FuncTaskItemListBuilder.java} | 36 +++++++++---------- .../fluent/func/FuncTransformations.java} | 18 +++++----- .../fluent/func/FuncWorkflowBuilder.java} | 30 ++++++++-------- .../fluent/func}/JavaWorkflowBuilderTest.java | 26 +++++++------- fluent/pom.xml | 6 ++-- fluent/{standard => spec}/pom.xml | 4 +-- .../AuthenticationPolicyUnionBuilder.java | 2 +- .../fluent/spec}/BaseDoTaskBuilder.java | 2 +- .../fluent/spec}/BaseTaskItemListBuilder.java | 2 +- .../fluent/spec}/BaseWorkflowBuilder.java | 14 ++++---- .../BasicAuthenticationPolicyBuilder.java | 2 +- .../BearerAuthenticationPolicyBuilder.java | 2 +- .../fluent/spec}/CallHTTPTaskBuilder.java | 2 +- .../DigestAuthenticationPolicyBuilder.java | 2 +- .../fluent/spec}/DoTaskBuilder.java | 2 +- .../fluent/spec}/DocumentBuilder.java | 2 +- .../fluent/spec}/DurationInlineBuilder.java | 2 +- .../fluent/spec}/EmitTaskBuilder.java | 2 +- .../fluent/spec}/EventPropertiesBuilder.java | 2 +- .../fluent/spec}/ForTaskBuilder.java | 2 +- .../fluent/spec}/ForkTaskBuilder.java | 2 +- .../fluent/spec}/InputBuilder.java | 2 +- .../fluent/spec}/ListenTaskBuilder.java | 2 +- .../OAuth2AuthenticationPolicyBuilder.java | 2 +- .../fluent/spec}/OIDCBuilder.java | 2 +- ...nIdConnectAuthenticationPolicyBuilder.java | 2 +- .../fluent/spec}/OutputBuilder.java | 2 +- .../fluent/spec}/RaiseTaskBuilder.java | 2 +- .../fluent/spec}/SetTaskBuilder.java | 2 +- .../fluent/spec}/SwitchTaskBuilder.java | 2 +- .../fluent/spec}/TaskBaseBuilder.java | 2 +- .../fluent/spec}/TaskItemListBuilder.java | 2 +- .../fluent/spec}/TransformationHandlers.java | 2 +- .../fluent/spec}/TryTaskBuilder.java | 2 +- .../fluent/spec}/UriTemplateBuilder.java | 2 +- .../spec}/UseAuthenticationsBuilder.java | 2 +- .../fluent/spec}/UseBuilder.java | 2 +- .../fluent/spec}/WorkflowBuilder.java | 2 +- .../spec}/WorkflowBuilderConsumers.java | 2 +- .../fluent/spec}/WorkflowBuilderTest.java | 4 +-- 48 files changed, 151 insertions(+), 151 deletions(-) rename fluent/{java => func}/pom.xml (87%) rename fluent/{java/src/main/java/io/serverlessworkflow/fluent/java/CallTaskJavaBuilder.java => func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java} (75%) rename fluent/{java/src/main/java/io/serverlessworkflow/fluent/java/DoTaskJavaBuilder.java => func/src/main/java/io/serverlessworkflow/fluent/func/FuncDoTaskBuilder.java} (58%) rename fluent/{java/src/main/java/io/serverlessworkflow/fluent/java/ForTaskJavaBuilder.java => func/src/main/java/io/serverlessworkflow/fluent/func/FuncForTaskBuilder.java} (76%) rename fluent/{java/src/main/java/io/serverlessworkflow/fluent/java/SwitchTaskJavaBuilder.java => func/src/main/java/io/serverlessworkflow/fluent/func/FuncSwitchTaskBuilder.java} (84%) rename fluent/{java/src/main/java/io/serverlessworkflow/fluent/java/TaskItemListJavaBuilder.java => func/src/main/java/io/serverlessworkflow/fluent/func/FuncTaskItemListBuilder.java} (55%) rename fluent/{java/src/main/java/io/serverlessworkflow/fluent/java/JavaTransformationHandlers.java => func/src/main/java/io/serverlessworkflow/fluent/func/FuncTransformations.java} (74%) rename fluent/{java/src/main/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilder.java => func/src/main/java/io/serverlessworkflow/fluent/func/FuncWorkflowBuilder.java} (52%) rename fluent/{java/src/test/java/io/serverlessworkflow/fluent/java => func/src/test/java/io/serverlessworkflow/fluent/func}/JavaWorkflowBuilderTest.java (93%) rename fluent/{standard => spec}/pom.xml (89%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/AuthenticationPolicyUnionBuilder.java (98%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/BaseDoTaskBuilder.java (98%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/BaseTaskItemListBuilder.java (99%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/BaseWorkflowBuilder.java (89%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/BasicAuthenticationPolicyBuilder.java (97%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/BearerAuthenticationPolicyBuilder.java (97%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/CallHTTPTaskBuilder.java (99%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/DigestAuthenticationPolicyBuilder.java (97%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/DoTaskBuilder.java (94%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/DocumentBuilder.java (98%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/DurationInlineBuilder.java (96%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/EmitTaskBuilder.java (97%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/EventPropertiesBuilder.java (97%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/ForTaskBuilder.java (97%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/ForkTaskBuilder.java (97%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/InputBuilder.java (97%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/ListenTaskBuilder.java (99%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/OAuth2AuthenticationPolicyBuilder.java (97%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/OIDCBuilder.java (99%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/OpenIdConnectAuthenticationPolicyBuilder.java (96%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/OutputBuilder.java (97%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/RaiseTaskBuilder.java (98%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/SetTaskBuilder.java (97%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/SwitchTaskBuilder.java (98%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/TaskBaseBuilder.java (98%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/TaskItemListBuilder.java (95%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/TransformationHandlers.java (95%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/TryTaskBuilder.java (99%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/UriTemplateBuilder.java (95%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/UseAuthenticationsBuilder.java (96%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/UseBuilder.java (96%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/WorkflowBuilder.java (97%) rename fluent/{standard/src/main/java/io/serverlessworkflow/fluent/standard => spec/src/main/java/io/serverlessworkflow/fluent/spec}/WorkflowBuilderConsumers.java (96%) rename fluent/{standard/src/test/java/io/serverlessworkflow/fluent/standard => spec/src/test/java/io/serverlessworkflow/fluent/spec}/WorkflowBuilderTest.java (99%) diff --git a/experimental/lambda/pom.xml b/experimental/lambda/pom.xml index 5c20338c..242917d1 100644 --- a/experimental/lambda/pom.xml +++ b/experimental/lambda/pom.xml @@ -19,7 +19,7 @@ io.serverlessworkflow - serverlessworkflow-fluent-java + serverlessworkflow-fluent-func org.junit.jupiter diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/FluentDSLCallTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/FluentDSLCallTest.java index dd4f9a29..9706be4f 100644 --- a/experimental/lambda/src/test/java/io/serverless/workflow/impl/FluentDSLCallTest.java +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/FluentDSLCallTest.java @@ -19,7 +19,7 @@ import io.serverlessworkflow.api.types.FlowDirectiveEnum; import io.serverlessworkflow.api.types.Workflow; -import io.serverlessworkflow.fluent.java.JavaWorkflowBuilder; +import io.serverlessworkflow.fluent.func.FuncWorkflowBuilder; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowDefinition; import java.util.Collection; @@ -33,7 +33,7 @@ public class FluentDSLCallTest { void testJavaFunction() throws InterruptedException, ExecutionException { try (WorkflowApplication app = WorkflowApplication.builder().build()) { final Workflow workflow = - JavaWorkflowBuilder.workflow("testJavaCall") + FuncWorkflowBuilder.workflow("testJavaCall") .tasks(tasks -> tasks.callFn(f -> f.fn(JavaFunctions::getName))) .build(); assertThat( @@ -51,7 +51,7 @@ void testJavaFunction() throws InterruptedException, ExecutionException { void testForLoop() throws InterruptedException, ExecutionException { try (WorkflowApplication app = WorkflowApplication.builder().build()) { Workflow workflow = - JavaWorkflowBuilder.workflow() + FuncWorkflowBuilder.workflow() .tasks( t -> t.forFn( @@ -76,7 +76,7 @@ void testForLoop() throws InterruptedException, ExecutionException { void testSwitch() throws InterruptedException, ExecutionException { try (WorkflowApplication app = WorkflowApplication.builder().build()) { Workflow workflow = - JavaWorkflowBuilder.workflow() + FuncWorkflowBuilder.workflow() .tasks( tasks -> tasks diff --git a/experimental/pom.xml b/experimental/pom.xml index e269b7b0..3312207e 100644 --- a/experimental/pom.xml +++ b/experimental/pom.xml @@ -32,7 +32,7 @@ io.serverlessworkflow - serverlessworkflow-fluent-java + serverlessworkflow-fluent-func ${project.version} diff --git a/fluent/java/pom.xml b/fluent/func/pom.xml similarity index 87% rename from fluent/java/pom.xml rename to fluent/func/pom.xml index aa12ec97..d1230b34 100644 --- a/fluent/java/pom.xml +++ b/fluent/func/pom.xml @@ -9,8 +9,8 @@ 8.0.0-SNAPSHOT - Serverless Workflow :: Fluent :: Java - serverlessworkflow-fluent-java + Serverless Workflow :: Fluent :: Functional + serverlessworkflow-fluent-func 17 @@ -29,7 +29,7 @@ io.serverlessworkflow - serverlessworkflow-fluent-standard + serverlessworkflow-fluent-spec diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/CallTaskJavaBuilder.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java similarity index 75% rename from fluent/java/src/main/java/io/serverlessworkflow/fluent/java/CallTaskJavaBuilder.java rename to fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java index 8149ebba..d045e5c0 100644 --- a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/CallTaskJavaBuilder.java +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java @@ -13,29 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.java; +package io.serverlessworkflow.fluent.func; import io.serverlessworkflow.api.types.CallJava; import io.serverlessworkflow.api.types.CallTaskJava; -import io.serverlessworkflow.fluent.standard.TaskBaseBuilder; +import io.serverlessworkflow.fluent.spec.TaskBaseBuilder; import java.util.function.Function; -public class CallTaskJavaBuilder extends TaskBaseBuilder - implements JavaTransformationHandlers { +public class FuncCallTaskBuilder extends TaskBaseBuilder + implements FuncTransformations { private CallTaskJava callTaskJava; - CallTaskJavaBuilder() { + FuncCallTaskBuilder() { callTaskJava = new CallTaskJava(new CallJava() {}); super.setTask(callTaskJava.getCallJava()); } @Override - protected CallTaskJavaBuilder self() { + protected FuncCallTaskBuilder self() { return this; } - public CallTaskJavaBuilder fn(Function function) { + public FuncCallTaskBuilder fn(Function function) { this.callTaskJava = new CallTaskJava(CallJava.function(function)); super.setTask(this.callTaskJava.getCallJava()); return this; diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/DoTaskJavaBuilder.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncDoTaskBuilder.java similarity index 58% rename from fluent/java/src/main/java/io/serverlessworkflow/fluent/java/DoTaskJavaBuilder.java rename to fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncDoTaskBuilder.java index b17cf643..ec721c1e 100644 --- a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/DoTaskJavaBuilder.java +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncDoTaskBuilder.java @@ -13,49 +13,49 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.java; +package io.serverlessworkflow.fluent.func; -import io.serverlessworkflow.fluent.standard.BaseDoTaskBuilder; +import io.serverlessworkflow.fluent.spec.BaseDoTaskBuilder; import java.util.function.Consumer; -public class DoTaskJavaBuilder extends BaseDoTaskBuilder - implements JavaTransformationHandlers { +public class FuncDoTaskBuilder extends BaseDoTaskBuilder + implements FuncTransformations { - DoTaskJavaBuilder() { - super(new TaskItemListJavaBuilder()); + FuncDoTaskBuilder() { + super(new FuncTaskItemListBuilder()); } @Override - protected DoTaskJavaBuilder self() { + protected FuncDoTaskBuilder self() { return this; } - public DoTaskJavaBuilder callFn(String name, Consumer consumer) { + public FuncDoTaskBuilder callFn(String name, Consumer consumer) { this.innerListBuilder().callJava(name, consumer); return this; } - public DoTaskJavaBuilder callFn(Consumer consumer) { + public FuncDoTaskBuilder callFn(Consumer consumer) { this.innerListBuilder().callJava(consumer); return this; } - public DoTaskJavaBuilder forFn(String name, Consumer consumer) { + public FuncDoTaskBuilder forFn(String name, Consumer consumer) { this.innerListBuilder().forFn(name, consumer); return this; } - public DoTaskJavaBuilder forFn(Consumer consumer) { + public FuncDoTaskBuilder forFn(Consumer consumer) { this.innerListBuilder().forFn(consumer); return this; } - public DoTaskJavaBuilder switchFn(String name, Consumer consumer) { + public FuncDoTaskBuilder switchFn(String name, Consumer consumer) { this.innerListBuilder().switchFn(name, consumer); return this; } - public DoTaskJavaBuilder switchFn(Consumer consumer) { + public FuncDoTaskBuilder switchFn(Consumer consumer) { this.innerListBuilder().switchFn(consumer); return this; } diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/ForTaskJavaBuilder.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForTaskBuilder.java similarity index 76% rename from fluent/java/src/main/java/io/serverlessworkflow/fluent/java/ForTaskJavaBuilder.java rename to fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForTaskBuilder.java index 241272f0..c24a5af5 100644 --- a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/ForTaskJavaBuilder.java +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForTaskBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.java; +package io.serverlessworkflow.fluent.func; import io.serverlessworkflow.api.types.CallJava; import io.serverlessworkflow.api.types.CallTaskJava; @@ -21,7 +21,7 @@ import io.serverlessworkflow.api.types.ForTaskFunction; import io.serverlessworkflow.api.types.Task; import io.serverlessworkflow.api.types.TaskItem; -import io.serverlessworkflow.fluent.standard.TaskBaseBuilder; +import io.serverlessworkflow.fluent.spec.TaskBaseBuilder; import io.serverlessworkflow.impl.expressions.LoopFunction; import io.serverlessworkflow.impl.expressions.LoopPredicate; import io.serverlessworkflow.impl.expressions.LoopPredicateIndex; @@ -32,13 +32,13 @@ import java.util.function.Consumer; import java.util.function.Function; -public class ForTaskJavaBuilder extends TaskBaseBuilder - implements JavaTransformationHandlers { +public class FuncForTaskBuilder extends TaskBaseBuilder + implements FuncTransformations { private final ForTaskFunction forTaskFunction; private final List items; - ForTaskJavaBuilder() { + FuncForTaskBuilder() { this.forTaskFunction = new ForTaskFunction(); this.forTaskFunction.withFor(new ForTaskConfiguration()); this.items = new ArrayList<>(); @@ -46,26 +46,26 @@ public class ForTaskJavaBuilder extends TaskBaseBuilder } @Override - protected ForTaskJavaBuilder self() { + protected FuncForTaskBuilder self() { return this; } - public ForTaskJavaBuilder whileC(LoopPredicate predicate) { + public FuncForTaskBuilder whileC(LoopPredicate predicate) { this.forTaskFunction.withWhile(predicate); return this; } - public ForTaskJavaBuilder whileC(LoopPredicateIndex predicate) { + public FuncForTaskBuilder whileC(LoopPredicateIndex predicate) { this.forTaskFunction.withWhile(predicate); return this; } - public ForTaskJavaBuilder collection(Function> collectionF) { + public FuncForTaskBuilder collection(Function> collectionF) { this.forTaskFunction.withCollection(collectionF); return this; } - public ForTaskJavaBuilder tasks(String name, LoopFunction function) { + public FuncForTaskBuilder tasks(String name, LoopFunction function) { this.items.add( new TaskItem( name, @@ -77,12 +77,12 @@ public ForTaskJavaBuilder tasks(String name, LoopFunction fun return this; } - public ForTaskJavaBuilder tasks(LoopFunction function) { + public FuncForTaskBuilder tasks(LoopFunction function) { return this.tasks(UUID.randomUUID().toString(), function); } - public ForTaskJavaBuilder tasks(Consumer consumer) { - final TaskItemListJavaBuilder builder = new TaskItemListJavaBuilder(); + public FuncForTaskBuilder tasks(Consumer consumer) { + final FuncTaskItemListBuilder builder = new FuncTaskItemListBuilder(); consumer.accept(builder); this.items.addAll(builder.build()); return this; diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/SwitchTaskJavaBuilder.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncSwitchTaskBuilder.java similarity index 84% rename from fluent/java/src/main/java/io/serverlessworkflow/fluent/java/SwitchTaskJavaBuilder.java rename to fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncSwitchTaskBuilder.java index 8b9d98e6..da010f7c 100644 --- a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/SwitchTaskJavaBuilder.java +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncSwitchTaskBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.java; +package io.serverlessworkflow.fluent.func; import io.serverlessworkflow.api.types.FlowDirective; import io.serverlessworkflow.api.types.FlowDirectiveEnum; @@ -21,35 +21,35 @@ import io.serverlessworkflow.api.types.SwitchCaseFunction; import io.serverlessworkflow.api.types.SwitchItem; import io.serverlessworkflow.api.types.SwitchTask; -import io.serverlessworkflow.fluent.standard.TaskBaseBuilder; +import io.serverlessworkflow.fluent.spec.TaskBaseBuilder; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.function.Consumer; import java.util.function.Predicate; -public class SwitchTaskJavaBuilder extends TaskBaseBuilder - implements JavaTransformationHandlers { +public class FuncSwitchTaskBuilder extends TaskBaseBuilder + implements FuncTransformations { private final SwitchTask switchTask; private final List switchItems; - SwitchTaskJavaBuilder() { + FuncSwitchTaskBuilder() { this.switchTask = new SwitchTask(); this.switchItems = new ArrayList<>(); super.setTask(switchTask); } @Override - protected SwitchTaskJavaBuilder self() { + protected FuncSwitchTaskBuilder self() { return this; } - public SwitchTaskJavaBuilder items(Consumer consumer) { + public FuncSwitchTaskBuilder items(Consumer consumer) { return this.items(UUID.randomUUID().toString(), consumer); } - public SwitchTaskJavaBuilder items(String name, Consumer consumer) { + public FuncSwitchTaskBuilder items(String name, Consumer consumer) { final SwitchCaseJavaBuilder switchCase = new SwitchCaseJavaBuilder(); consumer.accept(switchCase); this.switchItems.add(new SwitchItem(name, switchCase.build())); diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/TaskItemListJavaBuilder.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTaskItemListBuilder.java similarity index 55% rename from fluent/java/src/main/java/io/serverlessworkflow/fluent/java/TaskItemListJavaBuilder.java rename to fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTaskItemListBuilder.java index 40fa250b..e11063da 100644 --- a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/TaskItemListJavaBuilder.java +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTaskItemListBuilder.java @@ -13,61 +13,61 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.java; +package io.serverlessworkflow.fluent.func; import io.serverlessworkflow.api.types.Task; import io.serverlessworkflow.api.types.TaskItem; -import io.serverlessworkflow.fluent.standard.BaseTaskItemListBuilder; +import io.serverlessworkflow.fluent.spec.BaseTaskItemListBuilder; import java.util.UUID; import java.util.function.Consumer; -public class TaskItemListJavaBuilder extends BaseTaskItemListBuilder { +public class FuncTaskItemListBuilder extends BaseTaskItemListBuilder { - TaskItemListJavaBuilder() { + FuncTaskItemListBuilder() { super(); } @Override - protected TaskItemListJavaBuilder self() { + protected FuncTaskItemListBuilder self() { return this; } @Override - protected TaskItemListJavaBuilder newItemListBuilder() { - return new TaskItemListJavaBuilder(); + protected FuncTaskItemListBuilder newItemListBuilder() { + return new FuncTaskItemListBuilder(); } - public TaskItemListJavaBuilder callJava(String name, Consumer consumer) { + public FuncTaskItemListBuilder callJava(String name, Consumer consumer) { this.requireNameAndConfig(name, consumer); - final CallTaskJavaBuilder callTaskJavaBuilder = new CallTaskJavaBuilder(); + final FuncCallTaskBuilder callTaskJavaBuilder = new FuncCallTaskBuilder(); consumer.accept(callTaskJavaBuilder); return addTaskItem(new TaskItem(name, new Task().withCallTask(callTaskJavaBuilder.build()))); } - public TaskItemListJavaBuilder callJava(Consumer consumer) { + public FuncTaskItemListBuilder callJava(Consumer consumer) { return this.callJava(UUID.randomUUID().toString(), consumer); } - public TaskItemListJavaBuilder forFn(String name, Consumer consumer) { + public FuncTaskItemListBuilder forFn(String name, Consumer consumer) { this.requireNameAndConfig(name, consumer); - final ForTaskJavaBuilder forTaskJavaBuilder = new ForTaskJavaBuilder(); + final FuncForTaskBuilder forTaskJavaBuilder = new FuncForTaskBuilder(); consumer.accept(forTaskJavaBuilder); return this.addTaskItem(new TaskItem(name, new Task().withForTask(forTaskJavaBuilder.build()))); } - public TaskItemListJavaBuilder forFn(Consumer consumer) { + public FuncTaskItemListBuilder forFn(Consumer consumer) { return this.forFn(UUID.randomUUID().toString(), consumer); } - public TaskItemListJavaBuilder switchFn(String name, Consumer consumer) { + public FuncTaskItemListBuilder switchFn(String name, Consumer consumer) { this.requireNameAndConfig(name, consumer); - final SwitchTaskJavaBuilder switchTaskJavaBuilder = new SwitchTaskJavaBuilder(); - consumer.accept(switchTaskJavaBuilder); + final FuncSwitchTaskBuilder funcSwitchTaskBuilder = new FuncSwitchTaskBuilder(); + consumer.accept(funcSwitchTaskBuilder); return this.addTaskItem( - new TaskItem(name, new Task().withSwitchTask(switchTaskJavaBuilder.build()))); + new TaskItem(name, new Task().withSwitchTask(funcSwitchTaskBuilder.build()))); } - public TaskItemListJavaBuilder switchFn(Consumer consumer) { + public FuncTaskItemListBuilder switchFn(Consumer consumer) { return this.switchFn(UUID.randomUUID().toString(), consumer); } } diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaTransformationHandlers.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTransformations.java similarity index 74% rename from fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaTransformationHandlers.java rename to fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTransformations.java index c8651b83..df0ba767 100644 --- a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaTransformationHandlers.java +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTransformations.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.java; +package io.serverlessworkflow.fluent.func; import io.serverlessworkflow.api.types.Export; import io.serverlessworkflow.api.types.ExportAsFunction; @@ -21,24 +21,24 @@ import io.serverlessworkflow.api.types.InputFromFunction; import io.serverlessworkflow.api.types.Output; import io.serverlessworkflow.api.types.OutputAsFunction; -import io.serverlessworkflow.fluent.standard.TransformationHandlers; +import io.serverlessworkflow.fluent.spec.TransformationHandlers; import java.util.function.Function; -public interface JavaTransformationHandlers> +public interface FuncTransformations> extends TransformationHandlers { - default B exportAsFn(Function function) { + default SELF exportAsFn(Function function) { setExport(new Export().withAs(new ExportAsFunction().withFunction(function))); - return (B) this; + return (SELF) this; } - default B inputFrom(Function function) { + default SELF inputFrom(Function function) { setInput(new Input().withFrom(new InputFromFunction().withFunction(function))); - return (B) this; + return (SELF) this; } - default B outputAs(Function function) { + default SELF outputAs(Function function) { setOutput(new Output().withAs(new OutputAsFunction().withFunction(function))); - return (B) this; + return (SELF) this; } } diff --git a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilder.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncWorkflowBuilder.java similarity index 52% rename from fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilder.java rename to fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncWorkflowBuilder.java index 5d90b598..aad21591 100644 --- a/fluent/java/src/main/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilder.java +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncWorkflowBuilder.java @@ -13,39 +13,39 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.java; +package io.serverlessworkflow.fluent.func; -import io.serverlessworkflow.fluent.standard.BaseWorkflowBuilder; +import io.serverlessworkflow.fluent.spec.BaseWorkflowBuilder; import java.util.UUID; -public class JavaWorkflowBuilder - extends BaseWorkflowBuilder - implements JavaTransformationHandlers { +public class FuncWorkflowBuilder + extends BaseWorkflowBuilder + implements FuncTransformations { - private JavaWorkflowBuilder(final String name, final String namespace, final String version) { + private FuncWorkflowBuilder(final String name, final String namespace, final String version) { super(name, namespace, version); } - public static JavaWorkflowBuilder workflow(final String name, final String namespace) { - return new JavaWorkflowBuilder(name, namespace, DEFAULT_VERSION); + public static FuncWorkflowBuilder workflow(final String name, final String namespace) { + return new FuncWorkflowBuilder(name, namespace, DEFAULT_VERSION); } - public static JavaWorkflowBuilder workflow(final String name) { - return new JavaWorkflowBuilder(name, DEFAULT_NAMESPACE, DEFAULT_VERSION); + public static FuncWorkflowBuilder workflow(final String name) { + return new FuncWorkflowBuilder(name, DEFAULT_NAMESPACE, DEFAULT_VERSION); } - public static JavaWorkflowBuilder workflow() { - return new JavaWorkflowBuilder( + public static FuncWorkflowBuilder workflow() { + return new FuncWorkflowBuilder( UUID.randomUUID().toString(), DEFAULT_NAMESPACE, DEFAULT_VERSION); } @Override - protected DoTaskJavaBuilder newDo() { - return new DoTaskJavaBuilder(); + protected FuncDoTaskBuilder newDo() { + return new FuncDoTaskBuilder(); } @Override - protected JavaWorkflowBuilder self() { + protected FuncWorkflowBuilder self() { return this; } } diff --git a/fluent/java/src/test/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilderTest.java b/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/JavaWorkflowBuilderTest.java similarity index 93% rename from fluent/java/src/test/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilderTest.java rename to fluent/func/src/test/java/io/serverlessworkflow/fluent/func/JavaWorkflowBuilderTest.java index ef037cad..a087972d 100644 --- a/fluent/java/src/test/java/io/serverlessworkflow/fluent/java/JavaWorkflowBuilderTest.java +++ b/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/JavaWorkflowBuilderTest.java @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.java; +package io.serverlessworkflow.fluent.func; import static org.junit.jupiter.api.Assertions.*; import io.serverlessworkflow.api.types.*; -import io.serverlessworkflow.fluent.standard.BaseWorkflowBuilder; +import io.serverlessworkflow.fluent.spec.BaseWorkflowBuilder; // if you reuse anything import java.util.List; import java.util.Map; @@ -26,13 +26,13 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -/** Tests for JavaWorkflowBuilder + Java DSL extensions. */ +/** Tests for FuncWorkflowBuilder + Java DSL extensions. */ class JavaWorkflowBuilderTest { @Test @DisplayName("Default Java workflow has auto-generated name and default namespace/version") void testDefaults() { - Workflow wf = JavaWorkflowBuilder.workflow().build(); + Workflow wf = FuncWorkflowBuilder.workflow().build(); assertNotNull(wf); Document doc = wf.getDocument(); assertNotNull(doc); @@ -46,7 +46,7 @@ void testDefaults() { @DisplayName("Spec style forE still works inside Java workflow") void testSpecForEachInJavaWorkflow() { Workflow wf = - JavaWorkflowBuilder.workflow("specLoopFlow") + FuncWorkflowBuilder.workflow("specLoopFlow") .tasks( d -> d.forEach(f -> f.each("pet").in("$.pets")) @@ -69,7 +69,7 @@ void testSpecForEachInJavaWorkflow() { @DisplayName("Java style forE with collection + whileC builds ForTaskFunction") void testJavaForEach() { Workflow wf = - JavaWorkflowBuilder.workflow("javaLoopFlow") + FuncWorkflowBuilder.workflow("javaLoopFlow") .tasks( d -> d.forFn( @@ -100,7 +100,7 @@ void testJavaForEach() { @DisplayName("Mixed spec and Java loops in one workflow") void testMixedLoops() { Workflow wf = - JavaWorkflowBuilder.workflow("mixed") + FuncWorkflowBuilder.workflow("mixed") .tasks( d -> d.forEach(f -> f.each("item").in("$.array")) // spec @@ -126,7 +126,7 @@ void testJavaFunctionalIO() { AtomicBoolean outputCalled = new AtomicBoolean(false); Workflow wf = - JavaWorkflowBuilder.workflow("fnIO") + FuncWorkflowBuilder.workflow("fnIO") .tasks( d -> d.set("init", s -> s.expr("$.x = 1")) @@ -189,13 +189,13 @@ void testJavaFunctionalIO() { @DisplayName("callJava task added and retains name + CallTask union") void testCallJavaTask() { Workflow wf = - JavaWorkflowBuilder.workflow("callJavaFlow") + FuncWorkflowBuilder.workflow("callJavaFlow") .tasks( d -> d.callFn( "invokeHandler", cj -> { - // configure your CallTaskJavaBuilder here + // configure your FuncCallTaskBuilder here // e.g., cj.className("com.acme.Handler").arg("key", "value"); })) .build(); @@ -207,7 +207,7 @@ void testCallJavaTask() { assertEquals("invokeHandler", ti.getName()); Task task = ti.getTask(); assertNotNull(task.getCallTask(), "CallTask should be present for callJava"); - // Additional assertions if CallTaskJavaBuilder populates fields + // Additional assertions if FuncCallTaskBuilder populates fields // e.g., assertEquals("com.acme.Handler", task.getCallTask().getCallJava().getClassName()); } @@ -215,7 +215,7 @@ void testCallJavaTask() { @DisplayName("switchCaseFn (Java variant) coexists with spec tasks") void testSwitchCaseJava() { Workflow wf = - JavaWorkflowBuilder.workflow("switchJava") + FuncWorkflowBuilder.workflow("switchJava") .tasks( d -> d.set("prepare", s -> s.expr("$.ready = true")) @@ -239,7 +239,7 @@ void testSwitchCaseJava() { @DisplayName("Combined: spec set + java forE + callJava inside nested do") void testCompositeScenario() { Workflow wf = - JavaWorkflowBuilder.workflow("composite") + FuncWorkflowBuilder.workflow("composite") .tasks( d -> d.set("init", s -> s.expr("$.val = 0")) diff --git a/fluent/pom.xml b/fluent/pom.xml index 1af30562..ff19f4d5 100644 --- a/fluent/pom.xml +++ b/fluent/pom.xml @@ -32,15 +32,15 @@ io.serverlessworkflow - serverlessworkflow-fluent-standard + serverlessworkflow-fluent-spec ${project.version} - standard - java + spec + func \ No newline at end of file diff --git a/fluent/standard/pom.xml b/fluent/spec/pom.xml similarity index 89% rename from fluent/standard/pom.xml rename to fluent/spec/pom.xml index 570d9665..5ec62a98 100644 --- a/fluent/standard/pom.xml +++ b/fluent/spec/pom.xml @@ -8,8 +8,8 @@ serverlessworkflow-fluent 8.0.0-SNAPSHOT - Serverless Workflow :: Fluent :: Standard - serverlessworkflow-fluent-standard + Serverless Workflow :: Fluent :: Spec + serverlessworkflow-fluent-spec 17 diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/AuthenticationPolicyUnionBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/AuthenticationPolicyUnionBuilder.java similarity index 98% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/AuthenticationPolicyUnionBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/AuthenticationPolicyUnionBuilder.java index 2699c809..82f84a74 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/AuthenticationPolicyUnionBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/AuthenticationPolicyUnionBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.AuthenticationPolicyUnion; import java.util.function.Consumer; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseDoTaskBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseDoTaskBuilder.java similarity index 98% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseDoTaskBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseDoTaskBuilder.java index fab86cdd..d4c70aec 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseDoTaskBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseDoTaskBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.DoTask; import java.util.function.Consumer; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseTaskItemListBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseTaskItemListBuilder.java similarity index 99% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseTaskItemListBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseTaskItemListBuilder.java index 566b493a..bb2a34dc 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseTaskItemListBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseTaskItemListBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.CallTask; import io.serverlessworkflow.api.types.Task; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseWorkflowBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseWorkflowBuilder.java similarity index 89% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseWorkflowBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseWorkflowBuilder.java index b93bdda0..e286d5fb 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BaseWorkflowBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BaseWorkflowBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.Document; import io.serverlessworkflow.api.types.Export; @@ -23,9 +23,9 @@ import java.util.function.Consumer; public abstract class BaseWorkflowBuilder< - SELF extends BaseWorkflowBuilder, - DO extends BaseDoTaskBuilder, - LIST extends BaseTaskItemListBuilder> + SELF extends BaseWorkflowBuilder, + DBuilder extends BaseDoTaskBuilder, + IListBuilder extends BaseTaskItemListBuilder> implements TransformationHandlers { public static final String DSL = "1.0.0"; @@ -45,7 +45,7 @@ protected BaseWorkflowBuilder(final String name, final String namespace, final S this.workflow.setDocument(this.document); } - protected abstract DO newDo(); + protected abstract DBuilder newDo(); protected abstract SELF self(); @@ -79,8 +79,8 @@ public SELF use(Consumer useBuilderConsumer) { return self(); } - public SELF tasks(Consumer doTaskConsumer) { - final DO doTaskBuilder = newDo(); + public SELF tasks(Consumer doTaskConsumer) { + final DBuilder doTaskBuilder = newDo(); doTaskConsumer.accept(doTaskBuilder); this.workflow.setDo(doTaskBuilder.build().getDo()); return self(); diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BasicAuthenticationPolicyBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BasicAuthenticationPolicyBuilder.java similarity index 97% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BasicAuthenticationPolicyBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BasicAuthenticationPolicyBuilder.java index c121f18f..780a976c 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BasicAuthenticationPolicyBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BasicAuthenticationPolicyBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.BasicAuthenticationPolicy; import io.serverlessworkflow.api.types.BasicAuthenticationPolicyConfiguration; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BearerAuthenticationPolicyBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BearerAuthenticationPolicyBuilder.java similarity index 97% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BearerAuthenticationPolicyBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BearerAuthenticationPolicyBuilder.java index 08e52522..04e76b41 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/BearerAuthenticationPolicyBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/BearerAuthenticationPolicyBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.BearerAuthenticationPolicy; import io.serverlessworkflow.api.types.BearerAuthenticationPolicyConfiguration; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/CallHTTPTaskBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/CallHTTPTaskBuilder.java similarity index 99% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/CallHTTPTaskBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/CallHTTPTaskBuilder.java index f2603903..95819162 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/CallHTTPTaskBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/CallHTTPTaskBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.CallHTTP; import io.serverlessworkflow.api.types.Endpoint; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DigestAuthenticationPolicyBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/DigestAuthenticationPolicyBuilder.java similarity index 97% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DigestAuthenticationPolicyBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/DigestAuthenticationPolicyBuilder.java index 8405a48b..a28f6a84 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DigestAuthenticationPolicyBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/DigestAuthenticationPolicyBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.DigestAuthenticationPolicy; import io.serverlessworkflow.api.types.DigestAuthenticationPolicyConfiguration; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DoTaskBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/DoTaskBuilder.java similarity index 94% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DoTaskBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/DoTaskBuilder.java index dee523b3..a9d4f6cb 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DoTaskBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/DoTaskBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; public class DoTaskBuilder extends BaseDoTaskBuilder { diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DocumentBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/DocumentBuilder.java similarity index 98% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DocumentBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/DocumentBuilder.java index de6d9ee3..afb13916 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DocumentBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/DocumentBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.Document; import io.serverlessworkflow.api.types.WorkflowMetadata; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DurationInlineBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/DurationInlineBuilder.java similarity index 96% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DurationInlineBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/DurationInlineBuilder.java index 23730731..f4933bb4 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/DurationInlineBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/DurationInlineBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.DurationInline; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/EmitTaskBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/EmitTaskBuilder.java similarity index 97% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/EmitTaskBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/EmitTaskBuilder.java index 77ea9d98..7a043c0a 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/EmitTaskBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/EmitTaskBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.EmitEventDefinition; import io.serverlessworkflow.api.types.EmitTask; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/EventPropertiesBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/EventPropertiesBuilder.java similarity index 97% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/EventPropertiesBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/EventPropertiesBuilder.java index 86863804..ccfccfec 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/EventPropertiesBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/EventPropertiesBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.EventData; import io.serverlessworkflow.api.types.EventProperties; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForTaskBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/ForTaskBuilder.java similarity index 97% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForTaskBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/ForTaskBuilder.java index ba0cec7c..70e97c64 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForTaskBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/ForTaskBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.ForTask; import io.serverlessworkflow.api.types.ForTaskConfiguration; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForkTaskBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/ForkTaskBuilder.java similarity index 97% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForkTaskBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/ForkTaskBuilder.java index 59754ed8..51557259 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ForkTaskBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/ForkTaskBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.ForkTask; import io.serverlessworkflow.api.types.ForkTaskConfiguration; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/InputBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/InputBuilder.java similarity index 97% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/InputBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/InputBuilder.java index 81ebfcc0..1161c82a 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/InputBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/InputBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.Endpoint; import io.serverlessworkflow.api.types.ExternalResource; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ListenTaskBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/ListenTaskBuilder.java similarity index 99% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ListenTaskBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/ListenTaskBuilder.java index 16791cab..5c722cb5 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/ListenTaskBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/ListenTaskBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.AllEventConsumptionStrategy; import io.serverlessworkflow.api.types.AnyEventConsumptionStrategy; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OAuth2AuthenticationPolicyBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/OAuth2AuthenticationPolicyBuilder.java similarity index 97% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OAuth2AuthenticationPolicyBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/OAuth2AuthenticationPolicyBuilder.java index 7eac31b4..af76983d 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OAuth2AuthenticationPolicyBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/OAuth2AuthenticationPolicyBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.OAuth2AuthenticationPolicy; import io.serverlessworkflow.api.types.OAuth2AuthenticationPolicyConfiguration; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OIDCBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/OIDCBuilder.java similarity index 99% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OIDCBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/OIDCBuilder.java index e757de8f..f12ffbaf 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OIDCBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/OIDCBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.AuthenticationPolicy; import io.serverlessworkflow.api.types.OAuth2AutenthicationData; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OpenIdConnectAuthenticationPolicyBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/OpenIdConnectAuthenticationPolicyBuilder.java similarity index 96% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OpenIdConnectAuthenticationPolicyBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/OpenIdConnectAuthenticationPolicyBuilder.java index 98b9152b..b5271006 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OpenIdConnectAuthenticationPolicyBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/OpenIdConnectAuthenticationPolicyBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.OpenIdConnectAuthenticationPolicy; import io.serverlessworkflow.api.types.OpenIdConnectAuthenticationPolicyConfiguration; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OutputBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/OutputBuilder.java similarity index 97% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OutputBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/OutputBuilder.java index 797f8872..58791172 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/OutputBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/OutputBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.Endpoint; import io.serverlessworkflow.api.types.ExternalResource; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/RaiseTaskBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/RaiseTaskBuilder.java similarity index 98% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/RaiseTaskBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/RaiseTaskBuilder.java index f2d370e9..a036cb05 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/RaiseTaskBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/RaiseTaskBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.ErrorDetails; import io.serverlessworkflow.api.types.ErrorTitle; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SetTaskBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/SetTaskBuilder.java similarity index 97% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SetTaskBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/SetTaskBuilder.java index 6c43ddfc..a3166127 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SetTaskBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/SetTaskBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.Set; import io.serverlessworkflow.api.types.SetTask; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SwitchTaskBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/SwitchTaskBuilder.java similarity index 98% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SwitchTaskBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/SwitchTaskBuilder.java index fca04cdf..e82b42c5 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/SwitchTaskBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/SwitchTaskBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.FlowDirective; import io.serverlessworkflow.api.types.FlowDirectiveEnum; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskBaseBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/TaskBaseBuilder.java similarity index 98% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskBaseBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/TaskBaseBuilder.java index e4b9a623..2bd068ce 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskBaseBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/TaskBaseBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.Endpoint; import io.serverlessworkflow.api.types.Export; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskItemListBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/TaskItemListBuilder.java similarity index 95% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskItemListBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/TaskItemListBuilder.java index ebc3353c..66cd59e2 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TaskItemListBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/TaskItemListBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; public class TaskItemListBuilder extends BaseTaskItemListBuilder { diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TransformationHandlers.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/TransformationHandlers.java similarity index 95% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TransformationHandlers.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/TransformationHandlers.java index 298f2fbe..f677fe22 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TransformationHandlers.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/TransformationHandlers.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.Export; import io.serverlessworkflow.api.types.Input; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TryTaskBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/TryTaskBuilder.java similarity index 99% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TryTaskBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/TryTaskBuilder.java index 677a36f0..a707e916 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/TryTaskBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/TryTaskBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.CatchErrors; import io.serverlessworkflow.api.types.Constant; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UriTemplateBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/UriTemplateBuilder.java similarity index 95% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UriTemplateBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/UriTemplateBuilder.java index da95f6b7..5fb74102 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UriTemplateBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/UriTemplateBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.UriTemplate; import java.net.URI; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UseAuthenticationsBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/UseAuthenticationsBuilder.java similarity index 96% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UseAuthenticationsBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/UseAuthenticationsBuilder.java index f24fec2f..d865c802 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UseAuthenticationsBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/UseAuthenticationsBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.UseAuthentications; import java.util.function.Consumer; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UseBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/UseBuilder.java similarity index 96% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UseBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/UseBuilder.java index 5b4dd836..66833111 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/UseBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/UseBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import io.serverlessworkflow.api.types.Use; import java.util.List; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilder.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/WorkflowBuilder.java similarity index 97% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilder.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/WorkflowBuilder.java index 278e1df8..374a519f 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilder.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/WorkflowBuilder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import java.util.UUID; diff --git a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderConsumers.java b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/WorkflowBuilderConsumers.java similarity index 96% rename from fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderConsumers.java rename to fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/WorkflowBuilderConsumers.java index 28652c5f..e0dbb54c 100644 --- a/fluent/standard/src/main/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderConsumers.java +++ b/fluent/spec/src/main/java/io/serverlessworkflow/fluent/spec/WorkflowBuilderConsumers.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; import java.util.function.Consumer; diff --git a/fluent/standard/src/test/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderTest.java b/fluent/spec/src/test/java/io/serverlessworkflow/fluent/spec/WorkflowBuilderTest.java similarity index 99% rename from fluent/standard/src/test/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderTest.java rename to fluent/spec/src/test/java/io/serverlessworkflow/fluent/spec/WorkflowBuilderTest.java index cdf1c3ba..49d41dbb 100644 --- a/fluent/standard/src/test/java/io/serverlessworkflow/fluent/standard/WorkflowBuilderTest.java +++ b/fluent/spec/src/test/java/io/serverlessworkflow/fluent/spec/WorkflowBuilderTest.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.fluent.standard; +package io.serverlessworkflow.fluent.spec; -import static io.serverlessworkflow.fluent.standard.WorkflowBuilderConsumers.authBasic; +import static io.serverlessworkflow.fluent.spec.WorkflowBuilderConsumers.authBasic; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; From 0a39d4ae5d2db5e9e1ca5ebedd4809148ea1d604 Mon Sep 17 00:00:00 2001 From: fjtirado Date: Tue, 22 Jul 2025 12:12:12 +0200 Subject: [PATCH 25/30] [Fix #648] Avoid splitting packages Signed-off-by: fjtirado --- api/pom.xml | 3 +- .../api/ObjectMapperFactory.java | 2 +- .../{ => func}/JavaCallExecutor.java | 5 +-- .../{ => func}/JavaForExecutorBuilder.java | 6 ++-- .../{ => func}/JavaSwitchExecutorBuilder.java | 4 +-- .../{ => func}/JavaTaskExecutorFactory.java | 4 ++- .../{ => func}/JavaExpressionFactory.java | 4 ++- .../expressions/{ => func}/JavaModel.java | 2 +- .../{ => func}/JavaModelCollection.java | 2 +- .../{ => func}/JavaModelFactory.java | 2 +- ...erlessworkflow.impl.executors.CallableTask | 2 +- ...orkflow.impl.executors.TaskExecutorFactory | 2 +- ...orkflow.impl.expressions.ExpressionFactory | 2 +- .../io/serverless/workflow/impl/CallTest.java | 8 ++--- .../serverless/workflow/impl/ModelTest.java | 2 +- .../api/types/{ => func}/CallJava.java | 3 +- .../api/types/{ => func}/CallTaskJava.java | 4 ++- .../types/{ => func}/ExportAsFunction.java | 3 +- .../api/types/{ => func}/ForTaskFunction.java | 3 +- .../types/{ => func}/InputFromFunction.java | 3 +- .../types/{ => func}/OutputAsFunction.java | 3 +- .../types/{ => func}/SwitchCaseFunction.java | 3 +- .../fluent/func/FuncCallTaskBuilder.java | 4 +-- .../fluent/func/FuncForTaskBuilder.java | 6 ++-- .../fluent/func/FuncSwitchTaskBuilder.java | 2 +- .../fluent/func/FuncTransformations.java | 6 ++-- .../fluent/func/JavaWorkflowBuilderTest.java | 10 +++++- .../generator/jackson/GeneratorUtils.java | 32 ++++++++++--------- .../generator/jackson/JacksonMixInPojo.java | 24 +++++++------- impl/core/.checkstyle | 14 -------- impl/http/.checkstyle | 14 -------- .../executors/{ => http}/HttpExecutor.java | 3 +- .../{ => http}/HttpModelConverter.java | 2 +- ...erlessworkflow.impl.executors.CallableTask | 2 +- .../{ => json}/JacksonCloudEventUtils.java | 4 +-- .../expressions/{ => jq}/JQExpression.java | 9 ++++-- .../{ => jq}/JQExpressionFactory.java | 5 ++- .../expressions/{ => jq}/JacksonModel.java | 4 +-- .../{ => jq}/JacksonModelCollection.java | 4 +-- .../{ => jq}/JacksonModelFactory.java | 6 ++-- .../{ => jq}/JacksonModelSerializer.java | 2 +- .../impl/{json => jackson}/JsonUtils.java | 2 +- .../impl/{json => jackson}/MergeUtils.java | 2 +- .../{ => json}/JsonSchemaValidator.java | 3 +- .../JsonSchemaValidatorFactory.java | 6 ++-- ...orkflow.impl.expressions.ExpressionFactory | 2 +- ...orkflow.impl.schema.SchemaValidatorFactory | 2 +- .../impl/DateTimeDescriptorTest.java | 2 +- .../impl/EventDefinitionTest.java | 2 +- .../impl/WorkflowDefinitionTest.java | 2 +- 50 files changed, 127 insertions(+), 121 deletions(-) rename experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/{ => func}/JavaCallExecutor.java (94%) rename experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/{ => func}/JavaForExecutorBuilder.java (93%) rename experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/{ => func}/JavaSwitchExecutorBuilder.java (93%) rename experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/{ => func}/JavaTaskExecutorFactory.java (89%) rename experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/{ => func}/JavaExpressionFactory.java (92%) rename experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/{ => func}/JavaModel.java (98%) rename experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/{ => func}/JavaModelCollection.java (98%) rename experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/{ => func}/JavaModelFactory.java (97%) rename experimental/types/src/main/java/io/serverlessworkflow/api/types/{ => func}/CallJava.java (97%) rename experimental/types/src/main/java/io/serverlessworkflow/api/types/{ => func}/CallTaskJava.java (90%) rename experimental/types/src/main/java/io/serverlessworkflow/api/types/{ => func}/ExportAsFunction.java (89%) rename experimental/types/src/main/java/io/serverlessworkflow/api/types/{ => func}/ForTaskFunction.java (95%) rename experimental/types/src/main/java/io/serverlessworkflow/api/types/{ => func}/InputFromFunction.java (89%) rename experimental/types/src/main/java/io/serverlessworkflow/api/types/{ => func}/OutputAsFunction.java (89%) rename experimental/types/src/main/java/io/serverlessworkflow/api/types/{ => func}/SwitchCaseFunction.java (91%) delete mode 100644 impl/core/.checkstyle delete mode 100644 impl/http/.checkstyle rename impl/http/src/main/java/io/serverlessworkflow/impl/executors/{ => http}/HttpExecutor.java (98%) rename impl/http/src/main/java/io/serverlessworkflow/impl/executors/{ => http}/HttpModelConverter.java (95%) rename impl/jackson/src/main/java/io/serverlessworkflow/impl/events/{ => json}/JacksonCloudEventUtils.java (96%) rename impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/{ => jq}/JQExpression.java (91%) rename impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/{ => jq}/JQExpressionFactory.java (88%) rename impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/{ => jq}/JacksonModel.java (97%) rename impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/{ => jq}/JacksonModelCollection.java (97%) rename impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/{ => jq}/JacksonModelFactory.java (95%) rename impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/{ => jq}/JacksonModelSerializer.java (95%) rename impl/jackson/src/main/java/io/serverlessworkflow/impl/{json => jackson}/JsonUtils.java (99%) rename impl/jackson/src/main/java/io/serverlessworkflow/impl/{json => jackson}/MergeUtils.java (98%) rename impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/{ => json}/JsonSchemaValidator.java (94%) rename impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/{ => json}/JsonSchemaValidatorFactory.java (87%) diff --git a/api/pom.xml b/api/pom.xml index 662d25c2..8b7e32a9 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -87,7 +87,8 @@ ${project.version} - io.serverlessworkflow.api.types + io.serverlessworkflow.api.types + io.serverlessworkflow.api.types.jackson diff --git a/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java b/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java index 78b1e24e..25a3f2a2 100644 --- a/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java +++ b/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java @@ -21,7 +21,7 @@ import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator.Feature; -import io.serverlessworkflow.api.types.JacksonMixInModule; +import io.serverlessworkflow.api.types.jackson.JacksonMixInModule; import io.serverlessworkflow.serialization.BeanDeserializerModifierWithValidation; import io.serverlessworkflow.serialization.URIDeserializer; import io.serverlessworkflow.serialization.URISerializer; diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaCallExecutor.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaCallExecutor.java similarity index 94% rename from experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaCallExecutor.java rename to experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaCallExecutor.java index 8d166986..7e448f77 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaCallExecutor.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaCallExecutor.java @@ -13,15 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.executors; +package io.serverlessworkflow.impl.executors.func; -import io.serverlessworkflow.api.types.CallJava; import io.serverlessworkflow.api.types.TaskBase; +import io.serverlessworkflow.api.types.func.CallJava; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowModelFactory; +import io.serverlessworkflow.impl.executors.CallableTask; import io.serverlessworkflow.impl.resources.ResourceLoader; import java.util.concurrent.CompletableFuture; diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaForExecutorBuilder.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaForExecutorBuilder.java similarity index 93% rename from experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaForExecutorBuilder.java rename to experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaForExecutorBuilder.java index faa1942c..08fd5c28 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaForExecutorBuilder.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaForExecutorBuilder.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package io.serverlessworkflow.impl.executors; +package io.serverlessworkflow.impl.executors.func; -import static io.serverlessworkflow.impl.executors.JavaCallExecutor.safeObject; +import static io.serverlessworkflow.impl.executors.func.JavaCallExecutor.safeObject; import io.serverlessworkflow.api.types.ForTask; -import io.serverlessworkflow.api.types.ForTaskFunction; import io.serverlessworkflow.api.types.Workflow; +import io.serverlessworkflow.api.types.func.ForTaskFunction; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowFilter; import io.serverlessworkflow.impl.WorkflowPosition; diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaSwitchExecutorBuilder.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaSwitchExecutorBuilder.java similarity index 93% rename from experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaSwitchExecutorBuilder.java rename to experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaSwitchExecutorBuilder.java index 3b42825d..69585a90 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaSwitchExecutorBuilder.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaSwitchExecutorBuilder.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package io.serverlessworkflow.impl.executors; +package io.serverlessworkflow.impl.executors.func; import io.serverlessworkflow.api.types.SwitchCase; -import io.serverlessworkflow.api.types.SwitchCaseFunction; import io.serverlessworkflow.api.types.SwitchTask; import io.serverlessworkflow.api.types.Workflow; +import io.serverlessworkflow.api.types.func.SwitchCaseFunction; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowFilter; import io.serverlessworkflow.impl.WorkflowPosition; diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaTaskExecutorFactory.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaTaskExecutorFactory.java similarity index 89% rename from experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaTaskExecutorFactory.java rename to experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaTaskExecutorFactory.java index 26177287..8dfce9de 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/JavaTaskExecutorFactory.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaTaskExecutorFactory.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.executors; +package io.serverlessworkflow.impl.executors.func; import io.serverlessworkflow.api.types.Task; import io.serverlessworkflow.api.types.TaskBase; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowPosition; +import io.serverlessworkflow.impl.executors.DefaultTaskExecutorFactory; +import io.serverlessworkflow.impl.executors.TaskExecutorBuilder; import io.serverlessworkflow.impl.resources.ResourceLoader; public class JavaTaskExecutorFactory extends DefaultTaskExecutorFactory { diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaExpressionFactory.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaExpressionFactory.java similarity index 92% rename from experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaExpressionFactory.java rename to experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaExpressionFactory.java index a6e89ae8..2f1820a0 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaExpressionFactory.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaExpressionFactory.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.expressions; +package io.serverlessworkflow.impl.expressions.func; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowFilter; import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowModelFactory; +import io.serverlessworkflow.impl.expressions.Expression; +import io.serverlessworkflow.impl.expressions.ExpressionFactory; import java.util.function.BiFunction; import java.util.function.BiPredicate; import java.util.function.Function; diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModel.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModel.java similarity index 98% rename from experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModel.java rename to experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModel.java index 00481d54..bb236ed7 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModel.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModel.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.expressions; +package io.serverlessworkflow.impl.expressions.func; import io.cloudevents.CloudEventData; import io.serverlessworkflow.impl.WorkflowModel; diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelCollection.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModelCollection.java similarity index 98% rename from experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelCollection.java rename to experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModelCollection.java index 501cc287..0b9c914f 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelCollection.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModelCollection.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.expressions; +package io.serverlessworkflow.impl.expressions.func; import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowModelCollection; diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelFactory.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModelFactory.java similarity index 97% rename from experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelFactory.java rename to experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModelFactory.java index 6ca4cc06..59975e50 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/JavaModelFactory.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaModelFactory.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.expressions; +package io.serverlessworkflow.impl.expressions.func; import io.cloudevents.CloudEvent; import io.cloudevents.CloudEventData; diff --git a/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.CallableTask b/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.CallableTask index e413059c..780aa17e 100644 --- a/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.CallableTask +++ b/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.CallableTask @@ -1 +1 @@ -io.serverlessworkflow.impl.executors.JavaCallExecutor \ No newline at end of file +io.serverlessworkflow.impl.executors.func.JavaCallExecutor \ No newline at end of file diff --git a/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.TaskExecutorFactory b/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.TaskExecutorFactory index 6fd5dc15..710fa4db 100644 --- a/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.TaskExecutorFactory +++ b/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.TaskExecutorFactory @@ -1 +1 @@ -io.serverlessworkflow.impl.executors.JavaTaskExecutorFactory \ No newline at end of file +io.serverlessworkflow.impl.executors.func.JavaTaskExecutorFactory \ No newline at end of file diff --git a/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory b/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory index 171ce036..722ea0f0 100644 --- a/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory +++ b/experimental/lambda/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory @@ -1 +1 @@ -io.serverlessworkflow.impl.expressions.JavaExpressionFactory \ No newline at end of file +io.serverlessworkflow.impl.expressions.func.JavaExpressionFactory \ No newline at end of file diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java index 7d95410b..04543004 100644 --- a/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java @@ -17,19 +17,19 @@ import static org.assertj.core.api.Assertions.assertThat; -import io.serverlessworkflow.api.types.CallJava; -import io.serverlessworkflow.api.types.CallTaskJava; import io.serverlessworkflow.api.types.Document; import io.serverlessworkflow.api.types.FlowDirective; import io.serverlessworkflow.api.types.FlowDirectiveEnum; import io.serverlessworkflow.api.types.ForTaskConfiguration; -import io.serverlessworkflow.api.types.ForTaskFunction; -import io.serverlessworkflow.api.types.SwitchCaseFunction; import io.serverlessworkflow.api.types.SwitchItem; import io.serverlessworkflow.api.types.SwitchTask; 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.func.SwitchCaseFunction; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowDefinition; import java.util.Collection; diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/ModelTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/ModelTest.java index 7702fffe..8c917dbe 100644 --- a/experimental/lambda/src/test/java/io/serverless/workflow/impl/ModelTest.java +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/ModelTest.java @@ -20,7 +20,6 @@ import io.serverlessworkflow.api.types.Document; import io.serverlessworkflow.api.types.DurationInline; import io.serverlessworkflow.api.types.Output; -import io.serverlessworkflow.api.types.OutputAsFunction; import io.serverlessworkflow.api.types.Set; import io.serverlessworkflow.api.types.SetTask; import io.serverlessworkflow.api.types.SetTaskConfiguration; @@ -29,6 +28,7 @@ import io.serverlessworkflow.api.types.TimeoutAfter; import io.serverlessworkflow.api.types.WaitTask; import io.serverlessworkflow.api.types.Workflow; +import io.serverlessworkflow.api.types.func.OutputAsFunction; import io.serverlessworkflow.impl.WorkflowApplication; import java.util.List; import java.util.Map; diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/CallJava.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallJava.java similarity index 97% rename from experimental/types/src/main/java/io/serverlessworkflow/api/types/CallJava.java rename to experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallJava.java index c3115de2..4158ee57 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/CallJava.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallJava.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.api.types; +package io.serverlessworkflow.api.types.func; +import io.serverlessworkflow.api.types.TaskBase; import io.serverlessworkflow.impl.expressions.LoopFunction; import io.serverlessworkflow.impl.expressions.LoopFunctionIndex; import java.util.function.Consumer; diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/CallTaskJava.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallTaskJava.java similarity index 90% rename from experimental/types/src/main/java/io/serverlessworkflow/api/types/CallTaskJava.java rename to experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallTaskJava.java index e1b406ec..cde6281f 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/CallTaskJava.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/CallTaskJava.java @@ -13,7 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.api.types; +package io.serverlessworkflow.api.types.func; + +import io.serverlessworkflow.api.types.CallTask; public class CallTaskJava extends CallTask { diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/ExportAsFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ExportAsFunction.java similarity index 89% rename from experimental/types/src/main/java/io/serverlessworkflow/api/types/ExportAsFunction.java rename to experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ExportAsFunction.java index fd279cd2..e7879af3 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/ExportAsFunction.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ExportAsFunction.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.api.types; +package io.serverlessworkflow.api.types.func; +import io.serverlessworkflow.api.types.ExportAs; import java.util.function.Function; public class ExportAsFunction extends ExportAs { diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/ForTaskFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ForTaskFunction.java similarity index 95% rename from experimental/types/src/main/java/io/serverlessworkflow/api/types/ForTaskFunction.java rename to experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ForTaskFunction.java index 00e29614..779dccd4 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/ForTaskFunction.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/ForTaskFunction.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.api.types; +package io.serverlessworkflow.api.types.func; +import io.serverlessworkflow.api.types.ForTask; import io.serverlessworkflow.impl.expressions.LoopPredicate; import io.serverlessworkflow.impl.expressions.LoopPredicateIndex; import java.util.Collection; diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/InputFromFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/InputFromFunction.java similarity index 89% rename from experimental/types/src/main/java/io/serverlessworkflow/api/types/InputFromFunction.java rename to experimental/types/src/main/java/io/serverlessworkflow/api/types/func/InputFromFunction.java index abea6daa..49249bc2 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/InputFromFunction.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/InputFromFunction.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.api.types; +package io.serverlessworkflow.api.types.func; +import io.serverlessworkflow.api.types.InputFrom; import java.util.function.Function; public class InputFromFunction extends InputFrom { diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/OutputAsFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/OutputAsFunction.java similarity index 89% rename from experimental/types/src/main/java/io/serverlessworkflow/api/types/OutputAsFunction.java rename to experimental/types/src/main/java/io/serverlessworkflow/api/types/func/OutputAsFunction.java index 7ae183a4..b593cb13 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/OutputAsFunction.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/OutputAsFunction.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.api.types; +package io.serverlessworkflow.api.types.func; +import io.serverlessworkflow.api.types.OutputAs; import java.util.function.Function; public class OutputAsFunction extends OutputAs { diff --git a/experimental/types/src/main/java/io/serverlessworkflow/api/types/SwitchCaseFunction.java b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/SwitchCaseFunction.java similarity index 91% rename from experimental/types/src/main/java/io/serverlessworkflow/api/types/SwitchCaseFunction.java rename to experimental/types/src/main/java/io/serverlessworkflow/api/types/func/SwitchCaseFunction.java index 51b4175a..234fcc80 100644 --- a/experimental/types/src/main/java/io/serverlessworkflow/api/types/SwitchCaseFunction.java +++ b/experimental/types/src/main/java/io/serverlessworkflow/api/types/func/SwitchCaseFunction.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.api.types; +package io.serverlessworkflow.api.types.func; +import io.serverlessworkflow.api.types.SwitchCase; import java.util.function.Predicate; public class SwitchCaseFunction extends SwitchCase { diff --git a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java index d045e5c0..a8bcf08b 100644 --- a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncCallTaskBuilder.java @@ -15,8 +15,8 @@ */ package io.serverlessworkflow.fluent.func; -import io.serverlessworkflow.api.types.CallJava; -import io.serverlessworkflow.api.types.CallTaskJava; +import io.serverlessworkflow.api.types.func.CallJava; +import io.serverlessworkflow.api.types.func.CallTaskJava; import io.serverlessworkflow.fluent.spec.TaskBaseBuilder; import java.util.function.Function; diff --git a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForTaskBuilder.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForTaskBuilder.java index c24a5af5..921ec920 100644 --- a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForTaskBuilder.java +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncForTaskBuilder.java @@ -15,12 +15,12 @@ */ package io.serverlessworkflow.fluent.func; -import io.serverlessworkflow.api.types.CallJava; -import io.serverlessworkflow.api.types.CallTaskJava; import io.serverlessworkflow.api.types.ForTaskConfiguration; -import io.serverlessworkflow.api.types.ForTaskFunction; 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.fluent.spec.TaskBaseBuilder; import io.serverlessworkflow.impl.expressions.LoopFunction; import io.serverlessworkflow.impl.expressions.LoopPredicate; diff --git a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncSwitchTaskBuilder.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncSwitchTaskBuilder.java index da010f7c..aff365f7 100644 --- a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncSwitchTaskBuilder.java +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncSwitchTaskBuilder.java @@ -18,9 +18,9 @@ import io.serverlessworkflow.api.types.FlowDirective; import io.serverlessworkflow.api.types.FlowDirectiveEnum; import io.serverlessworkflow.api.types.SwitchCase; -import io.serverlessworkflow.api.types.SwitchCaseFunction; import io.serverlessworkflow.api.types.SwitchItem; import io.serverlessworkflow.api.types.SwitchTask; +import io.serverlessworkflow.api.types.func.SwitchCaseFunction; import io.serverlessworkflow.fluent.spec.TaskBaseBuilder; import java.util.ArrayList; import java.util.List; diff --git a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTransformations.java b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTransformations.java index df0ba767..f1516f7f 100644 --- a/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTransformations.java +++ b/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncTransformations.java @@ -16,11 +16,11 @@ package io.serverlessworkflow.fluent.func; import io.serverlessworkflow.api.types.Export; -import io.serverlessworkflow.api.types.ExportAsFunction; import io.serverlessworkflow.api.types.Input; -import io.serverlessworkflow.api.types.InputFromFunction; import io.serverlessworkflow.api.types.Output; -import io.serverlessworkflow.api.types.OutputAsFunction; +import io.serverlessworkflow.api.types.func.ExportAsFunction; +import io.serverlessworkflow.api.types.func.InputFromFunction; +import io.serverlessworkflow.api.types.func.OutputAsFunction; import io.serverlessworkflow.fluent.spec.TransformationHandlers; import java.util.function.Function; diff --git a/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/JavaWorkflowBuilderTest.java b/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/JavaWorkflowBuilderTest.java index a087972d..529a9b8d 100644 --- a/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/JavaWorkflowBuilderTest.java +++ b/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/JavaWorkflowBuilderTest.java @@ -17,7 +17,15 @@ import static org.junit.jupiter.api.Assertions.*; -import io.serverlessworkflow.api.types.*; +import io.serverlessworkflow.api.types.Document; +import io.serverlessworkflow.api.types.Export; +import io.serverlessworkflow.api.types.Output; +import io.serverlessworkflow.api.types.SetTask; +import io.serverlessworkflow.api.types.Task; +import io.serverlessworkflow.api.types.TaskBase; +import io.serverlessworkflow.api.types.TaskItem; +import io.serverlessworkflow.api.types.Workflow; +import io.serverlessworkflow.api.types.func.*; import io.serverlessworkflow.fluent.spec.BaseWorkflowBuilder; // if you reuse anything import java.util.List; diff --git a/generators/jackson/src/main/java/io/serverlessworkflow/generator/jackson/GeneratorUtils.java b/generators/jackson/src/main/java/io/serverlessworkflow/generator/jackson/GeneratorUtils.java index 9463106f..5687de8c 100644 --- a/generators/jackson/src/main/java/io/serverlessworkflow/generator/jackson/GeneratorUtils.java +++ b/generators/jackson/src/main/java/io/serverlessworkflow/generator/jackson/GeneratorUtils.java @@ -28,6 +28,7 @@ import com.sun.codemodel.JInvocation; import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; +import com.sun.codemodel.JPackage; import com.sun.codemodel.JType; import com.sun.codemodel.JVar; import io.serverlessworkflow.serialization.DeserializeHelper; @@ -48,14 +49,14 @@ public interface DeserializerFiller { void accept(JMethod method, JVar parserParam); } - public static JDefinedClass serializerClass(JClass relatedClass) + public static JDefinedClass serializerClass(JPackage jPackage, JClass relatedClass) throws JClassAlreadyExistsException { - return createClass(relatedClass, JsonSerializer.class, "Serializer"); + return createClass(jPackage, relatedClass, JsonSerializer.class, "Serializer"); } - public static JDefinedClass deserializerClass(JClass relatedClass) + public static JDefinedClass deserializerClass(JPackage jPackage, JClass relatedClass) throws JClassAlreadyExistsException { - return createClass(relatedClass, JsonDeserializer.class, "Deserializer"); + return createClass(jPackage, relatedClass, JsonDeserializer.class, "Deserializer"); } public static void fillSerializer( @@ -80,17 +81,16 @@ public static void fillDeserializer( } private static JDefinedClass createClass( - JClass relatedClass, Class serializerClass, String suffix) + JPackage jPackage, JClass relatedClass, Class serializerClass, String suffix) throws JClassAlreadyExistsException { - JDefinedClass definedClass = - relatedClass._package()._class(JMod.NONE, relatedClass.name() + suffix); + JDefinedClass definedClass = jPackage._class(JMod.NONE, relatedClass.name() + suffix); definedClass._extends(definedClass.owner().ref(serializerClass).narrow(relatedClass)); return definedClass; } - public static JDefinedClass generateSerializer(JClass relatedClass) + public static JDefinedClass generateSerializer(JPackage jPackage, JClass relatedClass) throws JClassAlreadyExistsException { - JDefinedClass definedClass = GeneratorUtils.serializerClass(relatedClass); + JDefinedClass definedClass = GeneratorUtils.serializerClass(jPackage, relatedClass); GeneratorUtils.fillSerializer( definedClass, relatedClass, @@ -104,8 +104,9 @@ public static JDefinedClass generateSerializer(JClass relatedClass) } public static JDefinedClass generateDeserializer( - JClass relatedClass, Collection oneOfTypes) throws JClassAlreadyExistsException { - JDefinedClass definedClass = GeneratorUtils.deserializerClass(relatedClass); + JPackage jPackage, JClass relatedClass, Collection oneOfTypes) + throws JClassAlreadyExistsException { + JDefinedClass definedClass = GeneratorUtils.deserializerClass(jPackage, relatedClass); GeneratorUtils.fillDeserializer( definedClass, relatedClass, @@ -124,9 +125,10 @@ public static JDefinedClass generateDeserializer( return definedClass; } - public static JDefinedClass generateDeserializer(JClass relatedClass, JType propertyType) + public static JDefinedClass generateDeserializer( + JPackage jPackage, JClass relatedClass, JType propertyType) throws JClassAlreadyExistsException { - JDefinedClass definedClass = GeneratorUtils.deserializerClass(relatedClass); + JDefinedClass definedClass = GeneratorUtils.deserializerClass(jPackage, relatedClass); GeneratorUtils.fillDeserializer( definedClass, relatedClass, @@ -145,9 +147,9 @@ public static JDefinedClass generateDeserializer(JClass relatedClass, JType prop } public static JDefinedClass generateSerializer( - JClass relatedClass, String keyMethod, String valueMethod) + JPackage jPackage, JClass relatedClass, String keyMethod, String valueMethod) throws JClassAlreadyExistsException { - JDefinedClass definedClass = GeneratorUtils.serializerClass(relatedClass); + JDefinedClass definedClass = GeneratorUtils.serializerClass(jPackage, relatedClass); GeneratorUtils.fillSerializer( definedClass, relatedClass, 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 b80238ed..2e7b9107 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 @@ -75,14 +75,11 @@ public class JacksonMixInPojo extends AbstractMojo { defaultValue = "${project.build.directory}/generated-sources/jacksonmixinpojo") private File outputDirectory; - /** - * Package name used for generated Java classes (for types where a fully qualified name has not - * been supplied in the schema using the 'javaType' property). - * - * @since 0.1.0 - */ + @Parameter(property = "jsonschema2pojo.srcPackage") + private String srcPackage; + @Parameter(property = "jsonschema2pojo.targetPackage") - private String targetPackage = ""; + private String targetPackage; private static final String MIXIN_METHOD = "setMixInAnnotation"; private static final String ADD_PROPERTIES_METHOD = "getAdditionalProperties"; @@ -104,7 +101,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { new ClassGraph() .enableAnnotationInfo() .enableMethodInfo() - .acceptPackages(targetPackage) + .acceptPackages(srcPackage) .scan()) { codeModel = new JCodeModel(); rootPackage = codeModel._package(targetPackage); @@ -183,10 +180,12 @@ private void buildItemMixIn(ClassInfo classInfo, JDefinedClass mixClass) .param( "using", GeneratorUtils.generateSerializer( - relClass, keyMethod.getName(), valueMethod.getName())); + rootPackage, relClass, keyMethod.getName(), valueMethod.getName())); mixClass .annotate(JsonDeserialize.class) - .param("using", GeneratorUtils.generateDeserializer(relClass, getReturnType(valueMethod))); + .param( + "using", + GeneratorUtils.generateDeserializer(rootPackage, relClass, getReturnType(valueMethod))); } private void buildUnionMixIn(ClassInfo unionClassInfo, JDefinedClass unionMixClass) @@ -194,12 +193,13 @@ private void buildUnionMixIn(ClassInfo unionClassInfo, JDefinedClass unionMixCla JClass unionClass = codeModel.ref(unionClassInfo.getName()); unionMixClass .annotate(JsonSerialize.class) - .param("using", GeneratorUtils.generateSerializer(unionClass)); + .param("using", GeneratorUtils.generateSerializer(rootPackage, unionClass)); unionMixClass .annotate(JsonDeserialize.class) .param( "using", - GeneratorUtils.generateDeserializer(unionClass, getUnionClasses(unionClassInfo))); + GeneratorUtils.generateDeserializer( + rootPackage, unionClass, getUnionClasses(unionClassInfo))); } private void buildEnumMixIn(ClassInfo classInfo, JDefinedClass mixClass) diff --git a/impl/core/.checkstyle b/impl/core/.checkstyle deleted file mode 100644 index cdd4188c..00000000 --- a/impl/core/.checkstyle +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/impl/http/.checkstyle b/impl/http/.checkstyle deleted file mode 100644 index a33f7d91..00000000 --- a/impl/http/.checkstyle +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/HttpExecutor.java b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutor.java similarity index 98% rename from impl/http/src/main/java/io/serverlessworkflow/impl/executors/HttpExecutor.java rename to impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutor.java index cede1880..fe2fe971 100644 --- a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/HttpExecutor.java +++ b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutor.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.executors; +package io.serverlessworkflow.impl.executors.http; import io.serverlessworkflow.api.types.CallHTTP; import io.serverlessworkflow.api.types.Endpoint; @@ -29,6 +29,7 @@ import io.serverlessworkflow.impl.WorkflowFilter; import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowUtils; +import io.serverlessworkflow.impl.executors.CallableTask; import io.serverlessworkflow.impl.expressions.Expression; import io.serverlessworkflow.impl.expressions.ExpressionFactory; import io.serverlessworkflow.impl.resources.ResourceLoader; diff --git a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/HttpModelConverter.java b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpModelConverter.java similarity index 95% rename from impl/http/src/main/java/io/serverlessworkflow/impl/executors/HttpModelConverter.java rename to impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpModelConverter.java index a8c264dd..161db2ec 100644 --- a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/HttpModelConverter.java +++ b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpModelConverter.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.executors; +package io.serverlessworkflow.impl.executors.http; import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowModelFactory; diff --git a/impl/http/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.CallableTask b/impl/http/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.CallableTask index 7d5e6bf9..2a9aac2d 100644 --- a/impl/http/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.CallableTask +++ b/impl/http/src/main/resources/META-INF/services/io.serverlessworkflow.impl.executors.CallableTask @@ -1 +1 @@ -io.serverlessworkflow.impl.executors.HttpExecutor \ No newline at end of file +io.serverlessworkflow.impl.executors.http.HttpExecutor \ No newline at end of file diff --git a/impl/jackson/src/main/java/io/serverlessworkflow/impl/events/JacksonCloudEventUtils.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/events/json/JacksonCloudEventUtils.java similarity index 96% rename from impl/jackson/src/main/java/io/serverlessworkflow/impl/events/JacksonCloudEventUtils.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/events/json/JacksonCloudEventUtils.java index f51f4547..10ea02be 100644 --- a/impl/jackson/src/main/java/io/serverlessworkflow/impl/events/JacksonCloudEventUtils.java +++ b/impl/jackson/src/main/java/io/serverlessworkflow/impl/events/json/JacksonCloudEventUtils.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.events; +package io.serverlessworkflow.impl.events.json; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.NullNode; @@ -21,7 +21,7 @@ import io.cloudevents.CloudEvent; import io.cloudevents.CloudEventData; import io.cloudevents.jackson.JsonCloudEventData; -import io.serverlessworkflow.impl.json.JsonUtils; +import io.serverlessworkflow.impl.jackson.JsonUtils; import java.io.IOException; import java.io.UncheckedIOException; import java.time.OffsetDateTime; diff --git a/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JQExpression.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/jq/JQExpression.java similarity index 91% rename from impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JQExpression.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/jq/JQExpression.java index e55f9877..f7fd2504 100644 --- a/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JQExpression.java +++ b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/jq/JQExpression.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.expressions; +package io.serverlessworkflow.impl.expressions.jq; -import static io.serverlessworkflow.impl.json.JsonUtils.modelToJson; +import static io.serverlessworkflow.impl.jackson.JsonUtils.modelToJson; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -23,7 +23,10 @@ import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowModelFactory; -import io.serverlessworkflow.impl.json.JsonUtils; +import io.serverlessworkflow.impl.expressions.Expression; +import io.serverlessworkflow.impl.expressions.TaskDescriptor; +import io.serverlessworkflow.impl.expressions.WorkflowDescriptor; +import io.serverlessworkflow.impl.jackson.JsonUtils; import java.util.function.Supplier; import net.thisptr.jackson.jq.Output; import net.thisptr.jackson.jq.Scope; diff --git a/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JQExpressionFactory.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/jq/JQExpressionFactory.java similarity index 88% rename from impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JQExpressionFactory.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/jq/JQExpressionFactory.java index e5e9c481..2909eb7b 100644 --- a/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JQExpressionFactory.java +++ b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/jq/JQExpressionFactory.java @@ -13,9 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.expressions; +package io.serverlessworkflow.impl.expressions.jq; import io.serverlessworkflow.impl.WorkflowModelFactory; +import io.serverlessworkflow.impl.expressions.Expression; +import io.serverlessworkflow.impl.expressions.ExpressionUtils; +import io.serverlessworkflow.impl.expressions.ObjectExpressionFactory; import java.util.function.Supplier; import net.thisptr.jackson.jq.BuiltinFunctionLoader; import net.thisptr.jackson.jq.Scope; diff --git a/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModel.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/jq/JacksonModel.java similarity index 97% rename from impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModel.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/jq/JacksonModel.java index 1deb520e..dc71fcb2 100644 --- a/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModel.java +++ b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/jq/JacksonModel.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.expressions; +package io.serverlessworkflow.impl.expressions.jq; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.annotation.JsonSerialize; @@ -23,7 +23,7 @@ import io.cloudevents.CloudEventData; import io.cloudevents.jackson.JsonCloudEventData; import io.serverlessworkflow.impl.WorkflowModel; -import io.serverlessworkflow.impl.json.JsonUtils; +import io.serverlessworkflow.impl.jackson.JsonUtils; import java.time.Instant; import java.time.OffsetDateTime; import java.time.ZoneOffset; diff --git a/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelCollection.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/jq/JacksonModelCollection.java similarity index 97% rename from impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelCollection.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/jq/JacksonModelCollection.java index 43da790a..70dee439 100644 --- a/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelCollection.java +++ b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/jq/JacksonModelCollection.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.expressions; +package io.serverlessworkflow.impl.expressions.jq; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowModelCollection; -import io.serverlessworkflow.impl.json.JsonUtils; +import io.serverlessworkflow.impl.jackson.JsonUtils; import java.util.Collection; import java.util.Iterator; import java.util.Optional; diff --git a/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelFactory.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/jq/JacksonModelFactory.java similarity index 95% rename from impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelFactory.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/jq/JacksonModelFactory.java index 00906165..866c3264 100644 --- a/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelFactory.java +++ b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/jq/JacksonModelFactory.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.expressions; +package io.serverlessworkflow.impl.expressions.jq; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.DoubleNode; @@ -27,8 +27,8 @@ import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowModelCollection; import io.serverlessworkflow.impl.WorkflowModelFactory; -import io.serverlessworkflow.impl.events.JacksonCloudEventUtils; -import io.serverlessworkflow.impl.json.JsonUtils; +import io.serverlessworkflow.impl.events.json.JacksonCloudEventUtils; +import io.serverlessworkflow.impl.jackson.JsonUtils; import java.math.BigDecimal; import java.time.OffsetDateTime; import java.util.Map; diff --git a/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelSerializer.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/jq/JacksonModelSerializer.java similarity index 95% rename from impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelSerializer.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/jq/JacksonModelSerializer.java index 874bd674..7d4b07c9 100644 --- a/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/JacksonModelSerializer.java +++ b/impl/jackson/src/main/java/io/serverlessworkflow/impl/expressions/jq/JacksonModelSerializer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.expressions; +package io.serverlessworkflow.impl.expressions.jq; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; diff --git a/impl/jackson/src/main/java/io/serverlessworkflow/impl/json/JsonUtils.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/jackson/JsonUtils.java similarity index 99% rename from impl/jackson/src/main/java/io/serverlessworkflow/impl/json/JsonUtils.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/jackson/JsonUtils.java index 42a32b7d..04822457 100644 --- a/impl/jackson/src/main/java/io/serverlessworkflow/impl/json/JsonUtils.java +++ b/impl/jackson/src/main/java/io/serverlessworkflow/impl/jackson/JsonUtils.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.json; +package io.serverlessworkflow.impl.jackson; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; diff --git a/impl/jackson/src/main/java/io/serverlessworkflow/impl/json/MergeUtils.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/jackson/MergeUtils.java similarity index 98% rename from impl/jackson/src/main/java/io/serverlessworkflow/impl/json/MergeUtils.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/jackson/MergeUtils.java index a3615d35..aca5d63d 100644 --- a/impl/jackson/src/main/java/io/serverlessworkflow/impl/json/MergeUtils.java +++ b/impl/jackson/src/main/java/io/serverlessworkflow/impl/jackson/MergeUtils.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.json; +package io.serverlessworkflow.impl.jackson; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; diff --git a/impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/JsonSchemaValidator.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/json/JsonSchemaValidator.java similarity index 94% rename from impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/JsonSchemaValidator.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/json/JsonSchemaValidator.java index 142faf73..c4859ba9 100644 --- a/impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/JsonSchemaValidator.java +++ b/impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/json/JsonSchemaValidator.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.schema; +package io.serverlessworkflow.impl.schema.json; import com.fasterxml.jackson.databind.JsonNode; import com.networknt.schema.JsonSchema; @@ -21,6 +21,7 @@ import com.networknt.schema.SpecVersion.VersionFlag; import com.networknt.schema.ValidationMessage; import io.serverlessworkflow.impl.WorkflowModel; +import io.serverlessworkflow.impl.schema.SchemaValidator; import java.util.Set; public class JsonSchemaValidator implements SchemaValidator { diff --git a/impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/JsonSchemaValidatorFactory.java b/impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/json/JsonSchemaValidatorFactory.java similarity index 87% rename from impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/JsonSchemaValidatorFactory.java rename to impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/json/JsonSchemaValidatorFactory.java index 269bebb4..9b89e46c 100644 --- a/impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/JsonSchemaValidatorFactory.java +++ b/impl/jackson/src/main/java/io/serverlessworkflow/impl/schema/json/JsonSchemaValidatorFactory.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.serverlessworkflow.impl.schema; +package io.serverlessworkflow.impl.schema.json; import com.fasterxml.jackson.databind.ObjectMapper; import io.serverlessworkflow.api.WorkflowFormat; import io.serverlessworkflow.api.types.SchemaInline; -import io.serverlessworkflow.impl.json.JsonUtils; +import io.serverlessworkflow.impl.jackson.JsonUtils; import io.serverlessworkflow.impl.resources.StaticResource; +import io.serverlessworkflow.impl.schema.SchemaValidator; +import io.serverlessworkflow.impl.schema.SchemaValidatorFactory; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; diff --git a/impl/jackson/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory b/impl/jackson/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory index 1853d536..420f62ad 100644 --- a/impl/jackson/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory +++ b/impl/jackson/src/main/resources/META-INF/services/io.serverlessworkflow.impl.expressions.ExpressionFactory @@ -1 +1 @@ -io.serverlessworkflow.impl.expressions.JQExpressionFactory \ No newline at end of file +io.serverlessworkflow.impl.expressions.jq.JQExpressionFactory \ No newline at end of file diff --git a/impl/jackson/src/main/resources/META-INF/services/io.serverlessworkflow.impl.schema.SchemaValidatorFactory b/impl/jackson/src/main/resources/META-INF/services/io.serverlessworkflow.impl.schema.SchemaValidatorFactory index b4bc2dd0..3100f8fa 100644 --- a/impl/jackson/src/main/resources/META-INF/services/io.serverlessworkflow.impl.schema.SchemaValidatorFactory +++ b/impl/jackson/src/main/resources/META-INF/services/io.serverlessworkflow.impl.schema.SchemaValidatorFactory @@ -1 +1 @@ -io.serverlessworkflow.impl.schema.JsonSchemaValidatorFactory \ No newline at end of file +io.serverlessworkflow.impl.schema.json.JsonSchemaValidatorFactory \ No newline at end of file diff --git a/impl/jackson/src/test/java/io/serverlessworkflow/impl/DateTimeDescriptorTest.java b/impl/jackson/src/test/java/io/serverlessworkflow/impl/DateTimeDescriptorTest.java index 46937cbc..add57b92 100644 --- a/impl/jackson/src/test/java/io/serverlessworkflow/impl/DateTimeDescriptorTest.java +++ b/impl/jackson/src/test/java/io/serverlessworkflow/impl/DateTimeDescriptorTest.java @@ -19,7 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.impl.expressions.DateTimeDescriptor; -import io.serverlessworkflow.impl.json.JsonUtils; +import io.serverlessworkflow.impl.jackson.JsonUtils; import java.time.Instant; import org.junit.jupiter.api.Test; diff --git a/impl/jackson/src/test/java/io/serverlessworkflow/impl/EventDefinitionTest.java b/impl/jackson/src/test/java/io/serverlessworkflow/impl/EventDefinitionTest.java index f76dfd30..353b777b 100644 --- a/impl/jackson/src/test/java/io/serverlessworkflow/impl/EventDefinitionTest.java +++ b/impl/jackson/src/test/java/io/serverlessworkflow/impl/EventDefinitionTest.java @@ -22,7 +22,7 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.serverlessworkflow.api.WorkflowReader; -import io.serverlessworkflow.impl.json.JsonUtils; +import io.serverlessworkflow.impl.jackson.JsonUtils; import java.io.IOException; import java.util.Map; import java.util.concurrent.CompletableFuture; diff --git a/impl/jackson/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java b/impl/jackson/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java index bb600990..1a338248 100644 --- a/impl/jackson/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java +++ b/impl/jackson/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java @@ -22,7 +22,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; -import io.serverlessworkflow.impl.json.JsonUtils; +import io.serverlessworkflow.impl.jackson.JsonUtils; import java.io.IOException; import java.time.Instant; import java.util.Arrays; From 29f73893954c365a82d24ef542c896b557f20ed3 Mon Sep 17 00:00:00 2001 From: fjtirado Date: Tue, 22 Jul 2025 12:58:39 +0200 Subject: [PATCH 26/30] Http dependencies Http should depend on Jaxrs client, not in jersey Signed-off-by: fjtirado --- examples/pom.xml | 16 ++++++++++++++++ examples/simpleGet/pom.xml | 8 ++++++++ impl/http/pom.xml | 23 +++++++++++------------ impl/pom.xml | 8 ++++++++ 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/examples/pom.xml b/examples/pom.xml index e393cb8d..3ab3e22a 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -8,6 +8,9 @@ Serverless Workflow :: Examples serverlessworkflow-examples + + 3.1.10 + pom @@ -30,6 +33,19 @@ org.slf4j slf4j-simple ${version.org.slf4j} + runtime + + + org.glassfish.jersey.core + jersey-client + ${version.org.glassfish.jersey} + runtime + + + org.glassfish.jersey.media + jersey-media-json-jackson + ${version.org.glassfish.jersey} + runtime diff --git a/examples/simpleGet/pom.xml b/examples/simpleGet/pom.xml index 4d07f168..1a913363 100644 --- a/examples/simpleGet/pom.xml +++ b/examples/simpleGet/pom.xml @@ -17,6 +17,14 @@ io.serverlessworkflow serverlessworkflow-impl-http + + org.glassfish.jersey.media + jersey-media-json-jackson + + + org.glassfish.jersey.core + jersey-client + org.slf4j slf4j-simple diff --git a/impl/http/pom.xml b/impl/http/pom.xml index 65c48aac..516669a3 100644 --- a/impl/http/pom.xml +++ b/impl/http/pom.xml @@ -8,18 +8,21 @@ serverlessworkflow-impl-http Serverless Workflow :: Impl :: HTTP - - org.glassfish.jersey.core - jersey-client + + jakarta.ws.rs + jakarta.ws.rs-api + + + io.serverlessworkflow + serverlessworkflow-impl-core - org.glassfish.jersey.media - jersey-media-json-jackson - runtime + org.glassfish.jersey.media + jersey-media-json-jackson - io.serverlessworkflow - serverlessworkflow-impl-core + org.glassfish.jersey.core + jersey-client io.serverlessworkflow @@ -34,22 +37,18 @@ org.junit.jupiter junit-jupiter-api - test org.junit.jupiter junit-jupiter-engine - test org.junit.jupiter junit-jupiter-params - test org.assertj assertj-core - test ch.qos.logback diff --git a/impl/pom.xml b/impl/pom.xml index 7e76bbf4..37f3543d 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -13,6 +13,7 @@ 4.0.1 1.3.0 5.2.3 + 3.1.0 @@ -35,11 +36,13 @@ org.glassfish.jersey.core jersey-client ${version.org.glassfish.jersey} + runtime org.glassfish.jersey.media jersey-media-json-jackson ${version.org.glassfish.jersey} + runtime io.cloudevents @@ -61,6 +64,11 @@ ulid-creator ${version.com.github.f4b6a3} + + jakarta.ws.rs + jakarta.ws.rs-api + ${version.jakarta.ws.rs} + From 7aece00c00369234fd08c64e0b8430b8a8257b4f Mon Sep 17 00:00:00 2001 From: fjtirado Date: Tue, 22 Jul 2025 13:21:14 +0200 Subject: [PATCH 27/30] Jersey from runtime to test scope Signed-off-by: fjtirado --- impl/pom.xml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/impl/pom.xml b/impl/pom.xml index 37f3543d..4ecd7a7b 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -32,18 +32,6 @@ serverlessworkflow-impl-jackson ${project.version} - - org.glassfish.jersey.core - jersey-client - ${version.org.glassfish.jersey} - runtime - - - org.glassfish.jersey.media - jersey-media-json-jackson - ${version.org.glassfish.jersey} - runtime - io.cloudevents cloudevents-core @@ -69,6 +57,18 @@ jakarta.ws.rs-api ${version.jakarta.ws.rs} + + org.glassfish.jersey.core + jersey-client + ${version.org.glassfish.jersey} + test + + + org.glassfish.jersey.media + jersey-media-json-jackson + ${version.org.glassfish.jersey} + test + From 78a427646d6fc689ba3f0fac293a2cc076eda92f Mon Sep 17 00:00:00 2001 From: fjtirado Date: Tue, 22 Jul 2025 13:32:51 +0200 Subject: [PATCH 28/30] Fixing maven plugin prefix Signed-off-by: fjtirado --- .../generator/jackson/JacksonMixInPojo.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 2e7b9107..dc84e9ed 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 @@ -75,10 +75,10 @@ public class JacksonMixInPojo extends AbstractMojo { defaultValue = "${project.build.directory}/generated-sources/jacksonmixinpojo") private File outputDirectory; - @Parameter(property = "jsonschema2pojo.srcPackage") + @Parameter(property = "jacksonmixinpojo.srcPackage") private String srcPackage; - @Parameter(property = "jsonschema2pojo.targetPackage") + @Parameter(property = "jacksonmixinpojo.targetPackage") private String targetPackage; private static final String MIXIN_METHOD = "setMixInAnnotation"; From 0f1bce61591c4d1041d10133a0c1603a78354df4 Mon Sep 17 00:00:00 2001 From: fjtirado Date: Tue, 22 Jul 2025 13:48:18 +0200 Subject: [PATCH 29/30] [Fix #665] While predicate invoked in proper place Signed-off-by: fjtirado --- .../func/JavaForExecutorBuilder.java | 9 ++++---- .../io/serverless/workflow/impl/CallTest.java | 2 +- .../impl/executors/ForExecutor.java | 23 +++++++++++-------- 3 files changed, 18 insertions(+), 16 deletions(-) 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 08fd5c28..672f6aca 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 @@ -54,11 +54,10 @@ protected Optional buildWhileFilter() { return application .modelFactory() .from( - item == null - || whilePred.test( - n.asJavaObject(), - item, - (Integer) safeObject(t.variables().get(indexName)))); + whilePred.test( + n.asJavaObject(), + item, + (Integer) safeObject(t.variables().get(indexName)))); }); } } diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java index 04543004..b32fd5e7 100644 --- a/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java @@ -96,7 +96,7 @@ void testForLoop() throws InterruptedException, ExecutionException { assertThat( app.workflowDefinition(workflow) - .instance(List.of(2, 4, 6)) + .instance(List.of(2, 4, 6, 7)) .start() .get() .asNumber() 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 e0aa8d29..977152ec 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 @@ -83,19 +83,22 @@ protected CompletableFuture internalExecute( int i = 0; CompletableFuture future = CompletableFuture.completedFuture(taskContext.input()); - while (iter.hasNext() - && whileExpr - .map(w -> w.apply(workflow, taskContext, taskContext.rawOutput())) - .map(n -> n.asBoolean().orElse(true)) - .orElse(true)) { + while (iter.hasNext()) { WorkflowModel item = iter.next(); taskContext.variables().put(task.getFor().getEach(), item); taskContext.variables().put(task.getFor().getAt(), i++); - future = - future.thenCompose( - input -> - TaskExecutorHelper.processTaskList( - taskExecutor, workflow, Optional.of(taskContext), input)); + if (whileExpr + .map(w -> w.apply(workflow, taskContext, taskContext.input())) + .map(n -> n.asBoolean().orElse(true)) + .orElse(true)) { + future = + future.thenCompose( + input -> + TaskExecutorHelper.processTaskList( + taskExecutor, workflow, Optional.of(taskContext), input)); + } else { + break; + } } return future; } From 2eed8d8d9b0e70c40560af5dbe95ea8701da1e04 Mon Sep 17 00:00:00 2001 From: fjtirado Date: Thu, 24 Jul 2025 19:46:33 +0200 Subject: [PATCH 30/30] Add if predicate support Signed-off-by: fjtirado --- .../func/JavaForExecutorBuilder.java | 1 - .../func/JavaExpressionFactory.java | 20 +++++++++++- .../io/serverless/workflow/impl/CallTest.java | 32 +++++++++++++++++++ .../impl/WorkflowUtils.java | 7 ++++ .../impl/executors/AbstractTaskExecutor.java | 5 +-- .../impl/expressions/ExpressionFactory.java | 6 ++++ 6 files changed, 67 insertions(+), 4 deletions(-) diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaForExecutorBuilder.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaForExecutorBuilder.java index 672f6aca..0d80fa6d 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaForExecutorBuilder.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/executors/func/JavaForExecutorBuilder.java @@ -39,7 +39,6 @@ protected JavaForExecutorBuilder( WorkflowApplication application, ResourceLoader resourceLoader) { super(position, task, workflow, application, resourceLoader); - if (task instanceof ForTaskFunction taskFunctions) {} } protected Optional buildWhileFilter() { diff --git a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaExpressionFactory.java b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaExpressionFactory.java index 2f1820a0..48f8b804 100644 --- a/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaExpressionFactory.java +++ b/experimental/lambda/src/main/java/io/serverlessworkflow/impl/expressions/func/JavaExpressionFactory.java @@ -15,6 +15,8 @@ */ package io.serverlessworkflow.impl.expressions.func; +import io.serverlessworkflow.api.types.TaskBase; +import io.serverlessworkflow.api.types.TaskMetadata; import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowFilter; @@ -22,6 +24,7 @@ import io.serverlessworkflow.impl.WorkflowModelFactory; import io.serverlessworkflow.impl.expressions.Expression; import io.serverlessworkflow.impl.expressions.ExpressionFactory; +import java.util.Optional; import java.util.function.BiFunction; import java.util.function.BiPredicate; import java.util.function.Function; @@ -29,6 +32,7 @@ public class JavaExpressionFactory implements ExpressionFactory { + public static final String IF_PREDICATE = "if_predicate"; private final WorkflowModelFactory modelFactory = new JavaModelFactory(); private final Expression dummyExpression = new Expression() { @@ -49,7 +53,7 @@ public WorkflowFilter buildFilter(String expr, Object value) { if (value instanceof Function func) { return (w, t, n) -> modelFactory.fromAny(func.apply(n.asJavaObject())); } else if (value instanceof Predicate pred) { - return (w, t, n) -> modelFactory.from(pred.test(n.asJavaObject())); + return fromPredicate(pred); } else if (value instanceof BiPredicate pred) { return (w, t, n) -> modelFactory.from(pred.test(w, t)); } else if (value instanceof BiFunction func) { @@ -61,6 +65,20 @@ public WorkflowFilter buildFilter(String expr, Object value) { } } + @SuppressWarnings({"rawtypes", "unchecked"}) + private WorkflowFilter fromPredicate(Predicate pred) { + return (w, t, n) -> modelFactory.from(pred.test(n.asJavaObject())); + } + + @Override + public Optional buildIfFilter(TaskBase task) { + TaskMetadata metadata = task.getMetadata(); + return metadata != null + && metadata.getAdditionalProperties().get(IF_PREDICATE) instanceof Predicate pred + ? Optional.of(fromPredicate(pred)) + : ExpressionFactory.super.buildIfFilter(task); + } + @Override public WorkflowModelFactory modelFactory() { return modelFactory; diff --git a/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java b/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java index b32fd5e7..12b8353b 100644 --- a/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java +++ b/experimental/lambda/src/test/java/io/serverless/workflow/impl/CallTest.java @@ -25,6 +25,7 @@ import io.serverlessworkflow.api.types.SwitchTask; import io.serverlessworkflow.api.types.Task; import io.serverlessworkflow.api.types.TaskItem; +import io.serverlessworkflow.api.types.TaskMetadata; import io.serverlessworkflow.api.types.Workflow; import io.serverlessworkflow.api.types.func.CallJava; import io.serverlessworkflow.api.types.func.CallTaskJava; @@ -32,9 +33,11 @@ import io.serverlessworkflow.api.types.func.SwitchCaseFunction; import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowDefinition; +import io.serverlessworkflow.impl.expressions.func.JavaExpressionFactory; import java.util.Collection; import java.util.List; import java.util.concurrent.ExecutionException; +import java.util.function.Predicate; import org.junit.jupiter.api.Test; class CallTest { @@ -140,6 +143,35 @@ void testSwitch() throws InterruptedException, ExecutionException { } } + @Test + void testIf() throws InterruptedException, ExecutionException { + try (WorkflowApplication app = WorkflowApplication.builder().build()) { + Workflow workflow = + new Workflow() + .withDocument( + new Document().withNamespace("test").withName("testIf").withVersion("1.0")) + .withDo( + List.of( + new TaskItem( + "java", + new Task() + .withCallTask( + new CallTaskJava( + (CallJava) + CallJava.function(CallTest::zero) + .withMetadata( + new TaskMetadata() + .withAdditionalProperty( + JavaExpressionFactory.IF_PREDICATE, + (Predicate) + 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); + } + } + public static boolean isEven(Object model, Integer number) { return !isOdd(number); } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java index d9bf2824..4a343e3c 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java @@ -116,6 +116,13 @@ public static Optional optionalFilter(WorkflowApplication app, S return str != null ? Optional.of(buildWorkflowFilter(app, str)) : Optional.empty(); } + public static Optional optionalFilter( + WorkflowApplication app, Object obj, String str) { + return str != null || obj != null + ? Optional.of(buildWorkflowFilter(app, str, obj)) + : Optional.empty(); + } + public static String toString(UriTemplate template) { URI uri = template.getLiteralUri(); return uri != null ? uri.toString() : template.getLiteralUriTemplate(); diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java index 414c82c6..a9681f39 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java @@ -15,7 +15,8 @@ */ package io.serverlessworkflow.impl.executors; -import static io.serverlessworkflow.impl.WorkflowUtils.*; +import static io.serverlessworkflow.impl.WorkflowUtils.buildWorkflowFilter; +import static io.serverlessworkflow.impl.WorkflowUtils.getSchemaValidator; import io.serverlessworkflow.api.types.Export; import io.serverlessworkflow.api.types.FlowDirective; @@ -101,7 +102,7 @@ protected AbstractTaskExecutorBuilder( this.contextSchemaValidator = getSchemaValidator(application.validatorFactory(), resourceLoader, export.getSchema()); } - this.ifFilter = optionalFilter(application, task.getIf()); + this.ifFilter = application.expressionFactory().buildIfFilter(task); } protected final TransitionInfoBuilder next( diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionFactory.java index 696e4fda..cb14555e 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionFactory.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/expressions/ExpressionFactory.java @@ -15,8 +15,10 @@ */ package io.serverlessworkflow.impl.expressions; +import io.serverlessworkflow.api.types.TaskBase; import io.serverlessworkflow.impl.WorkflowFilter; import io.serverlessworkflow.impl.WorkflowModelFactory; +import java.util.Optional; public interface ExpressionFactory { /** @@ -29,4 +31,8 @@ public interface ExpressionFactory { WorkflowFilter buildFilter(String expr, Object value); WorkflowModelFactory modelFactory(); + + default Optional buildIfFilter(TaskBase task) { + return task.getIf() != null ? Optional.of(buildFilter(task.getIf(), null)) : Optional.empty(); + } }