jsonapi-resources 0.9.0 → 0.9.12

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a367b0f58fd1570cdc45f28570f440ab2b68c1c7
4
- data.tar.gz: 48fdfb4691776dea1491b237c7f9fc1eca401072
2
+ SHA256:
3
+ metadata.gz: bfb7891ac7686be582e0831afd808f08504e492998968e704de2086c1656e9af
4
+ data.tar.gz: edeb27a90bec0a6fc79f67e0311cdf7e588fdcf347426489c21d94bcd40a3f28
5
5
  SHA512:
6
- metadata.gz: a1664c0f610a4baaa22c80642575cdb177910476d0d3154bf259902e24b27261ee26b2f385820a360bb50a229a5e74c04ed55fd69f6507ab3596db6cf5324cc9
7
- data.tar.gz: b372447e19a732fadcdb1154894f963460187a2b92e5481df411b798b27347808f4d79f90c51f49b6198a95cd7c1e2d05901ca5a42463064ee89340c80ffa4ad
6
+ metadata.gz: 645534a3b1811e939fb6e36098e7624072793b59a39eecc99b05743ff77e91289ca6b35441c614220833049b689f8bcf7b6b6fb9fb1f09190b239b706b3d2da7
7
+ data.tar.gz: 7b562179f5f6ab17179c71208838fccaa1703d6c88eb99356075daa255a57242f59972e5ba0118740d4d80f7bc5706f2f87b4e99b0840cca32d00b19056f5712
@@ -125,7 +125,8 @@ module JSONAPI
125
125
  base_url: base_url,
126
126
  key_formatter: key_formatter,
127
127
  route_formatter: route_formatter,
128
- serialization_options: serialization_options
128
+ serialization_options: serialization_options,
129
+ controller: self
129
130
  )
130
131
  @resource_serializer
131
132
  end
@@ -234,7 +235,7 @@ module JSONAPI
234
235
  end
235
236
 
236
237
  render_options[:location] = content[:data]["links"][:self] if (
237
- response_doc.status == :created && content[:data].class != Array
238
+ response_doc.status == :created && content[:data].class != Array && content[:data]["links"]
238
239
  )
239
240
 
240
241
  # For whatever reason, `render` ignores :status and :content_type when :body is set.
@@ -8,6 +8,7 @@ module JSONAPI
8
8
  :resource_key_type,
9
9
  :route_format,
10
10
  :raise_if_parameters_not_allowed,
11
+ :warn_on_missing_routes,
11
12
  :allow_include,
12
13
  :allow_sort,
13
14
  :allow_filter,
@@ -32,7 +33,8 @@ module JSONAPI
32
33
  :resource_cache,
33
34
  :default_resource_cache_field,
34
35
  :resource_cache_digest_function,
35
- :resource_cache_usage_report_function
36
+ :resource_cache_usage_report_function,
37
+ :default_exclude_links
36
38
 
37
39
  def initialize
38
40
  #:underscored_key, :camelized_key, :dasherized_key, or custom
@@ -51,6 +53,8 @@ module JSONAPI
51
53
 
52
54
  self.raise_if_parameters_not_allowed = true
53
55
 
56
+ self.warn_on_missing_routes = true
57
+
54
58
  # :none, :offset, :paged, or a custom paginator name
55
59
  self.default_paginator = :none
56
60
 
@@ -131,6 +135,12 @@ module JSONAPI
131
135
  # Optionally provide a callable which JSONAPI will call with information about cache
132
136
  # performance. Should accept three arguments: resource name, hits count, misses count.
133
137
  self.resource_cache_usage_report_function = nil
138
+
139
+ # Global configuration for links exclusion
140
+ # Controls whether to generate links like `self`, `related` with all the resources
141
+ # and relationships. Accepts either `:default`, `:none`, or array containing the
142
+ # specific default links to exclude, which may be `:self` and `:related`.
143
+ self.default_exclude_links = :none
134
144
  end
135
145
 
136
146
  def cache_formatters=(bool)
@@ -235,6 +245,8 @@ module JSONAPI
235
245
 
236
246
  attr_writer :raise_if_parameters_not_allowed
237
247
 
