Você está na página 1de 20

‭Configurando a base da API‬

‭Introdução‬

‭ aplicação funcionará utilizando a arquitetura REST (Representational State Transfer).‬


A
‭Isto quer dizer que:‬
‭1.‬ ‭Não guardará estado num request‬
‭2.‬ ‭Trabalhará com recursos‬
‭3.‬ ‭Os recursos utilizam os verbos HTTP (GET, POST, PUT, DELETE)‬
‭4.‬ ‭Utilizam o padrão URI (Uniform Resource Identifier):‬
‭a.‬ ‭URL: Uniform Resource Locator‬
‭b.‬ ‭URN: Uniform Resource Name‬

‭ xemplo:‬
E
‭GET -> http://www.traducaoendIP:80/task/21‬
‭Verbo -> protocolo://url:porta/path_do_recurso‬

‭Definindo branches no projeto‬

‭ e por analogia imaginarmos um projeto git como uma árvore, um branch é o análogo a um‬
S
‭galho com o objetivo de realizar uma melhor orientação e organização no desenvolvimento da‬
‭aplicação. Numa branch pode ser estabelecida quais regras serão implementada e para quem‬
‭elas serão destinadas, facilitando na rastreabilidade de uma feature ou até mesmo perceber‬
‭métricas no desenvolvimento da mesma.‬

‭ essa forma sempre que se entender necessário será derivada da linha master uma nova‬
D
‭branch para desenvolvimento de uma feature por exemplo e posteriormente se realizar a‬
‭inclusão da mesma na master por meio do comando merge.‬

‭Primeira branch: Configurações básicas da API‬

‭Para se criar uma branch num projeto git inicializado, basta-se utilizar o comando:‬

‭git checkout -b setting-api-base‬

‭E para se saber mais informações de branches no projeto, basta digitar o comando:‬

‭git branch‬
‭ lém do mais será explorado o conceito de namespace no arquivo de rotas e pasta‬‭controllers‬
A
‭para melhor separação/organização da api do restante do projeto, outras configurações default‬
‭como mimetype (json), restrições (constraints) e subdomínios:‬

‭controllers > api‬

c‭ onfig > routes.rb:‬


‭. . .‬
‭namespace :api, defaults: { format: :json }, constraints: { subdomain: 'api' }, path: '/'‬
‭. . .‬

‭ configuração de subdomínios favorece na escalabilidade do projeto, uma vez que flexibiliza‬


A
‭na configuração dos DNS.‬

‭ or hora, as definições aplicadas resultaria numa url como por exemplo:‬


P
‭https://api.‬‭ip_ou_tradução_do_mesmo‬‭/path_relativo‬

‭ ma configuração adicional que deve ser realizada é a tradução do IP 127.0.0.1 de localhost‬


U
‭para outro (p.e.: api.task-manager.test)‬

‭Isto pode ser realizado ajustando o arquivo de hosts e depende do sistema operacional, p.e.:‬
‭ :\Windows\System32\drivers\etc\hosts‬
C
‭127.0.0.1 api.task-manager.test‬

‭Além do mais, é necessário incluí-lo na whitelist na pasta de configurações da aplicação:‬

‭> config/environments/development.rb.‬

‭Rails.application.configure do‬
‭# Whitelist one hostname‬
‭config.hosts << "api.task-manager.test" ou‬
‭config.hosts.clear‬
‭end‬

‭ para que a aplicação funcione é necessário criar o banco de dados através do‬
E
‭comando no terminal:‬

‭rails db:create‬

‭E agora ela funciona:‬


‭rails s -b 0.0.0.0‬
‭Para registrar as modificações, adiciona-se ao repositório nesta branch as configurações:‬

‭git add . ; git commit -m “Setting API route file, database.yml and whitelists”‬

‭ ensando ainda na escalabilidade da aplicação, realiza-se a criação de um versionamento da‬


