Skip to content

ajay-kmr/swagger

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

30 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Table of Contents

  1. Introduction

  2. Installation

  3. Configuration

  4. How To Use

  5. Sample Code

Introduction

This plugin provides easy integration of Swagger with GRAILS. It is highly aligned with Swagger and its 2.0 specification, thereby creating a language-agnostic interface to REST APIs. This allows both humans and computers to discover and understand the capabilities of service without access to source code, documentation simply through network traffic inspection

Installation

  1. Add repository and dependency in your build.gradle file using below lines:-

repositories {
    maven { url "https://dl.bintray.com/ajay-kumar/plugins" }
}

dependencies {
    compile 'org.grails.plugins:swagger:1.0.1'
}
Note
If you are using version 1.0.0 or older, your logging might not work as per your expected logback config file.
This is an issue with swagger servlet jar. Issue has already been raised for this:- swagger-api/swagger-core#1813
However, you can easily overcome this issue by explicitly setting the name of your logback configuration file in your build.gradle using:-
bootRun {
    jvmArgs = [
            "-Dlogging.config=${project.projectDir}/grails-app/conf/logback.groovy"
    ]
}
Note
Alternatively, you can also pass the name of logback configuration file using JVM argumants or system properties as follow:-
java -Dlogback.configurationFile=/path/to/config.xml

Please refer this link to know more about how to set logback config file location.

Configuration

  1. Expose an API from your application by adding line- "/apidoc/$action?/$id?"(controller: "apiDoc", action: "getDocuments") in your UrlMappings.groovy file under the “static mappings” block.

    Note
    This API will be used to generate swagger document. So if you want to access your swagger document with different URI, do the necessary changes here eg if you want to expose your swagger document using URI- api/vi/myDoc then you should add "/api/vi/myDoc/$action?/$id?"(controller: "apiDoc", action: "getDocuments") in your UrlMappings.groovy file under the “static mappings” block.
  2. If your application is located behind a proxy that enables the required CORS headers then you need to enable CORS support too.

    Note
    Please read this article to know if CORS support needs to be enabled or not for your application.
  3. Create a configuration file (eg application.groovy) in conf folder present inside your application’s grails-app folder (if not created yet) and add the following configuration details:-

application.groovy

import io.swagger.models.Scheme

swagger {
   info {
       description = "Move your app forward with the Swagger API Documentation"
       version = "ttn-swagger-1.0.0"
       title = "Swagger API"
       termsOfServices = "http://swagger.io/"
       contact {
           name = "Contact Us"
           url = "http://swagger.io"
           email = "[email protected]"
       }
       license {
           name = "licence under http://www.tothenew.com/"
           url = "http://www.tothenew.com/"
       }
   }
   schemes = [Scheme.HTTP]
   consumes = ["application/json"]
}

How To Use

  1. Next there is a good example to show you how the set of annotations can be used to generate swagger documentation. Please refer this sample example to know more.

  2. A set of annotations is used to generate swagger documentation. Please refer this link to know more about the available annotations.

    Note
    Only annotations provided by swagger-core are supported and JAX-RS annotations are not supported.
  3. Annotate your controller class with annotation @io.swagger.annotations.Api to make it available for documentation. For more details see sample code.

  4. Annotate your methods/action of controller with @io.swagger.annotations.ApiOperation, @io.swagger.annotations.ApiResponses, @io.swagger.annotations.ApiImplicitParams to include various method level information in your documentation. See next section to know more details about how to use these annotations.

    Note
    The value corresponding to “value” attribute of API annotation present at class level and the value corresponding to “nickname” attribute of “ApiOperation” annotation present at method level when combined together, it should form the relative end point provided in UrlMappings.groovy. Refer to explanation below the provided sample.
  5. If the “value” attribute of “API” annotation present at class label is not provided then it defaults to the controller name.

  6. The “nickname” attribute of “ApiOperation” annotation is also new and serves as unique name for the operation. If this is not provided this default to the method name

    Note
    If you are not explicitly providing the mapping of your actions defined in controller to the end points using UrlMappings.groovy file, you need not to bother about “value” attribute of “API” annotation present at class label and “nickname” attribute of “ApiOperation” annotation present at method label. Its default value will work fine.

