diff --git a/.github/project.yml b/.github/project.yml
index 274b48af..633f883e 100644
--- a/.github/project.yml
+++ b/.github/project.yml
@@ -1,3 +1,3 @@
release:
- current-version: 7.0.0-alpha5
+ current-version: 7.0.0-alpha5.1
next-version: 7.0.0-SNAPSHOT
diff --git a/.github/workflows/maven-verify.yml b/.github/workflows/maven-verify.yml
index 1b7d432c..a9f5077c 100644
--- a/.github/workflows/maven-verify.yml
+++ b/.github/workflows/maven-verify.yml
@@ -14,13 +14,13 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- - name: Set up JDK 11
- uses: actions/setup-java@v3
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
with:
distribution: temurin
- java-version: 11
+ java-version: 17
cache: 'maven'
- name: Verify with Maven
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index ef4ee698..2d002124 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -20,7 +20,7 @@ jobs:
github-token: ${{secrets.GITHUB_TOKEN}}
metadata-file-path: '.github/project.yml'
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Import GPG key
id: import_gpg
@@ -29,11 +29,11 @@ jobs:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.GPG_PASSPHRASE }}
- - name: Set up JDK 11
- uses: actions/setup-java@v3
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
with:
distribution: temurin
- java-version: 11
+ java-version: 17
cache: 'maven'
server-id: ossrh
server-username: MAVEN_USERNAME
@@ -57,4 +57,4 @@ jobs:
MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
- name: Push tags
- run: git push && git push --tags
\ No newline at end of file
+ run: git push && git push --tags
diff --git a/api/pom.xml b/api/pom.xml
index 0b0a95d5..466a2754 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -4,7 +4,7 @@
io.serverlessworkflow
serverlessworkflow-parent
- 7.0.0-alpha5
+ 7.0.0-alpha5.1
serverlessworkflow-api
@@ -121,77 +121,6 @@
-
- org.apache.maven.plugins
- maven-checkstyle-plugin
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ${project.build.directory}/checkstyle.log
- true
- true
- true
- false
- false
- ${checkstyle.logViolationsToConsole}
- ${checkstyle.failOnViolation}
-
- ${project.build.sourceDirectory}
- ${project.build.testSourceDirectory}
-
-
-
-
- compile
-
- check
-
-
-
-
-
- com.spotify.fmt
- fmt-maven-plugin
-
- src/main/java
- src/test/java
- false
- .*\.java
- false
- false
-
-
-
-
-
- format
-
-
-
-
-
- org.apache.maven.plugins
- maven-jar-plugin
-
-
-
- test-jar
-
-
-
-
diff --git a/api/src/main/java/io/serverlessworkflow/api/OneOfValueProvider.java b/api/src/main/java/io/serverlessworkflow/api/OneOfValueProvider.java
index f3d2ab26..9d17b872 100644
--- a/api/src/main/java/io/serverlessworkflow/api/OneOfValueProvider.java
+++ b/api/src/main/java/io/serverlessworkflow/api/OneOfValueProvider.java
@@ -15,6 +15,6 @@
*/
package io.serverlessworkflow.api;
-public interface OneOfValueProvider {
- Object get();
+public interface OneOfValueProvider {
+ T get();
}
diff --git a/api/src/main/java/io/serverlessworkflow/api/WorkflowReader.java b/api/src/main/java/io/serverlessworkflow/api/WorkflowReader.java
index 01c6c8b0..4decc696 100644
--- a/api/src/main/java/io/serverlessworkflow/api/WorkflowReader.java
+++ b/api/src/main/java/io/serverlessworkflow/api/WorkflowReader.java
@@ -16,10 +16,12 @@
package io.serverlessworkflow.api;
import io.serverlessworkflow.api.types.Workflow;
+import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
+import java.io.StringReader;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -37,6 +39,19 @@ public static Workflow readWorkflow(Path path, WorkflowFormat format) throws IOE
return format.mapper().readValue(Files.readAllBytes(path), Workflow.class);
}
+ public static Workflow readWorkflow(byte[] content, WorkflowFormat format) throws IOException {
+ try (InputStream input = new ByteArrayInputStream(content)) {
+ return readWorkflow(input, format);
+ }
+ }
+
+ public static Workflow readWorkflowFromString(String content, WorkflowFormat format)
+ throws IOException {
+ try (Reader reader = new StringReader(content)) {
+ return readWorkflow(reader, format);
+ }
+ }
+
public static Workflow readWorkflowFromClasspath(String classpath) throws IOException {
return readWorkflowFromClasspath(
classpath,
diff --git a/api/src/main/java/io/serverlessworkflow/api/WorkflowWriter.java b/api/src/main/java/io/serverlessworkflow/api/WorkflowWriter.java
index f98e6402..29115396 100644
--- a/api/src/main/java/io/serverlessworkflow/api/WorkflowWriter.java
+++ b/api/src/main/java/io/serverlessworkflow/api/WorkflowWriter.java
@@ -16,8 +16,10 @@
package io.serverlessworkflow.api;
import io.serverlessworkflow.api.types.Workflow;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.io.StringWriter;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -45,5 +47,21 @@ public static void writeWorkflow(Path output, Workflow workflow, WorkflowFormat
}
}
+ public static String workflowAsString(Workflow workflow, WorkflowFormat format)
+ throws IOException {
+ try (Writer writer = new StringWriter()) {
+ writeWorkflow(writer, workflow, format);
+ return writer.toString();
+ }
+ }
+
+ public static byte[] workflowAsBytes(Workflow workflow, WorkflowFormat format)
+ throws IOException {
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ writeWorkflow(out, workflow, format);
+ return out.toByteArray();
+ }
+ }
+
private WorkflowWriter() {}
}
diff --git a/api/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java b/api/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java
index 72b4cce0..cfbd54ca 100644
--- a/api/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java
+++ b/api/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java
@@ -21,24 +21,57 @@
import com.fasterxml.jackson.databind.JsonMappingException;
import jakarta.validation.ConstraintViolationException;
import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
import java.util.Collection;
public class DeserializeHelper {
public static T deserializeOneOf(
- JsonParser p, Class targetClass, Collection> unionTypes) throws IOException {
+ JsonParser p, Class targetClass, Collection> oneOfTypes) throws IOException {
TreeNode node = p.readValueAsTree();
- JsonProcessingException ex =
- new JsonMappingException(p, "Problem deserializing " + targetClass);
- for (Class> unionType : unionTypes) {
- try {
- Object object = p.getCodec().treeToValue(node, unionType);
- return targetClass.getConstructor(unionType).newInstance(object);
- } catch (IOException | ReflectiveOperationException | ConstraintViolationException io) {
- ex.addSuppressed(io);
+ try {
+ T result = targetClass.getDeclaredConstructor().newInstance();
+ Collection exceptions = new ArrayList<>();
+ for (Class> oneOfType : oneOfTypes) {
+ try {
+ assingIt(p, result, node, targetClass, oneOfType);
+ break;
+ } catch (IOException | ConstraintViolationException | InvocationTargetException ex) {
+ exceptions.add(ex);
+ }
+ }
+ if (exceptions.size() == oneOfTypes.size()) {
+ JsonMappingException ex =
+ new JsonMappingException(
+ p,
+ String.format(
+ "Error deserializing class %s, all oneOf alternatives %s has failed ",
+ targetClass, oneOfTypes));
+ exceptions.forEach(ex::addSuppressed);
+ throw ex;
+ }
+ return result;
+ } catch (ReflectiveOperationException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ private static void assingIt(
+ JsonParser p, T result, TreeNode node, Class targetClass, Class> type)
+ throws JsonProcessingException, ReflectiveOperationException {
+ findSetMethod(targetClass, type).invoke(result, p.getCodec().treeToValue(node, type));
+ }
+
+ private static Method findSetMethod(Class> targetClass, Class> type) {
+ for (Method method : targetClass.getMethods()) {
+ OneOfSetter oneOfSetter = method.getAnnotation(OneOfSetter.class);
+ if (oneOfSetter != null && type.equals(oneOfSetter.value())) {
+ return method;
}
}
- throw ex;
+ throw new IllegalStateException("Cannot find a setter for type " + type);
}
public static T deserializeItem(JsonParser p, Class targetClass, Class> valueClass)
diff --git a/api/src/main/java/io/serverlessworkflow/serialization/OneOfSetter.java b/api/src/main/java/io/serverlessworkflow/serialization/OneOfSetter.java
new file mode 100644
index 00000000..098df425
--- /dev/null
+++ b/api/src/main/java/io/serverlessworkflow/serialization/OneOfSetter.java
@@ -0,0 +1,28 @@
+/*
+ * 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.serialization;
+
+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 OneOfSetter {
+ Class> value();
+}
diff --git a/api/src/main/resources/schema/workflow.yaml b/api/src/main/resources/schema/workflow.yaml
index 42456ec6..aecbeacb 100644
--- a/api/src/main/resources/schema/workflow.yaml
+++ b/api/src/main/resources/schema/workflow.yaml
@@ -777,7 +777,10 @@ $defs:
errors:
type: object
title: CatchErrors
- description: The configuration of a concept used to catch errors.
+ properties:
+ with:
+ $ref: '#/$defs/errorFilter'
+ description: static error filter
as:
type: string
title: CatchAs
@@ -785,11 +788,11 @@ $defs:
when:
type: string
title: CatchWhen
- description: A runtime expression used to determine whether or not to catch the filtered error.
+ description: A runtime expression used to determine whether to catch the filtered error.
exceptWhen:
type: string
title: CatchExceptWhen
- description: A runtime expression used to determine whether or not to catch the filtered error.
+ description: A runtime expression used to determine whether not to catch the filtered error.
retry:
oneOf:
- $ref: '#/$defs/retryPolicy'
@@ -1152,6 +1155,27 @@ $defs:
title: ErrorDetails
description: A human-readable explanation specific to this occurrence of the error.
required: [ type, status ]
+ errorFilter:
+ type: object
+ title: ErrorFilter
+ description: Error filtering base on static values. For error filtering on dynamic values, use catch.when property
+ minProperties: 1
+ properties:
+ type:
+ type: string
+ description: if present, means this value should be used for filtering
+ status:
+ type: integer
+ description: if present, means this value should be used for filtering
+ instance:
+ type: string
+ description: if present, means this value should be used for filtering
+ title:
+ type: string
+ description: if present, means this value should be used for filtering
+ details:
+ type: string
+ description: if present, means this value should be used for filtering
uriTemplate:
title: UriTemplate
anyOf:
diff --git a/api/src/test/java/io/serverlessworkflow/api/FeaturesTest.java b/api/src/test/java/io/serverlessworkflow/api/FeaturesTest.java
index fd16b952..81d10ecf 100644
--- a/api/src/test/java/io/serverlessworkflow/api/FeaturesTest.java
+++ b/api/src/test/java/io/serverlessworkflow/api/FeaturesTest.java
@@ -17,7 +17,10 @@
import static io.serverlessworkflow.api.WorkflowReader.readWorkflow;
import static io.serverlessworkflow.api.WorkflowReader.readWorkflowFromClasspath;
+import static io.serverlessworkflow.api.WorkflowWriter.workflowAsBytes;
+import static io.serverlessworkflow.api.WorkflowWriter.workflowAsString;
import static io.serverlessworkflow.api.WorkflowWriter.writeWorkflow;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import io.serverlessworkflow.api.types.Workflow;
@@ -32,6 +35,13 @@ public class FeaturesTest {
@ParameterizedTest
@ValueSource(
strings = {
+ "features/authentication-bearer.yaml",
+ "features/authentication-bearer-uri-format.yaml",
+ "features/authentication-oauth2.yaml",
+ "features/authentication-oauth2-secret.yaml",
+ "features/authentication-oidc.yaml",
+ "features/authentication-oidc-secret.yaml",
+ "features/authentication-reusable.yaml",
"features/callHttp.yaml",
"features/callOpenAPI.yaml",
"features/composite.yaml",
@@ -45,12 +55,13 @@ public class FeaturesTest {
"features/try.yaml",
"features/listen.yaml",
"features/callFunction.yaml",
- "features/callCustomFunction.yaml"
+ "features/callCustomFunction.yaml",
+ "features/call-http-query-parameters.yaml"
})
public void testSpecFeaturesParsing(String workflowLocation) throws IOException {
Workflow workflow = readWorkflowFromClasspath(workflowLocation);
assertWorkflow(workflow);
- assertWorkflow(writeAndReadInMemory(workflow));
+ assertWorkflowEquals(workflow, writeAndReadInMemory(workflow));
}
private static Workflow writeAndReadInMemory(Workflow workflow) throws IOException {
@@ -69,4 +80,11 @@ private static void assertWorkflow(Workflow workflow) {
assertNotNull(workflow.getDocument());
assertNotNull(workflow.getDo());
}
+
+ private static void assertWorkflowEquals(Workflow workflow, Workflow other) throws IOException {
+ assertThat(workflowAsString(workflow, WorkflowFormat.YAML))
+ .isEqualTo(workflowAsString(other, WorkflowFormat.YAML));
+ assertThat(workflowAsBytes(workflow, WorkflowFormat.JSON))
+ .isEqualTo(workflowAsBytes(other, WorkflowFormat.JSON));
+ }
}
diff --git a/api/src/test/resources/features/authentication-bearer-uri-format.yaml b/api/src/test/resources/features/authentication-bearer-uri-format.yaml
new file mode 100644
index 00000000..b0019fbb
--- /dev/null
+++ b/api/src/test/resources/features/authentication-bearer-uri-format.yaml
@@ -0,0 +1,15 @@
+document:
+ dsl: '1.0.0-alpha5'
+ namespace: examples
+ name: bearer-auth
+ version: '0.1.0'
+do:
+ - getPet:
+ call: http
+ with:
+ method: get
+ endpoint:
+ uri: https://petstore.swagger.io/v2/pet/{petId}
+ authentication:
+ bearer:
+ token: ${ .token }
diff --git a/api/src/test/resources/features/authentication-bearer.yaml b/api/src/test/resources/features/authentication-bearer.yaml
new file mode 100644
index 00000000..f0c42741
--- /dev/null
+++ b/api/src/test/resources/features/authentication-bearer.yaml
@@ -0,0 +1,15 @@
+document:
+ dsl: '1.0.0-alpha5'
+ namespace: examples
+ name: bearer-auth-uri-format
+ version: '0.1.0'
+do:
+ - getPet:
+ call: http
+ with:
+ method: get
+ endpoint:
+ uri: https://petstore.swagger.io/v2/pet/1
+ authentication:
+ bearer:
+ token: ${ .token }
\ No newline at end of file
diff --git a/api/src/test/resources/features/authentication-oauth2-secret.yaml b/api/src/test/resources/features/authentication-oauth2-secret.yaml
new file mode 100644
index 00000000..635076ab
--- /dev/null
+++ b/api/src/test/resources/features/authentication-oauth2-secret.yaml
@@ -0,0 +1,18 @@
+document:
+ dsl: 1.0.0-alpha1
+ namespace: examples
+ name: oauth2-authentication
+ version: 1.0.0-alpha1
+use:
+ secrets:
+ - mySecret
+do:
+ - getPet:
+ call: http
+ with:
+ method: get
+ endpoint:
+ uri: https://petstore.swagger.io/v2/pet/{petId}
+ authentication:
+ oauth2:
+ use: mySecret
\ No newline at end of file
diff --git a/api/src/test/resources/features/authentication-oauth2.yaml b/api/src/test/resources/features/authentication-oauth2.yaml
new file mode 100644
index 00000000..625a1e2c
--- /dev/null
+++ b/api/src/test/resources/features/authentication-oauth2.yaml
@@ -0,0 +1,22 @@
+document:
+ dsl: '1.0.0-alpha5'
+ namespace: examples
+ name: oauth2-authentication
+ version: '0.1.0'
+do:
+ - getPet:
+ call: http
+ with:
+ method: get
+ endpoint:
+ uri: https://petstore.swagger.io/v2/pet/{petId}
+ authentication:
+ oauth2:
+ authority: http://keycloak/realms/fake-authority
+ endpoints: #optional
+ token: /auth/token #defaults to /oauth2/token
+ introspection: /auth/introspect #defaults to /oauth2/introspect
+ grant: client_credentials
+ client:
+ id: workflow-runtime-id
+ secret: workflow-runtime-secret
\ No newline at end of file
diff --git a/api/src/test/resources/features/authentication-oidc-secret.yaml b/api/src/test/resources/features/authentication-oidc-secret.yaml
new file mode 100644
index 00000000..19c387c1
--- /dev/null
+++ b/api/src/test/resources/features/authentication-oidc-secret.yaml
@@ -0,0 +1,18 @@
+document:
+ dsl: 1.0.0-alpha1
+ namespace: examples
+ name: oidc-authentication
+ version: 1.0.0-alpha1
+use:
+ secrets:
+ - mySecret
+do:
+ - getPet:
+ call: http
+ with:
+ method: get
+ endpoint:
+ uri: https://petstore.swagger.io/v2/pet/{petId}
+ authentication:
+ oidc:
+ use: mySecret
\ No newline at end of file
diff --git a/api/src/test/resources/features/authentication-oidc.yaml b/api/src/test/resources/features/authentication-oidc.yaml
new file mode 100644
index 00000000..18aec74d
--- /dev/null
+++ b/api/src/test/resources/features/authentication-oidc.yaml
@@ -0,0 +1,19 @@
+document:
+ dsl: '1.0.0-alpha5'
+ namespace: examples
+ name: oidc-authentication
+ version: '0.1.0'
+do:
+ - getPet:
+ call: http
+ with:
+ method: get
+ endpoint:
+ uri: https://petstore.swagger.io/v2/pet/{petId}
+ authentication:
+ oidc:
+ authority: http://keycloak/realms/fake-authority #endpoints are resolved using the OIDC configuration located at '/.well-known/openid-configuration'
+ grant: client_credentials
+ client:
+ id: workflow-runtime-id
+ secret: workflow-runtime-secret
\ No newline at end of file
diff --git a/api/src/test/resources/features/authentication-reusable.yaml b/api/src/test/resources/features/authentication-reusable.yaml
new file mode 100644
index 00000000..a5da803d
--- /dev/null
+++ b/api/src/test/resources/features/authentication-reusable.yaml
@@ -0,0 +1,19 @@
+document:
+ dsl: '1.0.0-alpha5'
+ namespace: examples
+ name: bearer-auth
+ version: '0.1.0'
+use:
+ authentications:
+ petStoreAuth:
+ bearer:
+ token: ${ .token }
+do:
+ - getPet:
+ call: http
+ with:
+ method: get
+ endpoint:
+ uri: https://petstore.swagger.io/v2/pet/{petId}
+ authentication:
+ use: petStoreAuth
diff --git a/api/src/test/resources/features/call-http-query-parameters.yaml b/api/src/test/resources/features/call-http-query-parameters.yaml
new file mode 100644
index 00000000..95934315
--- /dev/null
+++ b/api/src/test/resources/features/call-http-query-parameters.yaml
@@ -0,0 +1,24 @@
+document:
+ dsl: 1.0.0-alpha2
+ namespace: examples
+ name: http-query-params
+ version: 1.0.0-alpha2
+input:
+ schema:
+ format: json
+ document:
+ type: object
+ required:
+ - searchQuery
+ properties:
+ searchQuery:
+ type: string
+do:
+ - searchStarWarsCharacters:
+ call: http
+ with:
+ method: get
+ endpoint: https://swapi.dev/api/people/
+ query:
+ search: ${.searchQuery}
+
diff --git a/custom-generator/pom.xml b/custom-generator/pom.xml
index 5cbea0a3..8444bb2a 100644
--- a/custom-generator/pom.xml
+++ b/custom-generator/pom.xml
@@ -3,7 +3,7 @@
io.serverlessworkflow
serverlessworkflow-parent
- 7.0.0-alpha5
+ 7.0.0-alpha5.1
custom-generator
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 ab0c1a23..d14ba357 100644
--- a/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java
+++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java
@@ -136,96 +136,138 @@ public JType apply(
Schema schema) {
Optional refType = refType(nodeName, schemaNode, parent, generatableType, schema);
- List unionTypes = new ArrayList<>();
+ List oneOfTypes = new ArrayList<>();
+ List allOfTypes = new ArrayList<>();
- unionType("oneOf", nodeName, schemaNode, parent, generatableType, schema, unionTypes);
- unionType("anyOf", nodeName, schemaNode, parent, generatableType, schema, unionTypes);
- unionType("allOf", nodeName, schemaNode, parent, generatableType, schema, unionTypes);
+ unionType("oneOf", nodeName, schemaNode, parent, generatableType, schema, oneOfTypes);
+ unionType("anyOf", nodeName, schemaNode, parent, generatableType, schema, oneOfTypes);
+ unionType("allOf", nodeName, schemaNode, parent, generatableType, schema, allOfTypes);
- Collections.sort(unionTypes);
+ Collections.sort(oneOfTypes);
JType javaType;
if (schemaNode.has("enum")) {
javaType =
ruleFactory.getEnumRule().apply(nodeName, schemaNode, parent, generatableType, schema);
- } else if (!schemaNode.has("properties") && unionTypes.isEmpty() && refType.isPresent()) {
+ } else if (!schemaNode.has("properties")
+ && oneOfTypes.isEmpty()
+ && allOfTypes.isEmpty()
+ && refType.isPresent()) {
javaType = refType.get();
-
} else {
- javaType =
- ruleFactory
- .getTypeRule()
- .apply(nodeName, schemaNode, parent, generatableType.getPackage(), schema);
+ JPackage container = generatableType.getPackage();
+ javaType = ruleFactory.getTypeRule().apply(nodeName, schemaNode, parent, container, schema);
if (javaType instanceof JDefinedClass) {
- populateClass(schema, (JDefinedClass) javaType, refType, unionTypes);
- } else if (!unionTypes.isEmpty()) {
javaType =
- createUnionClass(
- schema, nodeName, schemaNode, generatableType.getPackage(), refType, unionTypes);
+ populateAllOf(
+ schema, populateRef((JDefinedClass) javaType, refType, schema), allOfTypes);
}
- schema.setJavaTypeIfEmpty(javaType);
+ if (!oneOfTypes.isEmpty()) {
+ try {
+ JDefinedClass unionClass;
+ Optional commonType;
+ if (javaType instanceof JDefinedClass) {
+ JDefinedClass clazz = (JDefinedClass) javaType;
+ if (clazz.methods().isEmpty()) {
+ unionClass = clazz;
+ commonType = Optional.empty();
+ } else {
+ unionClass = container._class(clazz.name() + "Union");
+ commonType = Optional.of(clazz);
+ }
+ } else {
+ unionClass =
+ container._class(
+ ruleFactory
+ .getNameHelper()
+ .getUniqueClassName(nodeName, schemaNode, container));
+ commonType = Optional.empty();
+ }
+ javaType = populateOneOf(schema, unionClass, commonType, oneOfTypes);
+ } catch (JClassAlreadyExistsException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+ schema.setJavaType(javaType);
}
return javaType;
}
- private JDefinedClass populateClass(
+ private JDefinedClass populateAllOf(
+ Schema parentSchema, JDefinedClass definedClass, Collection allOfTypes) {
+ return wrapAll(parentSchema, definedClass, Optional.empty(), allOfTypes, Optional.empty());
+ }
+
+ private JDefinedClass populateOneOf(
Schema parentSchema,
JDefinedClass definedClass,
- Optional refType,
- Collection unionTypes) {
- JType clazzClass = definedClass.owner()._ref(Object.class);
-
- Optional valueField;
- if (!unionTypes.isEmpty()) {
- valueField =
- Optional.of(
- definedClass.field(
- JMod.PRIVATE,
- clazzClass,
- ruleFactory.getNameHelper().getPropertyName("value", null),
- null));
-
- definedClass._implements(
- definedClass.owner().ref(GeneratorUtils.ONE_OF_VALUE_PROVIDER_INTERFACE_NAME));
-
- GeneratorUtils.implementInterface(definedClass, valueField.orElseThrow());
-
- try {
- JDefinedClass serializer = generateSerializer(definedClass);
- definedClass.annotate(JsonSerialize.class).param("using", serializer);
- } catch (JClassAlreadyExistsException ex) {
- // already serialized aware
- }
+ Optional commonType,
+ Collection oneOfTypes) {
- try {
- JDefinedClass deserializer = generateDeserializer(definedClass, unionTypes);
- definedClass.annotate(JsonDeserialize.class).param("using", deserializer);
- } catch (JClassAlreadyExistsException ex) {
- // already deserialized aware
- }
+ JFieldVar valueField =
+ definedClass.field(
+ JMod.PRIVATE,
+ commonType.orElse(definedClass.owner().ref(Object.class)),
+ ruleFactory.getNameHelper().getPropertyName("value", null),
+ null);
+
+ definedClass._implements(
+ definedClass
+ .owner()
+ .ref(GeneratorUtils.ONE_OF_VALUE_PROVIDER_INTERFACE_NAME)
+ .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
+ }
- Collection stringTypes = new ArrayList<>();
- for (JTypeWrapper unionType : unionTypes) {
- if (isStringType(unionType.getType())) {
- stringTypes.add(unionType);
- } else {
- wrapIt(parentSchema, definedClass, valueField, unionType.getType(), unionType.getNode());
+ try {
+ JDefinedClass deserializer =
+ generateDeserializer(definedClass, oneOfTypes, "deserializeOneOf");
+ definedClass.annotate(JsonDeserialize.class).param("using", deserializer);
+ } catch (JClassAlreadyExistsException ex) {
+ // already deserialized aware
+ }
+
+ return wrapAll(parentSchema, definedClass, commonType, oneOfTypes, Optional.of(valueField));
+ }
+
+ private JDefinedClass wrapAll(
+ Schema parentSchema,
+ JDefinedClass definedClass,
+ Optional commonType,
+ Collection types,
+ Optional valueField) {
+ Collection stringTypes = new ArrayList<>();
+ for (JTypeWrapper unionType : types) {
+ if (isStringType(unionType.getType())) {
+ stringTypes.add(unionType);
+ } else {
+ if (unionType.getType() instanceof JDefinedClass) {
+ commonType.ifPresent(
+ c -> ((JDefinedClass) unionType.getType())._extends((JDefinedClass) c));
}
- }
- if (!stringTypes.isEmpty()) {
- wrapStrings(parentSchema, definedClass, valueField, stringTypes);
- }
- } else {
- valueField = Optional.empty();
+ wrapIt(parentSchema, definedClass, valueField, unionType.getType(), unionType.getNode());
+ }
+ }
+ if (!stringTypes.isEmpty()) {
+ wrapStrings(parentSchema, definedClass, valueField, stringTypes);
}
+ return definedClass;
+ }
+ private JDefinedClass populateRef(
+ JDefinedClass definedClass, Optional refType, Schema parentSchema) {
refType.ifPresent(
type -> {
if (type instanceof JClass) {
definedClass._extends((JClass) type);
} else {
- wrapIt(parentSchema, definedClass, valueField, type, null);
+ wrapIt(parentSchema, definedClass, Optional.empty(), type, null);
}
});
@@ -258,7 +300,7 @@ private JDefinedClass generateSerializer(JDefinedClass relatedClass)
}
private JDefinedClass generateDeserializer(
- JDefinedClass relatedClass, Collection unionTypes)
+ JDefinedClass relatedClass, Collection oneOfTypes, String methodName)
throws JClassAlreadyExistsException {
JDefinedClass definedClass = GeneratorUtils.deserializerClass(relatedClass);
GeneratorUtils.fillDeserializer(
@@ -266,37 +308,23 @@ private JDefinedClass generateDeserializer(
relatedClass,
(method, parserParam) -> {
JBlock body = method.body();
- JInvocation list = definedClass.owner().ref(List.class).staticInvoke("of");
- unionTypes.forEach(c -> list.arg(((JClass) c.getType()).dotclass()));
+
body._return(
definedClass
.owner()
.ref(GeneratorUtils.DESERIALIZE_HELPER_NAME)
- .staticInvoke("deserializeOneOf")
+ .staticInvoke(methodName)
.arg(parserParam)
.arg(relatedClass.dotclass())
- .arg(list));
+ .arg(list(definedClass, oneOfTypes)));
});
return definedClass;
}
- private JDefinedClass createUnionClass(
- Schema parentSchema,
- String nodeName,
- JsonNode schemaNode,
- JPackage container,
- Optional refType,
- Collection unionTypes) {
- try {
- return populateClass(
- parentSchema,
- container._class(
- ruleFactory.getNameHelper().getUniqueClassName(nodeName, schemaNode, container)),
- refType,
- unionTypes);
- } catch (JClassAlreadyExistsException e) {
- throw new IllegalArgumentException(e);
- }
+ 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(
@@ -306,11 +334,39 @@ private void wrapIt(
JType unionType,
JsonNode node) {
JFieldVar instanceField = getInstanceField(parentSchema, definedClass, unionType, node);
- JMethod constructor = definedClass.constructor(JMod.PUBLIC);
- JVar instanceParam = constructor.param(unionType, instanceField.name());
- JBlock body = constructor.body();
- valueField.ifPresent(v -> body.assign(JExpr._this().ref(v), instanceParam));
- body.assign(JExpr._this().ref(instanceField), instanceParam);
+ JMethod method = getSetterMethod(definedClass, instanceField, node);
+ method
+ .body()
+ .assign(
+ JExpr._this().ref(instanceField),
+ setupMethod(definedClass, method, valueField, instanceField));
+ }
+
+ private JVar setupMethod(
+ JDefinedClass definedClass,
+ JMethod method,
+ Optional valueField,
+ JFieldVar instanceField) {
+ JVar methodParam = method.param(instanceField.type(), instanceField.name());
+ valueField.ifPresent(
+ v -> {
+ method.body().assign(JExpr._this().ref(v), methodParam);
+ method
+ .annotate(definedClass.owner().ref(GeneratorUtils.SETTER_ANNOTATION_NAME))
+ .param("value", instanceField.type());
+ });
+ return methodParam;
+ }
+
+ private JMethod getSetterMethod(
+ JDefinedClass definedClass, JFieldVar instanceField, JsonNode node) {
+ String setterName = ruleFactory.getNameHelper().getSetterName(instanceField.name(), node);
+ JMethod fluentMethod =
+ definedClass.method(JMod.PUBLIC, definedClass, setterName.replaceFirst("set", "with"));
+ JBlock body = fluentMethod.body();
+ body.assign(instanceField, fluentMethod.param(instanceField.type(), "value"));
+ body._return(JExpr._this());
+ return definedClass.method(JMod.PUBLIC, definedClass.owner().VOID, setterName);
}
private void wrapStrings(
@@ -320,21 +376,19 @@ private void wrapStrings(
Collection stringTypes) {
Iterator iter = stringTypes.iterator();
JTypeWrapper first = iter.next();
- JMethod constructor = definedClass.constructor(JMod.PUBLIC);
-
- JBlock body = constructor.body();
String pattern = pattern(first.getNode(), parentSchema);
if (pattern == null && iter.hasNext()) {
pattern = ".*";
}
JFieldVar instanceField =
getInstanceField(parentSchema, definedClass, first.getType(), first.getNode());
- JVar instanceParam = constructor.param(first.type, instanceField.name());
- valueField.ifPresent(v -> body.assign(JExpr._this().ref(v), instanceParam));
+ JMethod setterMethod = getSetterMethod(definedClass, instanceField, first.getNode());
+ JVar methodParam = setupMethod(definedClass, setterMethod, valueField, instanceField);
+ JBlock body = setterMethod.body();
if (pattern != null) {
JConditional condition =
- body._if(getPatternCondition(pattern, body, instanceField, instanceParam, definedClass));
- condition._then().assign(JExpr._this().ref(instanceField), instanceParam);
+ body._if(getPatternCondition(pattern, body, instanceField, methodParam, definedClass));
+ condition._then().assign(JExpr._this().ref(instanceField), methodParam);
while (iter.hasNext()) {
JTypeWrapper item = iter.next();
instanceField =
@@ -345,8 +399,8 @@ private void wrapStrings(
}
condition =
condition._elseif(
- getPatternCondition(pattern, body, instanceField, instanceParam, definedClass));
- condition._then().assign(JExpr._this().ref(instanceField), instanceParam);
+ getPatternCondition(pattern, body, instanceField, methodParam, definedClass));
+ condition._then().assign(JExpr._this().ref(instanceField), methodParam);
}
condition
._else()
@@ -358,10 +412,10 @@ private void wrapStrings(
.ref(String.class)
.staticInvoke("format")
.arg("%s does not match any pattern")
- .arg(instanceParam))
+ .arg(methodParam))
.arg(JExpr._null()));
} else {
- body.assign(JExpr._this().ref(instanceField), instanceParam);
+ body.assign(JExpr._this().ref(instanceField), methodParam);
}
}
@@ -374,7 +428,7 @@ private JFieldVar getInstanceField(
ruleFactory
.getNameHelper()
.getPropertyName(getTypeName(node, type, parentSchema), node));
- GeneratorUtils.buildMethod(
+ GeneratorUtils.getterMethod(
definedClass, instanceField, ruleFactory.getNameHelper(), instanceField.name());
return instanceField;
}
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 ce3badc2..e7af60d5 100644
--- a/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java
+++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java
@@ -38,6 +38,8 @@ public class GeneratorUtils {
"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 {
@@ -60,13 +62,13 @@ public static JDefinedClass deserializerClass(JDefinedClass relatedClass)
}
public static JMethod implementInterface(JDefinedClass definedClass, JFieldVar valueField) {
- JMethod method = definedClass.method(JMod.PUBLIC, Object.class, "get");
+ JMethod method = definedClass.method(JMod.PUBLIC, valueField.type(), "get");
method.annotate(Override.class);
method.body()._return(valueField);
return method;
}
- public static JMethod buildMethod(
+ public static JMethod getterMethod(
JDefinedClass definedClass, JFieldVar instanceField, NameHelper nameHelper, String name) {
JMethod method =
definedClass.method(
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 0e937658..18bdbba6 100644
--- a/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java
+++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/UnevaluatedPropertiesRule.java
@@ -70,7 +70,7 @@ private JDefinedClass addKeyValueFields(
JType stringClass = jclass.owner()._ref(String.class);
JFieldVar nameField =
jclass.field(JMod.PRIVATE, stringClass, nameHelper.getPropertyName("name", null));
- JMethod nameMethod = GeneratorUtils.buildMethod(jclass, nameField, nameHelper, "name");
+ JMethod nameMethod = GeneratorUtils.getterMethod(jclass, nameField, nameHelper, "name");
JType propertyType;
if (node != null && node.size() != 0) {
String pathToAdditionalProperties;
@@ -98,7 +98,7 @@ private JDefinedClass addKeyValueFields(
jclass.field(
JMod.PRIVATE, propertyType, nameHelper.getPropertyName(propertyType.name(), null));
JMethod valueMethod =
- GeneratorUtils.buildMethod(jclass, valueField, nameHelper, propertyType.name());
+ GeneratorUtils.getterMethod(jclass, valueField, nameHelper, propertyType.name());
jclass
.annotate(JsonSerialize.class)
.param("using", generateSerializer(jclass, nameMethod, valueMethod));
diff --git a/impl/bom/pom.xml b/impl/bom/pom.xml
new file mode 100644
index 00000000..604a8300
--- /dev/null
+++ b/impl/bom/pom.xml
@@ -0,0 +1,20 @@
+
+ 4.0.0
+
+ io.serverlessworkflow
+ serverlessworkflow-impl
+ 7.0.0-alpha5.1
+
+ serverlessworkflow-impl-bom
+ pom
+
+
+ io.serverlessworkflow
+ serverlessworkflow-impl-core
+
+
+ io.serverlessworkflow
+ serverlessworkflow-impl-http
+
+
+
\ No newline at end of file
diff --git a/impl/core/.checkstyle b/impl/core/.checkstyle
new file mode 100644
index 00000000..cdd4188c
--- /dev/null
+++ b/impl/core/.checkstyle
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/impl/core/pom.xml b/impl/core/pom.xml
new file mode 100644
index 00000000..9fac9df6
--- /dev/null
+++ b/impl/core/pom.xml
@@ -0,0 +1,59 @@
+
+ 4.0.0
+
+ io.serverlessworkflow
+ serverlessworkflow-impl
+ 7.0.0-alpha5.1
+
+ serverlessworkflow-impl-core
+
+ 1.1.0
+ 5.2.3
+
+
+
+ io.serverlessworkflow
+ serverlessworkflow-api
+ 7.0.0-alpha5.1
+
+
+ com.github.f4b6a3
+ ulid-creator
+ ${version.com.github.f4b6a3}
+
+
+ com.networknt
+ json-schema-validator
+
+
+ net.thisptr
+ jackson-jq
+ ${version.net.thisptr}
+
+
+ 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/ExecutorServiceFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/ExecutorServiceFactory.java
new file mode 100644
index 00000000..7c211149
--- /dev/null
+++ b/impl/core/src/main/java/io/serverlessworkflow/impl/ExecutorServiceFactory.java
@@ -0,0 +1,22 @@
+/*
+ * 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 java.util.concurrent.ExecutorService;
+import java.util.function.Supplier;
+
+@FunctionalInterface
+public interface ExecutorServiceFactory extends Supplier {}
diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/LongFilter.java b/impl/core/src/main/java/io/serverlessworkflow/impl/LongFilter.java
new file mode 100644
index 00000000..91b1b6c5
--- /dev/null
+++ b/impl/core/src/main/java/io/serverlessworkflow/impl/LongFilter.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;
+
+import java.util.function.BiFunction;
+
+@FunctionalInterface
+public interface LongFilter extends BiFunction, Long> {}
diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/QueueWorkflowPosition.java b/impl/core/src/main/java/io/serverlessworkflow/impl/QueueWorkflowPosition.java
new file mode 100644
index 00000000..5ad4934f
--- /dev/null
+++ b/impl/core/src/main/java/io/serverlessworkflow/impl/QueueWorkflowPosition.java
@@ -0,0 +1,70 @@
+/*
+ * 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 java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.stream.Collectors;
+
+public class QueueWorkflowPosition implements WorkflowPosition {
+
+ private Deque