Typhoeus 1.4.1 e como utilizar em seu projeto Ruby

Typhoeus 1.4.1 e como utilizar em seu projeto Ruby
Photo by Caspar Camille Rubin / Unsplash

Recebemos uma solicitação para criar um vídeo em nosso canal, e estamos empenhados em oferecer também conteúdo escrito para que todos possam encontrar alternativas educacionais. Buscarei explicar da melhor maneira possível; embora os vídeos tenham suas vantagens, aqui o conteúdo escrito também pode ser extremamente útil.

Essa solicitação foi enviada através do Discord, então vamos lá.

Requisitos:

  • Ruby: versão 3.2.0
  • Bundler: versão 2.4.12

Agradecemos pela interação da nossa comunidade e pela oportunidade de atender às suas necessidades educacionais. Fiquem atentos aos próximos artigos e vídeos, pois compartilharemos tutoriais e dicas. Estamos animados em fornecer esse conteúdo. Caso tenham mais sugestões ou solicitações, não hesitem em compartilhar conosco no Discord ou em outras plataformas.

Abra o seu terminal e digite os comandos abaixo:

➜  language mkdir brasilapi
➜  language cd brasilapi && bundle init
Writing new Gemfile to /Users/ropeixoto/Project/skill.dev/youtube/languages/Ruby/language/brasilapi/Gemfile
➜  brasilapi mkdir lib
➜  brasilapi
  1. Vamos criar uma nova pasta, com o comando mkdir vamos criar com o nome brasilapi
  2. depois com o comando cd brasilapi acessamos a pasta que criamos, e com o && dizemos que ele irá executar os dois comandos no bash, então depois vamos inicializar o projeto bundle init , ele irá gerar o Gemfile
  3. depois criamos a pasta mkdir lib

feito? vamos abrir o nosso editor favorite, abre o seu, beleza? o meu vai ser o visual studio code. então code . assim, deveria abrir por causa do . a pasta atual no VSCode.

Abra o brasilapi/Gemfile e adicione as gems

# frozen_string_literal: true

source "https://rubygems.org"

# gem "rails"
gem 'typhoeus' # Lib para fazermos requests com o protocolo HTTP
gem 'rspec'    # Lib para os testes automatizados
gem 'pry'      # Lib para fazer debugger mais poderoso

Execute o comando:

bundle install

Depois vamos gerar os arquivos do rspec com o comando:

rspec --init

Você verá no seu console uma criação dos arquivos .rspec e spec/spec_helper.rb

Vamos agora verificar a documentação da BrasilApi e vamos iniciar os nossos testes, como sempre, crie um arquivo na pasta spec/bank_spec.rb

require 'spec_helper'
require_relative '../lib/bank'

RSpec.describe Bank do
  let(:bank_response) { Bank.new }
  describe '.all' do
    it 'does return all banks' do
      expect(bank_response.all).to be_kind_of(Array)
      expect(bank_response.all.first).to have_key('ispb')
      expect(bank_response.all.first).to have_key('name')
      expect(bank_response.all.first).to have_key('code')
      expect(bank_response.all.first).to have_key('fullName')
    end
  end
end

Execute o comando rspec spec

➜  brasilapi rspec spec

An error occurred while loading ./spec/bank_spec.rb.
Failure/Error:
  RSpec.describe Bank do
    let(:bank_response) { Bank.new }
    describe '.all' do
      it 'does return all banks' do
        expect(bank_response.all).to be_kind_of(Array)
        expect(bank_response.all.first).to have_key(:ispb)
        expect(bank_response.all.first).to have_key(:name)
        expect(bank_response.all.first).to have_key(:code)
        expect(bank_response.all.first).to have_key(:fullName)
      end

NameError:
  uninitialized constant Bank
# ./spec/bank_spec.rb:4:in `<top (required)>'
No examples found.


Finished in 0.00003 seconds (files took 0.14774 seconds to load)
0 examples, 0 failures, 1 error occurred outside of examples

Agora precisamos criar o arquivo lib/bank.rb e vamos iniciar a mágica:

