Home | Source for Kata

Embedding live service contracts documentation with Swagger

Swagger is a REST specification that allows you to generate interactive API documentation.

  1. Swagger generates an interactive API console to quickly learn and try the API.

  2. Swagger generates the client SDK code needed for implementations on various platforms.

  3. Swagger allows to generate client code on a lot of different platforms.

  4. Swagger has a strong community with helpful contributors.

In this exercise you are going to create simple REST web service using Swagger API Documentation.

The API documentation generated by the Swagger file is minimal. It shows the resources, parameters, requests, and responses. However, it’s not going to provide any other detail about how your API works.

Step-by-step instructions to bootstrap the project

Follow Kata1 guide to setup a basic service: Creating Basic Web Service

Swagger Implementation

Add dependency jars

Add the following code to the build script:

build.gradle
compile("io.springfox:springfox-swagger2:2.2.2")
compile("io.springfox:springfox-swagger-ui:2.2.2")

This script will add swagger dependency jars into the application.

Bean configuration

The project contains an entry point in the Application.java file:

Application.java
@SpringBootApplication
@EnableSwagger2 (1)
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

  @Bean (2)
  public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select().paths(regex("/accountsvc.*")).build();
  }

  private ApiInfo apiInfo() {
    return new ApiInfoBuilder().title("Spring REST Sample with Swagger").description("Spring REST Sample with Swagger")
        .termsOfServiceUrl("http://dockerhost:8100/termsofservice").contact("service@ericsson.com")
        .license("Apache License Version 2.0").licenseUrl("http://dockerhost:8100/LICENSE").version("2.0").build();
  }
}
1 We have added @EnableSwagger2 annotation to enable this application to use Swagger.
2 The api() method creates a bean which contains the general information about the API and an inventory of the available resources.

We’re going to rename HomeController we created in Kata1 guide to AccountController and add a get API and describe it with swagger annotation.

Swagger annotation

AccountController.java
@Api(value = "accountsvc", description = "Endpoint for account management") (1)
@RequestMapping(value = "/accountsvc")
class AccountController {
  @ApiOperation(value = "list accounts", notes = "Lists all accounts. Account informtion contains id, name and email.",
          produces = "application/json") (2)
  @ApiResponses(value = { @ApiResponse(code = 200, message = "Success", response = Account.class) }) (3)
  @RequestMapping(method = GET, path = "/accounts", produces = "application/json")
  ResponseEntity<List<Account>> get() {
    return new ResponseEntity<List<Account>>(account, OK);
  }

  @ApiOperation(value = "look up  account by id", notes = "Looks up account by id.")
  @ApiImplicitParams({ @ApiImplicitParam(name = "id", value = "Account id", required = true, dataType = "int",
          paramType = "path", defaultValue = "1") }) (4)
  @ApiResponses(value = { @ApiResponse(code = 200, message = "Success", response = Account.class) })
  @RequestMapping(method = GET, path = "/accounts/{id}", produces = "application/json")
  ResponseEntity<Account> getById(@PathVariable int id) throws Exception {
    return new ResponseEntity<Account>(account.get(id - 1), OK);
  }

  @ApiOperation(value = "add new account", nickname = "Add a new account", response = Void.class)
  @ApiResponses(value = { @ApiResponse(code = 201, message = "Success", response = Account.class) })
  @RequestMapping(value = "/accounts", method = POST, produces = "application/json", consumes = "application/json")
  ResponseEntity<Account> post(@ApiParam(value = "Created Account object")
                               @RequestBody Account body) { (5)
    account.add(body);
    return new ResponseEntity<Account>(body, CREATED);
  }

  @ApiOperation(value = "update an Account", nickname = "Updates existing account with new name and/or email",
          response = Void.class)
  @ApiResponses(value = { @ApiResponse(code = 201, message = "Success", response = Account.class) })
  @RequestMapping(value = "/accounts/{id}", method = PUT, produces = "application/json", consumes = "application/json")
  ResponseEntity<Account> put(@PathVariable int id,
      @ApiParam(value = "Updated Account object") @RequestBody Account body) {
    account.remove(id - 1);
    account.add(body);
    return new ResponseEntity<Account>(body, OK);
  }

  @ApiOperation(value = "delete account", notes = "Deletes account by id")
  @RequestMapping(value = "/accounts/{id}", method = DELETE)
  @ApiResponses(value = { @ApiResponse(code = 400, message = "Account not found", response = void.class) })
  void delete(@PathVariable int id) throws Exception {
    if (id <= account.size()) {
      account.remove(id - 1);
    } else {
      throw new AccountException("Account not found");
    }
  }
@ApiModel( value = "Account", description = "Account resource representation" ) (6)
class Account {
  @ApiModelProperty( value = "Account id", required = true )
  private int id;

  @ApiModelProperty( value = "Account name", required = true )
  private String name;

  @ApiModelProperty( value = "Account email", required = false )
  private String email;

