From dfaf98a6958e11b3af673a2cc3bae60c21aa9ede Mon Sep 17 00:00:00 2001 From: Joe Woodward Date: Mon, 17 Sep 2018 13:56:45 +0700 Subject: [PATCH] Render error when unable to deserialize resource --- .../initializer/templates/initializer.rb | 14 ++++++ lib/jsonapi/rails/configuration.rb | 13 +++++- .../rails/controller/deserialization.rb | 1 + lib/jsonapi/rails/controller/hooks.rb | 6 +++ spec/deserialization_spec.rb | 43 +++++++++++++++++++ .../initializers/new_framework_defaults.rb | 4 +- 6 files changed, 79 insertions(+), 2 deletions(-) diff --git a/lib/generators/jsonapi/initializer/templates/initializer.rb b/lib/generators/jsonapi/initializer/templates/initializer.rb index 36a9774..d9c335c 100644 --- a/lib/generators/jsonapi/initializer/templates/initializer.rb +++ b/lib/generators/jsonapi/initializer/templates/initializer.rb @@ -69,6 +69,20 @@ # # Set a default pagination scheme. # config.jsonapi_pagination = ->(_) { {} } # + # # Set the default action when the payload cannot be deserialized + # config.jsonapi_payload_malformed = -> { + # render jsonapi_errors: { + # title: 'Non-compliant Request Body', + # detail: 'The request was not formatted in compliance with the application/vnd.api+json spec', + # links: { + # about: 'http://jsonapi.org/format/' + # } + # }, status: :bad_request + # } + # + # # Uncomment to take no action when the payload cannot be deserialized + # config.jsonapi_payload_malformed = nil + # # # Set a logger. # config.logger = Logger.new(STDOUT) # diff --git a/lib/jsonapi/rails/configuration.rb b/lib/jsonapi/rails/configuration.rb index 7a7f76f..57f1332 100644 --- a/lib/jsonapi/rails/configuration.rb +++ b/lib/jsonapi/rails/configuration.rb @@ -39,12 +39,22 @@ module Configurable DEFAULT_JSONAPI_PAGINATION = ->(_) { {} } + DEFAULT_JSONAPI_PAYLOAD_MALFORMED = -> { + render jsonapi_errors: { + title: 'Non-compliant Request Body', + detail: 'The request was not formatted in compliance with the application/vnd.api+json spec', + links: { + about: 'http://jsonapi.org/format/' + } + }, status: :bad_request + } + DEFAULT_LOGGER = Logger.new(STDERR) DEFAULT_CONFIG = { + jsonapi_cache: DEFAULT_JSONAPI_CACHE, jsonapi_class: DEFAULT_JSONAPI_CLASS, jsonapi_errors_class: DEFAULT_JSONAPI_ERRORS_CLASS, - jsonapi_cache: DEFAULT_JSONAPI_CACHE, jsonapi_expose: DEFAULT_JSONAPI_EXPOSE, jsonapi_fields: DEFAULT_JSONAPI_FIELDS, jsonapi_include: DEFAULT_JSONAPI_INCLUDE, @@ -52,6 +62,7 @@ module Configurable jsonapi_meta: DEFAULT_JSONAPI_META, jsonapi_object: DEFAULT_JSONAPI_OBJECT, jsonapi_pagination: DEFAULT_JSONAPI_PAGINATION, + jsonapi_payload_malformed: DEFAULT_JSONAPI_PAYLOAD_MALFORMED, logger: DEFAULT_LOGGER }.freeze diff --git a/lib/jsonapi/rails/controller/deserialization.rb b/lib/jsonapi/rails/controller/deserialization.rb index c89934e..33185c1 100644 --- a/lib/jsonapi/rails/controller/deserialization.rb +++ b/lib/jsonapi/rails/controller/deserialization.rb @@ -55,6 +55,7 @@ def deserializable_resource(key, options = {}, &block) "Unable to deserialize #{key} because no JSON API payload was" \ " found. (#{controller.controller_name}##{params[:action]})" end + controller.jsonapi_payload_malformed next end diff --git a/lib/jsonapi/rails/controller/hooks.rb b/lib/jsonapi/rails/controller/hooks.rb index d18a6c6..49370e3 100644 --- a/lib/jsonapi/rails/controller/hooks.rb +++ b/lib/jsonapi/rails/controller/hooks.rb @@ -69,6 +69,12 @@ def jsonapi_meta def jsonapi_pagination(resources) instance_exec(resources, &JSONAPI::Rails.config[:jsonapi_pagination]) end + + # Hook for rendering jsonapi_errors when no payload passed + def jsonapi_payload_malformed + return unless JSONAPI::Rails.config[:jsonapi_payload_malformed] + instance_exec(&JSONAPI::Rails.config[:jsonapi_payload_malformed]) + end end end end diff --git a/spec/deserialization_spec.rb b/spec/deserialization_spec.rb index f7a0454..90143d0 100644 --- a/spec/deserialization_spec.rb +++ b/spec/deserialization_spec.rb @@ -93,4 +93,47 @@ def create expect(controller.jsonapi_pointers).to eq(expected) end end + + context 'when unable to deserialize resource from params' do + controller do + deserializable_resource :user + + def create + render plain: :ok + end + end + + context 'with the default config' do + it 'makes the deserialized resource available in params' do + post :create + + expect(response.body).to eq( + { + :errors => [ + { + :links => { + :about => "http://jsonapi.org/format/" + }, + :title => "Non-compliant Request Body", + :detail => "The request was not formatted in compliance with the application/vnd.api+json spec" + } + ], + :jsonapi => { + :version=>"1.0" + } + }.to_json + ) + expect(response).to be_bad_request + end + end + + context 'when the config[:jsonapi_payload_malformed] == nil' do + it 'makes the deserialization mapping available via #jsonapi_pointers' do + with_config(jsonapi_payload_malformed: nil) { post :create } + + expect(response.body).to eq('ok') + expect(response).to be_success + end + end + end end diff --git a/spec/dummy/config/initializers/new_framework_defaults.rb b/spec/dummy/config/initializers/new_framework_defaults.rb index 0706caf..b6c8df7 100644 --- a/spec/dummy/config/initializers/new_framework_defaults.rb +++ b/spec/dummy/config/initializers/new_framework_defaults.rb @@ -18,7 +18,9 @@ Rails.application.config.active_record.belongs_to_required_by_default = true # Do not halt callback chains when a callback returns false. Previous versions had true. -ActiveSupport.halt_callback_chains_on_return_false = false +if Rails.version.to_f < 5.2 + ActiveSupport.halt_callback_chains_on_return_false = false +end # Configure SSL options to enable HSTS with subdomains. Previous versions had false. Rails.application.config.ssl_options = { hsts: { subdomains: true } }