Skip to content

Commit 1c27dc5

Browse files
authored
Merge pull request #120 from tsurdilo/errorsupdate
Update error handling and retries
2 parents 365fcf0 + 9991ef5 commit 1c27dc5

File tree

24 files changed

+358
-67
lines changed

24 files changed

+358
-67
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright 2020-Present The Serverless Workflow Specification Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.serverlessworkflow.api.deserializers;
17+
18+
import com.fasterxml.jackson.core.JsonParser;
19+
import com.fasterxml.jackson.databind.DeserializationContext;
20+
import com.fasterxml.jackson.databind.JsonNode;
21+
import com.fasterxml.jackson.databind.ObjectMapper;
22+
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
23+
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
24+
import io.serverlessworkflow.api.error.ErrorDefinition;
25+
import io.serverlessworkflow.api.interfaces.WorkflowPropertySource;
26+
import io.serverlessworkflow.api.utils.Utils;
27+
import io.serverlessworkflow.api.workflow.Errors;
28+
import org.json.JSONObject;
29+
import org.slf4j.Logger;
30+
import org.slf4j.LoggerFactory;
31+
32+
import java.io.IOException;
33+
import java.util.ArrayList;
34+
import java.util.List;
35+
36+
public class ErrorsDeserializer extends StdDeserializer<Errors> {
37+
38+
private static final long serialVersionUID = 510l;
39+
private static Logger logger = LoggerFactory.getLogger(ErrorsDeserializer.class);
40+
41+
@SuppressWarnings("unused")
42+
private WorkflowPropertySource context;
43+
44+
public ErrorsDeserializer() {
45+
this(Errors.class);
46+
}
47+
48+
public ErrorsDeserializer(Class<?> vc) {
49+
super(vc);
50+
}
51+
52+
public ErrorsDeserializer(WorkflowPropertySource context) {
53+
this(Errors.class);
54+
this.context = context;
55+
}
56+
57+
@Override
58+
public Errors deserialize(JsonParser jp,
59+
DeserializationContext ctxt) throws IOException {
60+
61+
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
62+
JsonNode node = jp.getCodec().readTree(jp);
63+
64+
Errors errors = new Errors();
65+
List<ErrorDefinition> errorDefinitions = new ArrayList<>();
66+
67+
if (node.isArray()) {
68+
for (final JsonNode nodeEle : node) {
69+
errorDefinitions.add(mapper.treeToValue(nodeEle, ErrorDefinition.class));
70+
}
71+
} else {
72+
String errorsFileDef = node.asText();
73+
String errorsFileSrc = Utils.getResourceFileAsString(errorsFileDef);
74+
JsonNode errorsRefNode;
75+
ObjectMapper jsonWriter = new ObjectMapper();
76+
if (errorsFileSrc != null && errorsFileSrc.trim().length() > 0) {
77+
// if its a yaml def convert to json first
78+
if (!errorsFileSrc.trim().startsWith("{")) {
79+
// convert yaml to json to validate
80+
ObjectMapper yamlReader = new ObjectMapper(new YAMLFactory());
81+
Object obj = yamlReader.readValue(errorsFileSrc, Object.class);
82+
83+
errorsRefNode = jsonWriter.readTree(new JSONObject(jsonWriter.writeValueAsString(obj)).toString());
84+
} else {
85+
errorsRefNode = jsonWriter.readTree(new JSONObject(errorsFileSrc).toString());
86+
}
87+
88+
JsonNode refErrors = errorsRefNode.get("errors");
89+
if (refErrors != null) {
90+
for (final JsonNode nodeEle : refErrors) {
91+
errorDefinitions.add(mapper.treeToValue(nodeEle, ErrorDefinition.class));
92+
}
93+
} else {
94+
logger.error("Unable to find error definitions in reference file: {}", errorsFileSrc);
95+
}
96+
97+
} else {
98+
logger.error("Unable to load errors defs reference file: {}", errorsFileSrc);
99+
}
100+
101+
}
102+
errors.setErrorDefs(errorDefinitions);
103+
return errors;
104+
105+
}
106+
}
107+

api/src/main/java/io/serverlessworkflow/api/mapper/WorkflowModule.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ private void addDefaultDeserializers() {
112112
addDeserializer(DataInputSchema.class, new DataInputSchemaDeserializer(workflowPropertySource));
113113
addDeserializer(AuthDefinition.class, new AuthDefinitionDeserializer(workflowPropertySource));
114114
addDeserializer(StateExecTimeout.class, new StateExecTimeoutDeserializer(workflowPropertySource));
115+
addDeserializer(Errors.class, new ErrorsDeserializer(workflowPropertySource));
115116
}
116117

