Search code examples
javaswaggerjax-rsswagger-ui

Generate jakarta swagger-ui compatible JSON from source code using maven


Lots of similar, but outdated answers here. I've recently been migrating JavaEE -> Jakarta of an old project when I get the chance. Noticed that all my swagger related profiles stopped generating the expected JSON/YAML.

So, how it worked before, is that it would use com.github.kongchen -> swagger-maven-plugin. It would do its black magic, and generate a json that swagger-ui could easily draw.

<plugin>
                    <groupId>com.github.kongchen</groupId>
                    <artifactId>swagger-maven-plugin</artifactId>
                    <version>${swagger.maven-plugin.version}</version>
                    <configuration>
                        <apiSources>
                            <apiSource>
                                <springmvc>false</springmvc>
                                <locations>
                                    <location>path.to.source</location>
                                </locations>
                                <schemes>
                                    <sheme>http</sheme>
                                </schemes>
                                <host>${swagger.api.host}</host>
                                <basePath>${swagger.api.basePath}</basePath>
                                <info>
                                    <title>Specs</title>
                                    <version>v1</version>
                                    <description>${swagger.api.header.description}</description>                                  
                                </info>                            
                                <templatePath>${apisource.templatePath}</templatePath>
                                <outputPath>${apisource.outputPath}</outputPath>
                                <swaggerDirectory>${apisource.swaggerDirectory}</swaggerDirectory>                                    
                            </apiSource>
                        </apiSources>
                    </configuration>
                    <executions>
                        <execution>
                            <phase>compile</phase>
                            <goals>
                                <goal>generate</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>

