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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +274 -102
  3. data/jsonapi-resources.gemspec +1 -0
  4. data/lib/jsonapi-resources.rb +15 -0
  5. data/lib/jsonapi/active_record_operations_processor.rb +21 -10
  6. data/lib/jsonapi/acts_as_resource_controller.rb +175 -0
  7. data/lib/jsonapi/configuration.rb +11 -0
  8. data/lib/jsonapi/error_codes.rb +7 -4
  9. data/lib/jsonapi/exceptions.rb +23 -15
  10. data/lib/jsonapi/formatter.rb +5 -5
  11. data/lib/jsonapi/include_directives.rb +67 -0
  12. data/lib/jsonapi/operation.rb +185 -65
  13. data/lib/jsonapi/operation_result.rb +38 -5
  14. data/lib/jsonapi/operation_results.rb +33 -0
  15. data/lib/jsonapi/operations_processor.rb +49 -9
  16. data/lib/jsonapi/paginator.rb +31 -17
  17. data/lib/jsonapi/request.rb +347 -163
  18. data/lib/jsonapi/resource.rb +159 -56
  19. data/lib/jsonapi/resource_controller.rb +1 -234
  20. data/lib/jsonapi/resource_serializer.rb +55 -69
  21. data/lib/jsonapi/resources/version.rb +1 -1
  22. data/lib/jsonapi/response_document.rb +87 -0
  23. data/lib/jsonapi/routing_ext.rb +17 -11
  24. data/test/controllers/controller_test.rb +602 -326
  25. data/test/fixtures/active_record.rb +96 -6
  26. data/test/fixtures/line_items.yml +7 -1
  27. data/test/fixtures/numeros_telefone.yml +3 -0
  28. data/test/fixtures/purchase_orders.yml +6 -0
  29. data/test/integration/requests/request_test.rb +129 -60
  30. data/test/integration/routes/routes_test.rb +17 -17
  31. data/test/test_helper.rb +23 -5
  32. data/test/unit/jsonapi_request/jsonapi_request_test.rb +48 -0
  33. data/test/unit/operation/operations_processor_test.rb +242 -54
  34. data/test/unit/resource/resource_test.rb +108 -2
  35. data/test/unit/serializer/include_directives_test.rb +108 -0
  36. data/test/unit/serializer/response_document_test.rb +61 -0
  37. data/test/unit/serializer/serializer_test.rb +679 -520
  38. metadata +26 -2
@@ -3,7 +3,7 @@ module JSONAPI
3
3
  def initialize(params)
4
4
  end
5
5
 
6
- def apply(relation)
6
+ def apply(relation, order_options)
7
7
  # relation
8
8
  end
9
9
 
@@ -19,9 +19,10 @@ end
19
19
  class OffsetPaginator < JSONAPI::Paginator
20
20
  def initialize(params)
21
21
  parse_pagination_params(params)
22
+ verify_pagination_params
22
23
  end
23
24
 
24
- def apply(relation)
25
+ def apply(relation, order_options)
25
26
  relation.offset(@offset).limit(@limit)
26
27
  end
27
28
 
@@ -35,27 +36,34 @@ class OffsetPaginator < JSONAPI::Paginator
35
36
 
36
37
  @offset = validparams[:offset] ? validparams[:offset].to_i : 0
37
38
  @limit = validparams[:limit] ? validparams[:limit].to_i : JSONAPI.configuration.default_page_size
38
-
39
- if @limit < 1
40
- raise JSONAPI::Exceptions::InvalidPageValue.new(:limit, validparams[:limit])
41
- elsif @limit > JSONAPI.configuration.maximum_page_size
42
- raise JSONAPI::Exceptions::InvalidPageValue.new(:limit, validparams[:limit],
43
- "Limit exceeds maximum page size of #{JSONAPI.configuration.maximum_page_size}.")
44
- end
45
39
  else
46
40
  raise JSONAPI::Exceptions::InvalidPageObject.new
47
41
  end
48
42
  rescue ActionController::UnpermittedParameters => e
49
43
  raise JSONAPI::Exceptions::PageParametersNotAllowed.new(e.params)
50
44
  end
