jsonapi-resources 0.9.0 → 0.10.6
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/active_relation/adapters/join_left_active_record_adapter.rb +27 -0
- data/lib/jsonapi/active_relation/join_manager.rb +303 -0
- data/lib/jsonapi/active_relation_resource.rb +884 -0
- data/lib/jsonapi/acts_as_resource_controller.rb +122 -105
- 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 +71 -8
- data/lib/jsonapi/error.rb +27 -0
- data/lib/jsonapi/error_codes.rb +2 -0
- data/lib/jsonapi/exceptions.rb +80 -50
- data/lib/jsonapi/formatter.rb +3 -3
- data/lib/jsonapi/include_directives.rb +18 -65
- data/lib/jsonapi/link_builder.rb +74 -80
- data/lib/jsonapi/operation.rb +16 -5
- data/lib/jsonapi/operation_result.rb +74 -16
- data/lib/jsonapi/path.rb +43 -0
- data/lib/jsonapi/path_segment.rb +76 -0
- data/lib/jsonapi/processor.rb +239 -111
- data/lib/jsonapi/relationship.rb +153 -15
- data/lib/jsonapi/request_parser.rb +430 -367
- data/lib/jsonapi/resource.rb +3 -1253
- 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 +143 -285
- data/lib/jsonapi/resource_set.rb +176 -0
- data/lib/jsonapi/resources/railtie.rb +9 -0
- data/lib/jsonapi/resources/version.rb +1 -1
- data/lib/jsonapi/response_document.rb +105 -83
- data/lib/jsonapi/routing_ext.rb +48 -26
- data/lib/jsonapi-resources.rb +20 -4
- data/lib/tasks/check_upgrade.rake +52 -0
- metadata +50 -20
- 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
@@ -9,9 +9,12 @@ module JSONAPI
|
|
9
9
|
base.extend ClassMethods
|
10
10
|
base.include Callbacks
|
11
11
|
base.cattr_reader :server_error_callbacks
|
12
|
-
base.define_jsonapi_resources_callbacks :process_operations
|
12
|
+
base.define_jsonapi_resources_callbacks :process_operations,
|
13
|
+
:transaction
|
13
14
|
end
|
14
15
|
|
16
|
+
attr_reader :response_document
|
17
|
+
|
15
18
|
def index
|
16
19
|
process_request
|
17
20
|
end
|
@@ -25,22 +28,18 @@ module JSONAPI
|
|
25
28
|
end
|
26
29
|
|
27
30
|
def create
|
28
|
-
return unless verify_content_type_header
|
29
31
|
process_request
|
30
32
|
end
|
31
33
|
|
32
34
|
def create_relationship
|
33
|
-
return unless verify_content_type_header
|
34
35
|
process_request
|
35
36
|
end
|
36
37
|
|
37
38
|
def update_relationship
|
38
|
-
return unless verify_content_type_header
|
39
39
|
process_request
|
40
40
|
end
|
41
41
|
|
42
42
|
def update
|
43
|
-
return unless verify_content_type_header
|
44
43
|
process_request
|
45
44
|
end
|
46
45
|
|
@@ -52,59 +51,94 @@ module JSONAPI
|
|
52
51
|
process_request
|
53
52
|
end
|
54
53
|
|
55
|
-
def
|
54
|
+
def show_related_resource
|
56
55
|
process_request
|
57
56
|
end
|
58
57
|
|
59
|
-
def
|
58
|
+
def index_related_resources
|
60
59
|
process_request
|
61
60
|
end
|
62
61
|
|
63
|
-
def
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
62
|
+
def get_related_resource
|
63
|
+
# :nocov:
|
64
|
+
ActiveSupport::Deprecation.warn "In #{self.class.name} you exposed a `get_related_resource`"\
|
65
|
+
" action. Please use `show_related_resource` instead."
|
66
|
+
show_related_resource
|
67
|
+
# :nocov:
|
68
|
+
end
|
69
69
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
end
|
77
|
-
results = process_operations(operations)
|
78
|
-
render_results(results)
|
79
|
-
end
|
80
|
-
rescue => e
|
81
|
-
handle_exceptions(e)
|
70
|
+
def get_related_resources
|
71
|
+
# :nocov:
|
72
|
+
ActiveSupport::Deprecation.warn "In #{self.class.name} you exposed a `get_related_resources`"\
|
73
|
+
" action. Please use `index_related_resources` instead."
|
74
|
+
index_related_resources
|
75
|
+
# :nocov:
|
82
76
|
end
|
83
77
|
|
84
|
-
def
|
85
|
-
|
86
|
-
|
78
|
+
def process_request
|
79
|
+
@response_document = create_response_document
|
80
|
+
|
81
|
+
unless verify_content_type_header && verify_accept_header
|
82
|
+
render_response_document
|
83
|
+
return
|
87
84
|
end
|
88
|
-
end
|
89
85
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
86
|
+
request_parser = JSONAPI::RequestParser.new(
|
87
|
+
params,
|
88
|
+
context: context,
|
89
|
+
key_formatter: key_formatter,
|
90
|
+
server_error_callbacks: (self.class.server_error_callbacks || []))
|
91
|
+
|
92
|
+
transactional = request_parser.transactional?
|
93
|
+
|
94
|
+
begin
|
95
|
+
process_operations(transactional) do
|
96
|
+
run_callbacks :process_operations do
|
97
|
+
request_parser.each(response_document) do |op|
|
98
|
+
op.options[:serializer] = resource_serializer_klass.new(
|
99
|
+
op.resource_klass,
|
100
|
+
include_directives: op.options[:include_directives],
|
101
|
+
fields: op.options[:fields],
|
102
|
+
base_url: base_url,
|
103
|
+
key_formatter: key_formatter,
|
104
|
+
route_formatter: route_formatter,
|
105
|
+
serialization_options: serialization_options,
|
106
|
+
controller: self
|
107
|
+
)
|
108
|
+
op.options[:cache_serializer_output] = !JSONAPI.configuration.resource_cache.nil?
|
109
|
+
|
110
|
+
process_operation(op)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
if response_document.has_errors?
|
114
|
+
raise ActiveRecord::Rollback
|
115
|
+
end
|
94
116
|
end
|
95
|
-
|
117
|
+
rescue => e
|
118
|
+
handle_exceptions(e)
|
119
|
+
end
|
120
|
+
render_response_document
|
96
121
|
end
|
97
122
|
|
98
|
-
def
|
99
|
-
|
100
|
-
|
101
|
-
|
123
|
+
def process_operations(transactional)
|
124
|
+
if transactional
|
125
|
+
run_callbacks :transaction do
|
126
|
+
ActiveRecord::Base.transaction do
|
127
|
+
yield
|
128
|
+
end
|
129
|
+
end
|
130
|
+
else
|
131
|
+
begin
|
132
|
+
yield
|
133
|
+
rescue ActiveRecord::Rollback
|
134
|
+
# Can't rollback without transaction, so just ignore it
|
135
|
+
end
|
136
|
+
end
|
102
137
|
end
|
103
138
|
|
104
|
-
def
|
105
|
-
|
106
|
-
|
107
|
-
server_error_callbacks: @request.server_error_callbacks)
|
139
|
+
def process_operation(operation)
|
140
|
+
result = operation.process
|
141
|
+
response_document.add_result(result, operation)
|
108
142
|
end
|
109
143
|
|
110
144
|
private
|
@@ -117,21 +151,8 @@ module JSONAPI
|
|
117
151
|
@resource_serializer_klass ||= JSONAPI::ResourceSerializer
|
118
152
|
end
|
119
153
|
|
120
|
-
def resource_serializer
|
121
|
-
@resource_serializer ||= resource_serializer_klass.new(
|
122
|
-
resource_klass,
|
123
|
-
include_directives: @request ? @request.include_directives : nil,
|
124
|
-
fields: @request ? @request.fields : {},
|
125
|
-
base_url: base_url,
|
126
|
-
key_formatter: key_formatter,
|
127
|
-
route_formatter: route_formatter,
|
128
|
-
serialization_options: serialization_options
|
129
|
-
)
|
130
|
-
@resource_serializer
|
131
|
-
end
|
132
|
-
|
133
154
|
def base_url
|
134
|
-
@base_url ||= request.protocol
|
155
|
+
@base_url ||= "#{request.protocol}#{request.host_with_port}#{Rails.application.config.relative_url_root}"
|
135
156
|
end
|
136
157
|
|
137
158
|
def resource_klass_name
|
@@ -139,8 +160,10 @@ module JSONAPI
|
|
139
160
|
end
|
140
161
|
|
141
162
|
def verify_content_type_header
|
142
|
-
|
143
|
-
|
163
|
+
if ['create', 'create_relationship', 'update_relationship', 'update'].include?(params[:action])
|
164
|
+
unless request.content_type == JSONAPI::MEDIA_TYPE
|
165
|
+
fail JSONAPI::Exceptions::UnsupportedMediaTypeError.new(request.content_type)
|
166
|
+
end
|
144
167
|
end
|
145
168
|
true
|
146
169
|
rescue => e
|
@@ -161,10 +184,9 @@ module JSONAPI
|
|
161
184
|
def valid_accept_media_type?
|
162
185
|
media_types = media_types_for('Accept')
|
163
186
|
|
164
|
-
media_types.blank? ||
|
165
|
-
|
166
|
-
|
167
|
-
end
|
187
|
+
media_types.blank? || media_types.any? do |media_type|
|
188
|
+
(media_type == JSONAPI::MEDIA_TYPE || media_type.start_with?(ALL_MEDIA_TYPES))
|
189
|
+
end
|
168
190
|
end
|
169
191
|
|
170
192
|
def media_types_for(header)
|
@@ -202,57 +224,43 @@ module JSONAPI
|
|
202
224
|
end
|
203
225
|
|
204
226
|
def base_meta
|
205
|
-
|
206
|
-
base_response_meta
|
207
|
-
else
|
208
|
-
base_response_meta.merge(warnings: @request.warnings)
|
209
|
-
end
|
227
|
+
base_response_meta
|
210
228
|
end
|
211
229
|
|
212
230
|
def base_response_links
|
213
231
|
{}
|
214
232
|
end
|
215
233
|
|
216
|
-
def
|
217
|
-
|
218
|
-
result = JSONAPI::ErrorsOperationResult.new(errors[0].status, errors)
|
219
|
-
operation_results.add_result(result)
|
220
|
-
|
221
|
-
render_results(operation_results)
|
222
|
-
end
|
223
|
-
|
224
|
-
def render_results(operation_results)
|
225
|
-
response_doc = create_response_document(operation_results)
|
226
|
-
content = response_doc.contents
|
234
|
+
def render_response_document
|
235
|
+
content = response_document.contents
|
227
236
|
|
228
237
|
render_options = {}
|
229
|
-
if
|
238
|
+
if response_document.has_errors?
|
230
239
|
render_options[:json] = content
|
231
240
|
else
|
232
|
-
#
|
241
|
+
# Bypassing ActiveSupport allows us to use CompiledJson objects for cached response fragments
|
233
242
|
render_options[:body] = JSON.generate(content)
|
234
|
-
end
|
235
243
|
|
236
|
-
|
237
|
-
|
238
|
-
|
244
|
+
if (response_document.status == 201 && content[:data].class != Array) &&
|
245
|
+
content['data'] && content['data']['links'] && content['data']['links']['self']
|
246
|
+
render_options[:location] = content['data']['links']['self']
|
247
|
+
end
|
248
|
+
end
|
239
249
|
|
240
250
|
# For whatever reason, `render` ignores :status and :content_type when :body is set.
|
241
251
|
# But, we can just set those values directly in the Response object instead.
|
242
|
-
response.status =
|
252
|
+
response.status = response_document.status
|
243
253
|
response.headers['Content-Type'] = JSONAPI::MEDIA_TYPE
|
244
254
|
|
245
255
|
render(render_options)
|
246
256
|
end
|
247
257
|
|
248
|
-
def create_response_document
|
258
|
+
def create_response_document
|
249
259
|
JSONAPI::ResponseDocument.new(
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
base_links: base_response_links,
|
255
|
-
request: @request
|
260
|
+
key_formatter: key_formatter,
|
261
|
+
base_meta: base_meta,
|
262
|
+
base_links: base_response_links,
|
263
|
+
request: request
|
256
264
|
)
|
257
265
|
end
|
258
266
|
|
@@ -260,21 +268,30 @@ module JSONAPI
|
|
260
268
|
# Note: Be sure to either call super(e) or handle JSONAPI::Exceptions::Error and raise unhandled exceptions
|
261
269
|
def handle_exceptions(e)
|
262
270
|
case e
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
fail e
|
271
|
+
when JSONAPI::Exceptions::Error
|
272
|
+
errors = e.errors
|
273
|
+
when ActionController::ParameterMissing
|
274
|
+
errors = JSONAPI::Exceptions::ParameterMissing.new(e.param).errors
|
268
275
|
else
|
269
|
-
|
270
|
-
|
271
|
-
|
276
|
+
if JSONAPI.configuration.exception_class_whitelisted?(e)
|
277
|
+
raise e
|
278
|
+
else
|
279
|
+
if self.class.server_error_callbacks
|
280
|
+
self.class.server_error_callbacks.each { |callback|
|
281
|
+
safe_run_callback(callback, e)
|
282
|
+
}
|
283
|
+
end
|
272
284
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
285
|
+
# Store exception for other middlewares
|
286
|
+
request.env['action_dispatch.exception'] ||= e
|
287
|
+
|
288
|
+
internal_server_error = JSONAPI::Exceptions::InternalServerError.new(e)
|
289
|
+
Rails.logger.error { "Internal Server Error: #{e.message} #{e.backtrace.join("\n")}" }
|
290
|
+
errors = internal_server_error.errors
|
291
|
+
end
|
277
292
|
end
|
293
|
+
|
294
|
+
response_document.add_result(JSONAPI::ErrorsOperationResult.new(errors[0].status, errors), nil)
|
278
295
|
end
|
279
296
|
|
280
297
|
def safe_run_callback(callback, error)
|
@@ -283,7 +300,7 @@ module JSONAPI
|
|
283
300
|
rescue => e
|
284
301
|
Rails.logger.error { "Error in error handling callback: #{e.message} #{e.backtrace.join("\n")}" }
|
285
302
|
internal_server_error = JSONAPI::Exceptions::InternalServerError.new(e)
|
286
|
-
|
303
|
+
return JSONAPI::ErrorsOperationResult.new(internal_server_error.errors[0].code, internal_server_error.errors)
|
287
304
|
end
|
288
305
|
end
|
289
306
|
|