Giter Club home page Giter Club logo

swagger's Introduction

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")

swagger's People

Contributors

ajay-kmr avatar binlecode avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

swagger's Issues

Feature Request: Support JAX-RS annotation @Path

Hi, thanks for valuable products.
Do you have any plan to support JAX-RS annotations, especcially @java.ws.rs.Path?

I know the document says:

Note:
Only annotations provided by swagger-core are supported and JAX-RS annotations are not supported.

But when generate java(okhttp-gson) SDK source code by swagger-codegen from annotated controller, @path annotation of JAX-RS are very required.
Because when generating swagger yaml from annotated grails application,
"nickname" property effects not only "operationId" but also changes "path" property.

We have to prevent that the "path" change, by using @path to override path change by "nickname".

ImplicitParams are not being retrieved

Hello,

Thanks for provinding this great plugin. However I have done the setup as stated, all the responses and api params are properly described with exception of the Request Params. I have tried different types and combinations, but they seem to be ignored by the service.

Could you please review this code part?

Thanks,
Andrio

Grails 4

Seems that the plugin is not working with grails 4. is there any plan to support it?

I'll try again maybe with an demo project to investigate better

Including this plugin stops logback logging from working in my application

Steps to reproduce

  1. Create new Grails 3 application
  2. Create dummy service method with log.info messages
  3. Configure logback.groovy so that log messages appear in the console (logger('grails.app', DEBUG)
  4. Add dependency on Swagger plugin (1.0.0)
  5. Re-run app - log messages do not appear

Any suggestions you might have would be greatly appreciated.

Place to configure validatorUrl

In application.groovy I added an additional line to disable validation
swagger { // https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md validatorUrl = null }
Nevertheless the error badge due to failing validation (firewall) remains. Did I set the option in the wrong place?

No support for UTF-8 encoding

ApiDocController does not support fetching HTML content from webjars (swagger-ui) with UTF-8, so international characters (like Cyrillic) from swagger annotations descriptions are rendered incorrectly (with question marks - '?')

swagger-ui version (2.2.5) is hardcoded, but should be configurable

Version of used swagger-ui library is hardcoded in ApiDocController:
redirect uri: "/webjars/swagger-ui/2.2.5/index.html?url=${request.getRequestURI()}"

Users should be able to add their own version of swagger-ui library in build.gradle and use it instead of 'default' version, provided with a plugin. I suggest moving swagger-ui library version in configuration file (application.yaml/application.groovy).

Issues when used in Grails 3.3.0

I added the swagger plugin to our Grails 3.3.0 project which uses Mongo DB. The integration tests started failing with these errors:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.grails.datastore.mapping.mongo.MongoDatastore]: Constructor threw exception; nested exception is java.lang.NoClassDefFoundError: org/hibernate/validator/spi/resourceloading/ResourceBundleLocator at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:154) at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:122) at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:271)
Since we're using Mongo, I was surprised to see these Hibernate-related errors. Am not really sure what's the issue, but I took a quick run at upgrading the swagger plugin to use Grails 3.3. I made the minimal changes to get the plugin to build, but the same issue occurred after I tried the 3.3 upgraded plugin. Am still investigating...

Note: I found that by adding Hibernate5 back into the dependencies, the issue went away; however, we don't want to pollute out dependencies with Hibernate. Also, doing so add further complications, like having to add special mapping configuration to each domain to get it to use Mongo.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.