45
+
46
+ def verify_pagination_params
47
+ if @limit < 1
48
+ raise JSONAPI::Exceptions::InvalidPageValue.new(:limit, @limit)
49
+ elsif @limit > JSONAPI.configuration.maximum_page_size
50
+ raise JSONAPI::Exceptions::InvalidPageValue.new(:limit, @limit,
51
+ "Limit exceeds maximum page size of #{JSONAPI.configuration.maximum_page_size}.")
52
+ end
53
+
54
+ if @offset < 0
55
+ raise JSONAPI::Exceptions::InvalidPageValue.new(:offset, @offset)
56
+ end
57
+ end
51
58
  end
52
59
 
53
60
  class PagedPaginator < JSONAPI::Paginator
54
61
  def initialize(params)
55
62
  parse_pagination_params(params)
63
+ verify_pagination_params
56
64
  end
57
65
 
58
- def apply(relation)
66
+ def apply(relation, order_options)
59
67
  offset = (@number - 1) * @size
60
68
  relation.offset(offset).limit(@size)
61
69
  end
@@ -70,13 +78,6 @@ class PagedPaginator < JSONAPI::Paginator
70
78
 
71
79
  @size = validparams[:size] ? validparams[:size].to_i : JSONAPI.configuration.default_page_size
72
80
  @number = validparams[:number] ? validparams[:number].to_i : 1
73
-
74
- if @size < 1
75
- raise JSONAPI::Exceptions::InvalidPageValue.new(:size, validparams[:size])
76
- elsif @size > JSONAPI.configuration.maximum_page_size
77
- raise JSONAPI::Exceptions::InvalidPageValue.new(:size, validparams[:size],
78
- "size exceeds maximum page size of #{JSONAPI.configuration.maximum_page_size}.")
79
- end
80
81
  else
81
82
  @size = JSONAPI.configuration.default_page_size
82
83
  @number = params.to_i
@@ -84,4 +85,17 @@ class PagedPaginator < JSONAPI::Paginator
84
85
  rescue ActionController::UnpermittedParameters => e
85
86
  raise JSONAPI::Exceptions::PageParametersNotAllowed.new(e.params)
86
87
  end
88
+
89
+ def verify_pagination_params
90
+ if @size < 1
91
+ raise JSONAPI::Exceptions::InvalidPageValue.new(:size, @size)
92
+ elsif @size > JSONAPI.configuration.maximum_page_size
93
+ raise JSONAPI::Exceptions::InvalidPageValue.new(:size, @size,
94
+ "size exceeds maximum page size of #{JSONAPI.configuration.maximum_page_size}.")
95
+ end
96
+
97
+ if @number < 1
98
+ raise JSONAPI::Exceptions::InvalidPageValue.new(:number, @number)
99
+ end
100
+ end
87
101
  end
@@ -4,71 +4,121 @@ require 'jsonapi/paginator'
4
4
  module JSONAPI
5
5
  class Request
6
6
  attr_accessor :fields, :include, :filters, :sort_criteria, :errors, :operations,
7
- :resource_klass, :context, :paginator, :source_klass, :source_id
7
+ :resource_klass, :context, :paginator, :source_klass, :source_id,
8
+ :include_directives
8
9
 
9
10
  def initialize(params = nil, options = {})
10
- @context = options.fetch(:context, nil)
11
+ @context = options[:context]
11
12
  @key_formatter = options.fetch(:key_formatter, JSONAPI.configuration.key_formatter)
12
13
  @errors = []
13
14
  @operations = []
14
15
  @fields = {}
15
- @include = []
16
16
  @filters = {}
17
- @sort_criteria = []
17
+ @sort_criteria = [{field: 'id', direction: :asc}]
18
18
  @source_klass = nil
19
19
  @source_id = nil
20
+ @include_directives = nil
21
+ @paginator = nil
22
+ @id = nil
20
23
 
21
- setup(params) if params
24
+ setup_action(params)
22
25
  end
23
26
 
24
- def setup(params)
27
+ def setup_action(params)
28
+ return if params.nil?
29
+
25
30
  @resource_klass ||= Resource.resource_for(params[:controller]) if params[:controller]
26
31
 
27
32
  unless params.nil?
