jsonapi-resources 0.9.12 → 0.10.0
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/LICENSE.txt +1 -1
- data/README.md +34 -11
- data/lib/bug_report_templates/rails_5_latest.rb +125 -0
- data/lib/bug_report_templates/rails_5_master.rb +140 -0
- data/lib/jsonapi/active_relation/adapters/join_left_active_record_adapter.rb +27 -0
- data/lib/jsonapi/active_relation/join_manager.rb +297 -0
- data/lib/jsonapi/active_relation_resource.rb +836 -0
- data/lib/jsonapi/acts_as_resource_controller.rb +123 -107
- data/lib/jsonapi/basic_resource.rb +1162 -0
- data/lib/jsonapi/cached_response_fragment.rb +127 -0
- data/lib/jsonapi/compiled_json.rb +11 -1
- data/lib/jsonapi/configuration.rb +36 -5
- data/lib/jsonapi/error.rb +27 -0
- data/lib/jsonapi/error_codes.rb +2 -0
- data/lib/jsonapi/exceptions.rb +63 -40
- data/lib/jsonapi/formatter.rb +3 -3
- data/lib/jsonapi/include_directives.rb +18 -75
- data/lib/jsonapi/link_builder.rb +18 -25
- data/lib/jsonapi/operation.rb +16 -5
- data/lib/jsonapi/operation_result.rb +73 -15
- data/lib/jsonapi/path.rb +43 -0
- data/lib/jsonapi/path_segment.rb +76 -0
- data/lib/jsonapi/processor.rb +234 -108
- data/lib/jsonapi/relationship.rb +108 -24
- data/lib/jsonapi/request_parser.rb +383 -396
- data/lib/jsonapi/resource.rb +3 -1376
- data/lib/jsonapi/resource_controller_metal.rb +5 -2
- data/lib/jsonapi/resource_fragment.rb +47 -0
- data/lib/jsonapi/resource_id_tree.rb +112 -0
- data/lib/jsonapi/resource_identity.rb +42 -0
- data/lib/jsonapi/resource_serializer.rb +124 -286
- data/lib/jsonapi/resource_set.rb +177 -0
- data/lib/jsonapi/resources/railtie.rb +9 -0
- data/lib/jsonapi/resources/version.rb +1 -1
- data/lib/jsonapi/response_document.rb +104 -87
- data/lib/jsonapi/routing_ext.rb +19 -21
- data/lib/jsonapi-resources.rb +20 -4
- data/lib/tasks/check_upgrade.rake +52 -0
- metadata +32 -29
- data/lib/jsonapi/cached_resource_fragment.rb +0 -127
- data/lib/jsonapi/operation_dispatcher.rb +0 -88
- data/lib/jsonapi/operation_results.rb +0 -35
- data/lib/jsonapi/relationship_builder.rb +0 -167
@@ -0,0 +1,127 @@
|
|
1
|
+
module JSONAPI
|
2
|
+
class CachedResponseFragment
|
3
|
+
|
4
|
+
Lookup = Struct.new(:resource_klass, :serializer_config_key, :context, :context_key, :cache_ids) do
|
5
|
+
|
6
|
+
def type
|
7
|
+
resource_klass._type
|
8
|
+
end
|
9
|
+
|
10
|
+
def keys
|
11
|
+
cache_ids.map do |(id, cache_key)|
|
12
|
+
[type, id, cache_key, serializer_config_key, context_key]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Write = Struct.new(:resource_klass, :resource, :serializer, :serializer_config_key, :context, :context_key, :relationship_data) do
|
18
|
+
def to_key_value
|
19
|
+
|
20
|
+
(id, cache_key) = resource.cache_id
|
21
|
+
|
22
|
+
json = serializer.object_hash(resource, relationship_data)
|
23
|
+
|
24
|
+
cr = CachedResponseFragment.new(
|
25
|
+
resource_klass,
|
26
|
+
id,
|
27
|
+
json['type'],
|
28
|
+
context,
|
29
|
+
resource.fetchable_fields,
|
30
|
+
json['relationships'],
|
31
|
+
json['links'],
|
32
|
+
json['attributes'],
|
33
|
+
json['meta']
|
34
|
+
)
|
35
|
+
|
36
|
+
key = [resource_klass._type, id, cache_key, serializer_config_key, context_key]
|
37
|
+
|
38
|
+
[key, cr]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_reader :resource_klass, :id, :type, :context, :fetchable_fields, :relationships,
|
43
|
+
:links_json, :attributes_json, :meta_json
|
44
|
+
|
45
|
+
def initialize(resource_klass, id, type, context, fetchable_fields, relationships,
|
46
|
+
links_json, attributes_json, meta_json)
|
47
|
+
@resource_klass = resource_klass
|
48
|
+
@id = id
|
49
|
+
@type = type
|
50
|
+
@context = context
|
51
|
+
@fetchable_fields = Set.new(fetchable_fields)
|
52
|
+
|
53
|
+
# Relationships left uncompiled because we'll often want to insert included ids on retrieval
|
54
|
+
@relationships = relationships
|
55
|
+
|
56
|
+
@links_json = CompiledJson.of(links_json)
|
57
|
+
@attributes_json = CompiledJson.of(attributes_json)
|
58
|
+
@meta_json = CompiledJson.of(meta_json)
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_cache_value
|
62
|
+
{
|
63
|
+
id: id,
|
64
|
+
type: type,
|
65
|
+
fetchable: fetchable_fields,
|
66
|
+
rels: relationships,
|
67
|
+
links: links_json.try(:to_s),
|
68
|
+
attrs: attributes_json.try(:to_s),
|
69
|
+
meta: meta_json.try(:to_s)
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
# @param [Lookup[]] lookups
|
74
|
+
# @return [Hash<Class<Resource>, Hash<ID, CachedResourceFragment>>]
|
75
|
+
def self.lookup(lookups, context)
|
76
|
+
type_to_klass = lookups.map {|l| [l.type, l.resource_klass]}.to_h
|
77
|
+
|
78
|
+
keys = lookups.map(&:keys).flatten(1)
|
79
|
+
|
80
|
+
hits = JSONAPI.configuration.resource_cache.read_multi(*keys).reject {|_, v| v.nil?}
|
81
|
+
|
82
|
+
return keys.inject({}) do |hash, key|
|
83
|
+
(type, id, _, _) = key
|
84
|
+
resource_klass = type_to_klass[type]
|
85
|
+
hash[resource_klass] ||= {}
|
86
|
+
|
87
|
+
if hits.has_key?(key)
|
88
|
+
hash[resource_klass][id] = self.from_cache_value(resource_klass, context, hits[key])
|
89
|
+
else
|
90
|
+
hash[resource_klass][id] = nil
|
91
|
+
end
|
92
|
+
|
93
|
+
hash
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# @param [Write[]] lookups
|
98
|
+
def self.write(writes)
|
99
|
+
key_values = writes.map(&:to_key_value)
|
100
|
+
|
101
|
+
to_write = key_values.map {|(k, v)| [k, v.to_cache_value]}.to_h
|
102
|
+
|
103
|
+
if JSONAPI.configuration.resource_cache.respond_to? :write_multi
|
104
|
+
JSONAPI.configuration.resource_cache.write_multi(to_write)
|
105
|
+
else
|
106
|
+
to_write.each do |key, value|
|
107
|
+
JSONAPI.configuration.resource_cache.write(key, value)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.from_cache_value(resource_klass, context, h)
|
114
|
+
new(
|
115
|
+
resource_klass,
|
116
|
+
h.fetch(:id),
|
117
|
+
h.fetch(:type),
|
118
|
+
context,
|
119
|
+
h.fetch(:fetchable),
|
120
|
+
h.fetch(:rels, nil),
|
121
|
+
h.fetch(:links, nil),
|
122
|
+
h.fetch(:attrs, nil),
|
123
|
+
h.fetch(:meta, nil)
|
124
|
+
)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -5,6 +5,7 @@ module JSONAPI
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def self.of(obj)
|
8
|
+
# :nocov:
|
8
9
|
case obj
|
9
10
|
when NilClass then nil
|
10
11
|
when CompiledJson then obj
|
@@ -12,6 +13,7 @@ module JSONAPI
|
|
12
13
|
when Hash then CompiledJson.compile(obj)
|
13
14
|
else raise "Can't figure out how to turn #{obj.inspect} into CompiledJson"
|
14
15
|
end
|
16
|
+
# :nocov:
|
15
17
|
end
|
16
18
|
|
17
19
|
def initialize(json, h = nil)
|
@@ -19,7 +21,7 @@ module JSONAPI
|
|
19
21
|
@h = h
|
20
22
|
end
|
21
23
|
|
22
|
-
def to_json(*
|
24
|
+
def to_json(*_args)
|
23
25
|
@json
|
24
26
|
end
|
25
27
|
|
@@ -27,9 +29,17 @@ module JSONAPI
|
|
27
29
|
@json
|
28
30
|
end
|
29
31
|
|
32
|
+
# :nocov:
|
30
33
|
def to_h
|
31
34
|
@h ||= JSON.parse(@json)
|
32
35
|
end
|
36
|
+
# :nocov:
|
37
|
+
|
38
|
+
def [](key)
|
39
|
+
# :nocov:
|
40
|
+
to_h[key]
|
41
|
+
# :nocov:
|
42
|
+
end
|
33
43
|
|
34
44
|
undef_method :as_json
|
35
45
|
end
|
@@ -8,8 +8,11 @@ module JSONAPI
|
|
8
8
|
:resource_key_type,
|
9
9
|
:route_format,
|
10
10
|
:raise_if_parameters_not_allowed,
|
11
|
+
:warn_on_route_setup_issues,
|
11
12
|
:warn_on_missing_routes,
|
12
|
-
:
|
13
|
+
:warn_on_performance_issues,
|
14
|
+
:default_allow_include_to_one,
|
15
|
+
:default_allow_include_to_many,
|
13
16
|
:allow_sort,
|
14
17
|
:allow_filter,
|
15
18
|
:default_paginator,
|
@@ -24,6 +27,7 @@ module JSONAPI
|
|
24
27
|
:top_level_meta_page_count_key,
|
25
28
|
:allow_transactions,
|
26
29
|
:include_backtraces_in_errors,
|
30
|
+
:include_application_backtraces_in_errors,
|
27
31
|
:exception_class_whitelist,
|
28
32
|
:whitelist_all_exceptions,
|
29
33
|
:always_include_to_one_linkage_data,
|
@@ -31,6 +35,7 @@ module JSONAPI
|
|
31
35
|
:cache_formatters,
|
32
36
|
:use_relationship_reflection,
|
33
37
|
:resource_cache,
|
38
|
+
:default_caching,
|
34
39
|
:default_resource_cache_field,
|
35
40
|
:resource_cache_digest_function,
|
36
41
|
:resource_cache_usage_report_function,
|
@@ -47,13 +52,16 @@ module JSONAPI
|
|
47
52
|
self.resource_key_type = :integer
|
48
53
|
|
49
54
|
# optional request features
|
50
|
-
self.
|
55
|
+
self.default_allow_include_to_one = true
|
56
|
+
self.default_allow_include_to_many = true
|
51
57
|
self.allow_sort = true
|
52
58
|
self.allow_filter = true
|
53
59
|
|
54
60
|
self.raise_if_parameters_not_allowed = true
|
55
61
|
|
62
|
+
self.warn_on_route_setup_issues = true
|
56
63
|
self.warn_on_missing_routes = true
|
64
|
+
self.warn_on_performance_issues = true
|
57
65
|
|
58
66
|
# :none, :offset, :paged, or a custom paginator name
|
59
67
|
self.default_paginator = :none
|
@@ -75,8 +83,12 @@ module JSONAPI
|
|
75
83
|
self.use_text_errors = false
|
76
84
|
|
77
85
|
# Whether or not to include exception backtraces in JSONAPI error
|
78
|
-
# responses. Defaults to `false` in
|
79
|
-
self.include_backtraces_in_errors =
|
86
|
+
# responses. Defaults to `false` in anything other than development or test.
|
87
|
+
self.include_backtraces_in_errors = (Rails.env.development? || Rails.env.test?)
|
88
|
+
|
89
|
+
# Whether or not to include exception application backtraces in JSONAPI error
|
90
|
+
# responses. Defaults to `false` in anything other than development or test.
|
91
|
+
self.include_application_backtraces_in_errors = (Rails.env.development? || Rails.env.test?)
|
80
92
|
|
81
93
|
# List of classes that should not be rescued by the operations processor.
|
82
94
|
# For example, if you use Pundit for authorization, you might
|
@@ -121,6 +133,11 @@ module JSONAPI
|
|
121
133
|
# Rails cache store.
|
122
134
|
self.resource_cache = nil
|
123
135
|
|
136
|
+
# Cache resources by default
|
137
|
+
# Cache resources by default. Individual resources can be excluded from caching by calling:
|
138
|
+
# `caching false`
|
139
|
+
self.default_caching = false
|
140
|
+
|
124
141
|
# Default resource cache field
|
125
142
|
# On Resources with caching enabled, this field will be used to check for out-of-date
|
126
143
|
# cache entries, unless overridden on a specific Resource. Defaults to "updated_at".
|
@@ -211,7 +228,13 @@ module JSONAPI
|
|
211
228
|
@default_processor_klass = default_processor_klass
|
212
229
|
end
|
213
230
|
|
214
|
-
|
231
|
+
def allow_include=(allow_include)
|
232
|
+
ActiveSupport::Deprecation.warn('`allow_include` has been replaced by `default_allow_include_to_one` and `default_allow_include_to_many` options.')
|
233
|
+
@default_allow_include_to_one = allow_include
|
234
|
+
@default_allow_include_to_many = allow_include
|
235
|
+
end
|
236
|
+
|
237
|
+
attr_writer :allow_sort, :allow_filter, :default_allow_include_to_one, :default_allow_include_to_many
|
215
238
|
|
216
239
|
attr_writer :default_paginator
|
217
240
|
|
@@ -235,6 +258,8 @@ module JSONAPI
|
|
235
258
|
|
236
259
|
attr_writer :include_backtraces_in_errors
|
237
260
|
|
261
|
+
attr_writer :include_application_backtraces_in_errors
|
262
|
+
|
238
263
|
attr_writer :exception_class_whitelist
|
239
264
|
|
240
265
|
attr_writer :whitelist_all_exceptions
|
@@ -245,12 +270,18 @@ module JSONAPI
|
|
245
270
|
|
246
271
|
attr_writer :raise_if_parameters_not_allowed
|
247
272
|
|
273
|
+
attr_writer :warn_on_route_setup_issues
|
274
|
+
|
248
275
|
attr_writer :warn_on_missing_routes
|
249
276
|
|
277
|
+
attr_writer :warn_on_performance_issues
|
278
|
+
|
250
279
|
attr_writer :use_relationship_reflection
|
251
280
|
|
252
281
|
attr_writer :resource_cache
|
253
282
|
|
283
|
+
attr_writer :default_caching
|
284
|
+
|
254
285
|
attr_writer :default_resource_cache_field
|
255
286
|
|
256
287
|
attr_writer :resource_cache_digest_function
|
data/lib/jsonapi/error.rb
CHANGED
@@ -24,6 +24,33 @@ module JSONAPI
|
|
24
24
|
instance_variables.each {|var| hash[var.to_s.delete('@')] = instance_variable_get(var) unless instance_variable_get(var).nil? }
|
25
25
|
hash
|
26
26
|
end
|
27
|
+
|
28
|
+
def update_with_overrides(error_object_overrides)
|
29
|
+
@title = error_object_overrides[:title] || @title
|
30
|
+
@detail = error_object_overrides[:detail] || @detail
|
31
|
+
@id = error_object_overrides[:id] || @id
|
32
|
+
@href = error_object_overrides[:href] || href
|
33
|
+
|
34
|
+
if error_object_overrides[:code]
|
35
|
+
# :nocov:
|
36
|
+
@code = if JSONAPI.configuration.use_text_errors
|
37
|
+
TEXT_ERRORS[error_object_overrides[:code]]
|
38
|
+
else
|
39
|
+
error_object_overrides[:code]
|
40
|
+
end
|
41
|
+
# :nocov:
|
42
|
+
end
|
43
|
+
|
44
|
+
@source = error_object_overrides[:source] || @source
|
45
|
+
@links = error_object_overrides[:links] || @links
|
46
|
+
|
47
|
+
if error_object_overrides[:status]
|
48
|
+
# :nocov:
|
49
|
+
@status = Rack::Utils::SYMBOL_TO_STATUS_CODE[error_object_overrides[:status]].to_s
|
50
|
+
# :nocov:
|
51
|
+
end
|
52
|
+
@meta = error_object_overrides[:meta] || @meta
|
53
|
+
end
|
27
54
|
end
|
28
55
|
|
29
56
|
class Warning
|
data/lib/jsonapi/error_codes.rb
CHANGED
@@ -20,6 +20,7 @@ module JSONAPI
|
|
20
20
|
INVALID_FILTERS_SYNTAX = '120'
|
21
21
|
SAVE_FAILED = '121'
|
22
22
|
INVALID_DATA_FORMAT = '122'
|
23
|
+
INVALID_RELATIONSHIP = '123'
|
23
24
|
BAD_REQUEST = '400'
|
24
25
|
FORBIDDEN = '403'
|
25
26
|
RECORD_NOT_FOUND = '404'
|
@@ -50,6 +51,7 @@ module JSONAPI
|
|
50
51
|
INVALID_FILTERS_SYNTAX => 'INVALID_FILTERS_SYNTAX',
|
51
52
|
SAVE_FAILED => 'SAVE_FAILED',
|
52
53
|
INVALID_DATA_FORMAT => 'INVALID_DATA_FORMAT',
|
54
|
+
INVALID_RELATIONSHIP => 'INVALID_RELATIONSHIP',
|
53
55
|
FORBIDDEN => 'FORBIDDEN',
|
54
56
|
RECORD_NOT_FOUND => 'RECORD_NOT_FOUND',
|
55
57
|
NOT_ACCEPTABLE => 'NOT_ACCEPTABLE',
|
data/lib/jsonapi/exceptions.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module JSONAPI
|
2
2
|
module Exceptions
|
3
3
|
class Error < RuntimeError
|
4
|
-
|
4
|
+
attr_reader :error_object_overrides
|
5
5
|
|
6
6
|
def initialize(error_object_overrides = {})
|
7
7
|
@error_object_overrides = error_object_overrides
|
@@ -18,6 +18,22 @@ module JSONAPI
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
+
class Errors < Error
|
22
|
+
def initialize(errors, error_object_overrides = {})
|
23
|
+
@errors = errors
|
24
|
+
|
25
|
+
@errors.each do |error|
|
26
|
+
error.update_with_overrides(error_object_overrides)
|
27
|
+
end
|
28
|
+
|
29
|
+
super(error_object_overrides)
|
30
|
+
end
|
31
|
+
|
32
|
+
def errors
|
33
|
+
@errors
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
21
37
|
class InternalServerError < Error
|
22
38
|
attr_accessor :exception
|
23
39
|
|
@@ -33,6 +49,12 @@ module JSONAPI
|
|
33
49
|
meta[:backtrace] = exception.backtrace
|
34
50
|
end
|
35
51
|
|
52
|
+
if JSONAPI.configuration.include_application_backtraces_in_errors
|
53
|
+
meta ||= Hash.new
|
54
|
+
meta[:exception] ||= exception.message
|
55
|
+
meta[:application_backtrace] = exception.backtrace.select{|line| line =~ /#{Rails.root}/}
|
56
|
+
end
|
57
|
+
|
36
58
|
[create_error_object(code: JSONAPI::INTERNAL_SERVER_ERROR,
|
37
59
|
status: :internal_server_error,
|
38
60
|
title: I18n.t('jsonapi-resources.exceptions.internal_server_error.title',
|
@@ -119,49 +141,30 @@ module JSONAPI
|
|
119
141
|
end
|
120
142
|
end
|
121
143
|
|
122
|
-
|
123
|
-
class HasManyRelationExists < Error
|
124
|
-
attr_accessor :id
|
125
|
-
|
126
|
-
def initialize(id, error_object_overrides = {})
|
127
|
-
@id = id
|
128
|
-
super(error_object_overrides)
|
129
|
-
end
|
130
|
-
|
131
|
-
def errors
|
132
|
-
[create_error_object(code: JSONAPI::RELATION_EXISTS,
|
133
|
-
status: :bad_request,
|
134
|
-
title: I18n.translate('jsonapi-resources.exceptions.has_many_relation.title',
|
135
|
-
default: 'Relation exists'),
|
136
|
-
detail: I18n.translate('jsonapi-resources.exceptions.has_many_relation.detail',
|
137
|
-
default: "The relation to #{id} already exists.",
|
138
|
-
id: id))]
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
144
|
class BadRequest < Error
|
143
|
-
def initialize(exception)
|
145
|
+
def initialize(exception, error_object_overrides = {})
|
144
146
|
@exception = exception
|
147
|
+
super(error_object_overrides)
|
145
148
|
end
|
146
149
|
|
147
150
|
def errors
|
148
|
-
[
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
151
|
+
[create_error_object(code: JSONAPI::BAD_REQUEST,
|
152
|
+
status: :bad_request,
|
153
|
+
title: I18n.translate('jsonapi-resources.exceptions.bad_request.title',
|
154
|
+
default: 'Bad Request'),
|
155
|
+
detail: I18n.translate('jsonapi-resources.exceptions.bad_request.detail',
|
156
|
+
default: @exception))]
|
154
157
|
end
|
155
158
|
end
|
156
159
|
|
157
160
|
class InvalidRequestFormat < Error
|
158
161
|
def errors
|
159
|
-
[
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
162
|
+
[create_error_object(code: JSONAPI::BAD_REQUEST,
|
163
|
+
status: :bad_request,
|
164
|
+
title: I18n.translate('jsonapi-resources.exceptions.invalid_request_format.title',
|
165
|
+
default: 'Bad Request'),
|
166
|
+
detail: I18n.translate('jsonapi-resources.exceptions.invalid_request_format.detail',
|
167
|
+
default: 'Request must be a hash'))]
|
165
168
|
end
|
166
169
|
end
|
167
170
|
|
@@ -324,6 +327,26 @@ module JSONAPI
|
|
324
327
|
end
|
325
328
|
end
|
326
329
|
|
330
|
+
class InvalidRelationship < Error
|
331
|
+
attr_accessor :relationship_name, :type
|
332
|
+
|
333
|
+
def initialize(type, relationship_name, error_object_overrides = {})
|
334
|
+
@relationship_name = relationship_name
|
335
|
+
@type = type
|
336
|
+
super(error_object_overrides)
|
337
|
+
end
|
338
|
+
|
339
|
+
def errors
|
340
|
+
[create_error_object(code: JSONAPI::INVALID_RELATIONSHIP,
|
341
|
+
status: :bad_request,
|
342
|
+
title: I18n.translate('jsonapi-resources.exceptions.invalid_relationship.title',
|
343
|
+
default: 'Invalid relationship'),
|
344
|
+
detail: I18n.translate('jsonapi-resources.exceptions.invalid_relationship.detail',
|
345
|
+
default: "#{relationship_name} is not a valid field for #{type}.",
|
346
|
+
relationship_name: relationship_name, type: type))]
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
327
350
|
class InvalidInclude < Error
|
328
351
|
attr_accessor :relationship, :resource
|
329
352
|
|
@@ -339,7 +362,7 @@ module JSONAPI
|
|
339
362
|
title: I18n.translate('jsonapi-resources.exceptions.invalid_include.title',
|
340
363
|
default: 'Invalid field'),
|
341
364
|
detail: I18n.translate('jsonapi-resources.exceptions.invalid_include.detail',
|
342
|
-
default: "#{relationship} is not a valid relationship of #{resource}",
|
365
|
+
default: "#{relationship} is not a valid includable relationship of #{resource}",
|
343
366
|
relationship: relationship, resource: resource))]
|
344
367
|
end
|
345
368
|
end
|
@@ -374,11 +397,11 @@ module JSONAPI
|
|
374
397
|
|
375
398
|
def errors
|
376
399
|
[create_error_object(code: JSONAPI::PARAM_NOT_ALLOWED,
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
400
|
+
status: :bad_request,
|
401
|
+
title: I18n.translate('jsonapi-resources.exceptions.parameter_not_allowed.title',
|
402
|
+
default: 'Param not allowed'),
|
403
|
+
detail: I18n.translate('jsonapi-resources.exceptions.parameters_not_allowed.detail',
|
404
|
+
default: "#{param} is not allowed.", param: param))]
|
382
405
|
end
|
383
406
|
end
|
384
407
|
|
data/lib/jsonapi/formatter.rb
CHANGED
@@ -108,7 +108,7 @@ end
|
|
108
108
|
|
109
109
|
class DasherizedKeyFormatter < JSONAPI::KeyFormatter
|
110
110
|
class << self
|
111
|
-
def format(
|
111
|
+
def format(_key)
|
112
112
|
super.underscore.dasherize
|
113
113
|
end
|
114
114
|
|
@@ -146,7 +146,7 @@ end
|
|
146
146
|
|
147
147
|
class CamelizedRouteFormatter < JSONAPI::RouteFormatter
|
148
148
|
class << self
|
149
|
-
def format(
|
149
|
+
def format(_route)
|
150
150
|
super.camelize(:lower)
|
151
151
|
end
|
152
152
|
|
@@ -158,7 +158,7 @@ end
|
|
158
158
|
|
159
159
|
class DasherizedRouteFormatter < JSONAPI::RouteFormatter
|
160
160
|
class << self
|
161
|
-
def format(
|
161
|
+
def format(_route)
|
162
162
|
super.dasherize
|
163
163
|
end
|
164
164
|
|
@@ -4,14 +4,12 @@ module JSONAPI
|
|
4
4
|
# For example ['posts.comments.tags']
|
5
5
|
# will transform into =>
|
6
6
|
# {
|
7
|
-
# posts:{
|
8
|
-
#
|
9
|
-
# include_related:{
|
7
|
+
# posts: {
|
8
|
+
# include_related: {
|
10
9
|
# comments:{
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
# include:true
|
10
|
+
# include_related: {
|
11
|
+
# tags: {
|
12
|
+
# include_related: {}
|
15
13
|
# }
|
16
14
|
# }
|
17
15
|
# }
|
@@ -19,9 +17,8 @@ module JSONAPI
|
|
19
17
|
# }
|
20
18
|
# }
|
21
19
|
|
22
|
-
def initialize(resource_klass, includes_array
|
20
|
+
def initialize(resource_klass, includes_array)
|
23
21
|
@resource_klass = resource_klass
|
24
|
-
@force_eager_load = force_eager_load
|
25
22
|
@include_directives_hash = { include_related: {} }
|
26
23
|
includes_array.each do |include|
|
27
24
|
parse_include(include)
|
@@ -32,79 +29,25 @@ module JSONAPI
|
|
32
29
|
@include_directives_hash
|
33
30
|
end
|
34
31
|
|
35
|
-
def model_includes
|
36
|
-
get_includes(@include_directives_hash)
|
37
|
-
end
|
38
|
-
|
39
|
-
def paths
|
40
|
-
delve_paths(get_includes(@include_directives_hash, false))
|
41
|
-
end
|
42
|
-
|
43
|
-
def merge_filter(relation, filter)
|
44
|
-
config = include_config(relation.to_sym)
|
45
|
-
config[:include_filters] ||= {}
|
46
|
-
config[:include_filters].merge!(filter)
|
47
|
-
end
|
48
|
-
|
49
|
-
def include_config(relation)
|
50
|
-
@include_directives_hash[:include_related][relation]
|
51
|
-
end
|
52
|
-
|
53
32
|
private
|
54
33
|
|
55
|
-
def
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
if current_resource_klass
|
62
|
-
current_relationship = current_resource_klass._relationships[fragment]
|
63
|
-
current_resource_klass = current_relationship.try(:resource_klass)
|
64
|
-
else
|
65
|
-
raise JSONAPI::Exceptions::InvalidInclude.new(current_resource_klass, current_path)
|
66
|
-
end
|
67
|
-
|
68
|
-
include_in_join = @force_eager_load || !current_relationship || current_relationship.eager_load_on_include
|
69
|
-
|
70
|
-
current[:include_related][fragment] ||= { include: false, include_related: {}, include_in_join: include_in_join }
|
71
|
-
current = current[:include_related][fragment]
|
72
|
-
end
|
73
|
-
current
|
74
|
-
end
|
75
|
-
|
76
|
-
def get_includes(directive, only_joined_includes = true)
|
77
|
-
ir = directive[:include_related]
|
78
|
-
ir = ir.select { |k,v| v[:include_in_join] } if only_joined_includes
|
34
|
+
def parse_include(include)
|
35
|
+
path = JSONAPI::Path.new(resource_klass: @resource_klass,
|
36
|
+
path_string: include,
|
37
|
+
ensure_default_field: false,
|
38
|
+
parse_fields: false)
|
79
39
|
|
80
|
-
|
81
|
-
sub = get_includes(sub_directive, only_joined_includes)
|
82
|
-
sub.any? ? { name => sub } : name
|
83
|
-
end
|
84
|
-
end
|
40
|
+
current = @include_directives_hash
|
85
41
|
|
86
|
-
|
87
|
-
|
88
|
-
local_path = ''
|
42
|
+
path.segments.each do |segment|
|
43
|
+
relationship_name = segment.relationship.name.to_sym
|
89
44
|
|
90
|
-
|
91
|
-
|
92
|
-
related = get_related(local_path)
|
93
|
-
related[:include] = true
|
45
|
+
current[:include_related][relationship_name] ||= { include_related: {} }
|
46
|
+
current = current[:include_related][relationship_name]
|
94
47
|
end
|
95
|
-
end
|
96
48
|
|
97
|
-
|
98
|
-
|
99
|
-
when Array
|
100
|
-
obj.map{|elem| delve_paths(elem)}.flatten(1)
|
101
|
-
when Hash
|
102
|
-
obj.map{|k,v| [[k]] + delve_paths(v).map{|path| [k] + path } }.flatten(1)
|
103
|
-
when Symbol, String
|
104
|
-
[[obj]]
|
105
|
-
else
|
106
|
-
raise "delve_paths cannot descend into #{obj.class.name}"
|
107
|
-
end
|
49
|
+
rescue JSONAPI::Exceptions::InvalidRelationship => _e
|
50
|
+
raise JSONAPI::Exceptions::InvalidInclude.new(@resource_klass, include)
|
108
51
|
end
|
109
52
|
end
|
110
53
|
end
|