jsonapi-resources 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +274 -102
- data/jsonapi-resources.gemspec +1 -0
- data/lib/jsonapi-resources.rb +15 -0
- data/lib/jsonapi/active_record_operations_processor.rb +21 -10
- data/lib/jsonapi/acts_as_resource_controller.rb +175 -0
- data/lib/jsonapi/configuration.rb +11 -0
- data/lib/jsonapi/error_codes.rb +7 -4
- data/lib/jsonapi/exceptions.rb +23 -15
- data/lib/jsonapi/formatter.rb +5 -5
- data/lib/jsonapi/include_directives.rb +67 -0
- data/lib/jsonapi/operation.rb +185 -65
- data/lib/jsonapi/operation_result.rb +38 -5
- data/lib/jsonapi/operation_results.rb +33 -0
- data/lib/jsonapi/operations_processor.rb +49 -9
- data/lib/jsonapi/paginator.rb +31 -17
- data/lib/jsonapi/request.rb +347 -163
- data/lib/jsonapi/resource.rb +159 -56
- data/lib/jsonapi/resource_controller.rb +1 -234
- data/lib/jsonapi/resource_serializer.rb +55 -69
- data/lib/jsonapi/resources/version.rb +1 -1
- data/lib/jsonapi/response_document.rb +87 -0
- data/lib/jsonapi/routing_ext.rb +17 -11
- data/test/controllers/controller_test.rb +602 -326
- data/test/fixtures/active_record.rb +96 -6
- data/test/fixtures/line_items.yml +7 -1
- data/test/fixtures/numeros_telefone.yml +3 -0
- data/test/fixtures/purchase_orders.yml +6 -0
- data/test/integration/requests/request_test.rb +129 -60
- data/test/integration/routes/routes_test.rb +17 -17
- data/test/test_helper.rb +23 -5
- data/test/unit/jsonapi_request/jsonapi_request_test.rb +48 -0
- data/test/unit/operation/operations_processor_test.rb +242 -54
- data/test/unit/resource/resource_test.rb +108 -2
- data/test/unit/serializer/include_directives_test.rb +108 -0
- data/test/unit/serializer/response_document_test.rb +61 -0
- data/test/unit/serializer/serializer_test.rb +679 -520
- metadata +26 -2
data/lib/jsonapi/resource.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'jsonapi/configuration'
|
2
|
-
require 'jsonapi/association'
|
3
1
|
require 'jsonapi/callbacks'
|
4
2
|
|
5
3
|
module JSONAPI
|
@@ -37,19 +35,26 @@ module JSONAPI
|
|
37
35
|
end
|
38
36
|
|
39
37
|
def change(callback)
|
38
|
+
completed = false
|
39
|
+
|
40
40
|
if @changing
|
41
41
|
run_callbacks callback do
|
42
|
-
yield
|
42
|
+
completed = (yield == :completed)
|
43
43
|
end
|
44
44
|
else
|
45
45
|
run_callbacks is_new? ? :create : :update do
|
46
46
|
@changing = true
|
47
47
|
run_callbacks callback do
|
48
|
-
yield
|
49
|
-
|
48
|
+
completed = (yield == :completed)
|
49
|
+
end
|
50
|
+
|
51
|
+
if @save_needed || is_new?
|
52
|
+
completed = (save == :completed)
|
50
53
|
end
|
51
54
|
end
|
52
55
|
end
|
56
|
+
|
57
|
+
return completed ? :completed : :accepted
|
53
58
|
end
|
54
59
|
|
55
60
|
def remove
|
@@ -100,7 +105,7 @@ module JSONAPI
|
|
100
105
|
end
|
101
106
|
|
102
107
|
# Override this on a resource to customize how the associated records
|
103
|
-
# are fetched for a model. Particularly helpful for
|
108
|
+
# are fetched for a model. Particularly helpful for authorization.
|
104
109
|
def records_for(association_name, options = {})
|
105
110
|
model.send association_name
|
106
111
|
end
|
@@ -112,15 +117,42 @@ module JSONAPI
|
|
112
117
|
end
|
113
118
|
end
|
114
119
|
|
120
|
+
# Override this on a resource to return a different result code. Any
|
121
|
+
# value other than :completed will result in operations returning
|
122
|
+
# `:accepted`
|
123
|
+
#
|
124
|
+
# For example to return `:accepted` if your model does not immediately
|
125
|
+
# save resources to the database you could override `_save` as follows:
|
126
|
+
#
|
127
|
+
# ```
|
128
|
+
# def _save
|
129
|
+
# super
|
130
|
+
# return :accepted
|
131
|
+
# end
|
132
|
+
# ```
|
115
133
|
def _save
|
116
|
-
@model.
|
117
|
-
|
118
|
-
|
119
|
-
|
134
|
+
unless @model.valid?
|
135
|
+
raise JSONAPI::Exceptions::ValidationErrors.new(@model.errors.messages)
|
136
|
+
end
|
137
|
+
|
138
|
+
if defined? @model.save
|
139
|
+
saved = @model.save
|
140
|
+
unless saved
|
141
|
+
raise JSONAPI::Exceptions::SaveFailed.new
|
142
|
+
end
|
143
|
+
else
|
144
|
+
saved = true
|
145
|
+
end
|
146
|
+
|
147
|
+
@save_needed = !saved
|
148
|
+
|
149
|
+
return :completed
|
120
150
|
end
|
121
151
|
|
122
152
|
def _remove
|
123
153
|
@model.destroy
|
154
|
+
|
155
|
+
return :completed
|
124
156
|
end
|
125
157
|
|
126
158
|
def _create_has_many_links(association_type, association_key_values)
|
@@ -137,6 +169,8 @@ module JSONAPI
|
|
137
169
|
raise JSONAPI::Exceptions::HasManyRelationExists.new(association_key_value)
|
138
170
|
end
|
139
171
|
end
|
172
|
+
|
173
|
+
return :completed
|
140
174
|
end
|
141
175
|
|
142
176
|
def _replace_has_many_links(association_type, association_key_values)
|
@@ -144,6 +178,8 @@ module JSONAPI
|
|
144
178
|
|
145
179
|
send("#{association.foreign_key}=", association_key_values)
|
146
180
|
@save_needed = true
|
181
|
+
|
182
|
+
return :completed
|
147
183
|
end
|
148
184
|
|
149
185
|
def _replace_has_one_link(association_type, association_key_value)
|
@@ -151,12 +187,16 @@ module JSONAPI
|
|
151
187
|
|
152
188
|
send("#{association.foreign_key}=", association_key_value)
|
153
189
|
@save_needed = true
|
190
|
+
|
191
|
+
return :completed
|
154
192
|
end
|
155
193
|
|
156
194
|
def _remove_has_many_link(association_type, key)
|
157
195
|
association = self.class._associations[association_type]
|
158
196
|
|
159
197
|
@model.send(association.type).delete(key)
|
198
|
+
|
199
|
+
return :completed
|
160
200
|
end
|
161
201
|
|
162
202
|
def _remove_has_one_link(association_type)
|
@@ -164,6 +204,8 @@ module JSONAPI
|
|
164
204
|
|
165
205
|
send("#{association.foreign_key}=", nil)
|
166
206
|
@save_needed = true
|
207
|
+
|
208
|
+
return :completed
|
167
209
|
end
|
168
210
|
|
169
211
|
def _replace_fields(field_data)
|
@@ -189,6 +231,8 @@ module JSONAPI
|
|
189
231
|
field_data[:has_many].each do |association_type, values|
|
190
232
|
replace_has_many_links(association_type, values)
|
191
233
|
end if field_data[:has_many]
|
234
|
+
|
235
|
+
return :completed
|
192
236
|
end
|
193
237
|
|
194
238
|
class << self
|
@@ -242,6 +286,10 @@ module JSONAPI
|
|
242
286
|
def attribute(attr, options = {})
|
243
287
|
check_reserved_attribute_name(attr)
|
244
288
|
|
289
|
+
if (attr.to_sym == :id) && (options[:format].nil?)
|
290
|
+
ActiveSupport::Deprecation.warn('Id without format is no longer supported. Please remove ids from attributes, or specify a format.')
|
291
|
+
end
|
292
|
+
|
245
293
|
@_attributes ||= {}
|
246
294
|
@_attributes[attr] = options
|
247
295
|
define_method attr do
|
@@ -270,25 +318,40 @@ module JSONAPI
|
|
270
318
|
end
|
271
319
|
|
272
320
|
def filters(*attrs)
|
273
|
-
@_allowed_filters.merge(attrs)
|
321
|
+
@_allowed_filters.merge!(attrs.inject( Hash.new ) { |h, attr| h[attr] = {}; h })
|
274
322
|
end
|
275
323
|
|
276
|
-
def filter(attr)
|
277
|
-
@_allowed_filters
|
324
|
+
def filter(attr, *args)
|
325
|
+
@_allowed_filters[attr.to_sym] = args.extract_options!
|
278
326
|
end
|
279
327
|
|
280
328
|
def primary_key(key)
|
281
329
|
@_primary_key = key.to_sym
|
282
330
|
end
|
283
331
|
|
284
|
-
#
|
285
|
-
|
286
|
-
|
332
|
+
# TODO: remove this after the createable_fields and updateable_fields are phased out
|
333
|
+
# :nocov:
|
334
|
+
def method_missing(method, *args)
|
335
|
+
if method.to_s.match /createable_fields/
|
336
|
+
ActiveSupport::Deprecation.warn("`createable_fields` is deprecated, please use `creatable_fields` instead")
|
337
|
+
self.creatable_fields(*args)
|
338
|
+
elsif method.to_s.match /updateable_fields/
|
339
|
+
ActiveSupport::Deprecation.warn("`updateable_fields` is deprecated, please use `updatable_fields` instead")
|
340
|
+
self.updatable_fields(*args)
|
341
|
+
else
|
342
|
+
super
|
343
|
+
end
|
344
|
+
end
|
345
|
+
# :nocov:
|
346
|
+
|
347
|
+
# Override in your resource to filter the updatable keys
|
348
|
+
def updatable_fields(context = nil)
|
349
|
+
_updatable_associations | _attributes.keys - [:id]
|
287
350
|
end
|
288
351
|
|
289
|
-
# Override in your resource to filter the
|
290
|
-
def
|
291
|
-
|
352
|
+
# Override in your resource to filter the creatable keys
|
353
|
+
def creatable_fields(context = nil)
|
354
|
+
_updatable_associations | _attributes.keys
|
292
355
|
end
|
293
356
|
|
294
357
|
# Override in your resource to filter the sortable keys
|
@@ -300,50 +363,86 @@ module JSONAPI
|
|
300
363
|
_associations.keys | _attributes.keys
|
301
364
|
end
|
302
365
|
|
303
|
-
def
|
366
|
+
def apply_includes(records, directives)
|
367
|
+
records = records.includes(*directives.model_includes) if directives
|
368
|
+
records
|
369
|
+
end
|
370
|
+
|
371
|
+
def apply_pagination(records, paginator, order_options)
|
304
372
|
if paginator
|
305
|
-
records = paginator.apply(records)
|
373
|
+
records = paginator.apply(records, order_options)
|
306
374
|
end
|
307
375
|
records
|
308
376
|
end
|
309
377
|
|
310
378
|
def apply_sort(records, order_options)
|
311
|
-
|
379
|
+
if order_options.any?
|
380
|
+
records.order(order_options)
|
381
|
+
else
|
382
|
+
records
|
383
|
+
end
|
312
384
|
end
|
313
385
|
|
314
|
-
def apply_filter(records, filter, value)
|
386
|
+
def apply_filter(records, filter, value, options = {})
|
315
387
|
records.where(filter => value)
|
316
388
|
end
|
317
389
|
|
318
|
-
def apply_filters(records, filters)
|
390
|
+
def apply_filters(records, filters, options = {})
|
319
391
|
required_includes = []
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
392
|
+
|
393
|
+
if filters
|
394
|
+
filters.each do |filter, value|
|
395
|
+
if _associations.include?(filter)
|
396
|
+
if _associations[filter].is_a?(JSONAPI::Association::HasMany)
|
397
|
+
required_includes.push(filter)
|
398
|
+
records = apply_filter(records, "#{filter}.#{_associations[filter].primary_key}", value, options)
|
399
|
+
else
|
400
|
+
records = apply_filter(records, "#{_associations[filter].foreign_key}", value, options)
|
401
|
+
end
|
325
402
|
else
|
326
|
-
records = apply_filter(records,
|
403
|
+
records = apply_filter(records, filter, value, options)
|
327
404
|
end
|
328
|
-
else
|
329
|
-
records = apply_filter(records, filter, value)
|
330
405
|
end
|
331
406
|
end
|
332
|
-
|
407
|
+
|
408
|
+
if required_includes.any?
|
409
|
+
records.includes(required_includes)
|
410
|
+
elsif records.respond_to? :to_ary
|
411
|
+
records
|
412
|
+
else
|
413
|
+
records.all
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
def filter_records(filters, options)
|
418
|
+
include_directives = options[:include_directives]
|
419
|
+
|
420
|
+
records = records(options)
|
421
|
+
records = apply_includes(records, include_directives)
|
422
|
+
apply_filters(records, filters, options)
|
423
|
+
end
|
424
|
+
|
425
|
+
def sort_records(records, order_options)
|
426
|
+
apply_sort(records, order_options)
|
427
|
+
end
|
428
|
+
|
429
|
+
def find_count(filters, options = {})
|
430
|
+
filter_records(filters, options).count
|
333
431
|
end
|
334
432
|
|
335
433
|
# Override this method if you have more complex requirements than this basic find method provides
|
336
434
|
def find(filters, options = {})
|
337
435
|
context = options[:context]
|
338
|
-
sort_criteria = options.fetch(:sort_criteria) { [] }
|
339
436
|
|
340
|
-
|
437
|
+
records = filter_records(filters, options)
|
341
438
|
|
342
|
-
|
343
|
-
|
344
|
-
records =
|
345
|
-
|
439
|
+
sort_criteria = options.fetch(:sort_criteria) { [] }
|
440
|
+
order_options = construct_order_options(sort_criteria)
|
441
|
+
records = sort_records(records, order_options)
|
442
|
+
|
443
|
+
records = apply_pagination(records, options[:paginator], order_options)
|
346
444
|
|
445
|
+
resources = []
|
347
446
|
records.each do |model|
|
348
447
|
resources.push self.new(model, context)
|
349
448
|
end
|
@@ -353,7 +452,10 @@ module JSONAPI
|
|
353
452
|
|
354
453
|
def find_by_key(key, options = {})
|
355
454
|
context = options[:context]
|
356
|
-
|
455
|
+
include_directives = options[:include_directives]
|
456
|
+
records = records(options)
|
457
|
+
records = apply_includes(records, include_directives)
|
458
|
+
model = records.where({_primary_key => key}).first
|
357
459
|
if model.nil?
|
358
460
|
raise JSONAPI::Exceptions::RecordNotFound.new(key)
|
359
461
|
end
|
@@ -394,7 +496,7 @@ module JSONAPI
|
|
394
496
|
def verify_key(key, context = nil)
|
395
497
|
key && Integer(key)
|
396
498
|
rescue
|
397
|
-
raise JSONAPI::Exceptions::InvalidFieldValue.new(
|
499
|
+
raise JSONAPI::Exceptions::InvalidFieldValue.new(:id, key)
|
398
500
|
end
|
399
501
|
|
400
502
|
# override to allow for key processing and checking
|
@@ -419,13 +521,8 @@ module JSONAPI
|
|
419
521
|
default_attribute_options.merge(@_attributes[attr])
|
420
522
|
end
|
421
523
|
|
422
|
-
def
|
423
|
-
|
424
|
-
|
425
|
-
@_associations.each do |key, association|
|
426
|
-
associations.push(key)
|
427
|
-
end
|
428
|
-
associations
|
524
|
+
def _updatable_associations
|
525
|
+
@_associations.map { |key, association| key }
|
429
526
|
end
|
430
527
|
|
431
528
|
def _has_association?(type)
|
@@ -451,13 +548,13 @@ module JSONAPI
|
|
451
548
|
end
|
452
549
|
|
453
550
|
def _allowed_filters
|
454
|
-
!@_allowed_filters.nil? ? @_allowed_filters :
|
551
|
+
!@_allowed_filters.nil? ? @_allowed_filters : { :id => {} }
|
455
552
|
end
|
456
553
|
|
457
554
|
def _resource_name_from_type(type)
|
458
555
|
class_name = @@resource_types[type]
|
459
556
|
if class_name.nil?
|
460
|
-
class_name = type.to_s.singularize.camelize
|
557
|
+
class_name = "#{type.to_s.singularize}_resource".camelize
|
461
558
|
@@resource_types[type] = class_name
|
462
559
|
end
|
463
560
|
return class_name
|
@@ -476,7 +573,7 @@ module JSONAPI
|
|
476
573
|
end
|
477
574
|
|
478
575
|
def _allowed_filter?(filter)
|
479
|
-
_allowed_filters.
|
576
|
+
!_allowed_filters[filter].nil?
|
480
577
|
end
|
481
578
|
|
482
579
|
def module_path
|
@@ -484,8 +581,11 @@ module JSONAPI
|
|
484
581
|
end
|
485
582
|
|
486
583
|
def construct_order_options(sort_params)
|
584
|
+
return {} unless sort_params
|
585
|
+
|
487
586
|
sort_params.each_with_object({}) { |sort, order_hash|
|
488
|
-
|
587
|
+
field = sort[:field] == 'id' ? _primary_key : sort[:field]
|
588
|
+
order_hash[field] = sort[:direction]
|
489
589
|
}
|
490
590
|
end
|
491
591
|
|
@@ -554,14 +654,16 @@ module JSONAPI
|
|
554
654
|
resource_class = Resource.resource_for(self.class.module_path + type_name)
|
555
655
|
filters = options.fetch(:filters, {})
|
556
656
|
sort_criteria = options.fetch(:sort_criteria, {})
|
557
|
-
paginator = options
|
657
|
+
paginator = options[:paginator]
|
558
658
|
|
559
659
|
resources = []
|
660
|
+
|
560
661
|
if resource_class
|
561
662
|
records = public_send(associated_records_method_name)
|
562
|
-
records =
|
563
|
-
|
564
|
-
records =
|
663
|
+
records = resource_class.apply_filters(records, filters, options)
|
664
|
+
order_options = self.class.construct_order_options(sort_criteria)
|
665
|
+
records = resource_class.apply_sort(records, order_options)
|
666
|
+
records = resource_class.apply_pagination(records, paginator, order_options)
|
565
667
|
records.each do |record|
|
566
668
|
resources.push resource_class.new(record, @context)
|
567
669
|
end
|
@@ -572,5 +674,6 @@ module JSONAPI
|
|
572
674
|
end
|
573
675
|
end
|
574
676
|
end
|
677
|
+
|
575
678
|
end
|
576
679
|
end
|
@@ -1,238 +1,5 @@
|
|
1
|
-
require 'jsonapi/resource_serializer'
|
2
|
-
require 'action_controller'
|
3
|
-
require 'jsonapi/exceptions'
|
4
|
-
require 'jsonapi/error'
|
5
|
-
require 'jsonapi/error_codes'
|
6
|
-
require 'jsonapi/request'
|
7
|
-
require 'jsonapi/operations_processor'
|
8
|
-
require 'jsonapi/active_record_operations_processor'
|
9
|
-
require 'csv'
|
10
|
-
|
11
1
|
module JSONAPI
|
12
2
|
class ResourceController < ActionController::Base
|
13
|
-
|
14
|
-
before_filter :setup_request
|
15
|
-
after_filter :setup_response
|
16
|
-
|
17
|
-
def index
|
18
|
-
serializer = JSONAPI::ResourceSerializer.new(resource_klass,
|
19
|
-
include: @request.include,
|
20
|
-
fields: @request.fields,
|
21
|
-
base_url: base_url,
|
22
|
-
key_formatter: key_formatter,
|
23
|
-
route_formatter: route_formatter)
|
24
|
-
|
25
|
-
resource_records = resource_klass.find(resource_klass.verify_filters(@request.filters, context),
|
26
|
-
context: context,
|
27
|
-
sort_criteria: @request.sort_criteria,
|
28
|
-
paginator: @request.paginator)
|
29
|
-
|
30
|
-
render json: serializer.serialize_to_hash(resource_records)
|
31
|
-
rescue => e
|
32
|
-
handle_exceptions(e)
|
33
|
-
end
|
34
|
-
|
35
|
-
def show
|
36
|
-
serializer = JSONAPI::ResourceSerializer.new(resource_klass,
|
37
|
-
include: @request.include,
|
38
|
-
fields: @request.fields,
|
39
|
-
base_url: base_url,
|
40
|
-
key_formatter: key_formatter,
|
41
|
-
route_formatter: route_formatter)
|
42
|
-
|
43
|
-
key = resource_klass.verify_key(params[resource_klass._primary_key], context)
|
44
|
-
|
45
|
-
resource_record = resource_klass.find_by_key(key, context: context)
|
46
|
-
|
47
|
-
render json: serializer.serialize_to_hash(resource_record)
|
48
|
-
rescue => e
|
49
|
-
handle_exceptions(e)
|
50
|
-
end
|
51
|
-
|
52
|
-
def show_association
|
53
|
-
association_type = params[:association]
|
54
|
-
|
55
|
-
parent_key = resource_klass.verify_key(params[resource_klass._as_parent_key], context)
|
56
|
-
|
57
|
-
parent_resource = resource_klass.find_by_key(parent_key, context: context)
|
58
|
-
|
59
|
-
association = resource_klass._association(association_type)
|
60
|
-
|
61
|
-
serializer = JSONAPI::ResourceSerializer.new(resource_klass,
|
62
|
-
fields: @request.fields,
|
63
|
-
base_url: base_url,
|
64
|
-
key_formatter: key_formatter,
|
65
|
-
route_formatter: route_formatter)
|
66
|
-
|
67
|
-
render json: serializer.serialize_to_links_hash(parent_resource, association)
|
68
|
-
rescue => e
|
69
|
-
handle_exceptions(e)
|
70
|
-
end
|
71
|
-
|
72
|
-
def create
|
73
|
-
process_request_operations
|
74
|
-
end
|
75
|
-
|
76
|
-
def create_association
|
77
|
-
process_request_operations
|
78
|
-
end
|
79
|
-
|
80
|
-
def update_association
|
81
|
-
process_request_operations
|
82
|
-
end
|
83
|
-
|
84
|
-
def update
|
85
|
-
process_request_operations
|
86
|
-
end
|
87
|
-
|
88
|
-
def destroy
|
89
|
-
process_request_operations
|
90
|
-
end
|
91
|
-
|
92
|
-
def destroy_association
|
93
|
-
process_request_operations
|
94
|
-
end
|
95
|
-
|
96
|
-
def get_related_resource
|
97
|
-
association_type = params[:association]
|
98
|
-
source_resource = @request.source_klass.find_by_key(@request.source_id, context: context)
|
99
|
-
|
100
|
-
serializer = JSONAPI::ResourceSerializer.new(@request.source_klass,
|
101
|
-
include: @request.include,
|
102
|
-
fields: @request.fields,
|
103
|
-
base_url: base_url,
|
104
|
-
key_formatter: key_formatter,
|
105
|
-
route_formatter: route_formatter)
|
106
|
-
|
107
|
-
render json: serializer.serialize_to_hash(source_resource.send(association_type))
|
108
|
-
end
|
109
|
-
|
110
|
-
def get_related_resources
|
111
|
-
association_type = params[:association]
|
112
|
-
source_resource = @request.source_klass.find_by_key(@request.source_id, context: context)
|
113
|
-
|
114
|
-
related_resources = source_resource.send(association_type,
|
115
|
-
{
|
116
|
-
filters: @request.source_klass.verify_filters(@request.filters, context),
|
117
|
-
sort_criteria: @request.sort_criteria,
|
118
|
-
paginator: @request.paginator
|
119
|
-
})
|
120
|
-
|
121
|
-
serializer = JSONAPI::ResourceSerializer.new(@request.source_klass,
|
122
|
-
include: @request.include,
|
123
|
-
fields: @request.fields,
|
124
|
-
base_url: base_url,
|
125
|
-
key_formatter: key_formatter,
|
126
|
-
route_formatter: route_formatter)
|
127
|
-
|
128
|
-
render json: serializer.serialize_to_hash(related_resources)
|
129
|
-
end
|
130
|
-
|
131
|
-
# Override this to use another operations processor
|
132
|
-
def create_operations_processor
|
133
|
-
JSONAPI::ActiveRecordOperationsProcessor.new
|
134
|
-
end
|
135
|
-
|
136
|
-
private
|
137
|
-
def resource_klass
|
138
|
-
@resource_klass ||= resource_klass_name.safe_constantize
|
139
|
-
end
|
140
|
-
|
141
|
-
def base_url
|
142
|
-
@base_url ||= request.protocol + request.host_with_port
|
143
|
-
end
|
144
|
-
|
145
|
-
def resource_klass_name
|
146
|
-
@resource_klass_name ||= "#{self.class.name.sub(/Controller$/, '').singularize}Resource"
|
147
|
-
end
|
148
|
-
|
149
|
-
def ensure_correct_media_type
|
150
|
-
unless request.content_type == JSONAPI::MEDIA_TYPE
|
151
|
-
raise JSONAPI::Exceptions::UnsupportedMediaTypeError.new(request.content_type)
|
152
|
-
end
|
153
|
-
rescue => e
|
154
|
-
handle_exceptions(e)
|
155
|
-
end
|
156
|
-
|
157
|
-
def setup_request
|
158
|
-
@request = JSONAPI::Request.new(params, {
|
159
|
-
context: context,
|
160
|
-
key_formatter: key_formatter
|
161
|
-
})
|
162
|
-
render_errors(@request.errors) unless @request.errors.empty?
|
163
|
-
rescue => e
|
164
|
-
handle_exceptions(e)
|
165
|
-
end
|
166
|
-
|
167
|
-
def setup_response
|
168
|
-
if response.body.size > 0
|
169
|
-
response.headers['Content-Type'] = JSONAPI::MEDIA_TYPE
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
# override to set context
|
174
|
-
def context
|
175
|
-
{}
|
176
|
-
end
|
177
|
-
|
178
|
-
# Control by setting in an initializer:
|
179
|
-
# JSONAPI.configuration.json_key_format = :camelized_key
|
180
|
-
# JSONAPI.configuration.route = :camelized_route
|
181
|
-
#
|
182
|
-
# Override if you want to set a per controller key format.
|
183
|
-
# Must return a class derived from KeyFormatter.
|
184
|
-
def key_formatter
|
185
|
-
JSONAPI.configuration.key_formatter
|
186
|
-
end
|
187
|
-
|
188
|
-
def route_formatter
|
189
|
-
JSONAPI.configuration.route_formatter
|
190
|
-
end
|
191
|
-
|
192
|
-
def render_errors(errors)
|
193
|
-
render(json: {errors: errors}, status: errors[0].status)
|
194
|
-
end
|
195
|
-
|
196
|
-
def process_request_operations
|
197
|
-
results = create_operations_processor.process(@request)
|
198
|
-
errors = results.select(&:has_errors?).flat_map(&:errors).compact
|
199
|
-
resources = results.reject(&:has_errors?).flat_map(&:resource).compact
|
200
|
-
|
201
|
-
status, json = case
|
202
|
-
when errors.any?
|
203
|
-
[errors[0].status, {errors: errors}]
|
204
|
-
when results.any? && resources.any?
|
205
|
-
res = resources.length > 1 ? resources : resources[0]
|
206
|
-
[results[0].code, processing_serializer.serialize_to_hash(res)]
|
207
|
-
else
|
208
|
-
[results[0].code, nil]
|
209
|
-
end
|
210
|
-
|
211
|
-
render status: status, json: json
|
212
|
-
rescue => e
|
213
|
-
handle_exceptions(e)
|
214
|
-
end
|
215
|
-
|
216
|
-
def processing_serializer
|
217
|
-
JSONAPI::ResourceSerializer.new(resource_klass,
|
218
|
-
include: @request.include,
|
219
|
-
fields: @request.fields,
|
220
|
-
base_url: base_url,
|
221
|
-
key_formatter: key_formatter,
|
222
|
-
route_formatter: route_formatter)
|
223
|
-
end
|
224
|
-
|
225
|
-
# override this to process other exceptions
|
226
|
-
# Note: Be sure to either call super(e) or handle JSONAPI::Exceptions::Error and raise unhandled exceptions
|
227
|
-
def handle_exceptions(e)
|
228
|
-
case e
|
229
|
-
when JSONAPI::Exceptions::Error
|
230
|
-
render_errors(e.errors)
|
231
|
-
else # raise all other exceptions
|
232
|
-
# :nocov:
|
233
|
-
raise e
|
234
|
-
# :nocov:
|
235
|
-
end
|
236
|
-
end
|
3
|
+
include JSONAPI::ActsAsResourceController
|
237
4
|
end
|
238
5
|
end
|