  public Account() {
  }
1 @Api annotation describes a top-level api. In order to make Swagger aware of your endpoint, you need to annotate your class with @Api annotation.
2 @ApiOperation annotation provides detailed description of what a certain method does.
3 @ApiResponses annotation represents a type of response from a server. This can be used to describe both success codes as well as errors. If your Api has different response classes, you can describe them here by associating a response class with a response code.
4 @ApiImplicitParams annotation represents a single parameter in an API Operation.This allows you to manually define a parameter in a fine-tuned manner.
5 @ApiParam annotation represents a single parameter in an Api Operation. A parameter is an input to the operation.When working with path or query parameter, you should always provide clarification of what this parameter represents.
6 @ApiModel annotation models classes to provide the model schema, which helps in understanding/documenting the request response structure using the specific annotation using @ApiModel annotations.
application.yml
server:
  port: 8100

endpoints:
  health:
    sensitive: false
  shutdown:
    enabled: true

Build and launch

Build the service:

$ cd accountsvc
$ gradle build

Note that this command will build the code, generate the jar and run all the necessary tests.

Building a Docker image

Build the project and generate Dockerfile by run the following command:

$ gradle prepDocker
...
:prepDocker
Run command: docker build -t msvcdojo/accounts-service:0.0.1 build/docker

BUILD SUCCESSFUL

Everything was prepared for you, so you can just copy the suggested command from the console and run it:

$ docker build -t msvcdojo/accounts-service:0.0.1 build/docker
Sending build context to Docker daemon 12.95 MB
Step 1 : FROM java:8
Step 2 : MAINTAINER Igor Moochnick "igor@igorshare.com"
Step 3 : VOLUME /tmp
Step 4 : EXPOSE 8100
Step 5 : ADD accountsvc-0.0.1.jar accountsvc.jar
Step 6 : ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -jar /accounts-service.jar
Successfully built cb55f79088db

This has created a Dockerimage with the tag msvcdojo/accounts-service:0.0.1. Now let’s see it in the Docker repository:

$ docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
msvcdojo/accounts-service   0.0.1               fdb0ace78a40        2 minutes ago       660.6 MB

Start container

Let’s start the container and map the service port to the external world by running command:

$ docker run -it --name=accountsvc -p 8100:8100 msvcdojo/accounts-service:0.0.1

The following output you’ll see on the screen.

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.3.2.RELEASE)

2016-02-26 20:35:16.875  INFO 1 --- [           main] svcdojo.Application                      : Starting Application on 013471f370b4 with PID 1 (/accountsvc.jar started by root in /)
2016-02-26 20:35:16.882  INFO 1 --- [           main] svcdojo.Application                      : No active profile set, falling back to default profiles: default
2016-02-26 20:35:17.065  INFO 1 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@2b3bb14b: startup date [Fri Feb 26 20:35:17 UTC 2016]; root of context hierarchy
2016-02-26 20:35:19.908  INFO 1 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'beanNameViewResolver' with a different definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]]
2016-02-26 20:35:20.252  INFO 1 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'managementServletContext' with a different definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.actuate.autoconfigure.EndpointWebMvcHypermediaManagementContextConfiguration; factoryMethodName=managementServletContext; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/actuate/autoconfigure/EndpointWebMvcHypermediaManagementContextConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration; factoryMethodName=managementServletContext; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.class]]
2016-02-26 20:35:22.289  INFO 1 --- [           main] org.eclipse.jetty.util.log               : Logging initialized @8055ms
2016-02-26 20:35:22.437  INFO 1 --- [           main] e.j.JettyEmbeddedServletContainerFactory : Server initialized with port: 8100
2016-02-26 20:35:22.448  INFO 1 --- [           main] org.eclipse.jetty.server.Server          : jetty-9.2.14.v20151106
2016-02-26 20:35:22.539  INFO 1 --- [           main] application                              : Initializing Spring embedded WebApplicationContext
..
2016-02-26 20:35:29.295  INFO 1 --- [           main] s.w.ClassOrApiAnnotationResourceGrouping : Group for method put was accountsvc
2016-02-26 20:35:29.313  INFO 1 --- [           main] s.w.ClassOrApiAnnotationResourceGrouping : Group for method put was accountsvc
2016-02-26 20:35:29.489  INFO 1 --- [           main] application                              : Initializing Spring FrameworkServlet 'dispatcherServlet'
2016-02-26 20:35:29.489  INFO 1 --- [           main] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2016-02-26 20:35:29.554  INFO 1 --- [           main] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 62 ms
(1)
2016-02-26 20:35:29.602  INFO 1 --- [           main] o.eclipse.jetty.server.ServerConnector   : Started ServerConnector@1d857713{HTTP/1.1}{0.0.0.0:8100}
2016-02-26 20:35:29.607  INFO 1 --- [           main] .s.b.c.e.j.JettyEmbeddedServletContainer : Jetty started on port(s) 8100 (http/1.1)
2016-02-26 20:35:29.638  INFO 1 --- [           main] svcdojo.Application                      : Started Application in 14.14 seconds (JVM running for 15.407)
1 Listening port of the web service

Play time

Swagger Endpoint Resource Listing

The Resource Listing serves as the root document for the API description. It contains general information about the API and an inventory of the available resources.

By default, this document SHOULD be served at the /api-docs path.

$ curl http://localhost:8100/v2/api-docs

The output of this CURL command will look like this:

{
    "swagger": "2.0",
    "info": {
    "description": "Spring REST Sample with Swagger",
    "version": "2.0",
    "title": "Spring REST Sample with Swagger",
        ...
        },
"host": "localhost:8100",
"basePath": "/",
"tags": [{
    "name": "accountsvc",
    "description": "Endpoint for account management"
}
"paths": {
    "/accountsvc/delete/{id}": {
                "delete": {
                        "tags": ["accountsvc"],
                        "summary": "delete Account By Id",
                        "description": "Deletes an account by passing accountId",
                        "operationId": "deleteUsingDELETE",
                        "consumes": ["application/json"],
                        "produces": ["*/*"],
                        ...
                        }
                }
        }
}

Running it with Swagger UI

Swagger UI takes Swagger specification files, presents them visually and allows you to execute operations.

$ curl http://localhost:8100/swagger-ui.html

The output of this CURL command will look like this:

swagger ui