fun_with_json_api 0.0.10.4 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- 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