From 668584c68b7aa6e98603964de44d3c6ff6553c58 Mon Sep 17 00:00:00 2001 From: Thor77 Date: Sat, 24 Nov 2018 19:14:31 +0100 Subject: [PATCH 1/7] Add require_owner! filter action --- app/controllers/application_controller.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 450068c..fa8fc24 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -10,4 +10,18 @@ class ApplicationController < ActionController::API def configure_permitted_parameters devise_parameter_sanitizer.permit(:sign_up, keys: [:username]) end + + private + + def require_owner! owner + render_forbidden_error if owner != current_user + end + + def render_forbidden_error + render json: { + errors: [ + 'Only the parent tournament owner can update this resource' + ] + }, status: :forbidden + end end From 8b3b8352e5b3e56348252853f08e0ab488ee4653 Mon Sep 17 00:00:00 2001 From: Thor77 Date: Sat, 24 Nov 2018 19:16:04 +0100 Subject: [PATCH 2/7] Delegate owner attribute to tournament --- app/models/team.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/team.rb b/app/models/team.rb index 023b9db..8738d12 100644 --- a/app/models/team.rb +++ b/app/models/team.rb @@ -6,4 +6,6 @@ class Team < ApplicationRecord has_many :scores, dependent: :destroy validates :name, presence: true + + delegate :owner, to: :tournament end From 3744bf6858e4f2e248751e4ebfaf11027a102cf9 Mon Sep 17 00:00:00 2001 From: Thor77 Date: Sat, 24 Nov 2018 19:16:55 +0100 Subject: [PATCH 3/7] Add authentification helper to RSpec config --- spec/auth_helpers.rb | 8 ++++++++ spec/rails_helper.rb | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 spec/auth_helpers.rb diff --git a/spec/auth_helpers.rb b/spec/auth_helpers.rb new file mode 100644 index 0000000..27869d4 --- /dev/null +++ b/spec/auth_helpers.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module AuthHelpers + def apply_authentication_headers_for(user) + user_headers = user.create_new_auth_token + @request.headers.merge!(user_headers) + end +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index dce28cc..28397cc 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true -# This file is copied to spec/ when you run 'rails generate rspec:install' require 'spec_helper' +require 'auth_helpers' + ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../config/environment', __dir__) # Prevent database truncation if the environment is production @@ -61,6 +62,8 @@ RSpec.configure do |config| # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") config.include FactoryBot::Syntax::Methods + config.include Devise::Test::ControllerHelpers, type: :controller + config.include AuthHelpers end Shoulda::Matchers.configure do |config| From 0308dc121fa0034affc04b98a0a15476d8cef714 Mon Sep 17 00:00:00 2001 From: Thor77 Date: Sat, 24 Nov 2018 19:17:23 +0100 Subject: [PATCH 4/7] Add TeamsController and corresponding specs --- app/controllers/teams_controller.rb | 31 +++++++++++ config/routes.rb | 1 + spec/controllers/teams_controller_spec.rb | 68 +++++++++++++++++++++++ spec/routing/teams_routing_spec.rb | 19 +++++++ 4 files changed, 119 insertions(+) create mode 100644 app/controllers/teams_controller.rb create mode 100644 spec/controllers/teams_controller_spec.rb create mode 100644 spec/routing/teams_routing_spec.rb diff --git a/app/controllers/teams_controller.rb b/app/controllers/teams_controller.rb new file mode 100644 index 0000000..fde8269 --- /dev/null +++ b/app/controllers/teams_controller.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +class TeamsController < ApplicationController + before_action :set_team, only: %i[show update] + before_action :authenticate_user!, only: %i[update] + before_action -> { require_owner! @team.owner }, only: %i[update] + + # GET /teams/1 + def show + render json: @team + end + + # PATCH/PUT /teams/1 + def update + if @team.update(team_params) + render json: @team + else + render json: @team.errors, status: :unprocessable_entity + end + end + + private + + def set_team + @team = Team.find(params[:id]) + end + + def team_params + ActiveModelSerializers::Deserialization.jsonapi_parse(params, only: [:name]) + end +end diff --git a/config/routes.rb b/config/routes.rb index b10df31..b52f9d6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,4 +4,5 @@ Rails.application.routes.draw do mount_devise_token_auth_for 'User', at: 'users' resources :matches, only: %i[show] + resources :teams, only: %i[show update] end diff --git a/spec/controllers/teams_controller_spec.rb b/spec/controllers/teams_controller_spec.rb new file mode 100644 index 0000000..823fa3f --- /dev/null +++ b/spec/controllers/teams_controller_spec.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe TeamsController, type: :controller do + before do + @team = create(:team) + @owner = @team.owner + end + + describe 'GET #show' do + it 'returns a success response' do + get :show, params: { id: @team.to_param } + expect(response).to be_successful + end + + it 'should return the correct team' do + get :show, params: { id: @team.to_param } + body = ActiveModelSerializers::Deserialization.jsonapi_parse(JSON.parse(response.body)) + expect(body[:name]).to eq(@team.name) + end + end + + describe 'PUT #update' do + let(:valid_update) do + { + data: { + id: @team.id, + type: 'teams', + attributes: { + name: Faker::Dog.name + } + } + } + end + + context 'with valid params as owner' do + before(:each) do + apply_authentication_headers_for @owner + end + + it 'updates the requested team' do + put :update, params: { id: @team.to_param }.merge(valid_update) + @team.reload + expect(response).to be_successful + expect(@team.name).to eq(valid_update[:data][:attributes][:name]) + end + + it 'renders a response with the updated team' do + put :update, params: { id: @team.to_param }.merge(valid_update) + expect(response).to be_successful + body = ActiveModelSerializers::Deserialization.jsonapi_parse(JSON.parse(response.body)) + expect(body[:name]).to eq(valid_update[:data][:attributes][:name]) + end + end + + context 'with valid params as another user' do + before(:each) do + apply_authentication_headers_for create(:user) + end + + it 'renders a forbidden error response' do + put :update, params: { id: @team.to_param }.merge(valid_update) + expect(response).to have_http_status(:forbidden) + end + end + end +end diff --git a/spec/routing/teams_routing_spec.rb b/spec/routing/teams_routing_spec.rb new file mode 100644 index 0000000..b5f5a40 --- /dev/null +++ b/spec/routing/teams_routing_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe TeamsController, type: :routing do + describe 'routing' do + it 'routes to #show' do + expect(get: '/teams/1').to route_to('teams#show', id: '1') + end + + it 'routes to #update via PUT' do + expect(put: '/teams/1').to route_to('teams#update', id: '1') + end + + it 'routes to #update via PATCH' do + expect(patch: '/teams/1').to route_to('teams#update', id: '1') + end + end +end From 92de7b8a57a067940551d646012eb951aceb9912 Mon Sep 17 00:00:00 2001 From: Thor77 Date: Sat, 24 Nov 2018 19:20:53 +0100 Subject: [PATCH 5/7] Fix rubocop Style/MethodDefParentheses --- app/controllers/application_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index fa8fc24..803d135 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -13,7 +13,7 @@ class ApplicationController < ActionController::API private - def require_owner! owner + def require_owner!(owner) render_forbidden_error if owner != current_user end From a8e6ac3dfd88490f252fe2ef72bff9ed9a29beab Mon Sep 17 00:00:00 2001 From: Thor77 Date: Sat, 24 Nov 2018 20:33:31 +0100 Subject: [PATCH 6/7] Add and include DeserializeHelpers --- spec/deserialize_helpers.rb | 7 +++++++ spec/rails_helper.rb | 2 ++ 2 files changed, 9 insertions(+) create mode 100644 spec/deserialize_helpers.rb diff --git a/spec/deserialize_helpers.rb b/spec/deserialize_helpers.rb new file mode 100644 index 0000000..2be5bf4 --- /dev/null +++ b/spec/deserialize_helpers.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module DeserializeHelpers + def deserialize_response(response) + ActiveModelSerializers::Deserialization.jsonapi_parse(JSON.parse(response.body)) + end +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 28397cc..3d93ab1 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -2,6 +2,7 @@ require 'spec_helper' require 'auth_helpers' +require 'deserialize_helpers' ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../config/environment', __dir__) @@ -64,6 +65,7 @@ RSpec.configure do |config| config.include FactoryBot::Syntax::Methods config.include Devise::Test::ControllerHelpers, type: :controller config.include AuthHelpers + config.include DeserializeHelpers end Shoulda::Matchers.configure do |config| From 20288ff7e4f78fea2885d4ad35df3471b0d84a91 Mon Sep 17 00:00:00 2001 From: Thor77 Date: Sat, 24 Nov 2018 20:34:13 +0100 Subject: [PATCH 7/7] Use DeserializeHelpers.deserialize_response --- spec/controllers/teams_controller_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/controllers/teams_controller_spec.rb b/spec/controllers/teams_controller_spec.rb index 823fa3f..2537aa7 100644 --- a/spec/controllers/teams_controller_spec.rb +++ b/spec/controllers/teams_controller_spec.rb @@ -16,7 +16,7 @@ RSpec.describe TeamsController, type: :controller do it 'should return the correct team' do get :show, params: { id: @team.to_param } - body = ActiveModelSerializers::Deserialization.jsonapi_parse(JSON.parse(response.body)) + body = deserialize_response response expect(body[:name]).to eq(@team.name) end end @@ -49,7 +49,7 @@ RSpec.describe TeamsController, type: :controller do it 'renders a response with the updated team' do put :update, params: { id: @team.to_param }.merge(valid_update) expect(response).to be_successful - body = ActiveModelSerializers::Deserialization.jsonapi_parse(JSON.parse(response.body)) + body = deserialize_response response expect(body[:name]).to eq(valid_update[:data][:attributes][:name]) end end