jsonapi-resources 0.9.12 → 0.10.0.beta1

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