248
+ attr_writer :warn_on_missing_routes
249
+
238
250
  attr_writer :use_relationship_reflection
239
251
 
240
252
  attr_writer :resource_cache
@@ -244,6 +256,8 @@ module JSONAPI
244
256
  attr_writer :resource_cache_digest_function
245
257
 
246
258
  attr_writer :resource_cache_usage_report_function
259
+
260
+ attr_writer :default_exclude_links
247
261
  end
248
262
 
249
263
  class << self
@@ -364,24 +364,21 @@ module JSONAPI
364
364
  end
365
365
  end
366
366
 
367
- class ParametersNotAllowed < Error
368
- attr_accessor :params
367
+ class ParameterNotAllowed < Error
368
+ attr_accessor :param
369
369
 
370
- def initialize(params, error_object_overrides = {})
371
- @params = params
370
+ def initialize(param, error_object_overrides = {})
371
+ @param = param
372
372
  super(error_object_overrides)
373
373
  end
374
374
 
375
375
  def errors
376
- params.collect do |param|
377
- create_error_object(code: JSONAPI::PARAM_NOT_ALLOWED,
378
- status: :bad_request,
379
- title: I18n.translate('jsonapi-resources.exceptions.parameters_not_allowed.title',
380
- default: 'Param not allowed'),
381
- detail: I18n.translate('jsonapi-resources.exceptions.parameters_not_allowed.detail',
382
- default: "#{param} is not allowed.", param: param))
383
-
384
- end
376
+ [create_error_object(code: JSONAPI::PARAM_NOT_ALLOWED,
377
+ status: :bad_request,
378
+ title: I18n.translate('jsonapi-resources.exceptions.parameter_not_allowed.title',
379
+ default: 'Param not allowed'),
380
+ detail: I18n.translate('jsonapi-resources.exceptions.parameter_not_allowed.detail',
381
+ default: "#{param} is not allowed.", param: param))]
385
382
  end
386
383
  end
387
384
 
@@ -451,11 +448,12 @@ module JSONAPI
451
448
  end
452
449
 
453
450
  class ValidationErrors < Error
454
- attr_reader :error_messages, :error_metadata, :resource_relationships
451
+ attr_reader :error_messages, :error_metadata, :resource_relationships, :resource_class
455
452
 
456
453
  def initialize(resource, error_object_overrides = {})
457
454
  @error_messages = resource.model_error_messages
458
455
  @error_metadata = resource.validation_error_metadata
456
+ @resource_class = resource.class
459
457
  @resource_relationships = resource.class._relationships.keys
460
458
  @key_formatter = JSONAPI.configuration.key_formatter
461
459
  super(error_object_overrides)
@@ -477,7 +475,7 @@ module JSONAPI
477
475
  create_error_object(code: JSONAPI::VALIDATION_ERROR,
478
476
  status: :unprocessable_entity,
479
477
  title: message,
480
- detail: "#{format_key(attr_key)} - #{message}",
478
+ detail: detail(attr_key, message),
481
479
  source: { pointer: pointer(attr_key) },
482
480
  meta: metadata_for(attr_key, message))
483
481
  end
@@ -487,7 +485,12 @@ module JSONAPI
487
485
  error_metadata[attr_key] ? error_metadata[attr_key][message] : nil
488
486
  end
489
487
 
488
+ def detail(attr_key, message)
489
+ general_error?(attr_key) ? message : "#{format_key(attr_key)} - #{message}"
490
+ end
491
+
490
492
  def pointer(attr_or_relationship_name)
493
+ return '/data' if general_error?(attr_or_relationship_name)
491
494
  formatted_attr_or_relationship_name = format_key(attr_or_relationship_name)
492
495
  if resource_relationships.include?(attr_or_relationship_name)
493
496
  "/data/relationships/#{formatted_attr_or_relationship_name}"
@@ -495,6 +498,10 @@ module JSONAPI
495
498
  "/data/attributes/#{formatted_attr_or_relationship_name}"
496
499
  end
497
500
  end
501
+
502
+ def general_error?(attr_key)
503
+ attr_key.to_sym == :base && !resource_class._has_attribute?(attr_key)
504
+ end
498
505
  end
