From ba4932e44d20aa2f59fba6b5c86948507b35578d Mon Sep 17 00:00:00 2001 From: Grace Karina Gonzalez Diaz Date: Sat, 2 Feb 2019 15:05:24 -0500 Subject: [PATCH] Enhance Readme Explaining parser options --- README.md | 602 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 578 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 9236b988c8..2648ba0b30 100644 --- a/README.md +++ b/README.md @@ -4,32 +4,130 @@ ![Build Master - Java 8, 11, and 14](https://github.com/swagger-api/swagger-parser/workflows/Build%20Test%20Deploy%20master/badge.svg?branch=master) +# Table of contents + + - [Overview](#overview) + - [Table of Contents](#table-of-contents) + - [Usage](#usage) + - [Adding to your project](#adding-to-your-project) + - [Prerequisites](#prerequisites) + - [Authentication](#authentication) + - [Options](#options) + - [Resolve](#1-resolve) + - [ResolveFully](#2-resolvefully) + - [Flatten](#3-flatten) + - [ResolveCombinators](#4-resolvecombinators) + - [Extensions](#extensions) + - [License](#license) + ## Overview -This is the Swagger Parser project, which reads OpenAPI definitions into current Java POJOs. It also provides a simple framework to add additional converters from different formats into the Swagger objects, making the entire toolchain available. + +This is the Swagger Parser project, which parses OpenAPI definitions in JSON or YAML format into [swagger-core](https://github.com/swagger-api/swagger-core) representation as [Java POJO](https://github.com/swagger-api/swagger-core/blob/master/modules/swagger-models/src/main/java/io/swagger/v3/oas/models/OpenAPI.java#L36), returning any validation warnings/errors. + +It also provides a simple framework to add additional converters from different formats into the Swagger objects, making the entire toolchain available. ### Usage Using the Swagger Parser is simple. Once included in your project, you can read a OpenAPI Specification from any location: ```java +import io.swagger.parser.OpenAPIParser; import io.swagger.v3.parser.OpenAPIV3Parser; +import io.swagger.v3.parser.core.models.SwaggerParseResult; import io.swagger.v3.oas.models.OpenAPI; // ... your code - // read a swagger description from the petstore + // parse a swagger description from the petstore and get the result + SwaggerParseResult result = new OpenAPIParser().readLocation("https://petstore3.swagger.io/api/v3/openapi.json", null, null); + // or from a file + // SwaggerParseResult result = new OpenAPIParser().readLocation("./path/to/openapi.yaml", null, null); - OpenAPI openAPI = new OpenAPIV3Parser().read("https://petstore3.swagger.io/api/v3/openapi.json"); + // the parsed POJO + OpenAPI openAPI = result.getOpenAPI(); + + if (result.getMessages() != null) result.getMessages().forEach(System.err::println); // validation errors and warnings + + if (openAPI != null) { + ... + } + +``` + +or from a string: +```java +import io.swagger.parser.OpenAPIParser; +import io.swagger.v3.parser.OpenAPIV3Parser; +import io.swagger.v3.parser.core.models.SwaggerParseResult; +import io.swagger.v3.oas.models.OpenAPI; + +// ... your code + + // parse a swagger description from the petstore and get the result + SwaggerParseResult result = new OpenAPIParser().readContents("https://petstore3.swagger.io/api/v3/openapi.json", null, null); + + // or from a file + // SwaggerParseResult result = new OpenAPIParser().readContents("./path/to/openapi.yaml", null, null); + + // the parsed POJO + OpenAPI openAPI = result.getOpenAPI(); + + if (result.getMessages() != null) result.getMessages().forEach(System.err::println); // validation errors and warnings + + if (openAPI != null) { + ... + } + +``` + +If you are providing a Swagger/OpenAPI 2.0 document to the parser , e.g.: + +```java +SwaggerParseResult result = new OpenAPIParser().readContents("./path/to/swagger.yaml", null, null); ``` -You can read from a file location as well: +the Swagger/OpenAPI 2.0 document will be first converted into a comparable OpenAPI 3.0 one. + +You can also directly use `OpenAPIV3Parser` which only handles OpenAPI 3.0 documents, and provides a convenience method to get directly the parsed `OpenAPI object: + ```java - OpenAPI openAPI = new OpenAPIV3Parser().read("./path/to/openapi.yaml"); +import io.swagger.v3.parser.OpenAPIV3Parser; +import io.swagger.v3.oas.models.OpenAPI; + +// ... your code + + // read a swagger description from the petstore + + OpenAPI openAPI = new OpenAPIV3Parser().read("https://petstore3.swagger.io/api/v3/openapi.json"); + +``` + +### Adding to your project +You can include this library from Sonatype OSS for SNAPSHOTS, or Maven central for releases. In your dependencies: +```xml + + io.swagger.parser.v3 + swagger-parser + 2.0.24 + ``` +#### Prerequisites +You need the following installed and available in your $PATH: + +* [Java 1.8](http://java.oracle.com) +* [Apache maven 3.x](http://maven.apache.org/) + +After cloning the project, you can build it from source with this command: + +``` +mvn package +``` + +### Authentication If your OpenAPI definition is protected, you can pass headers in the request: ```java @@ -51,7 +149,7 @@ import io.swagger.v3.parser.core.models.AuthorizationValue; ); ``` -### Dealing with self-signed SSL certificates +#### Dealing with self-signed SSL certificates If you're dealing with self-signed SSL certificates, or those signed by GoDaddy, you'll need to disable SSL Trust Manager. That's done by setting a system environment variable as such: @@ -62,7 +160,7 @@ export TRUST_ALL=true And then the Swagger Parser will _ignore_ invalid certificates. Of course this is generally a bad idea, but if you're working inside a firewall or really know what you're doing, well, there's your rope. -### Dealing with Let's Encrypt +#### Dealing with Let's Encrypt Depending on the version of Java that you use, certificates signed by the [Let's Encrypt](https://letsencrypt.org) certificate authority _may not work_ by default. If you are using any version of Java prior to 1.8u101, you most likely _must_ install an additional CA in your JVM. Also note that 1.8u101 may _not_ be sufficient on it's own. Some users have reported that certain operating systems are not accepting Let's Encrypt signed certificates. @@ -75,33 +173,489 @@ Your options include: But... this is all standard SSL configuration stuff and is well documented across the web. -### Prerequisites -You need the following installed and available in your $PATH: -* [Java 1.8](http://java.oracle.com) -* [Apache maven 3.x](http://maven.apache.org/) +### Options +Parser uses options as a way to customize the behavior while parsing: -After cloning the project, you can build it from source with this command: +#### 1. resolve: +```java +ParseOptions parseOptions = new ParseOptions(); +parseOptions.setResolve(true); +final OpenAPI openAPI = new OpenAPIV3Parser().read("a.yaml", null, parseOptions); ``` -mvn package + + +- When remote or relative references are found in the parsed document, parser will attempt to: + +1. resolve the reference in the remote or relative location +1. parse the resolved reference +1. add the resolved "component" (e.g. parameter, schema, response, etc.) to the resolved `OpenAPI` POJO components section +1. replace the remote/relative reference with a local reference, e.g. : `#/components/schemas/NameOfRemoteSchema`. + +This applies to schemas, parameters, responses, pretty much everything containing a ref. + +#### 2. resolveFully: + +```java +ParseOptions parseOptions = new ParseOptions(); +parseOptions.setResolve(true); // implicit +parseOptions.setResolveFully(true); +final OpenAPI openAPI = new OpenAPIV3Parser().read("a.yaml", null, parseOptions); ``` -### Extensions -This project has a core artifact--`swagger-parser`, which uses Java Service Provider Inteface (SPI) so additional extensions can be added. +- In some scenarios, after references are resolved (with `resolve`, see above), you might need to have all local references removed replacing the reference with the content of the referenced element. This is for example used in [Swagger Inflector](https://github.com/swagger-api/swagger-inflector). Be aware that the result could be more heavy/long due to duplication + +Original document: -To build your own extension, you simply need to create a `src/main/resources/META-INF/services/io.swagger.parser.SwaggerParserExtension` file with the full classname of your implementation. Your class must also implement the `io.swagger.parser.SwaggerParserExtension` interface. Then, including your library with the `swagger-parser` module will cause it to be triggered automatically. +`a.yaml` +``` +openapi: 3.0.1 +paths: + "/newPerson": + post: + summary: Create new person + description: Create new person + responses: + '200': + description: ok + content: + "*/*": + schema: + "$ref": "./ref-without-component/b.yaml#/components/schemas/CustomerType" +``` +`b.yaml` +``` +openapi: 3.0.1 +components: + schemas: + CustomerType: + type: string + example: Example value +``` -### Adding to your project -You can include this library from Sonatype OSS for SNAPSHOTS, or Maven central for releases. In your dependencies: +Serialized result after parsing with option `resolveFully(true)` + +`a.yaml` +``` +openapi: 3.0.1 +servers: +- url: / +paths: + /newPerson: + post: + summary: Create new person + description: Create new person + responses: + 200: + description: ok + content: + '*/*': + schema: + type: string + example: Example value +components: + schemas: + CustomerType: + type: string + example: Example value +``` + +#### 3. flatten : + +```java +ParseOptions parseOptions = new ParseOptions(); +parseOptions.setFlatten(true); +final OpenAPI openAPI = new OpenAPIV3Parser().read("a.yaml", null, parseOptions); +``` + + +This is kind of the opposite of resolveFully, limited to defined schemas. + +In some scenarios, you might need to have all schemas defined inline (e.g. a response schema) moved to the `components/schemas` section and replaced with a reference to the newly added schema within `components/schemas`. This is for example used in [Swagger Codegen](https://github.com/swagger-api/swagger-codegen). + +Original document: + +`flatten.yaml` + +``` +openapi: 3.0.0 +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +paths: + /pets: + get: + summary: List all pets + operationId: listPets + responses: + '200': + description: An paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string +``` + +Serialized result after parsing with option `flatten(true)` -```xml - - io.swagger.parser.v3 - swagger-parser - 2.0.24 - ``` +openapi: 3.0.0 +info: + title: Swagger Petstore + license: + name: MIT + version: 1.0.0 +servers: +- url: / +paths: + /pets: + get: + tags: + - pets + summary: List all pets + responses: + 200: + description: An paged array of pets + headers: + x-next: + description: A link to the next page of responses + style: simple + explode: false + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/inline_response_200' +components: + schemas: + inline_response_200: + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string +``` + +#### 4. resolveCombinators: + +```java +ParseOptions parseOptions = new ParseOptions(); +parseOptions.setResolve(true); // implicit +parseOptions.setResolveFully(true); +parseOptions.setResolveCombinators(false); // default is true +final OpenAPI openAPI = new OpenAPIV3Parser().read("a.yaml", null, parseOptions); +``` + +This option (only available with `resolveFully = true`) allows to customize behaviour related to `allOf/anyOf/oneOf` (composed schemas) processing. With option set to `true` (default), composed schemas are transformed into "non composed" ones, by having all properties merged into a single resulting schema (see example below). +If option is set to `false`, the resulting schema will instead maintain its "composed" nature, keeping properties within e.g. the `allOf` members. + +Please see examples below: + +**Unresolved yaml** + +``` +openapi: 3.0.1 +servers: +- url: http://petstore.swagger.io/api + +info: + description: 'This is a sample server Petstore' + version: 1.0.0 + title: testing source file + termsOfService: http://swagger.io/terms/ + +paths: + "/withInvalidComposedModel": + post: + operationId: withInvalidComposedModel + requestBody: + content: + "application/json": + schema: + "$ref": "#/components/schemas/ExtendedAddress" + required: false + responses: + '200': + description: success! +components: + schemas: + ExtendedAddress: + type: object + allOf: + - $ref: '#/components/schemas/Address' + - type: object + required: + - gps + properties: + gps: + type: string + Address: + required: + - street + type: object + properties: + street: + type: string + example: 12345 El Monte Road + city: + type: string + example: Los Altos Hills + state: + type: string + example: CA + zip: + type: string + example: '94022' +``` + +**resolvedCombinator = true (default) - Test case** + +``` +@Test + public void resolveAllOfWithoutAggregatingParameters(@Injectable final List auths) { + ParseOptions options = new ParseOptions(); + options.setResolveFully(true); + options.setResolveCombinators(true); + + // Testing components/schemas + OpenAPI openAPI = new OpenAPIV3Parser().readLocation("src/test/resources/composed.yaml",auths,options).getOpenAPI(); + + ComposedSchema allOf = (ComposedSchema) openAPI.getComponents().getSchemas().get("ExtendedAddress"); + assertEquals(allOf.getAllOf().size(), 2); + + assertTrue(allOf.getAllOf().get(0).get$ref() != null); + assertTrue(allOf.getAllOf().get(1).getProperties().containsKey("gps")); + + + // Testing path item + ObjectSchema schema = (ObjectSchema) openAPI.getPaths().get("/withInvalidComposedModel").getPost().getRequestBody().getContent().get("application/json").getSchema(); + + assertEquals(schema.getProperties().size(), 5); + assertTrue(schema.getProperties().containsKey("street")); + assertTrue(schema.getProperties().containsKey("gps")); + + } +``` + +**resolvedCombinator = true (default) - Resolved Yaml** + +``` +openapi: 3.0.1 +info: + title: testing source file + description: This is a sample server Petstore + termsOfService: http://swagger.io/terms/ + version: 1.0.0 +servers: +- url: http://petstore.swagger.io/api +paths: + /withInvalidComposedModel: + post: + operationId: withInvalidComposedModel + requestBody: + content: + application/json: + schema: + required: + - gps + - street + type: object + properties: + street: + type: string + example: 12345 El Monte Road + city: + type: string + example: Los Altos Hills + state: + type: string + example: CA + zip: + type: string + example: "94022" + gps: + type: string + required: false + responses: + 200: + description: success! +components: + schemas: + ExtendedAddress: + type: object + allOf: + - $ref: '#/components/schemas/Address' + - required: + - gps + type: object + properties: + gps: + type: string + Address: + required: + - street + type: object + properties: + street: + type: string + example: 12345 El Monte Road + city: + type: string + example: Los Altos Hills + state: + type: string + example: CA + zip: + type: string + example: "94022" + ``` + + **resolvedCombinator = false - Test case** + + ``` + @Test + public void resolveAllOfWithoutAggregatingParameters(@Injectable final List auths) { + ParseOptions options = new ParseOptions(); + options.setResolveFully(true); + options.setResolveCombinators(false); + + // Testing components/schemas + OpenAPI openAPI = new OpenAPIV3Parser().readLocation("src/test/resources/composed.yaml",auths,options).getOpenAPI(); + + ComposedSchema allOf = (ComposedSchema) openAPI.getComponents().getSchemas().get("ExtendedAddress"); + assertEquals(allOf.getAllOf().size(), 2); + assertTrue(allOf.getAllOf().get(0).getProperties().containsKey("street")); + assertTrue(allOf.getAllOf().get(1).getProperties().containsKey("gps")); + + // Testing path item + ComposedSchema schema = (ComposedSchema) openAPI.getPaths().get("/withInvalidComposedModel").getPost().getRequestBody().getContent().get("application/json").getSchema(); + // In fact the schema resolved previously is the same of /withInvalidComposedModel + assertEquals(schema, allOf); + assertEquals(schema.getAllOf().size(), 2); + assertTrue(schema.getAllOf().get(0).getProperties().containsKey("street")); + assertTrue(schema.getAllOf().get(1).getProperties().containsKey("gps")); + + } + ``` + + **resolvedCombinator = false - Resolved Yaml** + + ``` +openapi: 3.0.1 +info: + title: testing source file + description: This is a sample server Petstore + termsOfService: http://swagger.io/terms/ + version: 1.0.0 +servers: +- url: http://petstore.swagger.io/api +paths: + /withInvalidComposedModel: + post: + operationId: withInvalidComposedModel + requestBody: + content: + application/json: + schema: + type: object + allOf: + - required: + - street + type: object + properties: + street: + type: string + example: 12345 El Monte Road + city: + type: string + example: Los Altos Hills + state: + type: string + example: CA + zip: + type: string + example: "94022" + - required: + - gps + type: object + properties: + gps: + type: string + required: false + responses: + 200: + description: success! +components: + schemas: + ExtendedAddress: + type: object + allOf: + - required: + - street + type: object + properties: + street: + type: string + example: 12345 El Monte Road + city: + type: string + example: Los Altos Hills + state: + type: string + example: CA + zip: + type: string + example: "94022" + - required: + - gps + type: object + properties: + gps: + type: string + Address: + required: + - street + type: object + properties: + street: + type: string + example: 12345 El Monte Road + city: + type: string + example: Los Altos Hills + state: + type: string + example: CA + zip: + type: string + example: "94022" +``` + +### Extensions +This project has a core artifact--`swagger-parser`, which uses Java Service Provider Inteface (SPI) so additional extensions can be added. + +To build your own extension, you simply need to create a `src/main/resources/META-INF/services/io.swagger.parser.SwaggerParserExtension` file with the full classname of your implementation. Your class must also implement the `io.swagger.parser.SwaggerParserExtension` interface. Then, including your library with the `swagger-parser` module will cause it to be triggered automatically. ## Security contact