🔌 ATIVIDADE 14: DESIGN DE APIS REST E SWAGGER

📖 Fundamentação Teórica

Para realizar este laboratório com sucesso, certifique-se de ter compreendido os conceitos apresentados no:
👉 CAPÍTULO 14: DIAGRAMA DE SEQUÊNCIA

Bem-vindo a mais uma etapa prática de Engenharia de Software! Agora que você conhece as camadas internas do software, aprenderá como expor e conectar o seu sistema ao mundo externo. Hoje, vamos nos tornar arquitetos de comunicação móvel e web, projetando contratos de integração baseados em APIs RESTful, payloads de dados JSON e documentando tudo com o padrão profissional global Swagger (OpenAPI). 🛡️🧩


🎯 Objetivos de Aprendizagem do Laboratório

Ao final deste laboratório prático (estimativa: 4 horas presenciais / autoguiadas), você será capaz de:

  • Mapear recursos de sistemas no formato e boas práticas de APIs REST.
  • Utilizar os Verbos HTTP (GET, POST, PUT, DELETE) e os Códigos de Retorno (Status Codes) corretos.
  • Escrever payloads estruturados em formato JSON (JavaScript Object Notation).
  • Interpretar e projetar documentações no padrão Swagger/OpenAPI.

🏢 O Cenário Prático (Seu Desafio)

Na TecProExpress, os motoristas que usam o aplicativo Android precisam enviar em tempo real a geolocalização do caminhão para o painel de controle do SAC. Além disso, o app precisa buscar a lista de entregas pendentes direto do servidor central. No entanto, os desenvolvedores Mobile e os programadores de Backend não combinaram o "formato de dados" e a comunicação travou!

O app móvel mandava POST /api/enviar_localizacao e o servidor dava erro de conexão porque esperava receber um formato diferente do JSON enviado pelo celular!

"Seu desafio como Designer de APIs é desenhar o contrato profissional de endpoints para a TecProExpress. Você criará a modelagem de recursos RESTful para /api/entregas e /api/motoristas/localizacao, definindo os verbos corretos, os dados em formato JSON e gerando a especificação profissional baseada em Swagger/OpenAPI."


🧠 Fundamentos: A Teoria Traduzida

Uma API (Application Programming Interface) é uma porta que permite que sistemas diferentes conversem de forma organizada. REST é o estilo arquitetural padrão da web.

Regras de Ouro do Design REST:

  1. Recursos são Substantivos no Plural: Evite verbos no endereço!
    • Errado: POST /api/salvarMotorista
    • Certo: POST /api/motoristas
  2. Use os Verbos HTTP Adequados:
    • GET: Busca um ou vários registros (Ex: GET /api/entregas).
    • POST: Cria um novo registro (Ex: POST /api/entregas).
    • PUT: Atualiza o registro inteiro por completo.
    • DELETE: Exclui um registro da base de dados.
  3. Comunique-se por Status Codes:
    • 200 OK: Requisição de consulta ou alteração com sucesso.
    • 201 Created: Novo registro criado com sucesso (retorno típico de POST).
    • 400 Bad Request: Envio de dados incorretos ou campos faltantes.
    • 404 Not Found: O registro ou endpoint consultado não existe.
    • 500 Internal Server Error: Falha técnica no servidor (bug).

📊 Visualizando a Comunicação HTTP e Swagger

sequenceDiagram
    autonumber
    actor Cliente as 📱 App Mobile / Frontend
    participant API as 🌐 Flask Router
    participant Schema as 🛡️ Validação de Payload
    participant DB as 🗄️ PostgreSQL

    Cliente->>API: POST /api/entregas (JSON Payload)
    API->>Schema: Valida Tipos e Constraints
    alt Dados Válidos
        Schema-->>API: Dados Aprovados
        API->>DB: INSERT INTO entregas (...)
        DB-->>API: Confirmação de Persistência
        API-->>Cliente: 201 Created { "id": 125, "status": "PENDENTE" }
    else Dados Inválidos
        Schema-->>API: Erro de Validação (ex: peso <= 0)
        API-->>Cliente: 400 Bad Request (JSON com detalhes do erro)
    end

📖 Exemplo Guiado: Especificação OpenAPI / Swagger e API em Flask

No Flask, implementamos a lógica de rota e validação em Python, e documentamos o contrato correspondente no padrão OpenAPI 3.0 (Swagger):

🛠️ Código Python 3.11 no Flask (app_entregas.py):

import sys
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.post("/api/entregas")
def criar_entrega():
    """Cria uma nova entrega e retorna o objeto persistido."""
    dados = request.get_json()
    if not dados:
        return jsonify({"erro": "Payload JSON ausente"}), 400

    cliente_id = dados.get("cliente_id")
    endereco_destino = dados.get("endereco_destino")
    peso_carga_kg = dados.get("peso_carga_kg", 0.0)
    tipo_notificacao = dados.get("tipo_notificacao", "SMS")

    if not cliente_id or not endereco_destino:
        return jsonify({"erro": "cliente_id e endereco_destino são obrigatórios"}), 400
    if peso_carga_kg <= 0:
        return jsonify({"erro": "peso_carga_kg deve ser maior que zero"}), 400

    resposta = {
        "id": 125,
        "cliente_id": cliente_id,
        "endereco_destino": endereco_destino,
        "peso_carga_kg": peso_carga_kg,
        "tipo_notificacao": tipo_notificacao,
        "status": "PENDENTE"
    }
    return jsonify(resposta), 201

