jsonapi_compliable 0.11.34 → 1.0.alpha.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +5 -5
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +1 -2
  4. data/Rakefile +7 -3
  5. data/jsonapi_compliable.gemspec +7 -3
  6. data/lib/generators/jsonapi/resource_generator.rb +8 -79
  7. data/lib/generators/jsonapi/templates/application_resource.rb.erb +2 -1
  8. data/lib/generators/jsonapi/templates/controller.rb.erb +19 -64
  9. data/lib/generators/jsonapi/templates/resource.rb.erb +5 -47
  10. data/lib/generators/jsonapi/templates/resource_reads_spec.rb.erb +62 -0
  11. data/lib/generators/jsonapi/templates/resource_writes_spec.rb.erb +63 -0
  12. data/lib/jsonapi_compliable.rb +87 -18
  13. data/lib/jsonapi_compliable/adapters/abstract.rb +202 -45
  14. data/lib/jsonapi_compliable/adapters/active_record.rb +6 -130
  15. data/lib/jsonapi_compliable/adapters/active_record/base.rb +247 -0
  16. data/lib/jsonapi_compliable/adapters/active_record/belongs_to_sideload.rb +17 -0
  17. data/lib/jsonapi_compliable/adapters/active_record/has_many_sideload.rb +17 -0
  18. data/lib/jsonapi_compliable/adapters/active_record/has_one_sideload.rb +17 -0
  19. data/lib/jsonapi_compliable/adapters/active_record/inferrence.rb +12 -0
  20. data/lib/jsonapi_compliable/adapters/active_record/many_to_many_sideload.rb +30 -0
  21. data/lib/jsonapi_compliable/adapters/null.rb +177 -6
  22. data/lib/jsonapi_compliable/base.rb +33 -320
  23. data/lib/jsonapi_compliable/context.rb +16 -0
  24. data/lib/jsonapi_compliable/deserializer.rb +14 -39
  25. data/lib/jsonapi_compliable/errors.rb +227 -24
  26. data/lib/jsonapi_compliable/extensions/extra_attribute.rb +3 -1
  27. data/lib/jsonapi_compliable/filter_operators.rb +25 -0
  28. data/lib/jsonapi_compliable/hash_renderer.rb +57 -0
  29. data/lib/jsonapi_compliable/query.rb +190 -202
  30. data/lib/jsonapi_compliable/rails.rb +12 -6
  31. data/lib/jsonapi_compliable/railtie.rb +64 -0
  32. data/lib/jsonapi_compliable/renderer.rb +60 -0
  33. data/lib/jsonapi_compliable/resource.rb +35 -663
  34. data/lib/jsonapi_compliable/resource/configuration.rb +239 -0
  35. data/lib/jsonapi_compliable/resource/dsl.rb +138 -0
  36. data/lib/jsonapi_compliable/resource/interface.rb +32 -0
  37. data/lib/jsonapi_compliable/resource/polymorphism.rb +68 -0
  38. data/lib/jsonapi_compliable/resource/sideloading.rb +102 -0
  39. data/lib/jsonapi_compliable/resource_proxy.rb +127 -0
  40. data/lib/jsonapi_compliable/responders.rb +19 -0
  41. data/lib/jsonapi_compliable/runner.rb +25 -0
  42. data/lib/jsonapi_compliable/scope.rb +37 -79
  43. data/lib/jsonapi_compliable/scoping/extra_attributes.rb +29 -0
  44. data/lib/jsonapi_compliable/scoping/filter.rb +39 -58
  45. data/lib/jsonapi_compliable/scoping/filterable.rb +9 -14
  46. data/lib/jsonapi_compliable/scoping/paginate.rb +9 -3
  47. data/lib/jsonapi_compliable/scoping/sort.rb +16 -4
  48. data/lib/jsonapi_compliable/sideload.rb +221 -347
  49. data/lib/jsonapi_compliable/sideload/belongs_to.rb +34 -0
  50. data/lib/jsonapi_compliable/sideload/has_many.rb +16 -0
  51. data/lib/jsonapi_compliable/sideload/has_one.rb +9 -0
  52. data/lib/jsonapi_compliable/sideload/many_to_many.rb +24 -0
  53. data/lib/jsonapi_compliable/sideload/polymorphic_belongs_to.rb +108 -0
  54. data/lib/jsonapi_compliable/stats/payload.rb +4 -8
  55. data/lib/jsonapi_compliable/types.rb +172 -0
  56. data/lib/jsonapi_compliable/util/attribute_check.rb +88 -0
  57. data/lib/jsonapi_compliable/util/persistence.rb +29 -7
  58. data/lib/jsonapi_compliable/util/relationship_payload.rb +4 -4
  59. data/lib/jsonapi_compliable/util/render_options.rb +4 -32
  60. data/lib/jsonapi_compliable/util/serializer_attributes.rb +98 -0
  61. data/lib/jsonapi_compliable/util/validation_response.rb +15 -9
  62. data/lib/jsonapi_compliable/version.rb +1 -1
  63. metadata +105 -24
  64. data/lib/generators/jsonapi/field_generator.rb +0 -0
  65. data/lib/generators/jsonapi/templates/create_request_spec.rb.erb +0 -29
  66. data/lib/generators/jsonapi/templates/destroy_request_spec.rb.erb +0 -20
  67. data/lib/generators/jsonapi/templates/index_request_spec.rb.erb +0 -22
  68. data/lib/generators/jsonapi/templates/payload.rb.erb +0 -39
  69. data/lib/generators/jsonapi/templates/serializer.rb.erb +0 -25
  70. data/lib/generators/jsonapi/templates/show_request_spec.rb.erb +0 -19
  71. data/lib/generators/jsonapi/templates/update_request_spec.rb.erb +0 -33
  72. data/lib/jsonapi_compliable/adapters/active_record_sideloading.rb +0 -152
  73. data/lib/jsonapi_compliable/scoping/extra_fields.rb +0 -58