Sample Code

Assume your UrlMappings.groovy class is as follow:-

package testswaggerplugin

class UrlMappings {
   static mappings = {
       "/$controller/$action?/$id?(.$format)?" {
           constraints {
               // apply constraints here
           }
       }
       "/"(view: "/index")
       "500"(view: '/error')
       "404"(view: '/notFound')
       "/api/v1/city/$cityId"(controller: "city", action: "getCity", method: "GET")
       "/api/v1/city/list"(controller: "city", action: "getCityList", method: "GET")
       "/api/v1/city/createUpdate"(controller: "city", action: "createOrUpdateCity", method: "POST")
       "/api/v1/city/$cityId"(controller: "city", action: "deleteCity", method: "DELETE")
   }
}

Then your swagger annotated CityController.groovy is as follow:-

package testswaggerplugin

import CityDTO
import ResponseDTO
import grails.converters.JSON
import io.swagger.annotations.*

@Api(value = "/api/v1", tags = ["City"], description = "City Api's")
class CityController {

   static namespace = 'v1'

   @ApiOperation(
           value = "List Cities",
           nickname = "city/{cityId}",
           produces = "application/json",
           consumes = "application/json",
           httpMethod = "GET",
           response = ResponseDTO.class
           )
   @ApiResponses([
           @ApiResponse(code = 405,
           message = "Method Not Allowed. Only GET is allowed"),

           @ApiResponse(code = 404,
           message = "Method Not Found")
           ])
   @ApiImplicitParams([
           @ApiImplicitParam(name = "cityId",
           paramType = "path",
           required = true,
           value = "City Id",
           dataType = "string"),

           @ApiImplicitParam(name = "applicationType",
           paramType = "header",
           required = true,
           defaultValue = "web",
           value = "Application Types",
           dataType = "string"),

           @ApiImplicitParam(name = "Accept-Language",
           paramType = "header",
           required = true,
           defaultValue = "en",
           value = "Accept-Language",
           dataType = "string")
   ])
   def getCity(String cityId) {
      // Demonstrate how to use swagger annotation to generate documentation
      // for method which accepts cityId as parameter provided in url path.

       render(new ResponseDTO(status: true,
       message: "New Delhi",
       data: ["key1": "value1", "key2": "value2"]) as JSON)
   }

   @ApiOperation(
           value = "List Cities",
           nickname = "city/list",
           produces = "application/json",
           consumes = "application/json",
           httpMethod = "GET",
           response = ResponseDTO.class
           )
   @ApiResponses([
           @ApiResponse(code = 405,
           message = "Method Not Allowed. Only GET is allowed"),

           @ApiResponse(code = 404,
           message = "Method Not Found")
           ])

   @ApiImplicitParams([
           @ApiImplicitParam(name = "offset",
           paramType = "query", required = true,
           value = "Offset", dataType = "integer"),

           @ApiImplicitParam(name = "limit",
           paramType = "query",
           required = true,
           value = "Max size",
           dataType = "integer"),

           @ApiImplicitParam(name = "applicationType",
           paramType = "header",
           required = true,
           defaultValue = "web",
           value = "Application Types",
           dataType = "string"),

           @ApiImplicitParam(name = "Accept-Language",
           paramType = "header",
           required = true,
           defaultValue = "en",
           value = "Accept-Language",
           dataType = "string")
   ])
   def getCityList(Integer offset, Integer limit) {
      // Demonstrate how to use swagger annotation to generate documentation
      // for method which accepts offset and limit as query parameter.
       render(new ResponseDTO(status: true,
       message: "City List fetched successfully",
       data: ["key1": "value1", "key2": "value2"]) as JSON)
   }