P
‭API de forma que se futuramente vier a ocorrer questões relacionadas a adequações‬
‭substanciais a aplicação já estará preparada para isso. Desta forma se basta determinar um‬
‭namespace, assim como foi realizado para os subdomínios,‬

‭controllers > api > v1‬

c‭ onfig > routes.rb:‬


‭. . .‬
‭namespace :api, defaults: { format: :json }, constraints: { subdomain: 'api' }, path: '/' do‬
‭namespace :v1, path:'/', constraints: ApiVersionConstraint.new(version: 1, default: true) do‬
‭. . .‬

‭ classe ApiVersionConstraint será responsável em determinar qual a versão se deseja como‬


A
‭default ou qual se deseja utilizar, a responsabilidade de se entrar na versão está relacionada ao‬
‭cabeçalho que a classe está implementando‬

‭ implementação da classe foi realizada na pasta lib, convencionado que nesta pasta, estão‬
A
‭incluídas classes que não estejam diretamente relacionadas a regra de negócio da aplicação.‬

‭class ApiVersionConstraint‬
‭def initialize(options)‬
‭@version = options[:version]‬
‭@default = options[:default]‬
‭end‬

‭def matches?(req)‬
‭@default ||‬
‭req.headers['Accept'].include?("application/vnd.task-manager.v#{@version}")‬
‭end‬
‭end‬

‭ opção de se incluir uma hash como parâmetro de inicialização deve-se ao fato da inclusão de‬
A
‭um parâmetro default como entrada na mesma.‬
‭Com as definições de rotas realizadas, cabe a realização do versionamento e merge:‬

‭ it add .‬
g
‭git commit -m “Defining elements to API version”‬
‭git checkout master‬
‭git merge setting-api-base‬

‭Com isso podemos finalizar a primeira branch.‬

‭Segunda branch: Adicionando usuários‬

‭Neste momento como de costume define-se uma nova branch:‬

‭git checkout -b adding-users‬

‭ esta forma já podemos trabalhar com a gem principal para auxílio na adição de usuários‬
D
‭adicionando-a no gemfile:‬

‭gem ‘devise’‬

‭Executar o comando:‬‭bundle‬

‭Executar o comando:‬‭rails generate devise:install‬

‭Executar o comando:‬‭rails g devise user‬

‭ sses comandos foram suficientes para gerar uma migração para o usuário, criar um model‬
E
‭User, inserir configurações no arquivo de rotas e arquivos para testes.‬

‭ ara continuar no desenvolvimento é necessário definir o caminho do arquivo responsável por‬


P
‭criar usuários para testes:‬

s‭ pec/factories/users.rb‬
‭FactoryBot.define do‬
‭factory :user do‬
‭email { Faker::Internet.email }‬
‭password {"123456"}‬
‭password_confirmation {"123456"}‬
‭end‬
‭end‬
‭ sta fábrica quando instanciada com o devido método irá possibilitar criar o objeto para uma‬
E
‭classe pelo qual queira-se testar.‬

‭E executar o comando de migração no banco de dados:‬‭rails db:migrate‬‭seguido do‬


‭comando para adição do repositório à branch:‬

‭ it add .‬
g
‭git commit -m "Adding devise user model"‬

‭Neste momento já estamos apto a realizar nosso primeiro teste para verificação do user:‬

‭require 'rails_helper'‬

‭RSpec.describe User, type: :model do‬

‭before { @user = FactoryBot.build(:user) }‬

i‭t { expect(@user).to respond_to(:email)}‬


‭it { expect(@user).to respond_to(:password)}‬
‭it { expect(@user).to respond_to(:password_confirmation)}‬
‭it { expect(@user).to be_valid}‬
‭end‬

‭Para verificação do mesmo, basta executar no terminal o comando:‬‭rspec‬


‭E para finalizar realiza-se um commit:‬

‭ it add .‬
g
‭git commit -m "Adding first tests to user model"‬

