jsonapi-resources 0.9.12 → 0.10.0.beta1
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 +5 -5
- 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-resources.rb +8 -3
- data/lib/jsonapi/active_relation_resource_finder.rb +640 -0
- data/lib/jsonapi/active_relation_resource_finder/join_tree.rb +126 -0
- data/lib/jsonapi/acts_as_resource_controller.rb +121 -106
- data/lib/jsonapi/{cached_resource_fragment.rb → cached_response_fragment.rb} +13 -30
- data/lib/jsonapi/compiled_json.rb +11 -1
- data/lib/jsonapi/configuration.rb +44 -18
- data/lib/jsonapi/error.rb +27 -0
- data/lib/jsonapi/exceptions.rb +43 -40
- data/lib/jsonapi/formatter.rb +3 -3
- data/lib/jsonapi/include_directives.rb +2 -45
- data/lib/jsonapi/link_builder.rb +87 -80
- data/lib/jsonapi/operation.rb +16 -5
- data/lib/jsonapi/operation_result.rb +74 -16
- data/lib/jsonapi/processor.rb +233 -112
- data/lib/jsonapi/relationship.rb +77 -53
- data/lib/jsonapi/request_parser.rb +378 -423
- data/lib/jsonapi/resource.rb +224 -524
- data/lib/jsonapi/resource_controller_metal.rb +2 -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 +133 -301
- data/lib/jsonapi/resource_set.rb +108 -0
- data/lib/jsonapi/resources/version.rb +1 -1
- data/lib/jsonapi/response_document.rb +100 -88
- data/lib/jsonapi/routing_ext.rb +21 -43
- metadata +29 -45
- data/lib/jsonapi/operation_dispatcher.rb +0 -88
- data/lib/jsonapi/operation_results.rb +0 -35
- data/lib/jsonapi/relationship_builder.rb +0 -167
@@ -1,37 +1,26 @@
|
|
1
1
|
module JSONAPI
|
2
|
-
class
|
3
|
-
def self.
|
4
|
-
serializer_config_key = serializer.config_key(resource_klass).gsub("/", "_")
|
2
|
+
class CachedResponseFragment
|
3
|
+
def self.fetch_cached_fragments(resource_klass, serializer_config_key, cache_ids, context)
|
5
4
|
context_json = resource_klass.attribute_caching_context(context).to_json
|
6
5
|
context_b64 = JSONAPI.configuration.resource_cache_digest_function.call(context_json)
|
7
6
|
context_key = "ATTR-CTX-#{context_b64.gsub("/", "_")}"
|
8
7
|
|
9
8
|
results = self.lookup(resource_klass, serializer_config_key, context, context_key, cache_ids)
|
10
9
|
|
11
|
-
miss_ids = results.select{|k,v| v.nil? }.keys
|
12
|
-
unless miss_ids.empty?
|
13
|
-
find_filters = {resource_klass._primary_key => miss_ids.uniq}
|
14
|
-
find_options = {context: context}
|
15
|
-
resource_klass.find(find_filters, find_options).each do |resource|
|
16
|
-
(id, cr) = write(resource_klass, resource, serializer, serializer_config_key, context, context_key)
|
17
|
-
results[id] = cr
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
10
|
if JSONAPI.configuration.resource_cache_usage_report_function
|
11
|
+
miss_ids = results.select{|_k,v| v.nil? }.keys
|
22
12
|
JSONAPI.configuration.resource_cache_usage_report_function.call(
|
23
|
-
|
24
|
-
|
25
|
-
|
13
|
+
resource_klass.name,
|
14
|
+
cache_ids.size - miss_ids.size,
|
15
|
+
miss_ids.size
|
26
16
|
)
|
27
17
|
end
|
28
18
|
|
29
|
-
|
19
|
+
results
|
30
20
|
end
|
31
21
|
|
32
22
|
attr_reader :resource_klass, :id, :type, :context, :fetchable_fields, :relationships,
|
33
|
-
:links_json, :attributes_json, :meta_json
|
34
|
-
:preloaded_fragments
|
23
|
+
:links_json, :attributes_json, :meta_json
|
35
24
|
|
36
25
|
def initialize(resource_klass, id, type, context, fetchable_fields, relationships,
|
37
26
|
links_json, attributes_json, meta_json)
|
@@ -47,9 +36,6 @@ module JSONAPI
|
|
47
36
|
@links_json = CompiledJson.of(links_json)
|
48
37
|
@attributes_json = CompiledJson.of(attributes_json)
|
49
38
|
@meta_json = CompiledJson.of(meta_json)
|
50
|
-
|
51
|
-
# A hash of hashes
|
52
|
-
@preloaded_fragments ||= Hash.new
|
53
39
|
end
|
54
40
|
|
55
41
|
def to_cache_value
|
@@ -64,11 +50,6 @@ module JSONAPI
|
|
64
50
|
}
|
65
51
|
end
|
66
52
|
|
67
|
-
def to_real_resource
|
68
|
-
rs = Resource.resource_for(self.type).find_by_keys([self.id], {context: self.context})
|
69
|
-
return rs.try(:first)
|
70
|
-
end
|
71
|
-
|
72
53
|
private
|
73
54
|
|
74
55
|
def self.lookup(resource_klass, serializer_config_key, context, context_key, cache_ids)
|
@@ -103,12 +84,14 @@ module JSONAPI
|
|
103
84
|
)
|
104
85
|
end
|
105
86
|
|
106
|
-
def self.write(resource_klass, resource, serializer, serializer_config_key, context, context_key)
|
87
|
+
def self.write(resource_klass, resource, serializer, serializer_config_key, context, context_key, relationship_data )
|
107
88
|
(id, cache_key) = resource.cache_id
|
108
|
-
|
89
|
+
|
90
|
+
json = serializer.object_hash(resource, relationship_data)
|
91
|
+
|
109
92
|
cr = self.new(
|
110
93
|
resource_klass,
|
111
|
-
|
94
|
+
id,
|
112
95
|
json['type'],
|
113
96
|
context,
|
114
97
|
resource.fetchable_fields,
|
@@ -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
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'jsonapi/formatter'
|
2
2
|
require 'jsonapi/processor'
|
3
|
+
require 'jsonapi/active_relation_resource_finder'
|
3
4
|
require 'concurrent'
|
4
5
|
|
5
6
|
module JSONAPI
|
@@ -8,13 +9,15 @@ module JSONAPI
|
|
8
9
|
:resource_key_type,
|
9
10
|
:route_format,
|
10
11
|
:raise_if_parameters_not_allowed,
|
11
|
-
:
|
12
|
-
:
|
12
|
+
:warn_on_route_setup_issues,
|
13
|
+
:default_allow_include_to_one,
|
14
|
+
:default_allow_include_to_many,
|
13
15
|
:allow_sort,
|
14
16
|
:allow_filter,
|
15
17
|
:default_paginator,
|
16
18
|
:default_page_size,
|
17
19
|
:maximum_page_size,
|
20
|
+
:resource_finder,
|
18
21
|
:default_processor_klass,
|
19
22
|
:use_text_errors,
|
20
23
|
:top_level_links_include_pagination,
|
@@ -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,10 +35,10 @@ 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
|
-
:resource_cache_usage_report_function
|
37
|
-
:default_exclude_links
|
41
|
+
:resource_cache_usage_report_function
|
38
42
|
|
39
43
|
def initialize
|
40
44
|
#:underscored_key, :camelized_key, :dasherized_key, or custom
|
@@ -47,13 +51,14 @@ module JSONAPI
|
|
47
51
|
self.resource_key_type = :integer
|
48
52
|
|
49
53
|
# optional request features
|
50
|
-
self.
|
54
|
+
self.default_allow_include_to_one = true
|
55
|
+
self.default_allow_include_to_many = true
|
51
56
|
self.allow_sort = true
|
52
57
|
self.allow_filter = true
|
53
58
|
|
54
59
|
self.raise_if_parameters_not_allowed = true
|
55
60
|
|
56
|
-
self.
|
61
|
+
self.warn_on_route_setup_issues = true
|
57
62
|
|
58
63
|
# :none, :offset, :paged, or a custom paginator name
|
59
64
|
self.default_paginator = :none
|
@@ -75,8 +80,12 @@ module JSONAPI
|
|
75
80
|
self.use_text_errors = false
|
76
81
|
|
77
82
|
# Whether or not to include exception backtraces in JSONAPI error
|
78
|
-
# responses. Defaults to `false` in
|
79
|
-
self.include_backtraces_in_errors =
|
83
|
+
# responses. Defaults to `false` in anything other than development or test.
|
84
|
+
self.include_backtraces_in_errors = (Rails.env.development? || Rails.env.test?)
|
85
|
+
|
86
|
+
# Whether or not to include exception application backtraces in JSONAPI error
|
87
|
+
# responses. Defaults to `false` in anything other than development or test.
|
88
|
+
self.include_application_backtraces_in_errors = (Rails.env.development? || Rails.env.test?)
|
80
89
|
|
81
90
|
# List of classes that should not be rescued by the operations processor.
|
82
91
|
# For example, if you use Pundit for authorization, you might
|
@@ -96,6 +105,12 @@ module JSONAPI
|
|
96
105
|
self.always_include_to_one_linkage_data = false
|
97
106
|
self.always_include_to_many_linkage_data = false
|
98
107
|
|
108
|
+
# ResourceFinder Mixin
|
109
|
+
# The default ResourceFinder is the ActiveRelationResourceFinder which provides
|
110
|
+
# access to ActiveRelation backed models. Custom ResourceFinders can be specified
|
111
|
+
# in order to support other ORMs.
|
112
|
+
self.resource_finder = JSONAPI::ActiveRelationResourceFinder
|
113
|
+
|
99
114
|
# The default Operation Processor to use if one is not defined specifically
|
100
115
|
# for a Resource.
|
101
116
|
self.default_processor_klass = JSONAPI::Processor
|
@@ -121,6 +136,11 @@ module JSONAPI
|
|
121
136
|
# Rails cache store.
|
122
137
|
self.resource_cache = nil
|
123
138
|
|
139
|
+
# Cache resources by default
|
140
|
+
# Cache resources by default. Individual resources can be excluded from caching by calling:
|
141
|
+
# `caching false`
|
142
|
+
self.default_caching = false
|
143
|
+
|
124
144
|
# Default resource cache field
|
125
145
|
# On Resources with caching enabled, this field will be used to check for out-of-date
|
126
146
|
# cache entries, unless overridden on a specific Resource. Defaults to "updated_at".
|
@@ -135,12 +155,6 @@ module JSONAPI
|
|
135
155
|
# Optionally provide a callable which JSONAPI will call with information about cache
|
136
156
|
# performance. Should accept three arguments: resource name, hits count, misses count.
|
137
157
|
self.resource_cache_usage_report_function = nil
|
138
|
-
|
139
|
-
# Global configuration for links exclusion
|
140
|
-
# Controls whether to generate links like `self`, `related` with all the resources
|
141
|
-
# and relationships. Accepts either `:default`, `:none`, or array containing the
|
142
|
-
# specific default links to exclude, which may be `:self` and `:related`.
|
143
|
-
self.default_exclude_links = :none
|
144
158
|
end
|
145
159
|
|
146
160
|
def cache_formatters=(bool)
|
@@ -211,7 +225,17 @@ module JSONAPI
|
|
211
225
|
@default_processor_klass = default_processor_klass
|
212
226
|
end
|
213
227
|
|
214
|
-
|
228
|
+
def resource_finder=(resource_finder)
|
229
|
+
@resource_finder = resource_finder
|
230
|
+
end
|
231
|
+
|
232
|
+
def allow_include=(allow_include)
|
233
|
+
ActiveSupport::Deprecation.warn('`allow_include` has been replaced by `default_allow_include_to_one` and `default_allow_include_to_many` options.')
|
234
|
+
@default_allow_include_to_one = allow_include
|
235
|
+
@default_allow_include_to_many = allow_include
|
236
|
+
end
|
237
|
+
|
238
|
+
attr_writer :allow_sort, :allow_filter, :default_allow_include_to_one, :default_allow_include_to_many
|
215
239
|
|
216
240
|
attr_writer :default_paginator
|
217
241
|
|
@@ -235,6 +259,8 @@ module JSONAPI
|
|
235
259
|
|
236
260
|
attr_writer :include_backtraces_in_errors
|
237
261
|
|
262
|
+
attr_writer :include_application_backtraces_in_errors
|
263
|
+
|
238
264
|
attr_writer :exception_class_whitelist
|
239
265
|
|
240
266
|
attr_writer :whitelist_all_exceptions
|
@@ -245,19 +271,19 @@ module JSONAPI
|
|
245
271
|
|
246
272
|
attr_writer :raise_if_parameters_not_allowed
|
247
273
|
|
248
|
-
attr_writer :
|
274
|
+
attr_writer :warn_on_route_setup_issues
|
249
275
|
|
250
276
|
attr_writer :use_relationship_reflection
|
251
277
|
|
252
278
|
attr_writer :resource_cache
|
253
279
|
|
280
|
+
attr_writer :default_caching
|
281
|
+
|
254
282
|
attr_writer :default_resource_cache_field
|
255
283
|
|
256
284
|
attr_writer :resource_cache_digest_function
|
257
285
|
|
258
286
|
attr_writer :resource_cache_usage_report_function
|
259
|
-
|
260
|
-
attr_writer :default_exclude_links
|
261
287
|
end
|
262
288
|
|
263
289
|
class << self
|
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/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
|
|
@@ -339,7 +342,7 @@ module JSONAPI
|
|
339
342
|
title: I18n.translate('jsonapi-resources.exceptions.invalid_include.title',
|
340
343
|
default: 'Invalid field'),
|
341
344
|
detail: I18n.translate('jsonapi-resources.exceptions.invalid_include.detail',
|
342
|
-
default: "#{relationship} is not a valid relationship of #{resource}",
|
345
|
+
default: "#{relationship} is not a valid includable relationship of #{resource}",
|
343
346
|
relationship: relationship, resource: resource))]
|
344
347
|
end
|
345
348
|
end
|
@@ -374,11 +377,11 @@ module JSONAPI
|
|
374
377
|
|
375
378
|
def errors
|
376
379
|
[create_error_object(code: JSONAPI::PARAM_NOT_ALLOWED,
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
380
|
+
status: :bad_request,
|
381
|
+
title: I18n.translate('jsonapi-resources.exceptions.parameter_not_allowed.title',
|
382
|
+
default: 'Param not allowed'),
|
383
|
+
detail: I18n.translate('jsonapi-resources.exceptions.parameters_not_allowed.detail',
|
384
|
+
default: "#{param} is not allowed.", param: param))]
|
382
385
|
end
|
383
386
|
end
|
384
387
|
|