Swagger Archives - Piotr's TechBlog https://piotrminkowski.com/tag/swagger/ Java, Spring, Kotlin, microservices, Kubernetes, containers Tue, 15 Dec 2020 09:11:37 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.1 https://i0.wp.com/piotrminkowski.com/wp-content/uploads/2020/08/cropped-me-2-tr-x-1.png?fit=32%2C32&ssl=1 Swagger Archives - Piotr's TechBlog https://piotrminkowski.com/tag/swagger/ 32 32 181738725 Guide to Quarkus with Kotlin https://piotrminkowski.com/2020/08/09/guide-to-quarkus-with-kotlin/ https://piotrminkowski.com/2020/08/09/guide-to-quarkus-with-kotlin/#comments Sun, 09 Aug 2020 08:28:56 +0000 http://piotrminkowski.com/?p=8353 Quarkus is a lightweight Java framework developed by RedHat. It is dedicated for cloud-native applications that require a small memory footprint and a fast startup time. Its programming model is built on top of proven standards like Eclipse MicroProfile. Recently it is growing in popularity. It may be considered as an alternative to Spring Boot […]

The post Guide to Quarkus with Kotlin appeared first on Piotr's TechBlog.

]]>
Quarkus is a lightweight Java framework developed by RedHat. It is dedicated for cloud-native applications that require a small memory footprint and a fast startup time. Its programming model is built on top of proven standards like Eclipse MicroProfile. Recently it is growing in popularity. It may be considered as an alternative to Spring Boot framework, especially if you are running your applications on Kubernetes or OpenShift.
In this guide, you will learn how to implement a simple Quarkus Kotlin application, that exposes REST endpoints and connects to a database. We will discuss the following topics:

  • Implementation of REST endpoints
  • Integration with H2 with Hibernate and Panache project
  • Generating and exposing OpenAPI/Swagger documentation
  • Exposing health checks
  • Exposing basic metrics
  • Logging request and response
  • Testing REST endpoints with RestAssured library

github-logo Source code

The source code with the sample Quarkus Kotlin applications is available on GitHub. First, you need to clone the following repository: https://github.com/piomin/sample-quarkus-applications.git. Then, you need to go to the employee-service directory.

1. Enable Quarkus Kotlin support

To enable Kotlin support in Quarkus we need to include quarkus-kotlin module. We also have to add kotlin-stdlib library.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-kotlin</artifactId>
</dependency>
<dependency>
   <groupId>org.jetbrains.kotlin</groupId>
   <artifactId>kotlin-stdlib</artifactId>
</dependency>

In the next step we need to include kotlin-maven-plugin. Besides standard configuration, we have to use all-open Kotlin compiler plugin. The all-open compiler plugin makes classes annotated with a specific annotation and their members open without the explicit open keyword. Since classes annotated with @Path, @ApplicationScoped, or @QuarkusTest should not be final, we need to add all those annotations to the pluginOptions section.

<build>
   <sourceDirectory>src/main/kotlin</sourceDirectory>
   <testSourceDirectory>src/test/kotlin</testSourceDirectory>
   <plugins>
      <plugin>
         <groupId>io.quarkus</groupId>
         <artifactId>quarkus-maven-plugin</artifactId>
         <version>${quarkus-plugin.version}</version>
         <executions>
            <execution>
               <goals>
                  <goal>build</goal>
               </goals>
            </execution>
         </executions>
      </plugin>
      <plugin>
         <groupId>org.jetbrains.kotlin</groupId>
         <artifactId>kotlin-maven-plugin</artifactId>
         <version>${kotlin.version}</version>
         <executions>
            <execution>
               <id>compile</id>
               <goals>
                  <goal>compile</goal>
               </goals>
            </execution>
            <execution>
               <id>test-compile</id>
               <goals>
                  <goal>test-compile</goal>
               </goals>
            </execution>
         </executions>
         <dependencies>
            <dependency>
               <groupId>org.jetbrains.kotlin</groupId>
               <artifactId>kotlin-maven-allopen</artifactId>
               <version>${kotlin.version}</version>
            </dependency>
         </dependencies>
         <configuration>
            <javaParameters>true</javaParameters>
            <jvmTarget>11</jvmTarget>
            <compilerPlugins>
               <plugin>all-open</plugin>
            </compilerPlugins>
            <pluginOptions>
               <option>all-open:annotation=javax.ws.rs.Path</option>
               <option>all-open:annotation=javax.enterprise.context.ApplicationScoped</option>
               <option>all-open:annotation=io.quarkus.test.junit.QuarkusTest</option>
            </pluginOptions>
         </configuration>
      </plugin>
   </plugins>
</build>

2. Implement REST endpoint

In Quarkus support for REST is built on top of Resteasy and JAX-RS libraries. You can choose between two available extentions for JSON serialization/deserialization: JsonB and Jackson. Since I decided to use Jackson I need to include quarkus-resteasy-jackson dependency. It also includes quarkus-resteasy module.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>

We mostly use JAX-RS annotations for mapping controller methods and fields into HTTP endpoints. We may also use Resteasy annotations like @PathParam, that does not require to set any fields. In order to interact with database, we are injecting a repository bean.

@Path("/employees")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
class EmployeeResource(val repository: EmployeeRepository) {

    @POST
    @Transactional
    fun add(employee: Employee): Response {
        repository.persist(employee)
        return Response.ok(employee).status(201).build()
    }

    @DELETE
    @Path("/{id}")
    @Transactional
    fun delete(@PathParam id: Long) {
        repository.deleteById(id)
    }

    @GET
    fun findAll(): List<Employee> = repository.listAll()

    @GET
    @Path("/{id}")
    fun findById(@PathParam id: Long): Employee? = repository.findById(id)

    @GET
    @Path("/first-name/{firstName}/last-name/{lastName}")
    fun findByFirstNameAndLastName(@PathParam firstName: String, @PathParam lastName: String): List<Employee>
            = repository.findByFirstNameAndLastName(firstName, lastName)

    @GET
    @Path("/salary/{salary}")
    fun findBySalary(@PathParam salary: Int): List<Employee> = repository.findBySalary(salary)

    @GET
    @Path("/salary-greater-than/{salary}")
    fun findBySalaryGreaterThan(@PathParam salary: Int): List<Employee>
            = repository.findBySalaryGreaterThan(salary)

}

3. Integration with database

Quarkus provides Panache JPA extension to simplify work with Hibernate ORM. It also provides driver extensions for the most popular SQL databases like Postgresql, MySQL, or H2. To enable both these features for H2 in-memory database we need to include the following dependencies.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-hibernate-orm-panache-kotlin</artifactId>
</dependency>
<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-jdbc-h2</artifactId>
</dependency>

We should also configure connection settings inside application.properties file.


quarkus.datasource.db-kind=h2
quarkus.datasource.username=sa
quarkus.datasource.password=password
quarkus.datasource.jdbc.url=jdbc:h2:mem:testdb

Panache extension allows to use well-known repository pattern. To use it we should first define entity that extends PanacheEntity class.

@Entity
data class Employee(var firstName: String = "",
                    var lastName: String = "",
                    var position: String = "",
                    var salary: Int = 0,
                    var organizationId: Int? = null,
                    var departmentId: Int? = null): PanacheEntity()

In the next step, we are defining repository bean that implements PanacheRepository interface. It comes with some basic methods like persist, deleteById or listAll. We may also use those basic methods to implement more advanced queries or operations.

@ApplicationScoped
class EmployeeRepository: PanacheRepository<Employee> {
    fun findByFirstNameAndLastName(firstName: String, lastName: String): List<Employee> =
           list("firstName = ?1 and lastName = ?2", firstName, lastName)

    fun findBySalary(salary: Int): List<Employee> = list("salary", salary)

    fun findBySalaryGreaterThan(salary: Int): List<Employee> = list("salary > ?1", salary)
}

