From dd0c71eeeaf3340f9c6e77cf0217b89561138364 Mon Sep 17 00:00:00 2001 From: Tiago Guedes Date: Mon, 23 May 2016 19:56:52 -0300 Subject: [PATCH 1/3] Setup and check request before action executes --- lib/jsonapi/utils.rb | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/jsonapi/utils.rb b/lib/jsonapi/utils.rb index 33d0ef9..3a31699 100644 --- a/lib/jsonapi/utils.rb +++ b/lib/jsonapi/utils.rb @@ -6,19 +6,14 @@ module JSONAPI module Utils def self.included(base) if base.respond_to?(:helper_method) - base.helper_method :jsonapi_serialize + base.before_action :setup_request, :check_request + base.helper_method :jsonapi_render, :jsonapi_serialize end end def jsonapi_render(json:, status: nil, options: {}) - setup_request - - if @request.errors.present? - render_errors(@request.errors) - else - body = jsonapi_serialize(json, options) - render json: body, status: status || @_response_document.status - end + body = jsonapi_serialize(json, options) + render json: body, status: status || @_response_document.status rescue => e handle_exceptions(e) ensure @@ -54,8 +49,6 @@ def jsonapi_render_not_found_with_null end def jsonapi_serialize(records, options = {}) - setup_request - if records.is_a?(Hash) hash = records.with_indifferent_access records = hash_to_active_record(hash[:data], options[:model]) @@ -258,5 +251,9 @@ def setup_request server_error_callbacks: (self.class.server_error_callbacks || []) ) end + + def check_request + @request.errors.blank? || render_errors(@request.errors) + end end end From fb99280482bbb6158ae2f32cadc799c042e49570 Mon Sep 17 00:00:00 2001 From: Tiago Guedes Date: Tue, 24 May 2016 01:46:52 -0300 Subject: [PATCH 2/3] Write examples for create action --- jsonapi-utils.gemspec | 7 ++-- lib/jsonapi/utils.rb | 5 ++- spec/controllers/posts_controller_spec.rb | 2 + spec/controllers/users_controller_spec.rb | 45 +++++++++++++++++++++++ spec/spec_helper.rb | 1 - spec/support/controllers.rb | 28 +++++++++++--- spec/support/exceptions.rb | 17 +++++++++ spec/support/models.rb | 1 + spec/support/shared/jsonapi_request.rb | 8 ++++ 9 files changed, 102 insertions(+), 12 deletions(-) create mode 100644 spec/support/exceptions.rb create mode 100644 spec/support/shared/jsonapi_request.rb diff --git a/jsonapi-utils.gemspec b/jsonapi-utils.gemspec index 504a8a8..15e2a39 100644 --- a/jsonapi-utils.gemspec +++ b/jsonapi-utils.gemspec @@ -23,10 +23,9 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'bundler', '~> 1.10' spec.add_development_dependency 'rake', '~> 10.0' - spec.add_development_dependency 'rspec-rails', '~> 3.1' - spec.add_development_dependency 'smart_rspec', '~> 0.1.4' spec.add_development_dependency 'sqlite3' - spec.add_development_dependency 'factory_girl', '~> 4.5' - spec.add_development_dependency 'jsonapi-resources', '~> 0.7.0' spec.add_development_dependency 'rails', '~> 4.2' + spec.add_development_dependency 'rspec-rails', '~> 3.1' + spec.add_development_dependency 'factory_girl', '~> 4.5' + spec.add_development_dependency 'smart_rspec', '~> 0.1.4' end diff --git a/lib/jsonapi/utils.rb b/lib/jsonapi/utils.rb index 3a31699..6cb6a28 100644 --- a/lib/jsonapi/utils.rb +++ b/lib/jsonapi/utils.rb @@ -23,8 +23,9 @@ def jsonapi_render(json:, status: nil, options: {}) end def jsonapi_render_errors(exception) - error = jsonapi_format_errors(exception) - render json: { errors: error.errors }, status: error.code + result = jsonapi_format_errors(exception) + errors = result.errors + render json: { errors: errors }, status: errors.first.status end def jsonapi_format_errors(exception) diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb index b4cc0ce..16b69f6 100644 --- a/spec/controllers/posts_controller_spec.rb +++ b/spec/controllers/posts_controller_spec.rb @@ -2,6 +2,8 @@ require 'rspec/expectations' describe PostsController, type: :controller do + include_context 'JSON API headers' + before(:all) { FactoryGirl.create_list(:post, 3) } let(:fields) { (PostResource.fetchable_fields - %i(id author)).map(&:to_s) } diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 2474df7..8d27869 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -1,10 +1,17 @@ require 'spec_helper' describe UsersController, type: :controller do + include_context 'JSON API headers' + before(:all) { FactoryGirl.create_list(:user, 3, :with_posts) } let(:fields) { (UserResource.fetchable_fields - %i(id posts)).map(&:to_s) } let(:relationships) { %w(posts) } + let(:attributes) { { first_name: 'Yehuda', last_name: 'Katz' } } + + let(:user_params) do + { data: { type: 'users', attributes: attributes } } + end include_examples 'JSON API invalid request', resource: :users @@ -225,4 +232,42 @@ end end end + + describe '#create' do + it 'creates a new user' do + expect { post :create, user_params }.to change(User, :count).by(1) + expect(response).to have_http_status :created + expect(response).to have_primary_data('users') + expect(response).to have_data_attributes(fields) + expect(data['attributes']['first_name']).to eq(user_params[:data][:attributes][:first_name]) + end + + shared_examples_for '400 response' do |hash| + it 'renders a 400 response' do + user_params[:data][:attributes].merge!(hash) + expect { post :create, user_params }.to change(User, :count).by(0) + expect(response).to have_http_status :bad_request + expect(error['title']).to eq('Param not allowed') + expect(error['code']).to eq(105) + end + end + + context 'with a not permitted param' do + it_behaves_like '400 response', foo: 'bar' + end + + context 'with a param not present in resource\'s attribute list' do + it_behaves_like '400 response', admin: true + end + + context 'when validation fails' do + it 'render a 422 response' do + user_params[:data][:attributes].merge!(first_name: '') + expect { post :create, user_params }.to change(User, :count).by(0) + expect(response).to have_http_status :unprocessable_entity + expect(error['title']).to eq('Impossible to change this User') + expect(error['code']).to eq(125) + end + end + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e5ce909..6d34af0 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -97,4 +97,3 @@ class TestApp < Rails::Application get :index_with_hash, to: 'posts#index_with_hash' get :show_with_hash, to: 'posts#show_with_hash' end - diff --git a/spec/support/controllers.rb b/spec/support/controllers.rb index ba59dd6..25bbd5d 100644 --- a/spec/support/controllers.rb +++ b/spec/support/controllers.rb @@ -1,3 +1,5 @@ +require 'support/exceptions' + class BaseController < JSONAPI::ResourceController include JSONAPI::Utils protect_from_forgery with: :null_session @@ -38,7 +40,7 @@ def create jsonapi_render json: new_post, status: :created end - private + protected def load_user @user = User.find(params[:user_id]) @@ -48,13 +50,29 @@ def load_user class UsersController < BaseController # GET /users def index - @users = User.all - jsonapi_render json: @users + users = User.all + jsonapi_render json: users end # GET /users/:id def show - @user = User.find(params[:id]) - jsonapi_render json: @user + user = User.find(params[:id]) + jsonapi_render json: user + end + + # POST /users + def create + user = User.new(user_params) + if user.save + jsonapi_render json: user, status: :created + else + jsonapi_render_errors ::Exceptions::ActiveRecordError.new(user) + end + end + + protected + + def user_params + params.require(:data).require(:attributes).permit(:first_name, :last_name, :admin) end end diff --git a/spec/support/exceptions.rb b/spec/support/exceptions.rb new file mode 100644 index 0000000..4e47dda --- /dev/null +++ b/spec/support/exceptions.rb @@ -0,0 +1,17 @@ +module Exceptions + class ActiveRecordError < ::JSONAPI::Exceptions::Error + attr_accessor :object + + def initialize(object) + @object = object + end + + def errors + [JSONAPI::Error.new( + code: 125, + status: :unprocessable_entity, + title: "Impossible to change this #{@object.class.name}", + detail: @object.errors)] + end + end +end diff --git a/spec/support/models.rb b/spec/support/models.rb index 0dba62e..f8a6aad 100644 --- a/spec/support/models.rb +++ b/spec/support/models.rb @@ -14,6 +14,7 @@ create_table :users, force: true do |t| t.string :first_name t.string :last_name + t.boolean :admin t.timestamps null: false end end diff --git a/spec/support/shared/jsonapi_request.rb b/spec/support/shared/jsonapi_request.rb new file mode 100644 index 0000000..052ff89 --- /dev/null +++ b/spec/support/shared/jsonapi_request.rb @@ -0,0 +1,8 @@ +shared_context 'JSON API headers' do + let(:headers) do + { 'Accept' => 'application/vnd.api+json', + 'Content-Type' => 'application/vnd.api+json' } + end + + before(:each) { request.headers.merge!(headers) } +end From 2a7fab692cf816fe577700b020a89619ea89636b Mon Sep 17 00:00:00 2001 From: Tiago Guedes Date: Tue, 24 May 2016 12:09:47 -0300 Subject: [PATCH 3/3] Update gem version --- lib/jsonapi/utils/version.rb | 2 +- spec/controllers/users_controller_spec.rb | 4 ++-- spec/support/exceptions.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/jsonapi/utils/version.rb b/lib/jsonapi/utils/version.rb index 465434d..a439975 100644 --- a/lib/jsonapi/utils/version.rb +++ b/lib/jsonapi/utils/version.rb @@ -1,5 +1,5 @@ module JSONAPI module Utils - VERSION = '0.4.3' + VERSION = '0.4.4' end end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 8d27869..4e20397 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -262,10 +262,10 @@ context 'when validation fails' do it 'render a 422 response' do - user_params[:data][:attributes].merge!(first_name: '') + user_params[:data][:attributes].merge!(first_name: nil) expect { post :create, user_params }.to change(User, :count).by(0) expect(response).to have_http_status :unprocessable_entity - expect(error['title']).to eq('Impossible to change this User') + expect(error['title']).to eq('Can\'t change this User') expect(error['code']).to eq(125) end end diff --git a/spec/support/exceptions.rb b/spec/support/exceptions.rb index 4e47dda..1a64de7 100644 --- a/spec/support/exceptions.rb +++ b/spec/support/exceptions.rb @@ -10,7 +10,7 @@ def errors [JSONAPI::Error.new( code: 125, status: :unprocessable_entity, - title: "Impossible to change this #{@object.class.name}", + title: "Can't change this #{@object.class.name}", detail: @object.errors)] end end