Skip to content

Commit 796bc9c

Browse files
committed
Add options to enable/disable validation
1 parent 9b94d82 commit 796bc9c

File tree

6 files changed

+247
-42
lines changed

6 files changed

+247
-42
lines changed

src/main/java/com/apiflows/parser/OpenAPIWorkflowParser.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ public class OpenAPIWorkflowParser {
1818
private static final Logger LOGGER = LoggerFactory.getLogger(OpenAPIWorkflowParser.class);
1919

2020
public OpenAPIWorkflowParserResult parse(String location) {
21+
return parse(location, new ParseOptions());
22+
}
23+
24+
public OpenAPIWorkflowParserResult parse(String location, ParseOptions options) {
2125

2226
OpenAPIWorkflowParserResult result = new OpenAPIWorkflowParserResult();
2327

@@ -37,10 +41,16 @@ public OpenAPIWorkflowParserResult parse(String location) {
3741

3842
OpenAPIWorkflow openAPIWorkflow = mapper.readValue(content, OpenAPIWorkflow.class);
3943
openAPIWorkflow.setLocation(location);
44+
openAPIWorkflow.setContent(content);
45+
openAPIWorkflow.setFormat(getFormat(content));
4046

4147
result.setOpenAPIWorkflow(openAPIWorkflow);
4248

43-
new OpenAPIWorkflowValidator().validate(openAPIWorkflow);
49+
if(options != null && options.isApplyValidation()) {
50+
OpenAPIWorkflowValidatorResult validatorResult = new OpenAPIWorkflowValidator().validate(openAPIWorkflow);
51+
result.setValid(validatorResult.isValid());
52+
result.setErrors(validatorResult.getErrors());
53+
}
4454

4555
new OperationBinder().bind(openAPIWorkflow);
4656

@@ -54,13 +64,19 @@ public OpenAPIWorkflowParserResult parse(String location) {
5464
return result;
5565
}
5666

57-
67+
OpenAPIWorkflow.Format getFormat(String content) {
68+
if (content.trim().startsWith("{")) {
69+
return OpenAPIWorkflow.Format.JSON;
70+
} else {
71+
return OpenAPIWorkflow.Format.YAML;
72+
}
73+
}
5874

5975

6076
private ObjectMapper getObjectMapper(String content) {
6177
ObjectMapper objectMapper = null;
6278
if (content.trim().startsWith("{")) {
63-
objectMapper = new ObjectMapper();
79+
objectMapper = new ObjectMapper();
6480
} else {
6581
objectMapper = new ObjectMapper(new YAMLFactory());
6682
}

src/main/java/com/apiflows/parser/OpenAPIWorkflowParserResult.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
import com.apiflows.model.OpenAPIWorkflow;
44

5+
import java.util.List;
6+
57
public class OpenAPIWorkflowParserResult {
68

79
private boolean valid = true;
10+
private List<String> errors = null;
811
private OpenAPIWorkflow openAPIWorkflow;
912

1013
public boolean isValid() {
@@ -22,4 +25,16 @@ public OpenAPIWorkflow getOpenAPIWorkflow() {
2225
public void setOpenAPIWorkflow(OpenAPIWorkflow openAPIWorkflow) {
2326
this.openAPIWorkflow = openAPIWorkflow;
2427
}
28+
29+
public List<String> getErrors() {
30+
return errors;
31+
}
32+
33+
public void setErrors(List<String> errors) {
34+
this.errors = errors;
35+
}
36+
37+
public void addError(String error) {
38+
this.errors.add(error);
39+
}
2540
}
Lines changed: 148 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,181 @@
11
package com.apiflows.parser;
22

3-
import com.apiflows.model.Step;
4-
import com.apiflows.model.Workflow;
5-
import com.apiflows.model.OpenAPIWorkflow;
6-
import com.apiflows.model.SourceDescription;
3+
import com.apiflows.model.*;
4+
5+
import java.util.Arrays;
6+
import java.util.HashSet;
7+
import java.util.List;
8+
import java.util.regex.Pattern;
79

810
public class OpenAPIWorkflowValidator {
911

10-
public void validate(OpenAPIWorkflow openAPIWorkflow) {
12+
public OpenAPIWorkflowValidatorResult validate(OpenAPIWorkflow openAPIWorkflow) {
13+
OpenAPIWorkflowValidatorResult result = new OpenAPIWorkflowValidatorResult();
14+
1115
if (openAPIWorkflow.getWorkflowsSpec() == null || openAPIWorkflow.getWorkflowsSpec().isEmpty()) {
12-
throw new RuntimeException("'workflowsSpec' is undefined");
16+
result.addError("'workflowsSpec' is undefined");
1317
}
1418

1519
if (openAPIWorkflow.getInfo() == null) {
16-
throw new RuntimeException("'Info' is undefined");
20+
result.addError("'Info' is undefined");
1721
}
18-
if (openAPIWorkflow.getInfo().getTitle() == null || openAPIWorkflow.getInfo().getTitle().isEmpty()) {
19-
throw new RuntimeException("'Info title' is undefined");
22+
if (openAPIWorkflow.getInfo() != null && (openAPIWorkflow.getInfo().getTitle() == null || openAPIWorkflow.getInfo().getTitle().isEmpty())) {
23+
result.addError("'Info title' is undefined");
2024
}
21-
if (openAPIWorkflow.getInfo().getVersion() == null || openAPIWorkflow.getInfo().getVersion().isEmpty()) {
22-
throw new RuntimeException("'Info version' is undefined");
25+
if (openAPIWorkflow.getInfo() != null && (openAPIWorkflow.getInfo().getVersion() == null || openAPIWorkflow.getInfo().getVersion().isEmpty())) {
26+
result.addError("'Info version' is undefined");
2327
}
2428

2529
if (openAPIWorkflow.getSourceDescriptions() == null || openAPIWorkflow.getSourceDescriptions().isEmpty()) {
26-
throw new RuntimeException("'SourceDescriptions' is undefined or empty");
30+
result.addError("'SourceDescriptions' is undefined");
2731
}
2832

29-
int i = 0;
30-
for (SourceDescription sourceDescription : openAPIWorkflow.getSourceDescriptions()) {
31-
if (sourceDescription.getName() == null || sourceDescription.getName().isEmpty()) {
32-
throw new RuntimeException("'SourceDescription[" + i + "] name' is undefined");
33+
if (openAPIWorkflow.getSourceDescriptions() != null) {
34+
int i = 0;
35+
for (SourceDescription sourceDescription : openAPIWorkflow.getSourceDescriptions()) {
36+
if (sourceDescription.getName() == null || sourceDescription.getName().isEmpty()) {
37+
result.addError("'SourceDescription[" + i + "] name' is undefined");
38+
}
39+
if (sourceDescription.getUrl() == null || sourceDescription.getUrl().isEmpty()) {
40+
result.addError("'SourceDescription[" + i + "] url' is undefined");
41+
}
42+
if (sourceDescription.getType() != null) {
43+
List<String> supportedValues = Arrays.asList("openapi", "workflowsSpec");
44+
if(!supportedValues.contains(sourceDescription.getType())) {
45+
result.addError("'SourceDescription[" + i + "] type' is invalid");
46+
}
47+
}
48+
i++;
3349
}
34-
if (sourceDescription.getUrl() == null || sourceDescription.getUrl().isEmpty()) {
35-
throw new RuntimeException("'SourceDescription[" + i + "] url' is undefined");
50+
51+
if(i == 0) {
52+
result.addError("'SourceDescriptions' is empty");
3653
}
37-
i++;
3854
}
3955

4056
if (openAPIWorkflow.getWorkflows() == null || openAPIWorkflow.getWorkflows().isEmpty()) {
41-
throw new RuntimeException("'Workflows' is undefined or empty");
57+
result.addError("'Workflows' is undefined");
4258
}
4359

44-
i = 0;
45-
for(Workflow workflow : openAPIWorkflow.getWorkflows()) {
46-
if (workflow.getWorkflowId() == null || workflow.getWorkflowId().isEmpty()) {
47-
throw new RuntimeException("'Workflow[" + i + "] workflowId' is undefined");
48-
}
49-
if (workflow.getSteps() == null) {
50-
throw new RuntimeException("'Workflow Steps' is undefined");
51-
}
52-
for(Step step : workflow.getSteps()) {
53-
if(step.getStepId() == null || step.getStepId().isEmpty()) {
54-
throw new RuntimeException("'Workflow[" + workflow.getWorkflowId() + "] stepId' is undefined");
60+
if (openAPIWorkflow.getWorkflows() != null) {
61+
for (Workflow workflow : openAPIWorkflow.getWorkflows()) {
62+
int i = 0;
63+
64+
if (workflow.getWorkflowId() == null || workflow.getWorkflowId().isEmpty()) {
65+
result.addError("'Workflow[" + i + "] workflowId' is undefined");
5566
}
56-
if(step.getOperationId() == null && step.getWorkflowId() == null && step.getOperationRef() == null) {
57-
throw new RuntimeException("'Workflow[" + workflow.getWorkflowId() + "]' should provide at least one of the following: [operationId, operationRef, workflowId]");
67+
if (workflow.getSteps() == null) {
68+
result.addError("'Workflow " + workflow.getWorkflowId() + "' no Steps are undefined");
5869
}
5970

60-
int numAssignedValues = (step.getOperationId() != null ? 1 : 0) +
61-
(step.getWorkflowId() != null ? 1 : 0) +
62-
(step.getOperationRef() != null ? 1 : 0);
71+
int j = 0;
72+
HashSet<String> stepIds = new HashSet<>();
73+
for (Step step : workflow.getSteps()) {
74+
if (step.getStepId() == null || step.getStepId().isEmpty()) {
75+
result.addError("'Workflow[" + workflow.getWorkflowId() + "] stepId' is undefined");
76+
} else {
77+
if(stepIds.contains(step.getStepId())) {
78+
result.addError("'Workflow[" + workflow.getWorkflowId() + "] stepId' " + step.getStepId() + " is not unique");
79+
} else {
80+
stepIds.add(step.getStepId());
81+
}
82+
}
83+
if (!isValidStepId(step.getStepId())) {
84+
result.addError("'Workflow[" + workflow.getWorkflowId() + "] stepId' is invalid (should match regex " + getStepIdRegularExpression() + ")");
85+
}
86+
if (step.getOperationId() == null && step.getWorkflowId() == null && step.getOperationRef() == null) {
87+
result.addError("'Workflow[" + workflow.getWorkflowId() + "]' should provide at least one of the following: [operationId, operationRef, workflowId]");
88+
}
89+
90+
int numAssignedValues = (step.getOperationId() != null ? 1 : 0) +
91+
(step.getWorkflowId() != null ? 1 : 0) +
92+
(step.getOperationRef() != null ? 1 : 0);
93+
94+
if (numAssignedValues != 1) {
95+
result.addError("'Workflow[" + workflow.getWorkflowId() + "]' should provide only one of the following: [operationId, operationRef, workflowId]");
96+
}
97+
98+
if(step.getParameters() != null) {
99+
for(Parameter parameter : step.getParameters()) {
100+
if(parameter.get$ref() != null) {
101+
// Reference object
102+
// check is URI
103+
} else {
104+
// Parameter object
105+
if(parameter.getName() == null) {
106+
result.addError("'Workflow[" + workflow.getWorkflowId() + "]' parameter has no name");
107+
}
108+
if(parameter.getIn() == null) {
109+
result.addError("'Workflow[" + workflow.getWorkflowId() + "]' parameter has no type");
110+
}
111+
if(parameter.getIn() != null) {
112+
List<String> supportedValues = Arrays.asList("path", "query", "header", "cookie", "body", "workflow");
113+
if(!supportedValues.contains(parameter.getIn())) {
114+
result.addError("'Workflow[" + workflow.getWorkflowId() + "]' parameter type (" + parameter.getIn() + ") is invalid");
115+
}
116+
}
117+
if(parameter.getValue() == null) {
118+
result.addError("'Workflow[" + workflow.getWorkflowId() + "]' parameter has no value");
119+
}
120+
}
121+
}
122+
}
123+
j++;
124+
125+
if(step.getDependsOn() != null) {
126+
boolean matchingStep = false;
127+
for (Step s : workflow.getSteps()) {
128+
if(s.getStepId().equals(step.getDependsOn())) {
129+
matchingStep = true;
130+
break;
131+
}
132+
}
133+
if(!matchingStep) {
134+
result.addError("'Workflow[" + workflow.getWorkflowId() + "] stepId' " + step.getStepId() + " 'dependsOn' is invalid (no such a step exists)");
135+
}
136+
}
63137

64-
if (numAssignedValues != 1) {
65-
throw new RuntimeException("'Workflow[" + workflow.getWorkflowId() + "]' should provide only one of the following: [operationId, operationRef, workflowId]");
138+
for(SuccessAction successAction : step.getOnSuccess()) {
139+
if(successAction.getType() == null) {
140+
result.addError("'Workflow[" + workflow.getWorkflowId() + "]' step SuccessAction has no type");
141+
}
142+
if(successAction.getType() != null) {
143+
List<String> supportedValues = Arrays.asList("end", "goto");
144+
if(!supportedValues.contains(successAction.getType())) {
145+
result.addError("'Workflow[" + workflow.getWorkflowId() + "]' step SuccessAction type (" + successAction.getType() + ") is invalid");
146+
}
147+
}
148+
}
149+
}
150+
if(j == 0) {
151+
result.addError("'Workflow " + workflow.getWorkflowId() + "' no Steps are undefined");
152+
}
153+
154+
for (String key : workflow.getOutputs().keySet()) {
155+
if(!isValidOutputsKey(key)) {
156+
result.addError("Workflow[" + workflow.getWorkflowId() + "] Outputs key is invalid (should match regex " + getOutputsKeyRegularExpression() + ")");
157+
}
66158
}
67159
}
68-
i++;
69160
}
161+
162+
return result;
163+
}
164+
165+
boolean isValidStepId(String stepId) {
166+
return Pattern.matches(getStepIdRegularExpression(), stepId);
167+
}
70168

169+
String getStepIdRegularExpression() {
170+
return "[A-Za-z0-9_\\-]+";
71171
}
172+
173+
boolean isValidOutputsKey(String key) {
174+
return Pattern.matches(getOutputsKeyRegularExpression(), key);
175+
}
176+
177+
String getOutputsKeyRegularExpression() {
178+
return "^[a-zA-Z0-9\\.\\-_]+$";
179+
}
180+
72181
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.apiflows.parser;
2+
3+
4+
import java.util.ArrayList;
5+
import java.util.List;
6+
7+
public class OpenAPIWorkflowValidatorResult {
8+
9+
private boolean valid = true;
10+
private List<String> errors = new ArrayList<>();
11+
12+
public boolean isValid() {
13+
return valid;
14+
}
15+
16+
public void setValid(boolean valid) {
17+
this.valid = valid;
18+
}
19+
20+
public List<String> getErrors() {
21+
return errors;
22+
}
23+
24+
public void setErrors(List<String> errors) {
25+
this.errors = errors;
26+
}
27+
28+
public void addError(String error) {
29+
this.errors.add(error);
30+
this.valid = false;
31+
}
32+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.apiflows.parser;
2+
3+
public class ParseOptions {
4+
5+
private boolean applyValidation = true;
6+
7+
public boolean isApplyValidation() {
8+
return applyValidation;
9+
}
10+
11+
public void setApplyValidation(boolean applyValidation) {
12+
this.applyValidation = applyValidation;
13+
}
14+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.apiflows.parser;
2+
3+
import com.apiflows.model.OpenAPIWorkflow;
4+
import org.junit.jupiter.api.Test;
5+
6+
import static org.junit.jupiter.api.Assertions.*;
7+
8+
class OpenAPIWorkflowValidatorTest {
9+
10+
@Test
11+
void validate() {
12+
OpenAPIWorkflow openAPIWorkflow = new OpenAPIWorkflow();
13+
OpenAPIWorkflowValidatorResult result = new OpenAPIWorkflowValidator().validate(openAPIWorkflow);
14+
15+
assertFalse(result.isValid());
16+
assertFalse(result.getErrors().isEmpty());
17+
assertEquals("'workflowsSpec' is undefined", result.getErrors().get(0));
18+
}
19+
}

0 commit comments

Comments
 (0)