sanger-jsonapi-resources 0.1.4 → 0.2.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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +26 -42
  4. data/lib/bug_report_templates/rails_5_latest.rb +125 -0
  5. data/lib/bug_report_templates/rails_5_master.rb +140 -0
  6. data/lib/jsonapi/active_relation/adapters/join_left_active_record_adapter.rb +26 -0
  7. data/lib/jsonapi/active_relation/join_manager.rb +297 -0
  8. data/lib/jsonapi/active_relation_resource.rb +898 -0
  9. data/lib/jsonapi/acts_as_resource_controller.rb +130 -113
  10. data/lib/jsonapi/basic_resource.rb +1164 -0
  11. data/lib/jsonapi/cached_response_fragment.rb +129 -0
  12. data/lib/jsonapi/callbacks.rb +2 -0
  13. data/lib/jsonapi/compatibility_helper.rb +29 -0
  14. data/lib/jsonapi/compiled_json.rb +13 -1
  15. data/lib/jsonapi/configuration.rb +88 -21
  16. data/lib/jsonapi/error.rb +24 -10
  17. data/lib/jsonapi/error_codes.rb +4 -0
  18. data/lib/jsonapi/exceptions.rb +84 -52
  19. data/lib/jsonapi/formatter.rb +5 -3
  20. data/lib/jsonapi/include_directives.rb +22 -67
  21. data/lib/jsonapi/link_builder.rb +76 -80
  22. data/lib/jsonapi/mime_types.rb +6 -10
  23. data/lib/jsonapi/naive_cache.rb +2 -0
  24. data/lib/jsonapi/operation.rb +18 -5
  25. data/lib/jsonapi/operation_result.rb +76 -16
  26. data/lib/jsonapi/paginator.rb +2 -0
  27. data/lib/jsonapi/path.rb +45 -0
  28. data/lib/jsonapi/path_segment.rb +78 -0
  29. data/lib/jsonapi/processor.rb +193 -115
  30. data/lib/jsonapi/relationship.rb +145 -14
  31. data/lib/jsonapi/request.rb +734 -0
  32. data/lib/jsonapi/resource.rb +3 -1251
  33. data/lib/jsonapi/resource_controller.rb +2 -0
  34. data/lib/jsonapi/resource_controller_metal.rb +7 -1
  35. data/lib/jsonapi/resource_fragment.rb +56 -0
  36. data/lib/jsonapi/resource_identity.rb +44 -0
  37. data/lib/jsonapi/resource_serializer.rb +158 -284
  38. data/lib/jsonapi/resource_set.rb +196 -0
  39. data/lib/jsonapi/resource_tree.rb +236 -0
  40. data/lib/jsonapi/resources/railtie.rb +9 -0
  41. data/lib/jsonapi/resources/version.rb +1 -1
  42. data/lib/jsonapi/response_document.rb +107 -83
  43. data/lib/jsonapi/routing_ext.rb +81 -30
  44. data/lib/jsonapi-resources.rb +23 -5
  45. data/lib/tasks/check_upgrade.rake +52 -0
  46. metadata +43 -82
  47. data/lib/jsonapi/cached_resource_fragment.rb +0 -127
  48. data/lib/jsonapi/operation_dispatcher.rb +0 -88
  49. data/lib/jsonapi/operation_results.rb +0 -35
  50. data/lib/jsonapi/relationship_builder.rb +0 -167
  51. data/lib/jsonapi/request_parser.rb +0 -679
