In order for us to generate API documentation for our modules we need to implement swagger to use the annotations assigned to controller classes within our modules.
The following packages should be added to the module's build.gradle:
implementation("io.swagger.core.v3:swagger-annotations:2.2.22") { exclude group: "javax.validation", module: "validation-api" }
Required additions to controllers/endpoints
In order to use the SwaggerApiService we need to add a controller to the module as follows:
public class SwaggerUIController { SwaggerApiService swaggerApiService; public def api() { // Generate the API documentation Map swaggerApiDoc = swaggerApiService.generateSwaggerApiDoc(); // This header is required if we are coming through okapi header("Access-Control-Allow-Origin", request.getHeader('Origin')); // We should now just have the calls we are interested in render(status: 200, contentType: "application/json", text: JsonOutput.toJson(swaggerApiDoc)); } }
The service used within this controller should be within the K-Int Web Toolkit (KIWT) so that it can be utilised across the modules.
Annotating the Code
To annotate a class use an annotation like the following:
@Path(value = "/licenses", tags = ["Swagger Controller"], description = "Swagger Api")
Value will be prefixed to the path of all operations specified in this class.
Tags will be associated with the operations so they can be grouped together
Description is a brief descrion of this class
For the methods within a class they will need to be annotated like the following:
@Operation( value = "List the states that a request can end up in after the action has been performed", nickname = "availableAction/toStates/{stateModel}/{actionCode}", produces = "application/json", httpMethod = "GET" ) @ApiResponses([ @ApiResponse(code = 200, message = "Success") ]) @ApiImplicitParams([ @ApiImplicitParam( name = "stateModel", paramType = "path", required = true, value = "The state model the action is applicable for", dataType = "string", defaultValue = "PatronRequest" ), @ApiImplicitParam( name = "actionCode", paramType = "path", required = true, value = "The action that you want to know which states a request could move onto after the action has been performed", dataType = "string" ) ])
Changing class extensions
In order to correctly implement the annotations for various grails methods such as update(), delete(), index() etc. We need to replace the OkapiTenantAwareController on the majority of controller classes with a new controller class extension called OkapiTenantAwareSwaggerController, this explicitly contained all these functions alongside their required annotations as shown below:
@ApiOperation( value = "Creates a new record with the supplied data", nickname = "/", httpMethod = "POST" ) @ApiResponses([ @ApiResponse(code = 201, message = "Created") ]) @ApiImplicitParams([ @ApiImplicitParam( paramType = "body", required = true, allowMultiple = false, value = "The json record that is going to be used for creation", defaultValue = "{}", dataType = "string" ) ]) @OkapiPermission(name = "item", permissionGroup = PermissionGroup.WRITE) @Transactional public def save() {
Additionally we have an additional class for handling purely GET related controllers, OkapiTenantAwareGetController. OkapiTenantAwareSwaggerController extends this class and it also contains the associated KIWT query params such as match, term, filters etc. so these can be include in the documentation automatically:
@ApiOperation( value = "Search with the supplied criteria", nickname = "/", produces = "application/json", httpMethod = "GET" ) @ApiResponses([ @ApiResponse(code = 200, message = "Success") ]) @ApiImplicitParams([ @ApiImplicitParam( name = "term", paramType = "query", required = false, allowMultiple = false, value = "The term to be searched for", dataType = "string" ), @ApiImplicitParam( name = "match", paramType = "query", required = false, allowMultiple = true, value = "The properties the match is to be applied to", dataType = "string" ),