Search code examples
swaggerswagger-codegenspringfoxswagger-editor

api-doc generated by Springfox not work with swagger-codegen


I'm testing if I can use the api-doc generated by springfox to generate Java client code through swagger-codegen.

I use the boot-swagger module from springfox-demos and the generated api-doc looks like below (pretty formatted)

{
  "swagger": "2.0",
  "info": {
    "description": "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
    "version": "2.0",
    "title": "Springfox petstore API",
    "termsOfService": "http://springfox.io",
    "contact": {
      "name": "springfox"
    },
    "license": {
      "name": "Apache License Version 2.0",
      "url": "https://github.com/springfox/springfox/blob/master/LICENSE"
    }
  },
  "host": "localhost:8080",
  "basePath": "/springfox",
  "tags": [
    {
      "name": "category-controller",
      "description": "Category Controller"
    }
  ],
  "paths": {
    "/categories{?categories}": {
      "post": {
        "tags": [
          "category-controller"
        ],
        "summary": "map",
        "operationId": "mapUsingPOST",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "*/*"
        ],
        "parameters": [
          {
            "name": "categories",
            "in": "query",
            "description": "categories",
            "required": false,
            "type": "array",
            "items": {
              "type": "string"
            },
            "collectionFormat": "multi"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "ONE",
                  "TWO",
                  "THREE"
                ]
              }
            }
          },
          "201": {
            "description": "Created"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    },
    "/category/Resource{?someEnum}": {
      "get": {
        "tags": [
          "category-controller"
        ],
        "summary": "search",
        "operationId": "searchUsingGET",
        "produces": [
          "*/*"
        ],
        "parameters": [
          {
            "name": "someEnum",
            "in": "query",
            "description": "someEnum",
            "required": true,
            "type": "string",
            "enum": [
              "ONE",
              "TWO",
              "THREE"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "string"
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    },
    "/category/map": {
      "get": {
        "tags": [
          "category-controller"
        ],
        "summary": "map",
        "operationId": "mapUsingGET",
        "produces": [
          "*/*"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "type": "object",
              "additionalProperties": {
                "type": "object",
                "additionalProperties": {
                  "$ref": "#/definitions/Pet"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    },
    "/category/{id}": {
      "post": {
        "tags": [
          "category-controller"
        ],
        "summary": "someOperation",
        "operationId": "someOperationUsingPOST",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "*/*"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "id",
            "required": true,
            "type": "integer",
            "format": "int64"
          },
          {
            "in": "body",
            "name": "userId",
            "description": "userId",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "201": {
            "description": "Created"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    },
    "/category/{id}/map{?test}": {
      "post": {
        "tags": [
          "category-controller"
        ],
        "summary": "map",
        "operationId": "mapUsingPOST_1",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "*/*"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "id",
            "required": true,
            "type": "string"
          },
          {
            "name": "test",
            "in": "query",
            "description": "test",
            "required": true,
            "items": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "201": {
            "description": "Created"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    },
    "/category/{id}/{userId}": {
      "post": {
        "tags": [
          "category-controller"
        ],
        "summary": "ignoredParam",
        "operationId": "ignoredParamUsingPOST",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "*/*"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "id",
            "required": true,
            "type": "integer",
            "format": "int64"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "201": {
            "description": "Created"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    }
  },
  "definitions": {
    "Category": {
      "type": "object",
      "properties": {
        "id": {
          "type": "integer",
          "format": "int64"
        },
        "name": {
          "type": "string"
        }
      },
      "title": "Category"
    },
    "Map«string,Pet»": {
      "type": "object",
      "title": "Map«string,Pet»",
      "additionalProperties": {
        "$ref": "#/definitions/Pet"
      }
    },
    "Pet": {
      "type": "object",
      "properties": {
        "category": {
          "$ref": "#/definitions/Category"
        },
        "id": {
          "type": "integer",
          "format": "int64"
        },
        "identifier": {
          "type": "integer",
          "format": "int64"
        },
        "name": {
          "type": "string"
        },
        "photoUrls": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "status": {
          "type": "string",
          "description": "pet status in the store",
          "allowEmptyValue": false,
          "enum": [
            "available",
            "pending",
            "sold"
          ]
        },
        "tags": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/Tag"
          }
        }
      },
      "title": "Pet"
    },
    "Tag": {
      "type": "object",
      "properties": {
        "id": {
          "type": "integer",
          "format": "int64"
        },
        "name": {
          "type": "string"
        }
      },
      "title": "Tag"
    }
  }
}

The code generation failed and it looks like the api-doc.json does not even fit for swagger specification.

I pasted the code into swagger editor, and it complains for a lot of errors such as

Semantic error at paths./categories{?categories} 
Query strings in paths are not allowed.
Jump to line 18

So is it possible to generate client code from the api-doc.json generated by Springfox?


Solution

  • After reading the document of Springfox Reference Documentation, I have solved this issue!

    Format like below is because by default springfox applies RFC 6570

    ./categories{?categories} 
    

    3.2. Configuration explained

    An example of this would be two apis: First, http://example.org/findCustomersBy?name=Test to find customers by name. Per RFC 6570, this would be represented as http://example.org/findCustomersBy{?name}. Second, http://example.org/findCustomersBy?zip=76051 to find customers by zip. Per RFC 6570, this would be represented as http://example.org/findCustomersBy{?zip}.

    One more issue I didn't mention in the question:

        "Map«string,Pet»": { // The generated JSON contains special characters
          "type": "object",
          "title": "Map«string,Pet»",
          "additionalProperties": {
            "$ref": "#/definitions/Pet"
          }
        }
    

    The document clearly mentioned the swagger-codegen situation:

    6.8.3. Changing how Generic Types are Named

    By default, types with generics will be labeled with '\u00ab'(<<), '\u00bb'(>>), and commas. This can be problematic with things like swagger-codegen. You can override this behavior by implementing your own GenericTypeNamingStrategy. For example, if you wanted List to be encoded as 'ListOfString' and Map to be encoded as 'MapOfStringAndObject' you could set the forCodeGeneration customization option to true during plugin customization:

     docket.forCodeGeneration(true|false);
    

    To summary, when we generate the docket in springfox for swagger-codegen, we need to specify the following switches:

    new Docket(DocumentationType.SWAGGER_2)
                    .forCodeGeneration(true)
                    .enableUrlTemplating(false)