28
- case params[:action]
29
- when 'index'
30
- parse_fields(params[:fields])
31
- parse_include(params[:include])
32
- parse_filters(params[:filter])
33
- parse_sort_criteria(params[:sort])
34
- parse_pagination(params[:page])
35
- when 'get_related_resource', 'get_related_resources'
36
- @source_klass = Resource.resource_for(params.require(:source))
37
- @source_id = @source_klass.verify_key(params.require(@source_klass._as_parent_key), @context)
38
- parse_fields(params[:fields])
39
- parse_include(params[:include])
40
- parse_filters(params[:filter])
41
- parse_sort_criteria(params[:sort])
42
- parse_pagination(params[:page])
43
- when 'show'
44
- parse_fields(params[:fields])
45
- parse_include(params[:include])
46
- when 'create'
47
- parse_fields(params[:fields])
48
- parse_include(params[:include])
49
- parse_add_operation(params.require(:data))
50
- when 'create_association'
51
- parse_add_association_operation(params.require(:data),
52
- params.require(:association),
53
- params.require(@resource_klass._as_parent_key))
54
- when 'update_association'
55
- parse_update_association_operation(params.fetch(:data),
56
- params.require(:association),
57
- params.require(@resource_klass._as_parent_key))
58
- when 'update'
59
- parse_fields(params[:fields])
60
- parse_include(params[:include])
61
- parse_replace_operation(params.require(:data), params.require(@resource_klass._primary_key))
62
- when 'destroy'
63
- parse_remove_operation(params)
64
- when 'destroy_association'
65
- parse_remove_association_operation(params)
33
+ setup_action_method_name = "setup_#{params[:action]}_action"
34
+ if respond_to?(setup_action_method_name)
35
+ send(setup_action_method_name, params)
66
36
  end
67
37
  end
68
38
  rescue ActionController::ParameterMissing => e
69
39
  @errors.concat(JSONAPI::Exceptions::ParameterMissing.new(e.param).errors)
70
40
  end
71
41
 
42
+ def setup_index_action(params)
43
+ parse_fields(params[:fields])
44
+ parse_include_directives(params[:include])
45
+ set_default_filters
46
+ parse_filters(params[:filter])
47
+ parse_sort_criteria(params[:sort])
48
+ parse_pagination(params[:page])
49
+ add_find_operation
50
+ end
51
+
52
+ def setup_get_related_resource_action(params)
53
+ initialize_source(params)
54
+ parse_fields(params[:fields])
55
+ parse_include_directives(params[:include])
56
+ set_default_filters
57
+ parse_filters(params[:filter])
58
+ parse_sort_criteria(params[:sort])
59
+ parse_pagination(params[:page])
60
+ add_show_related_resource_operation(params[:association])
61
+ end
62
+
63
+ def setup_get_related_resources_action(params)
64
+ initialize_source(params)
65
+ parse_fields(params[:fields])
66
+ parse_include_directives(params[:include])
67
+ set_default_filters
68
+ parse_filters(params[:filter])
69
+ parse_sort_criteria(params[:sort])
70
+ parse_pagination(params[:page])
71
+ add_show_related_resources_operation(params[:association])
72
+ end
73
+
74
+ def setup_show_action(params)
75
+ parse_fields(params[:fields])
76
+ parse_include_directives(params[:include])
77
+ @id = params[:id]
78
+ add_show_operation
79
+ end
80
+
81
+ def setup_show_association_action(params)
82
+ add_show_association_operation(params[:association], params.require(@resource_klass._as_parent_key))
83
+ end
84
+
85
+ def setup_create_action(params)
86
+ parse_fields(params[:fields])
87
+ parse_include_directives(params[:include])
88
+ parse_add_operation(params.require(:data))
89
+ end
90
+
91
+ def setup_create_association_action(params)
92
+ parse_add_association_operation(params.require(:data),
93
+ params.require(:association),
94
+ params.require(@resource_klass._as_parent_key))
95
+ end
96
+
97
+ def setup_update_association_action(params)
98
+ parse_update_association_operation(params.fetch(:data),
99
+ params.require(:association),
100
+ params.require(@resource_klass._as_parent_key))
101
+ end
102
+
103
+ def setup_update_action(params)
104
+ parse_fields(params[:fields])
105
+ parse_include_directives(params[:include])
106
+ parse_replace_operation(params.require(:data), params.require(:id))
107
+ end
108
+
109
+ def setup_destroy_action(params)
110
+ parse_remove_operation(params)
111
+ end
112
+
113
+ def setup_destroy_association_action (params)
114
+ parse_remove_association_operation(params)
115
+ end
116
+
117
+ def initialize_source(params)
118
+ @source_klass = Resource.resource_for(params.require(:source))
119
+ @source_id = @source_klass.verify_key(params.require(@source_klass._as_parent_key), @context)
120
+ end
121
+
72
122
  def parse_pagination(page)
