fun_with_json_api 0.0.10.4 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/config/locales/fun_with_json_api.en.yml +2 -0
- data/lib/fun_with_json_api/configuration.rb +10 -0
- data/lib/fun_with_json_api/middleware/catch_json_api_parse_errors.rb +36 -0
- data/lib/fun_with_json_api/railtie.rb +16 -1
- data/lib/fun_with_json_api/schema_validators/check_document_id_matches_resource.rb +20 -0
- data/lib/fun_with_json_api/version.rb +1 -1
- data/lib/fun_with_json_api.rb +11 -0
- data/spec/dummy/app/controllers/application_controller.rb +8 -0
- data/spec/dummy/config/application.rb +1 -1
- data/spec/dummy/config/environments/test.rb +7 -2
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/log/test.log +29214 -0
- data/spec/fun_with_json_api/configuration_spec.rb +19 -0
- data/spec/fun_with_json_api/railtie_spec.rb +2 -2
- data/spec/fun_with_json_api/schema_validators/check_document_id_matches_resource_spec.rb +42 -0
- data/spec/requests/request_parsing_spec.rb +79 -0
- metadata +12 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1328b91ef284a304fd92db41e7a2aadb2a095e2a
|
4
|
+
data.tar.gz: 0f457c751fa862274f6a164bc0f4a99896e99bb8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2901cbad60ecfb0558f25561f00953f99b898930e9812f28ee71e6984b7abb49bbecc96747451485b88923edc3696edf84a381dccfb52c0912c3aae759b205a0
|
7
|
+
data.tar.gz: 3c1a3fec849e9b3a93a030c3c6d767f21ff3e37c62efbb9dc8b8375276a4ad811c66f8732d3286b5972f9c013718f75da9511385e77eeb5c7c69bd883fc6d01a
|
@@ -1,6 +1,7 @@
|
|
1
1
|
en:
|
2
2
|
fun_with_json_api:
|
3
3
|
exceptions:
|
4
|
+
invalid_request_body: 'Request json_api body could not be parsed'
|
4
5
|
invalid_document: 'Request json_api document is invalid'
|
5
6
|
invalid_document_identifier: 'Request json_api data id is invalid'
|
6
7
|
invalid_client_generated_identifier: 'Request json_api data id has already been used for an existing resource'
|
@@ -27,6 +28,7 @@ en:
|
|
27
28
|
invalid_uuid_v4_attribute: 'UUID value must be RFC 4122 Version 4 UUID (i.e. "f47ac10b-58cc-4372-a567-0e02b2c3d479")'
|
28
29
|
collection_method_not_supported: 'The current relationship does not support this action'
|
29
30
|
schema_validators:
|
31
|
+
document_id_is_not_a_string_message: data id value must be a JSON String (i.e. "1234")
|
30
32
|
document_id_does_not_match_resource: "Expected data id to match resource at endpoint: %{expected}"
|
31
33
|
document_type_does_not_match_endpoint: "Expected data type to be a '%{expected}' resource"
|
32
34
|
invalid_relationship_type_in_array: "Expected '%{relationship}' relationship to be an Array of '%{relationship_type}' resource identifiers"
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module FunWithJsonApi
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :force_render_parse_errors_as_json_api
|
4
|
+
alias force_render_parse_errors_as_json_api? force_render_parse_errors_as_json_api
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@force_render_parse_errors_as_json_api = false
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module FunWithJsonApi
|
2
|
+
module Middleware
|
3
|
+
class CatchJsonApiParseErrors
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
@app.call(env)
|
10
|
+
rescue ActionDispatch::ParamsParser::ParseError => error
|
11
|
+
if env['CONTENT_TYPE'] == FunWithJsonApi::MEDIA_TYPE && respond_with_json_api_error?(env)
|
12
|
+
build_json_api_parse_error_response
|
13
|
+
else
|
14
|
+
raise error
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def build_json_api_parse_error_response
|
21
|
+
title = I18n.t('fun_with_json_api.exceptions.invalid_request_body')
|
22
|
+
[
|
23
|
+
400, { 'Content-Type' => FunWithJsonApi::MEDIA_TYPE },
|
24
|
+
[
|
25
|
+
{ errors: [{ code: 'invalid_request_body', title: title, status: '400' }] }.to_json
|
26
|
+
]
|
27
|
+
]
|
28
|
+
end
|
29
|
+
|
30
|
+
def respond_with_json_api_error?(env)
|
31
|
+
FunWithJsonApi.configuration.force_render_parse_errors_as_json_api? ||
|
32
|
+
env['HTTP_ACCEPT'] =~ %r{application\/vnd\.api\+json}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,12 +1,15 @@
|
|
1
1
|
require 'fun_with_json_api/controller_methods'
|
2
2
|
require 'fun_with_json_api/action_controller_extensions/serialization'
|
3
|
+
require 'fun_with_json_api/middleware/catch_json_api_parse_errors'
|
3
4
|
|
4
5
|
Mime::Type.register FunWithJsonApi::MEDIA_TYPE, :json_api
|
5
6
|
|
6
7
|
module FunWithJsonApi
|
7
8
|
# Mountable engine for fun with json_api
|
8
9
|
class Railtie < Rails::Railtie
|
9
|
-
|
10
|
+
class ParseError < ::StandardError; end
|
11
|
+
|
12
|
+
initializer :register_json_api_parser do |app|
|
10
13
|
parsers =
|
11
14
|
if Rails::VERSION::MAJOR >= 5
|
12
15
|
ActionDispatch::Http::Parameters
|
@@ -19,6 +22,18 @@ module FunWithJsonApi
|
|
19
22
|
data = { _json: data } unless data.is_a?(Hash)
|
20
23
|
data.with_indifferent_access
|
21
24
|
end
|
25
|
+
|
26
|
+
# Add Middleware for catching parser errors
|
27
|
+
if Rails::VERSION::MAJOR >= 5
|
28
|
+
ActionDispatch::Request.parameter_parsers = parsers::DEFAULT_PARSERS
|
29
|
+
app.config.middleware.use(
|
30
|
+
'FunWithJsonApi::Middleware::CatchJsonApiParseErrors'
|
31
|
+
)
|
32
|
+
else
|
33
|
+
app.config.middleware.insert_before(
|
34
|
+
parsers, 'FunWithJsonApi::Middleware::CatchJsonApiParseErrors'
|
35
|
+
)
|
36
|
+
end
|
22
37
|
end
|
23
38
|
initializer :register_json_api_renderer do
|
24
39
|
ActionController::Renderers.add :json_api do |json, options|
|
@@ -20,14 +20,27 @@ module FunWithJsonApi
|
|
20
20
|
def call
|
21
21
|
if resource.try(:persisted?)
|
22
22
|
# Ensure correct update document is being sent
|
23
|
+
check_resource_id_is_a_string
|
23
24
|
check_resource_id_matches_document_id
|
24
25
|
elsif document_id
|
25
26
|
# Ensure correct create document is being sent
|
27
|
+
check_resource_id_is_a_string
|
26
28
|
check_resource_id_can_be_client_generated
|
27
29
|
check_resource_id_has_not_already_been_used
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
33
|
+
def check_resource_id_is_a_string
|
34
|
+
unless document_id.is_a?(String)
|
35
|
+
payload = ExceptionPayload.new(
|
36
|
+
detail: document_id_is_not_a_string_message,
|
37
|
+
pointer: '/data/id'
|
38
|
+
)
|
39
|
+
message = "document id is not a string: #{document_id.class.name}"
|
40
|
+
raise Exceptions::InvalidDocumentIdentifier.new(message, payload)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
31
44
|
def check_resource_id_matches_document_id
|
32
45
|
if document_id != resource_id
|
33
46
|
message = "resource id '#{resource_id}' does not match the expected id for"\
|
@@ -67,6 +80,13 @@ module FunWithJsonApi
|
|
67
80
|
|
68
81
|
private
|
69
82
|
|
83
|
+
def document_id_is_not_a_string_message
|
84
|
+
I18n.t(
|
85
|
+
:document_id_is_not_a_string_message,
|
86
|
+
scope: 'fun_with_json_api.schema_validators'
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
70
90
|
def document_id_does_not_match_resource_message
|
71
91
|
I18n.t(
|
72
92
|
:document_id_does_not_match_resource,
|
data/lib/fun_with_json_api.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'fun_with_json_api/exception'
|
2
2
|
require 'fun_with_json_api/attribute'
|
3
3
|
|
4
|
+
require 'fun_with_json_api/configuration'
|
4
5
|
require 'fun_with_json_api/pre_deserializer'
|
5
6
|
require 'fun_with_json_api/deserializer'
|
6
7
|
require 'fun_with_json_api/schema_validator'
|
@@ -14,6 +15,16 @@ module FunWithJsonApi
|
|
14
15
|
|
15
16
|
module_function
|
16
17
|
|
18
|
+
attr_writer :configuration
|
19
|
+
|
20
|
+
def configuration
|
21
|
+
@configuration ||= Configuration.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def configure
|
25
|
+
yield(configuration)
|
26
|
+
end
|
27
|
+
|
17
28
|
def deserialize(document, deserializer_class, resource = nil, options = {})
|
18
29
|
# Prepare the deserializer and the expected config
|
19
30
|
deserializer = deserializer_class.create(options)
|
@@ -1,5 +1,13 @@
|
|
1
1
|
class ApplicationController < ActionController::Base
|
2
|
+
include FunWithJsonApi::ControllerMethods
|
3
|
+
|
4
|
+
rescue_from FunWithJsonApi::Exception, with: :render_fun_with_json_api_exception
|
5
|
+
|
2
6
|
# Prevent CSRF attacks by raising an exception.
|
3
7
|
# For APIs, you may want to use :null_session instead.
|
4
8
|
protect_from_forgery with: :exception
|
9
|
+
|
10
|
+
def echo
|
11
|
+
render json: params.slice(:data)
|
12
|
+
end
|
5
13
|
end
|
@@ -20,6 +20,6 @@ module Dummy
|
|
20
20
|
# config.i18n.default_locale = :de
|
21
21
|
|
22
22
|
# Do not swallow errors in after_commit/after_rollback callbacks.
|
23
|
-
config.active_record.raise_in_transactional_callbacks = true
|
23
|
+
# config.active_record.raise_in_transactional_callbacks = true
|
24
24
|
end
|
25
25
|
end
|
@@ -13,8 +13,13 @@ Rails.application.configure do
|
|
13
13
|
config.eager_load = false
|
14
14
|
|
15
15
|
# Configure static file server for tests with Cache-Control for performance.
|
16
|
-
config.
|
17
|
-
|
16
|
+
if config.respond_to?(:public_file_server)
|
17
|
+
config.public_file_server.enabled = true
|
18
|
+
config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=3600' }
|
19
|
+
else
|
20
|
+
config.serve_static_files = true
|
21
|
+
config.static_cache_control = 'public, max-age=3600'
|
22
|
+
end
|
18
23
|
|
19
24
|
# Show full error reports and disable caching.
|
20
25
|
config.consider_all_requests_local = true
|
data/spec/dummy/config/routes.rb
CHANGED