Open
Description
Unless there is a better or already existing home for a ready-to-use out-of-the-box cli for converting v2 to v3 openapi specs, I think changing the logic in io.swagger.v3.parser.SwaggerParser#readFromLocation
in modules/swagger-parser-cli/src/main/java/io/swagger/v3/parser/SwaggerParser.java
, which currently uses this pattern:
final SwaggerParseResult result = new OpenAPIV3Parser().readLocation(args.get(INPUT_FILE), null, options);
to use the pattern from io.swagger.parser.OpenAPIParser#readLocation
(in modules/swagger-parser/src/main/java/io/swagger/parser/OpenAPIParser.java
):
for(SwaggerParserExtension extension : OpenAPIV3Parser.getExtensions()) {
output = extension.readLocation(url, auth, options);
if(output != null && output.getOpenAPI() != null) {
return output;
}
}
should do the trick.
it would also require adding the v2-converter module as a dependency which includes this class:
//io.swagger.v3.parser.converter.SwaggerConverter
public class SwaggerConverter implements SwaggerParserExtension {
// ...
}
preview of implementation:
diff --git a/modules/swagger-parser-cli/pom.xml b/modules/swagger-parser-cli/pom.xml
index 942c53ee..0e66e8c6 100644
--- a/modules/swagger-parser-cli/pom.xml
+++ b/modules/swagger-parser-cli/pom.xml
@@ -88,6 +88,16 @@
</build>
<dependencies>
+ <dependency>
+ <groupId>io.swagger.parser.v3</groupId>
+ <artifactId>swagger-parser</artifactId>
+ <version>2.1.24</version>
+ </dependency>
+ <dependency>
+ <groupId>io.swagger.parser.v3</groupId>
+ <artifactId>swagger-parser-v2-converter</artifactId>
+ <version>2.1.24</version>
+ </dependency>
<dependency>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-v3</artifactId>
@@ -109,7 +119,7 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j-version}</version>
- <scope>test</scope>
+ <!--<scope>test</scope>-->
</dependency>
<dependency>
<groupId>org.testng</groupId>
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 4eb9a4a1..aa973a0d 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,5 +1,6 @@
package io.swagger.v3.parser;
+import io.swagger.parser.OpenAPIParser;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.core.util.Yaml;
@@ -100,7 +101,20 @@ public class SwaggerParser {
ParseOptions options;
try {
options = setOptions(args);
- final SwaggerParseResult result = new OpenAPIV3Parser().readLocation(args.get(INPUT_FILE), null, options);
+ SwaggerParseResult result;
+
+ // new code for parsing the result trying all the extension versions
+ try {
+ result = new OpenAPIParser().readLocation(args.get(INPUT_FILE), null, options);
+ } catch (Exception e) {
+ result = SwaggerParseResult.ofError(e.getMessage());
+ }
+
+ // in case of error, provide familiar error message
+ if (result.getOpenAPI() == null) {
+ result = new OpenAPIV3Parser().readLocation(args.get(INPUT_FILE), null, options);
+ }
+
if(args.getString(OUTPUT_FILE) != null) {
if (result.getOpenAPI() != null){
String output;
@@ -151,4 +165,4 @@ public class SwaggerParser {
}
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 623c79d1..faa414e2 100644
--- a/modules/swagger-parser-cli/src/test/java/SwaggerParserCLITest.java
+++ b/modules/swagger-parser-cli/src/test/java/SwaggerParserCLITest.java
@@ -145,4 +145,18 @@ public class SwaggerParserCLITest {
SwaggerParser.main(args);
Assert.assertTrue(Files.exists(path));
}
-}
\ No newline at end of file
+
+ @Test
+ public void validateOKWithSwaggerFormat(){
+ String[] args = new String[5];
+ args[0]="-i=src/test/resources/fileWithSwagger.yaml";
+ args[1]="-resolve";
+ args[2]="-resolveFully";
+ args[3]="-json";
+ args[4]="-o=target/test-classes/fileWithSwagger.json";
+
+ Path path = Paths.get(args[4].substring(3));
+ SwaggerParser.main(args);
+ Assert.assertTrue(Files.exists(path));
+ }
+}
diff --git a/modules/swagger-parser-cli/src/test/resources/fileWithSwagger.yaml b/modules/swagger-parser-cli/src/test/resources/fileWithSwagger.yaml
new file mode 100644
index 00000000..5eab5e79
--- /dev/null
+++ b/modules/swagger-parser-cli/src/test/resources/fileWithSwagger.yaml
@@ -0,0 +1,711 @@
+"swagger": "2.0"
+"info":
+ "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters."
+ "version": "1.0.7"
+ "title": "Swagger Petstore"
+ "termsOfService": "http://swagger.io/terms/"
+ "contact":
+ "email": "[email protected]"
+ "license":
+ "name": "Apache 2.0"
+ "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+"host": "petstore.swagger.io"
+"basePath": "/v2"
+"tags":
+ - "name": "pet"
+ "description": "Everything about your Pets"
+ "externalDocs":
+ "description": "Find out more"
+ "url": "http://swagger.io"
+ - "name": "store"
+ "description": "Access to Petstore orders"
+ - "name": "user"
+ "description": "Operations about user"
+ "externalDocs":
+ "description": "Find out more about our store"
+ "url": "http://swagger.io"
+"schemes":
+ - "https"
+ - "http"
+"paths":
+ "/pet/{petId}/uploadImage":
+ "post":
+ "tags":
+ - "pet"
+ "summary": "uploads an image"
+ "description": ""
+ "operationId": "uploadFile"
+ "consumes":
+ - "multipart/form-data"
+ "produces":
+ - "application/json"
+ "parameters":
+ - "name": "petId"
+ "in": "path"
+ "description": "ID of pet to update"
+ "required": true
+ "type": "integer"
+ "format": "int64"
+ - "name": "additionalMetadata"
+ "in": "formData"
+ "description": "Additional data to pass to server"
+ "required": false
+ "type": "string"
+ - "name": "file"
+ "in": "formData"
+ "description": "file to upload"
+ "required": false
+ "type": "file"
+ "responses":
+ "200":
+ "description": "successful operation"
+ "schema":
+ "$ref": "#/definitions/ApiResponse"
+ "security":
+ - "petstore_auth":
+ - "write:pets"
+ - "read:pets"
+ "/pet":
+ "post":
+ "tags":
+ - "pet"
+ "summary": "Add a new pet to the store"
+ "description": ""
+ "operationId": "addPet"
+ "consumes":
+ - "application/json"
+ - "application/xml"
+ "produces":
+ - "application/json"
+ - "application/xml"
+ "parameters":
+ - "in": "body"
+ "name": "body"
+ "description": "Pet object that needs to be added to the store"
+ "required": true
+ "schema":
+ "$ref": "#/definitions/Pet"
+ "responses":
+ "405":
+ "description": "Invalid input"
+ "security":
+ - "petstore_auth":
+ - "write:pets"
+ - "read:pets"
+ "put":
+ "tags":
+ - "pet"
+ "summary": "Update an existing pet"
+ "description": ""
+ "operationId": "updatePet"
+ "consumes":
+ - "application/json"
+ - "application/xml"
+ "produces":
+ - "application/json"
+ - "application/xml"
+ "parameters":
+ - "in": "body"
+ "name": "body"
+ "description": "Pet object that needs to be added to the store"
+ "required": true
+ "schema":
+ "$ref": "#/definitions/Pet"
+ "responses":
+ "400":
+ "description": "Invalid ID supplied"
+ "404":
+ "description": "Pet not found"
+ "405":
+ "description": "Validation exception"
+ "security":
+ - "petstore_auth":
+ - "write:pets"
+ - "read:pets"
+ "/pet/findByStatus":
+ "get":
+ "tags":
+ - "pet"
+ "summary": "Finds Pets by status"
+ "description": "Multiple status values can be provided with comma separated strings"
+ "operationId": "findPetsByStatus"
+ "produces":
+ - "application/json"
+ - "application/xml"
+ "parameters":
+ - "name": "status"
+ "in": "query"
+ "description": "Status values that need to be considered for filter"
+ "required": true
+ "type": "array"
+ "items":
+ "type": "string"
+ "enum":
+ - "available"
+ - "pending"
+ - "sold"
+ "default": "available"
+ "collectionFormat": "multi"
+ "responses":
+ "200":
+ "description": "successful operation"
+ "schema":
+ "type": "array"
+ "items":
+ "$ref": "#/definitions/Pet"
+ "400":
+ "description": "Invalid status value"
+ "security":
+ - "petstore_auth":
+ - "write:pets"
+ - "read:pets"
+ "/pet/findByTags":
+ "get":
+ "tags":
+ - "pet"
+ "summary": "Finds Pets by tags"
+ "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing."
+ "operationId": "findPetsByTags"
+ "produces":
+ - "application/json"
+ - "application/xml"
+ "parameters":
+ - "name": "tags"
+ "in": "query"
+ "description": "Tags to filter by"
+ "required": true
+ "type": "array"
+ "items":
+ "type": "string"
+ "collectionFormat": "multi"
+ "responses":
+ "200":
+ "description": "successful operation"
+ "schema":
+ "type": "array"
+ "items":
+ "$ref": "#/definitions/Pet"
+ "400":
+ "description": "Invalid tag value"
+ "security":
+ - "petstore_auth":
+ - "write:pets"
+ - "read:pets"
+ "deprecated": true
+ "/pet/{petId}":
+ "get":
+ "tags":
+ - "pet"
+ "summary": "Find pet by ID"
+ "description": "Returns a single pet"
+ "operationId": "getPetById"
+ "produces":
+ - "application/json"
+ - "application/xml"
+ "parameters":
+ - "name": "petId"
+ "in": "path"
+ "description": "ID of pet to return"
+ "required": true
+ "type": "integer"
+ "format": "int64"
+ "responses":
+ "200":
+ "description": "successful operation"
+ "schema":
+ "$ref": "#/definitions/Pet"
+ "400":
+ "description": "Invalid ID supplied"
+ "404":
+ "description": "Pet not found"
+ "security":
+ - "api_key": [ ]
+ "post":
+ "tags":
+ - "pet"
+ "summary": "Updates a pet in the store with form data"
+ "description": ""
+ "operationId": "updatePetWithForm"
+ "consumes":
+ - "application/x-www-form-urlencoded"
+ "produces":
+ - "application/json"
+ - "application/xml"
+ "parameters":
+ - "name": "petId"
+ "in": "path"
+ "description": "ID of pet that needs to be updated"
+ "required": true
+ "type": "integer"
+ "format": "int64"
+ - "name": "name"
+ "in": "formData"
+ "description": "Updated name of the pet"
+ "required": false
+ "type": "string"
+ - "name": "status"
+ "in": "formData"
+ "description": "Updated status of the pet"
+ "required": false
+ "type": "string"
+ "responses":
+ "405":
+ "description": "Invalid input"
+ "security":
+ - "petstore_auth":
+ - "write:pets"
+ - "read:pets"
+ "delete":
+ "tags":
+ - "pet"
+ "summary": "Deletes a pet"
+ "description": ""
+ "operationId": "deletePet"
+ "produces":
+ - "application/json"
+ - "application/xml"
+ "parameters":
+ - "name": "api_key"
+ "in": "header"
+ "required": false
+ "type": "string"
+ - "name": "petId"
+ "in": "path"
+ "description": "Pet id to delete"
+ "required": true
+ "type": "integer"
+ "format": "int64"
+ "responses":
+ "400":
+ "description": "Invalid ID supplied"
+ "404":
+ "description": "Pet not found"
+ "security":
+ - "petstore_auth":
+ - "write:pets"
+ - "read:pets"
+ "/store/inventory":
+ "get":
+ "tags":
+ - "store"
+ "summary": "Returns pet inventories by status"
+ "description": "Returns a map of status codes to quantities"
+ "operationId": "getInventory"
+ "produces":
+ - "application/json"
+ "parameters": [ ]
+ "responses":
+ "200":
+ "description": "successful operation"
+ "schema":
+ "type": "object"
+ "additionalProperties":
+ "type": "integer"
+ "format": "int32"
+ "security":
+ - "api_key": [ ]
+ "/store/order":
+ "post":
+ "tags":
+ - "store"
+ "summary": "Place an order for a pet"
+ "description": ""
+ "operationId": "placeOrder"
+ "consumes":
+ - "application/json"
+ "produces":
+ - "application/json"
+ - "application/xml"
+ "parameters":
+ - "in": "body"
+ "name": "body"
+ "description": "order placed for purchasing the pet"
+ "required": true
+ "schema":
+ "$ref": "#/definitions/Order"
+ "responses":
+ "200":
+ "description": "successful operation"
+ "schema":
+ "$ref": "#/definitions/Order"
+ "400":
+ "description": "Invalid Order"
+ "/store/order/{orderId}":
+ "get":
+ "tags":
+ - "store"
+ "summary": "Find purchase order by ID"
+ "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions"
+ "operationId": "getOrderById"
+ "produces":
+ - "application/json"
+ - "application/xml"
+ "parameters":
+ - "name": "orderId"
+ "in": "path"
+ "description": "ID of pet that needs to be fetched"
+ "required": true
+ "type": "integer"
+ "maximum": 10
+ "minimum": 1
+ "format": "int64"
+ "responses":
+ "200":
+ "description": "successful operation"
+ "schema":
+ "$ref": "#/definitions/Order"
+ "400":
+ "description": "Invalid ID supplied"
+ "404":
+ "description": "Order not found"
+ "delete":
+ "tags":
+ - "store"
+ "summary": "Delete purchase order by ID"
+ "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors"
+ "operationId": "deleteOrder"
+ "produces":
+ - "application/json"
+ - "application/xml"
+ "parameters":
+ - "name": "orderId"
+ "in": "path"
+ "description": "ID of the order that needs to be deleted"
+ "required": true
+ "type": "integer"
+ "minimum": 1
+ "format": "int64"
+ "responses":
+ "400":
+ "description": "Invalid ID supplied"
+ "404":
+ "description": "Order not found"
+ "/user/createWithList":
+ "post":
+ "tags":
+ - "user"
+ "summary": "Creates list of users with given input array"
+ "description": ""
+ "operationId": "createUsersWithListInput"
+ "consumes":
+ - "application/json"
+ "produces":
+ - "application/json"
+ - "application/xml"
+ "parameters":
+ - "in": "body"
+ "name": "body"
+ "description": "List of user object"
+ "required": true
+ "schema":
+ "type": "array"
+ "items":
+ "$ref": "#/definitions/User"
+ "responses":
+ "default":
+ "description": "successful operation"
+ "/user/{username}":
+ "get":
+ "tags":
+ - "user"
+ "summary": "Get user by user name"
+ "description": ""
+ "operationId": "getUserByName"
+ "produces":
+ - "application/json"
+ - "application/xml"
+ "parameters":
+ - "name": "username"
+ "in": "path"
+ "description": "The name that needs to be fetched. Use user1 for testing. "
+ "required": true
+ "type": "string"
+ "responses":
+ "200":
+ "description": "successful operation"
+ "schema":
+ "$ref": "#/definitions/User"
+ "400":
+ "description": "Invalid username supplied"
+ "404":
+ "description": "User not found"
+ "put":
+ "tags":
+ - "user"
+ "summary": "Updated user"
+ "description": "This can only be done by the logged in user."
+ "operationId": "updateUser"
+ "consumes":
+ - "application/json"
+ "produces":
+ - "application/json"
+ - "application/xml"
+ "parameters":
+ - "name": "username"
+ "in": "path"
+ "description": "name that need to be updated"
+ "required": true
+ "type": "string"
+ - "in": "body"
+ "name": "body"
+ "description": "Updated user object"
+ "required": true
+ "schema":
+ "$ref": "#/definitions/User"
+ "responses":
+ "400":
+ "description": "Invalid user supplied"
+ "404":
+ "description": "User not found"
+ "delete":
+ "tags":
+ - "user"
+ "summary": "Delete user"
+ "description": "This can only be done by the logged in user."
+ "operationId": "deleteUser"
+ "produces":
+ - "application/json"
+ - "application/xml"
+ "parameters":
+ - "name": "username"
+ "in": "path"
+ "description": "The name that needs to be deleted"
+ "required": true
+ "type": "string"
+ "responses":
+ "400":
+ "description": "Invalid username supplied"
+ "404":
+ "description": "User not found"
+ "/user/login":
+ "get":
+ "tags":
+ - "user"
+ "summary": "Logs user into the system"
+ "description": ""
+ "operationId": "loginUser"
+ "produces":
+ - "application/json"
+ - "application/xml"
+ "parameters":
+ - "name": "username"
+ "in": "query"
+ "description": "The user name for login"
+ "required": true
+ "type": "string"
+ - "name": "password"
+ "in": "query"
+ "description": "The password for login in clear text"
+ "required": true
+ "type": "string"
+ "responses":
+ "200":
+ "description": "successful operation"
+ "headers":
+ "X-Expires-After":
+ "type": "string"
+ "format": "date-time"
+ "description": "date in UTC when token expires"
+ "X-Rate-Limit":
+ "type": "integer"
+ "format": "int32"
+ "description": "calls per hour allowed by the user"
+ "schema":
+ "type": "string"
+ "400":
+ "description": "Invalid username/password supplied"
+ "/user/logout":
+ "get":
+ "tags":
+ - "user"
+ "summary": "Logs out current logged in user session"
+ "description": ""
+ "operationId": "logoutUser"
+ "produces":
+ - "application/json"
+ - "application/xml"
+ "parameters": [ ]
+ "responses":
+ "default":
+ "description": "successful operation"
+ "/user/createWithArray":
+ "post":
+ "tags":
+ - "user"
+ "summary": "Creates list of users with given input array"
+ "description": ""
+ "operationId": "createUsersWithArrayInput"
+ "consumes":
+ - "application/json"
+ "produces":
+ - "application/json"
+ - "application/xml"
+ "parameters":
+ - "in": "body"
+ "name": "body"
+ "description": "List of user object"
+ "required": true
+ "schema":
+ "type": "array"
+ "items":
+ "$ref": "#/definitions/User"
+ "responses":
+ "default":
+ "description": "successful operation"
+ "/user":
+ "post":
+ "tags":
+ - "user"
+ "summary": "Create user"
+ "description": "This can only be done by the logged in user."
+ "operationId": "createUser"
+ "consumes":
+ - "application/json"
+ "produces":
+ - "application/json"
+ - "application/xml"
+ "parameters":
+ - "in": "body"
+ "name": "body"
+ "description": "Created user object"
+ "required": true
+ "schema":
+ "$ref": "#/definitions/User"
+ "responses":
+ "default":
+ "description": "successful operation"
+"securityDefinitions":
+ "api_key":
+ "type": "apiKey"
+ "name": "api_key"
+ "in": "header"
+ "petstore_auth":
+ "type": "oauth2"
+ "authorizationUrl": "https://petstore.swagger.io/oauth/authorize"
+ "flow": "implicit"
+ "scopes":
+ "read:pets": "read your pets"
+ "write:pets": "modify pets in your account"
+"definitions":
+ "ApiResponse":
+ "type": "object"
+ "properties":
+ "code":
+ "type": "integer"
+ "format": "int32"
+ "type":
+ "type": "string"
+ "message":
+ "type": "string"
+ "Category":
+ "type": "object"
+ "properties":
+ "id":
+ "type": "integer"
+ "format": "int64"
+ "name":
+ "type": "string"
+ "xml":
+ "name": "Category"
+ "Pet":
+ "type": "object"
+ "required":
+ - "name"
+ - "photoUrls"
+ "properties":
+ "id":
+ "type": "integer"
+ "format": "int64"
+ "category":
+ "$ref": "#/definitions/Category"
+ "name":
+ "type": "string"
+ "example": "doggie"
+ "photoUrls":
+ "type": "array"
+ "xml":
+ "wrapped": true
+ "items":
+ "type": "string"
+ "xml":
+ "name": "photoUrl"
+ "tags":
+ "type": "array"
+ "xml":
+ "wrapped": true
+ "items":
+ "xml":
+ "name": "tag"
+ "$ref": "#/definitions/Tag"
+ "status":
+ "type": "string"
+ "description": "pet status in the store"
+ "enum":
+ - "available"
+ - "pending"
+ - "sold"
+ "xml":
+ "name": "Pet"
+ "Tag":
+ "type": "object"
+ "properties":
+ "id":
+ "type": "integer"
+ "format": "int64"
+ "name":
+ "type": "string"
+ "xml":
+ "name": "Tag"
+ "Order":
+ "type": "object"
+ "properties":
+ "id":
+ "type": "integer"
+ "format": "int64"
+ "petId":
+ "type": "integer"
+ "format": "int64"
+ "quantity":
+ "type": "integer"
+ "format": "int32"
+ "shipDate":
+ "type": "string"
+ "format": "date-time"
+ "status":
+ "type": "string"
+ "description": "Order Status"
+ "enum":
+ - "placed"
+ - "approved"
+ - "delivered"
+ "complete":
+ "type": "boolean"
+ "xml":
+ "name": "Order"
+ "User":
+ "type": "object"
+ "properties":
+ "id":
+ "type": "integer"
+ "format": "int64"
+ "username":
+ "type": "string"
+ "firstName":
+ "type": "string"
+ "lastName":
+ "type": "string"
+ "email":
+ "type": "string"
+ "password":
+ "type": "string"
+ "phone":
+ "type": "string"
+ "userStatus":
+ "type": "integer"
+ "format": "int32"
+ "description": "User Status"
+ "xml":
+ "name": "User"
+"externalDocs":
+ "description": "Find out more about Swagger"
+ "url": "http://swagger.io"