499
506
 
500
507
  class SaveFailed < Error
@@ -40,6 +40,16 @@ module JSONAPI
40
40
  delve_paths(get_includes(@include_directives_hash, false))
41
41
  end
42
42
 
43
+ def merge_filter(relation, filter)
44
+ config = include_config(relation.to_sym)
45
+ config[:include_filters] ||= {}
46
+ config[:include_filters].merge!(filter)
47
+ end
48
+
49
+ def include_config(relation)
50
+ @include_directives_hash[:include_related][relation]
51
+ end
52
+
43
53
  private
44
54
 
45
55
  def get_related(current_path)
@@ -52,7 +62,7 @@ module JSONAPI
52
62
  current_relationship = current_resource_klass._relationships[fragment]
53
63
  current_resource_klass = current_relationship.try(:resource_klass)
54
64
  else
55
- warn "[RELATIONSHIP NOT FOUND] Relationship could not be found for #{current_path}."
65
+ raise JSONAPI::Exceptions::InvalidInclude.new(current_resource_klass, current_path)
56
66
  end
57
67
 
58
68
  include_in_join = @force_eager_load || !current_relationship || current_relationship.eager_load_on_include
@@ -3,107 +3,100 @@ module JSONAPI
3
3
  attr_reader :base_url,
4
4
  :primary_resource_klass,
5
5
  :route_formatter,
6
- :engine_name
6
+ :engine,
7
+ :engine_mount_point,
8
+ :url_helpers
9
+
10
+ @@url_helper_methods = {}
7
11
 
8
12
  def initialize(config = {})
9
- @base_url = config[:base_url]
13
+ @base_url = config[:base_url]
10
14
  @primary_resource_klass = config[:primary_resource_klass]
11
- @route_formatter = config[:route_formatter]
12
- @engine_name = build_engine_name
15
+ @route_formatter = config[:route_formatter]
16
+ @engine = build_engine
17
+ @engine_mount_point = @engine ? @engine.routes.find_script_name({}) : ""
13
18
 
14
- # Warning: These make LinkBuilder non-thread-safe. That's not a problem with the
15
- # request-specific way it's currently used, though.
16
- @resources_path_cache = JSONAPI::NaiveCache.new do |source_klass|
17
- formatted_module_path_from_class(source_klass) + format_route(source_klass._type.to_s)
18
- end
19
+ # url_helpers may be either a controller which has the route helper methods, or the application router's
20
+ # url helpers module, `Rails.application.routes.url_helpers`. Because the method no longer behaves as a
21
+ # singleton, and it's expensive to generate the module, the controller is preferred.
22
+ @url_helpers = config[:url_helpers]
19
23
  end
20
24
 
21
25
  def engine?
22
- !!@engine_name
26
+ !!@engine
23
27
  end
24
28
 
25
29
  def primary_resources_url
26
- if engine?
27
- engine_primary_resources_url
30
+ if @primary_resource_klass._routed
31
+ primary_resources_path = resources_path(primary_resource_klass)
32
+ @primary_resources_url_cached ||= "#{ base_url }#{ serialized_engine_mount_point }#{ primary_resources_path }"
28
33
  else
29
- regular_primary_resources_url
34
+ if JSONAPI.configuration.warn_on_missing_routes && !@primary_resource_klass._warned_missing_route
35
+ warn "primary_resources_url for #{@primary_resource_klass} could not be generated"
36
+ @primary_resource_klass._warned_missing_route = true
37
+ end
38
+ nil
30
39
  end
31
40
  end
32
41
 
33
42
  def query_link(query_params)
34
- "#{ primary_resources_url }?#{ query_params.to_query }"
43
+ url = primary_resources_url
44
+ return url if url.nil?
45
+ "#{ url }?#{ query_params.to_query }"
35
46
  end
36
47
 
37
48
  def relationships_related_link(source, relationship, query_params = {})
