Skip to content
master
Switch branches/tags
Go to file
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
src
 
 
 
 
 
 
 
 
 
 
 
 

README.md

Swagger SBT Plugin

SBT plugin for extracting Swagger & JAX-RS (jsr311) annotations from compiled classes into Swagger API models, which are then serialized to JSON for consumption with swagger-ui.

Installation

Modify the following 2 files in your project as described below:

  • project/plugins.sbt
  • project/Build.scala

project/plugins.sbt

Add sbt-swagger to your project's plugins:

addSbtPlugin("com.hootsuite" %% "sbt-swagger" % "1.0.0")

project/Build.scala

Configure your project's SBT build to use it. Make sure to replace com.example.your.project.package in the example below with your project's package prefix.

import com.hootsuite.sbt.swagger
val swaggerSettings = Seq(
  swagger.Sbt.apiVersion := "1.0",
  swagger.Sbt.basePath := "http://localhost",
  swagger.Sbt.apiPath := "/",
  //swagger.Sbt.packages := swagger.Sbt.All
  swagger.Sbt.packages := swagger.Sbt.WhitelistPrefixes(Seq("com.example.your.project.package")),
  swagger.Sbt.excludePropertyClassPrefixes := Set("play.api.libs.json")
) ++ com.hootsuite.sbt.swagger.Sbt.swaggerSettings
libraryDependencies ++= Seq(
  // API annotations
  "com.wordnik"          %  "swagger-annotations" % "1.3.10",
  // The following is only necessary if you're having trouble resolving dependencies
  "javax.ws.rs"          % "jsr311-api"           % "1.1.1")

// merge swaggerSettings into the project's settings
lazy val myProject = Project(
  id = "something-meaingful",
  base = file("."),
  settings = platformSettings ++
             dependencySettings ++
             swaggerSettings ++
             ...
)

Example

Check https://github.com/hootsuite/sbt-swagger/tree/master/sbt-swagger-example directory. This is a small complete sample application that uses sbt-swagger.

Usage

sbt swagger