@@ -0,0 +1,30 @@
1
+ module JsonapiCompliable
2
+ module Adapters
3
+ module ActiveRecord
4
+ class ManyToManySideload < Sideload::ManyToMany
5
+ def default_base_scope
6
+ resource_class.model.all
7
+ end
8
+
9
+ def through_table_name
10
+ @through_table_name ||= parent_resource_class.model
11
+ .reflections[through.to_s].klass.table_name
12
+ end
13
+
14
+ def scope(parent_ids)
15
+ base_scope
16
+ .includes(through)
17
+ .where(through_table_name => { true_foreign_key => parent_ids })
18
+ .distinct
19
+ end
20
+
21
+ def infer_foreign_key
22
+ parent_model = parent_resource_class.model
23
+ key = parent_model.reflections[name.to_s].options[:through]
24
+ value = parent_model.reflections[key.to_s].foreign_key.to_sym
25
+ { key => value }
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -4,11 +4,186 @@ module JsonapiCompliable
4
4
  # Useful when your customization does not support all possible
5
5
  # configuration (e.g. the service you hit does not support sorting)
6
6
  class Null < Abstract
7
- # (see Adapters::Abstract#filter)
8
- def filter(scope, attribute, value)
7
+ def filter_string_eq(scope, attribute, value)
9
8
  scope
10
9
  end
11
10
 