4. Enable OpenAPI documentation for Quarkus Kotlin

It is possible to generate OpenAPI v3 specification automatically. To do that we need to include SmallRye OpenAPI extension. The specification is available under path /openapi.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>

We may provide some additional informations to the generated OpenAPI specification like description or version number. To do that we need to create application class that extends javax.ws.rs.core.Application, and annotate it with @OpenAPIDefinition, as shown below.

@OpenAPIDefinition(info = Info(title = "Employee API", version = "1.0"))
class EmployeeApplication: Application()

Usually, we want to expose OpenAPI specification using Swagger UI. Such a feature may be enabled using configuration property quarkus.swagger-ui.always-include=true.

quarkus-swagger

5. Health checks

We may expose built-in health checks implementation by including SmallRye Health extension.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-smallrye-health</artifactId>
</dependency>

It exposes three REST endpoints compliant with Kubernetes health checks pattern:

  • /health/live – The application is up and running (Kubernetes liveness probe).
  • /health/ready – The application is ready to serve requests (Kubernetes readiness probe).
  • /health – Accumulating all health check procedures in the application.

The default implementation of readiness health check verifies database connection status, while liveness just determines if the application is running.

quarkus-readiness

6. Expose metrics

We may enable metrics collection by adding SmallRye Metrics extension. By default, it collects only JVM, CPU and processes metrics.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-smallrye-metrics</artifactId>
</dependency>

We may force the library to collect metrics from JAX-RS endpoints. To do that we need to annotate the selected endpoints with @Timed.

@POST
@Transactional
@Timed(name = "add", unit = MetricUnits.MILLISECONDS)
fun add(employee: Employee): Response {
   repository.persist(employee)
   return Response.ok(employee).status(201).build()
}

Now, we may call endpoint POST /employee 100 times in a row. Here’s the list of metrics generated for the single endpoint. If you would like to ensure compatibility with Micrometer metrics format you need to set the following configuration property: quarkus.smallrye-metrics.micrometer.compatibility=true.

quarkus-metrics

7. Logging request and response for Quarkus Kotlin application

There is no built-in mechanism for logging HTTP requests and responses. We may implement custom logging filter that implements interfaces ContainerRequestFilter, and ContainerResponseFilter.

@Provider
class LoggingFilter: ContainerRequestFilter, ContainerResponseFilter {

    private val logger: Logger = LoggerFactory.getLogger(LoggingFilter::class.java)

    @Context
    lateinit var info: UriInfo
    @Context
    lateinit var request: HttpServerRequest

    override fun filter(ctx: ContainerRequestContext) {
        logger.info("Request {} {}", ctx.method, info.path)
    }

    override fun filter(r: ContainerRequestContext, ctx: ContainerResponseContext) {
        logger.info("Response {} {}: {}", r.method, info.path, ctx.status)
    }
    
}

8. Testing

The module quarkus-junit5 is required for testing, as it provides the @QuarkusTest annotation that controls the testing framework. The extension rest-assured is not required, but is a convenient way to test HTTP endpoints.

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-junit5</artifactId>
   <scope>test</scope>
</dependency>
<dependency>
   <groupId>io.rest-assured</groupId>
   <artifactId>kotlin-extensions</artifactId>
   <scope>test</scope>
</dependency>

We are adding new Employee in the first test. Then the second test verifies if there is a single Employee stored inside in-memory database.

@QuarkusTest
class EmployeeResourceTest {

    @Test
    fun testAddEmployee() {
        val emp = Employee(firstName = "John", lastName = "Smith", position = "Developer", salary = 20000)
        given().body(emp).contentType(ContentType.JSON)
                .post("/employees")
                .then()
                .statusCode(201)
    }

    @Test
    fun testGetAll() {
        given().get("/employees")
                .then()
                .statusCode(200)
                .assertThat().body("size()", `is`(1))
    }

}

Conclusion

In this guide, I showed you how to build a Quarkus Kotlin application that connects to a database and follows some best practices like exposing health checks, metrics, or logging incoming requests and outgoing responses. The last step is to run our sample application. To do that in development mode we just need to execute command mvn compile quarkus:dev. Here’s my start screen. You can see there, for example, the list of included Quarkus modules.

quarkus-run

If you are interested in Quarkus framework the next useful article for you is Guide to Quarkus on Kubernetes.

The post Guide to Quarkus with Kotlin appeared first on Piotr's TechBlog.

]]>
https://piotrminkowski.com/2020/08/09/guide-to-quarkus-with-kotlin/feed/ 5 8353
Microservices API Documentation with Springdoc OpenAPI https://piotrminkowski.com/2020/02/20/microservices-api-documentation-with-springdoc-openapi/ https://piotrminkowski.com/2020/02/20/microservices-api-documentation-with-springdoc-openapi/#comments Thu, 20 Feb 2020 12:52:40 +0000 http://piotrminkowski.com/?p=7756 I have already written about documentation for microservices more than two years ago in my article Microservices API Documentation with Swagger2. In that case, I used project SpringFox for auto-generating Swagger documentation for Spring Boot applications. Since that time the SpringFox library has not been actively developed by the maintainers – the latest version has […]

The post Microservices API Documentation with Springdoc OpenAPI appeared first on Piotr's TechBlog.

]]>
I have already written about documentation for microservices more than two years ago in my article Microservices API Documentation with Swagger2. In that case, I used project SpringFox for auto-generating Swagger documentation for Spring Boot applications. Since that time the SpringFox library has not been actively developed by the maintainers – the latest version has been released in June 2018. Currently, the most important problems with this library are a lack of support for OpenAPI in the newest version 3, and for Spring reactive APIs built using WebFlux. All these features are implemented by Springdoc OpenAPI library. Therefore, it may threaten as a replacement for SpringFox as Swagger and OpenAPI 3 generation tool for Spring Boot applications.

Example

As a code example in this article we will use a typical microservices architecture built with Spring Cloud. It consists of Spring Cloud Config Server, Eureka discovery, and Spring Cloud Gateway as API gateway. We also have three microservices, which expose the REST API and are hidden behind the gateway for an external client. Each of them is exposing OpenAPI documentation that may be accessed on the gateway using Swagger UI. The repository with source code is available on GitHub: https://github.com/piomin/sample-spring-microservices-new.git. This repository has been used as an example in another article, so it contains code not only for Springdoc library demo. The following picture shows the architecture of our system.

microservices-api-documentation-springdoc-openapi.png

Implementating microservice with Springdoc OpenAPI

The first good news related to the Springdoc OpenAPI library is that it may exist together with the SpringFox library without any conflicts. This may simplify your migration into a new tool if anybody is using your Swagger documentation, for example for code generation of contract tests. To enable Springdoc for standard Spring MVC based application you need to include the following dependency into Maven pom.xml.

<dependency>
   <groupId>org.springdoc</groupId>
   <artifactId>springdoc-openapi-webmvc-core</artifactId>
   <version>1.2.32</version>
</dependency>

Each of our Spring Boot microservices is built on top of Spring MVC and provides endpoints for standard synchronous REST communication. However, the API gateway, which is built on top of Spring Cloud Gateway uses Netty as an embedded server and is based on reactive Spring WebFlux. It is also providing Swagger UI for accessing documentation exposed by all the microservices, so it must include a library that enables UI. The following two libraries must be included to enable Springdoc support for a reactive application based on Spring WebFlux.

<dependency>
   <groupId>org.springdoc</groupId>
   <artifactId>springdoc-openapi-webflux-core</artifactId>
   <version>1.2.31</version>
</dependency>
<dependency>
   <groupId>org.springdoc</groupId>
   <artifactId>springdoc-openapi-webflux-ui</artifactId>
   <version>1.2.31</version>
</dependency>

