From 7853e8f96051640cf301f79bb3d49f1cb27ac467 Mon Sep 17 00:00:00 2001 From: Beppe Catanese Date: Tue, 6 Feb 2024 17:44:29 +0100 Subject: [PATCH 1/4] Make is camelCase --- src/main/java/com/apiflows/model/SourceDescription.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/apiflows/model/SourceDescription.java b/src/main/java/com/apiflows/model/SourceDescription.java index ad963e5..4a769c8 100644 --- a/src/main/java/com/apiflows/model/SourceDescription.java +++ b/src/main/java/com/apiflows/model/SourceDescription.java @@ -39,7 +39,7 @@ public boolean isOpenApi() { return "openapi".equals(this.type); } - public boolean IsWorkflowsSpec() { + public boolean isWorkflowsSpec() { return "workflowsSpec".equals(this.type); } From 97ae851312af89de2ffd0fc4301859c4822686b7 Mon Sep 17 00:00:00 2001 From: Beppe Catanese Date: Tue, 6 Feb 2024 17:45:31 +0100 Subject: [PATCH 2/4] Load operationIds --- .../parser/OpenAPIWorkflowValidator.java | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/apiflows/parser/OpenAPIWorkflowValidator.java b/src/main/java/com/apiflows/parser/OpenAPIWorkflowValidator.java index 353d0e8..7b12e3b 100644 --- a/src/main/java/com/apiflows/parser/OpenAPIWorkflowValidator.java +++ b/src/main/java/com/apiflows/parser/OpenAPIWorkflowValidator.java @@ -12,6 +12,7 @@ public class OpenAPIWorkflowValidator { private OpenAPIWorkflow openAPIWorkflow = null; Set workflowIds = new HashSet<>(); Map> stepIds = new HashMap<>(); + Set operationIds = new HashSet<>(); Set components = new HashSet<>(); OpenAPIWorkflowValidator() { @@ -27,8 +28,9 @@ public OpenAPIWorkflowValidatorResult validate() { throw new RuntimeException("OpenAPIWorkflow is not provided"); } - loadWorkflowIds(this.openAPIWorkflow.getWorkflows()); + loadWorkflowIds(this.openAPIWorkflow); loadStepIds(this.openAPIWorkflow.getWorkflows()); + loadOperationIds(this.openAPIWorkflow); OpenAPIWorkflowValidatorResult result = new OpenAPIWorkflowValidatorResult(); @@ -440,6 +442,49 @@ List loadStepIds(List workflows) { return errors; } + List loadOperationIds(OpenAPIWorkflow openAPIWorkflow) { + List errors = new ArrayList<>(); + + boolean multipleOpenApiFiles = getNumOpenApiSourceDescriptions(openAPIWorkflow.getSourceDescriptions()) > 1 ? true : false; + + for(Workflow workflow : openAPIWorkflow.getWorkflows()) { + errors.addAll(validateStepsOperationIds(workflow.getSteps(), multipleOpenApiFiles)); + + for(Step step : workflow.getSteps()) { + if(step.getOperationId() != null) { + this.operationIds.add(step.getOperationId()); + } + } + } + + return errors; + } + + public List validateStepsOperationIds(List steps, boolean multipleOpenApiFiles) { + List errors = new ArrayList<>(); + + for(Step step : steps) { + if(multipleOpenApiFiles) { + // must use runtime expression to map applicable SourceDescription + if(step.getOperationId() != null && !step.getOperationId().startsWith("$sourceDescriptions.")) { + errors.add("Operation " + step.getOperationId() + " must be specified using a runtime expression (e.g., $sourceDescriptions..)"); + } + } + } + + return errors; + } + + // num of SourceDescriptions with type 'openapi' + int getNumOpenApiSourceDescriptions(List sourceDescriptions) { + return (int) sourceDescriptions.stream().filter(p -> p.isOpenApi()).count(); + } + + // num of SourceDescriptions with type 'workflowsSpec' + int getNumWorkflowsSpecSourceDescriptions(List sourceDescriptions) { + return (int) sourceDescriptions.stream().filter(p -> p.isWorkflowsSpec()).count(); + } + boolean stepExists(String workflowId, String stepId) { return this.stepIds.get(workflowId) != null && this.stepIds.get(workflowId).contains(stepId); } From f0ff5ead625f3257db150e386ad137f213608f57 Mon Sep 17 00:00:00 2001 From: Beppe Catanese Date: Tue, 6 Feb 2024 17:45:56 +0100 Subject: [PATCH 3/4] Load and validate workflowIds --- .../parser/OpenAPIWorkflowValidator.java | 59 +++++++++++++++---- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/apiflows/parser/OpenAPIWorkflowValidator.java b/src/main/java/com/apiflows/parser/OpenAPIWorkflowValidator.java index 7b12e3b..7a402bb 100644 --- a/src/main/java/com/apiflows/parser/OpenAPIWorkflowValidator.java +++ b/src/main/java/com/apiflows/parser/OpenAPIWorkflowValidator.java @@ -348,8 +348,8 @@ List validateComponents(Components components) { if (components.getParameters() != null) { for(String key : components.getParameters().keySet()) { - if(isValidComponentKey(key)) { - errors.add("'Component parameter " + key + " is invalid (should match regex " + getComponentKeyRegularExpression() + ")"); + if(!isValidComponentKey(key)) { + errors.add("'Component parameter name " + key + " is invalid (should match regex " + getComponentKeyRegularExpression() + ")"); } } @@ -358,13 +358,11 @@ List validateComponents(Components components) { } } if (components.getInputs() != null) { - for(String key : components.getInputs().keySet()) { - if(isValidComponentKey(key)) { + if(!isValidComponentKey(key)) { errors.add("'Component input " + key + " is invalid (should match regex " + getComponentKeyRegularExpression() + ")"); } } - } } @@ -402,18 +400,24 @@ String getOutputsKeyRegularExpression() { return "^[a-zA-Z0-9\\.\\-_]+$"; } - List loadWorkflowIds(List workflows) { + List loadWorkflowIds(OpenAPIWorkflow openAPIWorkflow) { List errors = new ArrayList<>(); - if(workflows != null) { - for(Workflow workflow : workflows) { - if(!this.workflowIds.add(workflow.getWorkflowId())) { - // id already exists - errors.add("WorkflowId is not unique: " + workflow.getWorkflowId()); - } + boolean multipleWorkflowsSpec = getNumWorkflowsSpecSourceDescriptions(openAPIWorkflow.getSourceDescriptions()) > 1 ? true : false; + + + if(openAPIWorkflow.getWorkflows() != null) { + validateWorkflowIdsUniqueness(openAPIWorkflow.getWorkflows()); + + for (Workflow workflow : openAPIWorkflow.getWorkflows()) { + errors.addAll(validateStepsWorkflowIds(workflow.getSteps(), multipleWorkflowsSpec)); + } + + for (Workflow workflow : openAPIWorkflow.getWorkflows()) { + this.workflowIds.add(workflow.getWorkflowId()); } - } + } return errors; } @@ -489,6 +493,35 @@ boolean stepExists(String workflowId, String stepId) { return this.stepIds.get(workflowId) != null && this.stepIds.get(workflowId).contains(stepId); } + List validateWorkflowIdsUniqueness(List workflows) { + List errors = new ArrayList<>(); + + Set ids = new HashSet<>(); + for(Workflow workflow : workflows) { + if(!ids.add(workflow.getWorkflowId())) { + // id already exists + errors.add("WorkflowId is not unique: " + workflow.getWorkflowId()); + } + } + return errors; + } + + List validateStepsWorkflowIds(List steps, boolean multipleWorkflowsSpecFiles) { + List errors = new ArrayList<>(); + + for(Step step : steps) { + if(multipleWorkflowsSpecFiles) { + // must use runtime expression to map applicable SourceDescription + if(step.getWorkflowId() != null && !step.getWorkflowId().startsWith("$sourceDescriptions.")) { + errors.add("Operation " + step.getWorkflowId() + " must be specified using a runtime expression (e.g., $sourceDescriptions..)"); + } + } + } + + return errors; + } + + public boolean isValidJsonPointer(String jsonPointerString) { boolean ret; From 42b06b01c57a62b76e944b6c046fca4b6701c0ac Mon Sep 17 00:00:00 2001 From: Beppe Catanese Date: Tue, 6 Feb 2024 17:46:01 +0100 Subject: [PATCH 4/4] Unit test --- .../parser/OpenAPIWorkflowValidatorTest.java | 103 +++++++++++++++++- 1 file changed, 101 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/apiflows/parser/OpenAPIWorkflowValidatorTest.java b/src/test/java/com/apiflows/parser/OpenAPIWorkflowValidatorTest.java index 4139391..e361953 100644 --- a/src/test/java/com/apiflows/parser/OpenAPIWorkflowValidatorTest.java +++ b/src/test/java/com/apiflows/parser/OpenAPIWorkflowValidatorTest.java @@ -486,14 +486,14 @@ void validateFailureActionInvalidStepId() { @Test - void loadWorkflowIWithDuplicateIds() { + void validateWorkflowIdsUniqueness() { List list = List.of( new Workflow() .workflowId("one"), new Workflow() .workflowId("one")); - assertEquals(1, validator.loadWorkflowIds(list).size()); + assertEquals(1, validator.validateWorkflowIdsUniqueness(list).size()); } @Test @@ -510,6 +510,20 @@ void validateComponentsParameterInvalidIn() { assertEquals(1, validator.validateParameter(parameter, worklowId).size()); } + @Test + void validateComponentsParameter() { + Parameter parameter = new Parameter() + .name("page") + .value("1") + .in("query"); + String worklowId = "q1"; + + Components components = new Components(); + components.addParameter("page", parameter); + + assertEquals(0, validator.validateParameter(parameter, worklowId).size()); + } + @Test void loadStepsWithDuplicateIds() { List list = List.of( @@ -524,6 +538,91 @@ void loadStepsWithDuplicateIds() { assertEquals(1, validator.loadStepIds(list).size()); } + @Test + public void getNumOpenApiSourceDescriptions() { + List sourceDescriptions = List.of( + new SourceDescription() + .name("openapifile-1") + .type("openapi"), + new SourceDescription() + .name("openapifile-2") + .type("openapi"), + new SourceDescription() + .name("workflowspec-1") + .type("workflowsSpec") + ); + + assertEquals(2, new OpenAPIWorkflowValidator().getNumOpenApiSourceDescriptions(sourceDescriptions)); + } + + @Test + public void getNumWorkflowsSpecSourceDescriptions() { + List sourceDescriptions = List.of( + new SourceDescription() + .name("openapifile-1") + .type("openapi"), + new SourceDescription() + .name("openapifile-2") + .type("openapi"), + new SourceDescription() + .name("workflowspec-1") + .type("workflowsSpec") + ); + + assertEquals(1, new OpenAPIWorkflowValidator().getNumWorkflowsSpecSourceDescriptions(sourceDescriptions)); + } + + @Test + public void validateStepsOperationIds() { + boolean multipleOpenApiFiles = true; + + List steps = List.of( + new Step() + .stepId("step-one") + .operationId("post-operation") + ); + + assertEquals(1, new OpenAPIWorkflowValidator().validateStepsOperationIds(steps, multipleOpenApiFiles).size()); + } + + @Test + public void validateStepsOperationIdsWithoutRuntimeExpression() { + boolean multipleOpenApiFiles = false; + + List steps = List.of( + new Step() + .stepId("step-one") + .operationId("post-operation") + ); + + assertEquals(0, new OpenAPIWorkflowValidator().validateStepsOperationIds(steps, multipleOpenApiFiles).size()); + } + + @Test + public void validateStepsWorkflowIds() { + boolean multipleWorkflowsSpecFiles = true; + + List steps = List.of( + new Step() + .stepId("step-one") + .workflowId("w2") + ); + + assertEquals(1, new OpenAPIWorkflowValidator().validateStepsWorkflowIds(steps, multipleWorkflowsSpecFiles).size()); + } + + @Test + public void validateStepsWorkflowIdsWithoutRuntimeExpression() { + boolean multipleWorkflowsSpecFiles = false; + + List steps = List.of( + new Step() + .stepId("step-one") + .workflowId("w2") + ); + + assertEquals(0, new OpenAPIWorkflowValidator().validateStepsWorkflowIds(steps, multipleWorkflowsSpecFiles).size()); + } @Test void validWorkflowId() {