javaswaggerresteasyopenapiswagger-maven-plugin

Swagger/OpenAPI 3.0 generation - Endpoint with generic list from an interface does not show up in documentation


I'm trying to generate a API documentation/swagger client for a Java EE application using Swagger / OpenAPI 3.0, but one method keeps missing in the documentation. I created a sample project to clarify my problem:

New, less complex example:

I got an Interface with a List as a parameter for my REST endpoint:

    package de.rockbunker.api;

    import javax.ws.rs.core.Response;
    import java.util.List;

    public interface Pong<T> {
        Response pongList(List<T> pongs);
    }

I'll implement this service and add swagger annotations:


    package de.rockbunker.api;

    import io.swagger.v3.oas.annotations.Operation;

    import javax.enterprise.context.ApplicationScoped;
    import javax.ws.rs.POST;
    import javax.ws.rs.Path;
    import javax.ws.rs.core.Response;
    import java.util.List;

    @ApplicationScoped
    @Path("")
    public class PingService implements Pong<String> {

        // Does not show up in documentation!?
        @POST
        @Path("pongList")
        @Operation(summary = "Pong List in Interface", tags = "Pong")
        @Override
        public Response pongList(List<String> pongs) {
            return Response.ok().build();
        }

    }

I try to generate a openapi documentation using the maven plugin (full configuration see below):

            <groupId>io.swagger.core.v3</groupId>
            <artifactId>swagger-maven-plugin</artifactId>
            <version>2.1.1</version>

But the generated documentation remains empty, the endpoint does not show up:

openapi: 3.0.1

More complex example

My rest endpoint implements a interface with a type parameter. I tried to use this parameter in several ways, resulting in this interface

public interface Pong<T> {
    Response pongList(List<T> pongs);

    Response pongArray(T[] pongs);

    Response justPong(T pong);

    Response pongNoTypeParameter(List<Integer> intPongs);
}

The service that implements the interface looks like this:


    package de.rockbunker.api;

    import io.swagger.v3.oas.annotations.Operation;
    import io.swagger.v3.oas.annotations.Parameter;
    import io.swagger.v3.oas.annotations.media.Schema;

    import javax.enterprise.context.ApplicationScoped;
    import javax.ws.rs.GET;
    import javax.ws.rs.POST;
    import javax.ws.rs.Path;
    import javax.ws.rs.core.Response;
    import java.util.List;

    @ApplicationScoped
    @Path("")
    public class PingService implements Pong<String>{

        // Works
        @GET
        @Path("ping")
        @Operation(summary = "Just ping", tags = "Ping")
        public Response ping() {
            return Response.ok("Ping").build();
        }

        // Does not show up
        @POST
        @Path("pongListInInterface")
        @Operation(summary = "Pong List in Interface", tags = "Pong")
        @Override
        public Response pongList(List<String> pongs) {
            return Response.ok().build();
        }

        // Works
        @POST
        @Path("pongArrayInInterface")
        @Operation(summary = "Pong Array in Interface", tags = "Pong")
        @Override
        public Response pongArray(String[] pongs) {
            return Response.ok().build();
        }

        // Works
        @GET
        @Path("pong")
        @Operation(summary = "Just pong", tags = "Pong")
        @Override
        public Response justPong(String pong) {
            return Response.ok().build();
        }

        // Works
        @GET
        @Path("intPong")
        @Operation(summary = "Just Integer pongs", tags = "Pong")
        @Override
        public Response pongNoTypeParameter(List<Integer> intPongs) {
            return Response.ok().build();
        }

        // Works
        @POST
        @Path("pongListNoInterface")
        @Operation(summary = "Pong List no Interface", tags = "Pong")
        public Response pongListNoInterface(List<String> pongs) {
            return Response.ok().build();
        }
    }

The openapi docmentation is generated, using the Maven plugin io.swagger.core.v3.swagger-annotations in version 2.1.1, resulting in a documentation that contains every method, but Response pongList(List<T> pongs); Every other use of that type parameter somehow works nicely, only when I use the type parameter as the type of a list I can't get it to show up.

