Skip to content
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

Action to validate a definition using parser cli #1774

Merged
merged 15 commits into from
Sep 25, 2022
Merged
Show file tree
Hide file tree
Changes from 13 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
34 changes: 34 additions & 0 deletions .github/actions/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: 'Swagger Parser CLI'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change to "Swagger Parser"

description: 'Uses swagger parser CLI to allow validation of a definition'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change to "Parses, validates, dereferences an OpenAPI definition"

inputs:
inputSpec:
description: 'file of the openapi definition'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

path to OpenAPI definition file

required: true
options:
description: 'parser options'
required: false
logsPath:
description: 'file of the logs messages or errors'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

path to validation result file

required: true
parserSpecPath:
description: 'path where a file can be found with parsed definition'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

output path of the serialized parsed definition

required: false
runs:
using: "composite"
steps:
- id: java-version
run: java -version 2>&1 | fgrep -i version | cut -d'"' -f2 | sed -e 's/^1\./1\%/' -e 's/\..*//' -e 's/%/./'
shell: bash
- name: Build Java
if: steps.java-version == null || steps.java-version < 1.8
uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: '11'
- name: Download JAR
run: curl -L "https://repository.sonatype.org/service/local/artifact/maven/content?r=central-proxy&g=io.swagger.parser.v3&a=swagger-parser-cli&e=jar&v=LATEST" -o swagger-parser-cli.jar
shell: bash
- id: execute
name: Execute Jar
run: java -jar swagger-parser-cli.jar -i ${{ inputs.inputSpec }} ${{ inputs.options }} -o ${{ inputs.parserSpecPath }} -l ${{ inputs.logsPath }}
shell: bash
21 changes: 21 additions & 0 deletions .github/workflows/validate-workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: SwaggerParserCLI Test

on: [push]

jobs:
validate_job:
runs-on: ubuntu-latest
name: A job to validate a definition
steps:
- uses: actions/checkout@v3
with:
ref: action
- uses: ./.github/actions/
with:
inputSpec: '/home/runner/work/swagger-parser/swagger-parser/modules/swagger-parser-cli/src/test/resources/fileWithNoErrorMessages.yaml'
options: '-resolve -resolveFully'
logsPath: '/home/runner/work/swagger-parser/swagger-parser/modules/swagger-parser-cli/target/test-classes/parserLogs.yaml'
parserSpecPath: '/home/runner/work/swagger-parser/swagger-parser/modules/swagger-parser-cli/target/test-classes/specParsed.yaml'



10 changes: 10 additions & 0 deletions modules/swagger-parser-cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
</dependency>
<dependency>
<groupId>net.sourceforge.argparse4j</groupId>
<artifactId>argparse4j</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.0</version>
</dependency>
</dependencies>

<properties>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,144 @@
package io.swagger.v3.parser;

import io.swagger.v3.core.util.Json;
import io.swagger.v3.core.util.Yaml;

import io.swagger.v3.parser.core.models.ParseOptions;
import io.swagger.v3.parser.core.models.SwaggerParseResult;
import net.sourceforge.argparse4j.ArgumentParsers;
import net.sourceforge.argparse4j.impl.Arguments;
import net.sourceforge.argparse4j.inf.ArgumentParser;
import net.sourceforge.argparse4j.inf.ArgumentParserException;
import net.sourceforge.argparse4j.inf.Namespace;


import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;


