Criando Bibliotecas para APIs RESTful com OpenAPI, Swagger Editor e OpenAPI Generator

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

Aspecto do Swagger Editor.

Primeiramente, importe a descrição através do menu File, onde você terá duas opções:

Aspecto do menu File do Swagger Editor.

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.

Diálogo de importação através da URL.

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.

Conjunto de linguagens para a qual se pode gerar códigos através do Swagger Editor.

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/

2 thoughts on “Criando Bibliotecas para APIs RESTful com OpenAPI, Swagger Editor e OpenAPI Generator

Deixe um comentário