Conteúdos desta aula:
- Criando nosso Projeto – 00:02:27
- Configurando o banco de dados – 00:04:20
- Criando nossos Models – 00:06:54
- Habilitando o Jbuilder e preparando as pastas – 00:15:05
- Mostrando os detalhes de uma Store – 00:16:33
- Listando as Stores mais amadas – 00:45:31
- Criando um novo Rating – 00:54:17
- Criando nossa GOOGLE API KEY – 01:05:51
- Incluindo nossa chave no projeto de forma segura – 01:08:41
- Criando os endpoints de Google Store – 01:10:24
- Habilitando o CORS no nosso projeto – 01:34:37
2 – Vamos criar nosso projeto rails com o seguinte comando:
1 |
rails new find_my_coffee_api --api --database=postgresql |
Configurando o banco de dados
1 – Em config/database.yml coloque as configurações de acesso ao seu banco de dados:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
default: &default adapter: postgis encoding: unicode pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: leonardo password: 12345678 host: localhost development: <<: *default database: api_development test: <<: *default database: api_test production: <<: *default database: api_production username: api password: <%= ENV['API_DATABASE_PASSWORD'] %> |
2 – Adicione o seguinte código em seu gemfile:
1 2 |
# Gem to use PostGis gem 'activerecord-postgis-adapter' |
3 – Rode um bundle install.
Criando nossos Models
1 – Crie o model Store com o seguinte comando:
1 |
rails g model Store name:string address:string google_place_id:string |
2 – Agora crie o model Rating com o seguinte comando:
1 |
rails g model Rating store:references value:integer opinion:string user_name:string |
3 – Dentro da migration de Store gerada, cole o seguinte código para adicionar a latitude e longitude:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class CreateStores < ActiveRecord::Migration[5.2] def change create_table :stores do |t| t.st_point :lonlat, geographic: true t.string :name t.string :address t.string :google_place_id t.timestamps end add_index :stores, :lonlat, using: :gist end end |
4 – Crie o banco de dados e rode as migrations:
1 |
rails db:create db:migrate |
5 – Dentro do model Rating.rb, cole o seguinte código:
1 2 3 4 |
class Rating < ApplicationRecord belongs_to :store validates_presence_of :value, :opinion, :user_name end |
6 – E substitua o código do model Store.rb por:
1 2 3 4 5 |
class Store < ApplicationRecord has_many :ratings validates_presence_of :lonlat, :name, :google_place_id validates :google_place_id, uniqueness: true end |
Habilitando o Jbuilder e preparando as pastas
1 – Descomente no Gemfile:
1 |
gem 'jbuilder', '~> 2.7' |
2 – Instale rodando:
1 |
bundle |
3 – Agora crie a seguinte pasta:
1 |
app/views/api/v1/stores |
Mostrando os detalhes de uma Store
1 – No model store inclua o método de busca de lojas próximas:
1 2 3 4 5 |
scope :within, -> (longitude, latitude, distance_in_km = 5) { where(%{ ST_Distance(lonlat, 'POINT(%f %f)') < %d } % [longitude, latitude, distance_in_km * 1000]) } |
2 – Para testar, rode o console com o rails c e execute os comandos abaixo:
1 2 3 4 5 |
Store.create!(name: "Coffee 1", lonlat:"POINT(#{-49.269027} #{-25.447306})", address: "av paulista", google_place_id: "zyz") Store.create!(name: "Coffee 2 (too far)", lonlat:"POINT(#{-49.451053} #{-25.439841})", address: "av paulista", google_place_id: "zyz 2") Rating.create!(store: Store.first, value: 5, opinion: 'Great Place', user_name: "Josep") Rating.create!(store: Store.first, value: 3, opinion: 'Medium Place', user_name: "Paul") |
3 – Abra o rails console e teste a busca (O console mostrará as Stores cadastradas em um raio de 2000 milhas de distância):
1 |
Store.within(-49.267421, -25.437904, 2000) |
4 – Agora vamos gerar nossos controllers ratings_controller.rb e stores_controller.rb com os comandos:
1 |
rails g controller api/v1/stores |
5 – Agora no nosso config/routes.rb, vamos pré instanciar nossas rotas substituindo pelo seguinte código:
1 2 3 4 5 6 7 8 9 |
Rails.application.routes.draw do namespace :api do namespace :v1 do ... resources :stores, only: [:index, :show], :defaults => { :format => 'json' } end end end |
6 – Agora vamos criar o controller api/v1/stores_controller.rb.
7 – Nele, inclua o método show:
1 2 3 4 5 6 7 8 9 10 11 12 |
class Api::V1::StoresController < ApplicationController before_action :set_store, only: [:show] def show end private def set_store @store = Store.find_by!(google_place_id: params[:id]) end end |
8 – No model Store vamos adicionar um método para devolver a média de estrelas do café:
1 2 3 4 |
def ratings_average return 0 if self.ratings.empty? (self.ratings.sum(:value) / self.ratings.count).to_i end |
9 – Crie um template jbuilder dentro de stores chamador show.json.jbuilder e coloque nele:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
json.id @store.id json.lonlat @store.lonlat json.name @store.name json.address @store.address json.google_place_id @store.google_place_id json.ratings @store.ratings do |rating| json.value rating.value json.opinion rating.opinion json.user_name rating.user_name json.date rating.created_at.strftime("%d/%m/%Y") end json.ratings_count @store.ratings.count json.ratings_average @store.ratings_average |
10 – Teste esse método subindo o projeto com [rails s] e acesse a url
Listando as Stores mais amadas
1 – No controller Stores coloque:
1 2 3 4 5 6 7 8 9 10 11 12 |
class Api::V1::StoresController < ApplicationController before_action :set_store, only: [:show] def index return [] if params[:latitude].nil? || params[:longitude].nil? @stores = Store.within(params[:latitude].to_f, params[:longitude].to_f) .sort_by { |store| store.ratings_average } .reverse end ... end |
2 – Com esses endpoints criados, vamos às views.
3 – Crie o seguinte template jbuilder “api/v1/stores/index.json.jbuilder”
4 – Com os códigos essenciais setados, vamos ao api/v1/stores/index.json.jbuilder. Cole o código abaixo no mesmo:
1 2 3 4 5 6 7 8 9 10 |
json.array! @stores do |store| json.id store.id json.lonlat store.lonlat json.name store.name json.address store.address json.google_place_id store.google_place_id json.ratings_count store.ratings.count json.ratings_average store.ratings_average end |
5 – Teste esse método subindo o projeto com [rails s] e acesse a url
Criando um novo Rating
1 – Para iniciarmos, vamos gerar nossos controllers ratings_controller.rb e stores_controller.rb com os comandos:
1 |
rails g controller api/v1/ratings |
2 – Agora no nosso config/routes.rb, vamos pré instanciar nossas rotas substituindo pelo seguinte código:
1 2 3 4 5 6 7 8 9 |
Rails.application.routes.draw do namespace :api do namespace :v1 do resources :ratings, only: [:create], :defaults => { :format => 'json' } end end end |
3 – Agora vamos criar o método “create” do nosso controller ratings_controller.rb, coloque nele
1 2 3 4 5 6 7 8 |
def create ActiveRecord::Base.transaction do create_store create_rating render json: @rating end end |
12 – Agora, junto aos métodos privados, cole os seguintes métodos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
... private def create_rating @rating = Rating.new(ratings_params) @rating.store_id = @store.id @rating.save! end def create_store @store = Store.find_or_create_by!( lonlat: "POINT(#{params[:store][:longitude].to_f} #{params[:store][:latitude].to_f})", name: params[:store][:name], address: params[:store][:address], google_place_id: params[:store][:google_place_id] ) end def ratings_params params.require(:rating).permit(:value, :opinion, :user_name) end ... |
13 – Esses métodos servem para, assim que uma avaliação for realizada em uma loja, o endpoint cria a Store no BD ou acha essa Store caso a mesma já tenha sido avaliada, criando também sua respectiva avaliação.
Criando nossa GOOGLE API KEY
Documentação oficial: https://developers.google.com/places/web-service/get-api-key
1 – A chave API é um identificador exclusivo que autentica as solicitações associadas ao seu projeto para fins de uso e faturamento. Você deve ter pelo menos uma chave API associada ao seu projeto.
Para criar uma chave de API:
No Cloud Console, na página do seletor de projeto, selecione ou crie um projeto do Google Cloud para o qual deseja adicionar uma chave de API no link abaixo: https://console.cloud.google.com/projectselector2/home/dashboard
2 – Vá para a página APIs & Services > Credentials no link abaixo: https://console.cloud.google.com/apis/credentials
3 – Na página Credenciais, clique em Create credentials > API key. A caixa de diálogo Chave de API criada exibe sua chave de API recém-criada.
4 – Clique em Fechar.
A nova chave de API está listada na página Credenciais em Chaves de API. (Lembre-se de restringir a chave API antes de usá-la na produção, no link https://developers.google.com/places/web-service/get-api-key#restrict_key.)
Obs: É necessário colocar as informações do seu cartão no Google Console, ele não vai cobrar, é apenas para sinalizar que você vai pagar se passar do limite gratuito.
Incluindo nossa chave no projeto de forma segura
1 – Rode no console:
1 |
EDITOR='code --wait' rails credentials:edit |
Obs: Escolha o editor de sua preferencia
2 – Coloque no arquivo que abrir:
1 |
google_secret_key:<Sua Chave> |
3 – Salve o arquivo
Criando os endpoints de Google Store
1 – Para iniciarmos, vamos gerar nossos controllers ratings_controller.rb e stores_controller.rb com os comandos:
1 |
rails g controller api/v1/google_stores |
2 – Agora no nosso config/routes.rb, vamos pré instanciar nossas rotas substituindo pelo seguinte código:
1 2 3 4 5 6 7 8 9 |
Rails.application.routes.draw do namespace :api do namespace :v1 do ... resources :google_stores, only: [:index, :show], :defaults => { :format => 'json' } end end end |
3 – Crie uma pasta app/services, e nela crie os arquivos get_google_coffee_list_service.rb e o get_google_coffee_details_service.rb.
4 – No gemfile coloque:
1 |
gem 'rest-client' |
5 – Instale rodando:
1 |
bundle |
6 – Inicie o service all_coffees_service.rb com o seguinte código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
require 'rest-client' require 'json' class GetGoogleCoffeeListService def initialize() end def call begin rescue RestClient::ExceptionWithResponse => e e.response end end end |
7 – Agora vamos prepará-lo para receber os parâmetros de latitude e longitude ao ser instanciado. Deixe seu método “initialize” do seguinte modo:
1 2 3 4 |
def initialize(latitude, longitude) @latitude = latitude @longitude = longitude end |
8 – Agora no método “call”, deixe-o com a seguinte aparência:
1 2 3 4 5 6 7 8 9 |
def call begin base_url = "https://maps.googleapis.com/maps/api/place/textsearch/json?query=coffee+shop&location=-#{@latitude},#{@longitude}&radius=5000&key=#{Rails.application.credentials.google_secret_key}" response = RestClient.get base_url JSON.parse(response.body) rescue RestClient::ExceptionWithResponse => e e.response end end |
9 – Em nosso controller, google_stores_controller.rb, anteriormente gerado, vamos colocar a nossa chamada API pelo service. Coloque o seguinte código:
1 2 3 4 5 |
class Api::V1::GoogleStoresController < ApplicationController def index render json: GetGoogleCoffeeListService.new(params[:latitude].to_f, params[:longitude].to_f).call end end |
10 – Teste esse método subindo o projeto com [rails s] e acesse a url
11 – Agora, no service show_coffee_details_service.rb, coloque o seguinte código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
require 'rest-client' require 'json' class GetGoogleCoffeeDetailsService def initialize() end def call begin rescue RestClient::ExceptionWithResponse => e e.response end end end |
12 – Vamos prepará-lo para receber o nosso id do estabelecimento, vindo do google. Deixe o método “initialize” da seguinte forma:
1 2 3 |
def initialize(place_id) @place_id = place_id end |
13 – Agora vamos deixar o método “call” preparado para receber essa api:
1 2 3 4 5 6 7 8 9 10 |
def call begin base_url = "https://maps.googleapis.com/maps/api/place/details/json?place_id=#{@place_id}&key=#{Rails.application.credentials.google_secret_key}" response = RestClient.get base_url value = JSON.parse(response.body) rescue RestClient::ExceptionWithResponse => e e.response end end |
14 – Para finalizar, adicione mais um método no controller google_stores_controller.rb:
1 2 3 |
def show render json: GetGoogleCoffeeDetailsService.new(params[:id]).call end |
15 – Agora teste esse método subindo o projeto com [rails s] e acesse a url localhost:3000/api/v1/google_stores
Habilitando o CORS no nosso projeto
1 – Adicione a seguinte gem em seu Gemfile:
1 |
gem 'rack-cors' |
2 – Rode o blunde em seu terminal.
3 – Crie ou adicione o seguinte código no arquivo /config/initializers/cors.rb:
1 2 3 4 5 6 7 8 9 |
Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins 'http://localhost:3000' resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head] end end |