38
- url = "#{ self_link(source) }/#{ route_for_relationship(relationship) }"
39
- url = "#{ url }?#{ query_params.to_query }" if query_params.present?
40
- url
49
+ if relationship._routed
50
+ url = "#{ self_link(source) }/#{ route_for_relationship(relationship) }"
51
+ url = "#{ url }?#{ query_params.to_query }" if query_params.present?
52
+ url
53
+ else
54
+ if JSONAPI.configuration.warn_on_missing_routes && !relationship._warned_missing_route
55
+ warn "related_link for #{relationship} could not be generated"
56
+ relationship._warned_missing_route = true
57
+ end
58
+ nil
59
+ end
41
60
  end
42
61
 
43
62
  def relationships_self_link(source, relationship)
44
- "#{ self_link(source) }/relationships/#{ route_for_relationship(relationship) }"
63
+ if relationship._routed
64
+ "#{ self_link(source) }/relationships/#{ route_for_relationship(relationship) }"
65
+ else
66
+ if JSONAPI.configuration.warn_on_missing_routes && !relationship._warned_missing_route
67
+ warn "self_link for #{relationship} could not be generated"
68
+ relationship._warned_missing_route = true
69
+ end
70
+ nil
71
+ end
45
72
  end
46
73
 
47
74
  def self_link(source)
48
- if engine?
49
- engine_resource_url(source)
75
+ if source.class._routed
76
+ resource_url(source)
50
77
  else
51
- regular_resource_url(source)
78
+ if JSONAPI.configuration.warn_on_missing_routes && !source.class._warned_missing_route
79
+ warn "self_link for #{source.class} could not be generated"
80
+ source.class._warned_missing_route = true
81
+ end
82
+ nil
52
83
  end
53
84
  end
54
85
 
55
86
  private
56
87
 
57
- def build_engine_name
88
+ def build_engine
58
89
  scopes = module_scopes_from_class(primary_resource_klass)
59
90
 
60
91
  begin
61
92
  unless scopes.empty?
62
93
  "#{ scopes.first.to_s.camelize }::Engine".safe_constantize
63
94
  end
64
- rescue LoadError => e
65
- nil
66
- end
67
- end
68
-
69
- def engine_path_from_resource_class(klass)
70
- path_name = engine_resources_path_name_from_class(klass)
71
- engine_name.routes.url_helpers.public_send(path_name)
72
- end
73
95
 
74
- def engine_primary_resources_path
75
- engine_path_from_resource_class(primary_resource_klass)
76
- end
77
-
78
- def engine_primary_resources_url
79
- "#{ base_url }#{ engine_primary_resources_path }"
80
- end
81
-
82
- def engine_resource_path(source)
83
- resource_path_name = engine_resource_path_name_from_source(source)
84
- engine_name.routes.url_helpers.public_send(resource_path_name, source.id)
85
- end
86
-
87
- def engine_resource_path_name_from_source(source)
88
- scopes = module_scopes_from_class(source.class)[1..-1]
89
- base_path_name = scopes.map { |scope| scope.underscore }.join("_")
90
- end_path_name = source.class._type.to_s.singularize
91
- [base_path_name, end_path_name, "path"].reject(&:blank?).join("_")
92
- end
93
-
94
- def engine_resource_url(source)
95
- "#{ base_url }#{ engine_resource_path(source) }"
96
- end
97
-
98
- def engine_resources_path_name_from_class(klass)
99
- scopes = module_scopes_from_class(klass)[1..-1]
100
- base_path_name = scopes.map { |scope| scope.underscore }.join("_")
101
- end_path_name = klass._type.to_s
102
-
103
- if base_path_name.blank?
104
- "#{ end_path_name }_path"
105
- else
106
- "#{ base_path_name }_#{ end_path_name }_path"
96
+ # :nocov:
97
+ rescue LoadError => _e
98
+ nil
99
+ # :nocov:
107
100
  end
108
101
  end
109
102
 
@@ -112,12 +105,19 @@ module JSONAPI
112
105
  end
113
106
 
114
107
  def formatted_module_path_from_class(klass)
115
- scopes = module_scopes_from_class(klass)
108
+ @_module_path_cache ||= {}
109
+ @_module_path_cache[klass] ||= begin
110
+ scopes = if @engine
111
+ module_scopes_from_class(klass)[1..-1]
112
+ else
113
+ module_scopes_from_class(klass)
114
+ end
116
115
 