   @ApiOperation(
           value = "Create City",
           notes = "Creates a new City. Accepts a City json.",
           produces = "application/json",
           consumes = "application/json",
           httpMethod = "POST",
           nickname = "/city/createUpdate",
           response = ResponseDTO.class
           )
   @ApiResponses([
           @ApiResponse(code = 405,
           message = "Method Not Allowed. Only POST is allowed"),

           @ApiResponse(code = 404,
           message = "Method Not Found")
   ])
   @ApiImplicitParams([
           @ApiImplicitParam(name = "body",
           paramType = "body",
           required = true,
           value = "Requires City Details",
           dataType = "CityDTO"),

           @ApiImplicitParam(name = "applicationType",
           paramType = "header",
           required = true,
           defaultValue = "web",
           value = "Application Types",
           dataType = "string"),

           @ApiImplicitParam(name = "Accept-Language",
           paramType = "header",
           required = true,
           defaultValue = "en",
           value = "Accept-Language",
           dataType = "string")
   ])
   def createOrUpdateCity(CityDTO cityDTO) {
       render(new ResponseDTO(status: true,
       message: "City updated successfully",
       data: cityDTO) as JSON)
   }

   @ApiOperation(
           value = "Delete City",
           notes = "Deletes a City.Accepts a City ID .",
           produces = "application/json",
           consumes = "application/json",
           httpMethod = "DELETE",
           nickname = "/city/{cityId}",
           response = ResponseDTO.class
           )
   @ApiResponses([
           @ApiResponse(code = 405,
           message = "Method Not Allowed. Only Delete is allowed"),
           @ApiResponse(code = 404,
           message = "Method Not Found")])

   @ApiImplicitParams([
           @ApiImplicitParam(name = 'cityId',
           paramType = 'path',
           required = true, value = "Requires City id for delete",
           dataType = "string"),

           @ApiImplicitParam(name = "applicationType",
           paramType = "header",
           required = true,
           defaultValue = "web",
           value = "Application Types",
           dataType = "string"),

           @ApiImplicitParam(name = "Accept-Language",
           paramType = "header",
           required = true,
           defaultValue = "en",
           value = "Accept-Language",
           dataType = "string")
   ])
   def deleteCity(String cityId) {
       render(new ResponseDTO(status: true,
       message: "City deleted successfully") as JSON)
   }

    @ApiOperation(value = "Upload File Example",
            notes = "Upload File Example",
            nickname = "/city/upload",
            response = ResponseDTO.class,
            httpMethod = "POST")
    @ApiResponses(value = [
            @ApiResponse(code = 405, message = "Method Not Allowed. Only POST is allowed"),
            @ApiResponse(code = 404, message = "Method Not Found")
    ])
    @ApiImplicitParams([
            @ApiImplicitParam(name = 'cityFile', paramType = 'form',
                    required = true,
                    value = "Requires File Containing City Information",
                    dataType = "java.io.File")])
    ResponseDTO uploadCityData(HttpServletRequest request) {
      // Demonstrate how to use swagger annotation to generate documentation
      // for method which accepts MultipartFile in request.
        MultipartFile file = request.getFile('cityFile')
        //Do with file
        (new ResponseDTO(status: true, message: "File with name ${file?.originalFilename} uploaded successfully") as JSON)
    }
}

In the sample code provided above you can find that CityController is annotated with @Api(value = "/api/v1", tags = ["City"], description = "City Api’s"). So the value corresponding to “value” attribute of “API” annotation present at this class label is "/api/v1".

Similarly you can observe that getCity() method of CityController has annotation @ApiOperation(value = "List Cities", nickname = "city/{cityId}", produces = "application/json", consumes = "application/json", httpMethod = "GET", response = ResponseDTO.class). Hence the value corresponding to “nickname” attribute of “ApiOperation” annotation present at this method label is "city/{cityId}" and when these two values of API annotation and ApiOperation annotations are combined together it gives- "/api/v1/city/{cityId}".

This combined value is Swagger’s way of specifying the end url- "/api/v1/city/$cityId" defined in UrlMappings.groovy file for (controller: "city", action: "getCity", method: "GET")