if __name__ == "__main__":
    if "--server" in sys.argv:
        print("🚀 Servidor de Entregas Flask rodando em http://127.0.0.1:5000")
        app.run(port=5000, debug=True)
    else:
        print("--- Teste Automatizado com Flask test_client() ---")
        with app.test_client() as client:
            res = client.post("/api/entregas", json={
                "cliente_id": 45,
                "endereco_destino": "Av. Paulista, 1000 - São Paulo, SP",
                "peso_carga_kg": 12.5,
                "tipo_notificacao": "SMS"
            })
            print(f"Status HTTP: {res.status_code}")
            print(f"Resposta JSON: {res.get_json()}")

📄 O Contrato JSON enviado pelo App Mobile (Payload):

{
  "cliente_id": 45,
  "endereco_destino": "Av. Paulista, 1000 - São Paulo, SP",
  "peso_carga_kg": 12.5,
  "tipo_notificacao": "SMS"
}

📄 Documentação Swagger (OpenAPI 3.0 YAML) correspondente:

openapi: 3.0.3
info:
  title: API TecProExpress
  version: 1.0.0
paths:
  /api/entregas:
    post:
      summary: Criar nova entrega
      tags:
        - Entregas
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - cliente_id
                - endereco_destino
              properties:
                cliente_id:
                  type: integer
                endereco_destino:
                  type: string
                peso_carga_kg:
                  type: number
                tipo_notificacao:
                  type: string
      responses:
        '201':
          description: Criado com sucesso
        '400':
          description: Dados inválidos

💻 Execução do Servidor Flask & Teste cURL no Terminal

Para iniciar o servidor e executar requisições reais contra a API:

🔹 1. Iniciar Servidor:

python app_entregas.py --server

🔹 2. Requisição cURL enviando o Payload JSON:

curl -X POST "http://127.0.0.1:5000/api/entregas" \
     -H "Content-Type: application/json" \
     -d '{
       "cliente_id": 45,
       "endereco_destino": "Av. Paulista, 1000 - São Paulo, SP",
       "peso_carga_kg": 12.5,
       "tipo_notificacao": "SMS"
     }'

🖥️ Saída Esperada no Terminal:

HTTP/1.1 201 Created
content-type: application/json

{
  "cliente_id": 45,
  "endereco_destino": "Av. Paulista, 1000 - São Paulo, SP",
  "peso_carga_kg": 12.5,
  "tipo_notificacao": "SMS",
  "id": 125,
  "status": "PENDENTE"
}

🛠️ Prática Obrigatória 1: Tabela de Endpoints da API

Cenário: O projeto semestral da sua equipe.

  1. Mapeie 3 endpoints críticos que o seu sistema precisará expor para um aplicativo celular ou sistema parceiro.
  2. Organize-os em uma tabela contendo: Verbo HTTP, Caminho (URL), O que faz (Descrição) e Status Code de Sucesso (ex: 200, 201).

🏁 Resultado Esperado (Para sua Referência)

Uma tabela com o roteiro de comunicação REST detalhando as requisições principais de forma limpa e seguindo as boas práticas.


🛠️ Prática Obrigatória 2: O Payload JSON e Contrato Swagger

Cenário: Detalhando os dados de tráfego.

  1. Para o endpoint principal de inserção de dados da Prática 1 (Ex: criar agendamento, realizar venda), escreva o payload em formato JSON real que o cliente enviará.
  2. Escreva a especificação profissional simplificada no padrão Swagger/OpenAPI (formato YAML) que descreve esse endpoint e o corpo da requisição de forma estruturada.

📤 Instruções de Entrega (Microsoft Teams)

Após validar o seu design de API:

  1. Salve o arquivo contendo a tabela de endpoints, o JSON e a especificação Swagger YAML com o nome Atividade_14.md na pasta es-atv-14-apis-swagger/ do seu repositório GitHub.
  2. Certifique-se de fazer o commit e push para o repositório público.
  3. Submeta o link do seu repositório no Microsoft Teams para avaliação do professor.

💡 Checkpoint de Lógica

Importante

Reflexão Profissional: Por que no design REST profissional, nunca usamos o verbo GET para realizar a deleção de um dado (ex: GET /api/entregas/excluir?id=5) em vez do correto DELETE (ex: DELETE /api/entregas/5)? (Resposta: Pela natureza idempotente e segura dos verbos HTTP. Navegadores e proxies de cache assumem que requisições GET são seguras e não causam efeitos colaterais. Se você usar GET para excluir, um robô de busca de indexação automática da internet ao varrer os links do seu sistema poderia, acidentalmente, apagar o banco de dados inteiro da empresa!). 🧠🛡️

---

📊 Rubrica Formativa de Avaliação

Critério de Avaliação Insuficiente (0% - 40%) Regular (41% - 70%) Excelente (71% - 100%)
Design de Endpoints REST & Verbos HTTP Usa verbos incorretos (ex: GET para exclusão) ou URLs fora do padrão RESTful. Mapeia os 3 endpoints mas omite códigos de status de resposta (Status Codes). Tabela de endpoints impecável com verbos HTTP semânticos (GET, POST, PUT, DELETE) e Status Codes corretos (200, 201, 204).
Payload JSON & Especificação Swagger OpenAPI YAML Erros na sintaxe JSON ou formato YAML desalinhado. Escreve o JSON mas omite a especificação Swagger YAML. Payload JSON de requisição perfeito e contrato OpenAPI 3.0 YAML completamente válido.
Entrega no GitHub Entrega fora da pasta `es-atv-14-apis-swagger/`. Arquivo entregue mas com formatação YAML quebrada. Submete `Atividade_14.md` com formatação limpa e blocos de código formatados no repositório.