jsonapi-resources 0.10.0.beta2 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/lib/jsonapi/{active_relation_resource_finder → active_relation}/adapters/join_left_active_record_adapter.rb +2 -2
- data/lib/jsonapi/active_relation/join_manager.rb +297 -0
- data/lib/jsonapi/{active_relation_resource_finder.rb → active_relation_resource.rb} +189 -146
- data/lib/jsonapi/acts_as_resource_controller.rb +4 -3
- data/lib/jsonapi/basic_resource.rb +1162 -0
- data/lib/jsonapi/cached_response_fragment.rb +66 -49
- data/lib/jsonapi/configuration.rb +18 -13
- data/lib/jsonapi/include_directives.rb +6 -8
- data/lib/jsonapi/link_builder.rb +71 -85
- data/lib/jsonapi/operation_result.rb +2 -2
- data/lib/jsonapi/path.rb +8 -0
- data/lib/jsonapi/path_segment.rb +17 -5
- data/lib/jsonapi/processor.rb +6 -7
- data/lib/jsonapi/relationship.rb +32 -3
- data/lib/jsonapi/request_parser.rb +44 -10
- data/lib/jsonapi/resource.rb +3 -1106
- data/lib/jsonapi/resource_controller_metal.rb +3 -0
- data/lib/jsonapi/resource_serializer.rb +39 -33
- data/lib/jsonapi/resource_set.rb +118 -48
- data/lib/jsonapi/resources/railtie.rb +9 -0
- data/lib/jsonapi/resources/version.rb +1 -1
- data/lib/jsonapi/response_document.rb +7 -2
- data/lib/jsonapi/routing_ext.rb +24 -4
- data/lib/jsonapi-resources.rb +12 -3
- data/lib/tasks/check_upgrade.rake +52 -0
- metadata +28 -12
- data/lib/jsonapi/active_relation_resource_finder/join_tree.rb +0 -227
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
require 'jsonapi/active_relation_resource_finder/adapters/join_left_active_record_adapter'
|
|
2
|
-
|
|
3
1
|
module JSONAPI
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
base.extend ClassMethods
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
module ClassMethods
|
|
2
|
+
class ActiveRelationResource < BasicResource
|
|
3
|
+
root_resource
|
|
10
4
|
|
|
5
|
+
class << self
|
|
11
6
|
# Finds Resources using the `filters`. Pagination and sort options are used when provided
|
|
12
7
|
#
|
|
13
8
|
# @param filters [Hash] the filters hash
|
|
@@ -19,16 +14,15 @@ module JSONAPI
|
|
|
19
14
|
def find(filters, options = {})
|
|
20
15
|
sort_criteria = options.fetch(:sort_criteria) { [] }
|
|
21
16
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
sort_criteria: sort_criteria)
|
|
17
|
+
join_manager = ActiveRelation::JoinManager.new(resource_klass: self,
|
|
18
|
+
filters: filters,
|
|
19
|
+
sort_criteria: sort_criteria)
|
|
26
20
|
|
|
27
21
|
paginator = options[:paginator]
|
|
28
22
|
|
|
29
|
-
records =
|
|
30
|
-
filters: filters,
|
|
31
|
-
|
|
23
|
+
records = apply_request_settings_to_records(records: records(options),
|
|
24
|
+
sort_criteria: sort_criteria,filters: filters,
|
|
25
|
+
join_manager: join_manager,
|
|
32
26
|
paginator: paginator,
|
|
33
27
|
options: options)
|
|
34
28
|
|
|
@@ -42,13 +36,12 @@ module JSONAPI
|
|
|
42
36
|
#
|
|
43
37
|
# @return [Integer] the count
|
|
44
38
|
def count(filters, options = {})
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
filters: filters)
|
|
39
|
+
join_manager = ActiveRelation::JoinManager.new(resource_klass: self,
|
|
40
|
+
filters: filters)
|
|
48
41
|
|
|
49
|
-
records =
|
|
42
|
+
records = apply_request_settings_to_records(records: records(options),
|
|
50
43
|
filters: filters,
|
|
51
|
-
|
|
44
|
+
join_manager: join_manager,
|
|
52
45
|
options: options)
|
|
53
46
|
|
|
54
47
|
count_records(records)
|
|
@@ -72,6 +65,16 @@ module JSONAPI
|
|
|
72
65
|
resources_for(records, options[:context])
|
|
73
66
|
end
|
|
74
67
|
|
|
68
|
+
# Returns an array of Resources identified by the `keys` array. The resources are not filtered as this
|
|
69
|
+
# will have been done in a prior step
|
|
70
|
+
#
|
|
71
|
+
# @param keys [Array<key>] Array of primary keys to find resources for
|
|
72
|
+
# @option options [Hash] :context The context of the request, set in the controller
|
|
73
|
+
def find_to_populate_by_keys(keys, options = {})
|
|
74
|
+
records = records_for_populate(options).where(_primary_key => keys)
|
|
75
|
+
resources_for(records, options[:context])
|
|
76
|
+
end
|
|
77
|
+
|
|
75
78
|
# Finds Resource fragments using the `filters`. Pagination and sort options are used when provided.
|
|
76
79
|
# Retrieving the ResourceIdentities and attributes does not instantiate a model instance.
|
|
77
80
|
# Note: This is incompatible with Polymorphic resources (which are going to come from two separate tables)
|
|
@@ -93,26 +96,23 @@ module JSONAPI
|
|
|
93
96
|
|
|
94
97
|
sort_criteria = options.fetch(:sort_criteria) { [] }
|
|
95
98
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
options: options)
|
|
99
|
+
join_manager = ActiveRelation::JoinManager.new(resource_klass: resource_klass,
|
|
100
|
+
source_relationship: nil,
|
|
101
|
+
relationships: linkage_relationships,
|
|
102
|
+
sort_criteria: sort_criteria,
|
|
103
|
+
filters: filters)
|
|
102
104
|
|
|
103
105
|
paginator = options[:paginator]
|
|
104
106
|
|
|
105
|
-
records =
|
|
107
|
+
records = apply_request_settings_to_records(records: records(options),
|
|
106
108
|
filters: filters,
|
|
107
109
|
sort_criteria: sort_criteria,
|
|
108
110
|
paginator: paginator,
|
|
109
|
-
|
|
111
|
+
join_manager: join_manager,
|
|
110
112
|
options: options)
|
|
111
113
|
|
|
112
|
-
joins = join_tree.joins
|
|
113
|
-
|
|
114
114
|
# This alias is going to be resolve down to the model's table name and will not actually be an alias
|
|
115
|
-
resource_table_alias =
|
|
115
|
+
resource_table_alias = resource_klass._table_name
|
|
116
116
|
|
|
117
117
|
pluck_fields = [Arel.sql("#{concat_table_field(resource_table_alias, resource_klass._primary_key)} AS #{resource_table_alias}_#{resource_klass._primary_key}")]
|
|
118
118
|
|
|
@@ -131,7 +131,7 @@ module JSONAPI
|
|
|
131
131
|
klass = resource_klass_for(resource_type)
|
|
132
132
|
linkage_fields << {relationship_name: name, resource_klass: klass}
|
|
133
133
|
|
|
134
|
-
linkage_table_alias =
|
|
134
|
+
linkage_table_alias = join_manager.join_details_by_polymorphic_relationship(linkage_relationship, resource_type)[:alias]
|
|
135
135
|
primary_key = klass._primary_key
|
|
136
136
|
pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
|
|
137
137
|
end
|
|
@@ -139,7 +139,7 @@ module JSONAPI
|
|
|
139
139
|
klass = linkage_relationship.resource_klass
|
|
140
140
|
linkage_fields << {relationship_name: name, resource_klass: klass}
|
|
141
141
|
|
|
142
|
-
linkage_table_alias =
|
|
142
|
+
linkage_table_alias = join_manager.join_details_by_relationship(linkage_relationship)[:alias]
|
|
143
143
|
primary_key = klass._primary_key
|
|
144
144
|
pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
|
|
145
145
|
end
|
|
@@ -155,7 +155,7 @@ module JSONAPI
|
|
|
155
155
|
|
|
156
156
|
fragments = {}
|
|
157
157
|
rows = records.pluck(*pluck_fields)
|
|
158
|
-
rows.
|
|
158
|
+
rows.each do |row|
|
|
159
159
|
rid = JSONAPI::ResourceIdentity.new(resource_klass, pluck_fields.length == 1 ? row : row[0])
|
|
160
160
|
|
|
161
161
|
fragments[rid] ||= JSONAPI::ResourceFragment.new(rid)
|
|
@@ -181,6 +181,10 @@ module JSONAPI
|
|
|
181
181
|
end
|
|
182
182
|
end
|
|
183
183
|
|
|
184
|
+
if JSONAPI.configuration.warn_on_performance_issues && (rows.length > fragments.length)
|
|
185
|
+
warn "Performance issue detected: `#{self.name.to_s}.records` returned non-normalized results in `#{self.name.to_s}.find_fragments`."
|
|
186
|
+
end
|
|
187
|
+
|
|
184
188
|
fragments
|
|
185
189
|
end
|
|
186
190
|
|
|
@@ -229,28 +233,118 @@ module JSONAPI
|
|
|
229
233
|
filters = options.fetch(:filters, {})
|
|
230
234
|
|
|
231
235
|
# Joins in this case are related to the related_klass
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
options: options)
|
|
236
|
+
join_manager = ActiveRelation::JoinManager.new(resource_klass: self,
|
|
237
|
+
source_relationship: relationship,
|
|
238
|
+
filters: filters)
|
|
236
239
|
|
|
237
|
-
records =
|
|
240
|
+
records = apply_request_settings_to_records(records: records(options),
|
|
238
241
|
resource_klass: related_klass,
|
|
239
242
|
primary_keys: source_rid.id,
|
|
240
|
-
|
|
243
|
+
join_manager: join_manager,
|
|
241
244
|
filters: filters,
|
|
242
245
|
options: options)
|
|
243
246
|
|
|
244
|
-
|
|
245
|
-
related_alias = joins[''][:alias]
|
|
247
|
+
related_alias = join_manager.join_details_by_relationship(relationship)[:alias]
|
|
246
248
|
|
|
247
249
|
records = records.select(Arel.sql("#{concat_table_field(related_alias, related_klass._primary_key)}"))
|
|
248
250
|
|
|
249
251
|
count_records(records)
|
|
250
252
|
end
|
|
251
253
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
+
# This resource class (ActiveRelationResource) uses an `ActiveRecord::Relation` as the starting point for
|
|
255
|
+
# retrieving models. From this relation filters, sorts and joins are applied as needed.
|
|
256
|
+
# Depending on which phase of the request processing different `records` methods will be called, giving the user
|
|
257
|
+
# the opportunity to override them differently for performance and security reasons.
|
|
258
|
+
|
|
259
|
+
# begin `records`methods
|
|
260
|
+
|
|
261
|
+
# Base for the `records` methods that follow and is not directly used for accessing model data by this class.
|
|
262
|
+
# Overriding this method gives a single place to affect the `ActiveRecord::Relation` used for the resource.
|
|
263
|
+
#
|
|
264
|
+
# @option options [Hash] :context The context of the request, set in the controller
|
|
265
|
+
#
|
|
266
|
+
# @return [ActiveRecord::Relation]
|
|
267
|
+
def records_base(_options = {})
|
|
268
|
+
_model_class.all
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# The `ActiveRecord::Relation` used for finding user requested models. This may be overridden to enforce
|
|
272
|
+
# permissions checks on the request.
|
|
273
|
+
#
|
|
274
|
+
# @option options [Hash] :context The context of the request, set in the controller
|
|
275
|
+
#
|
|
276
|
+
# @return [ActiveRecord::Relation]
|
|
277
|
+
def records(options = {})
|
|
278
|
+
records_base(options)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# The `ActiveRecord::Relation` used for populating the ResourceSet. Only resources that have been previously
|
|
282
|
+
# identified through the `records` method will be accessed. Thus it should not be necessary to reapply permissions
|
|
283
|
+
# checks. However if the model needs to include other models adding `includes` is appropriate
|
|
284
|
+
#
|
|
285
|
+
# @option options [Hash] :context The context of the request, set in the controller
|
|
286
|
+
#
|
|
287
|
+
# @return [ActiveRecord::Relation]
|
|
288
|
+
def records_for_populate(options = {})
|
|
289
|
+
records_base(options)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# The `ActiveRecord::Relation` used for the finding related resources. Only resources that have been previously
|
|
293
|
+
# identified through the `records` method will be accessed and used as the basis to find related resources. Thus
|
|
294
|
+
# it should not be necessary to reapply permissions checks.
|
|
295
|
+
#
|
|
296
|
+
# @option options [Hash] :context The context of the request, set in the controller
|
|
297
|
+
#
|
|
298
|
+
# @return [ActiveRecord::Relation]
|
|
299
|
+
def records_for_source_to_related(options = {})
|
|
300
|
+
records_base(options)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# end `records` methods
|
|
304
|
+
|
|
305
|
+
def apply_join(records:, relationship:, resource_type:, join_type:, options:)
|
|
306
|
+
if relationship.polymorphic? && relationship.belongs_to?
|
|
307
|
+
case join_type
|
|
308
|
+
when :inner
|
|
309
|
+
records = records.joins(resource_type.to_s.singularize.to_sym)
|
|
310
|
+
when :left
|
|
311
|
+
records = records.joins_left(resource_type.to_s.singularize.to_sym)
|
|
312
|
+
end
|
|
313
|
+
else
|
|
314
|
+
relation_name = relationship.relation_name(options)
|
|
315
|
+
case join_type
|
|
316
|
+
when :inner
|
|
317
|
+
records = records.joins(relation_name)
|
|
318
|
+
when :left
|
|
319
|
+
records = records.joins_left(relation_name)
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
records
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def relationship_records(relationship:, join_type: :inner, resource_type: nil, options: {})
|
|
326
|
+
records = relationship.parent_resource.records_for_source_to_related(options)
|
|
327
|
+
strategy = relationship.options[:apply_join]
|
|
328
|
+
|
|
329
|
+
if strategy
|
|
330
|
+
records = call_method_or_proc(strategy, records, relationship, resource_type, join_type, options)
|
|
331
|
+
else
|
|
332
|
+
records = apply_join(records: records,
|
|
333
|
+
relationship: relationship,
|
|
334
|
+
resource_type: resource_type,
|
|
335
|
+
join_type: join_type,
|
|
336
|
+
options: options)
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
records
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def join_relationship(records:, relationship:, resource_type: nil, join_type: :inner, options: {})
|
|
343
|
+
relationship_records = relationship_records(relationship: relationship,
|
|
344
|
+
join_type: join_type,
|
|
345
|
+
resource_type: resource_type,
|
|
346
|
+
options: options)
|
|
347
|
+
records.merge(relationship_records)
|
|
254
348
|
end
|
|
255
349
|
|
|
256
350
|
protected
|
|
@@ -267,13 +361,13 @@ module JSONAPI
|
|
|
267
361
|
end
|
|
268
362
|
|
|
269
363
|
def find_record_by_key(key, options = {})
|
|
270
|
-
record =
|
|
364
|
+
record = apply_request_settings_to_records(records: records(options), primary_keys: key, options: options).first
|
|
271
365
|
fail JSONAPI::Exceptions::RecordNotFound.new(key) if record.nil?
|
|
272
366
|
record
|
|
273
367
|
end
|
|
274
368
|
|
|
275
369
|
def find_records_by_keys(keys, options = {})
|
|
276
|
-
|
|
370
|
+
apply_request_settings_to_records(records: records(options), primary_keys: keys, options: options)
|
|
277
371
|
end
|
|
278
372
|
|
|
279
373
|
def find_related_monomorphic_fragments(source_rids, relationship, options, connect_source_identity)
|
|
@@ -290,26 +384,24 @@ module JSONAPI
|
|
|
290
384
|
sort_criteria << { field: field, direction: sort[:direction] }
|
|
291
385
|
end
|
|
292
386
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
options: options)
|
|
387
|
+
join_manager = ActiveRelation::JoinManager.new(resource_klass: self,
|
|
388
|
+
source_relationship: relationship,
|
|
389
|
+
relationships: linkage_relationships,
|
|
390
|
+
sort_criteria: sort_criteria,
|
|
391
|
+
filters: filters)
|
|
299
392
|
|
|
300
393
|
paginator = options[:paginator] if source_rids.count == 1
|
|
301
394
|
|
|
302
|
-
records =
|
|
395
|
+
records = apply_request_settings_to_records(records: records_for_source_to_related(options),
|
|
303
396
|
resource_klass: resource_klass,
|
|
304
397
|
sort_criteria: sort_criteria,
|
|
305
398
|
primary_keys: source_ids,
|
|
306
399
|
paginator: paginator,
|
|
307
400
|
filters: filters,
|
|
308
|
-
|
|
401
|
+
join_manager: join_manager,
|
|
309
402
|
options: options)
|
|
310
403
|
|
|
311
|
-
|
|
312
|
-
resource_table_alias = joins[''][:alias]
|
|
404
|
+
resource_table_alias = join_manager.join_details_by_relationship(relationship)[:alias]
|
|
313
405
|
|
|
314
406
|
pluck_fields = [
|
|
315
407
|
Arel.sql("#{_table_name}.#{_primary_key} AS source_id"),
|
|
@@ -331,7 +423,7 @@ module JSONAPI
|
|
|
331
423
|
klass = resource_klass_for(resource_type)
|
|
332
424
|
linkage_fields << {relationship_name: name, resource_klass: klass}
|
|
333
425
|
|
|
334
|
-
linkage_table_alias =
|
|
426
|
+
linkage_table_alias = join_manager.join_details_by_polymorphic_relationship(linkage_relationship, resource_type)[:alias]
|
|
335
427
|
primary_key = klass._primary_key
|
|
336
428
|
pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
|
|
337
429
|
end
|
|
@@ -339,7 +431,7 @@ module JSONAPI
|
|
|
339
431
|
klass = linkage_relationship.resource_klass
|
|
340
432
|
linkage_fields << {relationship_name: name, resource_klass: klass}
|
|
341
433
|
|
|
342
|
-
linkage_table_alias =
|
|
434
|
+
linkage_table_alias = join_manager.join_details_by_relationship(linkage_relationship)[:alias]
|
|
343
435
|
primary_key = klass._primary_key
|
|
344
436
|
pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
|
|
345
437
|
end
|
|
@@ -354,7 +446,7 @@ module JSONAPI
|
|
|
354
446
|
end
|
|
355
447
|
|
|
356
448
|
fragments = {}
|
|
357
|
-
rows = records.pluck(*pluck_fields)
|
|
449
|
+
rows = records.distinct.pluck(*pluck_fields)
|
|
358
450
|
rows.each do |row|
|
|
359
451
|
rid = JSONAPI::ResourceIdentity.new(resource_klass, row[1])
|
|
360
452
|
|
|
@@ -418,27 +510,24 @@ module JSONAPI
|
|
|
418
510
|
end
|
|
419
511
|
end
|
|
420
512
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
options: options)
|
|
513
|
+
join_manager = ActiveRelation::JoinManager.new(resource_klass: self,
|
|
514
|
+
source_relationship: relationship,
|
|
515
|
+
relationships: linkage_relationships,
|
|
516
|
+
filters: filters)
|
|
426
517
|
|
|
427
518
|
paginator = options[:paginator] if source_rids.count == 1
|
|
428
519
|
|
|
429
520
|
# Note: We will sort by the source table. Without using unions we can't sort on a polymorphic relationship
|
|
430
521
|
# in any manner that makes sense
|
|
431
|
-
records =
|
|
522
|
+
records = apply_request_settings_to_records(records: records_for_source_to_related(options),
|
|
432
523
|
resource_klass: resource_klass,
|
|
433
524
|
sort_primary: true,
|
|
434
525
|
primary_keys: source_ids,
|
|
435
526
|
paginator: paginator,
|
|
436
527
|
filters: filters,
|
|
437
|
-
|
|
528
|
+
join_manager: join_manager,
|
|
438
529
|
options: options)
|
|
439
530
|
|
|
440
|
-
joins = join_tree.joins
|
|
441
|
-
|
|
442
531
|
primary_key = concat_table_field(_table_name, _primary_key)
|
|
443
532
|
related_key = concat_table_field(_table_name, relationship.foreign_key)
|
|
444
533
|
related_type = concat_table_field(_table_name, relationship.polymorphic_type)
|
|
@@ -467,7 +556,7 @@ module JSONAPI
|
|
|
467
556
|
|
|
468
557
|
cache_field = related_klass.attribute_to_model_field(:_cache_field) if options[:cache]
|
|
469
558
|
|
|
470
|
-
table_alias =
|
|
559
|
+
table_alias = join_manager.source_join_details(type)[:alias]
|
|
471
560
|
|
|
472
561
|
cache_offset = relation_index
|
|
473
562
|
if cache_field
|
|
@@ -515,7 +604,7 @@ module JSONAPI
|
|
|
515
604
|
klass = resource_klass_for(resource_type)
|
|
516
605
|
linkage_fields << {relationship: linkage_relationship, resource_klass: klass}
|
|
517
606
|
|
|
518
|
-
linkage_table_alias =
|
|
607
|
+
linkage_table_alias = join_manager.join_details_by_polymorphic_relationship(linkage_relationship, resource_type)[:alias]
|
|
519
608
|
primary_key = klass._primary_key
|
|
520
609
|
pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
|
|
521
610
|
end
|
|
@@ -523,13 +612,13 @@ module JSONAPI
|
|
|
523
612
|
klass = linkage_relationship.resource_klass
|
|
524
613
|
linkage_fields << {relationship: linkage_relationship, resource_klass: klass}
|
|
525
614
|
|
|
526
|
-
linkage_table_alias =
|
|
615
|
+
linkage_table_alias = join_manager.join_details_by_relationship(linkage_relationship)[:alias]
|
|
527
616
|
primary_key = klass._primary_key
|
|
528
617
|
pluck_fields << Arel.sql("#{concat_table_field(linkage_table_alias, primary_key)} AS #{linkage_table_alias}_#{primary_key}")
|
|
529
618
|
end
|
|
530
619
|
end
|
|
531
620
|
|
|
532
|
-
rows = records.pluck(*pluck_fields)
|
|
621
|
+
rows = records.distinct.pluck(*pluck_fields)
|
|
533
622
|
|
|
534
623
|
related_fragments = {}
|
|
535
624
|
|
|
@@ -581,26 +670,26 @@ module JSONAPI
|
|
|
581
670
|
related_fragments
|
|
582
671
|
end
|
|
583
672
|
|
|
584
|
-
def
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
673
|
+
def apply_request_settings_to_records(records:,
|
|
674
|
+
join_manager: ActiveRelation::JoinManager.new(resource_klass: self),
|
|
675
|
+
resource_klass: self,
|
|
676
|
+
filters: {},
|
|
677
|
+
primary_keys: nil,
|
|
678
|
+
sort_criteria: nil,
|
|
679
|
+
sort_primary: nil,
|
|
680
|
+
paginator: nil,
|
|
681
|
+
options: {})
|
|
593
682
|
|
|
594
683
|
opts = options.dup
|
|
595
|
-
records = resource_klass.apply_joins(records,
|
|
684
|
+
records = resource_klass.apply_joins(records, join_manager, opts)
|
|
596
685
|
|
|
597
686
|
if primary_keys
|
|
598
687
|
records = records.where(_primary_key => primary_keys)
|
|
599
688
|
end
|
|
600
689
|
|
|
601
|
-
opts[:
|
|
690
|
+
opts[:join_manager] = join_manager
|
|
602
691
|
|
|
603
|
-
|
|
692
|
+
unless filters.empty?
|
|
604
693
|
records = resource_klass.filter_records(records, filters, opts)
|
|
605
694
|
end
|
|
606
695
|
|
|
@@ -618,52 +707,8 @@ module JSONAPI
|
|
|
618
707
|
records
|
|
619
708
|
end
|
|
620
709
|
|
|
621
|
-
def
|
|
622
|
-
|
|
623
|
-
init_join_sources_length = init_join_sources.length
|
|
624
|
-
|
|
625
|
-
records = yield(records)
|
|
626
|
-
|
|
627
|
-
join_sources = records.arel.join_sources
|
|
628
|
-
if join_sources.length > init_join_sources_length
|
|
629
|
-
last_join = (join_sources - init_join_sources).last
|
|
630
|
-
join_alias =
|
|
631
|
-
case last_join.left
|
|
632
|
-
when Arel::Table
|
|
633
|
-
last_join.left.name
|
|
634
|
-
when Arel::Nodes::TableAlias
|
|
635
|
-
last_join.left.right
|
|
636
|
-
when Arel::Nodes::StringJoin
|
|
637
|
-
# :nocov:
|
|
638
|
-
warn "get_join_alias: Unsupported join type - use custom filtering and sorting"
|
|
639
|
-
nil
|
|
640
|
-
# :nocov:
|
|
641
|
-
end
|
|
642
|
-
else
|
|
643
|
-
# :nocov:
|
|
644
|
-
warn "get_join_alias: No join added"
|
|
645
|
-
join_alias = nil
|
|
646
|
-
# :nocov:
|
|
647
|
-
end
|
|
648
|
-
|
|
649
|
-
return records, join_alias
|
|
650
|
-
end
|
|
651
|
-
|
|
652
|
-
def apply_joins(records, join_tree, _options)
|
|
653
|
-
joins = join_tree.joins
|
|
654
|
-
|
|
655
|
-
joins.each_value do |join|
|
|
656
|
-
case join[:join_type]
|
|
657
|
-
when :inner
|
|
658
|
-
records, join_alias = get_join_alias(records) { |records| records.joins(join[:relation_join_hash]) }
|
|
659
|
-
join[:alias] = join_alias
|
|
660
|
-
when :left
|
|
661
|
-
records, join_alias = get_join_alias(records) { |records| records.joins_left(join[:relation_join_hash]) }
|
|
662
|
-
join[:alias] = join_alias
|
|
663
|
-
end
|
|
664
|
-
end
|
|
665
|
-
|
|
666
|
-
return records
|
|
710
|
+
def apply_joins(records, join_manager, options)
|
|
711
|
+
join_manager.join(records, options)
|
|
667
712
|
end
|
|
668
713
|
|
|
669
714
|
def apply_pagination(records, paginator, order_options)
|
|
@@ -689,9 +734,9 @@ module JSONAPI
|
|
|
689
734
|
if strategy
|
|
690
735
|
records = call_method_or_proc(strategy, records, direction, context)
|
|
691
736
|
else
|
|
692
|
-
|
|
737
|
+
join_manager = options[:join_manager]
|
|
693
738
|
|
|
694
|
-
records = records.order("#{get_aliased_field(field,
|
|
739
|
+
records = records.order(Arel.sql("#{get_aliased_field(field, join_manager)} #{direction}"))
|
|
695
740
|
end
|
|
696
741
|
records
|
|
697
742
|
end
|
|
@@ -758,22 +803,20 @@ module JSONAPI
|
|
|
758
803
|
records
|
|
759
804
|
end
|
|
760
805
|
|
|
761
|
-
def get_aliased_field(path_with_field,
|
|
806
|
+
def get_aliased_field(path_with_field, join_manager)
|
|
762
807
|
path = JSONAPI::Path.new(resource_klass: self, path_string: path_with_field)
|
|
763
808
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
relationship_path = path.relationship_path_string
|
|
809
|
+
relationship_segment = path.segments[-2]
|
|
810
|
+
field_segment = path.segments[-1]
|
|
767
811
|
|
|
768
|
-
if
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
812
|
+
if relationship_segment
|
|
813
|
+
join_details = join_manager.join_details[path.last_relationship]
|
|
814
|
+
table_alias = join_details[:alias]
|
|
815
|
+
else
|
|
816
|
+
table_alias = self._table_name
|
|
772
817
|
end
|
|
773
818
|
|
|
774
|
-
table_alias
|
|
775
|
-
|
|
776
|
-
concat_table_field(table_alias, field.delegated_field_name)
|
|
819
|
+
concat_table_field(table_alias, field_segment.delegated_field_name)
|
|
777
820
|
end
|
|
778
821
|
|
|
779
822
|
def apply_filter(records, filter, value, options = {})
|
|
@@ -782,12 +825,12 @@ module JSONAPI
|
|
|
782
825
|
if strategy
|
|
783
826
|
records = call_method_or_proc(strategy, records, value, options)
|
|
784
827
|
else
|
|
785
|
-
|
|
786
|
-
records = records.where(get_aliased_field(filter,
|
|
828
|
+
join_manager = options[:join_manager]
|
|
829
|
+
records = records.where(Arel.sql(get_aliased_field(filter, join_manager)) => value)
|
|
787
830
|
end
|
|
788
831
|
|
|
789
832
|
records
|
|
790
833
|
end
|
|
791
834
|
end
|
|
792
835
|
end
|
|
793
|
-
end
|
|
836
|
+
end
|
|
@@ -70,7 +70,7 @@ module JSONAPI
|
|
|
70
70
|
def get_related_resources
|
|
71
71
|
# :nocov:
|
|
72
72
|
ActiveSupport::Deprecation.warn "In #{self.class.name} you exposed a `get_related_resources`"\
|
|
73
|
-
" action. Please use `
|
|
73
|
+
" action. Please use `index_related_resources` instead."
|
|
74
74
|
index_related_resources
|
|
75
75
|
# :nocov:
|
|
76
76
|
end
|
|
@@ -102,7 +102,8 @@ module JSONAPI
|
|
|
102
102
|
base_url: base_url,
|
|
103
103
|
key_formatter: key_formatter,
|
|
104
104
|
route_formatter: route_formatter,
|
|
105
|
-
serialization_options: serialization_options
|
|
105
|
+
serialization_options: serialization_options,
|
|
106
|
+
controller: self
|
|
106
107
|
)
|
|
107
108
|
op.options[:cache_serializer_output] = !JSONAPI.configuration.resource_cache.nil?
|
|
108
109
|
|
|
@@ -151,7 +152,7 @@ module JSONAPI
|
|
|
151
152
|
end
|
|
152
153
|
|
|
153
154
|
def base_url
|
|
154
|
-
@base_url ||= request.protocol
|
|
155
|
+
@base_url ||= "#{request.protocol}#{request.host_with_port}#{Rails.application.config.relative_url_root}"
|
|
155
156
|
end
|
|
156
157
|
|
|
157
158
|
def resource_klass_name
|