diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index f23e469e..5ef5e9d5 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1 +1 @@
-* @tsurdilo @manuelstein @ricardozanini
\ No newline at end of file
+* @ricardozanini @fjtirado
\ No newline at end of file
diff --git a/.github/OWNERS b/.github/OWNERS
index da3ddc3f..0db9cb96 100644
--- a/.github/OWNERS
+++ b/.github/OWNERS
@@ -1,10 +1,8 @@
reviewers:
- - tsurdilo
- - manuelstein
- ricardozanini
+ - fjtirado
approvers:
- - tsurdilo
- - manuelstein
- ricardozanini
+ - fjtirado
labels:
- sig/contributor-experience
\ No newline at end of file
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..907b5f95
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,14 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
+
+version: 2
+updates:
+ - package-ecosystem: "maven" # See documentation for possible values
+ directory: "/" # Location of package manifests
+ schedule:
+ interval: "weekly"
+ assignees:
+ - ricardozanini
+ - fjtirado
\ No newline at end of file
diff --git a/.github/project.yml b/.github/project.yml
new file mode 100644
index 00000000..016dfafa
--- /dev/null
+++ b/.github/project.yml
@@ -0,0 +1,3 @@
+release:
+ current-version: 4.2.0.Final
+ next-version: 4.3.0-SNAPSHOT
diff --git a/.github/workflows/maven-deploy.yml b/.github/workflows/maven-deploy.yml
deleted file mode 100644
index 67812473..00000000
--- a/.github/workflows/maven-deploy.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-# This workflow will build a Java project with Maven
-# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
-
-name: Deploy JAVA SDK
-
-on:
- push:
- branches:
- - main
-jobs:
- publish:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - name: Set up Maven Central Repository
- uses: actions/setup-java@v1
- with:
- java-version: 1.8
- server-id: ossrh
- server-username: MAVEN_USERNAME
- server-password: MAVEN_PASSWORD
- - name: Publish package
- run: mvn -B -f pom.xml deploy
- env:
- MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }}
- MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
diff --git a/.github/workflows/maven-verify.yml b/.github/workflows/maven-verify.yml
index 5da52578..e5ed35ac 100644
--- a/.github/workflows/maven-verify.yml
+++ b/.github/workflows/maven-verify.yml
@@ -1,30 +1,28 @@
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
-name: Verify JAVA SDK
+name: sdk-java Verify
on:
push:
branches:
- - main
+ - 4.*
pull_request:
branches:
- - main
+ - 4.*
jobs:
build:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
- - name: Set up JDK 1.8
- uses: actions/setup-java@v1
- with:
- java-version: 1.8
- - name: Cache Maven packages
- uses: actions/cache@v2
- with:
- path: ~/.m2
- key: ${{ runner.os }}-m2-${{ hashFiles('pom.xml') }}
- restore-keys: ${{ runner.os }}-m2
- - name: Verify with Maven
- run: |
- mvn -B -f pom.xml clean install verify
+ - uses: actions/checkout@v4
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: 17
+ cache: 'maven'
+
+ - name: Verify with Maven
+ run: |
+ mvn -B -f pom.xml clean install verify
diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml
new file mode 100644
index 00000000..44f54117
--- /dev/null
+++ b/.github/workflows/pre-release.yml
@@ -0,0 +1,25 @@
+name: sdk-java Pre Release
+
+on:
+ pull_request:
+ paths:
+ - '.github/project.yml'
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ name: pre release
+
+ steps:
+ - uses: radcortez/project-metadata-action@main
+ name: retrieve project metadata
+ id: metadata
+ with:
+ github-token: ${{secrets.GITHUB_TOKEN}}
+ metadata-file-path: '.github/project.yml'
+
+ - name: Validate version
+ if: contains(steps.metadata.outputs.current-version, 'SNAPSHOT')
+ run: |
+ echo '::error::Cannot release a SNAPSHOT version.'
+ exit 1
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 00000000..2e4ec8ce
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,60 @@
+name: sdk-java Release
+
+on:
+ pull_request:
+ types: [closed]
+ paths:
+ - '.github/project.yml'
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ name: release
+ if: ${{ github.event.pull_request.merged == true }}
+
+ steps:
+ - uses: radcortez/project-metadata-action@main
+ name: Retrieve project metadata
+ id: metadata
+ with:
+ github-token: ${{secrets.GITHUB_TOKEN}}
+ metadata-file-path: '.github/project.yml'
+
+ - uses: actions/checkout@v4
+
+ - name: Import GPG key
+ id: import_gpg
+ uses: crazy-max/ghaction-import-gpg@v5
+ with:
+ gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
+ passphrase: ${{ secrets.GPG_PASSPHRASE }}
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: 17
+ cache: 'maven'
+ server-id: ossrh
+ server-username: MAVEN_USERNAME
+ server-password: MAVEN_PASSWORD
+
+ - name: Configure Git author
+ run: |
+ git config --local user.email "action@github.com"
+ git config --local user.name "GitHub Action"
+
+ - name: Maven release ${{steps.metadata.outputs.current-version}}
+ run: |
+ git checkout -b release
+ mvn -B release:prepare -Prelease -DreleaseVersion=${{steps.metadata.outputs.current-version}} -DdevelopmentVersion=${{steps.metadata.outputs.next-version}}
+ cat release.properties
+ git checkout ${{github.base_ref}}
+ git rebase release
+ mvn -B release:perform -Prelease -Darguments="-DperformRelease"
+ env:
+ MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }}
+ MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
+
+ - name: Push tags
+ run: git push && git push --tags
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index d4dfde66..1dfd7048 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+.DS_Store
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
diff --git a/README.md b/README.md
index efb425dc..7aef4f23 100644
--- a/README.md
+++ b/README.md
@@ -18,9 +18,9 @@ to parse and validate workflow definitions as well as generate the workflow diag
### Status
-| Latest Releases | Conformance to spec version |
-| :---: | :---: |
-| 4.0.0-SNAPSHOT (main branch) | [v0.8](https://github.com/serverlessworkflow/specification/tree/0.8.x) |
+| Latest Releases | Conformance to spec version |
+|:-----------------------------------------------------------------------:| :---: |
+| [4.1.0.Final](https://github.com/serverlessworkflow/sdk-java/releases/) | [v0.8](https://github.com/serverlessworkflow/specification/tree/0.8.x) |
| [3.0.0.Final](https://github.com/serverlessworkflow/sdk-java/releases/) | [v0.7](https://github.com/serverlessworkflow/specification/tree/0.7.x) |
| [2.0.0.Final](https://github.com/serverlessworkflow/sdk-java/releases/) | [v0.6](https://github.com/serverlessworkflow/specification/tree/0.6.x) |
| [1.0.3.Final](https://github.com/serverlessworkflow/sdk-java/releases/) | [v0.5](https://github.com/serverlessworkflow/specification/tree/0.5.x) |
@@ -64,31 +64,31 @@ b) Add the following dependencies to your pom.xml `dependencies` section:
io.serverlessworkflow
serverlessworkflow-api
- 4.0.0-SNAPSHOT
+ 4.1.0.Final
io.serverlessworkflow
serverlessworkflow-spi
- 4.0.0-SNAPSHOT
+ 4.1.0.Final
io.serverlessworkflow
serverlessworkflow-validation
- 4.0.0-SNAPSHOT
+ 4.1.0.Final
io.serverlessworkflow
serverlessworkflow-diagram
- 4.0.0-SNAPSHOT
+ 4.1.0.Final
io.serverlessworkflow
serverlessworkflow-util
- 4.0.0-SNAPSHOT
+ 4.1.0.Final
```
@@ -103,11 +103,11 @@ maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
b) Add the following dependencies to your build.gradle `dependencies` section:
```text
-implementation("io.serverlessworkflow:serverlessworkflow-api:4.0.0-SNAPSHOT")
-implementation("io.serverlessworkflow:serverlessworkflow-spi:4.0.0-SNAPSHOT")
-implementation("io.serverlessworkflow:serverlessworkflow-validation:4.0.0-SNAPSHOT")
-implementation("io.serverlessworkflow:serverlessworkflow-diagram:4.0.0-SNAPSHOT")
-implementation("io.serverlessworkflow:serverlessworkflow-util:4.0.0-SNAPSHOT")
+implementation("io.serverlessworkflow:serverlessworkflow-api:4.1.0.Final")
+implementation("io.serverlessworkflow:serverlessworkflow-spi:4.1.0.Final")
+implementation("io.serverlessworkflow:serverlessworkflow-validation:4.1.0.Final")
+implementation("io.serverlessworkflow:serverlessworkflow-diagram:4.1.0.Final")
+implementation("io.serverlessworkflow:serverlessworkflow-util:4.1.0.Final")
```
### How to Use
@@ -267,6 +267,22 @@ String diagramSVG = workflowDiagram.getSvgDiagram();
`diagramSVG` includes the diagram SVG source which you can then decide to save to a file,
print, or process further.
+In case default visualization of the workflow is not sufficient you can provide custom workflow template to be
+used while generating the SVG file. Easiest is to start off from the default template and customize it to your needs.
+
+Custom template must be on the classpath in `templates/plantuml` directory and must use `.txt` extension. Next
+template is set on `WorkflowDiagram` instance as shown below.
+
+``` java
+Workflow workflow = Workflow.fromSource(source);
+
+WorkflowDiagram workflowDiagram = new WorkflowDiagramImpl();
+workflowDiagram.setWorkflow(workflow);
+workflowDiagram.setTemplate("custom-template");
+
+String diagramSVG = workflowDiagram.getSvgDiagram();
+```
+
By default the diagram legend is now shown. If you want to enable it you can do:
``` java
@@ -296,15 +312,18 @@ Here are some generated diagrams from the specification examples (with legend en
Workflow utils provide a number of useful methods for extracting information from workflow definitions.
Once you have a `Workflow` instance, you can use it
##### Get Starting State
-```Java
+
+```java
State startingState = WorkflowUtils.getStartingState(workflow);
```
##### Get States by State Type
-```Java
+
+```java
List states = WorkflowUtils.getStates(workflow, DefaultState.Type.EVENT);
```
##### Get Consumed-Events, Produced-Events and their count
-```Java
+
+```java
List consumedEvents = WorkflowUtils.getWorkflowConsumedEvents(workflow);
int consumedEventsCount = WorkflowUtils.getWorkflowConsumedEventsCount(workflow);
@@ -312,7 +331,8 @@ State startingState = WorkflowUtils.getStartingState(workflow);
int producedEventsCount = WorkflowUtils.getWorkflowProducedEventsCount(workflow);
```
##### Get Defined Consumed-Events, Defined Produced-Events and their count
-```Java
+
+```java
List consumedEvents = WorkflowUtils.getWorkflowConsumedEventsCount(workflow);
int consumedEventsCount = WorkflowUtils.getWorkflowConsumedEventsCount(workflow);
@@ -320,12 +340,14 @@ State startingState = WorkflowUtils.getStartingState(workflow);
int producedEventsCount = WorkflowUtils.getWorkflowProducedEventsCount(workflow);
```
##### Get Function definitions which is used by an action
-```Java
+
+```java
FunctionDefinition finalizeApplicationFunctionDefinition =
WorkflowUtils.getFunctionDefinitionsForAction(workflow, "finalizeApplicationAction");
```
##### Get Actions which uses a Function definition
-```Java
+
+```java
List actionsForFunctionDefinition =
WorkflowUtils.getActionsForFunctionDefinition(workflow, functionRefName);
```
\ No newline at end of file
diff --git a/api/pom.xml b/api/pom.xml
index b20aaf22..c2d52765 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -1,17 +1,14 @@
-
+
4.0.0
io.serverlessworkflow
serverlessworkflow-parent
- 4.0.0-SNAPSHOT
+ 4.3.0-SNAPSHOT
serverlessworkflow-api
Serverless Workflow :: API
- ${project.parent.version}
jar
Java SDK for Serverless Workflow Specification
@@ -27,31 +24,19 @@
com.fasterxml.jackson.core
jackson-core
- [2.13.0,)
com.fasterxml.jackson.core
jackson-databind
- [2.13.0,)
com.fasterxml.jackson.dataformat
jackson-dataformat-yaml
- [2.13.0,)
- javax.validation
- validation-api
+ jakarta.validation
+ jakarta.validation-api
-
- org.json
- json
-
-
- com.github.erosb
- everit-json-schema
-
-
org.junit.jupiter
@@ -98,12 +83,13 @@
true
true
false
+ true
false
false
true
true
true
- 1.8
+ ${java.version}
true
@@ -122,13 +108,13 @@
-
-
+
+
-
-
+
+
@@ -156,21 +142,12 @@
- com.coveo
- fmt-maven-plugin
-
- src/main/java
- src/test/java
- false
- .*\.java
- false
- false
-
-
+ org.apache.maven.plugins
+ maven-jar-plugin
- format
+ test-jar
diff --git a/api/src/main/java/io/serverlessworkflow/api/deserializers/AuthDeserializer.java b/api/src/main/java/io/serverlessworkflow/api/deserializers/AuthDeserializer.java
new file mode 100644
index 00000000..aa078cb4
--- /dev/null
+++ b/api/src/main/java/io/serverlessworkflow/api/deserializers/AuthDeserializer.java
@@ -0,0 +1,88 @@
+/*
+ * 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.deserializers;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import io.serverlessworkflow.api.auth.AuthDefinition;
+import io.serverlessworkflow.api.interfaces.WorkflowPropertySource;
+import io.serverlessworkflow.api.utils.Utils;
+import io.serverlessworkflow.api.workflow.Auth;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AuthDeserializer extends StdDeserializer {
+
+ private static final long serialVersionUID = 520L;
+ private static Logger logger = LoggerFactory.getLogger(AuthDeserializer.class);
+
+ @SuppressWarnings("unused")
+ private WorkflowPropertySource context;
+
+ public AuthDeserializer() {
+ this(Auth.class);
+ }
+
+ public AuthDeserializer(Class> vc) {
+ super(vc);
+ }
+
+ public AuthDeserializer(WorkflowPropertySource context) {
+ this(Auth.class);
+ this.context = context;
+ }
+
+ @Override
+ public Auth deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
+
+ ObjectMapper mapper = (ObjectMapper) jp.getCodec();
+ JsonNode node = jp.getCodec().readTree(jp);
+
+ Auth auth = new Auth();
+ List authDefinitions = new ArrayList<>();
+
+ if (node.isArray()) {
+ for (final JsonNode nodeEle : node) {
+ authDefinitions.add(mapper.treeToValue(nodeEle, AuthDefinition.class));
+ }
+ } else {
+ String authFileDef = node.asText();
+ String authFileSrc = Utils.getResourceFileAsString(authFileDef);
+ if (authFileSrc != null && authFileSrc.trim().length() > 0) {
+ JsonNode authRefNode = Utils.getNode(authFileSrc);
+ JsonNode refAuth = authRefNode.get("auth");
+ if (refAuth != null) {
+ for (final JsonNode nodeEle : refAuth) {
+ authDefinitions.add(mapper.treeToValue(nodeEle, AuthDefinition.class));
+ }
+ } else {
+ logger.error("Unable to find auth definitions in reference file: {}", authFileSrc);
+ }
+
+ } else {
+ logger.error("Unable to load auth defs reference file: {}", authFileSrc);
+ }
+ }
+ auth.setAuthDefs(authDefinitions);
+ return auth;
+ }
+}
diff --git a/api/src/main/java/io/serverlessworkflow/api/deserializers/ConstantsDeserializer.java b/api/src/main/java/io/serverlessworkflow/api/deserializers/ConstantsDeserializer.java
index c3789b52..3859273c 100644
--- a/api/src/main/java/io/serverlessworkflow/api/deserializers/ConstantsDeserializer.java
+++ b/api/src/main/java/io/serverlessworkflow/api/deserializers/ConstantsDeserializer.java
@@ -18,14 +18,11 @@
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import io.serverlessworkflow.api.interfaces.WorkflowPropertySource;
import io.serverlessworkflow.api.utils.Utils;
import io.serverlessworkflow.api.workflow.Constants;
import java.io.IOException;
-import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -63,21 +60,8 @@ public Constants deserialize(JsonParser jp, DeserializationContext ctxt) throws
} else {
String constantsFileDef = node.asText();
String constantsFileSrc = Utils.getResourceFileAsString(constantsFileDef);
- JsonNode constantsRefNode;
- ObjectMapper jsonWriter = new ObjectMapper();
if (constantsFileSrc != null && constantsFileSrc.trim().length() > 0) {
- // if its a yaml def convert to json first
- if (!constantsFileSrc.trim().startsWith("{")) {
- // convert yaml to json to validate
- ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
- Object obj = yamlReader.readValue(constantsFileSrc, Object.class);
-
- constantsRefNode =
- jsonWriter.readTree(new JSONObject(jsonWriter.writeValueAsString(obj)).toString());
- } else {
- constantsRefNode = jsonWriter.readTree(new JSONObject(constantsFileSrc).toString());
- }
-
+ JsonNode constantsRefNode = Utils.getNode(constantsFileSrc);
JsonNode refConstants = constantsRefNode.get("constants");
if (refConstants != null) {
constantsDefinition = refConstants;
diff --git a/api/src/main/java/io/serverlessworkflow/api/deserializers/ErrorsDeserializer.java b/api/src/main/java/io/serverlessworkflow/api/deserializers/ErrorsDeserializer.java
index beedc7dd..6fe366ea 100644
--- a/api/src/main/java/io/serverlessworkflow/api/deserializers/ErrorsDeserializer.java
+++ b/api/src/main/java/io/serverlessworkflow/api/deserializers/ErrorsDeserializer.java
@@ -20,7 +20,6 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import io.serverlessworkflow.api.error.ErrorDefinition;
import io.serverlessworkflow.api.interfaces.WorkflowPropertySource;
import io.serverlessworkflow.api.utils.Utils;
@@ -28,7 +27,6 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -69,21 +67,8 @@ public Errors deserialize(JsonParser jp, DeserializationContext ctxt) throws IOE
} else {
String errorsFileDef = node.asText();
String errorsFileSrc = Utils.getResourceFileAsString(errorsFileDef);
- JsonNode errorsRefNode;
- ObjectMapper jsonWriter = new ObjectMapper();
if (errorsFileSrc != null && errorsFileSrc.trim().length() > 0) {
- // if its a yaml def convert to json first
- if (!errorsFileSrc.trim().startsWith("{")) {
- // convert yaml to json to validate
- ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
- Object obj = yamlReader.readValue(errorsFileSrc, Object.class);
-
- errorsRefNode =
- jsonWriter.readTree(new JSONObject(jsonWriter.writeValueAsString(obj)).toString());
- } else {
- errorsRefNode = jsonWriter.readTree(new JSONObject(errorsFileSrc).toString());
- }
-
+ JsonNode errorsRefNode = Utils.getNode(errorsFileSrc);
JsonNode refErrors = errorsRefNode.get("errors");
if (refErrors != null) {
for (final JsonNode nodeEle : refErrors) {
diff --git a/api/src/main/java/io/serverlessworkflow/api/deserializers/EventsDeserializer.java b/api/src/main/java/io/serverlessworkflow/api/deserializers/EventsDeserializer.java
index cfa207df..a02fdf4b 100644
--- a/api/src/main/java/io/serverlessworkflow/api/deserializers/EventsDeserializer.java
+++ b/api/src/main/java/io/serverlessworkflow/api/deserializers/EventsDeserializer.java
@@ -20,7 +20,6 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import io.serverlessworkflow.api.events.EventDefinition;
import io.serverlessworkflow.api.interfaces.WorkflowPropertySource;
import io.serverlessworkflow.api.utils.Utils;
@@ -28,7 +27,6 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -69,21 +67,9 @@ public Events deserialize(JsonParser jp, DeserializationContext ctxt) throws IOE
} else {
String eventsFileDef = node.asText();
String eventsFileSrc = Utils.getResourceFileAsString(eventsFileDef);
- JsonNode eventsRefNode;
- ObjectMapper jsonWriter = new ObjectMapper();
if (eventsFileSrc != null && eventsFileSrc.trim().length() > 0) {
// if its a yaml def convert to json first
- if (!eventsFileSrc.trim().startsWith("{")) {
- // convert yaml to json to validate
- ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
- Object obj = yamlReader.readValue(eventsFileSrc, Object.class);
-
- eventsRefNode =
- jsonWriter.readTree(new JSONObject(jsonWriter.writeValueAsString(obj)).toString());
- } else {
- eventsRefNode = jsonWriter.readTree(new JSONObject(eventsFileSrc).toString());
- }
-
+ JsonNode eventsRefNode = Utils.getNode(eventsFileSrc);
JsonNode refEvents = eventsRefNode.get("events");
if (refEvents != null) {
for (final JsonNode nodeEle : refEvents) {
diff --git a/api/src/main/java/io/serverlessworkflow/api/deserializers/FunctionsDeserializer.java b/api/src/main/java/io/serverlessworkflow/api/deserializers/FunctionsDeserializer.java
index c27e2c48..b706b2d3 100644
--- a/api/src/main/java/io/serverlessworkflow/api/deserializers/FunctionsDeserializer.java
+++ b/api/src/main/java/io/serverlessworkflow/api/deserializers/FunctionsDeserializer.java
@@ -20,7 +20,6 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import io.serverlessworkflow.api.functions.FunctionDefinition;
import io.serverlessworkflow.api.interfaces.WorkflowPropertySource;
import io.serverlessworkflow.api.utils.Utils;
@@ -28,7 +27,6 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -69,20 +67,9 @@ public Functions deserialize(JsonParser jp, DeserializationContext ctxt) throws
String functionsFileDef = node.asText();
String functionsFileSrc = Utils.getResourceFileAsString(functionsFileDef);
JsonNode functionsRefNode;
- ObjectMapper jsonWriter = new ObjectMapper();
if (functionsFileSrc != null && functionsFileSrc.trim().length() > 0) {
// if its a yaml def convert to json first
- if (!functionsFileSrc.trim().startsWith("{")) {
- // convert yaml to json to validate
- ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
- Object obj = yamlReader.readValue(functionsFileSrc, Object.class);
-
- functionsRefNode =
- jsonWriter.readTree(new JSONObject(jsonWriter.writeValueAsString(obj)).toString());
- } else {
- functionsRefNode = jsonWriter.readTree(new JSONObject(functionsFileSrc).toString());
- }
-
+ functionsRefNode = Utils.getNode(functionsFileSrc);
JsonNode refFunctions = functionsRefNode.get("functions");
if (refFunctions != null) {
for (final JsonNode nodeEle : refFunctions) {
diff --git a/api/src/main/java/io/serverlessworkflow/api/deserializers/RetriesDeserializer.java b/api/src/main/java/io/serverlessworkflow/api/deserializers/RetriesDeserializer.java
index ff2fe44d..9eb47b5f 100644
--- a/api/src/main/java/io/serverlessworkflow/api/deserializers/RetriesDeserializer.java
+++ b/api/src/main/java/io/serverlessworkflow/api/deserializers/RetriesDeserializer.java
@@ -20,7 +20,6 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import io.serverlessworkflow.api.interfaces.WorkflowPropertySource;
import io.serverlessworkflow.api.retry.RetryDefinition;
import io.serverlessworkflow.api.utils.Utils;
@@ -28,7 +27,6 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -69,21 +67,8 @@ public Retries deserialize(JsonParser jp, DeserializationContext ctxt) throws IO
} else {
String retriesFileDef = node.asText();
String retriesFileSrc = Utils.getResourceFileAsString(retriesFileDef);
- JsonNode retriesRefNode;
- ObjectMapper jsonWriter = new ObjectMapper();
if (retriesFileSrc != null && retriesFileSrc.trim().length() > 0) {
- // if its a yaml def convert to json first
- if (!retriesFileSrc.trim().startsWith("{")) {
- // convert yaml to json to validate
- ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
- Object obj = yamlReader.readValue(retriesFileSrc, Object.class);
-
- retriesRefNode =
- jsonWriter.readTree(new JSONObject(jsonWriter.writeValueAsString(obj)).toString());
- } else {
- retriesRefNode = jsonWriter.readTree(new JSONObject(retriesFileSrc).toString());
- }
-
+ JsonNode retriesRefNode = Utils.getNode(retriesFileSrc);
JsonNode refRetries = retriesRefNode.get("retries");
if (refRetries != null) {
for (final JsonNode nodeEle : refRetries) {
diff --git a/api/src/main/java/io/serverlessworkflow/api/deserializers/SecretsDeserializer.java b/api/src/main/java/io/serverlessworkflow/api/deserializers/SecretsDeserializer.java
index e9ec05e7..60cc2a82 100644
--- a/api/src/main/java/io/serverlessworkflow/api/deserializers/SecretsDeserializer.java
+++ b/api/src/main/java/io/serverlessworkflow/api/deserializers/SecretsDeserializer.java
@@ -18,16 +18,13 @@
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import io.serverlessworkflow.api.interfaces.WorkflowPropertySource;
import io.serverlessworkflow.api.utils.Utils;
import io.serverlessworkflow.api.workflow.Secrets;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -66,21 +63,9 @@ public Secrets deserialize(JsonParser jp, DeserializationContext ctxt) throws IO
} else {
String secretsFileDef = node.asText();
String secretsFileSrc = Utils.getResourceFileAsString(secretsFileDef);
- JsonNode secretsRefNode;
- ObjectMapper jsonWriter = new ObjectMapper();
if (secretsFileSrc != null && secretsFileSrc.trim().length() > 0) {
// if its a yaml def convert to json first
- if (!secretsFileSrc.trim().startsWith("{")) {
- // convert yaml to json to validate
- ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
- Object obj = yamlReader.readValue(secretsFileSrc, Object.class);
-
- secretsRefNode =
- jsonWriter.readTree(new JSONObject(jsonWriter.writeValueAsString(obj)).toString());
- } else {
- secretsRefNode = jsonWriter.readTree(new JSONObject(secretsFileSrc).toString());
- }
-
+ JsonNode secretsRefNode = Utils.getNode(secretsFileSrc);
JsonNode refSecrets = secretsRefNode.get("secrets");
if (refSecrets != null) {
for (final JsonNode nodeEle : refSecrets) {
diff --git a/api/src/main/java/io/serverlessworkflow/api/interfaces/WorkflowDiagram.java b/api/src/main/java/io/serverlessworkflow/api/interfaces/WorkflowDiagram.java
index 6bba312c..0c62d4d2 100644
--- a/api/src/main/java/io/serverlessworkflow/api/interfaces/WorkflowDiagram.java
+++ b/api/src/main/java/io/serverlessworkflow/api/interfaces/WorkflowDiagram.java
@@ -22,6 +22,8 @@ public interface WorkflowDiagram {
WorkflowDiagram setSource(String source);
+ WorkflowDiagram setTemplate(String template);
+
String getSvgDiagram() throws Exception;
WorkflowDiagram showLegend(boolean showLegend);
diff --git a/api/src/main/java/io/serverlessworkflow/api/mapper/JsonObjectMapperFactory.java b/api/src/main/java/io/serverlessworkflow/api/mapper/JsonObjectMapperFactory.java
new file mode 100644
index 00000000..eb34b0eb
--- /dev/null
+++ b/api/src/main/java/io/serverlessworkflow/api/mapper/JsonObjectMapperFactory.java
@@ -0,0 +1,27 @@
+/*
+ * 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.mapper;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class JsonObjectMapperFactory {
+
+ private static final ObjectMapper instance = new JsonObjectMapper();
+
+ public static final ObjectMapper mapper() {
+ return instance;
+ }
+}
diff --git a/api/src/main/java/io/serverlessworkflow/api/mapper/WorkflowModule.java b/api/src/main/java/io/serverlessworkflow/api/mapper/WorkflowModule.java
index 1486d32a..5b42598d 100644
--- a/api/src/main/java/io/serverlessworkflow/api/mapper/WorkflowModule.java
+++ b/api/src/main/java/io/serverlessworkflow/api/mapper/WorkflowModule.java
@@ -42,11 +42,11 @@
public class WorkflowModule extends SimpleModule {
- private static final long serialVersionUID = 510l;
+ private static final long serialVersionUID = 510L;
- private WorkflowPropertySource workflowPropertySource;
- private ExtensionSerializer extensionSerializer;
- private ExtensionDeserializer extensionDeserializer;
+ private final WorkflowPropertySource workflowPropertySource;
+ private final ExtensionSerializer extensionSerializer;
+ private final ExtensionDeserializer extensionDeserializer;
public WorkflowModule() {
this(null);
@@ -121,6 +121,7 @@ private void addDefaultDeserializers() {
StateExecTimeout.class, new StateExecTimeoutDeserializer(workflowPropertySource));
addDeserializer(Errors.class, new ErrorsDeserializer(workflowPropertySource));
addDeserializer(ContinueAs.class, new ContinueAsDeserializer(workflowPropertySource));
+ addDeserializer(Auth.class, new AuthDeserializer(workflowPropertySource));
}
public ExtensionSerializer getExtensionSerializer() {
diff --git a/api/src/main/java/io/serverlessworkflow/api/mapper/YamlObjectMapperFactory.java b/api/src/main/java/io/serverlessworkflow/api/mapper/YamlObjectMapperFactory.java
new file mode 100644
index 00000000..04371db4
--- /dev/null
+++ b/api/src/main/java/io/serverlessworkflow/api/mapper/YamlObjectMapperFactory.java
@@ -0,0 +1,27 @@
+/*
+ * 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.mapper;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class YamlObjectMapperFactory {
+
+ private static final ObjectMapper instance = new YamlObjectMapper();
+
+ public static final ObjectMapper mapper() {
+ return instance;
+ }
+}
diff --git a/api/src/main/java/io/serverlessworkflow/api/schemaclient/ResourceSchemaClient.java b/api/src/main/java/io/serverlessworkflow/api/schemaclient/ResourceSchemaClient.java
deleted file mode 100644
index a4da2387..00000000
--- a/api/src/main/java/io/serverlessworkflow/api/schemaclient/ResourceSchemaClient.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2020-Present The Serverless Workflow Specification Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package io.serverlessworkflow.api.schemaclient;
-
-import java.io.InputStream;
-import java.util.Objects;
-import org.everit.json.schema.loader.SchemaClient;
-
-public class ResourceSchemaClient implements SchemaClient {
-
- @SuppressWarnings("unused")
- private final SchemaClient fallbackClient;
-
- private final String baseResourcePath = "/schema/";
-
- public ResourceSchemaClient(SchemaClient fallbackClient) {
- this.fallbackClient = Objects.requireNonNull(fallbackClient, "fallbackClient cannot be null");
- }
-
- @Override
- public InputStream get(String path) {
- path = path.substring("https://wg-serverless.org/".length());
- return this.getClass().getResourceAsStream(baseResourcePath + path);
- }
-}
diff --git a/api/src/main/java/io/serverlessworkflow/api/serializers/FunctionRefSerializer.java b/api/src/main/java/io/serverlessworkflow/api/serializers/FunctionRefSerializer.java
index cf5ce81f..71f61606 100644
--- a/api/src/main/java/io/serverlessworkflow/api/serializers/FunctionRefSerializer.java
+++ b/api/src/main/java/io/serverlessworkflow/api/serializers/FunctionRefSerializer.java
@@ -41,12 +41,12 @@ public void serialize(FunctionRef functionRef, JsonGenerator gen, SerializerProv
&& (functionRef.getInvoke() == null
|| functionRef.getInvoke().equals(FunctionRef.Invoke.SYNC))
&& functionRef.getRefName() != null
- && functionRef.getRefName().length() > 0) {
+ && !functionRef.getRefName().isEmpty()) {
gen.writeString(functionRef.getRefName());
} else {
gen.writeStartObject();
- if (functionRef.getRefName() != null && functionRef.getRefName().length() > 0) {
+ if (functionRef.getRefName() != null && !functionRef.getRefName().isEmpty()) {
gen.writeStringField("refName", functionRef.getRefName());
}
@@ -54,7 +54,7 @@ public void serialize(FunctionRef functionRef, JsonGenerator gen, SerializerProv
gen.writeObjectField("arguments", functionRef.getArguments());
}
- if (functionRef.getSelectionSet() != null && functionRef.getSelectionSet().length() > 0) {
+ if (functionRef.getSelectionSet() != null && !functionRef.getSelectionSet().isEmpty()) {
gen.writeStringField("selectionSet", functionRef.getSelectionSet());
}
diff --git a/api/src/main/java/io/serverlessworkflow/api/serializers/WorkflowSerializer.java b/api/src/main/java/io/serverlessworkflow/api/serializers/WorkflowSerializer.java
index b45871fa..b5ac7cbb 100644
--- a/api/src/main/java/io/serverlessworkflow/api/serializers/WorkflowSerializer.java
+++ b/api/src/main/java/io/serverlessworkflow/api/serializers/WorkflowSerializer.java
@@ -15,198 +15,171 @@
*/
package io.serverlessworkflow.api.serializers;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.util.List;
+import java.util.UUID;
+
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import io.serverlessworkflow.api.Workflow;
-import io.serverlessworkflow.api.error.ErrorDefinition;
-import io.serverlessworkflow.api.events.EventDefinition;
-import io.serverlessworkflow.api.functions.FunctionDefinition;
import io.serverlessworkflow.api.interfaces.Extension;
import io.serverlessworkflow.api.interfaces.State;
-import io.serverlessworkflow.api.retry.RetryDefinition;
-import java.io.IOException;
-import java.security.MessageDigest;
-import java.util.UUID;
public class WorkflowSerializer extends StdSerializer {
- public WorkflowSerializer() {
- this(Workflow.class);
- }
-
- protected WorkflowSerializer(Class t) {
- super(t);
- }
-
- private static final char[] hexArray = "0123456789ABCDEF".toCharArray();
-
- @Override
- public void serialize(Workflow workflow, JsonGenerator gen, SerializerProvider provider)
- throws IOException {
-
- gen.writeStartObject();
-
- if (workflow.getId() != null && !workflow.getId().isEmpty()) {
- gen.writeStringField("id", workflow.getId());
- } else {
- gen.writeStringField("id", generateUniqueId());
- }
-
- gen.writeStringField("name", workflow.getName());
-
- if (workflow.getDescription() != null && !workflow.getDescription().isEmpty()) {
- gen.writeStringField("description", workflow.getDescription());
- }
-
- if (workflow.getVersion() != null && !workflow.getVersion().isEmpty()) {
- gen.writeStringField("version", workflow.getVersion());
- }
-
- if (workflow.getDataInputSchema() != null) {
- if (workflow.getDataInputSchema().getSchema() != null
- && workflow.getDataInputSchema().getSchema().length() > 0
- && workflow.getDataInputSchema().isFailOnValidationErrors()) {
- gen.writeStringField("dataInputSchema", workflow.getDataInputSchema().getSchema());
-
- } else if (workflow.getDataInputSchema().getSchema() != null
- && workflow.getDataInputSchema().getSchema().length() > 0
- && !workflow.getDataInputSchema().isFailOnValidationErrors()) {
- gen.writeObjectField("dataInputSchema", workflow.getDataInputSchema());
- }
- }
-
- if (workflow.getStart() != null) {
- gen.writeObjectField("start", workflow.getStart());
- }
-
- if (workflow.getSpecVersion() != null && !workflow.getSpecVersion().isEmpty()) {
- gen.writeStringField("specVersion", workflow.getSpecVersion());
- }
-
- if (workflow.getExtensions() != null && !workflow.getExpressionLang().isEmpty()) {
- gen.writeStringField("expressionLang", workflow.getExpressionLang());
- }
-
- if (workflow.isKeepActive()) {
- gen.writeBooleanField("keepActive", workflow.isKeepActive());
- }
-
- if (workflow.isAutoRetries()) {
- gen.writeBooleanField("autoRetries", workflow.isAutoRetries());
- }
-
- if (workflow.getMetadata() != null && !workflow.getMetadata().isEmpty()) {
- gen.writeObjectField("metadata", workflow.getMetadata());
- }
-
- if (workflow.getEvents() != null && !workflow.getEvents().getEventDefs().isEmpty()) {
- gen.writeArrayFieldStart("events");
- for (EventDefinition eventDefinition : workflow.getEvents().getEventDefs()) {
- gen.writeObject(eventDefinition);
- }
- gen.writeEndArray();
- } else {
- gen.writeArrayFieldStart("events");
- gen.writeEndArray();
- }
-
- if (workflow.getFunctions() != null && !workflow.getFunctions().getFunctionDefs().isEmpty()) {
- gen.writeArrayFieldStart("functions");
- for (FunctionDefinition function : workflow.getFunctions().getFunctionDefs()) {
- gen.writeObject(function);
- }
- gen.writeEndArray();
- } else {
- gen.writeArrayFieldStart("functions");
- gen.writeEndArray();
- }
-
- if (workflow.getRetries() != null && !workflow.getRetries().getRetryDefs().isEmpty()) {
- gen.writeArrayFieldStart("retries");
- for (RetryDefinition retry : workflow.getRetries().getRetryDefs()) {
- gen.writeObject(retry);
- }
- gen.writeEndArray();
- } else {
- gen.writeArrayFieldStart("retries");
- gen.writeEndArray();
- }
-
- if (workflow.getErrors() != null && !workflow.getErrors().getErrorDefs().isEmpty()) {
- gen.writeArrayFieldStart("errors");
- for (ErrorDefinition error : workflow.getErrors().getErrorDefs()) {
- gen.writeObject(error);
- }
- gen.writeEndArray();
- } else {
- gen.writeArrayFieldStart("errors");
- gen.writeEndArray();
- }
-
- if (workflow.getSecrets() != null && !workflow.getSecrets().getSecretDefs().isEmpty()) {
- gen.writeArrayFieldStart("secrets");
- for (String secretDef : workflow.getSecrets().getSecretDefs()) {
- gen.writeString(secretDef);
- }
- gen.writeEndArray();
- } else {
- gen.writeArrayFieldStart("secrets");
- gen.writeEndArray();
- }
-
- if (workflow.getConstants() != null && !workflow.getConstants().getConstantsDef().isEmpty()) {
- gen.writeObjectField("constants", workflow.getConstants().getConstantsDef());
- }
-
- if (workflow.getTimeouts() != null) {
- gen.writeObjectField("timeouts", workflow.getTimeouts());
- }
-
- if (workflow.getAuth() != null) {
- gen.writeObjectField("auth", workflow.getAuth());
- }
-
- if (workflow.getStates() != null && !workflow.getStates().isEmpty()) {
- gen.writeArrayFieldStart("states");
- for (State state : workflow.getStates()) {
- gen.writeObject(state);
- }
- gen.writeEndArray();
- } else {
- gen.writeArrayFieldStart("states");
- gen.writeEndArray();
- }
-
- if (workflow.getExtensions() != null && !workflow.getExtensions().isEmpty()) {
- gen.writeArrayFieldStart("extensions");
- for (Extension extension : workflow.getExtensions()) {
- gen.writeObject(extension);
- }
- gen.writeEndArray();
- }
-
- gen.writeEndObject();
- }
-
- protected static String generateUniqueId() {
- try {
- MessageDigest salt = MessageDigest.getInstance("SHA-256");
-
- salt.update(UUID.randomUUID().toString().getBytes("UTF-8"));
- return bytesToHex(salt.digest());
- } catch (Exception e) {
- return UUID.randomUUID().toString();
- }
- }
-
- protected static String bytesToHex(byte[] bytes) {
- char[] hexChars = new char[bytes.length * 2];
- for (int j = 0; j < bytes.length; j++) {
- int v = bytes[j] & 0xFF;
- hexChars[j * 2] = hexArray[v >>> 4];
- hexChars[j * 2 + 1] = hexArray[v & 0x0F];
+ private static final char[] hexArray = "0123456789ABCDEF".toCharArray();
+
+ public WorkflowSerializer() {
+ this(Workflow.class);
+ }
+
+ protected WorkflowSerializer(Class t) {
+ super(t);
+ }
+
+ protected static String generateUniqueId() {
+ try {
+ MessageDigest salt = MessageDigest.getInstance("SHA-256");
+ salt.update(UUID.randomUUID().toString().getBytes("UTF-8"));
+ return bytesToHex(salt.digest());
+ } catch (Exception e) {
+ return UUID.randomUUID().toString();
+ }
+ }
+
+ protected static String bytesToHex(byte[] bytes) {
+ char[] hexChars = new char[bytes.length * 2];
+ for (int j = 0; j < bytes.length; j++) {
+ int v = bytes[j] & 0xFF;
+ hexChars[j * 2] = hexArray[v >>> 4];
+ hexChars[j * 2 + 1] = hexArray[v & 0x0F];
+ }
+ return new String(hexChars);
+ }
+
+ // Helper to write either an array of items or a single string reference
+ private void writeListOrRef(
+ JsonGenerator gen,
+ SerializerProvider prov,
+ String fieldName,
+ List items,
+ String refValue) throws IOException {
+ if (items != null && !items.isEmpty()) {
+ gen.writeArrayFieldStart(fieldName);
+ for (T item : items) {
+ prov.defaultSerializeValue(item, gen);
+ }
+ gen.writeEndArray();
+ } else if (refValue != null) {
+ gen.writeStringField(fieldName, refValue);
+ }
+ }
+
+ @Override
+ public void serialize(Workflow workflow, JsonGenerator gen, SerializerProvider provider)
+ throws IOException {
+
+ gen.writeStartObject();
+
+ // --- ID / key / basic fields ---
+ if (workflow.getId() != null && !workflow.getId().isEmpty()) {
+ gen.writeStringField("id", workflow.getId());
+ } else {
+ gen.writeStringField("id", generateUniqueId());
+ }
+ if (workflow.getKey() != null) {
+ gen.writeStringField("key", workflow.getKey());
+ }
+ gen.writeStringField("name", workflow.getName());
+ if (workflow.getDescription() != null && !workflow.getDescription().isEmpty()) {
+ gen.writeStringField("description", workflow.getDescription());
+ }
+ if (workflow.getVersion() != null && !workflow.getVersion().isEmpty()) {
+ gen.writeStringField("version", workflow.getVersion());
+ }
+ if (workflow.getAnnotations() != null && !workflow.getAnnotations().isEmpty()) {
+ gen.writeObjectField("annotations", workflow.getAnnotations());
+ }
+ if (workflow.getDataInputSchema() != null) {
+ if (workflow.getDataInputSchema().getSchema() != null
+ && !workflow.getDataInputSchema().getSchema().isEmpty()
+ && workflow.getDataInputSchema().isFailOnValidationErrors()) {
+ gen.writeStringField("dataInputSchema", workflow.getDataInputSchema().getSchema());
+ } else if (workflow.getDataInputSchema().getSchema() != null
+ && !workflow.getDataInputSchema().getSchema().isEmpty()
+ && !workflow.getDataInputSchema().isFailOnValidationErrors()) {
+ gen.writeObjectField("dataInputSchema", workflow.getDataInputSchema());
+ }
+ }
+ if (workflow.getStart() != null) {
+ gen.writeObjectField("start", workflow.getStart());
+ }
+ if (workflow.getSpecVersion() != null && !workflow.getSpecVersion().isEmpty()) {
+ gen.writeStringField("specVersion", workflow.getSpecVersion());
+ }
+ if (workflow.getExpressionLang() != null && !workflow.getExpressionLang().isEmpty()) {
+ gen.writeStringField("expressionLang", workflow.getExpressionLang());
+ }
+ if (workflow.isKeepActive()) {
+ gen.writeBooleanField("keepActive", workflow.isKeepActive());
+ }
+ if (workflow.isAutoRetries()) {
+ gen.writeBooleanField("autoRetries", workflow.isAutoRetries());
+ }
+ if (workflow.getMetadata() != null && !workflow.getMetadata().isEmpty()) {
+ gen.writeObjectField("metadata", workflow.getMetadata());
+ }
+
+ // --- Collections or references ---
+ if (workflow.getEvents() != null) {
+ writeListOrRef(gen, provider,
+ "events",
+ workflow.getEvents().getEventDefs(),
+ workflow.getEvents().getRefValue());
+ }
+ if (workflow.getFunctions() != null) {
+ writeListOrRef(gen, provider,
+ "functions",
+ workflow.getFunctions().getFunctionDefs(),
+ workflow.getFunctions().getRefValue());
+ }
+ if (workflow.getRetries() != null) {
+ writeListOrRef(gen, provider,
+ "retries",
+ workflow.getRetries().getRetryDefs(),
+ workflow.getRetries().getRefValue());
+ }
+ if (workflow.getErrors() != null) {
+ writeListOrRef(gen, provider,
+ "errors",
+ workflow.getErrors().getErrorDefs(),
+ workflow.getErrors().getRefValue());
+ }
+ if (workflow.getSecrets() != null) {
+ writeListOrRef(gen, provider,
+ "secrets",
+ workflow.getSecrets().getSecretDefs(),
+ workflow.getSecrets().getRefValue());
+ }
+
+ // --- Always-array fields ---
+ if (workflow.getStates() != null && !workflow.getStates().isEmpty()) {
+ gen.writeArrayFieldStart("states");
+ for (State state : workflow.getStates()) {
+ gen.writeObject(state);
+ }
+ gen.writeEndArray();
+ }
+ if (workflow.getExtensions() != null && !workflow.getExtensions().isEmpty()) {
+ gen.writeArrayFieldStart("extensions");
+ for (Extension ext : workflow.getExtensions()) {
+ gen.writeObject(ext);
+ }
+ gen.writeEndArray();
+ }
+
+ gen.writeEndObject();
}
- return new String(hexChars);
- }
}
diff --git a/api/src/main/java/io/serverlessworkflow/api/utils/Utils.java b/api/src/main/java/io/serverlessworkflow/api/utils/Utils.java
index 3e4b4274..9bdce416 100644
--- a/api/src/main/java/io/serverlessworkflow/api/utils/Utils.java
+++ b/api/src/main/java/io/serverlessworkflow/api/utils/Utils.java
@@ -15,6 +15,11 @@
*/
package io.serverlessworkflow.api.utils;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.serverlessworkflow.api.mapper.JsonObjectMapperFactory;
+import io.serverlessworkflow.api.mapper.YamlObjectMapperFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
@@ -34,4 +39,14 @@ public static String getResourceFileAsString(String fileName) throws IOException
}
}
}
+
+ public static ObjectMapper getObjectMapper(String source) {
+ return !source.trim().startsWith("{")
+ ? YamlObjectMapperFactory.mapper()
+ : JsonObjectMapperFactory.mapper();
+ }
+
+ public static JsonNode getNode(String source) throws JsonProcessingException {
+ return getObjectMapper(source).readTree(source);
+ }
}
diff --git a/api/src/main/java/io/serverlessworkflow/api/validation/WorkflowSchemaLoader.java b/api/src/main/java/io/serverlessworkflow/api/validation/WorkflowSchemaLoader.java
index 830bb50a..847380fb 100644
--- a/api/src/main/java/io/serverlessworkflow/api/validation/WorkflowSchemaLoader.java
+++ b/api/src/main/java/io/serverlessworkflow/api/validation/WorkflowSchemaLoader.java
@@ -15,26 +15,23 @@
*/
package io.serverlessworkflow.api.validation;
-import io.serverlessworkflow.api.schemaclient.ResourceSchemaClient;
-import org.everit.json.schema.Schema;
-import org.everit.json.schema.loader.SchemaLoader;
-import org.everit.json.schema.loader.internal.DefaultSchemaClient;
-import org.json.JSONObject;
-import org.json.JSONTokener;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.io.UncheckedIOException;
public class WorkflowSchemaLoader {
- private static final JSONObject workflowSchema =
- new JSONObject(
- new JSONTokener(WorkflowSchemaLoader.class.getResourceAsStream("/schema/workflow.json")));
- public static Schema getWorkflowSchema() {
- SchemaLoader schemaLoader =
- SchemaLoader.builder()
- .schemaClient(new ResourceSchemaClient(new DefaultSchemaClient()))
- .schemaJson(workflowSchema)
- .resolutionScope("classpath:schema")
- .draftV7Support()
- .build();
- return schemaLoader.load().build();
+ public static JsonNode getWorkflowSchema() {
+ try {
+ return ObjectMapperHolder.objectMapper.readTree(
+ WorkflowSchemaLoader.class.getResourceAsStream("/schema/workflow.json"));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private static class ObjectMapperHolder {
+ public static final ObjectMapper objectMapper = new ObjectMapper();
}
}
diff --git a/api/src/main/java/io/serverlessworkflow/api/workflow/Auth.java b/api/src/main/java/io/serverlessworkflow/api/workflow/Auth.java
new file mode 100644
index 00000000..e7828370
--- /dev/null
+++ b/api/src/main/java/io/serverlessworkflow/api/workflow/Auth.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2022-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.workflow;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import io.serverlessworkflow.api.auth.AuthDefinition;
+
+public class Auth {
+ private String refValue;
+ private List authDefs;
+
+ public Auth() {
+ }
+
+ public Auth(AuthDefinition authDef) {
+ this.authDefs = new ArrayList<>();
+ this.authDefs.add(authDef);
+ }
+
+ public Auth(List authDefs) {
+ this.authDefs = authDefs;
+ }
+
+ public Auth(String refValue) {
+ this.refValue = refValue;
+ }
+
+ public String getRefValue() {
+ return refValue;
+ }
+
+ public void setRefValue(String refValue) {
+ this.refValue = refValue;
+ }
+
+ public List getAuthDefs() {
+ if (authDefs == null) {
+ return Collections.emptyList();
+ }
+ return authDefs;
+ }
+
+ public void setAuthDefs(List authDefs) {
+ this.authDefs = authDefs;
+ }
+}
diff --git a/api/src/main/java/io/serverlessworkflow/api/workflow/BaseWorkflow.java b/api/src/main/java/io/serverlessworkflow/api/workflow/BaseWorkflow.java
index 61692caf..e3fc3d55 100644
--- a/api/src/main/java/io/serverlessworkflow/api/workflow/BaseWorkflow.java
+++ b/api/src/main/java/io/serverlessworkflow/api/workflow/BaseWorkflow.java
@@ -15,6 +15,9 @@
*/
package io.serverlessworkflow.api.workflow;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
@@ -23,53 +26,53 @@
import io.serverlessworkflow.api.Workflow;
import io.serverlessworkflow.api.mapper.JsonObjectMapper;
import io.serverlessworkflow.api.mapper.YamlObjectMapper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-/** Base Workflow provides some extra functionality for the Workflow types */
+/**
+ * Base Workflow provides some extra functionality for the Workflow types
+ */
public class BaseWorkflow {
- private static JsonObjectMapper jsonObjectMapper = new JsonObjectMapper();
- private static YamlObjectMapper yamlObjectMapper = new YamlObjectMapper();
+ private static JsonObjectMapper jsonObjectMapper = new JsonObjectMapper();
+ private static YamlObjectMapper yamlObjectMapper = new YamlObjectMapper();
- private static Logger logger = LoggerFactory.getLogger(BaseWorkflow.class);
+ private static Logger logger = LoggerFactory.getLogger(BaseWorkflow.class);
- public static Workflow fromSource(String source) {
- // try it as json markup first, if fails try yaml
- try {
- return jsonObjectMapper.readValue(source, Workflow.class);
- } catch (Exception e) {
- logger.info("Unable to convert as json markup, trying as yaml");
- try {
- return yamlObjectMapper.readValue(source, Workflow.class);
- } catch (Exception ee) {
- throw new IllegalArgumentException(
- "Could not convert markup to Workflow: " + ee.getMessage());
- }
+ public static Workflow fromSource(String source) {
+ // try it as json markup first, if fails try yaml
+ try {
+ return jsonObjectMapper.readValue(source, Workflow.class);
+ } catch (Exception e) {
+ logger.info("Unable to convert as json markup, trying as yaml");
+ try {
+ return yamlObjectMapper.readValue(source, Workflow.class);
+ } catch (Exception ee) {
+ throw new IllegalArgumentException(
+ "Could not convert markup to Workflow: " + ee.getMessage());
+ }
+ }
}
- }
- public static String toJson(Workflow workflow) {
- try {
- return jsonObjectMapper.writeValueAsString(workflow);
- } catch (JsonProcessingException e) {
- logger.error("Error mapping to json: " + e.getMessage());
- return null;
+ public static String toJson(Workflow workflow) {
+ try {
+ return jsonObjectMapper.writeValueAsString(workflow);
+ } catch (JsonProcessingException e) {
+ logger.error("Error mapping to json: {}", e.getMessage());
+ throw new IllegalArgumentException("Could not convert workflow to json: " + e.getMessage());
+ }
}
- }
- public static String toYaml(Workflow workflow) {
- try {
- String jsonString = jsonObjectMapper.writeValueAsString(workflow);
- JsonNode jsonNode = jsonObjectMapper.readTree(jsonString);
- YAMLFactory yamlFactory =
- new YAMLFactory()
- .disable(YAMLGenerator.Feature.MINIMIZE_QUOTES)
- .disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);
- return new YAMLMapper(yamlFactory).writeValueAsString(jsonNode);
- } catch (Exception e) {
- logger.error("Error mapping to yaml: " + e.getMessage());
- return null;
+ public static String toYaml(Workflow workflow) {
+ try {
+ String jsonString = jsonObjectMapper.writeValueAsString(workflow);
+ JsonNode jsonNode = jsonObjectMapper.readTree(jsonString);
+ YAMLFactory yamlFactory =
+ new YAMLFactory()
+ .disable(YAMLGenerator.Feature.MINIMIZE_QUOTES)
+ .disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);
+ return new YAMLMapper(yamlFactory).writeValueAsString(jsonNode);
+ } catch (Exception e) {
+ logger.error("Error mapping to yaml: {}", e.getMessage());
+ throw new IllegalArgumentException("Could not convert workflow to yaml: " + e.getMessage());
+ }
}
- }
}
diff --git a/api/src/main/java/io/serverlessworkflow/api/workflow/Errors.java b/api/src/main/java/io/serverlessworkflow/api/workflow/Errors.java
index 8431b94a..9e474139 100644
--- a/api/src/main/java/io/serverlessworkflow/api/workflow/Errors.java
+++ b/api/src/main/java/io/serverlessworkflow/api/workflow/Errors.java
@@ -15,36 +15,42 @@
*/
package io.serverlessworkflow.api.workflow;
-import io.serverlessworkflow.api.error.ErrorDefinition;
+import java.util.Collections;
import java.util.List;
-public class Errors {
- private String refValue;
- private List errorDefs;
-
- public Errors() {}
-
- public Errors(List errorDefs) {
- this.errorDefs = errorDefs;
- }
-
- public Errors(String refValue) {
- this.refValue = refValue;
- }
-
- public String getRefValue() {
- return refValue;
- }
-
- public void setRefValue(String refValue) {
- this.refValue = refValue;
- }
-
- public List getErrorDefs() {
- return errorDefs;
- }
+import io.serverlessworkflow.api.error.ErrorDefinition;
- public void setErrorDefs(List errorDefs) {
- this.errorDefs = errorDefs;
- }
+public class Errors {
+ private String refValue;
+ private List errorDefs;
+
+ public Errors() {
+ }
+
+ public Errors(List errorDefs) {
+ this.errorDefs = errorDefs;
+ }
+
+ public Errors(String refValue) {
+ this.refValue = refValue;
+ }
+
+ public String getRefValue() {
+ return refValue;
+ }
+
+ public void setRefValue(String refValue) {
+ this.refValue = refValue;
+ }
+
+ public List getErrorDefs() {
+ if (errorDefs == null) {
+ return Collections.emptyList();
+ }
+ return errorDefs;
+ }
+
+ public void setErrorDefs(List errorDefs) {
+ this.errorDefs = errorDefs;
+ }
}
diff --git a/api/src/main/java/io/serverlessworkflow/api/workflow/Events.java b/api/src/main/java/io/serverlessworkflow/api/workflow/Events.java
index 24080e51..59af2c3f 100644
--- a/api/src/main/java/io/serverlessworkflow/api/workflow/Events.java
+++ b/api/src/main/java/io/serverlessworkflow/api/workflow/Events.java
@@ -15,36 +15,42 @@
*/
package io.serverlessworkflow.api.workflow;
-import io.serverlessworkflow.api.events.EventDefinition;
+import java.util.Collections;
import java.util.List;
-public class Events {
- private String refValue;
- private List eventDefs;
-
- public Events() {}
-
- public Events(List eventDefs) {
- this.eventDefs = eventDefs;
- }
-
- public Events(String refValue) {
- this.refValue = refValue;
- }
-
- public String getRefValue() {
- return refValue;
- }
-
- public void setRefValue(String refValue) {
- this.refValue = refValue;
- }
-
- public List getEventDefs() {
- return eventDefs;
- }
+import io.serverlessworkflow.api.events.EventDefinition;
- public void setEventDefs(List eventDefs) {
- this.eventDefs = eventDefs;
- }
+public class Events {
+ private String refValue;
+ private List eventDefs;
+
+ public Events() {
+ }
+
+ public Events(List eventDefs) {
+ this.eventDefs = eventDefs;
+ }
+
+ public Events(String refValue) {
+ this.refValue = refValue;
+ }
+
+ public String getRefValue() {
+ return refValue;
+ }
+
+ public void setRefValue(String refValue) {
+ this.refValue = refValue;
+ }
+
+ public List getEventDefs() {
+ if (eventDefs == null) {
+ return Collections.emptyList();
+ }
+ return eventDefs;
+ }
+
+ public void setEventDefs(List eventDefs) {
+ this.eventDefs = eventDefs;
+ }
}
diff --git a/api/src/main/java/io/serverlessworkflow/api/workflow/Functions.java b/api/src/main/java/io/serverlessworkflow/api/workflow/Functions.java
index f269bc08..6e927f9b 100644
--- a/api/src/main/java/io/serverlessworkflow/api/workflow/Functions.java
+++ b/api/src/main/java/io/serverlessworkflow/api/workflow/Functions.java
@@ -15,36 +15,42 @@
*/
package io.serverlessworkflow.api.workflow;
-import io.serverlessworkflow.api.functions.FunctionDefinition;
+import java.util.Collections;
import java.util.List;
-public class Functions {
- private String refValue;
- private List functionDefs;
-
- public Functions() {}
-
- public Functions(List functionDefs) {
- this.functionDefs = functionDefs;
- }
-
- public Functions(String refValue) {
- this.refValue = refValue;
- }
-
- public String getRefValue() {
- return refValue;
- }
-
- public void setRefValue(String refValue) {
- this.refValue = refValue;
- }
-
- public List getFunctionDefs() {
- return functionDefs;
- }
+import io.serverlessworkflow.api.functions.FunctionDefinition;
- public void setFunctionDefs(List functionDefs) {
- this.functionDefs = functionDefs;
- }
+public class Functions {
+ private String refValue;
+ private List functionDefs;
+
+ public Functions() {
+ }
+
+ public Functions(List functionDefs) {
+ this.functionDefs = functionDefs;
+ }
+
+ public Functions(String refValue) {
+ this.refValue = refValue;
+ }
+
+ public String getRefValue() {
+ return refValue;
+ }
+
+ public void setRefValue(String refValue) {
+ this.refValue = refValue;
+ }
+
+ public List getFunctionDefs() {
+ if (functionDefs == null) {
+ return Collections.emptyList();
+ }
+ return functionDefs;
+ }
+
+ public void setFunctionDefs(List functionDefs) {
+ this.functionDefs = functionDefs;
+ }
}
diff --git a/api/src/main/java/io/serverlessworkflow/api/workflow/Retries.java b/api/src/main/java/io/serverlessworkflow/api/workflow/Retries.java
index af1ae1e0..fa539ac0 100644
--- a/api/src/main/java/io/serverlessworkflow/api/workflow/Retries.java
+++ b/api/src/main/java/io/serverlessworkflow/api/workflow/Retries.java
@@ -15,36 +15,42 @@
*/
package io.serverlessworkflow.api.workflow;
-import io.serverlessworkflow.api.retry.RetryDefinition;
+import java.util.Collections;
import java.util.List;
-public class Retries {
- private String refValue;
- private List retryDefs;
-
- public Retries() {}
-
- public Retries(List retryDefs) {
- this.retryDefs = retryDefs;
- }
-
- public Retries(String refValue) {
- this.refValue = refValue;
- }
-
- public String getRefValue() {
- return refValue;
- }
-
- public void setRefValue(String refValue) {
- this.refValue = refValue;
- }
-
- public List getRetryDefs() {
- return retryDefs;
- }
+import io.serverlessworkflow.api.retry.RetryDefinition;
- public void setRetryDefs(List retryDefs) {
- this.retryDefs = retryDefs;
- }
+public class Retries {
+ private String refValue;
+ private List retryDefs;
+
+ public Retries() {
+ }
+
+ public Retries(List retryDefs) {
+ this.retryDefs = retryDefs;
+ }
+
+ public Retries(String refValue) {
+ this.refValue = refValue;
+ }
+
+ public String getRefValue() {
+ return refValue;
+ }
+
+ public void setRefValue(String refValue) {
+ this.refValue = refValue;
+ }
+
+ public List getRetryDefs() {
+ if (retryDefs == null) {
+ return Collections.emptyList();
+ }
+ return retryDefs;
+ }
+
+ public void setRetryDefs(List retryDefs) {
+ this.retryDefs = retryDefs;
+ }
}
diff --git a/api/src/main/java/io/serverlessworkflow/api/workflow/Secrets.java b/api/src/main/java/io/serverlessworkflow/api/workflow/Secrets.java
index 2dbb6b31..03edc0fd 100644
--- a/api/src/main/java/io/serverlessworkflow/api/workflow/Secrets.java
+++ b/api/src/main/java/io/serverlessworkflow/api/workflow/Secrets.java
@@ -15,35 +15,40 @@
*/
package io.serverlessworkflow.api.workflow;
+import java.util.Collections;
import java.util.List;
public class Secrets {
- private String refValue;
- private List secretDefs;
-
- public Secrets() {}
-
- public Secrets(String refValue) {
- this.refValue = refValue;
- }
-
- public Secrets(List secretDefs) {
- this.secretDefs = secretDefs;
- }
-
- public String getRefValue() {
- return refValue;
- }
-
- public void setRefValue(String refValue) {
- this.refValue = refValue;
- }
-
- public List getSecretDefs() {
- return secretDefs;
- }
-
- public void setSecretDefs(List secretDefs) {
- this.secretDefs = secretDefs;
- }
+ private String refValue;
+ private List secretDefs;
+
+ public Secrets() {
+ }
+
+ public Secrets(String refValue) {
+ this.refValue = refValue;
+ }
+
+ public Secrets(List secretDefs) {
+ this.secretDefs = secretDefs;
+ }
+
+ public String getRefValue() {
+ return refValue;
+ }
+
+ public void setRefValue(String refValue) {
+ this.refValue = refValue;
+ }
+
+ public List getSecretDefs() {
+ if (secretDefs == null) {
+ return Collections.emptyList();
+ }
+ return secretDefs;
+ }
+
+ public void setSecretDefs(List secretDefs) {
+ this.secretDefs = secretDefs;
+ }
}
diff --git a/api/src/main/resources/schema/events/eventdef.json b/api/src/main/resources/schema/events/eventdef.json
index 6670b856..a585f782 100644
--- a/api/src/main/resources/schema/events/eventdef.json
+++ b/api/src/main/resources/schema/events/eventdef.json
@@ -24,6 +24,11 @@
"$ref": "../correlation/correlationdef.json"
}
},
+ "dataOnly": {
+ "type": "boolean",
+ "default": true,
+ "description": "If `true`, only the Event payload is accessible to consuming Workflow states. If `false`, both event payload and context attributes should be accessible "
+ },
"kind": {
"type" : "string",
"enum": ["consumed", "produced"],
diff --git a/api/src/main/resources/schema/events/eventref.json b/api/src/main/resources/schema/events/eventref.json
index 76334993..c0e04a7a 100644
--- a/api/src/main/resources/schema/events/eventref.json
+++ b/api/src/main/resources/schema/events/eventref.json
@@ -16,7 +16,8 @@
"description": "Maximum amount of time (ISO 8601 format) to wait for the result event. If not defined it should default to the actionExecutionTimeout"
},
"data": {
- "type": "string",
+ "type": "object",
+ "existingJavaType": "com.fasterxml.jackson.databind.JsonNode",
"description": "Expression which selects parts of the states data output to become the data of the produced event."
},
"contextAttributes": {
diff --git a/api/src/main/resources/schema/produce/produceevent.json b/api/src/main/resources/schema/produce/produceevent.json
index b5bb7d5f..fd3ecdb4 100644
--- a/api/src/main/resources/schema/produce/produceevent.json
+++ b/api/src/main/resources/schema/produce/produceevent.json
@@ -8,8 +8,14 @@
"minLength": 1
},
"data": {
- "type": "string",
- "description": "Workflow expression which selects parts of the states data output to become the data of the produced event"
+ "type": "object",
+ "description": "Workflow expression which selects parts of the states data output to become the data of the produced event",
+ "existingJavaType": "com.fasterxml.jackson.databind.JsonNode"
+ },
+ "contextAttributes": {
+ "type": "object",
+ "description": "Add additional event extension context attributes",
+ "existingJavaType": "java.util.Map"
}
},
"required": [
diff --git a/api/src/main/resources/schema/workflow.json b/api/src/main/resources/schema/workflow.json
index 19164ca5..f8309b6c 100644
--- a/api/src/main/resources/schema/workflow.json
+++ b/api/src/main/resources/schema/workflow.json
@@ -1,5 +1,5 @@
{
- "$id": "https://wg-serverless.org/workflow.schema.json",
+ "$id": "classpath:schema/workflow.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Serverless Workflow is a vendor-neutral specification for defining the model of workflows responsible for orchestrating event-driven serverless applications.",
"type": "object",
@@ -14,6 +14,10 @@
"description": "Workflow unique identifier",
"minLength": 1
},
+ "key": {
+ "type": "string",
+ "description": "Workflow Domain-specific identifier"
+ },
"name": {
"type": "string",
"description": "Workflow name",
@@ -27,6 +31,14 @@
"type": "string",
"description": "Workflow version"
},
+ "annotations": {
+ "type": "array",
+ "description": "List of helpful terms describing the workflows intended purpose, subject areas, or other important qualities",
+ "minItems": 1,
+ "items": {
+ "type": "string"
+ }
+ },
"dataInputSchema": {
"$ref": "datainputschema/datainputschema.json",
"description": "Workflow data input schema"
@@ -92,7 +104,9 @@
"$ref": "timeouts/timeoutsdef.json"
},
"auth": {
- "$ref": "auth/auth.json"
+ "type": "object",
+ "existingJavaType": "io.serverlessworkflow.api.workflow.Auth",
+ "description": "Workflow Auth definitions"
},
"states": {
"type": "array",
@@ -145,10 +159,15 @@
}
}
},
- "required": [
- "id",
- "name",
- "version",
- "states"
- ]
-}
\ No newline at end of file
+ "required": [
+ "id",
+ "name",
+ "version",
+ "states"
+ ],
+ "dependencies":
+ {
+ "id": { "not": { "required": ["key"] } },
+ "key": { "not": { "required": ["id"] } }
+ }
+}
diff --git a/api/src/test/java/io/serverlessworkflow/api/test/MarkupToWorkflowTest.java b/api/src/test/java/io/serverlessworkflow/api/test/MarkupToWorkflowTest.java
index 2c6da083..f5b30d07 100644
--- a/api/src/test/java/io/serverlessworkflow/api/test/MarkupToWorkflowTest.java
+++ b/api/src/test/java/io/serverlessworkflow/api/test/MarkupToWorkflowTest.java
@@ -25,6 +25,7 @@
import io.serverlessworkflow.api.datainputschema.DataInputSchema;
import io.serverlessworkflow.api.defaultdef.DefaultConditionDefinition;
import io.serverlessworkflow.api.end.End;
+import io.serverlessworkflow.api.events.EventDefinition;
import io.serverlessworkflow.api.events.EventRef;
import io.serverlessworkflow.api.functions.FunctionDefinition;
import io.serverlessworkflow.api.functions.FunctionRef;
@@ -39,9 +40,11 @@
import io.serverlessworkflow.api.test.utils.WorkflowTestUtils;
import io.serverlessworkflow.api.timeouts.WorkflowExecTimeout;
import io.serverlessworkflow.api.workflow.Constants;
+import io.serverlessworkflow.api.workflow.Events;
import io.serverlessworkflow.api.workflow.Retries;
import io.serverlessworkflow.api.workflow.Secrets;
import java.util.List;
+import java.util.Map;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@@ -103,10 +106,11 @@ public void testSpecExamplesParsing(String workflowLocation) {
@ParameterizedTest
@ValueSource(strings = {"/features/applicantrequest.json", "/features/applicantrequest.yml"})
- public void testSpecFreatureFunctionRef(String workflowLocation) {
+ public void testSpecFeatureFunctionRef(String workflowLocation) {
Workflow workflow = Workflow.fromSource(WorkflowTestUtils.readWorkflowFile(workflowLocation));
assertNotNull(workflow);
+ assertNull(workflow.getKey());
assertNotNull(workflow.getId());
assertNotNull(workflow.getName());
assertNotNull(workflow.getStates());
@@ -116,6 +120,46 @@ public void testSpecFreatureFunctionRef(String workflowLocation) {
assertEquals(1, workflow.getFunctions().getFunctionDefs().size());
}
+ @ParameterizedTest
+ @ValueSource(
+ strings = {
+ "/features/applicantrequest-with-key.json",
+ "/features/applicantrequest-with-key.yml"
+ })
+ public void testSpecFeatureFunctionRefWithKey(String workflowLocation) {
+ Workflow workflow = Workflow.fromSource(WorkflowTestUtils.readWorkflowFile(workflowLocation));
+
+ assertNotNull(workflow);
+ assertEquals("applicant-key-request", workflow.getKey());
+ assertNull(workflow.getId());
+ assertNotNull(workflow.getName());
+ assertNotNull(workflow.getStates());
+ assertTrue(workflow.getStates().size() > 0);
+
+ assertNotNull(workflow.getFunctions());
+ assertEquals(1, workflow.getFunctions().getFunctionDefs().size());
+ }
+
+ @ParameterizedTest
+ @ValueSource(
+ strings = {
+ "/features/applicantrequest-with-id-and-key.json",
+ "/features/applicantrequest-with-id-and-key.yml"
+ })
+ public void testSpecFeatureFunctionRefWithIdAndKey(String workflowLocation) {
+ Workflow workflow = Workflow.fromSource(WorkflowTestUtils.readWorkflowFile(workflowLocation));
+
+ assertNotNull(workflow);
+ assertEquals("applicant-key-request", workflow.getKey());
+ assertEquals("applicant-with-key-and-id", workflow.getId());
+ assertNotNull(workflow.getName());
+ assertNotNull(workflow.getStates());
+ assertTrue(workflow.getStates().size() > 0);
+
+ assertNotNull(workflow.getFunctions());
+ assertEquals(1, workflow.getFunctions().getFunctionDefs().size());
+ }
+
@ParameterizedTest
@ValueSource(strings = {"/features/vetappointment.json", "/features/vetappointment.yml"})
public void testSpecFreatureEventRef(String workflowLocation) {
@@ -221,6 +265,12 @@ public void testTransitions(String workflowLocation) {
assertEquals("RejectApplication", cond2.getTransition().getNextState());
assertNotNull(cond2.getTransition().getProduceEvents());
assertEquals(1, cond2.getTransition().getProduceEvents().size());
+ assertNotNull(cond2.getTransition().getProduceEvents().get(0).getContextAttributes());
+ Map contextAttributes =
+ cond2.getTransition().getProduceEvents().get(0).getContextAttributes();
+ assertEquals(2, contextAttributes.size());
+ assertEquals("IN", contextAttributes.get("order_location"));
+ assertEquals("online", contextAttributes.get("order_type"));
assertFalse(cond2.getTransition().isCompensate());
assertNotNull(switchState.getDefaultCondition());
@@ -625,7 +675,7 @@ public void testAuthBasic(String workflowLocation) {
assertNotNull(workflow.getName());
assertNotNull(workflow.getAuth());
- AuthDefinition auth = workflow.getAuth();
+ AuthDefinition auth = workflow.getAuth().getAuthDefs().get(0);
assertNotNull(auth.getName());
assertEquals("authname", auth.getName());
assertNotNull(auth.getScheme());
@@ -645,7 +695,7 @@ public void testAuthBearer(String workflowLocation) {
assertNotNull(workflow.getName());
assertNotNull(workflow.getAuth());
- AuthDefinition auth = workflow.getAuth();
+ AuthDefinition auth = workflow.getAuth().getAuthDefs().get(0);
assertNotNull(auth.getName());
assertEquals("authname", auth.getName());
assertNotNull(auth.getScheme());
@@ -664,7 +714,7 @@ public void testAuthOAuth(String workflowLocation) {
assertNotNull(workflow.getName());
assertNotNull(workflow.getAuth());
- AuthDefinition auth = workflow.getAuth();
+ AuthDefinition auth = workflow.getAuth().getAuthDefs().get(0);
assertNotNull(auth.getName());
assertEquals("authname", auth.getName());
assertNotNull(auth.getScheme());
@@ -838,4 +888,39 @@ public void testFunctionInvoke(String workflowLocation) {
assertNotNull(action3.getEventRef().getInvoke());
assertEquals(EventRef.Invoke.ASYNC, action3.getEventRef().getInvoke());
}
+
+ @ParameterizedTest
+ @ValueSource(strings = {"/features/annotations.json", "/features/annotations.yml"})
+ public void testAnnotations(String workflowLocation) {
+ Workflow workflow = Workflow.fromSource(WorkflowTestUtils.readWorkflowFile(workflowLocation));
+
+ assertNotNull(workflow);
+ assertNotNull(workflow.getId());
+ assertNotNull(workflow.getName());
+
+ assertNotNull(workflow.getAnnotations());
+ List annotations = workflow.getAnnotations();
+ assertEquals(4, annotations.size());
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"/features/eventdefdataonly.json", "/features/eventdefdataonly.yml"})
+ public void testEventDefDataOnly(String workflowLocation) {
+ Workflow workflow = Workflow.fromSource(WorkflowTestUtils.readWorkflowFile(workflowLocation));
+
+ assertNotNull(workflow);
+ assertNotNull(workflow.getId());
+ assertNotNull(workflow.getName());
+
+ assertNotNull(workflow.getEvents());
+ Events events = workflow.getEvents();
+ assertNotNull(workflow.getEvents().getEventDefs());
+ assertEquals(2, events.getEventDefs().size());
+ EventDefinition eventDefOne = events.getEventDefs().get(0);
+ EventDefinition eventDefTwo = events.getEventDefs().get(1);
+ assertEquals("visaApprovedEvent", eventDefOne.getName());
+ assertFalse(eventDefOne.isDataOnly());
+ assertEquals("visaRejectedEvent", eventDefTwo.getName());
+ assertTrue(eventDefTwo.isDataOnly());
+ }
}
diff --git a/api/src/test/java/io/serverlessworkflow/api/test/WorkflowToMarkupTest.java b/api/src/test/java/io/serverlessworkflow/api/test/WorkflowToMarkupTest.java
index 514375e1..ff43f167 100644
--- a/api/src/test/java/io/serverlessworkflow/api/test/WorkflowToMarkupTest.java
+++ b/api/src/test/java/io/serverlessworkflow/api/test/WorkflowToMarkupTest.java
@@ -16,7 +16,9 @@
package io.serverlessworkflow.api.test;
import static io.serverlessworkflow.api.states.DefaultState.Type.SLEEP;
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import io.serverlessworkflow.api.Workflow;
import io.serverlessworkflow.api.auth.AuthDefinition;
@@ -29,6 +31,7 @@
import io.serverlessworkflow.api.schedule.Schedule;
import io.serverlessworkflow.api.start.Start;
import io.serverlessworkflow.api.states.SleepState;
+import io.serverlessworkflow.api.workflow.Auth;
import io.serverlessworkflow.api.workflow.Events;
import io.serverlessworkflow.api.workflow.Functions;
import java.util.Arrays;
@@ -162,22 +165,24 @@ public void testAuth() {
.withVersion("1.0")
.withStart(new Start())
.withAuth(
- new AuthDefinition()
- .withName("authname")
- .withScheme(AuthDefinition.Scheme.BASIC)
- .withBasicauth(
- new BasicAuthDefinition()
- .withUsername("testuser")
- .withPassword("testPassword")));
+ new Auth(
+ new AuthDefinition()
+ .withName("authname")
+ .withScheme(AuthDefinition.Scheme.BASIC)
+ .withBasicauth(
+ new BasicAuthDefinition()
+ .withUsername("testuser")
+ .withPassword("testPassword"))));
assertNotNull(workflow);
assertNotNull(workflow.getAuth());
- assertNotNull(workflow.getAuth().getName());
- assertEquals("authname", workflow.getAuth().getName());
- assertNotNull(workflow.getAuth().getScheme());
- assertEquals("basic", workflow.getAuth().getScheme().value());
- assertNotNull(workflow.getAuth().getBasicauth());
- assertEquals("testuser", workflow.getAuth().getBasicauth().getUsername());
- assertEquals("testPassword", workflow.getAuth().getBasicauth().getPassword());
+ assertNotNull(workflow.getAuth().getAuthDefs().get(0));
+ assertEquals("authname", workflow.getAuth().getAuthDefs().get(0).getName());
+ assertNotNull(workflow.getAuth().getAuthDefs().get(0).getScheme());
+ assertEquals("basic", workflow.getAuth().getAuthDefs().get(0).getScheme().value());
+ assertNotNull(workflow.getAuth().getAuthDefs().get(0).getBasicauth());
+ assertEquals("testuser", workflow.getAuth().getAuthDefs().get(0).getBasicauth().getUsername());
+ assertEquals(
+ "testPassword", workflow.getAuth().getAuthDefs().get(0).getBasicauth().getPassword());
}
}
diff --git a/api/src/test/resources/examples/applicantrequest.json b/api/src/test/resources/examples/applicantrequest.json
index 1621c2bd..652e361b 100644
--- a/api/src/test/resources/examples/applicantrequest.json
+++ b/api/src/test/resources/examples/applicantrequest.json
@@ -1,5 +1,6 @@
{
"id": "applicantrequest",
+ "key": "applicant-key-request",
"version": "1.0",
"specVersion": "0.8",
"name": "Applicant Request Decision Workflow",
diff --git a/api/src/test/resources/features/annotations.json b/api/src/test/resources/features/annotations.json
new file mode 100644
index 00000000..90ed6dd1
--- /dev/null
+++ b/api/src/test/resources/features/annotations.json
@@ -0,0 +1,6 @@
+{
+ "id" : "test-workflow",
+ "name" : "test-workflow-name",
+ "version" : "1.0",
+ "annotations": ["a", "b", "c", "d"]
+}
\ No newline at end of file
diff --git a/api/src/test/resources/features/annotations.yml b/api/src/test/resources/features/annotations.yml
new file mode 100644
index 00000000..54e359ae
--- /dev/null
+++ b/api/src/test/resources/features/annotations.yml
@@ -0,0 +1,8 @@
+id: test-workflow
+name: test-workflow-name
+version: '1.0'
+annotations:
+ - a
+ - b
+ - c
+ - d
diff --git a/api/src/test/resources/features/applicantrequest-with-id-and-key.json b/api/src/test/resources/features/applicantrequest-with-id-and-key.json
new file mode 100644
index 00000000..405d7c36
--- /dev/null
+++ b/api/src/test/resources/features/applicantrequest-with-id-and-key.json
@@ -0,0 +1,60 @@
+{
+ "id": "applicant-with-key-and-id",
+ "key": "applicant-key-request",
+ "version": "1.0",
+ "specVersion": "0.8",
+ "name": "Applicant Request Decision Workflow",
+ "description": "Determine if applicant request is valid",
+ "start": "CheckApplication",
+ "functions": [
+ {
+ "name": "sendRejectionEmailFunction",
+ "operation": "http://myapis.org/applicationapi.json#emailRejection"
+ }
+ ],
+ "states":[
+ {
+ "name":"CheckApplication",
+ "type":"switch",
+ "dataConditions": [
+ {
+ "condition": "${ .applicants | .age >= 18 }",
+ "transition": "StartApplication"
+ },
+ {
+ "condition": "${ .applicants | .age < 18 }",
+ "transition": "RejectApplication"
+ }
+ ],
+ "defaultCondition": {
+ "transition": "RejectApplication"
+ }
+ },
+ {
+ "name": "StartApplication",
+ "type": "operation",
+ "actions": [
+ {
+ "subFlowRef": "startApplicationWorkflowId"
+ }
+ ],
+ "end": true
+ },
+ {
+ "name":"RejectApplication",
+ "type":"operation",
+ "actionMode":"sequential",
+ "actions":[
+ {
+ "functionRef": {
+ "refName": "sendRejectionEmailFunction",
+ "arguments": {
+ "applicant": "${ .applicant }"
+ }
+ }
+ }
+ ],
+ "end": true
+ }
+ ]
+}
\ No newline at end of file
diff --git a/api/src/test/resources/features/applicantrequest-with-id-and-key.yml b/api/src/test/resources/features/applicantrequest-with-id-and-key.yml
new file mode 100644
index 00000000..8a123663
--- /dev/null
+++ b/api/src/test/resources/features/applicantrequest-with-id-and-key.yml
@@ -0,0 +1,34 @@
+id: applicant-with-key-and-id
+key: applicant-key-request
+version: '1.0'
+specVersion: '0.8'
+name: Applicant Request Decision Workflow
+description: Determine if applicant request is valid
+start: CheckApplication
+functions:
+ - name: sendRejectionEmailFunction
+ operation: http://myapis.org/applicationapi.json#emailRejection
+states:
+ - name: CheckApplication
+ type: switch
+ dataConditions:
+ - condition: "${ .applicants | .age >= 18 }"
+ transition: StartApplication
+ - condition: "${ .applicants | .age < 18 }"
+ transition: RejectApplication
+ defaultCondition:
+ transition: RejectApplication
+ - name: StartApplication
+ type: operation
+ actions:
+ - subFlowRef: startApplicationWorkflowId
+ end: true
+ - name: RejectApplication
+ type: operation
+ actionMode: sequential
+ actions:
+ - functionRef:
+ refName: sendRejectionEmailFunction
+ arguments:
+ applicant: "${ .applicant }"
+ end: true
diff --git a/api/src/test/resources/features/applicantrequest-with-key.json b/api/src/test/resources/features/applicantrequest-with-key.json
new file mode 100644
index 00000000..f0481b00
--- /dev/null
+++ b/api/src/test/resources/features/applicantrequest-with-key.json
@@ -0,0 +1,59 @@
+{
+ "key": "applicant-key-request",
+ "version": "1.0",
+ "specVersion": "0.8",
+ "name": "Applicant Request Decision Workflow",
+ "description": "Determine if applicant request is valid",
+ "start": "CheckApplication",
+ "functions": [
+ {
+ "name": "sendRejectionEmailFunction",
+ "operation": "http://myapis.org/applicationapi.json#emailRejection"
+ }
+ ],
+ "states":[
+ {
+ "name":"CheckApplication",
+ "type":"switch",
+ "dataConditions": [
+ {
+ "condition": "${ .applicants | .age >= 18 }",
+ "transition": "StartApplication"
+ },
+ {
+ "condition": "${ .applicants | .age < 18 }",
+ "transition": "RejectApplication"
+ }
+ ],
+ "defaultCondition": {
+ "transition": "RejectApplication"
+ }
+ },
+ {
+ "name": "StartApplication",
+ "type": "operation",
+ "actions": [
+ {
+ "subFlowRef": "startApplicationWorkflowId"
+ }
+ ],
+ "end": true
+ },
+ {
+ "name":"RejectApplication",
+ "type":"operation",
+ "actionMode":"sequential",
+ "actions":[
+ {
+ "functionRef": {
+ "refName": "sendRejectionEmailFunction",
+ "arguments": {
+ "applicant": "${ .applicant }"
+ }
+ }
+ }
+ ],
+ "end": true
+ }
+ ]
+}
\ No newline at end of file
diff --git a/api/src/test/resources/features/applicantrequest-with-key.yml b/api/src/test/resources/features/applicantrequest-with-key.yml
new file mode 100644
index 00000000..85beed74
--- /dev/null
+++ b/api/src/test/resources/features/applicantrequest-with-key.yml
@@ -0,0 +1,33 @@
+key: applicant-key-request
+version: '1.0'
+specVersion: '0.8'
+name: Applicant Request Decision Workflow
+description: Determine if applicant request is valid
+start: CheckApplication
+functions:
+ - name: sendRejectionEmailFunction
+ operation: http://myapis.org/applicationapi.json#emailRejection
+states:
+ - name: CheckApplication
+ type: switch
+ dataConditions:
+ - condition: "${ .applicants | .age >= 18 }"
+ transition: StartApplication
+ - condition: "${ .applicants | .age < 18 }"
+ transition: RejectApplication
+ defaultCondition:
+ transition: RejectApplication
+ - name: StartApplication
+ type: operation
+ actions:
+ - subFlowRef: startApplicationWorkflowId
+ end: true
+ - name: RejectApplication
+ type: operation
+ actionMode: sequential
+ actions:
+ - functionRef:
+ refName: sendRejectionEmailFunction
+ arguments:
+ applicant: "${ .applicant }"
+ end: true
diff --git a/api/src/test/resources/features/authbasic.json b/api/src/test/resources/features/authbasic.json
index 92397db4..31e86599 100644
--- a/api/src/test/resources/features/authbasic.json
+++ b/api/src/test/resources/features/authbasic.json
@@ -1,13 +1,15 @@
{
- "id" : "test-workflow",
- "name" : "test-workflow-name",
- "version" : "1.0",
- "auth" : {
- "name" : "authname",
- "scheme" : "basic",
- "properties" : {
- "username" : "testuser",
- "password" : "testpassword"
+ "id": "test-workflow",
+ "name": "test-workflow-name",
+ "version": "1.0",
+ "auth": [
+ {
+ "name": "authname",
+ "scheme": "basic",
+ "properties": {
+ "username": "testuser",
+ "password": "testpassword"
+ }
}
- }
+ ]
}
\ No newline at end of file
diff --git a/api/src/test/resources/features/authbasic.yml b/api/src/test/resources/features/authbasic.yml
index 963dc63e..e04d1f7e 100644
--- a/api/src/test/resources/features/authbasic.yml
+++ b/api/src/test/resources/features/authbasic.yml
@@ -2,8 +2,8 @@ id: test-workflow
name: test-workflow-name
version: '1.0'
auth:
- name: authname
- scheme: basic
- properties:
- username: testuser
- password: testpassword
+ - name: authname
+ scheme: basic
+ properties:
+ username: testuser
+ password: testpassword
diff --git a/api/src/test/resources/features/authbearer.json b/api/src/test/resources/features/authbearer.json
index 304d1685..be7c037a 100644
--- a/api/src/test/resources/features/authbearer.json
+++ b/api/src/test/resources/features/authbearer.json
@@ -1,12 +1,14 @@
{
- "id" : "test-workflow",
- "name" : "test-workflow-name",
- "version" : "1.0",
- "auth" : {
- "name" : "authname",
- "scheme" : "bearer",
- "properties" : {
- "token" : "testtoken"
+ "id": "test-workflow",
+ "name": "test-workflow-name",
+ "version": "1.0",
+ "auth": [
+ {
+ "name": "authname",
+ "scheme": "bearer",
+ "properties": {
+ "token": "testtoken"
+ }
}
- }
+ ]
}
\ No newline at end of file
diff --git a/api/src/test/resources/features/authbearer.yml b/api/src/test/resources/features/authbearer.yml
index 0a815386..292fa3c2 100644
--- a/api/src/test/resources/features/authbearer.yml
+++ b/api/src/test/resources/features/authbearer.yml
@@ -2,7 +2,7 @@ id: test-workflow
name: test-workflow-name
version: '1.0'
auth:
- name: authname
- scheme: bearer
- properties:
- token: testtoken
+ - name: authname
+ scheme: bearer
+ properties:
+ token: testtoken
diff --git a/api/src/test/resources/features/authoauth.json b/api/src/test/resources/features/authoauth.json
index da845606..10b76d70 100644
--- a/api/src/test/resources/features/authoauth.json
+++ b/api/src/test/resources/features/authoauth.json
@@ -2,7 +2,7 @@
"id" : "test-workflow",
"name" : "test-workflow-name",
"version" : "1.0",
- "auth" : {
+ "auth" : [{
"name" : "authname",
"scheme" : "oauth2",
"properties" : {
@@ -11,5 +11,5 @@
"clientId": "${ $SECRETS.clientid }",
"clientSecret": "${ $SECRETS.clientsecret }"
}
- }
+ }]
}
\ No newline at end of file
diff --git a/api/src/test/resources/features/authoauth.yml b/api/src/test/resources/features/authoauth.yml
index 8741297c..cb2c52ba 100644
--- a/api/src/test/resources/features/authoauth.yml
+++ b/api/src/test/resources/features/authoauth.yml
@@ -2,10 +2,10 @@ id: test-workflow
name: test-workflow-name
version: '1.0'
auth:
- name: authname
- scheme: oauth2
- properties:
- authority: testauthority
- grantType: clientCredentials
- clientId: "${ $SECRETS.clientid }"
- clientSecret: "${ $SECRETS.clientsecret }"
+ - name: authname
+ scheme: oauth2
+ properties:
+ authority: testauthority
+ grantType: clientCredentials
+ clientId: "${ $SECRETS.clientid }"
+ clientSecret: "${ $SECRETS.clientsecret }"
diff --git a/api/src/test/resources/features/eventdefdataonly.json b/api/src/test/resources/features/eventdefdataonly.json
new file mode 100644
index 00000000..a181b864
--- /dev/null
+++ b/api/src/test/resources/features/eventdefdataonly.json
@@ -0,0 +1,73 @@
+{
+ "id": "eventdefdataonly",
+ "version": "1.0",
+ "specVersion": "0.8",
+ "name": "Event Definition Data Only Test",
+ "description": "Event Definition Data Only Test",
+ "start": "CheckVisaStatus",
+ "events": [
+ {
+ "name": "visaApprovedEvent",
+ "type": "VisaApproved",
+ "source": "visaCheckSource",
+ "dataOnly": false
+ },
+ {
+ "name": "visaRejectedEvent",
+ "type": "VisaRejected",
+ "source": "visaCheckSource"
+ }
+ ],
+ "states":[
+ {
+ "name":"CheckVisaStatus",
+ "type":"switch",
+ "eventConditions": [
+ {
+ "eventRef": "visaApprovedEvent",
+ "transition": "HandleApprovedVisa"
+ },
+ {
+ "eventRef": "visaRejectedEvent",
+ "transition": "HandleRejectedVisa"
+ }
+ ],
+ "timeouts": {
+ "eventTimeout": "PT1H"
+ },
+ "defaultCondition": {
+ "transition": "HandleNoVisaDecision"
+ }
+ },
+ {
+ "name": "HandleApprovedVisa",
+ "type": "operation",
+ "actions": [
+ {
+ "subFlowRef": "handleApprovedVisaWorkflowID"
+ }
+ ],
+ "end": true
+ },
+ {
+ "name": "HandleRejectedVisa",
+ "type": "operation",
+ "actions": [
+ {
+ "subFlowRef": "handleRejectedVisaWorkflowID"
+ }
+ ],
+ "end": true
+ },
+ {
+ "name": "HandleNoVisaDecision",
+ "type": "operation",
+ "actions": [
+ {
+ "subFlowRef": "handleNoVisaDecisionWorkflowId"
+ }
+ ],
+ "end": true
+ }
+ ]
+}
\ No newline at end of file
diff --git a/api/src/test/resources/features/eventdefdataonly.yml b/api/src/test/resources/features/eventdefdataonly.yml
new file mode 100644
index 00000000..e67a9ede
--- /dev/null
+++ b/api/src/test/resources/features/eventdefdataonly.yml
@@ -0,0 +1,41 @@
+id: eventdefdataonly
+version: '1.0'
+specVersion: '0.8'
+name: Event Definition Data Only Test
+description: Event Definition Data Only Test
+start: CheckVisaStatus
+events:
+ - name: visaApprovedEvent
+ type: VisaApproved
+ source: visaCheckSource
+ dataOnly: false
+ - name: visaRejectedEvent
+ type: VisaRejected
+ source: visaCheckSource
+states:
+ - name: CheckVisaStatus
+ type: switch
+ eventConditions:
+ - eventRef: visaApprovedEvent
+ transition: HandleApprovedVisa
+ - eventRef: visaRejectedEvent
+ transition: HandleRejectedVisa
+ timeouts:
+ eventTimeout: PT1H
+ defaultCondition:
+ transition: HandleNoVisaDecision
+ - name: HandleApprovedVisa
+ type: operation
+ actions:
+ - subFlowRef: handleApprovedVisaWorkflowID
+ end: true
+ - name: HandleRejectedVisa
+ type: operation
+ actions:
+ - subFlowRef: handleRejectedVisaWorkflowID
+ end: true
+ - name: HandleNoVisaDecision
+ type: operation
+ actions:
+ - subFlowRef: handleNoVisaDecisionWorkflowId
+ end: true
diff --git a/api/src/test/resources/features/transitions.json b/api/src/test/resources/features/transitions.json
index cacc94af..ed7b7626 100644
--- a/api/src/test/resources/features/transitions.json
+++ b/api/src/test/resources/features/transitions.json
@@ -23,7 +23,11 @@
"produceEvents": [
{
"eventRef": "provisioningCompleteEvent",
- "data": "${ .provisionedOrders }"
+ "data": "${ .provisionedOrders }",
+ "contextAttributes": {
+ "order_location": "IN",
+ "order_type": "online"
+ }
}
]
}
diff --git a/api/src/test/resources/features/transitions.yml b/api/src/test/resources/features/transitions.yml
index 1b89a85f..3ec34ae4 100644
--- a/api/src/test/resources/features/transitions.yml
+++ b/api/src/test/resources/features/transitions.yml
@@ -18,6 +18,9 @@ states:
produceEvents:
- eventRef: provisioningCompleteEvent
data: "${ .provisionedOrders }"
+ contextAttributes:
+ "order_location": "IN"
+ "order_type": "online"
defaultCondition:
transition:
nextState: RejectApplication
diff --git a/code-of-conduct.md b/code-of-conduct.md
index ddd14b6d..97a8526a 100644
--- a/code-of-conduct.md
+++ b/code-of-conduct.md
@@ -1,58 +1,11 @@
-## CNCF Community Code of Conduct v1.0
+# Code of Conduct
-Other languages available:
-- [Chinese/中文](https://github.com/cncf/foundation/blob/master/code-of-conduct-languages/zh.md)
-- [German/Deutsch](https://github.com/cncf/foundation/blob/master/code-of-conduct-languages/de.md)
-- [Spanish/Español](https://github.com/cncf/foundation/blob/master/code-of-conduct-languages/es.md)
-- [French/Français](https://github.com/cncf/foundation/blob/master/code-of-conduct-languages/fr.md)
-- [Italian/Italiano](https://github.com/cncf/foundation/blob/master/code-of-conduct-languages/it.md)
-- [Japanese/日本語](https://github.com/cncf/foundation/blob/master/code-of-conduct-languages/jp.md)
-- [Korean/한국어](https://github.com/cncf/foundation/blob/master/code-of-conduct-languages/ko.md)
-- [Ukrainian/Українська](https://github.com/cncf/foundation/blob/master/code-of-conduct-languages/uk.md)
-- [Russian/Русский](https://github.com/cncf/foundation/blob/master/code-of-conduct-languages/ru.md)
-- [Portuguese/Português](https://github.com/cncf/foundation/blob/master/code-of-conduct-languages/pt.md)
-- [Arabic/العربية](https://github.com/cncf/foundation/blob/master/code-of-conduct-languages/ar.md)
-- [Polish/Polski](https://github.com/cncf/foundation/blob/master/code-of-conduct-languages/pl.md)
+We follow the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md).
-### Contributor Code of Conduct
-
-As contributors and maintainers of this project, and in the interest of fostering
-an open and welcoming community, we pledge to respect all people who contribute
-through reporting issues, posting feature requests, updating documentation,
-submitting pull requests or patches, and other activities.
-
-We are committed to making participation in this project a harassment-free experience for
-everyone, regardless of level of experience, gender, gender identity and expression,
-sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
-religion, or nationality.
-
-Examples of unacceptable behavior by participants include:
-
-* The use of sexualized language or imagery
-* Personal attacks
-* Trolling or insulting/derogatory comments
-* Public or private harassment
-* Publishing others' private information, such as physical or electronic addresses,
- without explicit permission
-* Other unethical or unprofessional conduct.
-
-Project maintainers have the right and responsibility to remove, edit, or reject
-comments, commits, code, wiki edits, issues, and other contributions that are not
-aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers
-commit themselves to fairly and consistently applying these principles to every aspect
-of managing this project. Project maintainers who do not follow or enforce the Code of
-Conduct may be permanently removed from the project team.
-
-This code of conduct applies both within project spaces and in public spaces
-when an individual is representing the project or its community.
-
-Instances of abusive, harassing, or otherwise unacceptable behavior in Kubernetes may be reported by contacting the [Kubernetes Code of Conduct Committee](https://git.k8s.io/community/committee-code-of-conduct) via conduct@kubernetes.io. For other projects, please contact a CNCF project maintainer or our mediator, Mishi Choudhary via mishi@linux.com.
-
-This Code of Conduct is adapted from the Contributor Covenant
-(), version 1.2.0, available at
-
-
-### CNCF Events Code of Conduct
-
-CNCF events are governed by the Linux Foundation [Code of Conduct](https://events.linuxfoundation.org/code-of-conduct/) available on the event page.
-This is designed to be compatible with the above policy and also includes more details on responding to incidents.
\ No newline at end of file
+
+Please contact the [CNCF Code of Conduct Committee](mailto:conduct@cncf.io)
+in order to report violations of the Code of Conduct.
diff --git a/diagram/pom.xml b/diagram/pom.xml
index 41f8337a..d68a56cb 100644
--- a/diagram/pom.xml
+++ b/diagram/pom.xml
@@ -1,17 +1,14 @@
-
+
4.0.0
io.serverlessworkflow
serverlessworkflow-parent
- 4.0.0-SNAPSHOT
+ 4.3.0-SNAPSHOT
serverlessworkflow-diagram
Serverless Workflow :: Diagram
- ${project.parent.version}
jar
Java SDK for Serverless Workflow Specification
@@ -97,13 +94,13 @@
-
-
+
+
-
-
+
+
@@ -130,26 +127,6 @@
-
- com.coveo
- fmt-maven-plugin
-
- src/main/java
- src/test/java
- false
- .*\.java
- false
- false
-
-
-
-
-
- format
-
-
-
-
\ No newline at end of file
diff --git a/diagram/src/main/java/io/serverlessworkflow/diagram/WorkflowDiagramImpl.java b/diagram/src/main/java/io/serverlessworkflow/diagram/WorkflowDiagramImpl.java
index 5a2d4028..1fb5e656 100644
--- a/diagram/src/main/java/io/serverlessworkflow/diagram/WorkflowDiagramImpl.java
+++ b/diagram/src/main/java/io/serverlessworkflow/diagram/WorkflowDiagramImpl.java
@@ -26,9 +26,14 @@
public class WorkflowDiagramImpl implements WorkflowDiagram {
+ public static final String DEFAULT_TEMPLATE = "workflow-template";
+
@SuppressWarnings("unused")
private String source;
+ @SuppressWarnings("unused")
+ private String template = DEFAULT_TEMPLATE;
+
private Workflow workflow;
private boolean showLegend = false;
@@ -46,12 +51,18 @@ public WorkflowDiagram setSource(String source) {
return this;
}
+ @Override
+ public WorkflowDiagram setTemplate(String template) {
+ this.template = template;
+ return this;
+ }
+
@Override
public String getSvgDiagram() throws Exception {
if (workflow == null) {
throw new IllegalAccessException("Unable to get diagram - no workflow set.");
}
- String diagramSource = WorkflowToPlantuml.convert(workflow, showLegend);
+ String diagramSource = WorkflowToPlantuml.convert(template, workflow, showLegend);
SourceStringReader reader = new SourceStringReader(diagramSource);
final ByteArrayOutputStream os = new ByteArrayOutputStream();
reader.generateImage(os, new FileFormatOption(FileFormat.SVG));
diff --git a/diagram/src/main/java/io/serverlessworkflow/diagram/utils/WorkflowToPlantuml.java b/diagram/src/main/java/io/serverlessworkflow/diagram/utils/WorkflowToPlantuml.java
index acc112b8..956bcbeb 100644
--- a/diagram/src/main/java/io/serverlessworkflow/diagram/utils/WorkflowToPlantuml.java
+++ b/diagram/src/main/java/io/serverlessworkflow/diagram/utils/WorkflowToPlantuml.java
@@ -22,11 +22,12 @@
import org.thymeleaf.context.Context;
public class WorkflowToPlantuml {
- public static String convert(Workflow workflow, boolean showLegend) {
+
+ public static String convert(String template, Workflow workflow, boolean showLegend) {
TemplateEngine plantUmlTemplateEngine = ThymeleafConfig.templateEngine;
Context context = new Context();
context.setVariable("diagram", new WorkflowDiagramModel(workflow, showLegend));
- return plantUmlTemplateEngine.process("workflow-template", context);
+ return plantUmlTemplateEngine.process(template, context);
}
}
diff --git a/diagram/src/test/java/io/serverlessworkflow/diagram/test/CustomTemplateWorkflowDiagramTest.java b/diagram/src/test/java/io/serverlessworkflow/diagram/test/CustomTemplateWorkflowDiagramTest.java
new file mode 100644
index 00000000..5c9b7038
--- /dev/null
+++ b/diagram/src/test/java/io/serverlessworkflow/diagram/test/CustomTemplateWorkflowDiagramTest.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.diagram.test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.serverlessworkflow.api.Workflow;
+import io.serverlessworkflow.api.interfaces.WorkflowDiagram;
+import io.serverlessworkflow.diagram.WorkflowDiagramImpl;
+import io.serverlessworkflow.diagram.test.utils.DiagramTestUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+public class CustomTemplateWorkflowDiagramTest {
+
+ @ParameterizedTest
+ @ValueSource(strings = {"/examples/applicantrequest.json", "/examples/applicantrequest.yml"})
+ public void testSpecExamplesParsing(String workflowLocation) throws Exception {
+
+ Workflow workflow = Workflow.fromSource(DiagramTestUtils.readWorkflowFile(workflowLocation));
+
+ assertNotNull(workflow);
+ assertNotNull(workflow.getId());
+ assertNotNull(workflow.getName());
+ assertNotNull(workflow.getStates());
+
+ WorkflowDiagram workflowDiagram =
+ new WorkflowDiagramImpl()
+ .showLegend(true)
+ .setWorkflow(workflow)
+ .setTemplate("custom-template");
+
+ String diagramSVG = workflowDiagram.getSvgDiagram();
+
+ Assertions.assertNotNull(diagramSVG);
+ // custom template uses customcolor in the legend
+ Assertions.assertTrue(diagramSVG.contains("customcolor"));
+ }
+}
diff --git a/diagram/src/test/resources/templates/plantuml/custom-template.txt b/diagram/src/test/resources/templates/plantuml/custom-template.txt
new file mode 100644
index 00000000..e162afc5
--- /dev/null
+++ b/diagram/src/test/resources/templates/plantuml/custom-template.txt
@@ -0,0 +1,46 @@
+@startuml
+skinparam backgroundColor White
+skinparam legendBackgroundColor White
+skinparam legendBorderColor White
+skinparam state {
+ StartColor Green
+ EndColor Orange
+ BackgroundColor GhostWhite
+ BackgroundColor<< workflow >> White
+ BorderColor Black
+ ArrowColor Black
+
+ BorderColor<< event >> #7fe5f0
+ BorderColor<< operation >> #bada55
+ BorderColor<< switch >> #92a0f2
+ BorderColor<< sleep >> #b83b5e
+ BorderColor<< parallel >> #6a2c70
+ BorderColor<< inject >> #1e5f74
+ BorderColor<< foreach >> #931a25
+ BorderColor<< callback >> #ffcb8e
+}
+state "[(${diagram.title})]" as workflow << workflow >> {
+
+[# th:each="stateDef : ${diagram.modelStateDefs}" ]
+[(${stateDef.toString()})]
+[/]
+
+[# th:each="state : ${diagram.modelStates}" ]
+[(${state.toString()})]
+[/]
+
+[# th:each="connection : ${diagram.modelConnections}" ]
+[(${connection.toString()})]
+[/]
+
+}
+
+[# th:if="${diagram.showLegend}" ]
+legend center
+State Types and Border Colors:
+| Event | Operation | Switch | Sleep | Parallel | Inject | ForEach | CallBack |
+|<#7fe5f0>|<#bada55>|<#92a0f2>|<#b83b5e>|<#6a2c70>|<#1e5f74>|<#931a25>||
+endlegend
+[/]
+
+@enduml
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 5ef1c788..d1d9b444 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,74 +1,96 @@
-
- 4.0.0
+
+ 4.0.0
- io.serverlessworkflow
- serverlessworkflow-parent
- 4.0.0-SNAPSHOT
- pom
+ io.serverlessworkflow
+ serverlessworkflow-parent
+ 4.3.0-SNAPSHOT
+ pom
- Serverless Workflow :: Parent
- https://serverlessworkflow.io/sdk-java/
- Java SDK for Serverless Workflow Specification
- 2020
-
- CNCF
- https://www.cncf.io//
-
-
-
- The Apache Software License, Version 2.0
- http://www.apache.org/licenses/LICENSE-2.0.txt
- repo
-
-
+ Serverless Workflow :: Parent
+ https://serverlessworkflow.io/sdk-java/
+ Java SDK for Serverless Workflow Specification
+ 2020
+
+
+ serverless-workflow
+ Serverless Workflow Specification Authors
+ CNCF
+
+
+
+ CNCF
+ https://www.cncf.io//
+
+
+
+ The Apache Software License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+ scm:git:git@github.com:serverlessworkflow/sdk-java.git
+ scm:git:git@github.com:serverlessworkflow/sdk-java.git
+ https://github.com/serverlessworkflow/sdk-java
+ HEAD
+
-
- api
- spi
- validation
- diagram
- utils
-
+
+ api
+ spi
+ validation
+ diagram
+ utils
+
-
- 1.8
- 1.8
- 1.8
- UTF-8
- 3.6.2
- 3.0.0-M2
- 8
- 3.8.1
- 2.22.0
- 2.22.0
- 3.1.1
- 2.8.2
+
+ 17
+ ${java.version}
+ ${java.version}
+ ${java.version}
+ UTF-8
+ 3.9.7
- 1.7.25
- 2.10.3
- 2.0.1.Final
- 6.0
- 5.${version.org.junit.minor}
- ${version.org.junit}
- 3.0.0
- 1.1.3
- 3.13.2
- 1.0.1
- 3.9
- 1.3
- 1.5.0
- 1.12.1
- 20200518
- 3.0.11.RELEASE
- 8059
- 0.17.0
- 2.9
+
+ 3.2.1
+ 3.6.0
+ 3.14.0
+ 3.1.4
+ 3.5.0
+ 3.5.3
+ 2.25
+ 3.2.7
+ 3.4.2
+ ${java.version}
+ 1.2.2
+ 3.11.2
+ 3.1.1
+ 3.3.1
+ 3.5.3
+ 1.7.0
-
- true
-
+ 1.5.18
+ 2.19.0
+ 1.5.7
+ 3.17.0
+ 0.18.1
+ 3.0
+ 3.1.1
+ 1.5.3
+ 3.27.3
+ 5.13.1
+ 5.18.0
+ 2.0.17
+ 8059
+ 3.1.3.RELEASE
+
+
+
+ true
+
-
- java
- true
-
+
+ java
+ true
+
+
+
+
+
+ org.slf4j
+ slf4j-api
+ ${version.org.slf4j}
+
+
+ org.slf4j
+ jcl-over-slf4j
+ ${version.org.slf4j}
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ ${version.com.fasterxml.jackson}
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${version.com.fasterxml.jackson}
+
+
+ com.networknt
+ json-schema-validator
+ ${version.com.networknt}
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-yaml
+ ${version.com.fasterxml.jackson}
+
+
+ jakarta.validation
+ jakarta.validation-api
+ ${version.jakarta.validation}
+
+
+ org.apache.commons
+ commons-lang3
+ ${version.commons.lang}
+
+
+ org.thymeleaf
+ thymeleaf
+ ${version.thymeleaf}
+
+
+ net.sourceforge.plantuml
+ plantuml
+ ${version.plantuml}
+
+
+ guru.nidi
+ graphviz-java
+ ${version.graphviz}
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${version.org.junit.jupiter}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${version.org.junit.jupiter}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ ${version.org.junit.jupiter}
+ test
+
+
+ org.mockito
+ mockito-core
+ ${version.org.mockito}
+ test
+
+
+ ch.qos.logback
+ logback-classic
+ ${version.ch.qos.logback}
+ test
+
+
+ org.assertj
+ assertj-core
+ ${version.org.assertj}
+ test
+
+
+ org.hamcrest
+ hamcrest-library
+ ${version.hamcrest}
+ test
+
+
+ org.skyscreamer
+ jsonassert
+ ${version.jsonassert}
+ test
+
+
+
+
+
+
+ org.slf4j
+ slf4j-nop
+ ${version.org.slf4j}
+ test
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ buildnumber-maven-plugin
+ ${version.buildnumber.plugin}
+
+
+ get-scm-revision
+ initialize
+
+ create
+
+
+ false
+ false
+ UNKNOWN
+ true
+
+
+
+
+
+ maven-compiler-plugin
+ ${version.compiler.plugin}
+
+ true
+ true
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+ true
+
+ -Xlint:unchecked
+
+
+
+
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ ${version.nexus.plugin}
+ true
+
+ ossrh
+ https://oss.sonatype.org/
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ ${version.gpg.plugin}
+
+
+ maven-deploy-plugin
+ ${version.deploy.plugin}
+
+ 10
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ ${version.enforcer.plugin}
+
+
+ enforce-versions
+
+ enforce
+
+
+
+
+ ${version.maven}
+
+
+ ${version.jdk}
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ ${version.source.plugin}
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+ ${project.url}
+ ${java.version}
+ ${java.vendor}
+ ${os.name}
+ ${os.arch}
+ ${os.version}
+ ${project.scm.url}
+ ${project.scm.connection}
+ ${buildNumber}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ ${version.release.plugin}
+
+ clean install
+ true
+ @{project.version}
+ false
+ true
+ false
+
+
+
+ org.jsonschema2pojo
+ jsonschema2pojo-maven-plugin
+ ${version.jsonschema2pojo-maven-plugin}
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${version.surefire.plugin}
+
+ -Xmx1024m -XX:+IgnoreUnrecognizedVMOptions -XX:MaxPermSize=256m
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+ ${version.failsafe.plugin}
+
+ -Xmx1024m -XX:+IgnoreUnrecognizedVMOptions -XX:MaxPermSize=256m
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+ ${version.checkstyle.plugin}
+
+
+ com.spotify.fmt
+ fmt-maven-plugin
+
+ src/main/java
+ src/test/java
+ false
+ .*\.java
+ false
+ false
+
+
+
+
+
+ format
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ ${version.jar.plugin}
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+
+ ${project.url}
+ ${java.version}
+ ${java.vendor}
+ ${os.name}
+ ${os.arch}
+ ${os.version}
+ ${project.scm.url}
+ ${project.scm.connection}
+ ${buildNumber}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${version.javadoc.plugin}
+
+ false
+
+
+
+
+
-
-
-
- org.slf4j
- slf4j-api
- ${version.org.slf4j}
-
-
- org.slf4j
- jcl-over-slf4j
- ${version.org.slf4j}
-
-
- com.fasterxml.jackson.core
- jackson-core
- ${version.com.fasterxml.jackson}
-
-
- com.fasterxml.jackson.core
- jackson-databind
- ${version.com.fasterxml.jackson}
-
-
- com.fasterxml.jackson.dataformat
- jackson-dataformat-yaml
- ${version.com.fasterxml.jackson}
-
-
- javax.validation
- validation-api
- ${version.javax.validation}
-
-
- org.apache.commons
- commons-lang3
- ${commons.lang.version}
-
-
- com.github.erosb
- everit-json-schema
- ${json.schema.validation.version}
-
-
- commons-logging
- commons-logging
-
-
-
-
- org.json
- json
- ${version.json}
-
-
- org.thymeleaf
- thymeleaf
- ${version.thymeleaf}
-
-
- net.sourceforge.plantuml
- plantuml
- ${version.plantuml}
-
-
- guru.nidi
- graphviz-java
- ${version.graphviz}
-
-
-
- org.junit.jupiter
- junit-jupiter-api
- ${version.org.junit.jupiter}
- test
-
-
- org.junit.jupiter
- junit-jupiter-engine
- ${version.org.junit.jupiter}
- test
-
-
- org.junit.jupiter
- junit-jupiter-params
- ${version.org.junit.jupiter}
- test
-
-
- org.mockito
- mockito-core
- ${version.org.mockito}
- test
-
-
- ch.qos.logback
- logback-classic
- ${version.ch.qos.logback}
- test
-
-
- org.assertj
- assertj-core
- ${version.org.assertj}
- test
-
-
- org.hamcrest
- hamcrest-library
- ${hamcrest.version}
- test
-
-
- org.skyscreamer
- jsonassert
- ${jsonassert.version}
- test
-
-
-
+
+
+ ossrh-snapshots
+ https://oss.sonatype.org/content/repositories/snapshots
+
+
+ ossrh
+ https://oss.sonatype.org/service/local/staging/deploy/maven2/
+
+
-
-
-
-
- maven-deploy-plugin
- ${version.deploy.plugin}
-
- 10
-
-
-
- org.apache.maven.plugins
- maven-enforcer-plugin
- ${version.enforcer.plugin}
-
-
- enforce-versions
-
- enforce
-
-
-
-
- ${version.maven}
-
-
- ${version.jdk}
-
-
-
-
-
-
-
- maven-compiler-plugin
- ${version.compiler.plugin}
-
- true
- true
-
- -Xlint:unchecked
-
-
-
-
- org.jsonschema2pojo
- jsonschema2pojo-maven-plugin
- ${version.jsonschema2pojo-maven-plugin}
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
- ${version.surefire.plugin}
-
- -Xmx1024m -XX:MaxPermSize=256m
-
-
-
- org.apache.maven.plugins
- maven-failsafe-plugin
- ${version.failsafe.plugin}
-
- -Xmx1024m -XX:MaxPermSize=256m
-
-
-
- org.apache.maven.plugins
- maven-checkstyle-plugin
- ${version.checkstyle.plugin}
-
-
- com.coveo
- fmt-maven-plugin
- ${version.fmt-maven-plugin}
-
-
-
-
+
+
+ central
+ Central Repository
+ https://repo.maven.apache.org/maven2
+ default
+
+ false
+
+
+
-
-
- ossrh
- https://oss.sonatype.org/content/repositories/snapshots
-
-
- ossrh
- https://oss.sonatype.org/service/local/staging/deploy/maven2/
-
-
+
+
+ release
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+
+
+ --pinentry-mode
+ loopback
+
+
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+ package
+
+ jar
+
+
+
+
+
+
+
+
-
-
- central
- Central Repository
- https://repo.maven.apache.org/maven2
- default
-
- false
-
-
-
-
+
\ No newline at end of file
diff --git a/spi/pom.xml b/spi/pom.xml
index e713102e..1bb2c028 100644
--- a/spi/pom.xml
+++ b/spi/pom.xml
@@ -1,17 +1,14 @@
-
+
4.0.0
io.serverlessworkflow
serverlessworkflow-parent
- 4.0.0-SNAPSHOT
+ 4.3.0-SNAPSHOT
serverlessworkflow-spi
Serverless Workflow :: SPI
- ${project.parent.version}
jar
Java SDK for Serverless Workflow Specification
@@ -69,13 +66,13 @@
-
-
+
+
-
-
+
+
@@ -102,26 +99,6 @@
-
- com.coveo
- fmt-maven-plugin
-
- src/main/java
- src/test/java
- false
- .*\.java
- false
- false
-
-
-
-
-
- format
-
-
-
-
\ No newline at end of file
diff --git a/utils/pom.xml b/utils/pom.xml
index be6a7444..23a23711 100644
--- a/utils/pom.xml
+++ b/utils/pom.xml
@@ -1,17 +1,14 @@
-
+
4.0.0
io.serverlessworkflow
serverlessworkflow-parent
- 4.0.0-SNAPSHOT
+ 4.3.0-SNAPSHOT
serverlessworkflow-util
Serverless Workflow :: Utils
- ${project.parent.version}
jar
Java SDK for Serverless Workflow Specification
@@ -69,13 +66,13 @@
-
-
+
+
-
-
+
+
@@ -102,26 +99,6 @@
-
- com.coveo
- fmt-maven-plugin
-
- src/main/java
- src/test/java
- false
- .*\.java
- false
- false
-
-
-
-
-
- format
-
-
-
-
\ No newline at end of file
diff --git a/utils/src/main/java/io/serverlessworkflow/utils/WorkflowUtils.java b/utils/src/main/java/io/serverlessworkflow/utils/WorkflowUtils.java
index 29b8c7cc..a5673b40 100644
--- a/utils/src/main/java/io/serverlessworkflow/utils/WorkflowUtils.java
+++ b/utils/src/main/java/io/serverlessworkflow/utils/WorkflowUtils.java
@@ -574,7 +574,7 @@ public static JsonNode mergeNodes(JsonNode mainNode, JsonNode updateNode) {
if (mainNode instanceof ObjectNode) {
// Overwrite field
JsonNode value = updateNode.get(fieldName);
- ((ObjectNode) mainNode).put(fieldName, value);
+ ((ObjectNode) mainNode).set(fieldName, value);
}
}
}
@@ -591,7 +591,7 @@ public static JsonNode mergeNodes(JsonNode mainNode, JsonNode updateNode) {
* @return original, main node with field added
*/
public static JsonNode addNode(JsonNode mainNode, JsonNode toAddNode, String fieldName) {
- ((ObjectNode) mainNode).put(fieldName, toAddNode);
+ ((ObjectNode) mainNode).set(fieldName, toAddNode);
return mainNode;
}
@@ -604,7 +604,7 @@ public static JsonNode addNode(JsonNode mainNode, JsonNode toAddNode, String fie
* @return original, main node with array added
*/
public static JsonNode addArray(JsonNode mainNode, ArrayNode toAddArray, String arrayName) {
- ((ObjectNode) mainNode).put(arrayName, toAddArray);
+ ((ObjectNode) mainNode).set(arrayName, toAddArray);
return mainNode;
}
@@ -618,7 +618,7 @@ public static JsonNode addArray(JsonNode mainNode, ArrayNode toAddArray, String
*/
public static JsonNode addFieldValue(JsonNode mainNode, Object toAddValue, String fieldName) {
ObjectMapper mapper = new ObjectMapper();
- ((ObjectNode) mainNode).put(fieldName, mapper.valueToTree(toAddValue));
+ ((ObjectNode) mainNode).set(fieldName, mapper.valueToTree(toAddValue));
return mainNode;
}
}
diff --git a/validation/pom.xml b/validation/pom.xml
index 7219d952..63c7901f 100644
--- a/validation/pom.xml
+++ b/validation/pom.xml
@@ -1,17 +1,14 @@
-
+
4.0.0
io.serverlessworkflow
serverlessworkflow-parent
- 4.0.0-SNAPSHOT
+ 4.3.0-SNAPSHOT
serverlessworkflow-validation
Serverless Workflow :: Validation
- ${project.parent.version}
jar
Java SDK for Serverless Workflow Specification
@@ -35,12 +32,10 @@
org.apache.commons
commons-lang3
-
- com.github.erosb
- everit-json-schema
+ com.networknt
+ json-schema-validator
-
org.junit.jupiter
@@ -93,13 +88,13 @@
-
-
+
+
-
-
+
+
@@ -126,26 +121,6 @@
-
- com.coveo
- fmt-maven-plugin
-
- src/main/java
- src/test/java
- false
- .*\.java
- false
- false
-
-
-
-
-
- format
-
-
-
-
diff --git a/validation/src/main/java/io/serverlessworkflow/validation/WorkflowValidatorImpl.java b/validation/src/main/java/io/serverlessworkflow/validation/WorkflowValidatorImpl.java
index 130e4c24..12e4e915 100644
--- a/validation/src/main/java/io/serverlessworkflow/validation/WorkflowValidatorImpl.java
+++ b/validation/src/main/java/io/serverlessworkflow/validation/WorkflowValidatorImpl.java
@@ -15,8 +15,9 @@
*/
package io.serverlessworkflow.validation;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.networknt.schema.JsonSchemaFactory;
+import com.networknt.schema.SpecVersion.VersionFlag;
import io.serverlessworkflow.api.Workflow;
import io.serverlessworkflow.api.actions.Action;
import io.serverlessworkflow.api.events.EventDefinition;
@@ -27,15 +28,14 @@
import io.serverlessworkflow.api.states.*;
import io.serverlessworkflow.api.switchconditions.DataCondition;
import io.serverlessworkflow.api.switchconditions.EventCondition;
+import io.serverlessworkflow.api.utils.Utils;
import io.serverlessworkflow.api.validation.ValidationError;
import io.serverlessworkflow.api.validation.WorkflowSchemaLoader;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import org.everit.json.schema.Schema;
-import org.everit.json.schema.ValidationException;
-import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -44,7 +44,7 @@ public class WorkflowValidatorImpl implements WorkflowValidator {
private static final Logger logger = LoggerFactory.getLogger(WorkflowValidatorImpl.class);
private boolean schemaValidationEnabled = true;
private List validationErrors = new ArrayList<>();
- private Schema workflowSchema = WorkflowSchemaLoader.getWorkflowSchema();
+ private JsonNode workflowSchema = WorkflowSchemaLoader.getWorkflowSchema();
private String source;
private Workflow workflow;
@@ -66,34 +66,13 @@ public List validate() {
if (workflow == null) {
try {
if (schemaValidationEnabled && source != null) {
- try {
- if (!source.trim().startsWith("{")) {
- // convert yaml to json to validate
- ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
- Object obj = yamlReader.readValue(source, Object.class);
-
- ObjectMapper jsonWriter = new ObjectMapper();
-
- workflowSchema.validate(new JSONObject(jsonWriter.writeValueAsString(obj)));
- } else {
- workflowSchema.validate(new JSONObject(source));
- }
- } catch (ValidationException e) {
- e.getCausingExceptions().stream()
- .map(ValidationException::getMessage)
- .forEach(
- m -> {
- if ((!m.equals("#/functions: expected type: JSONObject, found: JSONArray")
- && !m.equals("#/events: expected type: JSONObject, found: JSONArray")
- && !m.equals("#/start: expected type: JSONObject, found: String")
- && !m.equals("#/retries: expected type: JSONObject, found: JSONArray"))) {
- addValidationError(m, ValidationError.SCHEMA_VALIDATION);
- }
- });
- }
+ JsonSchemaFactory.getInstance(VersionFlag.V7)
+ .getSchema(workflowSchema)
+ .validate(Utils.getNode(source))
+ .forEach(m -> addValidationError(m.getMessage(), ValidationError.SCHEMA_VALIDATION));
}
- } catch (Exception e) {
- logger.error("Schema validation exception: " + e.getMessage());
+ } catch (IOException e) {
+ logger.error("Unexpected error during validation", e);
}
}
@@ -101,42 +80,34 @@ public List validate() {
// there is no point of doing the workflow validation
if (validationErrors.size() > 0) {
return validationErrors;
- } else {
- if (workflow == null) {
- workflow = Workflow.fromSource(source);
- }
-
- List functions =
- workflow.getFunctions() != null ? workflow.getFunctions().getFunctionDefs() : null;
+ } else if (workflow == null) {
+ workflow = Workflow.fromSource(source);
+ }
- List events =
- workflow.getEvents() != null ? workflow.getEvents().getEventDefs() : null;
+ List functions =
+ workflow.getFunctions() != null ? workflow.getFunctions().getFunctionDefs() : null;
- if (workflow.getId() == null || workflow.getId().trim().isEmpty()) {
- addValidationError("Workflow id should not be empty", ValidationError.WORKFLOW_VALIDATION);
- }
+ List events =
+ workflow.getEvents() != null ? workflow.getEvents().getEventDefs() : null;
- if (workflow.getName() == null || workflow.getName().trim().isEmpty()) {
- addValidationError(
- "Workflow name should not be empty", ValidationError.WORKFLOW_VALIDATION);
- }
-
- if (workflow.getStart() == null) {
- addValidationError(
- "Workflow must define a starting state", ValidationError.WORKFLOW_VALIDATION);
- }
+ if ((workflow.getId() == null || workflow.getId().trim().isEmpty())
+ && (workflow.getKey() == null || workflow.getKey().trim().isEmpty())) {
+ addValidationError(
+ "Workflow id or key should not be empty", ValidationError.WORKFLOW_VALIDATION);
+ }
- if (workflow.getVersion() == null || workflow.getVersion().trim().isEmpty()) {
- addValidationError(
- "Workflow version should not be empty", ValidationError.WORKFLOW_VALIDATION);
- }
+ if (workflow.getVersion() == null || workflow.getVersion().trim().isEmpty()) {
+ addValidationError(
+ "Workflow version should not be empty", ValidationError.WORKFLOW_VALIDATION);
+ }
- if (workflow.getStates() == null || workflow.getStates().isEmpty()) {
- addValidationError("No states found", ValidationError.WORKFLOW_VALIDATION);
- }
+ if (workflow.getStates() == null || workflow.getStates().isEmpty()) {
+ addValidationError("No states found", ValidationError.WORKFLOW_VALIDATION);
+ }
- if (workflow.getStates() != null && !workflow.getStates().isEmpty()) {
- boolean existingStateWithStartProperty = false;
+ if (workflow.getStates() != null && !workflow.getStates().isEmpty()) {
+ boolean existingStateWithStartProperty = false;
+ if (workflow.getStart() != null) {
String startProperty = workflow.getStart().getStateName();
for (State s : workflow.getStates()) {
if (s.getName().equals(startProperty)) {
@@ -144,226 +115,209 @@ public List validate() {
break;
}
}
- if (!existingStateWithStartProperty) {
- addValidationError(
- "No state name found that matches the workflow start definition",
- ValidationError.WORKFLOW_VALIDATION);
- }
+ } else {
+ existingStateWithStartProperty = true;
}
+ if (!existingStateWithStartProperty) {
+ addValidationError(
+ "No state name found that matches the workflow start definition",
+ ValidationError.WORKFLOW_VALIDATION);
+ }
+ }
- Validation validation = new Validation();
- if (workflow.getStates() != null && !workflow.getStates().isEmpty()) {
- workflow
- .getStates()
- .forEach(
- s -> {
- if (s.getName() != null && s.getName().trim().isEmpty()) {
- addValidationError(
- "State name should not be empty", ValidationError.WORKFLOW_VALIDATION);
- } else {
- validation.addState(s.getName());
- }
-
- if (s.getEnd() != null) {
- validation.addEndState();
- }
-
- if (s instanceof OperationState) {
- OperationState operationState = (OperationState) s;
-
- List actions = operationState.getActions();
- for (Action action : actions) {
- if (action.getFunctionRef() != null) {
- if (action.getFunctionRef().getRefName().isEmpty()) {
- addValidationError(
- "Operation State action functionRef should not be null or empty",
- ValidationError.WORKFLOW_VALIDATION);
- }
-
- if (!haveFunctionDefinition(
- action.getFunctionRef().getRefName(), functions)) {
- addValidationError(
- "Operation State action functionRef does not reference an existing workflow function definition",
- ValidationError.WORKFLOW_VALIDATION);
- }
+ Validation validation = new Validation();
+ if (workflow.getStates() != null && !workflow.getStates().isEmpty()) {
+ workflow
+ .getStates()
+ .forEach(
+ s -> {
+ if (s.getName() != null && s.getName().trim().isEmpty()) {
+ addValidationError(
+ "State name should not be empty", ValidationError.WORKFLOW_VALIDATION);
+ } else {
+ validation.addState(s.getName());
+ }
+
+ if (s.getEnd() != null) {
+ validation.addEndState();
+ }
+
+ if (s instanceof OperationState) {
+ OperationState operationState = (OperationState) s;
+
+ List actions = operationState.getActions();
+ for (Action action : actions) {
+ if (action.getFunctionRef() != null) {
+ if (action.getFunctionRef().getRefName().isEmpty()) {
+ addValidationError(
+ "Operation State action functionRef should not be null or empty",
+ ValidationError.WORKFLOW_VALIDATION);
}
- if (action.getEventRef() != null) {
- if (action.getEventRef().getTriggerEventRef().isEmpty()) {
- addValidationError(
- "Operation State action trigger eventRef does not reference an existing workflow event definition",
- ValidationError.WORKFLOW_VALIDATION);
- }
-
- if (action.getEventRef().getResultEventRef().isEmpty()) {
- addValidationError(
- "Operation State action results eventRef does not reference an existing workflow event definition",
- ValidationError.WORKFLOW_VALIDATION);
- }
-
- if (!haveEventsDefinition(
- action.getEventRef().getTriggerEventRef(), events)) {
- addValidationError(
- "Operation State action trigger event def does not reference an existing workflow event definition",
- ValidationError.WORKFLOW_VALIDATION);
- }
-
- if (!haveEventsDefinition(
- action.getEventRef().getResultEventRef(), events)) {
- addValidationError(
- "Operation State action results event def does not reference an existing workflow event definition",
- ValidationError.WORKFLOW_VALIDATION);
- }
+ if (!haveFunctionDefinition(
+ action.getFunctionRef().getRefName(), functions)) {
+ addValidationError(
+ "Operation State action functionRef does not reference an existing workflow function definition",
+ ValidationError.WORKFLOW_VALIDATION);
}
}
- }
- if (s instanceof EventState) {
- EventState eventState = (EventState) s;
- if (eventState.getOnEvents() == null || eventState.getOnEvents().size() < 1) {
- addValidationError(
- "Event State has no eventActions defined",
- ValidationError.WORKFLOW_VALIDATION);
- }
- List eventsActionsList = eventState.getOnEvents();
- for (OnEvents onEvents : eventsActionsList) {
+ if (action.getEventRef() != null) {
- List eventRefs = onEvents.getEventRefs();
- if (eventRefs == null || eventRefs.size() < 1) {
+ if (!haveEventsDefinition(
+ action.getEventRef().getTriggerEventRef(), events)) {
addValidationError(
- "Event State eventsActions has no event refs",
+ "Operation State action trigger event def does not reference an existing workflow event definition",
+ ValidationError.WORKFLOW_VALIDATION);
+ }
+
+ if (!haveEventsDefinition(action.getEventRef().getResultEventRef(), events)) {
+ addValidationError(
+ "Operation State action results event def does not reference an existing workflow event definition",
ValidationError.WORKFLOW_VALIDATION);
- } else {
- for (String eventRef : eventRefs) {
- if (!haveEventsDefinition(eventRef, events)) {
- addValidationError(
- "Event State eventsActions eventRef does not match a declared workflow event definition",
- ValidationError.WORKFLOW_VALIDATION);
- }
- }
}
}
}
+ }
- if (s instanceof SwitchState) {
- SwitchState switchState = (SwitchState) s;
- if ((switchState.getDataConditions() == null
- || switchState.getDataConditions().size() < 1)
- && (switchState.getEventConditions() == null
- || switchState.getEventConditions().size() < 1)) {
- addValidationError(
- "Switch state should define either data or event conditions",
- ValidationError.WORKFLOW_VALIDATION);
- }
+ if (s instanceof EventState) {
+ EventState eventState = (EventState) s;
+ if (eventState.getOnEvents() == null || eventState.getOnEvents().size() < 1) {
+ addValidationError(
+ "Event State has no eventActions defined",
+ ValidationError.WORKFLOW_VALIDATION);
+ }
+ List eventsActionsList = eventState.getOnEvents();
+ for (OnEvents onEvents : eventsActionsList) {
- if (switchState.getDefaultCondition() == null) {
+ List eventRefs = onEvents.getEventRefs();
+ if (eventRefs == null || eventRefs.size() < 1) {
addValidationError(
- "Switch state should define a default transition",
+ "Event State eventsActions has no event refs",
ValidationError.WORKFLOW_VALIDATION);
- }
-
- if (switchState.getEventConditions() != null
- && switchState.getEventConditions().size() > 0) {
- List eventConditions = switchState.getEventConditions();
- for (EventCondition ec : eventConditions) {
- if (!haveEventsDefinition(ec.getEventRef(), events)) {
+ } else {
+ for (String eventRef : eventRefs) {
+ if (!haveEventsDefinition(eventRef, events)) {
addValidationError(
- "Switch state event condition eventRef does not reference a defined workflow event",
+ "Event State eventsActions eventRef does not match a declared workflow event definition",
ValidationError.WORKFLOW_VALIDATION);
}
- if (ec.getEnd() != null) {
- validation.addEndState();
- }
}
}
+ }
+ }
+
+ if (s instanceof SwitchState) {
+ SwitchState switchState = (SwitchState) s;
+ if ((switchState.getDataConditions() == null
+ || switchState.getDataConditions().size() < 1)
+ && (switchState.getEventConditions() == null
+ || switchState.getEventConditions().size() < 1)) {
+ addValidationError(
+ "Switch state should define either data or event conditions",
+ ValidationError.WORKFLOW_VALIDATION);
+ }
- if (switchState.getDataConditions() != null
- && switchState.getDataConditions().size() > 0) {
- List dataConditions = switchState.getDataConditions();
- for (DataCondition dc : dataConditions) {
- if (dc.getEnd() != null) {
- validation.addEndState();
- }
+ if (switchState.getDefaultCondition() == null) {
+ addValidationError(
+ "Switch state should define a default transition",
+ ValidationError.WORKFLOW_VALIDATION);
+ }
+
+ if (switchState.getEventConditions() != null
+ && switchState.getEventConditions().size() > 0) {
+ List eventConditions = switchState.getEventConditions();
+ for (EventCondition ec : eventConditions) {
+ if (!haveEventsDefinition(ec.getEventRef(), events)) {
+ addValidationError(
+ "Switch state event condition eventRef does not reference a defined workflow event",
+ ValidationError.WORKFLOW_VALIDATION);
+ }
+ if (ec.getEnd() != null) {
+ validation.addEndState();
}
}
}
- if (s instanceof SleepState) {
- SleepState sleepState = (SleepState) s;
- if (sleepState.getDuration() == null || sleepState.getDuration().length() < 1) {
- addValidationError(
- "Sleep state should have a non-empty time delay",
- ValidationError.WORKFLOW_VALIDATION);
+ if (switchState.getDataConditions() != null
+ && switchState.getDataConditions().size() > 0) {
+ List dataConditions = switchState.getDataConditions();
+ for (DataCondition dc : dataConditions) {
+ if (dc.getEnd() != null) {
+ validation.addEndState();
+ }
}
}
+ }
- if (s instanceof ParallelState) {
- ParallelState parallelState = (ParallelState) s;
-
- if (parallelState.getBranches() == null
- || parallelState.getBranches().size() < 2) {
- addValidationError(
- "Parallel state should have at lest two branches",
- ValidationError.WORKFLOW_VALIDATION);
- }
+ if (s instanceof SleepState) {
+ SleepState sleepState = (SleepState) s;
+ if (sleepState.getDuration() == null || sleepState.getDuration().length() < 1) {
+ addValidationError(
+ "Sleep state should have a non-empty time delay",
+ ValidationError.WORKFLOW_VALIDATION);
}
+ }
- if (s instanceof InjectState) {
- InjectState injectState = (InjectState) s;
- if (injectState.getData() == null || injectState.getData().isEmpty()) {
- addValidationError(
- "InjectState should have non-null data",
- ValidationError.WORKFLOW_VALIDATION);
- }
+ if (s instanceof ParallelState) {
+ ParallelState parallelState = (ParallelState) s;
+
+ if (parallelState.getBranches() == null
+ || parallelState.getBranches().size() < 2) {
+ addValidationError(
+ "Parallel state should have at lest two branches",
+ ValidationError.WORKFLOW_VALIDATION);
}
+ }
- if (s instanceof ForEachState) {
- ForEachState forEachState = (ForEachState) s;
- if (forEachState.getInputCollection() == null
- || forEachState.getInputCollection().isEmpty()) {
- addValidationError(
- "ForEach state should have a valid inputCollection",
- ValidationError.WORKFLOW_VALIDATION);
- }
+ if (s instanceof InjectState) {
+ InjectState injectState = (InjectState) s;
+ if (injectState.getData() == null || injectState.getData().isEmpty()) {
+ addValidationError(
+ "InjectState should have non-null data",
+ ValidationError.WORKFLOW_VALIDATION);
+ }
+ }
- if (forEachState.getIterationParam() == null
- || forEachState.getIterationParam().isEmpty()) {
- addValidationError(
- "ForEach state should have a valid iteration parameter",
- ValidationError.WORKFLOW_VALIDATION);
- }
+ if (s instanceof ForEachState) {
+ ForEachState forEachState = (ForEachState) s;
+ if (forEachState.getInputCollection() == null
+ || forEachState.getInputCollection().isEmpty()) {
+ addValidationError(
+ "ForEach state should have a valid inputCollection",
+ ValidationError.WORKFLOW_VALIDATION);
}
+ }
- if (s instanceof CallbackState) {
- CallbackState callbackState = (CallbackState) s;
+ if (s instanceof CallbackState) {
+ CallbackState callbackState = (CallbackState) s;
- if (!haveEventsDefinition(callbackState.getEventRef(), events)) {
- addValidationError(
- "CallbackState event ref does not reference a defined workflow event definition",
- ValidationError.WORKFLOW_VALIDATION);
- }
+ if (!haveEventsDefinition(callbackState.getEventRef(), events)) {
+ addValidationError(
+ "CallbackState event ref does not reference a defined workflow event definition",
+ ValidationError.WORKFLOW_VALIDATION);
+ }
- if (haveFunctionDefinition(
- callbackState.getAction().getFunctionRef().getRefName(), functions)) {
- addValidationError(
- "CallbackState action function ref does not reference a defined workflow function definition",
- ValidationError.WORKFLOW_VALIDATION);
- }
+ if (!haveFunctionDefinition(
+ callbackState.getAction().getFunctionRef().getRefName(), functions)) {
+ addValidationError(
+ "CallbackState action function ref does not reference a defined workflow function definition",
+ ValidationError.WORKFLOW_VALIDATION);
}
- });
+ }
+ });
- if (validation.endStates == 0) {
- addValidationError("No end state found.", ValidationError.WORKFLOW_VALIDATION);
- }
+ if (validation.endStates == 0) {
+ addValidationError("No end state found.", ValidationError.WORKFLOW_VALIDATION);
}
-
- return validationErrors;
}
+
+ return validationErrors;
}
@Override
public boolean isValid() {
- return validate().size() < 1;
+ return validate().isEmpty();
}
@Override
@@ -392,17 +346,30 @@ private boolean haveFunctionDefinition(String functionName, List events) {
+ if (eventName == null) {
+ return true;
+ }
if (events != null) {
EventDefinition eve =
events.stream().filter(e -> e.getName().equals(eventName)).findFirst().orElse(null);
-
return eve == null ? false : true;
} else {
return false;
}
}
+ private static final Set skipMessages =
+ new HashSet() {
+ {
+ add("$.start: string found, object expected");
+ add("$.functions: array found, object expected");
+ }
+ };
+
private void addValidationError(String message, String type) {
+ if (skipMessages.contains(message)) {
+ return;
+ }
ValidationError mainError = new ValidationError();
mainError.setMessage(message);
mainError.setType(type);
@@ -410,30 +377,9 @@ private void addValidationError(String message, String type) {
}
private class Validation {
-
- final Set events = new HashSet<>();
- final Set functions = new HashSet<>();
final Set states = new HashSet<>();
Integer endStates = 0;
- void addFunction(String name) {
- if (functions.contains(name)) {
- addValidationError(
- "Function does not have an unique name: " + name, ValidationError.WORKFLOW_VALIDATION);
- } else {
- functions.add(name);
- }
- }
-
- void addEvent(String name) {
- if (events.contains(name)) {
- addValidationError(
- "Event does not have an unique name: " + name, ValidationError.WORKFLOW_VALIDATION);
- } else {
- events.add(name);
- }
- }
-
void addState(String name) {
if (states.contains(name)) {
addValidationError(
diff --git a/validation/src/test/java/io/serverlessworkflow/validation/test/WorkflowValidationTest.java b/validation/src/test/java/io/serverlessworkflow/validation/test/WorkflowValidationTest.java
index 8baf9dfa..6ccef44f 100644
--- a/validation/src/test/java/io/serverlessworkflow/validation/test/WorkflowValidationTest.java
+++ b/validation/src/test/java/io/serverlessworkflow/validation/test/WorkflowValidationTest.java
@@ -15,14 +15,26 @@
*/
package io.serverlessworkflow.validation.test;
+import static io.serverlessworkflow.api.states.DefaultState.Type.OPERATION;
import static io.serverlessworkflow.api.states.DefaultState.Type.SLEEP;
import io.serverlessworkflow.api.Workflow;
+import io.serverlessworkflow.api.actions.Action;
import io.serverlessworkflow.api.end.End;
+import io.serverlessworkflow.api.events.EventDefinition;
+import io.serverlessworkflow.api.events.EventRef;
+import io.serverlessworkflow.api.functions.FunctionDefinition;
+import io.serverlessworkflow.api.functions.FunctionDefinition.Type;
+import io.serverlessworkflow.api.functions.FunctionRef;
import io.serverlessworkflow.api.interfaces.WorkflowValidator;
+import io.serverlessworkflow.api.retry.RetryDefinition;
import io.serverlessworkflow.api.start.Start;
+import io.serverlessworkflow.api.states.OperationState;
import io.serverlessworkflow.api.states.SleepState;
import io.serverlessworkflow.api.validation.ValidationError;
+import io.serverlessworkflow.api.workflow.Events;
+import io.serverlessworkflow.api.workflow.Functions;
+import io.serverlessworkflow.api.workflow.Retries;
import io.serverlessworkflow.validation.WorkflowValidatorImpl;
import java.util.Arrays;
import java.util.List;
@@ -44,9 +56,9 @@ public void testIncompleteJsonWithSchemaValidation() {
public void testIncompleteYamlWithSchemaValidation() {
WorkflowValidator workflowValidator = new WorkflowValidatorImpl();
List validationErrors =
- workflowValidator.setSource("---\n" + "id: abc\n").validate();
+ workflowValidator.setSource("---\n" + "key: abc\n").validate();
Assertions.assertNotNull(validationErrors);
- Assertions.assertEquals(3, validationErrors.size());
+ Assertions.assertEquals(4, validationErrors.size());
}
@Test
@@ -67,13 +79,10 @@ public void testFromIncompleteWorkflow() {
WorkflowValidator workflowValidator = new WorkflowValidatorImpl();
List validationErrors = workflowValidator.setWorkflow(workflow).validate();
Assertions.assertNotNull(validationErrors);
- Assertions.assertEquals(2, validationErrors.size());
-
- Assertions.assertEquals(
- "Workflow name should not be empty", validationErrors.get(0).getMessage());
+ Assertions.assertEquals(1, validationErrors.size());
Assertions.assertEquals(
"No state name found that matches the workflow start definition",
- validationErrors.get(1).getMessage());
+ validationErrors.get(0).getMessage());
}
@Test
@@ -96,6 +105,26 @@ public void testWorkflowMissingStates() {
Assertions.assertEquals("No states found", validationErrors.get(0).getMessage());
}
+ @Test
+ public void testWorkflowMissingStatesIdAndKey() {
+ WorkflowValidator workflowValidator = new WorkflowValidatorImpl();
+ List validationErrors =
+ workflowValidator
+ .setSource(
+ "{\n"
+ + "\t\"name\": \"test workflow\",\n"
+ + " \"version\": \"1.0\",\n"
+ + " \"start\": \"SomeState\",\n"
+ + " \"states\": []\n"
+ + "}")
+ .validate();
+ Assertions.assertNotNull(validationErrors);
+ Assertions.assertEquals(1, validationErrors.size());
+
+ Assertions.assertEquals(
+ "$: required property 'id' not found", validationErrors.get(0).getMessage());
+ }
+
@Test
public void testOperationStateNoFunctionRef() {
WorkflowValidator workflowValidator = new WorkflowValidatorImpl();
@@ -104,7 +133,7 @@ public void testOperationStateNoFunctionRef() {
.setSource(
"{\n"
+ "\"id\": \"checkInbox\",\n"
- + " \"name\": \"Check Inbox Workflow\",\n"
+ + "\"name\": \"Check Inbox Workflow\",\n"
+ "\"description\": \"Periodically Check Inbox\",\n"
+ "\"version\": \"1.0\",\n"
+ "\"start\": \"CheckInbox\",\n"
@@ -147,4 +176,161 @@ public void testOperationStateNoFunctionRef() {
"Operation State action functionRef does not reference an existing workflow function definition",
validationErrors.get(0).getMessage());
}
+
+ @Test
+ public void testValidateWorkflowForOptionalStartStateAndWorkflowName() {
+ Workflow workflow =
+ new Workflow()
+ .withId("test-workflow")
+ .withVersion("1.0")
+ .withStates(
+ Arrays.asList(
+ new SleepState()
+ .withName("sleepState")
+ .withType(SLEEP)
+ .withEnd(new End())
+ .withDuration("PT1M")));
+
+ WorkflowValidator workflowValidator = new WorkflowValidatorImpl();
+ List validationErrors = workflowValidator.setWorkflow(workflow).validate();
+ Assertions.assertNotNull(validationErrors);
+ Assertions.assertEquals(0, validationErrors.size());
+ }
+
+ @Test
+ public void testValidateWorkflowForOptionalIterationParam() {
+ WorkflowValidator workflowValidator = new WorkflowValidatorImpl();
+ List validationErrors =
+ workflowValidator
+ .setSource(
+ "{\n"
+ + "\"id\": \"checkInbox\",\n"
+ + " \"name\": \"Check Inbox Workflow\",\n"
+ + "\"description\": \"Periodically Check Inbox\",\n"
+ + "\"version\": \"1.0\",\n"
+ + "\"start\": \"CheckInbox\",\n"
+ + "\"functions\": [\n"
+ + "\n"
+ + "],\n"
+ + "\"states\": [\n"
+ + " {\n"
+ + " \"name\": \"CheckInbox\",\n"
+ + " \"type\": \"operation\",\n"
+ + " \"actionMode\": \"sequential\",\n"
+ + " \"actions\": [\n"
+ + " {\n"
+ + " \"functionRef\": {\n"
+ + " \"refName\": \"checkInboxFunction\"\n"
+ + " }\n"
+ + " }\n"
+ + " ],\n"
+ + " \"transition\": {\n"
+ + " \"nextState\": \"SendTextForHighPrioriry\"\n"
+ + " }\n"
+ + " },\n"
+ + " {\n"
+ + " \"name\": \"SendTextForHighPrioriry\",\n"
+ + " \"type\": \"foreach\",\n"
+ + " \"inputCollection\": \"${ .message }\",\n"
+ + " \"end\": {\n"
+ + " \"kind\": \"default\"\n"
+ + " }\n"
+ + " }\n"
+ + "]\n"
+ + "}")
+ .validate();
+
+ Assertions.assertNotNull(validationErrors);
+ Assertions.assertEquals(
+ 1,
+ validationErrors.size()); // validation error raised for functionref not for iterationParam
+ }
+
+ @Test
+ public void testMissingFunctionRefForCallbackState() {
+ WorkflowValidator workflowValidator = new WorkflowValidatorImpl();
+ List validationErrors =
+ workflowValidator
+ .setSource(
+ "{\n"
+ + " \"id\": \"callbackstatemissingfuncref\",\n"
+ + " \"version\": \"1.0\",\n"
+ + " \"specVersion\": \"0.8\",\n"
+ + " \"name\": \"Callback State Test\",\n"
+ + " \"start\": \"CheckCredit\",\n"
+ + " \"states\": [\n"
+ + " {\n"
+ + " \"name\": \"CheckCredit\",\n"
+ + " \"type\": \"callback\",\n"
+ + " \"action\": {\n"
+ + " \"functionRef\": {\n"
+ + " \"refName\": \"callCreditCheckMicroservice\",\n"
+ + " \"arguments\": {\n"
+ + " \"customer\": \"${ .customer }\"\n"
+ + " }\n"
+ + " }\n"
+ + " },\n"
+ + " \"eventRef\": \"CreditCheckCompletedEvent\",\n"
+ + " \"timeouts\": {\n"
+ + " \"stateExecTimeout\": \"PT15M\"\n"
+ + " },\n"
+ + " \"end\": true\n"
+ + " }\n"
+ + " ]\n"
+ + "}")
+ .validate();
+
+ Assertions.assertNotNull(validationErrors);
+ Assertions.assertEquals(2, validationErrors.size());
+ Assertions.assertEquals(
+ "CallbackState event ref does not reference a defined workflow event definition",
+ validationErrors.get(0).getMessage());
+ Assertions.assertEquals(
+ "CallbackState action function ref does not reference a defined workflow function definition",
+ validationErrors.get(1).getMessage());
+ }
+
+ @Test
+ void testFunctionCall() {
+ Workflow workflow =
+ new Workflow()
+ .withId("test-workflow")
+ .withVersion("1.0")
+ .withStart(new Start().withStateName("start"))
+ .withFunctions(
+ new Functions(
+ Arrays.asList(new FunctionDefinition("expression").withType(Type.EXPRESSION))))
+ .withStates(
+ Arrays.asList(
+ new OperationState()
+ .withName("start")
+ .withType(OPERATION)
+ .withActions(
+ Arrays.asList(
+ new Action().withFunctionRef(new FunctionRef("expression"))))
+ .withEnd(new End())));
+ Assertions.assertTrue(new WorkflowValidatorImpl().setWorkflow(workflow).validate().isEmpty());
+ }
+
+ @Test
+ void testEventCall() {
+ Workflow workflow =
+ new Workflow()
+ .withId("test-workflow")
+ .withVersion("1.0")
+ .withStart(new Start().withStateName("start"))
+ .withEvents(new Events(Arrays.asList(new EventDefinition().withName("event"))))
+ .withRetries(new Retries(Arrays.asList(new RetryDefinition("start", "PT1S"))))
+ .withStates(
+ Arrays.asList(
+ new OperationState()
+ .withName("start")
+ .withType(OPERATION)
+ .withActions(
+ Arrays.asList(
+ new Action()
+ .withEventRef(new EventRef().withTriggerEventRef("event"))))
+ .withEnd(new End())));
+ Assertions.assertTrue(new WorkflowValidatorImpl().setWorkflow(workflow).validate().isEmpty());
+ }
}