‭P‭a
‬ ra melhorar a velocidade dos testes utiliza-se um‬‭preloader no grupo de desenvolvimento:‬

‭gem 'spring-commands-rspec'‬

‭Para randomizar basta descomentar a configuração do arquivo‬‭rspec_helper.rb:‬

‭config.order = :random‬

‭No arquivo‬‭rails_helper.rb‬‭deverá inserido o código:‬‭config.include‬


‭FactoryBot::Syntax::Methods,‬‭desta forma é possível‬‭suprimir o trecho‬‭Factory Bot‬‭e realizar‬
‭simplesmente a chamada dos métodos.‬
‭ s teste serão realizados utilizando o shoulda-matcher, entretanto para entender um pouco‬
O
‭mais sobre contexto, para que se possa agrupá-los de acordo com a característica do teste:‬

‭RSpec.describe User, type: :model do‬


‭let(:user1) { build(:user) }‬
‭context 'when name is blank' do‬
‭before() { user1.name = " "}‬
‭it { expect(user1).not_to be_valid }‬
‭end‬
‭end‬

‭ este contexto serão validado para um usuário User para que, quando o nome estiver em‬
N
‭branco, que ele não seja válido.‬

‭ ara se complementar o teste entretanto, deve-se realizar uma validação no model user (com‬
P
‭um atributo virtual name):‬

‭ ttr_accessor :name‬
a
‭validates_presence_of :name‬

‭E para que a elaboração do teste seja mais enxuto, elabora-se através do shoulda-matcher:‬

‭RSpec.describe User, type: :model do‬


‭let(:user1) { build(:user) }‬
‭it { is_expected.to validate_presence_of(:email) }‬
‭it { is_expected.to validate_uniqueness_of(:email).case_insensitive }‬
‭it { is_expected.to validate_confirmation_of(:password) }‬
‭it { is_expected.to allow_value("mario@teste.com").for(:email) }‬
‭end‬

‭ este caso, shouda-matcher se encarrega de instanciar a classe‬‭user‬‭antes (‬‭before() {‬


N
‭user1.name = " "}‬‭) do início do teste e após isso‬‭verificar a presença do nome ou se é nulo por‬
‭exemplo.‬

‭Neste momento pode-se adicionar e comitar no repositório as modificações realizadas:‬

‭ it add .‬
g
‭git commit -m “Adding shoulda-matcher at spec users ”‬
‭ este momento o projeto já possui mais lastro para se iniciar com a criação do primeiro‬
N
‭endpoint. O primeiro endpoint estará relacionado a criação de um controller para o user, não se‬
‭esquecendo da nomenclatura utilizada para definição do versionamento da aplicação:‬

‭rails g controller api/v1/users‬

‭ elativamente aos testes, os mesmo serão realizados utilizando o request test pois são mais‬
R
‭completos abrangendo testes de requisição e rotas.‬

‭ lém de estar seguindo as boas práticas de que um teste de controller ser um teste de caráter‬
A
‭muito granular sendo considerado muitas vezes como antiprodutivo.‬

‭ essa forma na pasta spec, definimos primeiramente a estrutura de onde os testes irão serão‬
D
‭organizados:‬

‭rspec > requests > api > v1 > users_spec.rb‬

‭ no arquivo de teste, uma diferença em relação ao teste do model user é que haverá a‬
E
‭persistência no banco para isso devemos estabelecer a sintaxe a seguir:‬

‭let!(:user){ create(:user) }‬

‭ om os dados persistidos deve-se testar os elementos da API, no entanto é necessário montar‬


C
‭antes de mais nada os critérios mínimos para atender o contrato da mesma: método, header e‬
‭body.‬

‭Assim o teste para um HTTP GET assim seria estabelecido:‬

‭ efore { host! "api.taskmanager.test:3000"}‬


