Se você tem uma API REST , provavelmente esta esperando que essa API seja consumida pelo seus clientes. Mas como os seus cliente vão saber onde esta sua API, quais os recursos, quais métodos estão expostos?

Normalmente, o próprio desenvolvedor teria que gerar um documento explicando as funcionalidade da API, alguns exemplo JSON de envio e retorno e definir os endpoints. Como faríamos se houvesse alguma alteração na nossa aplicação? Qual é a chance do primeiro documento ficar desatualizado em uma possível V2 da sua API?

Para resolver esse problema entre outros, vamos ver uma ferramenta chama Swagger com implementação em Kotlin.

O Swagger é uma suit que contém diversas ferramentas. Por exemplo, eles possuem ferramentas para fazer design de API, para automatizar a criação da API. Baseado no projeto, ele vai gerar o código. Para testes e também para documentação. Se você tem uma API pronta, em Kotlin, usando Spring Boot, o Swagger tem ferramentas que conseguem automatizar a leitura dessa API e a geração de uma documentação.

Neste Post, vamos criar uma API REST de autorização de transação de banco utilizando como base o Swagger para gerar toda a documentação e a criação de Controller .

 

Configuração:

Acesse https://start.spring.io/ para criar o projeto Spring e utilize as seguintes configurações:

Feito isso, vai ser gerado um projeto padrão. No arquivo pom.xml vamos incluir as dependências necessárias. Vamos utilizar o Jackson para fazer algumas conversões e uma implementação do swagger chamada springfox, também vamos definir um plugin passando alguns parâmetros para ele saber onde devemos criar esse código gerado pelo Framework:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.5.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>br.com.banco</groupId>
	<artifactId>autorizacao</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>autorizacao</name>
	<description>Projeto de autorizacao</description>

	<properties>
		<spring-boot.version>2.1.11.RELEASE</spring-boot.version>
		<java.version>1.8</java.version>
		<kotlin.version>1.3.50</kotlin.version>
		<springfox-swagger2.version>2.9.2</springfox-swagger2.version>
		<io.springfox>1.6.0</io.springfox>
		<openapi-generator-maven-plugin.version>4.2.2</openapi-generator-maven-plugin.version>
		<jackson.version>2.9.10</jackson.version>
		<jackson-databind-nullable.version>0.2.0</jackson-databind-nullable.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</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>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>${springfox-swagger2.version}</version>
		</dependency>
		<dependency>
			<groupId>io.swagger</groupId>
			<artifactId>swagger-annotations</artifactId>
			<version>${io.springfox}</version>
		</dependency>
		<dependency>
			<groupId>io.swagger</groupId>
			<artifactId>swagger-models</artifactId>
			<version>${io.springfox}</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-bean-validators</artifactId>
			<version>${springfox-swagger2.version}</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>${springfox-swagger2.version}</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.datatype</groupId>
			<artifactId>jackson-datatype-hibernate5</artifactId>
			<version>${jackson.version}</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.datatype</groupId>
			<artifactId>jackson-datatype-hppc</artifactId>
			<version>${jackson.version}</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.datatype</groupId>
			<artifactId>jackson-datatype-jsr310</artifactId>
			<version>${jackson.version}</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.module</groupId>
			<artifactId>jackson-module-afterburner</artifactId>
			<version>${jackson.version}</version>
		</dependency>
		<dependency>
			<groupId>org.openapitools</groupId>
			<artifactId>jackson-databind-nullable</artifactId>
			<version>${jackson-databind-nullable.version}</version>
		</dependency>
	</dependencies>
	<build>
		<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
		<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
			<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>
			<plugin>
				<groupId>org.openapitools</groupId>
				<artifactId>openapi-generator-maven-plugin</artifactId>
			</plugin>
		</plugins>
		<pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.openapitools</groupId>
					<artifactId>openapi-generator-maven-plugin</artifactId>
					<version>${openapi-generator-maven-plugin.version}</version>
					<executions>
						<execution>
							<goals>
								<goal>generate</goal>
							</goals>
							<configuration>
								<inputSpec>${project.basedir}/src/main/resources/swagger/api.yml</inputSpec>
								<generatorName>spring</generatorName>
								<apiPackage>br.com.banco.autorizacao.web.api</apiPackage>
								<modelPackage>br.com.banco.autorizacao.web.api.model</modelPackage>
								<supportingFilesToGenerate>ApiUtil.java</supportingFilesToGenerate>
								<skipValidateSpec>false</skipValidateSpec>
								<configOptions>
									<delegatePattern>true</delegatePattern>
									<title>autorizacao</title>
								</configOptions>
							</configuration>
						</execution>
					</executions>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>
</project>

Vamos gerar um contrato para nossa API, em um arquivo que vai estar em resources/swagger/api.yml vamos colocar o seguinte código :

openapi: 3.0.1
info:
  title: Api do banco para autorizacao
  version: 1.0.0
servers:
  - url: /v1
paths:
  '/autorizacao/':
    post:
      operationId: autorizacaoCartao
      summary: Realizar a validação de uma transação
      description: Recurso para realizar uma transação
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AutorizacaoDTO'
      responses:
        '200':
          $ref: '#/components/responses/Ok'
        '400':
          $ref: '#/components/responses/NaoAutorizado'
components:
  responses:
    Ok:
      description: Transação autorizada
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AutorizacaoDTO'
    NaoAutorizado:
      description: Transação não autorizada
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AutorizacaoDTO'
  schemas:
    AutorizacaoDTO:
      type: object
      description: Entrada de dados
      properties:
        numeroCartao:
          type: string
          description: Nome do grupo
      required:
        - numeroCartao

Nesse código definimos quais vão ser o nossos métodos e objeto de entrada e saída . Com esse schema vamos rodar um comando para gerar os controles :

 

mvn generete-sources

 

Pronto, todos os nossos controllers e dtos já estão criados.

Criamos um método chamado autorizaCartao que vai receber um número de cartão e pode devolver dois tipos de objeto dependendo de como for implementado o nosso service, o OK ou NaoAutorizado

Agora vamos implementar a logica criando a camada service

package br.com.banco.autorizacao.service

import br.com.banco.autorizacao.web.api.AutorizacaoApiDelegate
import br.com.banco.autorizacao.web.api.model.AutorizacaoDTO
import org.springframework.http.ResponseEntity
import org.springframework.stereotype.Component

@Component
class AutorizacaoService :AutorizacaoApiDelegate{

    override fun autorizacaoCartao(autorizacaoDTO: AutorizacaoDTO): ResponseEntity<AutorizacaoDTO> {
        return if(autorizacaoDTO.numeroCartao == "0") {
            ResponseEntity.badRequest().body(autorizacaoDTO)
        }else{
            ResponseEntity.ok(autorizacaoDTO)
        }
    }
}

Reparem quem o service “AutorizacaoService ” implementa o “AutorizacaoApiDelegate” que foi gerado pelo swagger. No service vamos fazer uma lógica simples para demonstrar os dois tipos de retornos possíveis para nossa aplicação, se for zero ele vai retornar um “bad request” com nosso objeto no corpo, em qualquer outro caso retorna sucesso.

OBS: Como no nosso “api.yml” definimos que o número do cartão é obrigatório, ele já faz essa validação antes de chegar no nosso serviço.

Vamos criar um application.yaml (pode ser properties também) e colocar o nome da nossa aplicação que irá para a documentação.Você também pode colocar versão , descrição entre outros:

spring:
  application:
    name: autorizacao

Com quase tudo já configurado, falta indicarmos para swagger qual é o pacote base que ele deve percorrer para gerar a documentação, para isso vamos criar uma classe de configuração do SpringFox:

 

package br.com.banco.autorizacao.config

import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import springfox.documentation.builders.PathSelectors
import springfox.documentation.builders.RequestHandlerSelectors
import springfox.documentation.service.ApiInfo
import springfox.documentation.spi.DocumentationType
import springfox.documentation.spring.web.plugins.Docket
import springfox.documentation.swagger2.annotations.EnableSwagger2
import java.util.*

@Configuration
@EnableSwagger2
class SpringFoxConfig {

    @Value("\${spring.application.name}")
    var name: String? = null

    @Bean
    fun api(): Docket {
        return Docket(DocumentationType.SWAGGER_2)
                .select()
                .paths(PathSelectors.any())
                .apis(RequestHandlerSelectors.basePackage("br.com.banco.autorizacao"))
                .build()
                .apiInfo(apiInfo())
    }

    private fun apiInfo(): ApiInfo {
        return ApiInfo(
                name,
                null,
                null,
                null,
                null,
                null,
                null,
                ArrayList()
        )
    }
}

Pronto, já temos uma documentação pronto. O swagger sempre gera uma url padrão (pode ser configurada), que no nosso caso é http://localhost:8080/swagger-ui.html:

Nesse caminho Podemos testar a nossa API:

Por então é só. O código produzido nesse artigo esta no https://github.com/vqmonteiro/autorizacao