jsonapi-resources 0.1.1 → 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.
- checksums.yaml +4 -4
- data/README.md +68 -1
- data/Rakefile +3 -7
- data/lib/jsonapi-resources.rb +1 -0
- data/lib/jsonapi/configuration.rb +29 -4
- data/lib/jsonapi/error_codes.rb +6 -2
- data/lib/jsonapi/exceptions.rb +92 -19
- data/lib/jsonapi/operation.rb +0 -18
- data/lib/jsonapi/paginator.rb +98 -0
- data/lib/jsonapi/request.rb +257 -182
- data/lib/jsonapi/resource.rb +58 -47
- data/lib/jsonapi/resource_controller.rb +85 -29
- data/lib/jsonapi/resource_serializer.rb +88 -33
- data/lib/jsonapi/resources/version.rb +1 -1
- data/lib/jsonapi/routing_ext.rb +38 -12
- data/test/controllers/controller_test.rb +761 -455
- data/test/fixtures/active_record.rb +90 -18
- data/test/integration/requests/request_test.rb +183 -25
- data/test/integration/routes/routes_test.rb +0 -5
- data/test/test_helper.rb +31 -7
- data/test/unit/operation/operations_processor_test.rb +28 -1
- data/test/unit/resource/resource_test.rb +4 -0
- data/test/unit/serializer/serializer_test.rb +882 -377
- metadata +3 -2
data/lib/jsonapi/request.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
require 'jsonapi/resource_for'
|
2
2
|
require 'jsonapi/operation'
|
3
|
+
require 'jsonapi/paginator'
|
3
4
|
|
4
5
|
module JSONAPI
|
5
6
|
class Request
|
6
7
|
include ResourceFor
|
7
8
|
|
8
|
-
attr_accessor :fields, :include, :filters, :
|
9
|
+
attr_accessor :fields, :include, :filters, :sort_criteria, :errors, :operations,
|
10
|
+
:resource_klass, :context, :paginator, :source_klass, :source_id
|
9
11
|
|
10
12
|
def initialize(params = nil, options = {})
|
11
13
|
@context = options.fetch(:context, nil)
|
@@ -15,6 +17,9 @@ module JSONAPI
|
|
15
17
|
@fields = {}
|
16
18
|
@include = []
|
17
19
|
@filters = {}
|
20
|
+
@sort_criteria = []
|
21
|
+
@source_klass = nil
|
22
|
+
@source_id = nil
|
18
23
|
|
19
24
|
setup(params) if params
|
20
25
|
end
|
@@ -25,71 +30,90 @@ module JSONAPI
|
|
25
30
|
unless params.nil?
|
26
31
|
case params[:action]
|
27
32
|
when 'index'
|
28
|
-
parse_fields(params)
|
29
|
-
parse_include(params)
|
30
|
-
parse_filters(params)
|
31
|
-
|
32
|
-
|
33
|
+
parse_fields(params[:fields])
|
34
|
+
parse_include(params[:include])
|
35
|
+
parse_filters(params[:filter])
|
36
|
+
parse_sort_criteria(params[:sort])
|
37
|
+
parse_pagination(params[:page])
|
38
|
+
when 'get_related_resource', 'get_related_resources'
|
39
|
+
@source_klass = self.class.resource_for(params.require(:source))
|
40
|
+
@source_id = params.require(@source_klass._as_parent_key)
|
41
|
+
parse_fields(params[:fields])
|
42
|
+
parse_include(params[:include])
|
43
|
+
parse_filters(params[:filter])
|
44
|
+
parse_sort_criteria(params[:sort])
|
45
|
+
parse_pagination(params[:page])
|
33
46
|
when 'show'
|
34
|
-
parse_fields(params)
|
35
|
-
parse_include(params)
|
47
|
+
parse_fields(params[:fields])
|
48
|
+
parse_include(params[:include])
|
36
49
|
when 'create'
|
37
|
-
parse_fields(params)
|
38
|
-
parse_include(params)
|
39
|
-
parse_add_operation(params)
|
50
|
+
parse_fields(params[:fields])
|
51
|
+
parse_include(params[:include])
|
52
|
+
parse_add_operation(params.require(:data))
|
40
53
|
when 'create_association'
|
41
|
-
parse_add_association_operation(params)
|
54
|
+
parse_add_association_operation(params.require(:data),
|
55
|
+
params.require(:association),
|
56
|
+
params.require(@resource_klass._as_parent_key))
|
42
57
|
when 'update_association'
|
43
|
-
parse_update_association_operation(params)
|
58
|
+
parse_update_association_operation(params.require(:data),
|
59
|
+
params.require(:association),
|
60
|
+
params.require(@resource_klass._as_parent_key))
|
44
61
|
when 'update'
|
45
|
-
parse_fields(params)
|
46
|
-
parse_include(params)
|
47
|
-
parse_replace_operation(params)
|
62
|
+
parse_fields(params[:fields])
|
63
|
+
parse_include(params[:include])
|
64
|
+
parse_replace_operation(params.require(:data), params.require(@resource_klass._primary_key))
|
48
65
|
when 'destroy'
|
49
66
|
parse_remove_operation(params)
|
50
67
|
when 'destroy_association'
|
51
68
|
parse_remove_association_operation(params)
|
52
69
|
end
|
53
70
|
end
|
71
|
+
rescue ActionController::ParameterMissing => e
|
72
|
+
@errors.concat(JSONAPI::Exceptions::ParameterMissing.new(e.param).errors)
|
54
73
|
end
|
55
74
|
|
56
|
-
def
|
57
|
-
|
75
|
+
def parse_pagination(page)
|
76
|
+
paginator_name = @resource_klass._paginator
|
77
|
+
@paginator = JSONAPI::Paginator.paginator_for(paginator_name).new(page) unless paginator_name == :none
|
78
|
+
rescue JSONAPI::Exceptions::Error => e
|
79
|
+
@errors.concat(e.errors)
|
80
|
+
end
|
81
|
+
|
82
|
+
def parse_fields(fields)
|
83
|
+
return if fields.nil?
|
84
|
+
|
85
|
+
extracted_fields = {}
|
58
86
|
|
59
87
|
# Extract the fields for each type from the fields parameters
|
60
|
-
|
61
|
-
|
62
|
-
|
88
|
+
if fields.is_a?(String)
|
89
|
+
resource_fields = fields.split(',') unless fields.empty?
|
90
|
+
type = @resource_klass._type
|
91
|
+
extracted_fields[type] = resource_fields
|
92
|
+
elsif fields.is_a?(ActionController::Parameters)
|
93
|
+
fields.each do |field, value|
|
63
94
|
resource_fields = value.split(',') unless value.nil? || value.empty?
|
64
|
-
|
65
|
-
fields[type] = resource_fields
|
66
|
-
elsif params[:fields].is_a?(ActionController::Parameters)
|
67
|
-
params[:fields].each do |param, value|
|
68
|
-
resource_fields = value.split(',') unless value.nil? || value.empty?
|
69
|
-
type = param
|
70
|
-
fields[type] = resource_fields
|
71
|
-
end
|
95
|
+
extracted_fields[field] = resource_fields
|
72
96
|
end
|
73
97
|
end
|
74
98
|
|
75
99
|
# Validate the fields
|
76
|
-
|
100
|
+
extracted_fields.each do |type, values|
|
77
101
|
underscored_type = unformat_key(type)
|
78
|
-
|
102
|
+
extracted_fields[type] = []
|
79
103
|
begin
|
80
104
|
type_resource = self.class.resource_for(@resource_klass.module_path + underscored_type.to_s)
|
81
105
|
rescue NameError
|
82
106
|
@errors.concat(JSONAPI::Exceptions::InvalidResource.new(type).errors)
|
83
107
|
end
|
84
|
-
|
85
|
-
|
86
|
-
|
108
|
+
if type_resource.nil? || !(@resource_klass._type == underscored_type ||
|
109
|
+
@resource_klass._has_association?(underscored_type))
|
110
|
+
@errors.concat(JSONAPI::Exceptions::InvalidResource.new(type).errors)
|
87
111
|
else
|
88
112
|
unless values.nil?
|
89
|
-
valid_fields = type_resource.fields.collect {|key| format_key(key)}
|
113
|
+
valid_fields = type_resource.fields.collect { |key| format_key(key) }
|
90
114
|
values.each do |field|
|
91
115
|
if valid_fields.include?(field)
|
92
|
-
|
116
|
+
extracted_fields[type].push unformat_key(field)
|
93
117
|
else
|
94
118
|
@errors.concat(JSONAPI::Exceptions::InvalidField.new(type, field).errors)
|
95
119
|
end
|
@@ -100,7 +124,7 @@ module JSONAPI
|
|
100
124
|
end
|
101
125
|
end
|
102
126
|
|
103
|
-
@fields =
|
127
|
+
@fields = extracted_fields.deep_transform_keys { |key| unformat_key(key) }
|
104
128
|
end
|
105
129
|
|
106
130
|
def check_include(resource_klass, include_parts)
|
@@ -113,100 +137,137 @@ module JSONAPI
|
|
113
137
|
end
|
114
138
|
else
|
115
139
|
@errors.concat(JSONAPI::Exceptions::InvalidInclude.new(format_key(resource_klass._type),
|
116
|
-
include_parts.first,
|
140
|
+
include_parts.first,).errors)
|
117
141
|
end
|
118
142
|
end
|
119
143
|
|
120
|
-
def parse_include(
|
121
|
-
|
144
|
+
def parse_include(include)
|
145
|
+
return if include.nil?
|
146
|
+
|
147
|
+
included_resources = CSV.parse_line(include)
|
148
|
+
return if included_resources.nil?
|
149
|
+
|
122
150
|
@include = []
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
@include.push(unformat_key(include).to_s)
|
151
|
+
included_resources.each do |included_resource|
|
152
|
+
check_include(@resource_klass, included_resource.partition('.'))
|
153
|
+
@include.push(unformat_key(included_resource).to_s)
|
127
154
|
end
|
128
155
|
end
|
129
156
|
|
130
|
-
def parse_filters(
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
params.delete(:ids)
|
135
|
-
end
|
136
|
-
|
137
|
-
filters = {}
|
138
|
-
params.each do |key, value|
|
139
|
-
filter = key.to_sym
|
140
|
-
|
141
|
-
# Ignore non-filter parameters
|
142
|
-
next if JSONAPI.configuration.allowed_request_params.include?(filter)
|
143
|
-
|
157
|
+
def parse_filters(filters)
|
158
|
+
return unless filters
|
159
|
+
@filters = {}
|
160
|
+
filters.each do |key, value|
|
144
161
|
filter = unformat_key(key).to_sym
|
145
162
|
if @resource_klass._allowed_filter?(filter)
|
146
|
-
filters[filter] = value
|
163
|
+
@filters[filter] = value
|
147
164
|
else
|
148
165
|
@errors.concat(JSONAPI::Exceptions::FilterNotAllowed.new(filter).errors)
|
149
166
|
end
|
150
167
|
end
|
151
|
-
@filters = filters
|
152
168
|
end
|
153
169
|
|
154
|
-
def
|
155
|
-
|
156
|
-
CSV.parse_line(params[:sort]).collect do |sort_param|
|
157
|
-
# A parameter name may not start with a dash
|
158
|
-
# We need to preserve the dash as the sort direction before we unformat the string
|
159
|
-
if sort_param.start_with?('-')
|
160
|
-
unformatted_sort_param = unformat_key(sort_param[1..-1]).to_s
|
161
|
-
unformatted_sort_param.prepend('-')
|
162
|
-
else
|
163
|
-
unformatted_sort_param = unformat_key(sort_param).to_s
|
164
|
-
end
|
170
|
+
def parse_sort_criteria(sort_criteria)
|
171
|
+
return unless sort_criteria
|
165
172
|
|
166
|
-
|
167
|
-
|
173
|
+
@sort_criteria = CSV.parse_line(sort_criteria).collect do |sort|
|
174
|
+
sort_criteria = {field: unformat_key(sort[1..-1]).to_s}
|
175
|
+
if sort.start_with?('+')
|
176
|
+
sort_criteria[:direction] = :asc
|
177
|
+
elsif sort.start_with?('-')
|
178
|
+
sort_criteria[:direction] = :desc
|
179
|
+
else
|
180
|
+
@errors.concat(JSONAPI::Exceptions::InvalidSortFormat
|
181
|
+
.new(format_key(resource_klass._type), sort).errors)
|
168
182
|
end
|
169
|
-
|
170
|
-
|
183
|
+
|
184
|
+
check_sort_criteria(@resource_klass, sort_criteria)
|
185
|
+
sort_criteria
|
171
186
|
end
|
172
187
|
end
|
173
188
|
|
174
|
-
def
|
175
|
-
|
189
|
+
def check_sort_criteria(resource_klass, sort_criteria)
|
190
|
+
sort_field = sort_criteria[:field]
|
176
191
|
sortable_fields = resource_klass.sortable_fields(context)
|
177
192
|
|
178
|
-
unless sortable_fields.include?
|
179
|
-
@errors.concat(JSONAPI::Exceptions::
|
180
|
-
|
193
|
+
unless sortable_fields.include? sort_field.to_sym
|
194
|
+
@errors.concat(JSONAPI::Exceptions::InvalidSortCriteria
|
195
|
+
.new(format_key(resource_klass._type), sort_field).errors)
|
181
196
|
end
|
182
197
|
end
|
183
198
|
|
184
|
-
def parse_add_operation(
|
185
|
-
|
186
|
-
|
187
|
-
if object_params_raw.is_a?(Array)
|
188
|
-
object_params_raw.each do |p|
|
199
|
+
def parse_add_operation(data)
|
200
|
+
if data.is_a?(Array)
|
201
|
+
data.each do |p|
|
189
202
|
@operations.push JSONAPI::CreateResourceOperation.new(@resource_klass,
|
190
|
-
parse_params(p,
|
203
|
+
parse_params(verify_and_remove_type(p),
|
204
|
+
@resource_klass.createable_fields(@context)))
|
191
205
|
end
|
192
206
|
else
|
193
207
|
@operations.push JSONAPI::CreateResourceOperation.new(@resource_klass,
|
194
|
-
parse_params(
|
208
|
+
parse_params(verify_and_remove_type(data),
|
209
|
+
@resource_klass.createable_fields(@context)))
|
195
210
|
end
|
196
|
-
rescue ActionController::ParameterMissing => e
|
197
|
-
@errors.concat(JSONAPI::Exceptions::ParameterMissing.new(e.param).errors)
|
198
211
|
rescue JSONAPI::Exceptions::Error => e
|
199
212
|
@errors.concat(e.errors)
|
200
213
|
end
|
201
214
|
|
202
|
-
def
|
203
|
-
#
|
204
|
-
if params[:
|
205
|
-
params
|
206
|
-
|
215
|
+
def verify_and_remove_type(params)
|
216
|
+
#remove type and verify it matches the resource
|
217
|
+
if params[:type] == @resource_klass._type.to_s
|
218
|
+
params.delete(:type)
|
219
|
+
else
|
220
|
+
if params[:type].nil?
|
221
|
+
raise JSONAPI::Exceptions::ParameterMissing.new(:type)
|
222
|
+
else
|
223
|
+
raise JSONAPI::Exceptions::InvalidResource.new(params[:type])
|
224
|
+
end
|
225
|
+
end
|
226
|
+
params
|
227
|
+
end
|
228
|
+
|
229
|
+
def parse_has_one_links_object(raw)
|
230
|
+
if raw.nil?
|
231
|
+
return {
|
232
|
+
type: nil,
|
233
|
+
id: nil
|
234
|
+
}
|
235
|
+
end
|
236
|
+
|
237
|
+
if !raw.is_a?(Hash) || raw.length != 2 || !(raw.has_key?('type') && raw.has_key?('id'))
|
238
|
+
raise JSONAPI::Exceptions::InvalidLinksObject.new(raw)
|
239
|
+
end
|
240
|
+
|
241
|
+
{
|
242
|
+
type: raw['type'],
|
243
|
+
id: raw['id']
|
244
|
+
}
|
245
|
+
end
|
246
|
+
|
247
|
+
def parse_has_many_links_object(raw)
|
248
|
+
if raw.nil?
|
249
|
+
raise JSONAPI::Exceptions::InvalidLinksObject.new(raw)
|
250
|
+
end
|
251
|
+
|
252
|
+
links_object = {}
|
253
|
+
if raw.is_a?(Hash)
|
254
|
+
if raw.length != 2 || !(raw.has_key?('type') && raw.has_key?('ids')) || !(raw['ids'].is_a?(Array))
|
255
|
+
raise JSONAPI::Exceptions::InvalidLinksObject.new(raw)
|
207
256
|
end
|
208
|
-
|
257
|
+
links_object[raw['type']] = raw['ids']
|
258
|
+
elsif raw.is_a?(Array)
|
259
|
+
raw.each do |link|
|
260
|
+
link_object = parse_has_one_links_object(link)
|
261
|
+
links_object[link_object[:type]] ||= []
|
262
|
+
links_object[link_object[:type]].push(link_object[:id])
|
263
|
+
end
|
264
|
+
else
|
265
|
+
raise JSONAPI::Exceptions::InvalidLinksObject.new(raw)
|
209
266
|
end
|
267
|
+
links_object
|
268
|
+
end
|
269
|
+
|
270
|
+
def parse_params(params, allowed_fields)
|
210
271
|
verify_permitted_params(params, allowed_fields)
|
211
272
|
|
212
273
|
checked_attributes = {}
|
@@ -214,21 +275,54 @@ module JSONAPI
|
|
214
275
|
checked_has_many_associations = {}
|
215
276
|
|
216
277
|
params.each do |key, value|
|
217
|
-
|
278
|
+
if key == 'links' || key == :links
|
279
|
+
value.each do |link_key, link_value|
|
280
|
+
param = unformat_key(link_key)
|
281
|
+
|
282
|
+
association = @resource_klass._association(param)
|
283
|
+
|
284
|
+
if association.is_a?(JSONAPI::Association::HasOne)
|
285
|
+
links_object = parse_has_one_links_object(link_value)
|
286
|
+
# Since we do not yet support polymorphic associations we will raise an error if the type does not match the
|
287
|
+
# association's type.
|
288
|
+
# ToDo: Support Polymorphic associations
|
289
|
+
if links_object[:type] && (links_object[:type] != association.type.to_s)
|
290
|
+
raise JSONAPI::Exceptions::TypeMismatch.new(links_object[:type])
|
291
|
+
end
|
218
292
|
|
219
|
-
|
293
|
+
unless links_object[:id].nil?
|
294
|
+
association_resource = @resource_klass.resource_for(@resource_klass.module_path + links_object[:type])
|
295
|
+
checked_has_one_associations[param] = association_resource.verify_key(links_object[:id], @context)
|
296
|
+
else
|
297
|
+
checked_has_one_associations[param] = nil
|
298
|
+
end
|
299
|
+
elsif association.is_a?(JSONAPI::Association::HasMany)
|
300
|
+
links_object = parse_has_many_links_object(link_value)
|
220
301
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
302
|
+
# Since we do not yet support polymorphic associations we will raise an error if the type does not match the
|
303
|
+
# association's type.
|
304
|
+
# ToDo: Support Polymorphic associations
|
305
|
+
|
306
|
+
if links_object.length == 0
|
307
|
+
checked_has_many_associations[param] = []
|
308
|
+
else
|
309
|
+
if links_object.length > 1 || !links_object.has_key?(association.type.to_s)
|
310
|
+
raise JSONAPI::Exceptions::TypeMismatch.new(links_object[:type])
|
311
|
+
end
|
312
|
+
|
313
|
+
links_object.each_pair do |type, keys|
|
314
|
+
association_resource = @resource_klass.resource_for(@resource_klass.module_path + type)
|
315
|
+
checked_has_many_associations[param] = association_resource.verify_keys(keys, @context)
|
316
|
+
end
|
317
|
+
end
|
318
|
+
else
|
319
|
+
# :nocov:
|
320
|
+
raise JSONAPI::Exceptions::InvalidLinksObject.new(key)
|
321
|
+
# :nocov:
|
322
|
+
end
|
229
323
|
end
|
230
|
-
checked_has_many_associations[param] = keys
|
231
324
|
else
|
325
|
+
param = unformat_key(key)
|
232
326
|
checked_attributes[param] = unformat_value(param, value)
|
233
327
|
end
|
234
328
|
end
|
@@ -237,7 +331,7 @@ module JSONAPI
|
|
237
331
|
'attributes' => checked_attributes,
|
238
332
|
'has_one' => checked_has_one_associations,
|
239
333
|
'has_many' => checked_has_many_associations
|
240
|
-
}.deep_transform_keys{ |key| unformat_key(key) }
|
334
|
+
}.deep_transform_keys { |key| unformat_key(key) }
|
241
335
|
end
|
242
336
|
|
243
337
|
def unformat_value(attribute, value)
|
@@ -246,67 +340,49 @@ module JSONAPI
|
|
246
340
|
end
|
247
341
|
|
248
342
|
def verify_permitted_params(params, allowed_fields)
|
249
|
-
formatted_allowed_fields = allowed_fields.collect {|field| format_key(field).to_sym}
|
343
|
+
formatted_allowed_fields = allowed_fields.collect { |field| format_key(field).to_sym }
|
250
344
|
params_not_allowed = []
|
251
|
-
params.
|
252
|
-
|
345
|
+
params.each do |key, value|
|
346
|
+
if key == 'links' || key == :links
|
347
|
+
value.each_key do |links_key|
|
348
|
+
params_not_allowed.push(links_key) unless formatted_allowed_fields.include?(links_key.to_sym)
|
349
|
+
end
|
350
|
+
else
|
351
|
+
params_not_allowed.push(key) unless formatted_allowed_fields.include?(key.to_sym)
|
352
|
+
end
|
253
353
|
end
|
254
354
|
raise JSONAPI::Exceptions::ParametersNotAllowed.new(params_not_allowed) if params_not_allowed.length > 0
|
255
355
|
end
|
256
356
|
|
257
|
-
def parse_add_association_operation(
|
258
|
-
association_type = params[:association]
|
259
|
-
|
260
|
-
parent_key = params[resource_klass._as_parent_key]
|
261
|
-
|
357
|
+
def parse_add_association_operation(data, association_type, parent_key)
|
262
358
|
association = resource_klass._association(association_type)
|
263
359
|
|
264
|
-
if association.is_a?(JSONAPI::Association::
|
265
|
-
|
266
|
-
|
267
|
-
if params[plural_association_type].nil?
|
268
|
-
raise ActionController::ParameterMissing.new(plural_association_type)
|
269
|
-
end
|
270
|
-
|
271
|
-
object_params = {links: {association_type => params[plural_association_type]}}
|
272
|
-
verified_param_set = parse_params(object_params, @resource_klass.updateable_fields(@context))
|
273
|
-
|
274
|
-
@operations.push JSONAPI::CreateHasOneAssociationOperation.new(resource_klass,
|
275
|
-
parent_key,
|
276
|
-
association_type,
|
277
|
-
verified_param_set[:has_one].values[0])
|
278
|
-
else
|
279
|
-
if params[association_type].nil?
|
280
|
-
raise ActionController::ParameterMissing.new(association_type)
|
281
|
-
end
|
360
|
+
if association.is_a?(JSONAPI::Association::HasMany)
|
361
|
+
ids = data.require(:ids)
|
362
|
+
type = data.require(:type)
|
282
363
|
|
283
|
-
object_params = {links: {
|
364
|
+
object_params = {links: {association.name => {'type' => type, 'ids' => ids}}}
|
284
365
|
verified_param_set = parse_params(object_params, @resource_klass.updateable_fields(@context))
|
285
366
|
|
286
367
|
@operations.push JSONAPI::CreateHasManyAssociationOperation.new(resource_klass,
|
287
368
|
parent_key,
|
288
369
|
association_type,
|
289
370
|
verified_param_set[:has_many].values[0])
|
371
|
+
else
|
372
|
+
# :nocov:
|
373
|
+
@errors.concat(JSONAPI::Exceptions::InvalidLinksObject.new(:data).errors)
|
374
|
+
# :nocov:
|
290
375
|
end
|
291
376
|
rescue ActionController::ParameterMissing => e
|
292
377
|
@errors.concat(JSONAPI::Exceptions::ParameterMissing.new(e.param).errors)
|
293
378
|
end
|
294
379
|
|
295
|
-
def parse_update_association_operation(
|
296
|
-
association_type = params[:association]
|
297
|
-
|
298
|
-
parent_key = params[resource_klass._as_parent_key]
|
299
|
-
|
380
|
+
def parse_update_association_operation(data, association_type, parent_key)
|
300
381
|
association = resource_klass._association(association_type)
|
301
382
|
|
302
383
|
if association.is_a?(JSONAPI::Association::HasOne)
|
303
|
-
|
304
|
-
|
305
|
-
if params[plural_association_type].nil?
|
306
|
-
raise ActionController::ParameterMissing.new(plural_association_type)
|
307
|
-
end
|
384
|
+
object_params = {links: {association.name => data}}
|
308
385
|
|
309
|
-
object_params = {links: {association_type => params[plural_association_type]}}
|
310
386
|
verified_param_set = parse_params(object_params, @resource_klass.updateable_fields(@context))
|
311
387
|
|
312
388
|
@operations.push JSONAPI::ReplaceHasOneAssociationOperation.new(resource_klass,
|
@@ -314,11 +390,7 @@ module JSONAPI
|
|
314
390
|
association_type,
|
315
391
|
verified_param_set[:has_one].values[0])
|
316
392
|
else
|
317
|
-
|
318
|
-
raise ActionController::ParameterMissing.new(association_type)
|
319
|
-
end
|
320
|
-
|
321
|
-
object_params = {links: {association_type => params[association_type]}}
|
393
|
+
object_params = {links: {association.name => data}}
|
322
394
|
verified_param_set = parse_params(object_params, @resource_klass.updateable_fields(@context))
|
323
395
|
|
324
396
|
@operations.push JSONAPI::ReplaceHasManyAssociationOperation.new(resource_klass,
|
@@ -326,43 +398,46 @@ module JSONAPI
|
|
326
398
|
association_type,
|
327
399
|
verified_param_set[:has_many].values[0])
|
328
400
|
end
|
329
|
-
rescue ActionController::ParameterMissing => e
|
330
|
-
@errors.concat(JSONAPI::Exceptions::ParameterMissing.new(e.param).errors)
|
331
401
|
end
|
332
402
|
|
333
|
-
def
|
334
|
-
|
403
|
+
def parse_single_replace_operation(data, keys)
|
404
|
+
if data[@resource_klass._primary_key].nil?
|
405
|
+
raise JSONAPI::Exceptions::MissingKey.new
|
406
|
+
end
|
407
|
+
|
408
|
+
type = data[:type]
|
409
|
+
if type.nil? || type != @resource_klass._type.to_s
|
410
|
+
raise JSONAPI::Exceptions::ParameterMissing.new(:type)
|
411
|
+
end
|
412
|
+
|
413
|
+
key = data[@resource_klass._primary_key]
|
414
|
+
if !keys.include?(key)
|
415
|
+
raise JSONAPI::Exceptions::KeyNotIncludedInURL.new(key)
|
416
|
+
end
|
417
|
+
|
418
|
+
if !keys.include?(@resource_klass._primary_key)
|
419
|
+
data.delete(:id)
|
420
|
+
end
|
421
|
+
|
422
|
+
@operations.push(JSONAPI::ReplaceFieldsOperation.new(@resource_klass,
|
423
|
+
key,
|
424
|
+
parse_params(verify_and_remove_type(data),
|
425
|
+
@resource_klass.updateable_fields(@context))))
|
426
|
+
end
|
335
427
|
|
336
|
-
|
337
|
-
if
|
338
|
-
if keys.count !=
|
428
|
+
def parse_replace_operation(data, keys)
|
429
|
+
if data.is_a?(Array)
|
430
|
+
if keys.count != data.count
|
339
431
|
raise JSONAPI::Exceptions::CountMismatch
|
340
432
|
end
|
341
433
|
|
342
|
-
|
343
|
-
|
344
|
-
raise JSONAPI::Exceptions::MissingKey.new
|
345
|
-
end
|
346
|
-
|
347
|
-
if !keys.include?(object_params[@resource_klass._primary_key])
|
348
|
-
raise JSONAPI::Exceptions::KeyNotIncludedInURL.new(object_params[@resource_klass._primary_key])
|
349
|
-
end
|
350
|
-
@operations.push JSONAPI::ReplaceFieldsOperation.new(@resource_klass,
|
351
|
-
object_params[@resource_klass._primary_key],
|
352
|
-
parse_params(object_params, @resource_klass.updateable_fields(@context)))
|
434
|
+
data.each do |object_params|
|
435
|
+
parse_single_replace_operation(object_params, keys)
|
353
436
|
end
|
354
437
|
else
|
355
|
-
|
356
|
-
raise JSONAPI::Exceptions::KeyNotIncludedInURL.new(object_params_raw[@resource_klass._primary_key])
|
357
|
-
end
|
358
|
-
|
359
|
-
@operations.push JSONAPI::ReplaceFieldsOperation.new(@resource_klass,
|
360
|
-
params[@resource_klass._primary_key],
|
361
|
-
parse_params(object_params_raw, @resource_klass.updateable_fields(@context)))
|
438
|
+
parse_single_replace_operation(data, [keys])
|
362
439
|
end
|
363
440
|
|
364
|
-
rescue ActionController::ParameterMissing => e
|
365
|
-
@errors.concat(JSONAPI::Exceptions::ParameterMissing.new(e.param).errors)
|
366
441
|
rescue JSONAPI::Exceptions::Error => e
|
367
442
|
@errors.concat(e.errors)
|
368
443
|
end
|
@@ -389,14 +464,14 @@ module JSONAPI
|
|
389
464
|
keys = parse_key_array(params[:keys])
|
390
465
|
keys.each do |key|
|
391
466
|
@operations.push JSONAPI::RemoveHasManyAssociationOperation.new(resource_klass,
|
392
|
-
|
393
|
-
|
394
|
-
|
467
|
+
parent_key,
|
468
|
+
association_type,
|
469
|
+
key)
|
395
470
|
end
|
396
471
|
else
|
397
472
|
@operations.push JSONAPI::RemoveHasOneAssociationOperation.new(resource_klass,
|
398
|
-
|
399
|
-
|
473
|
+
parent_key,
|
474
|
+
association_type)
|
400
475
|
end
|
401
476
|
end
|
402
477
|
|