Generates swagger JSON output in target/swagger/*.json files.

API Annotations

Swagger's @Api* annotations are the main markers and can be complemented with additional semantics with javax.ws.rs.* annotations. Neither Swagger nor JAX-RS alone offer a complete set of functionality.

This extractor favors Swagger's @Api* annotations over javax.ws.rs.* ones where there is an overlap: if a piece of information is specified using both Swagger @Api* and javax.ws.rs.* annotation, the generated models will use the information from Swagger's annotation.

// com.wordnik.swagger.annotations.*
// class level
@Api
@ApiModel
// method level
@ApiOperation
@ApiResponses
@ApiResponse
// parameter level
@ApiParam
@ApiModelProperty
// field level
@ApiModelProperty
// javax.ws.rs.*
// class level:
@Path
@Consumes
@Produces
// method level:
@GET
@POST
@HEAD
@PUT
@DELETE
@Path
@Consumes
@Produces
// parameter level:
@PathParam
@HeaderParam
@QueryParam
@FormParam
@CookieParam
@DefaultValue

Examples

See TweetEndpoints :

@Path("/tweet")
@Api(value = "/tweet", position = 2, description = "View a tweet, list tweets, or post a tweet. Everything about Tweets!")
@Produces(Array("text/plain"))
trait TweetEndpoints  {

  @GET // one operation to rule them all #annotations
  @Path("/get/{userId}")
  @ApiOperation(
    value = "Get a tweet of given tweetId",
    notes = "It actually returns a fixed value just for now.",
    position = 8, // relative to others in the generated output
    response = classOf[Tweet],
    responseContainer = "List")
  @Produces(Array("application/json"))
  @ApiResponses(value = Array(
    new ApiResponse(code = 403, message = "no tweets for you!"),
    new ApiResponse(code = 404, message = "invalid user id"),
    new ApiResponse(code = 405, message = "GETs only")
  ))
  def get( @PathParam("userId") userId: String,
           @HeaderParam("fromDate") fromDate: String,
           @QueryParam("toDate") toDate: String,
           @FormParam("what") what: String,
           @CookieParam("who") @DefaultValue("me") who: String
         ): Future[List[Tweet]] = {
    Future.successful(List(Tweet(123123, s"$userId", "an awesome message"), Tweet(79878999, s"$userId", "another one")))
  }
}

case class Tweet(id: Int, userId: String, body: String)

Known issues and workarounds

  • Repeated invocations of the swagger command from the sbt console won't see updated/recompiled classes. Need to quit/reload sbt, or run sbt swagger from the shell each time. TODO: see issue #6 https://github.com/hootsuite/sbt-swagger/issues/6.

  • All method parameters are automatically considered API parameters - no option to hide some that are not part of the API.

Workaround: extract a method that represents a 1:1 mapping with the actual exposed API in terms of parameters and input-output models, and wrap it as necessary for pre- (validation, etc) and post-processing.

  • Map data types are not supported by the extractor.

Workaround: use List/Seq (and case classes).

  • Options are supported in models (in most cases), but not as API parameters.

Workaround: perform validation outside of the designated API method, pass non-optional values to the API (and annotate with @ApiParam(required = true|false) when necessary).

  • By default API parameters (all except path params) and model fields are considered optional. This should be made configurable by the plugin to allow projects to minimize the amount of required = true|false explicit annotations.

Workaround: use explicit annotations:

// for API parameters
@ApiParam(required = true)
// for model fields
@ApiModelProperty(required = true)
// for case class fields
@(ApiModelProperty @scala.annotation.meta.field)(required = true)
  • Data type of API parameters and model fields is not extracted correctly for some combinations.

Workaround: use @ApiModelProperty(dataType = "...") to explicitly set it for the extractor. @ApiModelProperty works and is picked up from annotated API params as well, not just model properties.

// "list", "set" and "array" all map to the same "array[T]" in the generated apidocs JSON
// List container ignored for API parameters (but not for model properties)
@ApiModelProperty(dataType = "list[CreatePostRequest]")
postRequests: List[CreatePostRequest]
// extracted as Object otherwise
@(ApiModelProperty @field)(dataType = "set[integer]")
errors: Option[Set[Int]] = None
@(ApiModelProperty @field)(dataType = "integer")
minAge: Option[Int] = None,
  • Enum values need to be explicitly set (compiler enforces that annotation values are constants).
@(ApiModelProperty @field)(allowableValues = "Public,DirectConnections,DirectAndSecondaryConnections", dataType = "enum")
globalRestriction: Option[GlobalRestriction.Value] = None,

Swagger API annotations quirks

(why we need to compensate with javax.ws.rs)

@ApiOperation :
  - does not allow to specify a (sub)path - would require one class per endpoint-path
  - javax.ws.rs.@Path does, can annotate methods

@ApiParam :
  - does not allow to specify the source of a parameter (header/path/query/form/cookie)
  - allows a few extra attributes to be specified:
    - required         (defaults to false)
    - dataType         useful when extractor didn't figure it out
    - allowMultiple    (defaults to false - except for path parameters)
    - allowableValues  useful for enum types (defaults to "")
    - description

@ApiModel :
 - marks explicitly a class as a model
   (though all models should be recursively discovered from API inputs/outputs)

@ApiModelProperty :
 - marks explicitly a method or field as a model property
 - 'hidden = true' useful for hiding implicits (e.g. JSON formatters)
   that are not part of the API and should be filtered out by the extractor

Contributing

Submit a bug report and pull request in GitHub.

Maintainers

About

SBT plugin for extracting Swagger & JAX-RS (jsr311) annotations from compiled classes into Swagger API models, which are then serialized to JSON for consumption with swagger-ui.

Resources

License

Releases

No releases published

Packages

No packages published

Languages