Author: Ole Lensmar
The recent Swagger core tooling for java adds a number of features to the core annotations used by the Swagger runtime to generate a Swagger definition for your API. Let's have a quick look at these to see how they can help you provide more complete API metadata when using a bottom-up approach to creating your Swagger definitions.
@SwaggerDefinition
The @SwaggerDefinition annotation is the single biggest addition to the core annotations; it provides you a means to add definition-level metadata to the generated Swagger, all directly correlating to properties of the Swagger object in the Swagger 2.0 specification:
- info - Provides metadata about the API as a whole (title, description, license, etc)
- consumes - a global list of mime-types that are consumed by the APIs described in this definition
- produces - a global list of mime-types that produced by the APIs described in this definition
- schemes - the transfer protocols supported by this API
- basePath - overrides the basePath deduced by the Swagger runtime
- host - overrides the host deduced by the Swagger runtime
- tags - a list of tags used by the specification with additional metadata.
- externalDocs - additional external documentation references
The annotation can be on any class scanned by the Swagger runtime. For example, you could have an empty interface in your project with this annotation only - to separate general API metadata from metadata that is specific to your API resources. For example, putting this alongside a standard JAX-RS resource:
@SwaggerDefinition(
info = @Info(
description = "My API",
version = "V1.2.3",
title = "The only API you'll ever need to learn about me",
termsOfService = "share and care",
contact = @Contact(name = "Sponge-Bob", email = "[email protected]", url = "http://swagger.io"),
license = @License(name = "Apache 2.0", url = "http://www.apache.org"),
consumes = {"application/json" },
produces = {"application/json" },
schemes = {SwaggerDefinition.Scheme.HTTP, SwaggerDefinition.Scheme.HTTPS},
externalDocs = @ExternalDocs(value = "About me", url = "http://about.me/me")
)
public interface MyApiDefinition {}
results in this being added to the generated Swagger definition:
{
"swagger": "2.0",
"info": {
"description": "My API",
"version": "V1.2.3",
"title": "The only API you'll ever need to learn about me",
"termsOfService": "share and care",
"contact": {
"name": "Sponge-Bob",
"url": "http://swagger.io",
"email": "[email protected]"
},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org"
},
"schemes": [
"http",
"https"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
...
You can read more about the SwaggerDefinition annotation on the wiki and its corresponding javadoc.
@Extension
The extension annotation allows you to add extension properties to a Swagger definition. It is currently supported within the @ApiOperation, @Info and @Tag annotations. There are two ways to use it:
...
extensions = {
@Extension(properties = {
@ExtensionProperty(name = "test1", value = "value1"),
@ExtensionProperty(name = "test2", value = "value2")
})
}
...
which will result in the following JSON:
...
"x-test1" : "value1",
"x-test2" : "value2"
...
The property name will automatically be prefixed with "x-" if it isn't done so explicitly in the annotation.
Alternatively you can name the extension:
...
extensions = {
@Extension( name = "my-extension", properties = {
@ExtensionProperty(name = "test1", value = "value1"),
@ExtensionProperty(name = "test2", value = "value2")
})
}
...
which will result in the following JSON:
...
"x-my-extension" : {
"test1" : "value1",
"test2" : "value2"
}
...
which wraps the contained extension properties in a JSON object.
ReaderListener
If these annotation extensions still don't help you craft a Swagger definition the way you want, you can opt to create a ReaderListener that will be invoked before and after the Swagger runtime. This extension reads all Swagger and JAX-RS annotations and builds the corresponding Swagger definition. Implementing either handlers gives you full control over the generated definition, which allows you to change it any way you want:
- Add security definitions or custom model objects
- Filter out unwanted information based on some contextual attribute
- Read and add API definitions defined elsewhere
- etc..
All you need to do is provide a class that implements the ReaderListener interface and make sure that class is found by the classpath scanner used by the Swagger runtime. If you create a @SwaggerDefinition annotated interface as shown above, you could change that to a class and implement this interface in it:
@SwaggerDefinition(...)
public class MyApiDefinition implements ReaderListener {
public static final String TOKEN_AUTH_SCHEME = "tokenAuthScheme";
public static final String ACCOUNT_READ_SCOPE = "account_read";
public static final String ACCOUNT_WRITE_SCOPE = "account_write";
@Override
public void beforeScan(Reader reader, Swagger swagger) {
}
@Override
public void afterScan(Reader reader, Swagger swagger) {
OAuth2Definition tokenScheme = new OAuth2Definition();
tokenScheme.setFlow("password");
tokenScheme.setTokenUrl("https://" + swagger.getHost() + "/tokens");
Map<String, String> scopes = new HashMap<>();
scopes.put(ACCOUNT_READ_SCOPE, "Read my data");
scopes.put(ACCOUNT_WRITE_SCOPE, "Update my data");
tokenScheme.setScopes(scopes);
swagger.addSecurityDefinition(TOKEN_AUTH_SCHEME, tokenScheme);
}
}
This example adds an OAuth2 Password Credentials Grant security definition, to which you can refer to in your @ApiOperation annotations with something like:
@ApiOperation(value = "Updates user data",
authorizations = @Authorization(value = MyApiDefinition.TOKEN_AUTH_SCHEME, scopes =
@AuthorizationScope(scope = MyApiDefinition.ACCOUNT_WRITE_SCOPE, description = "Write access to user data")))
public Response updateUser( UserData data ) throws NotFoundException {
...
}
Since the ReaderLicenser is specific to the JAX-RS implementation module in the Swagger Core project, it is available in the io.swagger.swagger-jaxrs module - and not in io.swagger.swagger-annotations as the others described above.
More improvements to the annotations
The above aren't the only improvements and additions that have been made to the Swagger annotations - we've also:
- Added a @ResponseHeader annotation for adding custom response headers
- Improved support for Ranged values in @ApiParam
- Added the ability to set multiple tags per @ApiOperation
- Improved support for container-based responses/parameters.
As always - if you're missing something or find an issue - please get in touch on the Google Group or GitHub so we can sort things out and make Swagger even better!