11
+ def filter_string_eql(scope, attribute, value)
12
+ scope
13
+ end
14
+
15
+ def filter_string_not_eq(scope, attribute, value)
16
+ scope
17
+ end
18
+
19
+ def filter_string_not_eql(scope, attribute, value)
20
+ scope
21
+ end
22
+
23
+ def filter_string_prefix_eq(scope, attribute, value)
24
+ scope
25
+ end
26
+
27
+ def filter_string_not_prefix_eq(scope, attribute, value)
28
+ scope
29
+ end
30
+
31
+ def filter_string_suffix_eq(scope, attribute, value)
32
+ scope
33
+ end
34
+
35
+ def filter_string_not_suffix_eq(scope, attribute, value)
36
+ scope
37
+ end
38
+
39
+ def filter_string_like_eq(scope, attribute, value)
40
+ scope
41
+ end
42
+
43
+ def filter_string_not_like_eq(scope, attribute, value)
44
+ scope
45
+ end
46
+
47
+ def filter_integer_eq(scope, attribute, value)
48
+ scope
49
+ end
50
+
51
+ def filter_integer_not_eq(scope, attribute, value)
52
+ scope
53
+ end
54
+
55
+ def filter_integer_gt(scope, attribute, value)
56
+ scope
57
+ end
58
+
59
+ def filter_integer_gte(scope, attribute, value)
60
+ scope
61
+ end
62
+
63
+ def filter_integer_lt(scope, attribute, value)
64
+ scope
65
+ end
66
+
67
+ def filter_integer_lte(scope, attribute, value)
68
+ scope
69
+ end
70
+
71
+ def filter_datetime_eq(scope, attribute, value)
72
+ scope
73
+ end
74
+
75
+ def filter_datetime_not_eq(scope, attribute, value)
76
+ scope
77
+ end
78
+
79
+ def filter_datetime_lte(scope, attribute, value)
80
+ scope
81
+ end
82
+
83
+ def filter_float_eq(scope, attribute, value)
84
+ scope
85
+ end
86
+
87
+ def filter_float_not_eq(scope, attribute, value)
88
+ scope
89
+ end
90
+
91
+ def filter_float_gt(scope, attribute, value)
92
+ scope
93
+ end
94
+
95
+ def filter_float_gte(scope, attribute, value)
96
+ scope
97
+ end
98
+
99
+ def filter_float_lt(scope, attribute, value)
100
+ scope
101
+ end
102
+
103
+ def filter_float_lte(scope, attribute, value)
104
+ scope
105
+ end
106
+
107
+ def filter_decimal_eq(scope, attribute, value)
108
+ scope
109
+ end
110
+
111
+ def filter_decimal_not_eq(scope, attribute, value)
112
+ scope
113
+ end
114
+
115
+ def filter_decimal_gt(scope, attribute, value)
116
+ scope
117
+ end
118
+
119
+ def filter_decimal_gte(scope, attribute, value)
120
+ scope
121
+ end
122
+
123
+ def filter_decimal_lt(scope, attribute, value)
124
+ scope
125
+ end
126
+
127
+ def filter_decimal_lte(scope, attribute, value)
128
+ scope
129
+ end
130
+
131
+ def filter_datetime_eq(scope, attribute, value)
132
+ scope
133
+ end
134
+
135
+ def filter_datetime_not_eq(scope, attribute, value)
136
+ scope
137
+ end
138
+
139
+ def filter_datetime_gt(scope, attribute, value)
140
+ scope
141
+ end
142
+
143
+ def filter_datetime_gte(scope, attribute, value)
144
+ scope
145
+ end
146
+
147
+ def filter_datetime_lt(scope, attribute, value)
148
+ scope
149
+ end
150
+
151
+ def filter_datetime_lte(scope, attribute, value)
152
+ scope
153
+ end
154
+
155
+ def filter_date_eq(scope, attribute, value)
156
+ scope
157
+ end
158
+
159
+ def filter_date_not_eq(scope, attribute, value)
160
+ scope
161
+ end
162
+
163
+ def filter_date_gt(scope, attribute, value)
164
+ scope
165
+ end
166
+
167
+ def filter_date_gte(scope, attribute, value)
168
+ scope
169
+ end
170
+
171
+ def filter_date_lt(scope, attribute, value)
172
+ scope
173
+ end
174
+
175
+ def filter_date_lte(scope, attribute, value)
176
+ scope
177
+ end
178
+
179
+ def filter_boolean_eq(scope, attribute, value)
180
+ scope
181
+ end
182
+
183
+ def base_scope(model)
184
+ raise 'Null adapter has no base scope!'
185
+ end
186
+
12
187
  # (see Adapters::Abstract#order)
13
188
  def order(scope, attribute, direction)
14
189
  scope
@@ -56,10 +231,6 @@ module JsonapiCompliable
56
231
  def resolve(scope)
57
232
  scope
58
233
  end
59
-
60
- # (see Adapters::Abstract#associate)
61
- def associate(parent, child, association_name, association_type)
62
- end
63
234
  end
64
235
  end
65
236
  end
@@ -1,357 +1,70 @@
1
1
  module JsonapiCompliable
2
- # Provides main interface to jsonapi_compliable
3
- #
4
- # This gets mixed in to a "context" class, such as a Rails controller.
5
2
  module Base
6
3
  extend ActiveSupport::Concern
7
4
 
