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