Skip to content

Arazzo refactor components #45

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 3, 2024
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
24 changes: 24 additions & 0 deletions src/main/java/com/apiflows/model/Components.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public class Components {

private Map<String, Schema> inputs = new HashMap<>();
private Map<String, Parameter> parameters = new HashMap<>();
private Map<String, SuccessAction> successActions = new HashMap<>();
private Map<String, FailureAction> failureActions = new HashMap<>();

// Getters and setters

Expand Down Expand Up @@ -39,5 +41,27 @@ public void addInput(String key, Schema input) {
this.inputs.put(key, input);
}

@JsonProperty("successActions")
public Map<String, SuccessAction> getSuccessActions() {
return successActions;
}

public void setSuccessActions(Map<String, SuccessAction> successActions) {
this.successActions = successActions;
}

@JsonProperty("failureActions")
public Map<String, FailureAction> getFailureActions() {
return failureActions;
}

public void setFailureActions(Map<String, FailureAction> failureActions) {
this.failureActions = failureActions;
}

public Components parameter(String key, Parameter parameter) {
this.addParameter(key, parameter);
return this;
}
}

15 changes: 8 additions & 7 deletions src/main/java/com/apiflows/model/Parameter.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class Parameter {
private String name;
private String in;
private String value;
private String style;
private String reference;

@JsonProperty("name")
public String getName() {
Expand Down Expand Up @@ -36,12 +36,13 @@ public void setValue(String value) {
this.value = value;
}

public String getStyle() {
return style;
@JsonProperty("reference")
public String getReference() {
return reference;
}

public void setStyle(String style) {
this.style = style;
public void setReference(String reference) {
this.reference = reference;
}

public Parameter name(String name) {
Expand All @@ -59,8 +60,8 @@ public Parameter value(String value) {
return this;
}

public Parameter style(String style) {
this.style = style;
public Parameter reference(String reference) {
this.reference = reference;
return this;
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/apiflows/model/SourceDescription.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public boolean isOpenApi() {
return "openapi".equals(this.type);
}

public boolean isWorkflowsSpec() {
return "workflowsSpec".equals(this.type);
public boolean isArazzo() {
return "arazzo".equals(this.type);
}

public SourceDescription name(String name) {
Expand Down
63 changes: 35 additions & 28 deletions src/main/java/com/apiflows/parser/OpenAPIWorkflowValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ public class OpenAPIWorkflowValidator {
Set<String> workflowIds = new HashSet<>();
Map<String, Set<String>> stepIds = new HashMap<>();
Set<String> operationIds = new HashSet<>();
Set<Schema> components = new HashSet<>();
Set<String> componentIds = new HashSet<>();
Components components = null;

OpenAPIWorkflowValidator() {
}
Expand All @@ -31,11 +32,12 @@ public OpenAPIWorkflowValidatorResult validate() {
loadWorkflowIds(this.openAPIWorkflow);
loadStepIds(this.openAPIWorkflow.getWorkflows());
loadOperationIds(this.openAPIWorkflow);
loadComponents(this.openAPIWorkflow.getComponents());

OpenAPIWorkflowValidatorResult result = new OpenAPIWorkflowValidatorResult();

if (openAPIWorkflow.getArazzo() == null || openAPIWorkflow.getArazzo().isEmpty()) {
result.addError("'workflowsSpec' is undefined");
result.addError("'arazzo' is undefined");
}

// Info
Expand Down Expand Up @@ -85,7 +87,7 @@ List<String> validateInfo(Info info) {
}

List<String> validateSourceDescriptions(List<SourceDescription> sourceDescriptions) {
List<String> SUPPORTED_TYPES = Arrays.asList("openapi", "workflowsSpec");
List<String> SUPPORTED_TYPES = Arrays.asList("openapi", "arazzo");

List<String> errors = new ArrayList<>();

Expand Down Expand Up @@ -171,15 +173,18 @@ List<String> validateStep(Step step, String workflowId ) {

if(step.getParameters() != null) {
for(Parameter parameter : step.getParameters()) {
if(isRuntimeExpression(parameter.getName())) {
if(isRuntimeExpression(parameter.getReference())) {
// reference a reusable object
errors.addAll(validateReusableParameter(parameter, workflowId, null));
} else {
// parameter
errors.addAll(validateParameter(parameter, workflowId, null));
}
if(step.getWorkflowId() == null) {
// when the step in context is NOT a workflowId the parameter IN must be defined
if(!isRuntimeExpression(parameter.getName()) && parameter.getIn() == null) {
errors.add("'Workflow[" + workflowId + "]' parameter IN must be defined");

if(step.getWorkflowId() == null) {
// when the step in context is NOT a workflowId the parameter IN must be defined
if(!isRuntimeExpression(parameter.getName()) && parameter.getIn() == null) {
errors.add("'Workflow[" + workflowId + "]' parameter IN must be defined");
}
}
}
}
Expand Down Expand Up @@ -212,7 +217,7 @@ List<String> validateStep(Step step, String workflowId ) {
}

List<String> validateParameter(Parameter parameter, String workflowId, String componentName) {
List<String> SUPPORTED_VALUES = Arrays.asList("path", "query", "header", "cookie", "body", "workflow");
List<String> SUPPORTED_VALUES = Arrays.asList("path", "query", "header", "cookie", "body");

String source;

Expand Down Expand Up @@ -264,19 +269,18 @@ List<String> validateReusableParameter(Parameter parameter, String workflowId, S
source = "Component[" + componentName + "]";
}

List<String> errors = new ArrayList<>();

if(isRuntimeExpression(parameter.getName())) {
// Reusable Parameter object
String name = parameter.getName();

if(parameter.getIn() != null) {
errors.add(source + "parameter " + name + " in (" + parameter.getIn() + ") should not be provided for a Reusable Parameter Object");
}
// reference to reusable object
String reference = parameter.getReference();
// normalize reference
String key = reference.replace("$components.parameters.", "");

// TODO: check reusable parameter exists in Components
List<String> errors = new ArrayList<>();

// check reusable parameter exists in Components
if(!this.components.getParameters().containsKey(key)) {
errors.add(source + " parameter '" + reference + "' not found");
}

return errors;
}

Expand Down Expand Up @@ -447,14 +451,13 @@ String getOutputsKeyRegularExpression() {
List<String> loadWorkflowIds(OpenAPIWorkflow openAPIWorkflow) {
List<String> errors = new ArrayList<>();

boolean multipleWorkflowsSpec = getNumWorkflowsSpecSourceDescriptions(openAPIWorkflow.getSourceDescriptions()) > 1 ? true : false;

boolean multipleSpecs = getNumArazzoTypeSourceDescriptions(openAPIWorkflow.getSourceDescriptions()) > 1 ? true : false;

if(openAPIWorkflow.getWorkflows() != null) {
validateWorkflowIdsUniqueness(openAPIWorkflow.getWorkflows());

for (Workflow workflow : openAPIWorkflow.getWorkflows()) {
errors.addAll(validateStepsWorkflowIds(workflow.getSteps(), multipleWorkflowsSpec));
errors.addAll(validateStepsWorkflowIds(workflow.getSteps(), multipleSpecs));
}

for (Workflow workflow : openAPIWorkflow.getWorkflows()) {
Expand Down Expand Up @@ -508,6 +511,10 @@ List<String> loadOperationIds(OpenAPIWorkflow openAPIWorkflow) {
return errors;
}

void loadComponents(Components components) {
this.components = components;
}

public List<String> validateStepsOperationIds(List<Step> steps, boolean multipleOpenApiFiles) {
List<String> errors = new ArrayList<>();

Expand All @@ -528,9 +535,9 @@ int getNumOpenApiSourceDescriptions(List<SourceDescription> sourceDescriptions)
return (int) sourceDescriptions.stream().filter(p -> p.isOpenApi()).count();
}

// num of SourceDescriptions with type 'workflowsSpec'
int getNumWorkflowsSpecSourceDescriptions(List<SourceDescription> sourceDescriptions) {
return (int) sourceDescriptions.stream().filter(p -> p.isWorkflowsSpec()).count();
// num of SourceDescriptions with type 'arazzo'
int getNumArazzoTypeSourceDescriptions(List<SourceDescription> sourceDescriptions) {
return (int) sourceDescriptions.stream().filter(p -> p.isArazzo()).count();
}

boolean stepExists(String workflowId, String stepId) {
Expand All @@ -554,11 +561,11 @@ List<String> validateWorkflowIdsUniqueness(List<Workflow> workflows) {
return errors;
}

List<String> validateStepsWorkflowIds(List<Step> steps, boolean multipleWorkflowsSpecFiles) {
List<String> validateStepsWorkflowIds(List<Step> steps, boolean multipleArazzoTypeFiles) {
List<String> errors = new ArrayList<>();

for(Step step : steps) {
if(multipleWorkflowsSpecFiles) {
if(multipleArazzoTypeFiles) {
// 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.<name>.<workflowId>)");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public void isYaml() {
@Test
public void getJsonFormat() {
final String CONTENT = "{" +
"\"workflowsSpec\" : \"1.0.0\"" +
"\"arazzo\" : \"1.0.0\"" +
"}";

OpenAPIWorkflowParserResult.Format format = parser.getFormat(CONTENT);
Expand All @@ -96,7 +96,7 @@ public void getJsonFormat() {
@Test
public void getYamlFormat() {
final String CONTENT = "" +
"workflowsSpec : 1.0.0" +
"arazzo : 1.0.0" +
"info:" +
" title: simple\n";

Expand Down
42 changes: 27 additions & 15 deletions src/test/java/com/apiflows/parser/OpenAPIWorkflowValidatorTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.apiflows.parser;

import com.apiflows.model.*;
import org.checkerframework.checker.units.qual.C;
import org.junit.jupiter.api.Test;

import java.util.*;
Expand All @@ -18,7 +19,7 @@ void validate() {

assertFalse(result.isValid());
assertFalse(result.getErrors().isEmpty());
assertEquals("'workflowsSpec' is undefined", result.getErrors().get(0));
assertEquals("'arazzo' is undefined", result.getErrors().get(0));
}

@Test
Expand Down Expand Up @@ -201,22 +202,33 @@ void validateStepWithoutInAttribute() {
@Test
void validateReusableParameter() {
Parameter parameter = new Parameter()
.name("$components.parameters.page")
.reference("$components.parameters.pageSize")
.value("1");
String worklowId = "q1";

Components components = new Components()
.parameter("pageSize", parameter);

validator.loadComponents(components);

assertEquals(0, validator.validateReusableParameter(parameter, worklowId, null).size());
}

@Test
void validateReusableParameterWithInAttribute() {
void validateReusableParameterNotFound() {
Parameter parameter = new Parameter()
.name("$components.parameters.page")
.value("1")
.in("query");
.reference("$components.parameters.pageSize")
.value("1");
Parameter anotherParameter = new Parameter()
.reference("$components.parameters.pageCounter")
.value("1");
String worklowId = "q1";

// error: must not define IN attribute
Components components = new Components()
.parameter("anotherParameter", anotherParameter);

validator.loadComponents(components);

assertEquals(1, validator.validateReusableParameter(parameter, worklowId, null).size());
}

Expand Down Expand Up @@ -570,14 +582,14 @@ public void getNumOpenApiSourceDescriptions() {
.type("openapi"),
new SourceDescription()
.name("workflowspec-1")
.type("workflowsSpec")
.type("arazzo")
);

assertEquals(2, new OpenAPIWorkflowValidator().getNumOpenApiSourceDescriptions(sourceDescriptions));
}

@Test
public void getNumWorkflowsSpecSourceDescriptions() {
public void getNumArazzoTypeSourceDescriptions() {
List<SourceDescription> sourceDescriptions = List.of(
new SourceDescription()
.name("openapifile-1")
Expand All @@ -587,10 +599,10 @@ public void getNumWorkflowsSpecSourceDescriptions() {
.type("openapi"),
new SourceDescription()
.name("workflowspec-1")
.type("workflowsSpec")
.type("arazzo")
);

assertEquals(1, new OpenAPIWorkflowValidator().getNumWorkflowsSpecSourceDescriptions(sourceDescriptions));
assertEquals(1, new OpenAPIWorkflowValidator().getNumArazzoTypeSourceDescriptions(sourceDescriptions));
}

@Test
Expand Down Expand Up @@ -621,28 +633,28 @@ public void validateStepsOperationIdsWithoutRuntimeExpression() {

@Test
public void validateStepsWorkflowIds() {
boolean multipleWorkflowsSpecFiles = true;
boolean multipleArazzoTypeFiles = true;

List<Step> steps = List.of(
new Step()
.stepId("step-one")
.workflowId("w2")
);

assertEquals(1, new OpenAPIWorkflowValidator().validateStepsWorkflowIds(steps, multipleWorkflowsSpecFiles).size());
assertEquals(1, new OpenAPIWorkflowValidator().validateStepsWorkflowIds(steps, multipleArazzoTypeFiles).size());
}

@Test
public void validateStepsWorkflowIdsWithoutRuntimeExpression() {
boolean multipleWorkflowsSpecFiles = false;
boolean multipleArazzoTypeFiles = false;

List<Step> steps = List.of(
new Step()
.stepId("step-one")
.workflowId("w2")
);

assertEquals(0, new OpenAPIWorkflowValidator().validateStepsWorkflowIds(steps, multipleWorkflowsSpecFiles).size());
assertEquals(0, new OpenAPIWorkflowValidator().validateStepsWorkflowIds(steps, multipleArazzoTypeFiles).size());
}

@Test
Expand Down