⚗️
Consumindo APIs com Elixir
  • Introdução
  • Sobre o autor
  • O valor desse livro
  • Introdução
    • Por que elixir?
    • Como ler este livro
    • Sobre o conteúdo do livro
  • Configurando ambiente
    • Instalando o Elixir
    • Criando um projeto
  • Construindo um cliente usando Tesla
    • Iniciando
    • Tesla
      • O que é o Tesla
      • Instalando Tesla
    • Criando o Client
    • Estruturando resposta
    • Estratégia de teste para requisições
    • Instalando Bypass
    • Mockando requisições do cliente com Bypass
    • Tratando dados da resposta
  • Problemas de API externa
    • Erro genérico
    • O que é o rate limit
    • Rate Limite de curta duração
      • Reexecutando uma requisição
    • Rate Limit de longa duração
      • Agendando uma nova tentativa de requisição
      • Configurações necessárias
      • Adicionando Ecto ao projeto
      • O que é o Oban
      • Instalando Oban
      • Criando uma requisição assíncrona
      • Configurando quantidade de tentativas no Oban
  • Compondo integrações
    • Level up
    • Marvel API
      • Criando uma conta
      • Lendo o endpoint de Comics
      • Criando o cliente da Marvel
        • Melhorando a segurança
      • Lidando com a resposta
    • Aproveitando ao máximo o Rate Limit
  • Em breve
    • WIP - Supervisor
    • WIP - OAuth
    • WIP - Cacheando requisições
Fornecido por GitBook
Nesta página

Isto foi útil?

  1. Compondo integrações
  2. Marvel API

Criando o cliente da Marvel

Vamos lá mais uma vez. Para termos a conexão com um serviço, precisamos criar essa conexão. Aqui estamos chamando ele de Client e vivera no contexto de integrações. Criaremos o arquivo lib/coffee_shop/integrations/marvel_comics/client.ex utilizando o mesmo padrão do cliente do café já criado.

Iremos decompor essa chamada do tesla para dentro de nosso novo modulo utilizando os padrões estabelecidos nos capitulos anteriores

Tesla.get("https://gateway.marvel.com/v1/public/comics?apikey=12345&hash=7EE341DFFE88E697114117AA51E1A210&ts=1")
  • Utilizaremos uma função privada para configurar nosso cliente Tesla de forma práticas para os testes.

  • Colocaremos a url base em um função isolada, decidindo chamar ela de https://gateway.marvel.com/v1/public uma vez que utilizaremos sempre essa base.

  • Criaremos uma função publica que fara a requisição

lib/coffee_shop/integrations/marvel_comics/client.ex
defmodule CoffeeShop.Integrations.MarvelComics.Client do
   def comics(opts \\ []) do
    base_url = Keyword.get(opts, :base_url, base_url())

    opts
    |> new_client()
    |> Tesla.get("#{base_url}/v1/public/comics")
  end

  defp new_client(_opts) do
    middlewares = []

    Tesla.client(middlewares)
  end

  defp base_url() do
    "https://gateway.marvel.com"
  end
end

Essa é a cara base. Precisamos de uma teste para ele.

test/coffee_shop/integrations/marvel_comics/client_test.exs
defmodule CoffeeShop.Integrations.MarvelComics.ClientTest do
  use ExUnit.Case

  alias CoffeeShop.Integrations.MarvelComics.Client

  setup do
    bypass = Bypass.open(port: 3000)
    {:ok, bypass: bypass}
  end

  describe "comics/0" do
    test "respond a list of comics", %{bypass: bypass} do
      response = ""

      Bypass.expect_once(bypass, "GET", "/v1/public/comics", fn conn ->
        assert %{
          "apiKey" => _,
          "hash" => _,
          "ts" => _
        } = conn.query_params

        Plug.Conn.resp(conn, 200, response)
      end)

      opts = [
        base_url: "http://localhost:3000"
      ]

      assert {:ok, response} = Client.comics(opts)
      assert response.status == 200
    end
  end
end

Nesse teste, queremos garantir que a requisição chamada é do recurso que queremos. Também queremos garantir que os parâmetros hash, api_key e ts estão sendo enviados juntos.

mix test test/coffee_shop/integrations/marvel_comics/client_test.exs
> mix test test/coffee_shop/integrations/marvel_comics/client_test.exs                                                                                                                                                                main [df2d2e3] modified untracked
Compiling 1 file (.ex)
....
16:33:14.917 [error] #PID<0.407.0> running Bypass.Plug (connection #PID<0.406.0>, stream id 1) terminated
Server: localhost:3000 (http)
Request: GET /v1/public/comics
** (exit) an exception was raised:
    ** (ExUnit.AssertionError) 

match (=) failed
code:  assert %{"apiKey" => _, "hash" => _, "ts" => _} = conn.query_params
left:  %{"apiKey" => _, "hash" => _, "ts" => _}
right: %{}

        test/coffee_shop/integrations/marvel_comics/client_test.exs:16: anonymous fn/1 in CoffeeShop.Integrations.MarvelComics.ClientTest."test comics/0 respond a list of comics"/1
        (bypass 2.1.0) lib/bypass/plug.ex:14: Bypass.Plug.call/2
        (plug_cowboy 2.7.1) lib/plug/cowboy/handler.ex:11: Plug.Cowboy.Handler.init/2
        (cowboy 2.12.0) /root/code/study/api/coffee_shop/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
        (cowboy 2.12.0) /root/code/study/api/coffee_shop/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
        (cowboy 2.12.0) /root/code/study/api/coffee_shop/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3
        (stdlib 5.0.2) proc_lib.erl:241: :proc_lib.init_p_do_apply/3


  1) test comics/0 respond a list of comics (CoffeeShop.Integrations.MarvelComics.ClientTest)
     test/coffee_shop/integrations/marvel_comics/client_test.exs:12
     Assertion with == failed
     code:  assert response.status == 200
     left:  500
     right: 200
     stacktrace:
       test/coffee_shop/integrations/marvel_comics/client_test.exs:30: (test)

Como vimos na leitura do endpoint, precisamos enviar os três parâmetros para conseguir realizar a chamada. Vamos adiciona-los.

lib/coffee_shop/integrations/marvel_comics/client.ex
defmodule CoffeeShop.Integrations.MarvelComics.Client do
  def comics(opts \\ []) do
    base_url = Keyword.get(opts, :base_url, base_url())

    opts
    |> new_client()
    |> Tesla.get("#{base_url}/v1/public/comics")
  end

  defp new_client(_opts) do
    middlewares = [
      {Tesla.Middleware.Query, [apikey: "12345", hash: "7EE341DFFE88E697114117AA51E1A210", ts: 1]}
    ]

    Tesla.client(middlewares)
  end

  defp base_url() do
    "https://gateway.marvel.com"
  end
end

Decidimos seguir o mesmo padrão adotado no cliente do café e tiramos proveito disso. Basta adicionar o middleware direto na criação do Tesla.client. Com isso, podemos rodar os teses novamente.

mix test test/coffee_shop/integrations/marvel_comics/client_test.exs
> mix test test/coffee_shop/integrations/marvel_comics/client_test.exs                                                                                                                         main [df2d2e3] modified untracked
.
Finished in 0.1 seconds (0.00s async, 0.1s sync)
1 test, 0 failures

Com isso garantimos que estamos passando todos os dados que precisamos para realizar a requisição.

AnteriorLendo o endpoint de ComicsPróximoMelhorando a segurança

Atualizado há 1 ano

Isto foi útil?

Para adicionar query_params, Tesla possui um que facilita o trabalho para nós. Vamos usa-lo.

middleware