b
‭describe 'GET users/:id' do‬
‭before do‬
‭headers = {"Accept" => "application/vnd.task-manager.v1"}‬
‭get "/users/#{user_id}", :params => {}, :headers=> headers‬
‭# ou get "/users/#{user_id}", params: {}, headers: headers‬
‭end‬
‭context 'when the user exists' do‬
‭it 'returns the user' do‬
‭user_response = JSON.parse(response.body)‬
‭expect(user['id']).to eq(user_id)‬
‭end‬
‭context "when the user doesn't exists" do‬
‭let(:user_id) { 1000 }‬

‭it "returns status code 404" do‬


‭expect(response).to have_http_status(404)‬
‭end‬
‭end‬
‭ nd‬
e
‭end‬
‭ alienta-se que a metodologia TDD, utilizada neste projeto, na grande maioria das vezes‬
S
‭implicará numa falha inicial, para que seja implementado um trecho do código e posterior‬
‭refatoração. Desta forma, cabe-nos reescrever o código com a inclusão das devidas rotas e‬
‭elaboração das actions no controller.‬
‭routes:‬
‭namespace :api, defaults: { format: :json }, constraints: { subdomain: 'api' }, path: '/' do‬
‭. . .‬
‭resources :users, only: [:show]‬
‭. . .‬
‭end‬

‭controllers/api/v1/users_controller.rb:‬
‭. . .‬
‭class Api::V1::UsersController < ApplicationController‬

‭def show‬
‭begin‬
‭@user = User.find(params[:id])‬
‭respond_with @user‬
‭rescue‬
‭head 404‬
‭end‬
‭end‬

‭ nd‬
e
‭. . .‬

‭Nota: Lembrar que para que as urls devem estar nas whitelists da aplicação‬
‭config/environments/development.rb e development.rb‬‭com‬‭config.hosts.clear‬

‭Versionando:‬
‭ it add .‬
g
‭git commit -m “git commit -m "Adding action:show and tests:happy and sad path"‬
‭Podemos realizar um teste de conformidade no curl:‬

‭curl -H “Accept: application/vnd.taskmanager.v1” http://api.taskmanager.test:3000/users/1 -v‬

‭E como resposta obtem-se o esperado poris não há nenhum usuário cadastrado‬