117
- unless scopes.empty?
118
- "/#{ scopes.map{ |scope| format_route(scope.to_s.underscore) }.join('/') }/"
119
- else
120
- "/"
116
+ unless scopes.empty?
117
+ "/#{ scopes.map {|scope| format_route(scope.to_s.underscore)}.compact.join('/') }/"
118
+ else
119
+ "/"
120
+ end
121
121
  end
122
122
  end
123
123
 
@@ -125,24 +125,25 @@ module JSONAPI
125
125
  klass.name.to_s.split("::")[0...-1]
126
126
  end
127
127
 
128
- def regular_resources_path(source_klass)
129
- @resources_path_cache.get(source_klass)
128
+ def resources_path(source_klass)
129
+ formatted_module_path_from_class(source_klass) + format_route(source_klass._type.to_s)
130
130
  end
131
131
 
132
- def regular_primary_resources_path
133
- regular_resources_path(primary_resource_klass)
134
- end
132
+ def resource_path(source)
133
+ url = "#{resources_path(source.class)}"
135
134
 
136
- def regular_primary_resources_url
137
- "#{ base_url }#{ regular_primary_resources_path }"
135
+ unless source.class.singleton?
136
+ url = "#{url}/#{source.id}"
137
+ end
138
+ url
138
139
  end
139
140
 
140
- def regular_resource_path(source)
141
- "#{regular_resources_path(source.class)}/#{source.id}"
141
+ def resource_url(source)
142
+ "#{ base_url }#{ serialized_engine_mount_point }#{ resource_path(source) }"
142
143
  end
143
144
 
144
- def regular_resource_url(source)
145
- "#{ base_url }#{ regular_resource_path(source) }"
145
+ def serialized_engine_mount_point
146
+ engine_mount_point == "/" ? "" : engine_mount_point
146
147
  end
147
148
 
148
149
  def route_for_relationship(relationship)
@@ -53,7 +53,7 @@ module JSONAPI
53
53
  end
54
54
  end
55
55
 
56
- class LinksObjectOperationResult < OperationResult
56
+ class RelationshipOperationResult < OperationResult
57
57
  attr_accessor :parent_resource, :relationship
58
58
 
59
59
  def initialize(code, parent_resource, relationship, options = {})
@@ -100,7 +100,7 @@ module JSONAPI
100
100
  end
101
101
 
102
102
  if JSONAPI.configuration.top_level_links_include_pagination && paginator
103
- page_options[:pagination_params] = paginator.links_page_params(page_options)
103
+ page_options[:pagination_params] = paginator.links_page_params(page_options.merge(fetched_resources: resource_records))
104
104
  end
105
105
 
106
106
  return JSONAPI::ResourcesOperationResult.new(:ok, resource_records, page_options)
@@ -136,9 +136,9 @@ module JSONAPI
136
136
 
137
137
  parent_resource = resource_klass.find_by_key(parent_key, context: context)
138
138
 
139
- return JSONAPI::LinksObjectOperationResult.new(:ok,
140
- parent_resource,
141
- resource_klass._relationship(relationship_type))
139
+ return JSONAPI::RelationshipOperationResult.new(:ok,
140
+ parent_resource,
141
+ resource_klass._relationship(relationship_type))
142
142
  end
143
143
 
144
144
  def show_related_resource
@@ -166,9 +166,10 @@ module JSONAPI
166
166
  include_directives = params[:include_directives]
167
167
 
168
168
  source_resource ||= source_klass.find_by_key(source_id, context: context, fields: fields)
169
+ verified_filters = resource_klass.verify_filters(filters, context)
169
170
 
170
171
  rel_opts = {
171
- filters: filters,
172
+ filters: verified_filters,
172
173
  sort_criteria: sort_criteria,
173
174
  paginator: paginator,
174
175
  fields: fields,
@@ -196,7 +197,7 @@ module JSONAPI
196
197
  (paginator && paginator.class.requires_record_count) ||
197
198
  (JSONAPI.configuration.top_level_meta_include_page_count))
198
199
  related_resource_records = source_resource.public_send("records_for_" + relationship_type)