73
123
  paginator_name = @resource_klass._paginator
74
124
  @paginator = JSONAPI::Paginator.paginator_for(paginator_name).new(page) unless paginator_name == :none
@@ -142,22 +192,29 @@ module JSONAPI
142
192
  end
143
193
  end
144
194
 
145
- def parse_include(include)
195
+ def parse_include_directives(include)
146
196
  return if include.nil?
147
197
 
148
198
  included_resources = CSV.parse_line(include)
149
199
  return if included_resources.nil?
150
200
 
151
- @include = []
201
+ include = []
152
202
  included_resources.each do |included_resource|
153
203
  check_include(@resource_klass, included_resource.partition('.'))
154
- @include.push(unformat_key(included_resource).to_s)
204
+ include.push(unformat_key(included_resource).to_s)
155
205
  end
206
+
207
+ @include_directives = JSONAPI::IncludeDirectives.new(include)
156
208
  end
157
209
 
158
210
  def parse_filters(filters)
159
211
  return unless filters
160
- @filters = {}
212
+
213
+ unless filters.class.method_defined?(:each)
214
+ @errors.concat(JSONAPI::Exceptions::InvalidFiltersSyntax.new(filters).errors)
215
+ return
216
+ end
217
+
161
218
  filters.each do |key, value|
162
219
  filter = unformat_key(key)
163
220
  if @resource_klass._allowed_filter?(filter)
@@ -168,18 +225,23 @@ module JSONAPI
168
225
  end
169
226
  end
170
227
 
228
+ def set_default_filters
229
+ @resource_klass._allowed_filters.each do |filter, opts|
230
+ next if opts[:default].nil? || !@filters[filter].nil?
231
+ @filters[filter] = opts[:default]
232
+ end
233
+ end
234
+
171
235
  def parse_sort_criteria(sort_criteria)
172
236
  return unless sort_criteria
173
237
 
174
238
  @sort_criteria = CSV.parse_line(URI.unescape(sort_criteria)).collect do |sort|
175
- sort_criteria = {field: unformat_key(sort[1..-1]).to_s}
176
- if sort.start_with?('+', ' ')
177
- sort_criteria[:direction] = :asc
178
- elsif sort.start_with?('-')
239
+ if sort.start_with?('-')
240
+ sort_criteria = {field: unformat_key(sort[1..-1]).to_s}
179
241
  sort_criteria[:direction] = :desc
180
242
  else
181
- @errors.concat(JSONAPI::Exceptions::InvalidSortFormat
182
- .new(format_key(resource_klass._type), sort).errors)
243
+ sort_criteria = {field: unformat_key(sort).to_s}
244
+ sort_criteria[:direction] = :asc
183
245
  end
184
246
 
185
247
  check_sort_criteria(@resource_klass, sort_criteria)
@@ -197,27 +259,96 @@ module JSONAPI
197
259
  end
198
260
  end
199
261
 
