diff --git a/.github/actions/action.yml b/.github/actions/action.yml new file mode 100644 index 0000000000..ae316464d3 --- /dev/null +++ b/.github/actions/action.yml @@ -0,0 +1,40 @@ +name: 'Swagger Parser' +description: 'Parses, validates, dereferences an OpenAPI definition' +inputs: + inputSpec: + description: 'path to OpenAPI definition file' + required: true + options: + description: 'parser options' + required: false + serializationType: + description: 'result file serialization type' + required: false + logsPath: + description: 'path to validation result file' + required: false + parserSpecPath: + description: 'output path of the serialized parsed definition' + required: false + parserVersion: + description: 'parser version' + 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=${{ inputs.parserVersion }}' -o swagger-parser-cli.jar + shell: bash + - id: execute + name: Execute Jar + run: java -jar swagger-parser-cli.jar -i ${{ inputs.inputSpec }} ${{ inputs.options }} ${{ inputs.serializationType }} -o ${{ inputs.parserSpecPath }} -l ${{ inputs.logsPath }} + shell: bash \ No newline at end of file diff --git a/.github/workflows/validate-workflow.yml b/.github/workflows/validate-workflow.yml new file mode 100644 index 0000000000..8c35c43e3a --- /dev/null +++ b/.github/workflows/validate-workflow.yml @@ -0,0 +1,25 @@ +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' + parserVersion: '2.1.2' + serializationType: '-json' + 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.json' + - run: cat /home/runner/work/swagger-parser/swagger-parser/modules/swagger-parser-cli/target/test-classes/specParsed.json + shell: bash + + + diff --git a/modules/swagger-parser-cli/pom.xml b/modules/swagger-parser-cli/pom.xml index 5ba74616ca..00f993ae61 100644 --- a/modules/swagger-parser-cli/pom.xml +++ b/modules/swagger-parser-cli/pom.xml @@ -76,6 +76,16 @@ org.testng testng + + net.sourceforge.argparse4j + argparse4j + 0.9.0 + + + org.slf4j + slf4j-simple + 2.0.0 + diff --git a/modules/swagger-parser-cli/src/main/java/io/swagger/v3/parser/SwaggerParser.java b/modules/swagger-parser-cli/src/main/java/io/swagger/v3/parser/SwaggerParser.java index 8fa2ec402a..904a31362c 100644 --- a/modules/swagger-parser-cli/src/main/java/io/swagger/v3/parser/SwaggerParser.java +++ b/modules/swagger-parser-cli/src/main/java/io/swagger/v3/parser/SwaggerParser.java @@ -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 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 readFromLocation(String location) { + private static void generateMessagesFile(List 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 readFromLocation(Namespace args) { List 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; + } } \ No newline at end of file diff --git a/modules/swagger-parser-cli/src/test/java/SwaggerParserCLITest.java b/modules/swagger-parser-cli/src/test/java/SwaggerParserCLITest.java index fa68bd9568..064d2010d1 100644 --- a/modules/swagger-parser-cli/src/test/java/SwaggerParserCLITest.java +++ b/modules/swagger-parser-cli/src/test/java/SwaggerParserCLITest.java @@ -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 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 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 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 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 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 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 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 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 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 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); } } \ No newline at end of file diff --git a/modules/swagger-parser-cli/src/test/resources/internal-references-in-external-files/external.yaml b/modules/swagger-parser-cli/src/test/resources/internal-references-in-external-files/external.yaml new file mode 100644 index 0000000000..361c56fffd --- /dev/null +++ b/modules/swagger-parser-cli/src/test/resources/internal-references-in-external-files/external.yaml @@ -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 diff --git a/modules/swagger-parser-cli/src/test/resources/internal-references-in-external-files/main.yaml b/modules/swagger-parser-cli/src/test/resources/internal-references-in-external-files/main.yaml new file mode 100644 index 0000000000..95e501c614 --- /dev/null +++ b/modules/swagger-parser-cli/src/test/resources/internal-references-in-external-files/main.yaml @@ -0,0 +1,29 @@ +openapi: 3.0.0 +info: + version: 0.0.1 + title: API +paths: + /: + post: + summary: Create + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/create' + responses: + '200': + description: Successful response +components: + schemas: + create: + type: object + properties: + attribute: + type: string + direct: + $ref: "./external.yaml#/common" + referenced: + type: array + items: + $ref: "./external.yaml#/common"