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.

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

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.

Atualizado