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, 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.
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.
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.
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 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.
Agora precisamos entender onde devemos requisitar o serviço
GET https://api.sampleapis.com/coffee/hot
Vamos traduzir isso para código.
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.
Perfeito. Uma coisa que tenho que te falar. Tesla possui o conceito de middlewares. 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. Com a macro e o middleware, podemos remover um pouco de codigo.
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(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.
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.
defmoduleCoffeeShop.Integrations.Coffee.ClientTestdouseExUnit.CasealiasCoffeeShop.Integrations.Coffee.Client describe "all_hot_coffees/0"do test "respond a list of hot coffees"do assert {:ok,_response} =Client.all_hot_coffees()endendend
Rodando o teste
mixtesttest/integrations/coffee/client_test.exs
> mix test test/integrations/coffee/client_test.exs.Finishedin0.6seconds (0.00s async,0.6ssync)1test,0failuresRandomizedwithseed209276
Precisamos também garantir que a resposta seja um Tesla.Env, estrutura que o Tesla devolve em suas respostas. Vamos adicionar ao nosso teste.
test/integrations/coffee/client_test.exs
defmoduleCoffeeShop.Integrations.Coffee.ClientTestdouseExUnit.CasealiasCoffeeShop.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()endendend
mixtesttest/integrations/coffee/client_test.exs
> mix test test/integrations/coffee/client_test.exs.Finishedin0.7seconds (0.00s async,0.7ssync)1test,0failures
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?