262
+ def add_find_operation
263
+ @operations.push JSONAPI::FindOperation.new(
264
+ @resource_klass,
265
+ {
266
+ filters: @filters,
267
+ include_directives: @include_directives,
268
+ sort_criteria: @sort_criteria,
269
+ paginator: @paginator
270
+ }
271
+ )
272
+ end
273
+
274
+ def add_show_operation
275
+ @operations.push JSONAPI::ShowOperation.new(
276
+ @resource_klass,
277
+ {
278
+ id: @id,
279
+ include_directives: @include_directives
280
+ }
281
+ )
282
+ end
283
+
284
+ def add_show_association_operation(association_type, parent_key)
285
+ @operations.push JSONAPI::ShowAssociationOperation.new(
286
+ @resource_klass,
287
+ {
288
+ association_type: association_type,
289
+ parent_key: @resource_klass.verify_key(parent_key)
290
+ }
291
+ )
292
+ end
293
+
294
+ def add_show_related_resource_operation(association_type)
295
+ @operations.push JSONAPI::ShowRelatedResourceOperation.new(
296
+ @resource_klass,
297
+ {
298
+ association_type: association_type,
299
+ source_klass: @source_klass,
300
+ source_id: @source_id
301
+ }
302
+ )
303
+ end
304
+
305
+ def add_show_related_resources_operation(association_type)
306
+ @operations.push JSONAPI::ShowRelatedResourcesOperation.new(
307
+ @resource_klass,
308
+ {
309
+ association_type: association_type,
310
+ source_klass: @source_klass,
311
+ source_id: @source_id,
312
+ filters: @source_klass.verify_filters(@filters, @context),
313
+ sort_criteria: @sort_criteria,
314
+ paginator: @paginator
315
+ }
316
+ )
317
+ end
318
+
319
+ # TODO: Please remove after `createable_fields` is removed
320
+ # :nocov:
321
+ def creatable_fields
322
+ if @resource_klass.respond_to?(:createable_fields)
323
+ creatable_fields = @resource_klass.createable_fields(@context)
324
+ else
325
+ creatable_fields = @resource_klass.creatable_fields(@context)
326
+ end
327
+ end
328
+ # :nocov:
329
+
200
330
  def parse_add_operation(data)
201
- Array.wrap(data).each do |p|
202
- values = parse_params(verify_and_remove_type(p), @resource_klass.createable_fields(@context))
203
- @operations.push JSONAPI::CreateResourceOperation.new(@resource_klass, values)
331
+ Array.wrap(data).each do |params|
332
+ verify_type(params[:type])
333
+
334
+ data = parse_params(params, creatable_fields)
335
+ @operations.push JSONAPI::CreateResourceOperation.new(
336
+ @resource_klass,
337
+ {
338
+ data: data
339
+ }
340
+ )
204
341
  end
205
342
  rescue JSONAPI::Exceptions::Error => e
206
343
  @errors.concat(e.errors)
207
344
  end
208
345
 
209
- def verify_and_remove_type(params)
210
- #remove type and verify it matches the resource
211
- if unformat_key(params[:type]) == @resource_klass._type
212
- params.delete(:type)
213
- else
214
- if params[:type].nil?
215
- raise JSONAPI::Exceptions::ParameterMissing.new(:type)
216
- else
217
- raise JSONAPI::Exceptions::InvalidResource.new(params[:type])
218
- end
346
+ def verify_type(type)
347
+ if type.nil?
348
+ raise JSONAPI::Exceptions::ParameterMissing.new(:type)
349
+ elsif unformat_key(type).to_sym != @resource_klass._type
350
+ raise JSONAPI::Exceptions::InvalidResource.new(type)
219
351
  end
220
- params
221
352
  end
222
353
 
223
354
  def parse_has_one_links_object(raw)
@@ -264,65 +395,70 @@ module JSONAPI
264
395
  checked_has_many_associations = {}
265
396
 
266
397
  params.each do |key, value|
267
- if key == 'links' || key == :links
268
- value.each do |link_key, link_value|
269
- param = unformat_key(link_key)
270
-
271
- association = @resource_klass._association(param)
272
-
273
- if association.is_a?(JSONAPI::Association::HasOne)
274
- if link_value.nil?
275
- linkage = nil
276
- else
277
- linkage = link_value[:linkage]
278
- end
398
+ case key.to_s
399
+ when 'relationships'
400
+ value.each do |link_key, link_value|
401
+ param = unformat_key(link_key)
402
+
403
+ association = @resource_klass._association(param)
404
+
405
+ if association.is_a?(JSONAPI::Association::HasOne)
406
+ if link_value.nil?
407
+ linkage = nil
408
+ else
409
+ linkage = link_value[:data]
410
+ end
279
411
 