8
- included do
9
- class << self
10
- attr_accessor :_jsonapi_compliable, :_sideload_whitelist
11
- end
12
-
13
- def self.inherited(klass)
14
- super
15
- klass._jsonapi_compliable = Class.new(_jsonapi_compliable)
16
- klass._sideload_whitelist = _sideload_whitelist.dup if _sideload_whitelist
17
- end
18
- end
19
-
20
- # @!classmethods
21
- module ClassMethods
22
- # Define your JSONAPI configuration
23
- #
24
- # @example Inline Resource
25
- # # 'Quick and Dirty' solution that does not require a separate
26
- # # Resource object
27
- # class PostsController < ApplicationController
28
- # jsonapi do
29
- # type :posts
30
- # use_adapter JsonapiCompliable::Adapters::ActiveRecord
31
- #
32
- # allow_filter :title
33
- # end
34
- # end
35
- #
36
- # @example Resource Class (preferred)
37
- # # Make code reusable by encapsulating it in a Resource class
38
- # class PostsController < ApplicationController
39
- # jsonapi resource: PostResource
40
- # end
41
- #
42
- # @see Resource
43
- # @param resource [Resource] the Resource class associated to this endpoint
44
- # @return [void]
45
- def jsonapi(foo = 'bar', resource: nil, &blk)
46
- if resource
47
- self._jsonapi_compliable = resource
48
- else
49
- if !self._jsonapi_compliable
50
- self._jsonapi_compliable = Class.new(JsonapiCompliable::Resource)
51
- end
52
- end
53
-
54
- self._jsonapi_compliable.class_eval(&blk) if blk
55
- end
56
-
57
- # Set the sideload whitelist. You may want to omit sideloads for
58
- # security or performance reasons.
59
- #
60
- # Uses JSONAPI::IncludeDirective from {{http://jsonapi-rb.org jsonapi-rb}}
61
- #
62
- # @example Whitelisting Relationships
63
- # # Given the following whitelist
64
- # class PostsController < ApplicationResource
65
- # jsonapi resource: MyResource
66
- #
67
- # sideload_whitelist({
68
- # index: [:blog],
69
- # show: [:blog, { comments: :author }]
70
- # })
71
- #
72
- # # ... code ...
73
- # end
74
- #
75
- # # A request to sideload 'tags'
76
- # #
77
- # # GET /posts/1?include=tags
78
- # #
79
- # # ...will silently fail.
80
- # #
81
- # # A request for comments and tags:
82
- # #
83
- # # GET /posts/1?include=tags,comments
84
- # #
85
- # # ...will only sideload comments
86
- #
87
- # @param [Hash, Array, Symbol] whitelist
88
- # @see Query#include_hash
89
- def sideload_whitelist(hash)
90
- self._sideload_whitelist = JSONAPI::IncludeDirective.new(hash).to_hash
91
- end
92
- end
93
-
94
- # @api private
95
- def sideload_whitelist
96
- self.class._sideload_whitelist || {}
97
- end
98
-
99
- # Returns an instance of the associated Resource
100
- #
101
- # In other words, if you configured your controller as:
102
- #
103
- # jsonapi resource: MyResource
104
- #
105
- # This returns MyResource.new
106
- #
107
- # @return [Resource] the configured Resource for this controller
108
5
  def jsonapi_resource
109
- @jsonapi_resource ||= begin
110
- resource = self.class._jsonapi_compliable
111
- if resource.is_a?(Hash)
112
- resource[action_name.to_sym].new
113
- else
114
- resource.new
115
- end
116
- end
6
+ @jsonapi_resource
117
7
  end
118
8
 
119
- # Instantiates the relevant Query object
120
- #
121
- # @see Query
122
- # @return [Query] the Query object for this resource/params
123
9
  def query
124
10
  @query ||= Query.new(jsonapi_resource, params)
125
11
  end
126
12
 
127
- # @see Query#to_hash
128
- # @return [Hash] the normalized query hash for only the *current* resource
129
13
  def query_hash
130
- @query_hash ||= query.to_hash[jsonapi_resource.type]
14
+ @query_hash ||= query.to_hash
131
15
  end
132
16
 
133
- # Tracks the current context so we can refer to it within any
134
- # random object. Helpful for easy-access to things like the current
135
- # user.
136
- #
137
- # @api private
138
- # @yieldreturn Code to run within the current context
139
17
  def wrap_context
140
- jsonapi_resource.with_context(jsonapi_context, action_name.to_sym) do
18
+ JsonapiCompliable.with_context(jsonapi_context, action_name.to_sym) do
141
19
  yield
142
20
  end
143
21
  end
144
22
 