public class SwaggerParser {

public static final String RESOLVE = "resolve";
public static final String RESOLVEFULLY = "resolvefully";
public static final String FLATTEN = "flatten";

public static void main(String[] args) {
if (args.length > 0){
List<String> messages = readFromLocation(args[0]);
if ( messages.size() > 0){
messages.forEach(System.out::println);
ArgumentParser parser = ArgumentParsers.newFor("swagger-parser").build()
.defaultHelp(true);
parser.addArgument("-i")
.dest("i")
.required(true)
.type(String.class)
.help("input file to be parsed");
parser.addArgument("-resolve")
.dest(RESOLVE)
.type(Boolean.class)
.action(Arguments.storeTrue())
.setDefault(false)
.help("resolve remote or local references");
parser.addArgument("-resolveFully")
.dest(RESOLVEFULLY)
.type(Boolean.class)
.action(Arguments.storeTrue())
.setDefault(false)
.help("");
parser.addArgument("-flatten")
.dest(FLATTEN)
.type(Boolean.class)
.action(Arguments.storeTrue())
.setDefault(false)
.help("");
parser.addArgument("-o")
.dest("o")
.type(String.class)
.help("output file parsed");
parser.addArgument("-l")
.dest("l")
.type(String.class)
.help("output error logs");
parser.addArgument("-json")
.dest("json")
.type(String.class)
.help("generate file as JSON");
parser.addArgument("-yaml")
.dest("yaml")
.type(String.class)
.help("generate file as YAML");
try{
readFromLocation(parser.parseArgs(args));
}catch (ArgumentParserException e) {
parser.handleError(e);
System.exit(1);
}
}
}

public static List<String> readFromLocation(String location) {
private static void generateMessagesFile(List<String> messages, Namespace arguments) {
if ( messages != null && !messages.isEmpty() && arguments != null && arguments.getString("l") != null){
if(arguments.getString("l") != null) {
generateParsedFile(arguments, "l", messages.toString());
}
}
}

public static List<String> readFromLocation(Namespace args) {
List<String> messages = new ArrayList<>();
ParseOptions options;
try {
final SwaggerParseResult result = new OpenAPIV3Parser().readLocation(location, null, null);
options = setOptions(args);
final SwaggerParseResult result = new OpenAPIV3Parser().readLocation(args.get("i"), null, options);
if(args.getString("o") != null) {
if (result.getOpenAPI() != null){
String output;
if(args.getString("json") != null){
output = Json.pretty(result.getOpenAPI());
}else if(args.getString("yaml") != null){
output = Yaml.pretty(result.getOpenAPI());
}else{
output= Yaml.pretty(result.getOpenAPI());
}
generateParsedFile(args, "o", output );
}
}
if(result.getOpenAPI() == null || !result.getMessages().isEmpty()){
messages = result.getMessages();
generateMessagesFile(messages, args);
}
}catch (Exception e){
e.printStackTrace();
System.exit(1);
}
return messages;
}

private static void generateParsedFile(Namespace args, String o, String result) {
try {
if(result != null) {
OutputStream out = Files.newOutputStream(Paths.get(args.getString(o)));
byte[] specBytes = result.getBytes();
out.write(specBytes);
out.close();
}
}catch (Exception e){
e.printStackTrace();
}
}

private static ParseOptions setOptions(Namespace parseOptions) {
ParseOptions options = new ParseOptions();

if (parseOptions.getString(RESOLVE) !=null && parseOptions.getString(RESOLVE).equals("true")) {
options.setResolve(true);
}
if (parseOptions.getString(RESOLVEFULLY) != null && parseOptions.getString(RESOLVEFULLY).equals("true")) {
options.setResolveFully(true);
}
if (parseOptions.getString(FLATTEN) != null && parseOptions.getString(FLATTEN).equals("true")) {
options.setFlatten(true);
}
return options;
}
}
114 changes: 103 additions & 11 deletions modules/swagger-parser-cli/src/test/java/SwaggerParserCLITest.java
Original file line number Diff line number Diff line change
@@ -1,28 +1,120 @@
import io.swagger.v3.parser.SwaggerParser;
import net.sourceforge.argparse4j.inf.ArgumentParserException;
import net.sourceforge.argparse4j.inf.Namespace;
import org.testng.Assert;
import org.testng.annotations.Test;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SwaggerParserCLITest {
@Test
public void validateOKFromLocationTest(){
String []args = new String[1];
args[0] = "src/test/resources/fileWithNoErrorMessages.yaml";
Assert.assertTrue(SwaggerParser.readFromLocation(args[0]).size() == 0);
Map<String,Object> args = new HashMap();
args.put("i","src/test/resources/fileWithNoErrorMessages.yaml");
Namespace namespace = new Namespace(args);
Assert.assertTrue(SwaggerParser.readFromLocation(namespace).size() == 0);
}

@Test
public void validateErrorFromLocationTest(){
String []args = new String[1];
args[0] = "src/test/resources/fileWithValidationErrorMessages.yaml";
Assert.assertEquals(SwaggerParser.readFromLocation(args[0]).get(0), "attribute info.version is missing");
Assert.assertEquals(SwaggerParser.readFromLocation(args[0]).get(1), "attribute paths.'/cu'(post).responses.200.description is missing");
Map<String,Object> args = new HashMap();
args.put("i","src/test/resources/fileWithValidationErrorMessages.yaml");
Namespace namespace = new Namespace(args);
Assert.assertEquals(SwaggerParser.readFromLocation(namespace).toString(), "[attribute info.version is missing, attribute paths.'/cu'(post).responses.200.description is missing]");
}

@Test
public void validateErrorTest(){
Map<String,Object> args = new HashMap();
args.put("i","src/test/resources/fileWithValidationErrorMessages.yaml");
args.put("resolve", "true");
args.put("resolvefully", "true");
args.put("o", "target/test-classes/parsedSpec.yaml");
args.put("l", "target/test-classes/errorLogs.yaml");
Namespace namespace = new Namespace(args);
Assert.assertEquals(SwaggerParser.readFromLocation(namespace).toString(), "[attribute info.version is missing, attribute paths.'/cu'(post).responses.200.description is missing]");
}

@Test
public void validateFileNotFoundInLocationTest(){
String []args = new String[1];
args[0] = "src/test/resources/WrongLocation.yaml";
Assert.assertTrue(SwaggerParser.readFromLocation(args[0]).size() == 1);
Assert.assertEquals(SwaggerParser.readFromLocation(args[0]).get(0), "Unable to read location `src/test/resources/WrongLocation.yaml`");
Map<String,Object> args = new HashMap();
args.put("i","src/test/resources/WrongLocation.yaml");
args.put("l", "target/test-classes/errorLogs.yaml");
Namespace namespace = new Namespace(args);
List<String> messages = new ArrayList<>();
try {
messages = SwaggerParser.readFromLocation(namespace);
}catch (Exception e){
Assert.fail("error");
}
Assert.assertTrue( messages.size() == 1);
Assert.assertEquals(messages.toString(), "[Unable to read location `src/test/resources/WrongLocation.yaml`]");
}

@Test
public void validateOKFromLocationWithResolveOptionTest(){
Map<String,Object> args = new HashMap();
args.put("i","src/test/resources/internal-references-in-external-files/main.yaml");
args.put("resolve", "true");
Namespace namespace = new Namespace(args);
Assert.assertTrue(SwaggerParser.readFromLocation(namespace).size() == 0);
}

@Test
public void validateOKFromLocationWithResolveFullyOptionTestYaml(){
Map<String,Object> args = new HashMap();
args.put("i","src/test/resources/internal-references-in-external-files/main.yaml");
args.put("resolve", "true");
args.put("resolvefully", "true");
args.put("yaml", "true");
args.put("o", "target/test-classes/parsedSpec.yaml");
Namespace namespace = new Namespace(args);
Path path = Paths.get("target/test-classes/parsedSpec.yaml");

Assert.assertTrue(SwaggerParser.readFromLocation(namespace).size() == 0);
Assert.assertTrue(Files.exists(path));
}

@Test
public void validateOKFromLocationWithResolveFullyOptionTestJson(){
Map<String,Object> args = new HashMap();
args.put("i","src/test/resources/internal-references-in-external-files/main.yaml");
args.put("resolve", "true");
args.put("resolvefully", "true");
args.put("json", "true");
args.put("o", "target/test-classes/parsedSpec.json");
Namespace namespace = new Namespace(args);
Path path = Paths.get("target/test-classes/parsedSpec.json");

Assert.assertTrue(SwaggerParser.readFromLocation(namespace).size() == 0);
Assert.assertTrue(Files.exists(path));
}

@Test
public void validateOKFromLocationWithOnlyResolveFullyOptionTest(){
Map<String,Object> args = new HashMap();
args.put("i","src/test/resources/internal-references-in-external-files/main.yaml");
args.put("resolvefully","true");
Namespace namespace = new Namespace(args);
Assert.assertTrue(SwaggerParser.readFromLocation(namespace).size() == 0);
}

@Test
public void validateOKFromLocationWithFlattenOptionTest() throws ArgumentParserException {
Map<String,Object> args = new HashMap();
args.put("i","src/test/resources/internal-references-in-external-files/main.yaml");
args.put("resolve", "true");
args.put("resolvefully", "true");
args.put("flatten", "true");
args.put("o", "target/test-classes/parsedSpec.yaml");
args.put("l", "target/test-classes/errorLogs.yaml");
Namespace namespace = new Namespace(args);
Assert.assertTrue(SwaggerParser.readFromLocation(namespace).size() == 0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
common:
allOf:
- $ref: "#/core"
- type: object
properties:
attribute:
type: string
direct:
$ref: "#/core"
referenced:
type: array
items:
$ref: "#/core"

core:
type: object
properties:
coreAttribute:
type: string
inner:
$ref: "#/innerCore"

innerCore:
type: object
properties:
innerCoreAttribute:
type: string
Loading