# Criando o Client

O cliente é parte que cuidará de sua integração. Terá todas as configurações necessárias para conseguirmos nos conectar a um serviço. Também teremos algumas estratégias como politica de *Retry* e *Cache*. Ele vai ser nosso contexto para o serviço.  Fazendo assim, deixando as coisas mais organizadas.

Vamos do início. Precisamos criar nosso cliente para se comunicar com o [SampleApis](https://sampleapis.com/), um serviço de exemplos de APIs que estamos usando como base em nossos estudos. Ele será responsável em realizar a requisição ao *serviço* e trazer os dados. Também irá lidar com possíveis erros.&#x20;

Em nosso primeiro exemplo, queremos trazer a lista do elixir da vida, também chamado de cafézinho quente. Para isso precisamos saber onde estamos pisando.&#x20;

Vamos fazer uma lista de objetivos. Gosto de listas bem definidas.

O serviço a ser usado encontrasse na URL `https://api.sampleapis.com/coffee/hot`, e seu verbo *HTTP* é *GET*. Isso quer dizer, se você abrir esse link em seu navegador você vai ver uma lista de itens no formato em JSON de cafezinhos quente, como vimos [nesse exemplo](https://app.gitbook.com/o/emUaSshwpl9F47G6Ig0P/s/egTKEnXxNB5i4htSH2x4/~/changes/46/construindo-um-cliente-usando-tesla/o-que-e-o-tesla-rever-titulo).

Agora precisamos delegar essa ação para o Tesla. Nossa lista de tarefa então será

* Obter lista de cafés quentes
* Testar resposta de sucesso com os cafés quentes

### Implementação

Criaremos um novo arquivo dentro de uma pasta ***integrations***. Esse será a pasta do contexto de integrações. Tudo referente a ele viverá ali. Seguirei a ideia de [Screaming Archtecture](https://medium.com/@mubashirhussain29/the-screaming-architecture-story-08750691291f) para ter um norte.

```
mkdir lib/coffee_shop/integrations/coffee
```

Em sua pasta, criamos um arquivo chamado *client.ex* referente ao cliente do serviço.

{% code title="lib/coffee\_shop/integrations/coffee/client.ex" lineNumbers="true" %}

```elixir
defmodule CoffeeShop.Integrations.Coffee.Client do
 
end
```

{% endcode %}

Nele vamos criar nossa função de requisição, chamarei ela de `all_hot_coffees/0`

{% code title="lib/coffee\_shop/integrations/coffee/client.ex" lineNumbers="true" %}

```elixir
defmodule CoffeeShop.Integrations.Coffee.Client do
  def all_hot_coffees do
  
  end
end
```

{% endcode %}

Agora precisamos entender onde devemos requisitar o serviço

```
GET https://api.sampleapis.com/coffee/hot
```

Vamos traduzir isso para código.&#x20;

Precisamos que o tesla execute uma chamada **GET** para esse recurso. O *Tesla* possui funçoes de acordo com o verbo que precisamos `Tesla.post/2`, `Tesla.get/1`, `Tesla.delete/1` e `Tesla.put/2` . Obviametne, precisamos do `Tesla.get/1` onde seu parâmetro é o recurso que iremos requisitar, sendo `https://api.sampleapis.com/coffee/hot`.

```elixir
Tesla.get("https://api.sampleapis.com/coffee/hot")
```

Uma boa leitura vindo do nosso requisito, certo? Vamos colocar esse código para funcionar.

<pre class="language-elixir" data-title="lib/coffee_shop/integrations/coffee/client.ex" data-line-numbers><code class="lang-elixir">defmodule CoffeeShop.Integrations.Coffee.Client do
  def all_hot_coffees do
<strong>    Tesla.get("https://api.sampleapis.com/coffee/hot")
</strong>  end
end
</code></pre>

Perfeito. Uma coisa que tenho que te falar. Tesla possui o conceito de [middlewares](https://hexdocs.pm/tesla/Tesla.Middleware.html). Eles são comportamentos que executam antes de um requisição acontecer. Podemos usar ele para simplificar um pouco o código. Como por exemplo, configurar nossa url base, ao invez de colocar tudo diretamente na função. Para conseguir usar os middlewares de forma limpa, podemos usar uma macro do Tesla e transformar nosso Client em um Tesla.Client. Isso cria uma dependência direta. Não gosto muito disso, mas iremos por esse lado para explorar todo o potencial do Tesla. Com a macro, podemos utilizar plug para configurar os middlewares e utilizaremos o middleware [BaseUrl](https://hexdocs.pm/tesla/Tesla.Middleware.BaseUrl.html). Com a macro e o middleware, podemos remover um pouco de codigo.

{% code title="lib/coffee\_shop/integrations/coffee/client.ex" lineNumbers="true" %}

```elixir
defmodule CoffeeShop.Integrations.Coffee.Client do
  use Tesla

  plug Tesla.Middleware.BaseUrl, "https://api.sampleapis.com"

  def all_hot_coffees do
    get("/coffee/hot")
  end
end
```

{% endcode %}

Nossa função ficou mais simples. Vamos continuar assim por um tempo. Com tudo devidamente configurado, podemos rodar nossa função de requisição pelo terminal iterativo. Acesse o terminal.

```
iex -S mix
```

Chame a função croiada diretamente.

```elixir
CoffeeShop.Integrations.Coffee.Client.all_hot_coffees()
```

```elixir
iex(1)> CoffeeShop.Integrations.Coffee.Client.all_hot_coffees()
{:ok,
 %Tesla.Env{
   method: :get,
   url: "https://api.sampleapis.com/coffee/hot",
   query: [],
   headers: [
     {"connection", "keep-alive"},
     {"date", "Wed, 10 Apr 2024 20:41:08 GMT"},
     {"etag", "W/\"21df-Qjes7uaeQItDszmliRPvMUXoGIs\""},
     {"server", "cloudflare"},
     {"content-length", "8671"},
     {"content-type", "application/json; charset=utf-8"},
     {"x-powered-by", "Express"},
     {"access-control-allow-origin", "*"},
     {"x-ratelimit-limit", "5000"},
     {"x-ratelimit-remaining", "4988"},
     {"x-ratelimit-reset", "1712781684"},
     {"x-content-type-options", "nosniff"},
     {"cf-cache-status", "DYNAMIC"},
     {"report-to",
      "{\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=vdnOEvbwkmUy92e5RTJWIlW8yFKQNctDQEAemw5dNp5KfO8XJWWpDUx7hg%2B%2F99CuPdhTNsilv0kbqA554IsuLjdGRG9kys%2FGX1SpC08NAxF8nrw67hqNs6lhnGsQffS6doqnODg%3D\"}],\"group\":\"cf-nel\",\"max_age\":604800}"},
     {"nel",
      "{\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}"},
     {"cf-ray", "87258e520d730319-GRU"},
     {"alt-svc", "h3=\":443\"; ma=86400"}
   ],
   body: "[{\"title\":\"Black Coffee\",\"description\":\"Svart kaffe är så enkelt som det kan bli med malda kaffebönor dränkta i hett vatten, serverat varmt. Och om du vill låta fancy kan du kalla svart kaffe med sitt rätta namn: café noir.\",\"ingredients\":[\"Coffee\"],\"image\":\"https://images.unsplash.com/photo-1494314671902-399b18174975?auto=format&fit=crop&q=80&w=1887&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\",\"id\":1},{\"title\":\"Latte\",\"description\":\"Som den mest populära kaffedrycken där ute består latte av en skvätt espresso och ångad mjölk med bara en gnutta skum. Den kan beställas utan smak eller med smak av allt från vanilj till pumpa kryddor.\",\"ingredients\":[\"Espresso\",\"Ångad mjölk\"],\"image\":\"https://images.unsplash.com/photo-1561882468-9110e03e0f78?auto=format&fit=crop&q=60&w=800&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTl8fGxhdHRlfGVufDB8fDB8fHww\",\"id\":2},{\"title\":\"Caramel Latte\",\"description\":\"Om du gillar latte med en speciell smak kan karamell latte vara det bästa alternativet för att ge dig en upplevelse av den naturliga sötman och krämigheten hos ångad mjölk och karamell.\",\"ingredients\":[\"Espresso\",\"Ångad mjölk\",\"Karamellsirap\"],\"image\":\"https://images.unsplash.com/photo-1599398054066-846f28917f38?auto=format&fit=crop&q=80&w=1887&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\",\"id\":3},{\"title\":\"Cappuccino\",\"description\":\"Cappuccino är en latte som är gjord med mer skum än ångad mjölk, ofta med ett strö av kakaopulver eller kanel på toppen. Ibland kan du hitta variationer som använder grädde istället för mjölk eller sådana som tillsätter smakämnen också.\",\"ingredients\":[\"Espresso\",\"Ångad mjölk\",\"Foam\"],\"image\":\"https://images.unsplash.com/photo-1557006021-b85faa2bc5e2?auto=format&fit=crop&q=80&w=1887&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\",\"id\":4},{\"title\":\"Americano\",\"description\":\"Med en liknande smak som svart kaffe består americano av en espresso skott utspätt med hett vatten.\",\"ingredients\":[\"Espresso\",\"Hett vatten\"],\"image\":\"https://images.unsplash.com/photo-1532004491497-ba35c367d634?auto=format&fit=crop&q=80&w=1887&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\",\"id\":5},{\"title\":\"Espresso\",\"description\":\"Ett espressoskott kan serveras ensamt eller användas som grund för de flesta kaffedrycker, som latte och macchiato.\",\"ingredients\":[\"Espresso\"],\"image\":\"https://images.unsplash.com/photo-1579992357154-faf4bde95b3d?auto=format&fit=crop&q=80&w=1887&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\",\"id\":6},{\"title\":\"Macchiato\",\"description\":\"Macchiaton är en annan espresso-baserad dryck som har en liten mängd skum på toppen. Det är det glada mellanrummet mellan en cappuccino och en doppio.\",\"ingredients\":[\"Espresso\",\"Foam\"],\"image\":\"https://images.unsplash.com/photo-1557772611-722dabe20327?auto=format&fit=crop&q=80&w=1887&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\",\"id\":7},{\"title\":\"Mocha\",\"description\":\"För alla chokladälskare där ute kommer ni att bli förälskade i en mocha. Mocha är en choklad-espressodryck med ångad mjölk och skum.\",\"ingredients\":[\"Espresso\",\"Ångad mjölk\",\"Choklad\"],\"image\":\"https://images.unsplash.com/photo-1607260550778-aa9d29444ce1?auto=format&fit=crop&q=80&w=1887&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\",\"id\":8},{\"title\":\"Hot Chocolate\",\"description\":\"Under kalla vinterdagar får en kopp varm choklad dig att känna dig bekväm och lycklig. Den får dig också att må bra eftersom den innehåller energigivande koffein.\",\"ingredients\":[\"Choklad\",\"Mjölk\"],\"image\":\"https://images.unsplash.com/photo-1542990253-0d0f5be5f0ed?auto=format&fit=crop&q=60&w=800&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8NDh8fGhvdCUyMGNob2NvbGF0ZXxlbnwwfHwwfHx8MA%3D%3D\",\"id\":9},{\"title\":\"Chai Latte\",\"description\":\"Om du letar efter en smakfull varm dryck mitt i vintern, välj chai latte. Kombinationen av kardemumma och kanel ger en underbar smak.\",\"ingredients\":[\"Te\",\"Mjölk\",\"Ingefära\",\"Kardemumma\",\"Kanel\"],\"image\":\"https://images.u" <> ...,
   status: 200,
   opts: [],
   __module__: CoffeeShop.Integrations.Coffee.Client,
   __client__: %Tesla.Client{fun: nil, pre: [], post: [], adapter: nil}
 }}
```

Que coisa linda não? Você fez sua primeira requisição a um serviço externo programaticamente. Você consegue ver a lista de cafés e usar em sua aplicação. Para confirmar que tudo está vindo como queremos, você pode ir no atributo *body* e notará que tem uma resposta em JSON.&#x20;

Muito mais fácil que você imaginava não? Porém, ainda temos alguns objetivos

* ~~Obter lista de cafés quentes~~
* Testar a resposta de sucesso com os cafés

### Teste

Estamos usando um serviço externo para obter dados. Isso nos poupa tempo em relação a desenvolver uma solução. Mas precisamos garantir que nossa implementação sempre venha a funcionar. Para isso, podemos realizar testes programaticamente.

Iremos criar nosso arquivo de teste replicando o caminho da implementação e adicionaremos a base para nossos cenários a serem cobertos.

{% code title="test/coffee\_shop/integrations/coffee/client\_test.exs" lineNumbers="true" %}

```elixir
defmodule CoffeeShop.Integrations.Coffee.ClientTest do
  use ExUnit.Case
  
  describe "all_hot_coffees/0" do
    test "respond a list of hot coffees" do
   
    end
  end
end
```

{% endcode %}

Estamos utilizando o [*ExUnit* ](https://hexdocs.pm/ex_unit/ExUnit.html)para nossos testes e com ele, vamos criar nossas afirmações:

<pre class="language-elixir" data-title="test/coffee_shop/integrations/coffee/client_test.exs" data-line-numbers><code class="lang-elixir">defmodule CoffeeShop.Integrations.Coffee.ClientTest do
  use ExUnit.Case
  
<strong>  alias CoffeeShop.Integrations.Coffee.Client
</strong>  
  describe "all_hot_coffees/0" do
    test "respond a list of hot coffees" do
<strong>      assert {:ok, _response} = Client.all_hot_coffees()
</strong>    end
  end
end
</code></pre>

Rodando o teste

```sh
mix test test/integrations/coffee/client_test.exs
```

```sh
> mix test test/integrations/coffee/client_test.exs
.
Finished in 0.6 seconds (0.00s async, 0.6s sync)
1 test, 0 failures

Randomized with seed 209276
```

Precisamos também garantir que a resposta seja um Tesla.Env, estrutura que o Tesla devolve em suas respostas. Vamos adicionar ao nosso teste.

{% code title="test/integrations/coffee/client\_test.exs" lineNumbers="true" %}

```elixir
defmodule CoffeeShop.Integrations.Coffee.ClientTest do
  use ExUnit.Case

  alias CoffeeShop.Integrations.Coffee.Client

  describe "all_hot_coffees/0" do
    test "respond a list of hot coffees" do
      assert {:ok, %Tesla.Env{}} = Client.all_hot_coffees()
    end
  end
end

```

{% endcode %}

```sh
mix test test/integrations/coffee/client_test.exs
```

```sh
> mix test test/integrations/coffee/client_test.exs
.
Finished in 0.7 seconds (0.00s async, 0.7s sync)
1 test, 0 failures
```

### Conclusão

Nosso primeiro *test* esta funcionando, isso quer dizer, menos um item na lista. Fácil não? Para que um livro desse? =D

* ~~Obter dados quando a resposta for um sucesso~~
* **Tratar resposta quando a resposta for um erro**
* Devemos ter acesso fácil a lista de cafés

Nosso próximo passo é testar o comportamento quando a resposta de nosso serviço volta um erro.  A pergunta mais comum aqui é, como diabos eu farei gerar um erro nisso se o serviço não é meu?

Hora do mock.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://consumindo-apis-com-elixir.cafecomelixir.com.br/construindo-um-cliente-usando-tesla/criando-o-client.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