117118
public ExtensionSerializer getExtensionSerializer() {

api/src/main/java/io/serverlessworkflow/api/serializers/WorkflowSerializer.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.fasterxml.jackson.databind.SerializerProvider;
2121
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
2222
import io.serverlessworkflow.api.Workflow;
23+
import io.serverlessworkflow.api.error.ErrorDefinition;
2324
import io.serverlessworkflow.api.events.EventDefinition;
2425
import io.serverlessworkflow.api.functions.FunctionDefinition;
2526
import io.serverlessworkflow.api.interfaces.Extension;
@@ -102,6 +103,10 @@ public void serialize(Workflow workflow,
102103
gen.writeBooleanField("keepActive", workflow.isKeepActive());
103104
}
104105

106+
if (workflow.isAutoRetries()) {
107+
gen.writeBooleanField("autoRetries", workflow.isAutoRetries());
108+
}
109+
105110
if (workflow.getMetadata() != null && !workflow.getMetadata().isEmpty()) {
106111
gen.writeObjectField("metadata",
107112
workflow.getMetadata());
@@ -140,6 +145,17 @@ public void serialize(Workflow workflow,
140145
gen.writeEndArray();
141146
}
142147

148+
if (workflow.getErrors() != null && !workflow.getErrors().getErrorDefs().isEmpty()) {
149+
gen.writeArrayFieldStart("errors");
150+
for (ErrorDefinition error : workflow.getErrors().getErrorDefs()) {
151+
gen.writeObject(error);
152+
}
153+
gen.writeEndArray();
154+
} else {
155+
gen.writeArrayFieldStart("errors");
156+
gen.writeEndArray();
157+
}
158+
143159
if (workflow.getSecrets() != null && !workflow.getSecrets().getSecretDefs().isEmpty()) {
144160
gen.writeArrayFieldStart("secrets");
145161
for (String secretDef : workflow.getSecrets().getSecretDefs()) {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2020-Present The Serverless Workflow Specification Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.serverlessworkflow.api.workflow;
17+
18+
import io.serverlessworkflow.api.error.ErrorDefinition;
19+
20+
import java.util.List;
21+
22+
public class Errors {
23+
private String refValue;
24+
private List<ErrorDefinition> errorDefs;
25+
26+
public Errors() {
27+
}
28+
29+
public Errors(List<ErrorDefinition> errorDefs) {
30+
this.errorDefs = errorDefs;
31+
}
32+
33+
public Errors(String refValue) {
34+
this.refValue = refValue;
35+
}
36+
37+
public String getRefValue() {
38+
return refValue;
39+
}
40+
41+
public void setRefValue(String refValue) {
42+
this.refValue = refValue;
43+
}
44+
45+
public List<ErrorDefinition> getErrorDefs() {
46+
return errorDefs;
47+
}
48+
49+
public void setErrorDefs(List<ErrorDefinition> errorDefs) {
50+
this.errorDefs = errorDefs;
51+
}
52+
}

api/src/main/resources/schema/actions/action.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,26 @@
2222
"sleep": {
2323
"$ref": "../sleep/sleep.json"
2424
},
25+
"retryRef": {
26+
"type": "string",
27+
"description": "References a defined workflow retry definition. If not defined the default retry policy is assumed"
28+
},
29+
"nonRetryableErrors": {
30+
"type": "array",
31+
"description": "List of unique references to defined workflow errors for which the action should not be retried. Used only when `autoRetries` is set to `true`",
32+
"minItems": 1,
33+
"items": {
34+
"type": "string"
35+
}
36+
},
37+
"retryableErrors": {
38+
"type": "array",
39+
"description": "List of unique references to defined workflow errors for which the action should be retried. Used only when `autoRetries` is set to `false`",
40+
"minItems": 1,
41+
"items": {
42+
"type": "string"
43+
}
44+
},
2545
"actionDataFilter": {
2646
"$ref": "../filters/actiondatafilter.json"
2747
}

api/src/main/resources/schema/error/error.json

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,18 @@
22
"type": "object",
33
"javaType": "io.serverlessworkflow.api.error.Error",
44
"properties": {
5-
"error": {
5+
"errorRef": {
66
"type": "string",
7-
"description": "Domain-specific error name, or '*' to indicate all possible errors",
7+
"description": "Reference to a unique workflow error definition. Used of errorRefs is not used",
88
"minLength": 1
99
},
10-
"code": {
11-
"type": "string",
12-
"description": "Error code. Can be used in addition to the name to help runtimes resolve to technical errors/exceptions. Should not be defined if error is set to '*'",
13-
"minLength": 1
14-
},
15-
"retryRef": {
16-
"type": "string",
17-
"description": "References a unique name of a retry definition.",
18-
"minLength": 1
10+
"errorRefs": {
11+
"type": "array",
12+
"description": "References one or more workflow error definitions. Used if errorRef is not used",
13+
"minItems": 1,
14+
"items": {
15+
"type": "string"
16+
}
1917
},
2018
"transition": {
2119
"$ref": "../transitions/transition.json",
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"type": "object",
3+
"javaType": "io.serverlessworkflow.api.error.ErrorDefinition",
4+
"properties": {
5+
"name": {
6+
"type": "string",
7+
"description": "Domain-specific error name",
8+
"minLength": 1
9+
},
10+
"code": {
11+
"type": "string",
12+
"description": "Error code. Can be used in addition to the name to help runtimes resolve to technical errors/exceptions. Should not be defined if error is set to '*'",
13+
"minLength": 1
14+
},
15+
"description": {
16+
"type": "string",
17+
"description": "Error description"
18+
}
19+
},
20+
"required": [
21+
"name"
22+
]
23+
}

api/src/main/resources/schema/workflow.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@
5050
"default": false,
5151
"description": "If 'true', workflow instances is not terminated when there are no active execution paths. Instance can be terminated via 'terminate end definition' or reaching defined 'execTimeout'"
5252
},
53+
"autoRetries": {
54+
"type": "boolean",
55+
"default": false,
56+
"description": "If set to true, actions should automatically be retried on unchecked errors. Default is false"
57+
},
5358
"metadata": {
5459
"$ref": "metadata/metadata.json"
5560
},
@@ -63,6 +68,11 @@
6368
"existingJavaType": "io.serverlessworkflow.api.workflow.Functions",
6469
"description": "Workflow function definitions"
6570
},
71+
"errors": {
72+
"type": "object",
73+
"existingJavaType": "io.serverlessworkflow.api.workflow.Errors",
74+
"description": "Workflow error definitions"
75+
},
6676
"retries": {
6777
"type": "object",
6878
"existingJavaType": "io.serverlessworkflow.api.workflow.Retries",

api/src/test/java/io/serverlessworkflow/api/test/MarkupToWorkflowTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,4 +700,40 @@ public void testActionsSleep(String workflowLocation) {
700700
assertEquals("${ .customer }", functionRef2.getArguments().get("applicant").asText());
701701

702702
}
703+
704+
@ParameterizedTest
705+
@ValueSource(strings = {"/features/errors.json", "/features/errors.yml"})
706+
public void testErrorsParams(String workflowLocation) {
707+
Workflow workflow = Workflow.fromSource(WorkflowTestUtils.readWorkflowFile(workflowLocation));
708+
709+
assertNotNull(workflow);
710+
assertNotNull(workflow.getId());
711+
assertNotNull(workflow.getName());
712+
assertNotNull(workflow.getStates());
713+
assertTrue(workflow.isAutoRetries());
714+
715+
assertNotNull(workflow.getStates());
716+
assertEquals(1, workflow.getStates().size());
717+
718+
assertNotNull(workflow.getErrors());
719+
assertEquals(2, workflow.getErrors().getErrorDefs().size());
720+
721+
assertTrue(workflow.getStates().get(0) instanceof OperationState);
722+
723+
OperationState operationState = (OperationState) workflow.getStates().get(0);
724+
assertNotNull(operationState.getActions());
725+
assertEquals(1, operationState.getActions().size());
726+
List<Action> actions = operationState.getActions();
727+
assertNotNull(actions.get(0).getFunctionRef());
728+
assertEquals("addPet", actions.get(0).getFunctionRef().getRefName());
729+
assertNotNull(actions.get(0).getRetryRef());
730+
assertEquals("testRetry", actions.get(0).getRetryRef());
731+
assertNotNull(actions.get(0).getNonRetryableErrors());
732+
assertEquals(2, actions.get(0).getNonRetryableErrors().size());
733+
734+
assertNotNull(operationState.getOnErrors());
735+
assertEquals(1, operationState.getOnErrors().size());
736+
assertNotNull(operationState.getOnErrors().get(0).getErrorRefs());
737+
assertEquals(2, operationState.getOnErrors().get(0).getErrorRefs().size());
738+
}
703739
}

api/src/test/resources/examples/jobmonitoring.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
],
4444
"onErrors": [
4545
{
46-
"error": "*",
46+
"errorRef": "AllErrors",
4747
"transition": "SubmitError"
4848
}
4949
],

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy