O que acontece quando juntamos um dos melhoeres frameworks de desenvolvimento web com uma ótima ferramenta que permite que humanos e computadores entendam a nossa aplicação? O resultado nós vamos descobrir logo.
Não vamos nos alongar muito sobre o porque o Swagger é uma ferramenta maravilhosa e porque todo mundo deveria usá-la. Tem vários artigos bons que fala sobre o tema.
O que faremos
Vamos criar um microsserviço que adiciona, lista e remove informações sobre bolos. Tanto o microsserviço quanto a entidade são simplificados, pois o nosso foco é no OpenAPI.
Depois disso, vamos colocar as anotações para descrever a nossa aplicação e utilizar o Swagger-UI para descrever e manipular esse serviço.
É hora da ação
Primeiro vamos criar o nosso serviço de bolos. Para fazer isso, vamos usar o plugin maven do Quarkus:
mvn io.quarkus:quarkus-maven-plugin:1.6.1.Final:create \
-DprojectGroupId=cadastro-de-bolo \
-DprojectArtifactId=open-api \
-DclassName="com.BoloResource" \
-Dpath="/bolo" \
-Dextensions="resteasy-jsonb, smallrye-openapi, quarkus-smallrye-openapi"
Com isso, criamos um projeto com as extensões necessárias.
Para evitar maiores problemas, vamos excluir os dois arquivos de teste que foram criados automaticamente.
Agora, vamos criar a entidade bolo. Para isso, vamos criar o arquivo Bolo.java dentro do diretório src/main/java/com. Depois, vamos colocar o código abaixo nesse arquivo.
package com;
public class Bolo {
String nome;
String descricao;
public Bolo() {
}
public Bolo(String nome, String descricao) {
this.nome = nome;
this.descricao = descricao;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getDescricao() {
return descricao;
}
public void setDescricao(String descricao) {
this.descricao = descricao;
}
}
Além do arquivo Bolo.java que criamos, vamos alterar o arquivo BoloResource.java com as regras do nosso serviço. Ele vai ficar com essa cara:
package com;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/bolo")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class BoloResource {
private Set<Bolo> bolos;
public BoloResource() {
bolos = new HashSet<>();
bolos.add(new Bolo("Chocolate", "Melhor bolo do mundo"));
bolos.add(new Bolo("Sensação", "Chocolate com morango"));
}
@GET
public Set<Bolo> list() {
return bolos;
}
@POST
public Set<Bolo> add(Bolo bolo) {
bolos.add(bolo);
return bolos;
}
@DELETE
@Path("/{nome}")
public Set<Bolo> delete(PathParam("nome") String nome) {
bolos.removeIf(boloExistente -> //
boloExistente.nome.equalsIgnoreCase(nome));
return bolos;
}
}
Esse serviço é bem simples. Ele lista, adiciona ou remove bolos e sempre ao fazer uma operação de adição ou remoção, ele retorna todos os bolos cadastrados.
Feito isso, basta apenas executar a aplicação com o comando:
mvn quarkus:dev
E está pronto. Temos nosso serviço de pé. Se acessarmos o endereço http://localhost:8080/openapi vamos ter o arquivo de descrição dos endpoints desse serviço. O arquivo tem mais ou menos essa cara:
---
openapi: 3.0.1
info:
title: Generated API
version: "1.0"
paths:
/bolo:
get:
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/SetBolo'
post:
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Bolo'
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/SetBolo'
/bolo/{nome}:
delete:
requestBody:
content:
application/json:
schema:
type: string
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/SetBolo'
components:
schemas:
Bolo:
type: object
properties:
descricao:
type: string
nome:
type: string
SetBolo:
uniqueItems: true
type: array
items:
$ref: '#/components/schemas/Bolo'
Realmente, temos uma descrição do nosso serviço. Porém, o fato de termos um arquivo de descrição, não quer dizer que essas informações são realmente úteis. Quer dizer, nós descobrimos que dentro do endpoint /bolo nós temos os métodos GET, Post e Delete. Mas e aí? O que é um bolo? O que ele recebe?
Já temos a nossa descrição do endpoint, agora vamos tratar de fazer com que essa descrição tenha informações que sejam realmente úteis a quem for utilizar o nosso serviço.
Vamos começar descrevendo o serviço propriamente dito. Para isso, basta criar um arquivo chamado Descricao.java no diretório com e colar o seguinte código.
package com;
import javax.ws.rs.core.Application;
import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition;
import org.eclipse.microprofile.openapi.annotations.info.Contact;
import org.eclipse.microprofile.openapi.annotations.info.Info;
import org.eclipse.microprofile.openapi.annotations.servers.Server;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
@OpenAPIDefinition(//
tags = { //
@Tag(name = "tutorial", description = "Exemplo de estudo."), //
@Tag(name = "bolo", description = "Pessoas gostam de bolo.")//
}, //
info = @Info(//
title = "A fantástica fábrica de bolo", //
version = "1.0.0", //
contact = @Contact(//
name = "Fale com o Aleatório", //
url = "https://github.com/lucasscharf")), //
servers = { //
@Server(url = "http://localhost:8080")//
}) //
public class Descricao extends Application {
}
Essa classe serve como âncora para as anotações de descrição.
Essas anotações estão descrevendo o que essa aplicação faz, quem fez, informações importantes, etc... Uma explicação completa e descrição melhor dessas anotações, pode ser encontrada na documentação do microprofile OpenAPI.
Também podemos editar o nosso arquivo BoloResource.java para adicionar as anotações de descrição do OpenAPI. O novo arquivo ficará assim:
package com;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.enums.SchemaType;
import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
@Path("/bolo")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class BoloResource {
private Set<Bolo> bolos;
public BoloResource() {
bolos = new HashSet<>();
bolos.add(new Bolo("Chocolate", "Melhor bolo do mundo"));
bolos.add(new Bolo("Sensação", "Chocolate com morango"));
}
@GET
@Operation(summary = "Retorna todos os bolos cadastrados")
@APIResponse(responseCode = "200", //
content = @Content(//
mediaType = MediaType.APPLICATION_JSON, //
schema = @Schema(//
implementation = Bolo.class, //
type = SchemaType.ARRAY)))
public Set<Bolo> list() {
return bolos;
}
@Operation(summary = "Cadastra um bolo")
@APIResponse(responseCode = "200", //
description = "Retorna todos os todos os bolos cadastrados, incluindo o novo bolo", //
content = @Content(//
mediaType = MediaType.APPLICATION_JSON, //
schema = @Schema(//
implementation = Bolo.class, //
type = SchemaType.ARRAY)))
@POST
public Set<Bolo> add(//
@RequestBody(required = true, //
content = @Content(//
mediaType = MediaType.APPLICATION_JSON, //
schema = @Schema(//
implementation = Bolo.class))) Bolo bolo) {
bolos.add(bolo);
return bolos;
}
@Operation(summary = "Deleta um bolo pelo nome do bolo")
@APIResponse(responseCode = "200", //
description = "Todos os bolos cadastrados menos aquele retirado", //
content = @Content(mediaType = MediaType.APPLICATION_JSON, //
schema = @Schema(implementation = Bolo.class, //
type = SchemaType.ARRAY)))
@DELETE
@Path("/{nome}")
public Set<Bolo> delete(//
@Parameter(description = "Nome do bolo a ser retirado", required = true) //
@PathParam("nome") String nome) {
bolos.removeIf(boloExistente -> //
boloExistente.nome.equalsIgnoreCase(nome));
return bolos;
}
}
Acessando a URL do OpenAPI, vamos ver que as informações estão bem mais detalhadas sobre a nossa aplicação. Porém, toda essa descrição ainda está presente em um arquivo yml o que não é tão amigável para um ser humano. Já ajuda, mas ainda dá para fazer melhor.
Para fazer tudo isso e muito mais é que temos o Swagger UI.
Swagger UI
Para quem não sabe, o Swagger UI é uma ferramenta que permite visualizar e interagir com os recursos da API. Ele é gerado através da documentação do OpenAPI (aquela que a gente descreveu lá em cima) e permite ter um acesso fácil ao back-end criando um cliente simples.
A parte mais legal é que o Quarkus já deixa tudo pronto para usarmos. Após colocar as anotações de descrição na nossa aplicação, é só acessar a url /swagger-ui do nosso serviço.
Ao fazer isso, nós teremos a seguinte tela:
Ela traz a descrição da nossa aplicação, dos endpoints e até permite que a gente chame diretamente o serviço para fazermos testes sem ter que levantar uma interface gráfica.
Aproveite bem para b̶r̶i̶n̶c̶a̶r̶ dedicar um tempo estudando e ver como tudo funciona. Testar outras anotações e adicionar diferentes comentários.
Considetrações
Documentação é algo muito importante. O OpenAPI permite tanto documentar facilmente nossa aplicação, como poupa o trabalho de ter que ficar criando classe repetitivas.
É vitória de tudo quanto é lado.
Uso muito o Swagger UI para testar diferentes entradas nos meus serviços e depois automatizar tudo em testes.
Ah, e o código desse post pode ser encontrado no github.
Top comments (1)
ótimo artigo