*‭ Trying 127.0.0.1:3000...‬
‭* TCP_NODELAY set‬
‭* Connected to api.taskmanager.test (127.0.0.1) port 3000 (#0)‬
‭> GET /users/1 HTTP/1.1‬
‭> Host: api.taskmanager.test:3000‬
‭> User-Agent: curl/7.68.0‬
‭> Accept: application/vnd.taskmanager.v1‬
‭>‬
‭* Mark bundle as not supporting multiuse‬
‭< HTTP/1.1 404 Not Found‬
‭< X-Frame-Options: SAMEORIGIN‬
‭< X-XSS-Protection: 1; mode=block‬
‭< X-Content-Type-Options: nosniff‬
‭< X-Download-Options: noopen‬
‭< X-Permitted-Cross-Domain-Policies: none‬
‭< Referrer-Policy: strict-origin-when-cross-origin‬
‭< Content-Type: application/json‬
‭< Cache-Control: no-cache‬
‭< X-Request-Id: 3f8e4ba1-688f-4b7a-959b-2413f4155d27‬
‭< X-Runtime: 0.025294‬
‭< Transfer-Encoding: chunked‬
‭<‬
‭* Connection #0 to host api.taskmanager.test left intact‬

‭Mas podemos usar o rails console para realizar um cadastro para testes‬

r‭ ails c‬
‭user = User.create(email: “teste@teste.com”, password = “123456”)‬
‭E a resposta no curl já será:‬

*‭ Trying 127.0.0.1:3000...‬
‭* TCP_NODELAY set‬
‭* Connected to api.taskmanager.test (127.0.0.1) port 3000 (#0)‬
‭> GET /users/1 HTTP/1.1‬
‭> Host: api.taskmanager.test:3000‬
‭> User-Agent: curl/7.68.0‬
‭> Accept: application/vnd.taskmanager.v1‬
‭>‬
‭* Mark bundle as not supporting multiuse‬
‭< HTTP/1.1 200 OK‬
‭< X-Frame-Options: SAMEORIGIN‬
‭< X-XSS-Protection: 1; mode=block‬
‭< X-Content-Type-Options: nosniff‬
‭< X-Download-Options: noopen‬
‭< X-Permitted-Cross-Domain-Policies: none‬
‭< Referrer-Policy: strict-origin-when-cross-origin‬
‭< Content-Type: application/json; charset=utf-8‬
‭< ETag: W/"6bc939080a78a3fd3468c6eefac86c6e"‬
‭< Cache-Control: max-age=0, private, must-revalidate‬
‭< X-Request-Id: 428b2ac8-b37c-4f02-9522-8245c5c2d28a‬
‭< X-Runtime: 0.108343‬
‭< Transfer-Encoding: chunked‬
‭<‬
‭* Connection #0 to host api.taskmanager.test left intact‬
{‭ "id":1,"email":"teste@teste.com","created_at":"2020-08-11T17:56:30.562Z","updated_at‬
‭":"2020-08-11T17:56:30.562Z"}‬

‭ esta altura deve-se implementar as ações de criação do usuário, em outras palavras‬


N
‭tratam-se dos métodos HTTP: POST, PUT e DELETE:‬

‭ ssim antes de mais nada implementa-se os testes nos contextos de parâmetros válidos e‬
A
‭inválidos:‬

‭describe 'POST /users' do‬


‭before do‬
‭headers = {'Accept'=> 'application/vnd.taskmanager.v1'}‬
‭post '/users', params:{user: user_params}, headers: headers‬
‭end‬

‭context 'when the request params are valids' do‬


‭let(:user_params){ attributes_for(:user)}‬

‭it 'returns status code 201' do‬


‭expect(response).to have_http_status(201)‬
‭end‬

‭it 'return JSON data for created user' do‬


‭user_response = JSON.parse(response.body, symbolize_names: true)‬
‭expect(user_response[:email]).to eq(user_params[:email])‬
‭end‬
‭end‬

‭context 'when the request params are invalids' do‬


‭let(:user_params){ attributes_for(:user, email: 'Invalid_email@')}‬

‭it 'returns status code 422' do‬


‭expect(response).to have_http_status(422)‬
‭end‬

‭it 'return JSON data with errors' do‬


‭user_response = JSON.parse(response.body, symbolize_names: true)‬
‭expect(user_response).to have_key(:errors)‬
‭end‬

‭end‬

‭end‬

‭describe 'PUT /users/:id' do‬


‭before do‬
‭headers = {'Accept' => 'application/vnd.taskmanager.v1'}‬
‭put "/users/#{user_id}", headers: headers, params:{user: user_params}‬
‭end‬

‭context 'when the request params are valid' do‬


‭let(:user_params) { { email: 'new_email@taskmanager.com' } }‬

‭it 'returns status code 200' do‬


‭expect(response).to have_http_status(200)‬
‭end‬

‭it 'return JSON data with update information' do‬


‭user_response = JSON.parse(response.body, symbolize_names: true)‬
‭expect(user_response[:email]).to eq(user_params[:email])‬
‭end‬

‭end‬

‭context 'when the request params are invalid' do‬


‭let(:user_params) { { email: 'New_email@' } }‬

‭it 'returns status code 422' do‬


‭expect(response).to have_http_status(422)‬
‭end‬

‭it 'return JSON data with errors' do‬


‭user_response = JSON.parse(response.body, symbolize_names: true)‬
‭expect(user_response).to have_key(:errors)‬
‭end‬
‭end‬
‭end‬

‭describe 'DELETE /users/:id' do‬


‭before do‬
‭headers = {'Accept'=> 'application/vnd.taskmanager.v1'}‬
‭delete "/users/#{user_id}", params:{}, headers: headers‬
‭end‬

‭it 'returns status code 204' do‬


‭expect(response).to have_http_status(204)‬
‭end‬

‭it 'removes the user from database' do‬


‭expect(User.find_by(id: user.id)).to be_nil‬
‭end‬

‭end‬
I‭nicialmente estes testes irão falhar por não haver a rota, nem as ações implementadas, desta‬
‭forma e atendendo às condições solicitadas pelo teste obteremos:‬

‭routes.rb:‬
‭resources :users, only: [:show, :create, :update, :destroy]‬

‭ ontrollers/api/v1/users_controller.rb:‬
c
‭. . .‬
‭def create‬
‭user = User.new(user_params)‬
‭if user.save‬
‭render json: user, status: 201‬
‭else‬
‭render json: { errors: user.errors}, status: 422‬
‭end‬
‭end‬

‭def update‬
‭user = User.find(params[:id])‬
‭if user.update(user_params)‬
‭render json: user, status: 200‬
‭else‬
‭render json: { errors: user.errors}, status: 422‬
‭end‬
‭end‬

‭def destroy‬
‭user = User.find(params[:id])‬
‭user.destroy‬
‭head 204‬
‭end‬

‭ rivate‬
p
‭def user_params‬
‭params.require(:user).permit(:email, :password, :password_confirmation)‬
‭end‬
‭Refatorações:‬

‭No arquivo request > user_spec.rbOnde está:‬

‭headers = {'Accept' => 'application/vnd.taskmanager.v1'}‬

‭Torna-se a variável:‬

‭let(:headers) do‬
‭{'Accept'=>'application/vnd.taskmanager.v1', 'Content-Type'=> Mime[:json].to_s}‬
‭end‬

‭Alem do mais deve-se converter o params que recebem corpo com o metodo to_json:‬

‭params:{user: user_params}.to_json‬

‭ omo há muita repetição sempre na conversão de uma resposta para json, pode-se realizar a‬
C
‭seguinte alteração:‬

‭●‬ ‭Criação de uma arquivo de suporte a conversão:‬


‭spec > support > request_spec_helper.rb‬

‭●‬ ‭Inclusão da chamada deste módulo no rails helper, descomentando a linha:‬


‭Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }‬

‭●‬ A
‭ dição da instrução para se carregar o método diretamente, exclusivo para os testes do‬
‭tipo request:‬
‭config.include RequestSpecHelper, type: :request‬

‭●‬ ‭Onde houver o objeto‬‭user_response‬‭, que utilizou do‬‭metodo‬‭JSON.parse‬‭trocamos‬


‭por‬‭json_body‬

‭ ode-se agora realizar a atualização do repositório e merge do mesmo, uma vez que essa‬
P
‭etapa foi concluída.‬
‭Terceira branch: Autenticação de usuários‬

‭git checkout -b adding-authentication‬

‭ otivações: elaborar mecanismos de autenticação‬


M
‭Req pro servidor para iniciar uma sessão (username e senha) -> Servidor lhe retorna um token‬
‭e validação:‬
‭Primeiro passo: Adicionar um campo authtoken ao model, adicionando uma migração:‬

‭rails generate migration add_auth_token_to_users auth_token:string:uniq:index‬

‭Essa migração resultará na adição de uma nova coluna à tabela users denominada‬
‭auth_token‬‭com índice único.‬

‭ lem do mais para que não haja nenhuma inconsistência entre o banco de desenvolvimento e‬
A
‭teste executar o comando:‬

‭rails db:test:prepare‬

‭O que já é suficiente para realização do primeiro teste no model:‬

‭it { is_expected.to validate_uniqueness_of(:auth_token)}‬

‭E no model deverá ser inserida a validação deste campo:‬

‭validates :auth_token, uniqueness: true‬

‭ ara testar um metodo convencionalmente utiliza-se o caracter # na frente do mesmo, o‬


P
‭objetivo de testar método é realizar o controle do mesmo e para isso utiliza-se os mocks.‬

‭ ocks são utilizados para retornar algum resultado que não se possua controle, por exemplo o‬
M
‭caso dos tokens “cada hora que se solicita ao método um token, um token diferente é retornado‬
‭e isso implica que um teste sempre irá falhar”, dessa forma utilizamos informações mocks.‬

‭E como fazer: objeto ou método dublê (diz como o método deve se comportar):‬

‭allow(Devise).to receive(:friendly_token).and_return(‘abc123xyzTOKEN’)‬
‭Nota sobre significados da exclamação:‬

‭●‬ u‭ ser.save!‬‭→ tenta salvar o usuário e se não salvar‬‭retorna um resultado booleano false‬
‭e retorne uma exceção‬
‭●‬ ‭describe '#generation_authenticate_token!' →‬‭neste‬‭caso irá alterar o estado do objeto‬

‭ ara o caso de uma implementação utilizando recursos mocks podemos apresentar para o‬
P
‭método de geração de token de autenticação o seguinte trecho de código:‬
‭it 'generate a unique token' do‬
‭allow(Devise).to receive(:friendly_token).and_return('abc123xyzTOKEN')‬
‭user.generate_authentication_token!‬

‭expect(user.auth_token).to eq('abc123xyzTOKEN')‬

‭end‬

‭E no model para que seja atendido o dublê (mock):‬

‭def generate_authentication_token!‬
‭self.auth_token = Devise.friendly_token‬
‭end‬

‭ m outras palavras sempre que for executado o método para geração de um token no model é‬
E
‭solicitado que o dado seja mocado e substituído pelo que está sendo retornado, isso para o‬
‭caso do início de uma sessão.‬

‭ á no caso que existir o token, sempre que solicitar um novo token ao sistema o sistema‬
J
‭deverá informar um segundo token e que para o dublê será um valor retornado como sendo um‬
‭segundo parâmetro.‬

i‭t 'generate another auth_token when the current auth token already has been taken'‬
‭do‬
‭allow(Devise).to‬
‭receive(:friendly_token).and_return('TOKENabc123xyz','TOKENabc123xyz','abcXYZ123‬
‭456789')‬
‭existing_user = create(:user)‬
‭user.generate_authentication_token!‬
‭expect(user.auth_token).not_to eq(existing_user.auth_token)‬
‭end‬
‭ que implica que no model essa verificação deverá ser realizada, inserindo um trecho de‬
O
‭código adicional no método de geração de token para autenticação:‬

‭def generate_authentication_token!‬
‭begin‬
‭self.auth_token = Devise.friendly_token‬
‭end while User.exists?(auth_token: auth_token)‬
‭end‬

‭Além do que este método deverá ser executado sempre que um usuário for criado:‬

‭before_create :generate_authentication_token!‬

‭Após estas modificações já possível adicionar ao repositório as modificações.‬

‭ essions:‬
S
‭Para que um usuário possua uma sessão é necessário criar um controller para a mesma de‬
‭forma que lhe seja permitido realizar ações numa área restrita.‬

‭ esta forma há a necessidade antes de mais nada de elaborar o ciclo de testes para‬
D
‭verificação dos elementos na camada de controle estão implementados.‬

‭ ara isso é necessário indicar o host onde se será realizado o teste, prepara as variaveis user‬
P
‭e header que serão utilizadas no momento do teste:‬

‭ efore { host! 'api.taskmanager.test'}‬


b
‭let(:user) { create(:user) }‬
‭let(:headers) do‬
‭{'Accept'=>'application/vnd.taskmanager.v1', 'Content-Type'=> Mime[:json].to_s}‬
‭end‬

‭Neste momento pode-se aplicar um teste de criação da sessão:‬‭describe 'POST /sessions'‬

‭Define-se antes do teste o tipo de método aplicando o cabeçalho e parâmetros enviados:‬

‭before do‬
‭post "/sessions", headers: headers, params: {session: credentials }.to_json‬
‭end‬

‭E realização do teste sob os contextos abrangidos:‬


‭context 'when the credentials are correct' do‬
‭let(:credentials) { { email: user.email, password: '123456' } }‬
‭it 'returns status code 200' do‬
‭expect(response).to have_http_status(200)‬
‭end‬
‭it 'returns the json data for the users with auth token' do‬
‭expect(json_body[:auth_token]).to eq(user.auth_token)‬
‭end‬
‭end‬
‭end‬

‭ embrando que o usuário deverá ser recarregado pois‬


L
‭Neste momento já se sabe quais serão as actions a serem testadas no controller, não se‬
‭esquecendo entretanto que as rotas devem estar devidamente configuradas:‬

‭ otas (lembrar que a rota é configurada no namespace da API, considerando inclusive o‬


R
‭versionamento da mesma)‬
‭resources :sessions, only: [:create]‬

‭Controller:‬‭Api > V1 > SessionsController‬


‭def create‬
‭user = User.find_by(email:session_params[:email])‬

‭if user && user.valid_password?(session_params[:password])‬


‭sign_in user, store: false‬
‭render json:user, status: 200‬
‭end‬
‭end‬

‭private‬

‭def session_params‬
‭params.require(:session).permit(:email,:password)‬
‭end‬

‭ método sign_in é um helper do devise que tem como função realizar o inicio de uma sessão‬
O
‭de um usuário, mantendo registro no banco de dados que o usuário foi logado, o parametro‬
‭store: false garante que não haverá gravação de estado ipor se tratar de REST.‬
‭ ambém as rotas devem ser ajustadas para definir que será aplicado apenas a controle de‬
T
‭sessões para as sessões definidas nesta api:‬

‭devise_for :users, only: [:sessions], controllers: {sessions: 'api/v1/sessions'}‬

‭ as deverá ser inserido que após a inserção do helper do devise que seja gerado um token e‬
M
‭que o usuário seja persistido no banco:‬

‭ ser.generate_authetication_token!‬
u
‭user.save‬

‭ nalogamente o teste deve ser atualizado porque o usuário deverá ser recarregado para que o‬
A
‭token seja o mesmo, uma vez que foi modificado.‬

‭user.reload‬
‭expect(json_body[:auth_token]).to eq(user.auth_token)‬

‭Considerando que as credenciais estejam incorretas, tem-se o contexto:‬

‭context 'when the credentials are incorrect' do‬


‭let(:credentials) { { email: user.email, password: 'invalid_password' } }‬

‭it 'returns status code 401' do‬


‭expect(response).to have_http_status(401)‬
‭end‬

‭it 'returns the json data for the errors' do‬


‭expect(json_body).to have_key(:errors)‬
‭end‬
‭end‬

‭E o controller session deverá ser ajustado com uma clausula else:‬

‭else‬
‭render json: {errors: 'Invalid password or email'}, status: 401‬

‭ ara realizar um logout basata tratar a aplicação com a inserção de um teste para o metodo‬
P
‭delete do auth_token, seguido da inserçãpo da rota com o metodo destroy para session e‬
‭assim implementar a action no controller.‬

‭Para ideintifação do usuário utilizamos o metodo current_user.‬


‭ oncerns: local específico para se colocar módulos de forma a extrair alguns comportamentos‬
C
‭da classe para reaproveitamento posterior. Isso é um ganho para deixar a classe menos‬
‭inchada. Se encontram no model e controller.‬

‭ ponto de entrada dos controllers é o ApplicationController, uma vez que os demais controllers‬
O
‭herdam do mesmo, porém os métodos estarão na pasta concern para esta camada‬

‭ o teste precisamos de indentificar o current user:‬


N
‭current_user -> procura usuario pelo token por uma requisição de um cabeçalho denominado‬
‭authorization‬

Você também pode gostar