280
- links_object = parse_has_one_links_object(linkage)
281
- # Since we do not yet support polymorphic associations we will raise an error if the type does not match the
282
- # association's type.
283
- # ToDo: Support Polymorphic associations
284
- if links_object[:type] && (links_object[:type].to_s != association.type.to_s)
285
- raise JSONAPI::Exceptions::TypeMismatch.new(links_object[:type])
286
- end
412
+ links_object = parse_has_one_links_object(linkage)
413
+ # Since we do not yet support polymorphic associations we will raise an error if the type does not match the
414
+ # association's type.
415
+ # ToDo: Support Polymorphic associations
416
+ if links_object[:type] && (links_object[:type].to_s != association.type.to_s)
417
+ raise JSONAPI::Exceptions::TypeMismatch.new(links_object[:type])
418
+ end
287
419
 
288
- unless links_object[:id].nil?
289
- association_resource = Resource.resource_for(@resource_klass.module_path + unformat_key(links_object[:type]).to_s)
290
- checked_has_one_associations[param] = association_resource.verify_key(links_object[:id], @context)
291
- else
292
- checked_has_one_associations[param] = nil
293
- end
294
- elsif association.is_a?(JSONAPI::Association::HasMany)
295
- if link_value.is_a?(Array) && link_value.length == 0
296
- linkage = []
297
- elsif link_value.is_a?(Hash)
298
- linkage = link_value[:linkage]
299
- else
300
- raise JSONAPI::Exceptions::InvalidLinksObject.new
301
- end
420
+ unless links_object[:id].nil?
421
+ association_resource = Resource.resource_for(@resource_klass.module_path + unformat_key(links_object[:type]).to_s)
422
+ checked_has_one_associations[param] = association_resource.verify_key(links_object[:id], @context)
423
+ else
424
+ checked_has_one_associations[param] = nil
425
+ end
426
+ elsif association.is_a?(JSONAPI::Association::HasMany)
427
+ if link_value.is_a?(Array) && link_value.length == 0
428
+ linkage = []
429
+ elsif link_value.is_a?(Hash)
430
+ linkage = link_value[:data]
431
+ else
432
+ raise JSONAPI::Exceptions::InvalidLinksObject.new
433
+ end
302
434
 
303
- links_object = parse_has_many_links_object(linkage)
435
+ links_object = parse_has_many_links_object(linkage)
304
436
 
305
- # Since we do not yet support polymorphic associations we will raise an error if the type does not match the
306
- # association's type.
307
- # ToDo: Support Polymorphic associations
437
+ # Since we do not yet support polymorphic associations we will raise an error if the type does not match the
438
+ # association's type.
439
+ # ToDo: Support Polymorphic associations
308
440
 
309
- if links_object.length == 0
310
- checked_has_many_associations[param] = []
311
- else
312
- if links_object.length > 1 || !links_object.has_key?(unformat_key(association.type).to_s)
313
- raise JSONAPI::Exceptions::TypeMismatch.new(links_object[:type])
314
- end
441
+ if links_object.length == 0
442
+ checked_has_many_associations[param] = []
443
+ else
444
+ if links_object.length > 1 || !links_object.has_key?(unformat_key(association.type).to_s)
445
+ raise JSONAPI::Exceptions::TypeMismatch.new(links_object[:type])
446
+ end
315
447
 
316
- links_object.each_pair do |type, keys|
317
- association_resource = Resource.resource_for(@resource_klass.module_path + unformat_key(type).to_s)
318
- checked_has_many_associations[param] = association_resource.verify_keys(keys, @context)
448
+ links_object.each_pair do |type, keys|
449
+ association_resource = Resource.resource_for(@resource_klass.module_path + unformat_key(type).to_s)
450
+ checked_has_many_associations[param] = association_resource.verify_keys(keys, @context)
451
+ end
319
452
  end
320
453
  end
321
454
  end
322
- end
323
- else
324
- param = unformat_key(key)
325
- checked_attributes[param] = unformat_value(param, value)
455
+ when 'id'
456
+ checked_attributes['id'] = unformat_value(:id, value)
457
+ when 'attributes'
458
+ value.each do |key, value|
459
+ param = unformat_key(key)
460
+ checked_attributes[param] = unformat_value(param, value)
461
+ end
326
462
  end
327
463
  end
328
464
 
@@ -335,35 +471,58 @@ module JSONAPI
335
471
 
336
472
  def unformat_value(attribute, value)
