Criar bibliotecas para consumo de sua API pode facilitar a integração de serviços e aplicações de terceiros a mesma, isolando detalhes de requisições, autenticação e tratamento dos dados enviados e recebidos. Neste artigo, veremos como gerar uma biblioteca Java destinada a importação em serviços Spring utilizando uma descrição de API seguindo o padrão OpenAPI e as ferramentas Swagger Editor ou OpenAPI Generator.
Atualização: Este artigo foi republicado aqui.
Diferenças entre bibliotecas, APIs e SDKs
Antes de tudo, precisamos estar cientes do conceito de uma biblioteca de programação, ainda que o mesmo pareça trivial. Ter isso em mente pode permitir que façamos algo mais direcionado a nosso objetivo e a decidir se o que temos o atinge ou não. Será que o que você quer ou precisa é de uma biblioteca? De um SDK inteiro, ou apenas uma API? Nesta seção, abordaremos a diferença entre estes três elementos de auxílio ao desenvolvimento e reuso de software. Baseei-me, em parte, no artigo “Framework vs Library vs Platform vs API vs SDK vs Toolkits vs IDE”, publicado na Medium, cujo conteúdo achei bem explicado e sucinto, além de ter outros conceitos que não abordaremos aqui, como toolkits e IDEs. No entanto, é importante ressaltar que utilizamos, muitas vezes, de maneira intercambiável estes conceitos.
Biblioteca
Conjunto de códigos com funcionalidades específicas e que são utilizadas recorrentemente, através da importação dos mesmos no seu próprio código. Podem vir disponíveis nativamente na linguagem de programação em uso (stdio.h e math.h do C/C++, por exemplo), sistemas operacionais (como o Linux Headers) ou por terceiros (como o OpenCV, para lidar com visão computacional). Permite que o desenvolvedor “terceirize” inúmeras tarefas e se preocupe com o negócio em si para o qual está programando. São disponibilizados, geralmente, através pacotes compilados ou não que são ligados ao programa em tempo de compilação ou execução.
Application Programming Interface (API)
Se refere a interface através do qual um ou mais recursos são disponibilizados para acesso ao programador, acesso este que pode ser feito através de bibliotecas. Pense na Google Maps API, por exemplo. Existem recursos computacionais na nuvem que lidam com problemas relacionados a geolocalização, cartografia e outros. Estas funcionalidades são disponibilizadas através de interfaces RESTful (através de URLs e verbos HTTP), por exemplo, sendo isto, então, um API. Por fim, o acesso a estas APIs podem ser feitos diretamente ou através do uso de bibliotecas, com códigos que encapsulam as consultas a esta interface.
Software Development Kit (SDK)
É um pacote completo e heterogêneo de desenvolvimento para alguma plataforma, que pode variar bastante e incluir bibliotecas, documentação, compiladores, ambientes de desenvolvimento integrado (IDEs), etc.
OpenAPI
Serviços RESTful se baseiam em disponibilizar recursos, que costumam ser entidades definidas no modelo de dados (passíveis de modificação de estado), através de endpoints URL (localizadores únicos) e verbos HTTP (que são relacionados a ações que se quer tomar em para modificar o estado do recurso).
Tomemos como exemplo um cadastro de livros em uma biblioteca. Após a modelagem de dados, encontramos a entidade Livro, com os atributos código, título, autor e número de páginas. Este recurso é disponibilizado em nosso serviço através do endpoint /livro. E há quatro ações de interesse: cadastrar, buscar/listar, editar e apagar um livro do sistema, que serão realizadas através dos verbos POST, GET, PUT e DELETE, respectivamente. Esta seria a visão geral do cenário:
# Cadastrar livro
POST /livro (código, título, autor, n_páginas)
# Buscar livro
GET /livro/{código}
# Listar livros
GET /livro
# Atualizar dados de um livro
PUT /livro/{código} (título, autor, n_páginas)
# Apagar livro do sistema
DELETE /livro/{código}
O que acabamos de fazer? Uma descrição da API. Um desenvolvedor pode utilizá-la para fazer consultas a API, seja diretamente ou através de programação, sem se preocupar com como a mesma é ou será implementada, desacoplando o trabalho, dividindo responsabilidades, facilitando testes e validações, além de servir como documentação do sistema. E que tal se existisse um padrão de descrição de APIs? Isso existe também, não somente um. Assim como serviços que seguem o padrão SOA podem ser descritos através do WSDL, os que seguem RESTful podem ser descritos através do OpenAPI, que é a iniciativa sobre a qual falaremos aqui. Além dos benefícios já citados, a descrição de uma API pode vir antes mesmo da construção da mesma, facilitando o planejamento e provendo diretrizes para a sua implementação.
A OpenAPI Initiative (OAI) tem como foco desenvolver um padrão “vendor neutral” para descrição de APIs. Começou com base no Swagger Specification, da SmartBear Software, e tem como integrantes players de peso tais como a Linux Foundation, Google, IBM, Atlassian e outros. Sua última versão é a 3.0.3. Há varias implementações do padrão, como os kits da Swagger, que contém um visualizador integrável a serviços, editor, geradores de código de bibliotecas e skeleton de servidores, bibliotecas integráveis a webservices que fazem varreduras dos endpoints e entradas nos comentários dos mesmos para gerar a descrição. Além disso, nos marketplaces de extensões das principais IDEs tais como Eclipse, VSCode e outros é possível encontrar plugins que auxiliam na edição e leitura de arquivos OpenAPI. Que podem ser em formato JSON ou YAML.
Vejamos como ficaria a descrição do nosso serviço utilizando OpenAPI 3.0.3 no formato YAML:
openapi: 3.0.1
info:
title: Serviço de Livros
description: Esta é a descrição de API do nosso serviço de registro de livros.
version: "1.0"
tags:
- name: livro
description: Everything about your livros
paths:
/livro:
get:
tags:
- livro
summary: Listar todos os livros
description: Retorna a lista de livros do sistema
operationId: listar_livros
responses:
200:
description: Ok
content:
application/json:
schema:
type: array
items:
type: object
properties:
codigo:
type: string
titulo:
type: string
autor:
type: string
n_paginas:
type: integer
400:
description: Requisição inválida
content: {}
post:
tags:
- livro
summary: Cadastrar um novo livro
operationId: criar_livro
requestBody:
description: Objeto que representa o livro a ser inserido
content:
application/json:
schema:
type: object
properties:
codigo:
type: string
titulo:
type: string
autor:
type: string
n_paginas:
type: string
required: true
responses:
405:
description: Entrada inválida
content: {}
x-codegen-request-body-name: body
/livro/{codigo}:
get:
tags:
- livro
summary: Buscar livro por código
description: Retorna o livro pelo código
operationId: buscar_por_codigo
parameters:
- name: codigo
in: path
description: Código do livro a ser retornado
required: true
schema:
type: string
responses:
200:
description: OK
content:
application/json:
schema:
type: object
properties:
codigo:
type: string
titulo:
type: string
autor:
type: string
n_paginas:
type: integer
400:
description: Código inválido
content: {}
404:
description: Livro não encontrado
content: {}
put:
tags:
- livro
summary: Atualiza os dados de um livro
description: Atualiza os dados de um livro pelo código
operationId: atualizar_livro
parameters:
- name: codigo
in: path
description: Código do livro a ser atualizado
required: true
schema:
type: string
requestBody:
description: Objeto que representa o livro a ser atualizado
content:
application/json:
schema:
type: object
properties:
codigo:
type: string
titulo:
type: string
autor:
type: string
n_paginas:
type: string
required: true
responses:
405:
description: Invalid input
content: {}
x-codegen-request-body-name: body
delete:
tags:
- livro
summary: Exclui um livro
description: Apaga um livro pelo código
operationId: remove_livro
parameters:
- name: codigo
in: path
description: Código do livro a ser removido
required: true
schema:
type: string
responses:
400:
description: Código inválido
content: {}
404:
description: Livro não encontrado
content: {}
Geração da Biblioteca
Uma vez de posse da descrição de nossa API no formato OpenAPI, seja gerado automaticamente, seja escrito manualmente, podemos gerar o código da biblioteca para inserção em códigos de terceiros e permitir que estes integrem-se a nossa API.
A forma mais simples, a princípio, é utilizar o Swagger Editor, que pode ser instalado localmente, através de uma imagem Docker ou diretamente online através deste link.
Gerando código com Swagger Editor
Primeiramente, importe a descrição através do menu File, onde você terá duas opções:
A primeira, Import URL, permite que você insira o endereço URL do padrão, se ele estiver disponível na rede. Muito útil quando se tem padrões gerados automaticamente ou de documentações de terceiro. Quando a utilizamos, devemos garantir que o CORS esteja habilitado no servidor de origem.
A segunda opção é importar através de um arquivo presente no próprio computador.
Além disso, podemos ainda copiar o padrão de algum lugar e colar diretamente no editor.
Vamos, agora, em Generate Cliente e selecionamos a linguagem para a qual queremos gerar uma biblioteca de cliente. Será gerado um pacote .ZIP e poderemos baixar em nosso computador.
Embora este processo possibilite gerar bibliotecas de cliente para várias linguagens e frameworks, carece ainda de customizações, que necessitariam estar em metadados na própria descrição de API. Uma vez que os da biblioteca gerada (swagger_client) e dos métodos não são, por padrão, muito amigáveis. Mais informações sobre estas customizações estão aqui.
Gerando código com Open API Generator
O OpenAPI generator é um programa que pode ser utilizado através da linha de comando e oferece uma maior gama de personalizações do que o Swagger Editor. Está disponível para Linux, Windows, MacOS, Java JAR, NPM e podemos baixá-lo aqui:
Para instalação no Linux baseado em Debian, execute os comandos:
mkdir -p ~/bin/openapitools
curl https://raw.githubusercontent.com/OpenAPITools/openapi-generator/master/bin/utils/openapi-generator-cli.sh > ~/bin/openapitools/openapi-generator-cli
chmod u+x ~/bin/openapitools/openapi-generator-cli
export PATH=$PATH:~/bin/openapitools/
Neste tutorial, utilizaremos a versão JAR, mais portável. Baixe no seu computador:
wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/4.3.0/openapi-generator-cli-4.3.0.jar -O openapi-generator-cli.jar
Após baixado, executamos o seguinte comando:
java -jar openapi-generator-cli-4.3.0.jar list
Com isso, podemos ver a diversidade de linguagens para a qual podemos gerar bibliotecas. Além disso, há a possibilidade de gerar aplicações de servidores, documentações e configurações, mas vamos deixar isso para outros artigos.
Agora vamos verificar, por exemplo, todas as opções de parâmetros possíveis de geração de bibliotecas para Java (poderia ser para qualquer outra linguagem listada no comando anterior).
java -jar openapi-generator-cli-4.3.0.jar config-help -g java
Dentre as opções listadas, é possível personalizar o nome do artefato (com.suaempresa.seuservico), licença, versão, etc. Além disso, podemos utilizar diversos frameworks para fazer consultas HTTP (seção library template). A escolha do framework ideal implica no grau de compatibilidade da biblioteca gerada com a aplicação de terceiros que irá integrar-se a sua aplicação. Por exemplo, o Spring, por exemplo, faz consultas utilizando o RestTemplate, por padrão. Utilizar outro framework implicaria em adicionar dependências no pom.xml ou build.gradle do seu projeto que poderiam, talvez, causar conflitos.
Vamos utilizar, então, o RestTemplate como biblioteca de consulta, ao invés do OkHTTP que vem por padrão. Execute o comando:
java -jar openapi-generator-cli-4.3.0.jar generate -i livro-api.yaml \
--api-package com.douglas.livros_service.api \
--model-package com.douglas.livros.model \
--invoker-package com.douglas.invoker \
--group-id com.douglas \
--artifact-id liblivros \
--artifact-version 0.0.1-SNAPSHOT \
-g java --library resttemplate -o liblivros
O código resultante pode ser tanto exportado com um pacote compilado .JAR quanto importado como código diretamente em outro projeto. Dentro da pasta, verificamos que foi gerado tanto um pom.xml quanto um build.gradle, isto é, permitindo a compilação tanto por Maven quanto por Gradle.
Vamos construir o nosso .JAR executando o seguinte comando dentro da biblioteca:
mvn clean install -Dmaven.javadoc.skip=true
Se o seu Java for versão 8 ou inferior, tudo ocorrerá bem. Caso contrário, haverá um problema devido a um bug relacionado a biblioteca javax.annotation. Para contornar este problema, no pom.xml adicione a seguinte dependência e execute novamente o comando anterior:
<!-- https://mvnrepository.com/artifact/javax.annotation/javax.annotation-api -->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version><!-- ou a versão mais atual -->
</dependency>
Está pronto. Dentro da pasta target você encontrará o seu pacote .JAR, neste exemplo o liblivros-0.0.1-SNAPSHOT-tests.jar, e poderá importar dentro de outra aplicação ou disponibilizá-la para terceiros o fazer.
Link da biblioteca gerada com a API: https://github.com/limadantas/exemplo_liblivros
Documentação oficial do OpenAPI Generator: https://openapi-generator.tech/docs/usage/
Parabéns, muito bom.
Parabéns, muito bom