require 'typhoeus'
require 'pry'
require 'json'

class Bank
  
  URL     = 'https://brasilapi.com.br/api/banks/v1'
  HEADERS = { 'Content-Type' => 'application/json' }
  
  def all
    bank_request = request(URL, :get).run
    JSON.parse(bank_request.body)
  end

  private

  def request(url, method)
    Typhoeus::Request.new(
      url,
      method: method,
      headers: HEADERS
    )
  end
end

Precisamos chamar as bibliotecas que são: typhoeus para a request, pry para debugger e a json para que eu possa transformar string em hash.

Na linha 5 criamos a classe e criamos duas constantes URL e HEADERS, esse são importantes pelo que a documentação nos mostra: https://brasilapi.com.br/docs#tag/BANKS

depois criamos um método privado request recebendo url como parâmetro e também o método get, post e etc... e depois inicializamos o objeto Typhoeus e passamos os parâmetros esperados.

então, criamos um all para podermos utilizar o endpoint principal e vamos complementar.

e agora vamos adicionar mais um endpooint

vai no arquivo spec/bank_spec.rb e adicione o novo bloco de código .find_by_id:

require 'spec_helper'
require_relative '../lib/bank'

RSpec.describe Bank do
  let(:bank_response) { Bank.new }
  describe '.all' do
    it 'does return all banks' do
      expect(bank_response.all).to be_kind_of(Array)
      expect(bank_response.all.first).to have_key('ispb')
      expect(bank_response.all.first).to have_key('name')
      expect(bank_response.all.first).to have_key('code')
      expect(bank_response.all.first).to have_key('fullName')
    end
  end
  describe '.find_by_id' do
    it 'does return all banks' do
      code = 1
      expect(bank_response.find_by_id(code)).to be_kind_of(Hash)
      expect(bank_response.find_by_id(code)).to have_key('ispb')
      expect(bank_response.find_by_id(code)).to have_key('name')
      expect(bank_response.find_by_id(code)).to have_key('code')
      expect(bank_response.find_by_id(code)).to have_key('fullName')
    end
  end
end

Adicione a URL e a interpolação com o code e assim, você conseguirá ter uma nova request, somente modificando um trecho do código:

require 'typhoeus'
require 'pry'
require 'json'

class Bank
  
  URL     = 'https://brasilapi.com.br/api/banks/v1'
  HEADERS = { 'Content-Type' => 'application/json' }
  
  def all
    banks_request = request(URL, :get).run
    JSON.parse(banks_request.body)
  end

  def find_by_id(code)
    bank_request = request("#{URL}/#{code}", :get).run
    JSON.parse(bank_request.body)
  end

  private

  def request(url, method)
    Typhoeus::Request.new(
      url,
      method: method,
      headers: HEADERS
    )
  end
end

Depois, vamos executar os nossos testes novamente, e estará perfeito passando o nosso exemplo:

rspec spec

saída

➜  brasilapi rspec
.

Finished in 0.5483 seconds (files took 0.30107 seconds to load)
1 example, 0 failures

➜  brasilapi

Algo que eu quero que você perceba é que fizemos uma request, então a todo momento que formos executar os testes, vamos ficar fazendo request a api? isso poderá ser um grande problema, sempre pense, tudo na computação é custo, se você utilizar o endpoint a todo momento a api te limitará, com um rate limite ou até mesmo, será bloqueado da api, então vamos e precisamos evitar.

Vamos para a parte II 😄 siga para os próximos capítulos.

Agora aparecerá o vcr vídeocacete em português. Vamos adicionar a gem no arquivo Gemfile , por que precisamos fazer talvez uma unica request e não fazer mais, salvar os dados localmente, certo?

então abra seu arquivo Gemfile e adicionar o código gem 'vcr':

# frozen_string_literal: true

source "https://rubygems.org"

# gem "rails"
gem 'typhoeus'
gem 'rspec'
gem 'pry'
gem 'vcr' # Essa gem

Vamos criar o Diretório para as Cassetes:

Certifique-se de criar o diretório para armazenar as cassetes. No seu projeto, crie a seguinte estrutura de diretórios:

spec/fixtures/vcr_cassettes e adicione também a pasta e arquivo spec/support/vcr_setup.rb

# spec/support/vcr_setup.rb

require 'vcr'

VCR.configure do |config|
  config.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
  config.hook_into :typhoeus
end

e depois abra o arquivo spec/spec_helper.rb

require_relative 'support/vcr_setup' # adicionar

Altere o código do arquivo spec/bank_spec.rb e verifique que adicionamos o vcr e um caminho que salvará o payload, vá na pasta dentro do seu arquivo, após executar os testes e verá uns dados yml.

require 'spec_helper'
require_relative '../lib/bank'
require 'vcr'

RSpec.describe Bank do
  let(:bank_response) { Bank.new }
  describe '.all' do
    it 'does return all banks' do
      VCR.use_cassette('bank/all') do
        expect(bank_response.all).to be_kind_of(Array)
        expect(bank_response.all.first).to have_key('ispb')
        expect(bank_response.all.first).to have_key('name')
        expect(bank_response.all.first).to have_key('code')
        expect(bank_response.all.first).to have_key('fullName')
      end
    end
  end
  describe '.find_by_id', vcr: { cassette_name: 'bank/find_by_id' } do
    it 'does return all banks' do
      VCR.use_cassette('bank/find_by_id') do
        code = 1
        expect(bank_response.find_by_id(code)).to be_kind_of(Hash)
        expect(bank_response.find_by_id(code)).to have_key('ispb')
        expect(bank_response.find_by_id(code)).to have_key('name')
        expect(bank_response.find_by_id(code)).to have_key('code')
        expect(bank_response.find_by_id(code)).to have_key('fullName')
      end
    end
  end
end

Verifique agora sua pasta brasilapi/spec/fixtures/vcr_cassettes/bank/*.yml
Poderá verificar que há o payload organizado em yml e a partir de agora ele vai olhar para esse arquivo, caso queira fazer request novamente, basta remover o arquivo.

Agora testando o 404 que está na documentação, adicionamos a linha 33:

context 'when error' do it 'does return BANK_CODE_NOT_FOUND for non-existing bank code' do

require 'spec_helper'
require_relative '../lib/bank'
require 'vcr'

RSpec.describe Bank do
  let(:bank_response) { Bank.new }
  describe '.all' do
    context 'when is success' do
      it 'does return all banks' do
        VCR.use_cassette('bank/all') do
          expect(bank_response.all).to be_kind_of(Array)
          expect(bank_response.all.first).to have_key('ispb')
          expect(bank_response.all.first).to have_key('name')
          expect(bank_response.all.first).to have_key('code')
          expect(bank_response.all.first).to have_key('fullName')
        end
      end
    end
  end
  describe '.find_by_id', vcr: { cassette_name: 'bank/find_by_id' } do
    context 'when success' do
      it 'does return all banks' do
        VCR.use_cassette('bank/find_by_id') do
          code = 1
          expect(bank_response.find_by_id(code)).to be_kind_of(Hash)
          expect(bank_response.find_by_id(code)).to have_key('ispb')
          expect(bank_response.find_by_id(code)).to have_key('name')
          expect(bank_response.find_by_id(code)).to have_key('code')
          expect(bank_response.find_by_id(code)).to have_key('fullName')
        end
      end
    end
    context 'when error' do
      it 'does return BANK_CODE_NOT_FOUND for non-existing bank code' do
        VCR.use_cassette('bank/find_by_id_404') do
          bank_id = 'non_existing_code'
          response = bank_response.find_by_id(bank_id)
  
          expect(response['message']).to eq('Código bancário não encontrado')
          expect(response['type']).to eq('BANK_CODE_NOT_FOUND')
        end
      end
    end
  end
end

executamos o código

rspec spec

Pronto, chegamos ao fim, tudo funcionando e você evitando a quantidade de requisições desnecessárias.

Read more