The JSON it generated looked like this:

    {
  "swagger" : "2.0",
  "info" : {
    "description" : "some description"
    "version" : "v1",
    "title" : "Title"
  },
  "host" : "localhost:8080",
  "basePath" : "/path/rest",
  "tags" : [ {
    "name" : "Service 1"
  }, {
    "name" : "Service 2"
    ...

The requests, responses models etc. were generated alright. Basically what I'm trying to do is, have this, but compatible with Jakarta namespace.

Migrating this, I had to use a different plugin, since kongchen doesn't support jakarta.

The resulting profile looks like this

<profile>
        <id>new-generate-api-doc</id>
        <dependencies>
            <dependency>
                <groupId>io.swagger.core.v3</groupId>
                <artifactId>swagger-jaxrs2-jakarta</artifactId>
                <version>2.2.8</version>
            </dependency>
            <dependency>
                <groupId>org.webjars</groupId>
                <artifactId>swagger-ui</artifactId>
                <version>${swagger-ui.version}</version>
            </dependency>
            <dependency>
                <groupId>io.swagger.core.v3</groupId>
                <artifactId>swagger-integration-jakarta</artifactId>
                <version>2.2.8</version>
            </dependency>
            <dependency>
                <groupId>io.swagger.core.v3</groupId>
                <artifactId>swagger-annotations-jakarta</artifactId>
                <version>2.2.8</version>
            </dependency>

        </dependencies>
        <properties>
            <swagger.api.version>v1</swagger.api.version>
        </properties>
        <build>
            <plugins>
                <plugin>
                    <groupId>io.swagger.core.v3</groupId>
                    <artifactId>swagger-maven-plugin-jakarta</artifactId>
                    <version>2.2.8</version>

                    <configuration>
                        <outputFileName>swagger</outputFileName>
                        <outputPath>${apisource.swaggerDirectory}</outputPath>
                        <outputFormat>JSONANDYAML</outputFormat>
                        <resourcePackages>
                            <package>com.packages.source</package>
                        </resourcePackages>
                        <prettyPrint>true</prettyPrint>
                        <ignoredRoutes></ignoredRoutes>
                        <configurationFilePath>${apisource.swaggerDirectory}/openapi-configuration.yaml</configurationFilePath>
                    </configuration>
                    <executions>
                        <execution>
                            <phase>compile</phase>
                            <goals>
                                <goal>resolve</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>

Someone's going to ask, so here's the openapi-configuration.yaml:

resourcePackages:
- io.swagger.sample.resource
prettyPrint: true
cacheTTL: 0
openAPI:
  info:
    version: '1.0'
    title: API services specification
    description: Desc
    termsOfService: http://swagger.io/terms/

But the resolved JSON looks nothing like the one before. No tags, no responses. I'm clearly doing something very wrong.

Example:

  {
  "openapi" : "3.0.1",
  "info" : {
    "title" : "services specification",
    "termsOfService" : "http://swagger.io/terms/",
    "version" : "1.0"
  },
  "paths" : {
    "/path/ann" : {
      "get" : {
        "operationId" : "getAnn",
        "parameters" : [ {
          "name" : "type",
          "in" : "query",
          "schema" : {
            "type" : "string"
          }
        }, {
          "name" : "orderby",
          "in" : "query",
          "schema" : {
            "type" : "string"
          }
        } ],
        **"responses" : {
          "default" : {
            "description" : "default response",
            "content" : {
              "application/json" : { }
            }
          }**
        }
      },
...
    "/api/v1/asset" : {
      "get" : {
        "operationId" : "getAllAssetsFromAssetIndex",
        "parameters" : [ {
          "name" : "regnumber",
          "in" : "query",
          "schema" : {
            "type" : "string"
          }
        }, {
          "name" : "vinnumber",
          "in" : "query",
          "schema" : {
            "type" : "string"
          }
        }, {
          "name" : "orderby",
          "in" : "query",
          "schema" : {
            "type" : "string"
          }
        } ],
        "responses" : {
          "default" : {
            "description" : "default response",
            "content" : {
              "application/json" : { }
            }
          }
        }
      },

Notice the empty "content" field, no tags, etc. The code remained the same in this case, so the same annotations as before are used.

Requests are properly generated, so are the namespaces. But it's noticeable that I'm generating a completely different JSON, which is then reflected in the display.

Example of a service's annotations:

    @Stateless
@Path(AssetServiceApiV1.BASE_URL)
@Api(value = "/" + AssetServiceApiV1.BASE_URL, tags = {AssetServiceApiV1.ASSET_SERVICE_TAG})
public class AssetServiceApiV1 extends RestServiceBaseApiV1 {

    public static final String BASE_URL = "api/v1/asset";
    public static final String ASSET_SERVICE_TAG = "Asset service";

    @GET
    @ApiOperation(
            httpMethod = "GET",
            value = "Get assets" + BEGIN_PARENS + APIFunctionalities.API_ASSET_READ + END_PARENS,
            notes = "Returns active assets with basic data. Various optional parameters are available.",
            response = AssetListV1Dto.class,
            responseContainer = OBJECT,
            produces = MediaType.APPLICATION_JSON,
            tags = {ASSET_SERVICE_TAG},
            code = 207
    )
    @Produces(MediaType.APPLICATION_JSON)
    public Response getAllAssetsFromAssetIndex(
            @ApiParam(value = "Registration number") @QueryParam("regnumber") String registrationNumber,
            @ApiParam(value = "...") @QueryParam("...") String ...,
            @ApiParam(value = "Available order by parameters are id, ..., ...") @QueryParam("orderby") String orderBy) {
...
    }

And here's the response object:

 @ApiModel(value = "Asset List Resource", description = "Asset list resource representation")
    public static class AssetListV1Dto {

        @ApiModelProperty(value = "List of assets", required = true)
        public List<AssetV1Dto> assetList;
        @ApiModelProperty(value = "List size", required = true)
        public int size;
...

    }

I could be just miles away from the right solution, and am generating something completely different. Anything helps. Not using Spring here, btw.

But I'm absolutely stuck, I have no idea what to use to generate a json similar to the one that the old plugin generated, and I've been using swagger-editor on the cloud to verify the output.


Solution

  • https://www.david-merrick.com/2017/11/15/useful-regexes-for-transitioning-swagger-2-0-to-3-0-annotations/

    Turns out something else still included the old annotations, so my IDE and maven didn't complain at all.

    Strange. This is a lot of work, but I think, within the scope of this question, is the solution.