We can customize the default behavior of this library by setting properties in the Spring Boot configuration file or using @Beans. For example, we don’t want to generate OpenAPI manifests for all HTTP endpoints exposed by the application like Spring specific endpoints, so we may define a base package property for scanning as shown below. In our source code example each application YAML configuration file is located inside the config-service module.


springdoc:
  packagesToScan: pl.piomin.services.department

Here’s the main class of employee-service. We use @OpenAPIDefinition annotation to define a description for the application displayed on the Swagger site. As you see we can still have SpringFox enabled with @EnableSwagger2.

@SpringBootApplication
@EnableDiscoveryClient
@EnableSwagger2
@OpenAPIDefinition(info =
   @Info(title = "Employee API", version = "1.0", description = "Documentation Employee API v1.0")
)
public class EmployeeApplication {

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

}

OpenAPI on Spring Cloud Gateway

Once you start every microservice it will expose endpoint /v3/api-docs. We can customize that context by using property springdoc.api-docs.path in Spring configuration file. Since it is not required we may proceed to the implementation on the Spring Cloud Gateway. Springdoc doesn’t provide a similar class to SpringFox SwaggerResource, which has been used for exposing multiple APIs from different microservices in the previous article. Fortunately, there is a grouping mechanism that allows splitting OpenAPI definitions into different groups with a given name. To use it we need to declare a list of GroupOpenAPI beans.
Here’s the fragment of code inside gateway-service responsible for creating a list of OpenAPI resources handled by the gateway. First, we get all defined routes for services using RouteDefinitionLocator bean. Then we are fetching the id of each route and set it as a group name. As a result we have multiple OpenAPI resources under path /v3/api-docs/{SERVICE_NAME}, for example /v3/api-docs/employee.

@Autowired
RouteDefinitionLocator locator;

@Bean
public List<GroupedOpenApi> apis() {
   List<GroupedOpenApi> groups = new ArrayList<>();
   List<RouteDefinition> definitions = locator.getRouteDefinitions().collectList().block();
   definitions.stream().filter(routeDefinition -> routeDefinition.getId().matches(".*-service")).forEach(routeDefinition -> {
      String name = routeDefinition.getId().replaceAll("-service", "");
      GroupedOpenApi.builder().pathsToMatch("/" + name + "/**").setGroup(name).build();
   });
   return groups;
}