@@ -1,679 +0,0 @@
1
- require 'jsonapi/operation'
2
- require 'jsonapi/paginator'
3
- require 'cgi'
4
-
5
- module JSONAPI
6
- class RequestParser
7
- attr_accessor :fields, :include, :filters, :sort_criteria, :errors, :operations,
8
- :resource_klass, :context, :paginator, :source_klass, :source_id,
9
- :include_directives, :params, :warnings, :server_error_callbacks
10
-
11
- def initialize(params = nil, options = {})
12
- @params = params
13
- @context = options[:context]
14
- @key_formatter = options.fetch(:key_formatter, JSONAPI.configuration.key_formatter)
15
- @errors = []
16
- @warnings = []
17
- @operations = []
18
- @fields = {}
19
- @filters = {}
20
- @sort_criteria = nil
21
- @source_klass = nil
22
- @source_id = nil
23
- @include_directives = nil
24
- @paginator = nil
25
- @id = nil
26
- @server_error_callbacks = options.fetch(:server_error_callbacks, [])
27
-
28
- setup_action(@params)
29
- end
30
-
31
- def setup_action(params)
32
- return if params.nil?
33
-
34
- @resource_klass ||= Resource.resource_for(params[:controller]) if params[:controller]
35
-
36
- setup_action_method_name = "setup_#{params[:action]}_action"
37
- if respond_to?(setup_action_method_name)
38
- raise params[:_parser_exception] if params[:_parser_exception]
39
- send(setup_action_method_name, params)
40
- end
41
- rescue ActionController::ParameterMissing => e
42
- @errors.concat(JSONAPI::Exceptions::ParameterMissing.new(e.param).errors)
43
- end
44
-
45
- def setup_index_action(params)
46
- parse_fields(params[:fields])
47
- parse_include_directives(params[:include])
48
- set_default_filters
49
- parse_filters(params[:filter])
50
- parse_sort_criteria(params[:sort])
51
- parse_pagination(params[:page])
52
- add_find_operation
53
- end
54
-
55
- def setup_get_related_resource_action(params)
56
- initialize_source(params)
57
- parse_fields(params[:fields])
58
- parse_include_directives(params[:include])
59
- set_default_filters
60
- parse_filters(params[:filter])
61
- parse_sort_criteria(params[:sort])
62
- parse_pagination(params[:page])
63
- add_show_related_resource_operation(params[:relationship])
64
- end
65
-
66
- def setup_get_related_resources_action(params)
67
- initialize_source(params)
68
- parse_fields(params[:fields])
69
- parse_include_directives(params[:include])
70
- set_default_filters
71
- parse_filters(params[:filter])
72
- parse_sort_criteria(params[:sort])
73
- parse_pagination(params[:page])
74
- add_show_related_resources_operation(params[:relationship])
75
- end
76
-
77
- def setup_show_action(params)
78
- parse_fields(params[:fields])
79
- parse_include_directives(params[:include])
80
- @id = params[:id]
81
- add_show_operation
82
- end
83
-
84
- def setup_show_relationship_action(params)
85
- add_show_relationship_operation(params[:relationship], params.require(@resource_klass._as_parent_key))
86
- end
87
-
88
- def setup_create_action(params)
89
- parse_fields(params[:fields])
90
- parse_include_directives(params[:include])
91
- parse_add_operation(params.require(:data))
92
- end
93
-
94
- def setup_create_relationship_action(params)
95
- parse_modify_relationship_action(params, :add)
96
- end
97
-
98
- def setup_update_relationship_action(params)
99
- parse_modify_relationship_action(params, :update)
100
- end
101
-
102
- def setup_update_action(params)
103
- parse_fields(params[:fields])
104
- parse_include_directives(params[:include])
105
- parse_replace_operation(params.require(:data), params[:id])
106
- end
107
-
108
- def setup_destroy_action(params)
109
- parse_remove_operation(params)
110
- end
111
-
112
- def setup_destroy_relationship_action(params)
113
- parse_modify_relationship_action(params, :remove)
114
- end
115
-
116
- def parse_modify_relationship_action(params, modification_type)
117
- relationship_type = params.require(:relationship)
118
- parent_key = params.require(@resource_klass._as_parent_key)
119
- relationship = @resource_klass._relationship(relationship_type)
120
-
121
- # Removals of to-one relationships are done implicitly and require no specification of data
122
- data_required = !(modification_type == :remove && relationship.is_a?(JSONAPI::Relationship::ToOne))
123
-
124
- if data_required
125
- data = params.fetch(:data)
126
- object_params = { relationships: { format_key(relationship.name) => { data: data } } }
127
- verified_params = parse_params(object_params, @resource_klass.updatable_fields(@context))
128
-
129
- parse_arguments = [verified_params, relationship, parent_key]
130
- else
131
- parse_arguments = [params, relationship, parent_key]
132
- end
133
-
134
- send(:"parse_#{modification_type}_relationship_operation", *parse_arguments)
135
- end
136
-
137
- def initialize_source(params)
138
- @source_klass = Resource.resource_for(params.require(:source))
139
- @source_id = @source_klass.verify_key(params.require(@source_klass._as_parent_key), @context)
140
- end
141
-
142
- def parse_pagination(page)
143
- paginator_name = @resource_klass._paginator
144
- @paginator = JSONAPI::Paginator.paginator_for(paginator_name).new(page) unless paginator_name == :none
145
- rescue JSONAPI::Exceptions::Error => e
146
- @errors.concat(e.errors)
147
- end
148
-
149
- def parse_fields(fields)
150
- return if fields.nil?
151
-
152
- extracted_fields = {}
153
-
154
- # Extract the fields for each type from the fields parameters
155
- if fields.is_a?(ActionController::Parameters)
156
- fields.each do |field, value|
157
- resource_fields = value.split(',') unless value.nil? || value.empty?
158
- extracted_fields[field] = resource_fields
159
- end
160
- else
161
- fail JSONAPI::Exceptions::InvalidFieldFormat.new
162
- end
163
-
164
- # Validate the fields
165
- extracted_fields.each do |type, values|
166
- underscored_type = unformat_key(type)
167
- extracted_fields[type] = []
168
- begin
169
- if type != format_key(type)
170
- fail JSONAPI::Exceptions::InvalidResource.new(type)
171
- end
172
- type_resource = Resource.resource_for(@resource_klass.module_path + underscored_type.to_s)
173
- rescue NameError
174
- @errors.concat(JSONAPI::Exceptions::InvalidResource.new(type).errors)
175
- rescue JSONAPI::Exceptions::InvalidResource => e
176
- @errors.concat(e.errors)
177
- end
178
-
179
- if type_resource.nil?
180
- @errors.concat(JSONAPI::Exceptions::InvalidResource.new(type).errors)
181
- else
182
- unless values.nil?
183
- valid_fields = type_resource.fields.collect { |key| format_key(key) }
184
- values.each do |field|
185
- if valid_fields.include?(field)
186
- extracted_fields[type].push unformat_key(field)
187
- else
188
- @errors.concat(JSONAPI::Exceptions::InvalidField.new(type, field).errors)
189
- end
190
- end
191
- else
192
- @errors.concat(JSONAPI::Exceptions::InvalidField.new(type, 'nil').errors)
193
- end
194
- end
195
- end
196
-
197
- @fields = extracted_fields.deep_transform_keys { |key| unformat_key(key) }
198
- end
199
-
200
- def check_include(resource_klass, include_parts)
201
- relationship_name = unformat_key(include_parts.first)
202
-
203
- relationship = resource_klass._relationship(relationship_name)
204
- if relationship && format_key(relationship_name) == include_parts.first
205
- unless include_parts.last.empty?
206
- check_include(Resource.resource_for(resource_klass.module_path + relationship.class_name.to_s.underscore), include_parts.last.partition('.'))
207
- end
208
- else
209
- @errors.concat(JSONAPI::Exceptions::InvalidInclude.new(format_key(resource_klass._type),
210
- include_parts.first).errors)
211
- end
212
- end
213
-
214
- def parse_include_directives(raw_include)
215
- return unless raw_include
216
-
217
- unless JSONAPI.configuration.allow_include
218
- fail JSONAPI::Exceptions::ParametersNotAllowed.new([:include])
219
- end
220
-
221
- included_resources = []
222
- begin
223
- included_resources += CSV.parse_line(raw_include)
224
- rescue CSV::MalformedCSVError
225
- fail JSONAPI::Exceptions::InvalidInclude.new(format_key(@resource_klass._type), raw_include)
226
- end
227
-
228
- return if included_resources.empty?
229
-
230
- result = included_resources.compact.map do |included_resource|
231
- check_include(@resource_klass, included_resource.partition('.'))
232
- unformat_key(included_resource).to_s
233
- end
234
-
235
- @include_directives = JSONAPI::IncludeDirectives.new(@resource_klass, result)
236
- end
237
-
238
- def parse_filters(filters)
239
- return unless filters
240
-
241
- unless JSONAPI.configuration.allow_filter
242
- fail JSONAPI::Exceptions::ParametersNotAllowed.new([:filter])
243
- end
244
-
245
- unless filters.class.method_defined?(:each)
246
- @errors.concat(JSONAPI::Exceptions::InvalidFiltersSyntax.new(filters).errors)
247
- return
248
- end
249
-
250
- filters.each do |key, value|
251
- filter = unformat_key(key)
252
- if @resource_klass._allowed_filter?(filter)
253
- @filters[filter] = value
254
- else
255
- @errors.concat(JSONAPI::Exceptions::FilterNotAllowed.new(filter).errors)
256
- end
257
- end
258
- end
259
-
260
- def set_default_filters
261
- @resource_klass._allowed_filters.each do |filter, opts|
262
- next if opts[:default].nil? || !@filters[filter].nil?
263
- @filters[filter] = opts[:default]
264
- end
265
- end
266
-
267
- def parse_sort_criteria(sort_criteria)
268
- return unless sort_criteria.present?
269
-
270
- unless JSONAPI.configuration.allow_sort
271
- fail JSONAPI::Exceptions::ParametersNotAllowed.new([:sort])
272
- end
273
-
274
- sorts = []
275
- begin
276
- raw = CGI.unescape(sort_criteria)
277
- sorts += CSV.parse_line(raw)
278
- rescue CSV::MalformedCSVError
279
- fail JSONAPI::Exceptions::InvalidSortCriteria.new(format_key(@resource_klass._type), raw)
280
- end
281
-
282
- @sort_criteria = sorts.collect do |sort|
283
- if sort.start_with?('-')
284
- sort_criteria = { field: unformat_key(sort[1..-1]).to_s }
285
- sort_criteria[:direction] = :desc
286
- else
287
- sort_criteria = { field: unformat_key(sort).to_s }
288
- sort_criteria[:direction] = :asc
289
- end
290
-
291
- check_sort_criteria(@resource_klass, sort_criteria)
292
- sort_criteria
293
- end
294
- end
295
-
296
- def check_sort_criteria(resource_klass, sort_criteria)
297
- sort_field = sort_criteria[:field]
298
- sortable_fields = resource_klass.sortable_fields(context)
299
-
300
- unless sortable_fields.include? sort_field.to_sym
301
- @errors.concat(JSONAPI::Exceptions::InvalidSortCriteria
302
- .new(format_key(resource_klass._type), sort_field).errors)
303
- end
304
- end
305
-
306
- def add_find_operation
307
- @operations.push JSONAPI::Operation.new(:find,
308
- @resource_klass,
309
- context: @context,
310
- filters: @filters,
311
- include_directives: @include_directives,
312
- sort_criteria: @sort_criteria,
313
- paginator: @paginator,
314
- fields: @fields
315
- )
316
- end
317
-
318
- def add_show_operation
319
- @operations.push JSONAPI::Operation.new(:show,
320
- @resource_klass,
321
- context: @context,
322
- id: @id,
323
- include_directives: @include_directives,
324
- fields: @fields
325
- )
326
- end
327
-
328
- def add_show_relationship_operation(relationship_type, parent_key)
329
- @operations.push JSONAPI::Operation.new(:show_relationship,
330
- @resource_klass,
331
- context: @context,
332
- relationship_type: relationship_type,
333
- parent_key: @resource_klass.verify_key(parent_key)
334
- )
335
- end
336
-
337
- def add_show_related_resource_operation(relationship_type)
338
- @operations.push JSONAPI::Operation.new(:show_related_resource,
339
- @resource_klass,
340
- context: @context,
341
- relationship_type: relationship_type,
342
- source_klass: @source_klass,
343
- source_id: @source_id,
344
- fields: @fields,
345
- include_directives: @include_directives
346
- )
347
- end
348
-
349
- def add_show_related_resources_operation(relationship_type)
350
- @operations.push JSONAPI::Operation.new(:show_related_resources,
351
- @resource_klass,
352
- context: @context,
353
- relationship_type: relationship_type,
354
- source_klass: @source_klass,
355
- source_id: @source_id,
356
- filters: @source_klass.verify_filters(@filters, @context),
357
- sort_criteria: @sort_criteria,
358
- paginator: @paginator,
359
- fields: @fields,
360
- include_directives: @include_directives
361
- )
362
- end
363
-
364
- def parse_add_operation(params)
365
- fail JSONAPI::Exceptions::InvalidDataFormat unless params.respond_to?(:each_pair)
366
-
367
- verify_type(params[:type])
368
-
369
- data = parse_params(params, @resource_klass.creatable_fields(@context))
370
- @operations.push JSONAPI::Operation.new(:create_resource,
371
- @resource_klass,
372
- context: @context,
373
- data: data,
374
- fields: @fields,
375
- include_directives: @include_directives
376
- )
377
- rescue JSONAPI::Exceptions::Error => e
378
- @errors.concat(e.errors)
379
- end
380
-
381
- def verify_type(type)
382
- if type.nil?
383
- fail JSONAPI::Exceptions::ParameterMissing.new(:type)
384
- elsif unformat_key(type).to_sym != @resource_klass._type
385
- fail JSONAPI::Exceptions::InvalidResource.new(type)
386
- end
387
- end
388
-
389
- def parse_to_one_links_object(raw)
390
- if raw.nil?
391
- return {
392
- type: nil,
393
- id: nil
394
- }
395
- end
396
-
397
- if !(raw.is_a?(Hash) || raw.is_a?(ActionController::Parameters)) ||
398
- raw.keys.length != 2 || !(raw.key?('type') && raw.key?('id'))
399
- fail JSONAPI::Exceptions::InvalidLinksObject.new
400
- end
401
-
402
- {
403
- type: unformat_key(raw['type']).to_s,
404
- id: raw['id']
405
- }
406
- end
407
-
408
- def parse_to_many_links_object(raw)
409
- fail JSONAPI::Exceptions::InvalidLinksObject.new if raw.nil?
410
-
411
- links_object = {}
412
- if raw.is_a?(Array)
413
- raw.each do |link|
414
- link_object = parse_to_one_links_object(link)
415
- links_object[link_object[:type]] ||= []
416
- links_object[link_object[:type]].push(link_object[:id])
417
- end
418
- else
419
- fail JSONAPI::Exceptions::InvalidLinksObject.new
420
- end
421
- links_object
422
- end
423
-
424
- def parse_params(params, allowed_fields)
425
- verify_permitted_params(params, allowed_fields)
426
-
427
- checked_attributes = {}
428
- checked_to_one_relationships = {}
429
- checked_to_many_relationships = {}
430
-
431
- params.each do |key, value|
432
- case key.to_s
433
- when 'relationships'
434
- value.each do |link_key, link_value|
435
- param = unformat_key(link_key)
436
- relationship = @resource_klass._relationship(param)
437
-
438
- if relationship.is_a?(JSONAPI::Relationship::ToOne)
439
- checked_to_one_relationships[param] = parse_to_one_relationship(link_value, relationship)
440
- elsif relationship.is_a?(JSONAPI::Relationship::ToMany)
441
- parse_to_many_relationship(link_value, relationship) do |result_val|
442
- checked_to_many_relationships[param] = result_val
443
- end
444
- end
445
- end
446
- when 'id'
447
- checked_attributes['id'] = unformat_value(:id, value)
448
- when 'attributes'
449
- value.each do |key, value|
450
- param = unformat_key(key)
451
- checked_attributes[param] = unformat_value(param, value)
452
- end
453
- end
454
- end
455
-
456
- return {
457
- 'attributes' => checked_attributes,
458
- 'to_one' => checked_to_one_relationships,
459
- 'to_many' => checked_to_many_relationships
460
- }.deep_transform_keys { |key| unformat_key(key) }
461
- end
462
-
463
- def parse_to_one_relationship(link_value, relationship)
464
- if link_value.nil?
465
- linkage = nil
466
- else
467
- linkage = link_value[:data]
468
- end
469
-
470
- links_object = parse_to_one_links_object(linkage)
471
- if !relationship.polymorphic? && links_object[:type] && (links_object[:type].to_s != relationship.type.to_s)
472
- fail JSONAPI::Exceptions::TypeMismatch.new(links_object[:type])
473
- end
474
-
475
- unless links_object[:id].nil?
476
- resource = self.resource_klass || Resource
477
- relationship_resource = resource.resource_for(unformat_key(links_object[:type]).to_s)
478
- relationship_id = relationship_resource.verify_key(links_object[:id], @context)
479
- if relationship.polymorphic?
480
- { id: relationship_id, type: unformat_key(links_object[:type].to_s) }
481
- else
482
- relationship_id
483
- end
484
- else
485
- nil
486
- end
487
- end
488
-
489
- def parse_to_many_relationship(link_value, relationship, &add_result)
490
- if link_value.is_a?(Array) && link_value.length == 0
491
- linkage = []
492
- elsif (link_value.is_a?(Hash) || link_value.is_a?(ActionController::Parameters))
493
- linkage = link_value[:data]
494
- else
495
- fail JSONAPI::Exceptions::InvalidLinksObject.new
496
- end
497
-
498
- links_object = parse_to_many_links_object(linkage)
499
-
500
- # Since we do not yet support polymorphic to_many relationships we will raise an error if the type does not match the
501
- # relationship's type.
502
- # ToDo: Support Polymorphic relationships
503
-
504
- if links_object.length == 0
505
- add_result.call([])
506
- else
507
- if links_object.length > 1 || !links_object.has_key?(unformat_key(relationship.type).to_s)
508
- fail JSONAPI::Exceptions::TypeMismatch.new(links_object[:type])
509
- end
510
-
511
- links_object.each_pair do |type, keys|
512
- relationship_resource = Resource.resource_for(@resource_klass.module_path + unformat_key(type).to_s)
513
- add_result.call relationship_resource.verify_keys(keys, @context)
514
- end
515
- end
516
- end
517
-
518
- def unformat_value(attribute, value)
519
- value_formatter = JSONAPI::ValueFormatter.value_formatter_for(@resource_klass._attribute_options(attribute)[:format])
520
- value_formatter.unformat(value)
521
- end
522
-
523
- def verify_permitted_params(params, allowed_fields)
524
- formatted_allowed_fields = allowed_fields.collect { |field| format_key(field).to_sym }
525
- params_not_allowed = []
526
-
527
- params.each do |key, value|
528
- case key.to_s
529
- when 'relationships'
530
- value.keys.each do |links_key|
531
- unless formatted_allowed_fields.include?(links_key.to_sym)
532
- params_not_allowed.push(links_key)
533
- unless JSONAPI.configuration.raise_if_parameters_not_allowed
534
- value.delete links_key
535
- end
536
- end
537
- end
538
- when 'attributes'
539
- value.each do |attr_key, attr_value|
540
- unless formatted_allowed_fields.include?(attr_key.to_sym)
541
- params_not_allowed.push(attr_key)
542
- unless JSONAPI.configuration.raise_if_parameters_not_allowed
543
- value.delete attr_key
544
- end
545
- end
546
- end
547
- when 'type'
548
- when 'id'
549
- unless formatted_allowed_fields.include?(:id)
550
- params_not_allowed.push(:id)
551
- unless JSONAPI.configuration.raise_if_parameters_not_allowed
552
- params.delete :id
553
- end
554
- end
555
- else
556
- params_not_allowed.push(key)
557
- end
558
- end
559
-
560
- if params_not_allowed.length > 0
561
- if JSONAPI.configuration.raise_if_parameters_not_allowed
562
- fail JSONAPI::Exceptions::ParametersNotAllowed.new(params_not_allowed)
563
- else
564
- params_not_allowed_warnings = params_not_allowed.map do |key|
565
- JSONAPI::Warning.new(code: JSONAPI::PARAM_NOT_ALLOWED,
566
- title: 'Param not allowed',
567
- detail: "#{key} is not allowed.")
568
- end
569
- self.warnings.concat(params_not_allowed_warnings)
570
- end
571
- end
572
- end
573
-
574
- def parse_add_relationship_operation(verified_params, relationship, parent_key)
575
- if relationship.is_a?(JSONAPI::Relationship::ToMany)
576
- @operations.push JSONAPI::Operation.new(:create_to_many_relationships,
577
- resource_klass,
578
- context: @context,
579
- resource_id: parent_key,
580
- relationship_type: relationship.name,
581
- data: verified_params[:to_many].values[0]
582
- )
583
- end
584
- end
585
-
586
- def parse_update_relationship_operation(verified_params, relationship, parent_key)
587
- options = {
588
- context: @context,
589
- resource_id: parent_key,
590
- relationship_type: relationship.name
591
- }
592
-
593
- if relationship.is_a?(JSONAPI::Relationship::ToOne)
594
- if relationship.polymorphic?
595
- options[:key_value] = verified_params[:to_one].values[0][:id]
596
- options[:key_type] = verified_params[:to_one].values[0][:type]
597
-
598
- operation_type = :replace_polymorphic_to_one_relationship
599
- else
600
- options[:key_value] = verified_params[:to_one].values[0]
601
- operation_type = :replace_to_one_relationship
602
- end
603
- elsif relationship.is_a?(JSONAPI::Relationship::ToMany)
604
- unless relationship.acts_as_set
605
- fail JSONAPI::Exceptions::ToManySetReplacementForbidden.new
606
- end
607
- options[:data] = verified_params[:to_many].values[0]
608
- operation_type = :replace_to_many_relationships
609
- end
610
-
611
- @operations.push JSONAPI::Operation.new(operation_type, resource_klass, options)
612
- end
613
-
614
- def parse_single_replace_operation(data, keys, id_key_presence_check_required: true)
615
- fail JSONAPI::Exceptions::InvalidDataFormat unless data.respond_to?(:each_pair)
616
-
617
- fail JSONAPI::Exceptions::MissingKey.new if data[:id].nil?
618
-
619
- key = data[:id].to_s
620
- if id_key_presence_check_required && !keys.include?(key)
621
- fail JSONAPI::Exceptions::KeyNotIncludedInURL.new(key)
622
- end
623
-
624
- data.delete(:id) unless keys.include?(:id)
625
-
626
- verify_type(data[:type])
627
-
628
- @operations.push JSONAPI::Operation.new(:replace_fields,
629
- @resource_klass,
630
- context: @context,
631
- resource_id: key,
632
- data: parse_params(data, @resource_klass.updatable_fields(@context)),
633
- fields: @fields,
634
- include_directives: @include_directives
635
- )
636
- end
637
-
638
- def parse_replace_operation(data, keys)
639
- parse_single_replace_operation(data, [keys], id_key_presence_check_required: keys.present?)
640
- rescue JSONAPI::Exceptions::Error => e
641
- @errors.concat(e.errors)
642
- end
643
-
644
- def parse_remove_operation(params)
645
- @operations.push JSONAPI::Operation.new(:remove_resource,
646
- @resource_klass,
647
- context: @context,
648
- resource_id: @resource_klass.verify_key(params.require(:id), context))
649
- rescue JSONAPI::Exceptions::Error => e
650
- @errors.concat(e.errors)
651
- end
652
-
653
- def parse_remove_relationship_operation(params, relationship, parent_key)
654
- operation_base_args = [resource_klass].push(
655
- context: @context,
656
- resource_id: parent_key,
657
- relationship_type: relationship.name
658
- )
659
-
660
- if relationship.is_a?(JSONAPI::Relationship::ToMany)
661
- operation_args = operation_base_args.dup
662
- keys = params[:to_many].values[0]
663
- operation_args[1] = operation_args[1].merge(associated_keys: keys)
664
- @operations.push JSONAPI::Operation.new(:remove_to_many_relationships, *operation_args)
665
- else
666
- @operations.push JSONAPI::Operation.new(:remove_to_one_relationship, *operation_base_args)
667
- end
668
- end
669
-
670
- def format_key(key)
671
- @key_formatter.format(key)
672
- end
673
-
674
- def unformat_key(key)
675
- unformatted_key = @key_formatter.unformat(key)
676
- unformatted_key.nil? ? nil : unformatted_key.to_sym
677
- end
678
- end
679
- end