holon-platform/holon-jaxrs

How to configure an @ApiDefinition annotation for all resource classes in a project?

DarkStar1 opened this issue · 2 comments

I'm currently trying out the 5.2.0-alpha1 version with swagger jersey components in my springboot project.
I get the following error when I annotate more than one class with the @Api() swagger annotation:

Validation of the application resource model has failed during application initialization. [[FATAL] A resource model has ambiguous (sub-)resource method for HTTP method GET and input mime-types as defined by"@consumes" and "@produces" annotations at Java methods public javax.ws.rs.core.Response com.holonplatform.jaxrs.swagger.internal.SwaggerApiListingResource.getApiListing(java.lang.String) and public javax.ws.rs.core.Response com.holonplatform.jaxrs.swagger.internal.SwaggerApiListingResource.getApiListing(java.lang.String) at matching regular expression /api-docs/default. These two methods produces and consumes exactly the same mime-types and therefore their invocation as a resource methods will always fail.; source='org.glassfish.jersey.server.model.RuntimeResource@2a7bb45c', [FATAL] A resource model has ambiguous (sub-)resource method for HTTP method GET and input mime-types as defined by"@consumes" and "@produces" annotations at Java methods public javax.ws.rs.core.Response com.holonplatform.jaxrs.swagger.internal.SwaggerApiListingResource.getApiListing(java.lang.String) and public javax.ws.rs.core.Response com.holonplatform.jaxrs.swagger.internal.SwaggerApiListingResource.getApiListing(java.lang.String) at matching regular expression /api-docs. These two methods produces and consumes exactly the same mime-types and therefore their invocation as a resource methods will always fail.; source='org.glassfish.jersey.server.model.RuntimeResource@258b4dfa']

So below is an reduced version of my setup:

@ApiDefinition(docsPath = "/api/docs", title = "...", version = "v1", prettyPrint = true)
@Api(value = "Trolley", consumes = "application/json", produces = "application/json")
@Path("Object")
public class SomeResource {
    @ApiOperation("Just returns a JSON object.")
    @ApiResponses(.....)
    @GET
    @Path("{objectId}")
    public Response getObject(@PathParam("objectId") String templateVar){....}
    
}

@Api(value = "System Test", produces = "text/html")
@Component
@Path("system")
public class TestResource {
    @ApiOperation("Just returns a HTML basic page with information.")
    @ApiResponses(.....)
    @GET
    @Path("check")
    public Response returnSomeHTMLTexT() {....}

}

The other question I have, as it is not clear in the documentation, must I add an @ApiDefinition to all resource classes in order to have them provide JSOn output at the same endpoint uri?

Hi @DarkStar1,
we just releases the Holon Platform version 5.2.0-alpha2, which fixes some bugs related to Swagger JAX-RS auto-configuration.

We're improving the documentation, you can find the 5.2.0-alpha2 Swagger JAX-RS auto-configuration docs here.

In short words, the Swagger API documentation endpoints auto-configuration works this way:

  • If the Holon Swagger configuration properties are available from Spring Boot application properties, they take the precedence over the @ApiDefinition annotation.
  • Otherwise, the @ApiDefinition annotation is used for the Swagger endpoints configuration.

Anyhow, only the JAX-RS endpoints annotated with both @Path and @Api are taken into account for API documentation generation. This because is how the Swagger JAX-RS integration works by default.

So, if you use the Holon Swagger configuration properties, for example:

holon:
  swagger:
    resource-package: "my.api.endpoints.package"

To declare the package (one or more) to scan to detect the JAX-RS endpoints annotated with @Path and @Api and generate the Swagger API endpoint. The endpoint path is /api-docs by default, or you can specify it using the path property:

holon:
  swagger:
    resource-package: "my.api.endpoints.package"
    path: "/my-docs"

So, if you don't use the Holon Swagger configuration properties, all the @Path and @Api annotated JAX-RS endpoint Spring beans are detected from classpath. So you have to ensure the JAX-RS endpoints are Spring beans too, for example using the @Component annotation.

In this scenario, the @ApiDefinition can be used (but it is optional) to configure:

  • The Swagger API endpoint path, using the annotation value()
  • The Swagger API general configuration, for example the API title or description

The API path declaration can be used to declare more than one API documentation endpoint. The documentation of each valid JAX-RS endpoint bound to the same API path will be merged into a single Swagger API documentation endpoint.

For example, given the following JAX-RS endpoints:

@Api 
@Path("example1")
@Component 
class ExampleEndpoint1 {
    /* Operations omitted */
}

@Api 
@Path("example2")
@Component 
class ExampleEndpoint2 {
    /* Operations omitted */
}

A single Swagger API documentation endpoint will be auto-generated and mapped to the default /api-docs path, and will include all the API operations provided by the two endpoint.

To declare two different Swagger API documentation endpoints, the @ApiDefinition annotation can be used, for example:

@ApiDefinition("/docs1")
@Api 
@Path("example1")
@Component 
class ExampleEndpoint1 {
    /* Operations omitted */
}

@ApiDefinition("/docs2")
@Api 
@Path("example2")
@Component 
class ExampleEndpoint2 {
    /* Operations omitted */
}

This way, two Swagger API documentation endpoints will be auto-generated, one mapped to the /docs1 path and the other mapped to the /docs2 path.

To merge another JAX-RS endpoint, an @ApiDefinition annotation with the same path can be used:

@ApiDefinition("/docs1")
@Api 
@Path("example1")
@Component 
class ExampleEndpoint1 {
    /* Operations omitted */
}

@ApiDefinition("/docs2")
@Api 
@Path("example2")
@Component 
class ExampleEndpoint2 {
    /* Operations omitted */
}

@ApiDefinition("/docs2")
@Api 
@Path("example3")
@Component 
class ExampleEndpoint3 {
    /* Operations omitted */
}

In the example above, two Swagger API documentation endpoints will be auto-generated (one mapped to the /docs1 path and the other mapped to the /docs2 path): the first one will only contain the ExampleEndpoint1 operations, while the second one will contain both the ExampleEndpoint2 and ExampleEndpoint3 operations.

When you use the @ApiDefinition annotation for the documentation configuration (title, description and so on), the @ApiDefinition attribute values for the same API documentation path must be consistent, a configuration exception is thrown otherwise.

Thanks for your detailed explanation.