jsonapi-resources 0.9.12 → 0.10.7
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 +303 -0
- data/lib/jsonapi/active_relation_resource.rb +884 -0
- data/lib/jsonapi/acts_as_resource_controller.rb +122 -106
- 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 +57 -8
- 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/paginator.rb +17 -0
- data/lib/jsonapi/path.rb +43 -0
- data/lib/jsonapi/path_segment.rb +76 -0
- data/lib/jsonapi/processor.rb +246 -111
- data/lib/jsonapi/relationship.rb +117 -24
- data/lib/jsonapi/request_parser.rb +383 -396
- data/lib/jsonapi/resource.rb +3 -1376
- data/lib/jsonapi/resource_controller_metal.rb +3 -0
- 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 +176 -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 +34 -31
- 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,22 +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
|
-
controller: self
|
130
|
-
)
|
131
|
-
@resource_serializer
|
132
|
-
end
|
133
|
-
|
134
154
|
def base_url
|
135
|
-
@base_url ||= request.protocol
|
155
|
+
@base_url ||= "#{request.protocol}#{request.host_with_port}#{Rails.application.config.relative_url_root}"
|
136
156
|
end
|
137
157
|
|
138
158
|
def resource_klass_name
|
@@ -140,8 +160,10 @@ module JSONAPI
|
|
140
160
|
end
|
141
161
|
|
142
162
|
def verify_content_type_header
|
143
|
-
|
144
|
-
|
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
|
145
167
|
end
|
146
168
|
true
|
147
169
|
rescue => e
|
@@ -162,10 +184,9 @@ module JSONAPI
|
|
162
184
|
def valid_accept_media_type?
|
163
185
|
media_types = media_types_for('Accept')
|
164
186
|
|
165
|
-
media_types.blank? ||
|
166
|
-
|
167
|
-
|
168
|
-
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
|
169
190
|
end
|
170
191
|
|
171
192
|
def media_types_for(header)
|
@@ -203,57 +224,43 @@ module JSONAPI
|
|
203
224
|
end
|
204
225
|
|
205
226
|
def base_meta
|
206
|
-
|
207
|
-
base_response_meta
|
208
|
-
else
|
209
|
-
base_response_meta.merge(warnings: @request.warnings)
|
210
|
-
end
|
227
|
+
base_response_meta
|
211
228
|
end
|
212
229
|
|
213
230
|
def base_response_links
|
214
231
|
{}
|
215
232
|
end
|
216
233
|
|
217
|
-
def
|
218
|
-
|
219
|
-
result = JSONAPI::ErrorsOperationResult.new(errors[0].status, errors)
|
220
|
-
operation_results.add_result(result)
|
221
|
-
|
222
|
-
render_results(operation_results)
|
223
|
-
end
|
224
|
-
|
225
|
-
def render_results(operation_results)
|
226
|
-
response_doc = create_response_document(operation_results)
|
227
|
-
content = response_doc.contents
|
234
|
+
def render_response_document
|
235
|
+
content = response_document.contents
|
228
236
|
|
229
237
|
render_options = {}
|
230
|
-
if
|
238
|
+
if response_document.has_errors?
|
231
239
|
render_options[:json] = content
|
232
240
|
else
|
233
|
-
#
|
241
|
+
# Bypassing ActiveSupport allows us to use CompiledJson objects for cached response fragments
|
234
242
|
render_options[:body] = JSON.generate(content)
|
235
|
-
end
|
236
243
|
|
237
|
-
|
238
|
-
|
239
|
-
|
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
|
240
249
|
|
241
250
|
# For whatever reason, `render` ignores :status and :content_type when :body is set.
|
242
251
|
# But, we can just set those values directly in the Response object instead.
|
243
|
-
response.status =
|
252
|
+
response.status = response_document.status
|
244
253
|
response.headers['Content-Type'] = JSONAPI::MEDIA_TYPE
|
245
254
|
|
246
255
|
render(render_options)
|
247
256
|
end
|
248
257
|
|
249
|
-
def create_response_document
|
258
|
+
def create_response_document
|
250
259
|
JSONAPI::ResponseDocument.new(
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
base_links: base_response_links,
|
256
|
-
request: @request
|
260
|
+
key_formatter: key_formatter,
|
261
|
+
base_meta: base_meta,
|
262
|
+
base_links: base_response_links,
|
263
|
+
request: request
|
257
264
|
)
|
258
265
|
end
|
259
266
|
|
@@ -261,21 +268,30 @@ module JSONAPI
|
|
261
268
|
# Note: Be sure to either call super(e) or handle JSONAPI::Exceptions::Error and raise unhandled exceptions
|
262
269
|
def handle_exceptions(e)
|
263
270
|
case e
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
fail e
|
271
|
+
when JSONAPI::Exceptions::Error
|
272
|
+
errors = e.errors
|
273
|
+
when ActionController::ParameterMissing
|
274
|
+
errors = JSONAPI::Exceptions::ParameterMissing.new(e.param).errors
|
269
275
|
else
|
270
|
-
|
271
|
-
|
272
|
-
|
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
|
273
284
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
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
|
278
292
|
end
|
293
|
+
|
294
|
+
response_document.add_result(JSONAPI::ErrorsOperationResult.new(errors[0].status, errors), nil)
|
279
295
|
end
|
280
296
|
|
281
297
|
def safe_run_callback(callback, error)
|
@@ -284,7 +300,7 @@ module JSONAPI
|
|
284
300
|
rescue => e
|
285
301
|
Rails.logger.error { "Error in error handling callback: #{e.message} #{e.backtrace.join("\n")}" }
|
286
302
|
internal_server_error = JSONAPI::Exceptions::InternalServerError.new(e)
|
287
|
-
|
303
|
+
return JSONAPI::ErrorsOperationResult.new(internal_server_error.errors[0].code, internal_server_error.errors)
|
288
304
|
end
|
289
305
|
end
|
290
306
|
|