145
- # Override if you'd like the "context" to be something other than
146
- # an instance of this class.
147
- # In Rails, context is the controller instance.
148
- #
149
- # @example Overriding
150
- # # within your controller
151
- # def jsonapi_context
152
- # current_user
153
- # end
154
- #
155
- # # within a resource
156
- # default_filter :by_user do |scope, context|
157
- # scope.where(user_id: context.id)
158
- # end
159
- #
160
- # @return the context object you'd like
161
23
  def jsonapi_context
162
24
  self
163
25
  end
164
26
 
165
- # Use when direct, low-level access to the scope is required.
166
- #
167
- # @example Show Action
168
- # # Scope#resolve returns an array, but we only want to render
169
- # # one object, not an array
170
- # scope = jsonapi_scope(Employee.where(id: params[:id]))
171
- # render_jsonapi(scope.resolve.first, scope: false)
172
- #
173
- # @example Scope Chaining
174
- # # Chain onto scope after running through typical DSL
175
- # # Here, we'll add active: true to our hash if the user
176
- # # is filtering on something
177
- # scope = jsonapi_scope({})
178
- # scope.object.merge!(active: true) if scope.object[:filter]
179
- #
180
- # @see Resource#build_scope
181
- # @return [Scope] the configured scope
182
27
  def jsonapi_scope(scope, opts = {})
183
28
  jsonapi_resource.build_scope(scope, query, opts)
184
29
  end
185
30
 
186
- # @see Deserializer#initialize
187
- # @return [Deserializer]
188
- def deserialized_params
189
- @deserialized_params ||= JsonapiCompliable::Deserializer.new(params, request.env)
190
- end
191
-
192
- # Create the resource model and process all nested relationships via the
193
- # serialized parameters. Any error, including validation errors, will roll
194
- # back the transaction.
195
- #
196
- # @example Basic Rails
197
- # # Example Resource must have 'model'
198
- # #
199
- # # class PostResource < ApplicationResource
200
- # # model Post
201
- # # end
202
- # def create
203
- # post, success = jsonapi_create.to_a
204
- #
205
- # if success
206
- # render_jsonapi(post, scope: false)
207
- # else
208
- # render_errors_for(post)
209
- # end
210
- # end
211
- #
212
- # @see Resource.model
213
- # @see #resource
214
- # @see #deserialized_params
215
- # @return [Util::ValidationResponse]
216
- def jsonapi_create
217
- _persist do
218
- jsonapi_resource.persist_with_relationships \
219
- deserialized_params.meta,
220
- deserialized_params.attributes,
221
- deserialized_params.relationships
31
+ def normalized_params
32
+ normalized = params
33
+ if normalized.respond_to?(:to_unsafe_h)
34
+ normalized = normalized.to_unsafe_h.deep_symbolize_keys
222
35
  end
36
+ normalized
223
37
  end
224
38
 
