Show Navigation

swagger

Grails Swagger Plugin

Owner: ajay-kumar | 1.0.1 | Apr 6, 2019 | Package | Issues | Source | License: Apache-2.0


dependencies {
    compile 'swagger:swagger:1.0.1'
}

            

Optional[Table of Contents

. Introduction
. Installation
. Configuration
. How To Use
. 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
~~~~~~~~~~~~~
. Add repository and dependency in your *_build.gradle_* file using below lines:-
[source,java]
-----------------
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:- https://github.com/swagger-api/swagger-core/issues/1813[https://github.com/swagger-api/swagger-core/issues/1813] +
However, you can easily overcome this issue by explicitly setting the name of your logback configuration file in your *_build.gradle_* using:- +
[source,java]
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:-
[source,java]
java -Dlogback.configurationFile=/path/to/config.xml


Please refer https://logback.qos.ch/manual/configuration.html#configFileProperty[this link] to know more about how to set logback config file location.

Configuration
~~~~~~~~~~~~~

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

. 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 https://github.com/swagger-api/swagger-ui#testing-cors-support[this article] to know if CORS support needs to be enabled or not for your application.

. 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
[source,java]
-----------------
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
~~~~~~~~~~~
. Next there is a good example to show you how the set of annotations can be used to generate swagger documentation.
 Please refer https://github.com/ajay-kmr/swagger/blob/master/swagger/src/test/groovy/services/swaggerResources/SwaggerAnnotatedResource.groovy[this sample example] to know more.
. A set of annotations is used to generate swagger documentation. Please refer https://github.com/swagger-api/swagger-core/wiki/Annotations[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.

. Annotate your controller class with annotation [email protected]_ to make it available for documentation. For more details see sample code.

. Annotate your methods/action of controller with [email protected], @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.*

. If the *“value”* attribute of *“API”* annotation present at class label is not provided then it defaults to the controller name.
. 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:-

[source,java]
-----------------
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:-


[source,java]
-----------------
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")*




]