openapi: 3.0.1
paths:
  /pong:
    get:
      tags:
      - Pong
      summary: Just pong
      operationId: justPong
      requestBody:
        content:
          '*/*':
            schema:
              type: string
      responses:
        default:
          description: default response
          content:
            '*/*': {}
  /pongArrayInInterface:
    post:
      tags:
      - Pong
      summary: Pong Array in Interface
      operationId: pongArray
      requestBody:
        content:
          '*/*':
            schema:
              type: array
              items:
                type: string
      responses:
        default:
          description: default response
          content:
            '*/*': {}
  /ping:
    get:
      tags:
      - Ping
      summary: Just ping
      operationId: ping
      responses:
        default:
          description: default response
          content:
            '*/*': {}
  /intPong:
    get:
      tags:
      - Pong
      summary: Just Integer pongs
      operationId: pongNoTypeParameter
      requestBody:
        content:
          '*/*':
            schema:
              type: array
              items:
                type: integer
                format: int32
      responses:
        default:
          description: default response
          content:
            '*/*': {}
  /pongListNoInterface:
    post:
      tags:
      - Pong
      summary: Pong List no Interface
      operationId: pongListNoInterface
      requestBody:
        content:
          '*/*':
            schema:
              type: array
              items:
                type: string
      responses:
        default:
          description: default response
          content:
            '*/*': {}

Plugin configuration:

<plugin>
            <groupId>io.swagger.core.v3</groupId>
            <artifactId>swagger-maven-plugin</artifactId>
            <version>2.1.1</version>
            <configuration>
                <outputFileName>openapi</outputFileName>
                <outputPath>${project.build.directory}/${project.artifactId}-${project.version}</outputPath>
                <outputFormat>JSONANDYAML</outputFormat>
                <resourcePackages>
                    <package>de.rockbunker.api</package>
                </resourcePackages>
                <prettyPrint>true</prettyPrint>
            </configuration>
            <executions>
                <execution>
                    <phase>compile</phase>
                    <goals>
                        <goal>resolve</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

Complete pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>de.rock-bunker</groupId>
    <artifactId>swagger-ui-test</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>8.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-client</artifactId>
            <version>4.3.1.Final</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.jboss.resteasy/resteasy-jackson2-provider -->
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jackson2-provider</artifactId>
            <version>4.3.1.Final</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.swagger.core.v3/swagger-jaxrs2 -->
<!--        <dependency>-->
<!--            <groupId>io.swagger.core.v3</groupId>-->
<!--            <artifactId>swagger-jaxrs2</artifactId>-->
<!--            <version>2.1.1</version>-->
<!--        </dependency>-->
        <dependency>
            <groupId>io.swagger.core.v3</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>2.1.1</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.2.3</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>com.googlecode.maven-download-plugin</groupId>
                <artifactId>download-maven-plugin</artifactId>
                <version>1.2.1</version>
                <executions>
                    <execution>
                        <id>swagger-ui</id>
                        <goals>
                            <goal>wget</goal>
                        </goals>
                        <configuration>
                            <url>https://github.com/swagger-api/swagger-ui/archive/master.tar.gz</url>
                            <unpack>true</unpack>
                            <outputDirectory>${project.build.directory}</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <id>copy-resources</id>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>target/${project.artifactId}-${project.version}</outputDirectory>
                            <resources>
                                <resource>
                                    <directory>${project.build.directory}/swagger-ui-master/dist</directory>

                                </resource>
                            </resources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
            <groupId>io.swagger.core.v3</groupId>
            <artifactId>swagger-maven-plugin</artifactId>
            <version>2.1.1</version>
            <configuration>
                <outputFileName>openapi</outputFileName>
                <outputPath>${project.build.directory}/${project.artifactId}-${project.version}</outputPath>
                <outputFormat>JSONANDYAML</outputFormat>
                <resourcePackages>
                    <package>de.rockbunker.api</package>
                </resourcePackages>
                <prettyPrint>true</prettyPrint>
            </configuration>
            <executions>
                <execution>
                    <phase>compile</phase>
                    <goals>
                        <goal>resolve</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

        </plugins>
    </build>
</project>

I dont quite get it. Why is the method Response pongList(List<T> pongs); missing in the documentation? Maybe you can tell me :-).

Greetings!


Solution

  • this feels like a bug to me. As a work-around you could just wrap your list into another object, without any type parameter it should work.

    Greetings