225
- # Update the resource model and process all nested relationships via the
226
- # serialized parameters. Any error, including validation errors, will roll
227
- # back the transaction.
228
- #
229
- # @example Basic Rails
230
- # # Example Resource must have 'model'
231
- # #
232
- # # class PostResource < ApplicationResource
233
- # # model Post
234
- # # end
235
- # def update
236
- # post, success = jsonapi_update.to_a
237
- #
238
- # if success
239
- # render_jsonapi(post, scope: false)
240
- # else
241
- # render_errors_for(post)
242
- # end
243
- # end
244
- #
245
- # @see #jsonapi_create
246
- # @return [Util::ValidationResponse]
247
- def jsonapi_update
248
- _persist do
249
- jsonapi_resource.persist_with_relationships \
250
- deserialized_params.meta,
251
- deserialized_params.attributes,
252
- deserialized_params.relationships
253
- end
254
- end
255
-
256
- # Delete the model
257
- # Any error, including validation errors, will roll back the transaction.
258
- #
259
- # Note: +before_commit+ hooks still run unless excluded
260
- #
261
- # @return [Util::ValidationResponse]
262
- def jsonapi_destroy
263
- jsonapi_resource.transaction do
264
- model = jsonapi_resource.destroy(params[:id])
265
- validator = ::JsonapiCompliable::Util::ValidationResponse.new \
266
- model, deserialized_params
267
- validator.validate!
268
- jsonapi_resource.before_commit(model, :destroy)
269
- validator
270
- end
271
- end
272
-
273
- # Similar to +render :json+ or +render :jsonapi+
274
- #
275
- # By default, this will "build" the scope via +#jsonapi_scope+. To avoid
276
- # this, pass +scope: false+
277
- #
278
- # This builds relevant options and sends them to
279
- # +JSONAPI::Serializable::SuccessRenderer#render+from
280
- # {http://jsonapi-rb.org jsonapi-rb}
281
- #
282
- # @example Build Scope by Default
283
- # # Employee.all returns an ActiveRecord::Relation. No SQL is fired at this point.
284
- # # We further 'chain' onto this scope, applying pagination, sorting,
285
- # # filters, etc that the user has requested.
286
- # def index
287
- # employees = Employee.all
288
- # render_jsonapi(employees)
289
- # end
290
- #
291
- # @example Avoid Building Scope by Default
292
- # # Maybe we already manually scoped, and don't want to fire the logic twice
293
- # # This code is equivalent to the above example
294
- # def index
295
- # scope = jsonapi_scope(Employee.all)
296
- # # ... do other things with the scope ...
297
- # render_jsonapi(scope.resolve, scope: false)
298
- # end
299
- #
300
- # @param scope [Scope, Object] the scope to build or render.
301
- # @param [Hash] opts the render options passed to {http://jsonapi-rb.org jsonapi-rb}
302
- # @option opts [Boolean] :scope Default: true. Should we call #jsonapi_scope on this object?
303
- # @see #jsonapi_scope
304
- def render_jsonapi(scope, opts = {})
305
- scope = jsonapi_scope(scope) unless opts[:scope] == false || scope.is_a?(JsonapiCompliable::Scope)
306
- opts = default_jsonapi_render_options.merge(opts)
307
- opts = Util::RenderOptions.generate(scope, query_hash, opts)
308
- opts[:expose][:context] = self
309
-
310
- if opts[:include].empty? && force_includes?
311
- opts[:include] = deserialized_params.include_directive
312
- end
313
-
314
- perform_render_jsonapi(opts)
315
- end
316
-
317
- # Define a hash that will be automatically merged into your
318
- # render_jsonapi call
319
- #
320
- # @example
321
- # # this
322
- # render_jsonapi(foo)
323
- # # is equivalent to this
324
- # render jsonapi: foo, default_jsonapi_render_options
325
- #
326
- # @see #render_jsonapi
327
- # @return [Hash] the options hash you define
328
- def default_jsonapi_render_options
329
- {}.tap do |options|
330
- end
331
- end
332
-
333
- private
334
-
335
- def _persist
336
- jsonapi_resource.transaction do
337
- ::JsonapiCompliable::Util::Hooks.record do
338
- model = yield
339
- validator = ::JsonapiCompliable::Util::ValidationResponse.new \
340
- model, deserialized_params
341
- validator.validate!
342
- validator
39
+ def deserialized_params
40
+ @deserialized_params ||= begin
41
+ payload = normalized_params
42
+ if payload[:data] && payload[:data][:type]
43
+ JsonapiCompliable::Deserializer.new(payload)
343
44
  end
344
45
  end
345
46
  end
346
47
 
347
- def force_includes?
348
- not (deserialized_params.data.nil? || deserialized_params.data.empty?)
349
- end
350
-
351
- def perform_render_jsonapi(opts)
352
- # TODO(beauby): Reuse renderer.
353
- JSONAPI::Serializable::Renderer.new
354
- .render(opts.delete(:jsonapi), opts).to_json
48
+ def jsonapi_render_options
49
+ options = {}
50
+ options.merge!(default_jsonapi_render_options)
51
+ options[:meta] ||= {}
52
+ options[:expose] ||= {}
53
+ options[:expose][:context] = jsonapi_context
54
+ options
55
+ end
56
+
57
+ def proxy(base = nil, opts = {})
58
+ base ||= jsonapi_resource.base_scope
59
+ scope_opts = opts.slice :sideload_parent_length,
60
+ :default_paginate, :after_resolve
61
+ scope = jsonapi_scope(base, scope_opts)
62
+ ResourceProxy.new jsonapi_resource,
63
+ scope,
64
+ query,
65
+ payload: deserialized_params,
66
+ single: opts[:single],
67
+ raise_on_missing: opts[:raise_on_missing]
355
68
  end
356
69
  end
357
70
  end