337
473
  value_formatter = JSONAPI::ValueFormatter.value_formatter_for(@resource_klass._attribute_options(attribute)[:format])
338
- value_formatter.unformat(value, @context)
474
+ value_formatter.unformat(value)
339
475
  end
340
476
 
341
477
  def verify_permitted_params(params, allowed_fields)
342
478
  formatted_allowed_fields = allowed_fields.collect { |field| format_key(field).to_sym }
343
479
  params_not_allowed = []
480
+
344
481
  params.each do |key, value|
345
- if key == 'links' || key == :links
346
- value.each_key do |links_key|
347
- params_not_allowed.push(links_key) unless formatted_allowed_fields.include?(links_key.to_sym)
348
- end
349
- else
350
- params_not_allowed.push(key) unless formatted_allowed_fields.include?(key.to_sym)
482
+ case key.to_s
483
+ when 'relationships'
484
+ value.each_key do |links_key|
485
+ params_not_allowed.push(links_key) unless formatted_allowed_fields.include?(links_key.to_sym)
486
+ end
487
+ when 'attributes'
488
+ value.each do |attr_key, attr_value|
489
+ params_not_allowed.push(attr_key) unless formatted_allowed_fields.include?(attr_key.to_sym)
490
+ end
491
+ when 'type', 'id'
492
+ else
493
+ params_not_allowed.push(key)
351
494
  end
352
495
  end
496
+
353
497
  raise JSONAPI::Exceptions::ParametersNotAllowed.new(params_not_allowed) if params_not_allowed.length > 0
354
498
  end
355
499
 
500
+ # TODO: Please remove after `updateable_fields` is removed
501
+ # :nocov:
502
+ def updatable_fields
503
+ if @resource_klass.respond_to?(:updateable_fields)
504
+ @resource_klass.updateable_fields(@context)
505
+ else
506
+ @resource_klass.updatable_fields(@context)
507
+ end
508
+ end
509
+ # :nocov:
510
+
356
511
  def parse_add_association_operation(data, association_type, parent_key)
357
512
  association = resource_klass._association(association_type)
358
513
 
359
514
  if association.is_a?(JSONAPI::Association::HasMany)
360
- object_params = {links: {association.name => {linkage: data}}}
361
- verified_param_set = parse_params(object_params, @resource_klass.updateable_fields(@context))
362
-
363
- @operations.push JSONAPI::CreateHasManyAssociationOperation.new(resource_klass,
364
- parent_key,
365
- association_type,
366
- verified_param_set[:has_many].values[0])
515
+ object_params = {relationships: {format_key(association.name) => {data: data}}}
516
+ verified_param_set = parse_params(object_params, updatable_fields)
517
+
518
+ @operations.push JSONAPI::CreateHasManyAssociationOperation.new(
519
+ resource_klass,
520
+ {
521
+ resource_id: parent_key,
522
+ association_type: association_type,
523
+ data: verified_param_set[:has_many].values[0]
524
+ }
525
+ )
367
526
  end
368
527
  end
369
528
 
@@ -371,31 +530,38 @@ module JSONAPI
371
530
  association = resource_klass._association(association_type)
372
531
 
373
532
  if association.is_a?(JSONAPI::Association::HasOne)
374
- object_params = {links: {association.name => {linkage: data}}}
375
-
376
- verified_param_set = parse_params(object_params, @resource_klass.updateable_fields(@context))
377
-
378
- @operations.push JSONAPI::ReplaceHasOneAssociationOperation.new(resource_klass,
379
- parent_key,
380
- association_type,
381
- verified_param_set[:has_one].values[0])
533
+ object_params = {relationships: {format_key(association.name) => {data: data}}}
534
+ verified_param_set = parse_params(object_params, updatable_fields)
535
+
536
+ @operations.push JSONAPI::ReplaceHasOneAssociationOperation.new(
537
+ resource_klass,
538
+ {
539
+ resource_id: parent_key,
540
+ association_type: association_type,
541
+ key_value: verified_param_set[:has_one].values[0]
542
+ }
543
+ )
382
544
  else
383
545
  unless association.acts_as_set
384
546
  raise JSONAPI::Exceptions::HasManySetReplacementForbidden.new
385
547
  end
386
548
 