199
- records = resource_klass.filter_records(filters, {},
200
+ records = resource_klass.filter_records(verified_filters, rel_opts,
200
201
  related_resource_records)
201
202
 
202
203
  record_count = resource_klass.count_records(records)
@@ -209,7 +210,7 @@ module JSONAPI
209
210
  pagination_params = if paginator && JSONAPI.configuration.top_level_links_include_pagination
210
211
  page_options = {}
211
212
  page_options[:record_count] = record_count if paginator.class.requires_record_count
212
- paginator.links_page_params(page_options)
213
+ paginator.links_page_params(page_options.merge(fetched_resources: related_resources))
213
214
  else
214
215
  {}
215
216
  end
@@ -4,6 +4,8 @@ module JSONAPI
4
4
  :class_name, :polymorphic, :always_include_linkage_data,
5
5
  :parent_resource, :eager_load_on_include
6
6
 
7
+ attr_accessor :_routed, :_warned_missing_route
8
+
7
9
  def initialize(name, options = {})
8
10
  @name = name.to_s
9
11
  @options = options
@@ -14,6 +16,10 @@ module JSONAPI
14
16
  @polymorphic = options.fetch(:polymorphic, false) == true
15
17
  @always_include_linkage_data = options.fetch(:always_include_linkage_data, false) == true
16
18
  @eager_load_on_include = options.fetch(:eager_load_on_include, true) == true
19
+ @_routed = false
20
+ @_warned_missing_route = false
21
+
22
+ exclude_links(options.fetch(:exclude_links, JSONAPI.configuration.default_exclude_links))
17
23
  end
18
24
 
19
25
  alias_method :polymorphic?, :polymorphic
@@ -49,8 +55,14 @@ module JSONAPI
49
55
 
50
56
  def type_for_source(source)
51
57
  if polymorphic?
52
- resource = source.public_send(name)
53
- resource.class._type if resource
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
54
66
  else
55
67
  type
56
68
  end
@@ -60,6 +72,27 @@ module JSONAPI
60
72
  false
61
73
  end
62
74
 
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)
94
+ end
95
+
63
96
  class ToOne < Relationship
64
97
  attr_reader :foreign_key_on
65
98
 
@@ -70,6 +103,12 @@ module JSONAPI
70
103
  @foreign_key_on = options.fetch(:foreign_key_on, :self)
71
104
  end
72
105
 
106
+ def to_s
107
+ # :nocov:
108
+ "#{parent_resource}.#{name}(#{belongs_to? ? 'BelongsToOne' : 'ToOne'})"
109
+ # :nocov:
110
+ end
111
+
73
112
  def belongs_to?
74
113
  foreign_key_on == :self
75
114
  end
@@ -89,6 +128,12 @@ module JSONAPI
89
128
  @reflect = options.fetch(:reflect, true) == true
90
129
  @inverse_relationship = options.fetch(:inverse_relationship, parent_resource._type.to_s.singularize.to_sym) if parent_resource
91
130
  end
131
+
132
+ def to_s
133
+ # :nocov:
134
+ "#{parent_resource}.#{name}(ToMany)"
135
+ # :nocov:
136
+ end
92
137
  end
93
138
  end
94
139
  end
@@ -60,16 +60,16 @@ module JSONAPI
60
60
 
61
61
  resource_klass = relationship.resource_klass
62
62
 
63
+ records = resource_klass.apply_includes(records, options)
64
+
63
65
  filters = options.fetch(:filters, {})
64
66
  unless filters.nil? || filters.empty?
65
67
  records = resource_klass.apply_filters(records, filters, options)
66
68
  end
67
69
 
68
70
  sort_criteria = options.fetch(:sort_criteria, {})
69
- unless sort_criteria.nil? || sort_criteria.empty?
70
- order_options = relationship.resource_klass.construct_order_options(sort_criteria)
71
- records = resource_klass.apply_sort(records, order_options, @context)
72
- end
71
+ order_options = relationship.resource_klass.construct_order_options(sort_criteria)
72
+ records = resource_klass.apply_sort(records, order_options, @context)
73
73
 
74
74
  paginator = options[:paginator]
75
75
  if paginator