Skip to content

2 Tutorial API Discogs

Pedro Germano Cervi edited this page Jun 22, 2019 · 3 revisions

Setup

Implemente o Tutorial CRUD ou comece esse tutorial a partir dessa branch. Para esse tutorial vamos precisar executar os testes da pasta tutorials/discogs_api_tutorial somente com: rspec spec/tutorials/discogs_api_tutorial/*.

Instalar gem discogs-wrapper

abrir arquivo Gemfile e adicionar abaixo da linha gem 'faker' a seguinte linha:

gem 'discogs-wrapper'

executar bundle install

Criar módulo de requisições à API Discogs

na pasta app do projeto criar uma pasta chamada services e dentro dela um arquivo com o nome discogs_requests_service.rb e nesse arquivo adicionar as seguintes linhas:

module DiscogsRequestsService
  DISCOGS_TOKEN = 'cvxyzWiAjGkjVBIgWBwsnsoWVqckMFwyATbuZRGU'.freeze
  APP_NAME = 'Getonrails'.freeze
end

verifique que as constantes estão acessíveis do console com por exemplo: DiscogsRequestsService::DISCOGS_TOKEN

modifique a mesma classe para que ela crie uma instância autenticada do wrapper Discogs:

module DiscogsRequestsService
  DISCOGS_TOKEN = 'cvxyzWiAjGkjVBIgWBwsnsoWVqckMFwyATbuZRGU'.freeze
  APP_NAME = 'Getonrails'.freeze

  class Client
    private

    def client
      @client ||= Discogs::Wrapper.new(APP_NAME, user_token: DISCOGS_TOKEN)
    end
  end
end

modifique novamente a mesma classe para criar uma função que busca artists no Discogs a partir de seu nome:

module DiscogsRequestsService
  ...

  class Client
    def search_artist_by_name(name, results_per_page: 10)
      client.search(name, per_page: results_per_page, type: :artist)
    end

    private

    def client
      ...
    end
  end
end

através do console teste algumas buscas com:

  DiscogsRequestsService::Client.new.search_artist_by_name('Zé Ramalho')

modifique novamente a classe para criar uma função que resolve a id de um artist a partir de seu nome:

module DiscogsRequestsService
  ...

  class Client
    def search_artist_by_name(name, results_per_page: 10)
      ...
    end

    def resolve_artist_id(name)
      search = search_artist_by_name(name)
      search['results'][0]['id']
    end

    private

    def client
      ...
    end
  end
end

agora modifique mais uma vez para que o client retorne um artist procurando por seu nome, inferindo sua id e retornando seu objeto.

module DiscogsRequestsService
  ...

  class Client
    def get_artist(name)
      artist_id = resolve_artist_id(name)
      client.get_artist(artist_id)
    end

    private

    def search_artist_by_name(name, results_per_page: 10)
      ...
    end

    def resolve_artist_id(name)
      ...
    end

    def client
      ...
    end
  end
end

nesse ponto já é possível acessar as infromações do artist vindas da API mais facilmente:

artist = DiscogsRequestsService::Client.new.get_artist('Led Zeppelin')
artist.name # => "Led Zeppelin"

modifique mais uma vez a classe para que seja possível buscar as releases do artist através de seu nome:

module DiscogsRequestsService
  ...

  class Client
    def get_artist(name)
      ...
    end

    def search_artist_releases(name, max_results: 10)
      artist_id = resolve_artist_id(name)
      client.get_artist_releases(artist_id.to_s, { per_page: max_results})['releases']
    end

    private

    def search_artist_by_name(name, results_per_page: 10)
      ...
    end

    def resolve_artist_id(name)
      ...
    end

    def client
      ...
    end
  end
end

com o resultado dessa requisição é possível iterar nos dados e usá-los como quisermos:

releases = DiscogsRequestsService::Client.new.search_artist_releases('Led Zeppelin')

releases[1]['title'] # => "Free Me / Whole Lotta Love"
releases[1]['year'] # => 1969
releases[1]['id'] # => 2625192

ainda não estamos tratando casos em que o artista não é encontrado. primeiro altere a função resolve_artist_id dessa forma:

def resolve_artist_id(name)
  search = search_artist_by_name(name)
  search['results'].blank? ? nil : search['results'][0]['id']
end

e agora adicione um retorno condicional nas funções públicas de busca:

def get_artist(name)
  artist_id = resolve_artist_id(name)

  return {} unless artist_id

  client.get_artist(artist_id)
end

def search_artist_releases(name, max_results: 10)
  artist_id = resolve_artist_id(name)

  return {} unless artist_id

  client.get_artist_releases(artist_id.to_s, { per_page: max_results})['releases']
end

no fim o módulo deve ficar dessa forma:

module DiscogsRequestsService
  class Client
    def get_artist(name)
      artist_id = resolve_artist_id(name)

      return {} unless artist_id

      client.get_artist(artist_id)
    end

    def search_artist_releases(name, max_results: 10)
      artist_id = resolve_artist_id(name)

      return {} unless artist_id

      client.get_artist_releases(artist_id.to_s, { per_page: max_results})['releases']
    end

    private

    def search_artist_by_name(name, results_per_page: 10)
      client.search(name, per_page: results_per_page, type: :artist)
    end

    def resolve_artist_id(name)
      search = search_artist_by_name(name)
      search['results'].blank? ? nil : search['results'][0]['id']
    end

    def client
      @client ||= Discogs::Wrapper.new(ENV['APP_NAME'], user_token: ENV['DISCOGS_TOKEN'])
    end
  end
end

Extrair token do serviço para um arquivo .env

no ínicio do arquivo Gemfile após a versão do ruby adicione a seguinte linha:

gem 'dotenv-rails', groups: [:development, :test]

agora execute sua instalação no console com bundle install

em seguida crie um arquivo na pasta root do projeto com o nome .env

Nesse arquivo adicione as sequintes linhas:

DISCOGS_TOKEN=cvxyzWiAjGkjVBIgWBwsnsoWVqckMFwyATbuZRGU
APP_NAME=Getonrails

agora remova as constantes definidas no módulo DiscogsRequestsService

e modifique nessa mesma classe a função que instancia o client dessa forma:

def client
  @client ||= Discogs::Wrapper.new(ENV['APP_NAME'], user_token: ENV['DISCOGS_TOKEN'])
end

buscar releases de um artist qualquer deve continuar funcionando normalmente.

por fim ignore o arquivo .env ao submeter commits ao git adicionando no arquivo .gitignore as seguintes linhas:

# Ignore local config envs
.env

Criar endpoint de importações de artists

crie uma rota para importações no arquivo routes.rb:

post 'artists/import', to: 'imports#artist'

agora crie na pasta app/controllers um controller para importações chamado imports_controller.rb

nesse controller inclua o seguinte conteúdo:

class ImportsController < ApplicationController
  def artist; end
end

Criar modal para importações de artists

agora modifique o arquivo app/views/artists/index.html.erb para incluir um novo botão abaixo do botão já existente:

<%= link_to('Import', '#', class: "btn btn-primary btn-lg btn-block login-button", 'data-toggle' => "modal", 'data-target'=>"#importArtistModal" ) %>

no mesmo arquivo adicione um modal para receber os dados de importação:

<div class="modal fade" id="importArtistModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">Import Artist</h5>
        <button class="close" type="button" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">×</span>
        </button>
      </div>
      <%= form_with(url: artists_import_path, local: true) do |form| %>
        <div class="modal-body">
          <div class="card-body">
            <div class="row">
              <div class="col-12">
                <div class="form-group">
                  <label for="name" class="control-label custom-control-label">Name</label>
                  <div class="input-group">
                    <span class="input-group-addon"><i class="fas fa-compact-disc" aria-hidden="true"></i></span>
                    <input type="text" class="form-control" id="import-artist-name" name="import_artist[name]" value="">
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
        <div class="modal-footer">
          <button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
          <button type="submit" class="btn btn-primary">Import</button>
        </div>
      </div>
    <% end %>
  </div>
</div>

com isso já deve ser possível ver um modal abrir na tela quando o botão Import é acionado.

Importar artists usando o cliente de importações do Discogs

modifique o arquivo app/controllers/imports_controller.rb para receber dados do front da seguinte forma:

class ImportsController < ApplicationController
  def artist; end

  private

  def import_artist_params
    params.require(:import_artist).permit(
      :name
    )
  end
end

agora modifique novamente para usar os dados recebidos numa requisição ao cliente Discogs:

class ImportsController < ApplicationController
  def artist
    client = DiscogsRequestsService::Client.new
    artist = client.get_artist(import_artist_params[:name])
  end

  private

  ...

end

salve os dados recebidos no banco e redirecione o usuário dessa forma:

class ImportsController < ApplicationController
  def artist
    client = DiscogsRequestsService::Client.new
    artist = client.get_artist(import_artist_params[:name])

    Artist.new(name: artist.name, profile: artist.profile, user: current_user).save

    redirect_to artists_path
  end

  private

  ...

end

por fim retorne uma mensagem para o usuário quando nenhum dado foi informado:

class ImportsController < ApplicationController
  def artist
    if import_artist_params[:name].blank?
      redirect_to artists_path, notice: 'Please inform an artist'

      return
    end

    ...

  end

  private

  ...

end

com essas modificações já deve ser possível salvar artists a partir de dados do discogs

Desafio

sem a ajuda de um tutorial, crie um endpoint imports#releases que deve ser chamado dentro de um modal na página app/views/releases/index.html.erb e que deve receber o número de releases desejadas. Os dados recebidos devem ser salvos na relação artist.releases