387
- object_params = {links: {association.name => {linkage: data}}}
388
- verified_param_set = parse_params(object_params, @resource_klass.updateable_fields(@context))
389
-
390
- @operations.push JSONAPI::ReplaceHasManyAssociationOperation.new(resource_klass,
391
- parent_key,
392
- association_type,
393
- verified_param_set[:has_many].values[0])
549
+ object_params = {relationships: {format_key(association.name) => {data: data}}}
550
+ verified_param_set = parse_params(object_params, updatable_fields)
551
+
552
+ @operations.push JSONAPI::ReplaceHasManyAssociationOperation.new(
553
+ resource_klass,
554
+ {
555
+ resource_id: parent_key,
556
+ association_type: association_type,
557
+ data: verified_param_set[:has_many].values[0]
558
+ }
559
+ )
394
560
  end
395
561
  end
396
562
 
397
563
  def parse_single_replace_operation(data, keys)
398
- if data[@resource_klass._primary_key].nil?
564
+ if data[:id].nil?
399
565
  raise JSONAPI::Exceptions::MissingKey.new
400
566
  end
401
567
 
@@ -404,19 +570,24 @@ module JSONAPI
404
570
  raise JSONAPI::Exceptions::ParameterMissing.new(:type)
405
571
  end
406
572
 
407
- key = data[@resource_klass._primary_key]
573
+ key = data[:id]
408
574
  if !keys.include?(key)
409
575
  raise JSONAPI::Exceptions::KeyNotIncludedInURL.new(key)
410
576
  end
411
577
 
412
- if !keys.include?(@resource_klass._primary_key)
578
+ if !keys.include?(:id)
413
579
  data.delete(:id)
414
580
  end
415
581
 
416
- @operations.push(JSONAPI::ReplaceFieldsOperation.new(@resource_klass,
417
- key,
418
- parse_params(verify_and_remove_type(data),
419
- @resource_klass.updateable_fields(@context))))
582
+ verify_type(data[:type])
583
+
584
+ @operations.push JSONAPI::ReplaceFieldsOperation.new(
585
+ @resource_klass,
586
+ {
587
+ resource_id: key,
588
+ data: parse_params(data, updatable_fields)
589
+ }
590
+ )
420
591
  end
421
592
 
422
593
  def parse_replace_operation(data, keys)
@@ -437,10 +608,15 @@ module JSONAPI
437
608
  end
438
609
 
439
610
  def parse_remove_operation(params)
440
- keys = parse_key_array(params.permit(@resource_klass._primary_key)[@resource_klass._primary_key])
611
+ keys = parse_key_array(params.permit(:id)[:id])
441
612
 
442
613
  keys.each do |key|
443
- @operations.push JSONAPI::RemoveResourceOperation.new(@resource_klass, key)
614
+ @operations.push JSONAPI::RemoveResourceOperation.new(
615
+ @resource_klass,
616
+ {
617
+ resource_id: key
618
+ }
619
+ )
444
620
  end
445
621
  rescue ActionController::UnpermittedParameters => e
446
622
  @errors.concat(JSONAPI::Exceptions::ParametersNotAllowed.new(e.params).errors)
@@ -457,15 +633,23 @@ module JSONAPI
457
633
  if association.is_a?(JSONAPI::Association::HasMany)
458
634
  keys = parse_key_array(params[:keys])
459
635
  keys.each do |key|
460
- @operations.push JSONAPI::RemoveHasManyAssociationOperation.new(resource_klass,
461
- parent_key,
462
- association_type,
463
- key)
636
+ @operations.push JSONAPI::RemoveHasManyAssociationOperation.new(
637
+ resource_klass,
638
+ {
639
+ resource_id: parent_key,
640
+ association_type: association_type,
641
+ associated_key: key
642
+ }
643
+ )
464
644
  end
465
645
  else
466
- @operations.push JSONAPI::RemoveHasOneAssociationOperation.new(resource_klass,
467
- parent_key,
468
- association_type)
646
+ @operations.push JSONAPI::RemoveHasOneAssociationOperation.new(
647
+ resource_klass,
648
+ {
649
+ resource_id: parent_key,
650
+ association_type: association_type
651
+ }
652
+ )
469
653
  end
470
654
  end
471
655