jsonapi-resources 0.3.3 → 0.4.0
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/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
|