jsonapi-resources 0.9.12 → 0.10.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|