holon-platform / holon-jaxrs

Holon Platform JAX-RS integration module, including RestClient implementation, Swagger/OpenAPI integration and Spring Boot auto-configuration for Jersey and RestEasy
https://holon-platform.com
Apache License 2.0
7 stars 1 forks source link

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

Closed DarkStar1 closed 5 years ago

DarkStar1 commented 5 years ago

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?

rrighi commented 5 years ago

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:

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 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.

DarkStar1 commented 5 years ago

Thanks for your detailed explanation.