The API path like /v3/api-docs/{SERVICE_NAME} is not exactly what we want to achieve, because our routing to the downstream services is based on the service name fetched from discovery. So if you call address like http://localhost:8060/employee/** it is automatically load balanced between all registered instances of employee-service. Here’s the routes definition in gateway-service configuration.

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
      - id: employee-service
        uri: lb://employee-service
        predicates:
        - Path=/employee/**
        filters:
        - RewritePath=/employee/(?<path>.*), /$\{path}
      - id: department-service
        uri: lb://department-service
        predicates:
        - Path=/department/**
        filters:
        - RewritePath=/department/(?<path>.*), /$\{path}
      - id: organization-service
        uri: lb://organization-service
        predicates:
        - Path=/organization/**
        filters:
        - RewritePath=/organization/(?<path>.*), /$\{path}

Since Springdoc doesn’t allow us to customize the default behavior of the grouping mechanism to change the generated paths, we need to provide some workaround. My proposition is just to add a new route definition inside gateway configuration dedicated to Open API path handling. It rewrites path /v3/api-docs/{SERVICE_NAME} into /{SERVICE_NAME}/v3/api-docs, which is handled by the another routes responsible for interacting with Eureka discovery.

  - id: openapi
   uri: http://localhost:${server.port}
   predicates:
   - Path=/v3/api-docs/**
   filters:
   - RewritePath=/v3/api-docs/(?<path>.*), /$\{path}/v3/api-docs

Testing

To test our sample simple we need to run all microservice, config server, discovery and gateway. While microservices are available under a dynamically generated port, config server is available under 8888, discovery under 8061, and gateway under 8060. We can access each microservice by calling http://localhost:8060/{SERVICE_PATH}/**, for example http://localhost:8060/employee/**. The Swagger UI is available under address http://localhost:8060/swagger-ui.html. Before let’s take a look on Eureka after running all required Spring Boot applications.

microservice-api-documentation-with-springdoc-openapi

After accessing Swagger UI exposed on the gateway you may see that we can choose between all three microservices registered in the discovery. This is exactly what we wanted to achieve.

microservice-api-documentation-with-springdoc-openapi-ui

Conclusion

Springdoc OpenAPI is compatible with OpenAPI 3, and supports Spring WebFlux, while SpringFox is not. Therefore, it seems that the choice is obvious especially if you are using reactive APIs or Spring Cloud Gateway. In this article I demonstrated you how to use Springdoc in microservices architecture with a gateway pattern.

The post Microservices API Documentation with Springdoc OpenAPI appeared first on Piotr's TechBlog.

]]>
https://piotrminkowski.com/2020/02/20/microservices-api-documentation-with-springdoc-openapi/feed/ 41 7756
Kotlin Microservice with Spring Boot https://piotrminkowski.com/2019/01/15/kotlin-microservice-with-spring-boot/ https://piotrminkowski.com/2019/01/15/kotlin-microservice-with-spring-boot/#respond Tue, 15 Jan 2019 10:34:08 +0000 https://piotrminkowski.wordpress.com/?p=6960 In this tutorial I will show you step-by-step how to implement Spring Boot Kotlin microservices. You may find many examples of microservices built with Spring Boot on my blog, but the most of them is written in Java. With the rise in popularity of Kotlin language it is more often used with Spring Boot for […]

The post Kotlin Microservice with Spring Boot appeared first on Piotr's TechBlog.

]]>
In this tutorial I will show you step-by-step how to implement Spring Boot Kotlin microservices. You may find many examples of microservices built with Spring Boot on my blog, but the most of them is written in Java. With the rise in popularity of Kotlin language it is more often used with Spring Boot for building backend services. Starting with version 5 Spring Framework has introduced first-class support for Kotlin. In this article I’m going to show you an example of microservice build with Kotlin and Spring Boot 2. I’ll describe some interesting features of Spring Boot, which can be treated as a set of good practices when building backend, REST-based microservices.

1. Spring Boot Kotlin configuration and dependencies

To use Kotlin in your Maven project you have to include plugin kotlin-maven-plugin, and /src/main/kotlin, /src/test/kotlin directories to the build configuration. We will also set the -Xjsr305 compiler flag to strict. This option is responsible for checking support for JSR-305 annotations (for example @NotNull annotation).

<build>
   <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
   <testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
   <plugins>
      <plugin>
         <groupId>org.jetbrains.kotlin</groupId>
         <artifactId>kotlin-maven-plugin</artifactId>
         <configuration>
            <args>
               <arg>-Xjsr305=strict</arg>
            </args>
            <compilerPlugins>
               <plugin>spring</plugin>
            </compilerPlugins>
         </configuration>
         <dependencies>
            <dependency>
               <groupId>org.jetbrains.kotlin</groupId>
               <artifactId>kotlin-maven-allopen</artifactId>
               <version>${kotlin.version}</version>
            </dependency>
         </dependencies>
      </plugin>
   </plugins>
</build>

We should also include some core Kotlin libraries like kotlin-stdlib-jdk8 and kotlin-reflect. They are provided by default for a Kotlin project on start.spring.io. For REST-based applications you will also need Jackson library used for JSON serialization/deserialization. Of course, we have to include Spring starters for Web application together with Actuator responsible for providing management endpoints.

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
   <groupId>com.fasterxml.jackson.module</groupId>
   <artifactId>jackson-module-kotlin</artifactId>
</dependency>
<dependency>
   <groupId>org.jetbrains.kotlin</groupId>
   <artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
   <groupId>org.jetbrains.kotlin</groupId>
   <artifactId>kotlin-stdlib-jdk8</artifactId>
</dependency>

We use the latest stable version of Spring Boot with Kotlin 1.2.71

<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>2.1.2.RELEASE</version>
</parent>
<properties>
   <java.version>1.8</java.version>
   <kotlin.version>1.2.71</kotlin.version>
</properties>

2. Building Spring Boot Kotlin microservice application

Let’s begin from the basics. If you are familiar with Spring Boot and Java, the biggest difference is in the main class declaration. You will call runApplication method outside the Spring Boot application class. The main class, the same as in Java, is annotated with @SpringBootApplication.

@SpringBootApplication
class SampleSpringKotlinMicroserviceApplication

fun main(args: Array<String>) {
    runApplication<SampleSpringKotlinMicroserviceApplication>(*args)
}

Our sample application is very simple. It exposes some REST endpoints providing CRUD operations for a model object. Even at this fragment of code illustrating controller implementation you can see some nice Kotlin features. We may use shortened function declaration with inferred return type. Annotation @PathVariable does not require any arguments. The input parameter name is considered to be the same as the variable name. Of course, we are using the same annotations as with Java. In Kotlin, every property declared as having a non-null type must be initialized in the constructor. So, if you are initializing it using dependency injection it has to be declared as lateinit. Here’s the implementation of PersonController.

@RestController
@RequestMapping("/persons")
class PersonController {

    @Autowired
    lateinit var repository: PersonRepository

    @GetMapping("/{id}")
    fun findById(@PathVariable id: Int): Person? = repository.findById(id)

    @GetMapping
    fun findAll(): List<Person> = repository.findAll()

    @PostMapping
    fun add(@RequestBody person: Person): Person = repository.save(person)

    @PutMapping
    fun update(@RequestBody person: Person): Person = repository.update(person)

    @DeleteMapping("/{id}")
    fun remove(@PathVariable id: Int): Boolean = repository.removeById(id)

}

Kotlin automatically generates getters and setters for class properties declared as var. Also if you declare a model as a data class it generates equals, hashCode, and toString methods. The declaration of our model class Person is very concise as shown below.


data class Person(var id: Int?, var name: String, var age: Int, var gender: Gender)

I have implemented my own in-memory repository class. I use Kotlin extensions for manipulating a list of elements. This built-in Kotlin feature is similar to Java streams, with the difference that you don’t have to perform any conversion between Collection and Stream.

@Repository
class PersonRepository {
    val persons: MutableList<Person> = ArrayList()

    fun findById(id: Int): Person? {
        return persons.singleOrNull { it.id == id }
    }

    fun findAll(): List<Person> {
        return persons
    }

    fun save(person: Person): Person {
        person.id = (persons.maxBy { it.id!! }?.id ?: 0) + 1
        persons.add(person)
        return person
    }

    fun update(person: Person): Person {
        val index = persons.indexOfFirst { it.id == person.id }
        if (index >= 0) {
            persons[index] = person
        }
        return person
    }

    fun removeById(id: Int): Boolean {
        return persons.removeIf { it.id == id }
    }

}

The sample application source code is available on GitHub in repository https://github.com/piomin/sample-spring-kotlin-microservice.git.

3. Enabling Spring Boot Actuator endpoints

Since we have already included Spring Boot starter with Actuator into the application code, we can take advantage of its production-ready features. Spring Boot Actuator gives you very powerful tools for monitoring and managing your apps. You can provide advanced healthchecks, info endpoints or send metrics to numerous monitoring systems like InfluxDB. After including Actuator artifacts the only thing we have to do is to enable all its endpoints for our application via HTTP.


management.endpoints.web.exposure.include: '*'

We can customize Actuator endpoints to provide more details about our app. A good practice is to expose information about version and git commit to info endpoint. As usual Spring Boot provides auto-configuration for such features, so the only thing we need to do is to include some Maven plugins to build configuration in pom.xml. The goal build-info set for spring-boot-maven-plugin forces it to generate a properties file with basic information about the version. The file is located in directory META-INF/build-info.properties. Plugin git-commit-id-plugin will generate git.properties file in the root directory.

<plugin>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-maven-plugin</artifactId>
   <executions>
      <execution>
         <goals>
            <goal>build-info</goal>
         </goals>
      </execution>
   </executions>
</plugin>
<plugin>
   <groupId>pl.project13.maven</groupId>
   <artifactId>git-commit-id-plugin</artifactId>
   <configuration>
      <failOnNoGitDirectory>false</failOnNoGitDirectory>
   </configuration>
</plugin>

Now you should just build your application using mvn clean install command and then run it.

$ java -jar target\sample-spring-kotlin-microservice-1.0-SNAPSHOT.jar

The info endpoint is available under address http://localhost:8080/actuator/info. It exposes all interesting information for us.

{
   "git":{
      "commit":{
         "time":"2019-01-14T16:20:31Z",
         "id":"f7cb437"
      },
      "branch":"master"
   },
   "build":{
      "version":"1.0-SNAPSHOT",
      "artifact":"sample-spring-kotlin-microservice",
      "name":"sample-spring-kotlin-microservice",
      "group":"pl.piomin.services",
      "time":"2019-01-15T09:18:48.836Z"
   }
}

4. Enabling Swagger API documentation

Build info and git properties may be easily injected into the application code. It can be useful in some cases. One of those cases is if you have enabled auto-generated API documentation. The most popular tool to use for it is Swagger. You can easily integrate Swagger2 with Spring Boot using the SpringFox Swagger project. First, you need to include the following dependencies to your pom.xml.

<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger2</artifactId>
   <version>2.9.2</version>
</dependency>
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger-ui</artifactId>
   <version>2.9.2</version>
</dependency>

Then, you should enable Swagger by annotating configuration class with @EnableSwagger2. Required information is available inside beans BuildProperties and GitProperties. We just have to inject them into Swagger configuration class as shown below. We set them as optional to prevent application startup failure in case they are not present on classpath.

@Configuration
@EnableSwagger2
class SwaggerConfig {

    @Autowired
    lateinit var build: Optional<BuildProperties>
    @Autowired
    lateinit var git: Optional<GitProperties>

    @Bean
    fun api(): Docket {
        var version = "1.0"
        if (build.isPresent && git.isPresent) {
            var buildInfo = build.get()
            var gitInfo = git.get()
            version = "${buildInfo.version}-${gitInfo.shortCommitId}-${gitInfo.branch}"
        }
        return Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo(version))
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths{ it.equals("/persons")}
                .build()
                .useDefaultResponseMessages(false)
                .forCodeGeneration(true)
    }

    @Bean
    fun uiConfig(): UiConfiguration {
        return UiConfiguration(java.lang.Boolean.TRUE, java.lang.Boolean.FALSE, 1, 1, ModelRendering.MODEL, java.lang.Boolean.FALSE, DocExpansion.LIST, java.lang.Boolean.FALSE, null, OperationsSorter.ALPHA, java.lang.Boolean.FALSE, TagsSorter.ALPHA, UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS, null)
    }

    private fun apiInfo(version: String): ApiInfo {
        return ApiInfoBuilder()
                .title("API - Person Service")
                .description("Persons Management")
                .version(version)
                .build()
    }

}

The documentation is available under context path /swagger-ui.html. Besides API documentation it displays the full information about application version, git commit id and branch name.

kotlin-microservices-1.PNG

5. Choosing your app server

Spring Boot Web can be run on three different embedded servers: Tomcat, Jetty or Undertow. By default it uses Tomcat. To change the default server you just need include the suitable Spring Boot starter and exclude spring-boot-starter-tomcat. The good practice may be to enable switching between servers during application build. You can achieve it by declaring Maven profiles as shown below.

<profiles>
   <profile>
      <id>tomcat</id>
      <activation>
         <activeByDefault>true</activeByDefault>
      </activation>
      <dependencies>
         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
      </dependencies>
   </profile>
   <profile>
      <id>jetty</id>
      <dependencies>
         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
               <exclusion>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-tomcat</artifactId>
               </exclusion>
            </exclusions>
         </dependency>
         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
         </dependency>
      </dependencies>
   </profile>
   <profile>
      <id>undertow</id>
      <dependencies>
         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
               <exclusion>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-tomcat</artifactId>
               </exclusion>
            </exclusions>
         </dependency>
         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
         </dependency>
      </dependencies>
   </profile>
</profiles>

Now, if you would like to enable another server than Tomcat for your application you should activate the appropriate profile during Maven build.

$ mvn clean install -Pjetty

Conclusion

Development of microservices using Kotlin and Spring Boot is nice and simple. Based on the sample application I have introduced the main Spring Boot features for Kotlin. I also described some good practices you may apply to your microservices when building it using Spring Boot and Kotlin. You can compare described approach with some other micro-frameworks used with Kotlin, for example Ktor described in one of my previous articles Kotlin Microservices with Ktor.

The post Kotlin Microservice with Spring Boot appeared first on Piotr's TechBlog.

]]>
https://piotrminkowski.com/2019/01/15/kotlin-microservice-with-spring-boot/feed/ 0 6960
Spring REST Docs versus SpringFox Swagger for API documentation https://piotrminkowski.com/2018/07/19/spring-rest-docs-versus-springfox-swagger-for-api-documentation/ https://piotrminkowski.com/2018/07/19/spring-rest-docs-versus-springfox-swagger-for-api-documentation/#respond Thu, 19 Jul 2018 12:44:09 +0000 https://piotrminkowski.wordpress.com/?p=6744 Recently, I have come across some articles and mentions about Spring REST Docs, where it has been presented as a better alternative to traditional Swagger docs. Until now, I was always using Swagger for building API documentation, so I decided to try Spring REST Docs. You may even read on the main page of that […]

The post Spring REST Docs versus SpringFox Swagger for API documentation appeared first on Piotr's TechBlog.

]]>
Recently, I have come across some articles and mentions about Spring REST Docs, where it has been presented as a better alternative to traditional Swagger docs. Until now, I was always using Swagger for building API documentation, so I decided to try Spring REST Docs. You may even read on the main page of that Spring project (https://spring.io/projects/spring-restdocs) some references to Swagger, for example: “This approach frees you from the limitations of the documentation produced by tools like Swagger”. Are you interested in building API documentation using Spring REST Docs? Let’s take a closer look at that project!

A first difference in comparison to Swagger is a test-driven approach to generating API documentation. Thanks to that Spring REST Docs ensures that the documentation is always generated accurately matches the actual behavior of the API. When using Swagger SpringFox library you just need to enable it for the project and provide some configuration to force it work following your expectations. I have already described usage of Swagger 2 for automated build API documentation for Spring Boot based application in my two previous articles:

The articles mentioned above describe in the details how to use SpringFox Swagger in your Spring Boot application to automatically generate API documentation based on the source code. Here I’ll give you only a short introduction to that technology, to easily find out differences between usage of Swagger2 and Spring REST Docs.

1. Using Swagger2 with Spring Boot

To enable SpringFox library for your application you need to include the following dependencies to pom.xml.

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

Then you should annotate the main or configuration class with @EnableSwagger2. You can also customize the behaviour of SpringFox library by declaring Docket bean.

@Bean
public Docket swaggerEmployeeApi() {
   return new Docket(DocumentationType.SWAGGER_2)
      .select()
         .apis(RequestHandlerSelectors.basePackage("pl.piomin.services.employee.controller"))
         .paths(PathSelectors.any())
      .build()
      .apiInfo(new ApiInfoBuilder().version("1.0").title("Employee API").description("Documentation Employee API v1.0").build());
}

Now, after running the application the documentation is available under context path /v2/api-docs. You can also display it in your web browser using Swagger UI available at site /swagger-ui.html.

spring-cloud-3
It looks easy? Let’s see how to do this with Spring REST Docs.

2. Using Asciidoctor with Spring Boot

There are some other differences between Spring REST Docs and SpringFox Swagger. By default, Spring REST Docs uses Asciidoctor. Asciidoctor processes plain text and produces HTML, styled and layed out to suit your needs. If you prefer, Spring REST Docs can also be configured to use Markdown. This really distinguished it from Swagger, which uses its own notation called OpenAPI Specification.
Spring REST Docs makes use of snippets produced by tests written with Spring MVC’s test framework, Spring WebFlux’s WebTestClient or REST Assured 3. I’ll show you an example based on Spring MVC.
I suggest you begin from creating a base Asciidoc file. It should be placed in src/main/asciidoc directory in your application source code. I don’t know if you are familiar with Asciidoctor notation, but it is really intuitive. The sample visible below shows two important things. First we’ll display the version of the project taken from pom.xml. Then we’ll include the snippets generated during JUnit tests by declaring a macro called operation containing document name and list of snippets. We can choose between such snippets like curl-request, http-request, http-response, httpie-request, links, request-body, request-fields, response-body, response-fields or path-parameters. The document name is determined by the name of the test method in our JUnit test class.

= RESTful Employee API Specification
{project-version}
:doctype: book

== Add a new person

A `POST` request is used to add a new person

operation::add-person[snippets='http-request,request-fields,http-response']

== Find a person by id

A `GET` request is used to find a new person by id

operation::find-person-by-id[snippets='http-request,path-parameters,http-response,response-fields']

The source code fragment with Asciidoc natation is just a template. We would like to generate an HTML file, which prettily displays all our automatically generated staff. To achieve it we should enable plugin asciidoctor-maven-plugin in the project’s pom.xml. In order to display Maven project version we need to pass it to the Asciidoc plugin configuration attributes. We also need to spring-restdocs-asciidoctor dependency to that plugin.

<plugin>
   <groupId>org.asciidoctor</groupId>
   <artifactId>asciidoctor-maven-plugin</artifactId>
   <version>1.5.6</version>
   <executions>
      <execution>
         <id>generate-docs</id>
         <phase>prepare-package</phase>
         <goals>
            <goal>process-asciidoc</goal>
         </goals>
         <configuration>
            <backend>html</backend>
            <doctype>book</doctype>
            <attributes>
               <project-version>${project.version}</project-version>
            </attributes>
         </configuration>
      </execution>
   </executions>
   <dependencies>
      <dependency>
         <groupId>org.springframework.restdocs</groupId>
         <artifactId>spring-restdocs-asciidoctor</artifactId>
         <version>2.0.0.RELEASE</version>
      </dependency>
   </dependencies>
</plugin>

Ok, the documentation is automatically generated during Maven build from our api.adoc file located inside src/main/asciidoc directory. But we still need to develop JUnit API tests that automatically generate required snippets. Let’s do that in the next step.

3. Generating snippets for Spring MVC

First, we should enable Spring REST Docs for our project. To achieve it we have to include the following dependency.

<dependency>
   <groupId>org.springframework.restdocs</groupId>
   <artifactId>spring-restdocs-mockmvc</artifactId>
   <scope>test</scope>
</dependency>

Now, all we need to do is to implement JUnit tests. Spring Boot provides an @AutoConfigureRestDocs annotation that allows you to leverage Spring REST Docs in your tests.
In fact, we need to prepare for the standard Spring MVC test using MockMvc bean. I also mocked some methods implemented by EmployeeRepository. Then, I used some static methods provided by Spring REST Docs with support for generating documentation of request and response payloads. First of those method is document("{method-name}/",...), which is responsible for generating snippets under directory target/generated-snippets/{method-name}, where method name is the name of the test method formatted using kebab-case. I have described all the JSON fields in the requests using requestFields(...) and responseFields(...) methods.

@RunWith(SpringRunner.class)
@WebMvcTest(EmployeeController.class)
@AutoConfigureRestDocs
public class EmployeeControllerTest {

   @MockBean
   EmployeeRepository repository;
   @Autowired
   MockMvc mockMvc;
   
   private ObjectMapper mapper = new ObjectMapper();

   @Before
   public void setUp() {
      Employee e = new Employee(1L, 1L, "John Smith", 33, "Developer");
      e.setId(1L);
      when(repository.add(Mockito.any(Employee.class))).thenReturn(e);
      when(repository.findById(1L)).thenReturn(e);
   }

   @Test
   public void addPerson() throws JsonProcessingException, Exception {
      Employee employee = new Employee(1L, 1L, "John Smith", 33, "Developer");
      mockMvc.perform(post("/").contentType(MediaType.APPLICATION_JSON).content(mapper.writeValueAsString(employee)))
         .andExpect(status().isOk())
         .andDo(document("{method-name}/", requestFields(
            fieldWithPath("id").description("Employee id").ignored(),
            fieldWithPath("organizationId").description("Employee's organization id"),
            fieldWithPath("departmentId").description("Employee's department id"),
            fieldWithPath("name").description("Employee's name"),
            fieldWithPath("age").description("Employee's age"),
            fieldWithPath("position").description("Employee's position inside organization")
         )));
   }
   
   @Test
   public void findPersonById() throws JsonProcessingException, Exception {
      this.mockMvc.perform(get("/{id}", 1).accept(MediaType.APPLICATION_JSON))
         .andExpect(status().isOk())
         .andDo(document("{method-name}/", responseFields(
            fieldWithPath("id").description("Employee id"),
            fieldWithPath("organizationId").description("Employee's organization id"),
            fieldWithPath("departmentId").description("Employee's department id"),
            fieldWithPath("name").description("Employee's name"),
            fieldWithPath("age").description("Employee's age"),
            fieldWithPath("position").description("Employee's position inside organization")
         ), pathParameters(parameterWithName("id").description("Employee id"))));
   }

}

If you would like to customize some settings of Spring REST Docs you should provide @TestConfiguration class inside JUnit test class. In the following code fragment you may see an example of such customization. I overridden default snippets output directory from index to test method-specific name, and force generation of sample request and responses using prettyPrint option (single parameter in the separated line).

@TestConfiguration
static class CustomizationConfiguration implements RestDocsMockMvcConfigurationCustomizer {

   @Override
   public void customize(MockMvcRestDocumentationConfigurer configurer) {
      configurer.operationPreprocessors()
         .withRequestDefaults(prettyPrint())
         .withResponseDefaults(prettyPrint());
   }
   
   @Bean
   public RestDocumentationResultHandler restDocumentation() {
      return MockMvcRestDocumentation.document("{method-name}");
   }
}

Now, if you execute mvn clean install on your project you should see the following structure inside your output directory.
rest-api-docs-3

4. Viewing and publishing API docs

Once we have successfully built our project, the documentation has been generated. We can display HTML files available at target/generated-docs/api.html. It provides the full documentation of our API.

rest-api-docs-1
And the next part…

rest-api-docs-2
You may also want to publish it inside your application fat JAR file. If you configure maven-resources-plugin following example vibisle below it would be available under /static/docs directory inside JAR.

<plugin>
   <artifactId>maven-resources-plugin</artifactId>
   <executions>
      <execution>
         <id>copy-resources</id>
         <phase>prepare-package</phase>
         <goals>
            <goal>copy-resources</goal>
         </goals>
         <configuration>
            <outputDirectory>
               ${project.build.outputDirectory}/static/docs
            </outputDirectory>
            <resources>
               <resource>
                  <directory>
                     ${project.build.directory}/generated-docs
                  </directory>
               </resource>
            </resources>
         </configuration>
      </execution>
   </executions>
</plugin>

Conclusion

That’s all what I wanted to show in this article. The sample service generating documentation using Spring REST Docs is available on GitHub under repository https://github.com/piomin/sample-spring-microservices-new/tree/rest-api-docs/employee-service. I’m not sure that Swagger and Spring REST Docs should be treated as competitive solutions. I use Swagger for simple testing an API on the running application or exposing specifications that can be used for automated generation of a client code. Spring REST Docs is rather used for generating documentation that can be published somewhere, and “is accurate, concise, and well-structured. This documentation then allows your users to get the information they need with a minimum of fuss”. I think there is no obstacle to using Spring REST Docs and SpringFox Swagger together in your project in order to provide the most valuable documentation of API exposed by the application.

The post Spring REST Docs versus SpringFox Swagger for API documentation appeared first on Piotr's TechBlog.

]]>
https://piotrminkowski.com/2018/07/19/spring-rest-docs-versus-springfox-swagger-for-api-documentation/feed/ 0 6744
Versioning REST API with Spring Boot and Swagger https://piotrminkowski.com/2018/02/19/versioning-rest-api-with-spring-boot-and-swagger/ https://piotrminkowski.com/2018/02/19/versioning-rest-api-with-spring-boot-and-swagger/#respond Mon, 19 Feb 2018 14:26:04 +0000 https://piotrminkowski.wordpress.com/?p=6330 One thing’s for sure. If you don’t have to version your API, do not try to do that. However, sometimes you have to. A large part of the most popular services like Twitter, Facebook, Netflix, or PayPal is versioning their REST APIs. The advantages and disadvantages of that approach are obvious. On the one hand, […]

The post Versioning REST API with Spring Boot and Swagger appeared first on Piotr's TechBlog.

]]>
One thing’s for sure. If you don’t have to version your API, do not try to do that. However, sometimes you have to. A large part of the most popular services like Twitter, Facebook, Netflix, or PayPal is versioning their REST APIs. The advantages and disadvantages of that approach are obvious. On the one hand, you don’t have to worry about making changes in your API even if many external clients and applications consume it. But on the other hand, you have maintained different versions of API implementation in your code, which sometimes may be troublesome.

In this article, I’m going to show you how to maintain several versions of the REST API in your application in the most comfortable way. We will base on the sample application written on the top of the Spring Boot framework and exposing API documentation using Swagger and SpringFox libraries.

Spring Boot does not provide any dedicated solutions for versioning APIs. The situation is different for SpringFox Swagger2 library, which provides grouping mechanism from version 2.8.0, which is perfect for generating documentation of versioned REST API.

I have already introduced Swagger2 together with Spring Boot application in one of my previous posts. In the article Microservices API Documentation with Swagger2 you may read how to use Swagger2 for generating API documentation for all the independent microservices and publishing it in one place – on API Gateway.

Different approaches to API versioning

There are some different ways to provide an API versioning in your application. The most popular of them are:

  1. Through an URI path – you include the version number in the URL path of the endpoint, for example /api/v1/persons
  2. Through query parameters – you pass the version number as a query parameter with specified name, for example /api/persons?version=1
  3. Through custom HTTP headers – you define a new header that contains the version number in the request
  4. Through a content negotiation – the version number is included to the “Accept” header together with accepted content type. The request with cURL would look like in the following sample: curl -H "Accept: application/vnd.piomin.v1+json" http://localhost:8080/api/persons

The decision, which of that approach implement in the application is up to you. We would discuss the advantages and disadvantages of every single approach, however it is not the main purpose of that article. The main purpose is to show you how to implement versioning in Spring Boot application and then publish the API documentation automatically using Swagger2. The sample application source code is available on GitHub (https://github.com/piomin/sample-api-versioning.git). I have implemented two of the approaches described above – in point 1 and 4.

Enabling Swagger for Spring Boot

Swagger2 can be enabled in Spring Boot application by including SpringFox library. In fact, this is the suite of java libraries used for automating the generation of machine and human readable specifications for JSON APIs written using Spring Framework. It supports such formats like swagger, RAML and jsonapi. To enable it for your application include the following Maven dependencies to the project: io.springfox:springfox-swagger-ui, io.springfox:springfox-swagger2, io.springfox:springfox-spring-web. Then you will have to annotate the main class with @EnableSwagger2 and define Docker object. Docket is a Springfox’s primary configuration mechanism for Swagger 2.0. We will discuss the details about it in the next section along with the sample for each way of versioning API.

Sample API

Our sample API is very simple. It exposes basic CRUD methods for Person entity. There are three versions of API available for external clients: 1.0, 1.1 and 1.2. In the version 1.1 I have changed the method for updating Person entity. In version 1.0 it was available under /person path, while now it is available under /person/{id} path. This is the only difference between versions 1.0 and 1.1. There is also one only difference in API between versions 1.1 and 1.2. Instead of field birthDate it returns age as integer parameter. This change affects to all the endpoints except DELETE /person/{id}. Now, let’s proceed to the implementation.

Versioning using URI path

Here’s the full implementation of URI path versioning inside Spring @RestController.

@RestController
@RequestMapping("/person")
public class PersonController {

   @Autowired
   PersonMapper mapper;
   @Autowired
   PersonRepository repository;

   @PostMapping({"/v1.0", "/v1.1"})
   public PersonOld add(@RequestBody PersonOld person) {
      return (PersonOld) repository.add(person);
   }

   @PostMapping("/v1.2")
   public PersonCurrent add(@RequestBody PersonCurrent person) {
      return mapper.map((PersonOld) repository.add(person));
   }

   @PutMapping("/v1.0")
   @Deprecated
   public PersonOld update(@RequestBody PersonOld person) {
      return (PersonOld) repository.update(person);
   }

   @PutMapping("/v1.1/{id}")
   public PersonOld update(@PathVariable("id") Long id, @RequestBody PersonOld person) {
      return (PersonOld) repository.update(person);
   }

   @PutMapping("/v1.2/{id}")
   public PersonCurrent update(@PathVariable("id") Long id, @RequestBody PersonCurrent person) {
      return mapper.map((PersonOld) repository.update(person));
   }

   @GetMapping({"/v1.0/{id}", "/v1.1/{id}"})
   public PersonOld findByIdOld(@PathVariable("id") Long id) {
      return (PersonOld) repository.findById(id);
   }

   @GetMapping("/v1.2/{id}")
   public PersonCurrent findById(@PathVariable("id") Long id) {
      return mapper.map((PersonOld) repository.findById(id));
   }

   @DeleteMapping({"/v1.0/{id}", "/v1.1/{id}", "/v1.2/{id}"})
   public void delete(@PathVariable("id") Long id) {
      repository.delete(id);
   }

}

If you would like to have three different versions available in the single generated API specification you should declare three Docket @Beans – one per single version. In this case, the swagger group concept, which has been already introduced by SpringFox, would be helpful for us. The reason this concept has been introduced is a necessity for support applications that require more than one swagger resource listing. Usually, you need more than one resource listing in order to provide different versions of the same API. We can assign a group to every Docket just by invoking groupName DSL method on it. Because different versions of the API method are implemented within the same controller, we have to distinguish them by declaring path regex matching the selected version. All other settings are standard.

@Bean
public Docket swaggerPersonApi10() {
   return new Docket(DocumentationType.SWAGGER_2)
      .groupName("person-api-1.0")
      .select()
      .apis(RequestHandlerSelectors.basePackage("pl.piomin.services.versioning.controller"))
      .paths(regex("/person/v1.0.*"))
      .build()
      .apiInfo(new ApiInfoBuilder().version("1.0").title("Person API").description("Documentation Person API v1.0").build());
}

@Bean
public Docket swaggerPersonApi11() {
   return new Docket(DocumentationType.SWAGGER_2)
      .groupName("person-api-1.1")
      .select()
      .apis(RequestHandlerSelectors.basePackage("pl.piomin.services.versioning.controller"))
      .paths(regex("/person/v1.1.*"))
      .build()
      .apiInfo(new ApiInfoBuilder().version("1.1").title("Person API").description("Documentation Person API v1.1").build());
}

@Bean
public Docket swaggerPersonApi12() {
   return new Docket(DocumentationType.SWAGGER_2)
      .groupName("person-api-1.2")
      .select()
      .apis(RequestHandlerSelectors.basePackage("pl.piomin.services.versioning.controller"))
      .paths(regex("/person/v1.2.*"))
      .build()
      .apiInfo(new ApiInfoBuilder().version("1.2").title("Person API").description("Documentation Person API v1.2").build());
}

Now, we may display Swagger UI for our API just by calling URL in the web browser path /swagger-ui.html. You can switch between all available versions of API as you can see in the picture below.

api-1
Switching between available versions of API

Specification is generated by the exact version of API. Here’s documentation for version 1.0. Because method PUT /person is annotated with @Deprecated it is crossed out on the generated HTML documentation page.

api-2
Person API 1.0 specification

If you switch to group person-api-1 you will see all the methods that contains v1.1 in the path. Along them you may recognize the current version of PUT method with {id} field in the path.

api-3
Person API 1.1 specification

When using documentation generated by Swagger you may easily call every method after expanding it. Here’s the sample of calling method PUT /person/{id} from implemented for version 1.2.

api-5
Updating Person entity by calling method PUT from 1.2 version

Versioning using Accept header

To access the implementation of versioning witt ‘Accept’ header you should switch to branch header (https://github.com/piomin/sample-api-versioning/tree/header). Here’s the full implementation of content negotiation using ‘Accept’ header versioning inside Spring @RestController.

@RestController
@RequestMapping("/person")
public class PersonController {

   @Autowired
   PersonMapper mapper;
   @Autowired
   PersonRepository repository;

   @PostMapping(produces = {"application/vnd.piomin.app-v1.0+json", "application/vnd.piomin.app-v1.1+json"})
   public PersonOld add(@RequestBody PersonOld person) {
      return (PersonOld) repository.add(person);
   }

   @PostMapping(produces = "application/vnd.piomin.app-v1.2+json")
   public PersonCurrent add(@RequestBody PersonCurrent person) {
      return mapper.map((PersonOld) repository.add(person));
   }

   @PutMapping(produces = "application/vnd.piomin.app-v1.0+json")
   @Deprecated
   public PersonOld update(@RequestBody PersonOld person) {
      return (PersonOld) repository.update(person);
   }

   @PutMapping(value = "/{id}", produces = "application/vnd.piomin.app-v1.1+json")
   public PersonOld update(@PathVariable("id") Long id, @RequestBody PersonOld person) {
      return (PersonOld) repository.update(person);
   }

   @PutMapping(value = "/{id}", produces = "application/vnd.piomin.app-v1.2+json")
   public PersonCurrent update(@PathVariable("id") Long id, @RequestBody PersonCurrent person) {
      return mapper.map((PersonOld) repository.update(person));
   }

   @GetMapping(name = "findByIdOld", value = "/{idOld}", produces = {"application/vnd.piomin.app-v1.0+json", "application/vnd.piomin.app-v1.1+json"})
   @Deprecated
   public PersonOld findByIdOld(@PathVariable("idOld") Long id) {
      return (PersonOld) repository.findById(id);
   }

   @GetMapping(name = "findById", value = "/{id}", produces = "application/vnd.piomin.app-v1.2+json")
   public PersonCurrent findById(@PathVariable("id") Long id) {
      return mapper.map((PersonOld) repository.findById(id));
   }

   @DeleteMapping(value = "/{id}", produces = {"application/vnd.piomin.app-v1.0+json", "application/vnd.piomin.app-v1.1+json", "application/vnd.piomin.app-v1.2+json"})
   public void delete(@PathVariable("id") Long id) {
      repository.delete(id);
   }

}

We still have to define three Docker @Beans, but the filtering criterias are slightly different. The simple filtering by path is not an option here. We have to crate Predicate for RequestHandler object and pass it to apis DSL method. The predicate implementation should filter every method in order to find only those which have produces field with required version number. Here’s sample Docket implementation for version 1.2.

@Bean
public Docket swaggerPersonApi12() {
   return new Docket(DocumentationType.SWAGGER_2)
      .groupName("person-api-1.2")
      .select()
      .apis(p -> {
         if (p.produces() != null) {
            for (MediaType mt : p.produces()) {
               if (mt.toString().equals("application/vnd.piomin.app-v1.2+json")) {
                  return true;
               }
            }
         }
         return false;
      })
      .build()
      .produces(Collections.singleton("application/vnd.piomin.app-v1.2+json"))
      .apiInfo(new ApiInfoBuilder().version("1.2").title("Person API").description("Documentation Person API v1.2").build());
}

As you can see in the picture below the generated methods do not have the version number in the path.

api-6
Person API 1.2 specification for a content negotiation approach

When calling method for the selected version of API the only difference is in the response’s required content type.

api-7
Updating person and setting response content type

Summary

Versioning is one of the most important concepts around HTTP APIs designing. No matter which approaches to versioning you choose you should do everything to describe your API well. This seems to be especially important in the era of microservices, where your interface may be called by many other independent applications. In this case, creating documentation in isolation from the source code could be troublesome. Swagger solves all of the described problems. It may be easily integrated with your application, supports versioning. Thanks to the SpringFox project it also can be easily customized in your Spring Boot application to meet more advanced demands.

The post Versioning REST API with Spring Boot and Swagger appeared first on Piotr's TechBlog.

]]>
https://piotrminkowski.com/2018/02/19/versioning-rest-api-with-spring-boot-and-swagger/feed/ 0 6330
Microservices API Documentation with Swagger2 https://piotrminkowski.com/2017/04/14/microservices-api-documentation-with-swagger2/ https://piotrminkowski.com/2017/04/14/microservices-api-documentation-with-swagger2/#comments Fri, 14 Apr 2017 07:53:49 +0000 https://piotrminkowski.wordpress.com/?p=2400 Swagger is the most popular tool for designing, building and documenting RESTful APIs. It has nice integration with Spring Boot. To use it in conjunction with Spring we need to add the following two dependencies to Maven pom.xml. Swagger configuration for a single Spring Boot service is pretty simple. The level of complexity is greater […]

The post Microservices API Documentation with Swagger2 appeared first on Piotr's TechBlog.

]]>
Swagger is the most popular tool for designing, building and documenting RESTful APIs. It has nice integration with Spring Boot. To use it in conjunction with Spring we need to add the following two dependencies to Maven pom.xml.

<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger2</artifactId>
   <version>2.6.1</version>
</dependency>
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger-ui</artifactId>
   <version>2.6.1</version>
</dependency>

Swagger configuration for a single Spring Boot service is pretty simple. The level of complexity is greater if you want to create one documentation for several separated microservices. Such documentation should be available on API gateway. In the picture below you can see the architecture of our sample solution.

swagger

First, we should configure Swagger on every microservice. To enable it we have to declare @EnableSwagger2 on the main class. API documentation will be automatically generated from source code by Swagger library during application startup. The process is controlled by Docket @Bean which is also declared in the main class. The API version is read from pom.xml file using MavenXpp3Reader. We also set some other properties like title, author and description using apiInfo method. By default, Swagger generates documentation for all REST services including those created by Spring Boot. We would like to limit documentation only to our @RestController located inside pl.piomin.microservices.advanced.account.api package.

@Bean
public Docket api() throws IOException, XmlPullParserException {
   MavenXpp3Reader reader = new MavenXpp3Reader();
   Model model = reader.read(new FileReader("pom.xml"));
   return new Docket(DocumentationType.SWAGGER_2)
      .select()
      .apis(RequestHandlerSelectors.basePackage("pl.piomin.microservices.advanced.account.api"))
      .paths(PathSelectors.any())
      .build().apiInfo(new ApiInfo("Account Service Api Documentation", "Documentation automatically generated", model.getParent().getVersion(), null, new Contact("Piotr Mińkowski", "piotrminkowski.wordpress.com", "piotr.minkowski@gmail.com"), null, null));
}

Here’s our API RESTful controller.

@RestController
public class AccountController {

   @Autowired
   AccountRepository repository;

   protected Logger logger = Logger.getLogger(AccountController.class.getName());

   @RequestMapping(value = "/accounts/{number}", method = RequestMethod.GET)
   public Account findByNumber(@PathVariable("number") String number) {
      logger.info(String.format("Account.findByNumber(%s)", number));
      return repository.findByNumber(number);
   }

   @RequestMapping(value = "/accounts/customer/{customer}", method = RequestMethod.GET)
   public List findByCustomer(@PathVariable("customer") String customerId) {
      logger.info(String.format("Account.findByCustomer(%s)", customerId));
      return repository.findByCustomerId(customerId);
   }

   @RequestMapping(value = "/accounts", method = RequestMethod.GET)
   public List findAll() {
      logger.info("Account.findAll()");
      return repository.findAll();
   }

   @RequestMapping(value = "/accounts", method = RequestMethod.POST)
   public Account add(@RequestBody Account account) {
      logger.info(String.format("Account.add(%s)", account));
      return repository.save(account);
   }

   @RequestMapping(value = "/accounts", method = RequestMethod.PUT)
   public Account update(@RequestBody Account account) {
      logger.info(String.format("Account.update(%s)", account));
      return repository.save(account);
   }

}

The similar Swagger’s configuration exists on every microservice. API documentation UI is available under /swagger-ui.html. Now, we would like to enable one documentation embedded on the gateway for all microservices. Here’s Spring @Component implementing SwaggerResourcesProvider interface which overrides default provider configuration exists in Spring context.

@Component
@Primary
@EnableAutoConfiguration
public class DocumentationController implements SwaggerResourcesProvider {

   @Override
   public List get() {
      List resources = new ArrayList<>();
      resources.add(swaggerResource("account-service", "/api/account/v2/api-docs", "2.0"));
      resources.add(swaggerResource("customer-service", "/api/customer/v2/api-docs", "2.0"));
      resources.add(swaggerResource("product-service", "/api/product/v2/api-docs", "2.0"));
      resources.add(swaggerResource("transfer-service", "/api/transfer/v2/api-docs", "2.0"));
      return resources;
   }

   private SwaggerResource swaggerResource(String name, String location, String version) {
      SwaggerResource swaggerResource = new SwaggerResource();
      swaggerResource.setName(name);
      swaggerResource.setLocation(location);
      swaggerResource.setSwaggerVersion(version);
      return swaggerResource;
   }

}

All microservices api-docs are added as Swagger resources. The location address is proxied via Zuul gateway. Here’s gateway route configuration.

zuul:
  prefix: /api
     routes:
       account:
         path: /account/**
         serviceId: account-service
       customer:
         path: /customer/**
         serviceId: customer-service
       product:
         path: /product/**
         serviceId: product-service
       transfer:
         path: /transfer/**
         serviceId: transfer-service

Now, API documentation is available under gateway address http://localhost:8765/swagger-ui.html. You can see how it looks for the account-service in the picture below. We can select the source service in the combo box placed inside the title panel.

swagger-1

Documentation appearance can be easily customized by providing UIConfiguration @Bean. In the code below I changed default operations expansion level by setting “list” as a second constructor parameter – docExpansion.

@Bean
UiConfiguration uiConfig() {
   return new UiConfiguration("validatorUrl", "list", "alpha", "schema",
UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS, false, true, 60000L);
}

You can expand every operation to see the details. Every operation can be test by providing required parameters and clicking Try it out! button.

swagger-2

swagger-3

Sample application source code is available on GitHub.

The post Microservices API Documentation with Swagger2 appeared first on Piotr's TechBlog.

]]>
https://piotrminkowski.com/2017/04/14/microservices-api-documentation-with-swagger2/feed/ 42 2400