Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 44 additions & 8 deletions src/main/java/com/apiflows/parser/OpenAPIWorkflowValidator.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package com.apiflows.parser;

import com.apiflows.model.*;
import io.swagger.models.auth.In;
import com.fasterxml.jackson.core.JsonPointer;
import io.swagger.v3.oas.models.media.Schema;

import java.util.*;
import java.util.regex.Pattern;

public class OpenAPIWorkflowValidator {

private OpenAPIWorkflow openAPIWorkflow = null;
private Set<String> workflowIds = new HashSet<>();
private Map<String, Set<String>> stepIds = new HashMap<>();
Set<String> workflowIds = new HashSet<>();
Map<String, Set<String>> stepIds = new HashMap<>();
Set<Schema> components = new HashSet<>();

OpenAPIWorkflowValidator() {
}
Expand Down Expand Up @@ -179,7 +181,7 @@ List<String> validateStep(Step step, String workflowId ) {
}

if(step.getDependsOn() != null) {
if(this.stepIds.get(workflowId) == null || !this.stepIds.get(workflowId).contains(step.getDependsOn())) {
if(!stepExists(workflowId, step.getDependsOn())) {
errors.add("'Step " + stepId + " 'dependsOn' is invalid (no such a step exists)");
}

Expand All @@ -193,11 +195,11 @@ List<String> validateStep(Step step, String workflowId ) {
}

for(SuccessAction successAction: step.getOnSuccess()) {
errors.addAll(validateSuccessAction(successAction, stepId));
errors.addAll(validateSuccessAction(workflowId, stepId, successAction));
}

for(FailureAction failureAction : step.getOnFailure()) {
errors.addAll(validateFailureAction(failureAction, stepId));
errors.addAll(validateFailureAction(workflowId, stepId, failureAction));

}

Expand Down Expand Up @@ -239,7 +241,7 @@ List<String> validateParameter(Parameter parameter, String workflowId ) {
return errors;
}

List<String> validateSuccessAction(SuccessAction successAction, String stepId) {
List<String> validateSuccessAction(String workflowId, String stepId, SuccessAction successAction) {
List<String> SUPPORTED_VALUES = Arrays.asList("end", "goto");

List<String> errors = new ArrayList<>();
Expand All @@ -262,10 +264,17 @@ List<String> validateSuccessAction(SuccessAction successAction, String stepId) {
errors.add("Step " + stepId + " SuccessAction cannot define both workflowId and stepId");
}

if(successAction.getStepId() != null && successAction.getType() != null && successAction.getType().equals("goto")) {
// when type `goto` stepId must exist (if provided)
if (!stepExists(workflowId, successAction.getStepId())) {
errors.add("Step " + stepId + " SuccessAction stepId is invalid (no such a step exists)");
}
}

return errors;
}

List<String> validateFailureAction(FailureAction failureAction, String stepId) {
List<String> validateFailureAction(String workflowId, String stepId, FailureAction failureAction) {
List<String> SUPPORTED_VALUES = Arrays.asList("end", "retry", "goto");

List<String> errors = new ArrayList<>();
Expand Down Expand Up @@ -298,6 +307,15 @@ List<String> validateFailureAction(FailureAction failureAction, String stepId) {

}

if(failureAction.getStepId() != null && failureAction.getType() != null
&& (failureAction.getType().equals("goto") || failureAction.getType().equals("retry"))) {
// when type `goto` or `retry` stepId must exist (if provided)
if (!stepExists(workflowId, failureAction.getStepId())) {
errors.add("Step " + stepId + " FailureAction stepId is invalid (no such a step exists)");
}
}


return errors;
}

Expand Down Expand Up @@ -422,4 +440,22 @@ List<String> loadStepIds(List<Workflow> workflows) {
return errors;
}

boolean stepExists(String workflowId, String stepId) {
return this.stepIds.get(workflowId) != null && this.stepIds.get(workflowId).contains(stepId);
}

public boolean isValidJsonPointer(String jsonPointerString) {

boolean ret;

try {
JsonPointer jsonPointer = JsonPointer.compile(jsonPointerString);
ret = true;
} catch (IllegalArgumentException e) {
ret = false;
}

return ret;
}

}
111 changes: 97 additions & 14 deletions src/test/java/com/apiflows/parser/OpenAPIWorkflowValidatorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
import com.apiflows.model.*;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.*;

import static org.junit.jupiter.api.Assertions.*;

Expand Down Expand Up @@ -235,7 +234,9 @@ void validateParameterWithoutValue() {

@Test
void validateSuccessAction() {
String workflowId = "w1";
String stepId = "step-one";

SuccessAction successAction = new SuccessAction()
.type("end")
.stepId("step-one");
Expand All @@ -244,12 +245,14 @@ void validateSuccessAction() {
new Criterion()
.context("$statusCode == 200"));

assertEquals(0, validator.validateSuccessAction(successAction, stepId).size());
assertEquals(0, validator.validateSuccessAction(workflowId, stepId, successAction).size());
}

@Test
void validateSuccessActionInvalidType() {
String workflowId = "w1";
String stepId = "step-one";

SuccessAction successAction = new SuccessAction()
.type("invalid-type")
.stepId("step-one");
Expand All @@ -258,12 +261,14 @@ void validateSuccessActionInvalidType() {
new Criterion()
.context("$statusCode == 200"));

assertEquals(1, validator.validateSuccessAction(successAction, stepId).size());
assertEquals(1, validator.validateSuccessAction(workflowId, stepId, successAction).size());
}

@Test
void validateSuccessActionMissingEntity() {
String workflowId = "w1";
String stepId = "step-one";

SuccessAction successAction = new SuccessAction()
.type("end")
.stepId(null)
Expand All @@ -273,12 +278,14 @@ void validateSuccessActionMissingEntity() {
new Criterion()
.context("$statusCode == 200"));

assertEquals(1, validator.validateSuccessAction(successAction, stepId).size());
assertEquals(1, validator.validateSuccessAction(workflowId, stepId, successAction).size());
}

@Test
void validateSuccessActionInvalidEntity() {
String workflowId = "w1";
String stepId = "step-one";

SuccessAction successAction = new SuccessAction()
.type("end")
.stepId("step-one")
Expand All @@ -288,9 +295,33 @@ void validateSuccessActionInvalidEntity() {
new Criterion()
.condition("$statusCode == 200"));

assertEquals(1, validator.validateSuccessAction(successAction, stepId).size());
assertEquals(1, validator.validateSuccessAction(workflowId, stepId, successAction).size());
}

@Test
void validateSuccessActionInvalidStepId() {
String workflowId = "w1";

OpenAPIWorkflowValidator validator = new OpenAPIWorkflowValidator();

Map<String, Set<String>> stepIds = new HashMap<>();
stepIds.put("w1", Set.of("step-one", "step-two", "step-three"));

validator.stepIds = stepIds;

String stepId = "step-one";
SuccessAction successAction = new SuccessAction()
.type("goto")
.stepId("step-dummy");

successAction.addCriteria(
new Criterion()
.context("$statusCode == 200"));

assertEquals(1, validator.validateSuccessAction(workflowId, stepId, successAction).size());
}


@Test
void validateCriterion() {
String stepId = "step-one";
Expand Down Expand Up @@ -338,9 +369,11 @@ void validateCriterionMissingContext() {

@Test
void validateFailureAction() {
String workflowId = "w1";
String stepId = "step-one";

FailureAction failureAction = new FailureAction()
.type("retry")
.type("end")
.stepId("step-one")
.retryAfter(1000L)
.retryLimit(3);
Expand All @@ -349,12 +382,14 @@ void validateFailureAction() {
new Criterion()
.context("$statusCode == 200"));

assertEquals(0, validator.validateFailureAction(failureAction, stepId).size());
assertEquals(0, validator.validateFailureAction(workflowId, stepId, failureAction).size());
}

@Test
void validateFailureActionInvalidType() {
String workflowId = "w1";
String stepId = "step-one";

FailureAction failureAction = new FailureAction()
.type("dummy")
.stepId("step-one")
Expand All @@ -365,14 +400,16 @@ void validateFailureActionInvalidType() {
new Criterion()
.context("$statusCode == 200"));

assertEquals(1, validator.validateFailureAction(failureAction, stepId).size());
assertEquals(1, validator.validateFailureAction(workflowId, stepId, failureAction).size());
}

@Test
void validateFailureActionInvalidRetrySettings() {
String workflowId = "w1";
String stepId = "step-one";

FailureAction failureAction = new FailureAction()
.type("retry")
.type("end")
.stepId("step-one")
.retryAfter(-1000L)
.retryLimit(-3);
Expand All @@ -381,12 +418,14 @@ void validateFailureActionInvalidRetrySettings() {
new Criterion()
.context("$statusCode == 200"));

assertEquals(2, validator.validateFailureAction(failureAction, stepId).size());
assertEquals(2, validator.validateFailureAction(workflowId, stepId, failureAction).size());
}

@Test
void validateFailureActionMissingEntity() {
String workflowId = "w1";
String stepId = "step-one";

FailureAction failureAction = new FailureAction()
.type("retry")
.stepId(null)
Expand All @@ -398,14 +437,16 @@ void validateFailureActionMissingEntity() {
new Criterion()
.context("$statusCode == 200"));

assertEquals(1, validator.validateFailureAction(failureAction, stepId).size());
assertEquals(1, validator.validateFailureAction(workflowId, stepId, failureAction).size());
}

@Test
void validateFailureActionInvalidEntity() {
String workflowId = "w1";
String stepId = "step-one";

FailureAction failureAction = new FailureAction()
.type("retry")
.type("end")
.stepId("step-one")
.workflowId("workflow-test")
.retryAfter(1000L)
Expand All @@ -415,9 +456,35 @@ void validateFailureActionInvalidEntity() {
new Criterion()
.context("$statusCode == 200"));

assertEquals(1, validator.validateFailureAction(failureAction, stepId).size());
assertEquals(1, validator.validateFailureAction(workflowId, stepId, failureAction).size());
}

@Test
void validateFailureActionInvalidStepId() {
String workflowId = "w1";
String stepId = "step-one";

OpenAPIWorkflowValidator validator = new OpenAPIWorkflowValidator();

Map<String, Set<String>> stepIds = new HashMap<>();
stepIds.put("w1", Set.of("step-one", "step-two", "step-three"));

validator.stepIds = stepIds;

FailureAction failureAction = new FailureAction()
.type("retry")
.stepId("step-dummy")
.retryAfter(1000L)
.retryLimit(3);

failureAction.addCriteria(
new Criterion()
.context("$statusCode == 200"));

assertEquals(1, validator.validateFailureAction(workflowId, stepId, failureAction).size());
}


@Test
void loadWorkflowIWithDuplicateIds() {
List<Workflow> list = List.of(
Expand Down Expand Up @@ -493,4 +560,20 @@ void invalidComponentKey() {
assertFalse(new OpenAPIWorkflowValidator().isValidComponentKey("pagination order"));
}


@Test
void isValidJsonPointer() {
assertTrue(new OpenAPIWorkflowValidator().isValidJsonPointer("/user/id"));
}

@Test
void invalidJsonPointer() {
assertFalse(new OpenAPIWorkflowValidator().isValidJsonPointer("user/id"));
}

// @Test
// void isValidJsonPointer2() {
// assertTrue(new OpenAPIWorkflowValidator().isValidJsonPointer("#/petId"));
// }

}