Skip to content

Commit 3cbf7f7

Browse files
committed
WIP Update for jsonapi-resources v0.10
Note this will break with earlier versions of JR
1 parent 0433692 commit 3cbf7f7

File tree

3 files changed

+81
-30
lines changed

3 files changed

+81
-30
lines changed

jsonapi-authorization.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,5 @@ Gem::Specification.new do |spec|
3131
spec.add_development_dependency "pry-rails"
3232
spec.add_development_dependency "rubocop", "~> 0.36.0"
3333
spec.add_development_dependency "phare", "~> 0.7.1"
34-
spec.add_development_dependency "sqlite3", "~> 1.3.6"
34+
spec.add_development_dependency "sqlite3", "~> 1.4.0"
3535
end

lib/jsonapi/authorization/authorizing_processor.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def authorize_show_relationship
7272
related_resource =
7373
case relationship
7474
when JSONAPI::Relationship::ToOne
75-
parent_resource.public_send(params[:relationship_type].to_sym)
75+
resources_from_relationship(source_klass, source_id, relationship.type, context).first
7676
when JSONAPI::Relationship::ToMany
7777
# Do nothing — already covered by policy scopes
7878
else
@@ -91,7 +91,7 @@ def authorize_show_related_resource
9191

9292
source_resource = source_klass.find_by_key(source_id, context: context)
9393

94-
related_resource = source_resource.public_send(relationship_type)
94+
related_resource = resources_from_relationship(source_klass, source_id, relationship_type, context).first
9595

9696
source_record = source_resource._model
9797
related_record = related_resource._model unless related_resource.nil?
@@ -282,6 +282,14 @@ def authorizer
282282
@authorizer ||= ::JSONAPI::Authorization.configuration.authorizer.new(context: context)
283283
end
284284

285+
def resources_from_relationship(source_klass, source_id, relationship_type, context)
286+
rid = source_klass.find_related_fragments([JSONAPI::ResourceIdentity.new(source_klass, source_id)],
287+
relationship_type,
288+
context: context).keys.first
289+
290+
rid.resource_klass.find_to_populate_by_keys(rid.id)
291+
end
292+
285293
# TODO: Communicate with upstream to fix this nasty hack
286294
def operation_resource_id
287295
case operation_type

lib/jsonapi/authorization/pundit_scoped_resource.rb

Lines changed: 70 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,85 @@
11
require 'pundit'
22

33
module JSONAPI
4+
5+
module ActiveRelation
6+
7+
# Stores relationship paths starting from the resource_klass, consolidating duplicate paths from
8+
# relationships, filters and sorts. When joins are made the table aliases are tracked in join_details
9+
class JoinManager
10+
def perform_joins(records, options)
11+
join_array = flatten_join_tree_by_depth
12+
13+
join_array.each do |level_joins|
14+
level_joins.each do |join_details|
15+
relationship = join_details[:relationship]
16+
relationship_details = join_details[:relationship_details]
17+
related_resource_klass = join_details[:related_resource_klass]
18+
join_type = relationship_details[:join_type]
19+
20+
join_options = {
21+
relationship: relationship,
22+
relationship_details: relationship_details,
23+
related_resource_klass: related_resource_klass,
24+
}
25+
26+
if relationship == :root
27+
unless source_relationship
28+
add_join_details('', {alias: resource_klass._table_name, join_type: :root, join_options: join_options})
29+
end
30+
next
31+
end
32+
33+
records, join_node = self.class.get_join_arel_node(records, options) {|records, options|
34+
related_resource_klass.join_relationship(
35+
records: records,
36+
resource_type: related_resource_klass._type,
37+
join_type: join_type,
38+
relationship: relationship,
39+
options: options)
40+
}
41+
42+
details = {alias: self.class.alias_from_arel_node(join_node), join_type: join_type, join_options: join_options}
43+
44+
if relationship == source_relationship
45+
if relationship.polymorphic? && relationship.belongs_to?
46+
add_join_details("##{related_resource_klass._type}", details)
47+
else
48+
add_join_details('', details)
49+
end
50+
end
51+
52+
# We're adding the source alias with two keys. We only want the check for duplicate aliases once.
53+
# See the note in `add_join_details`.
54+
check_for_duplicate_alias = !(relationship == source_relationship)
55+
add_join_details(PathSegment::Relationship.new(relationship: relationship, resource_klass: related_resource_klass), details, check_for_duplicate_alias)
56+
end
57+
end
58+
records
59+
end
60+
end
61+
end
62+
463
module Authorization
564
module PunditScopedResource
665
extend ActiveSupport::Concern
766

867
module ClassMethods
968
def records(options = {})
1069
user_context = JSONAPI::Authorization.configuration.user_context(options[:context])
11-
::Pundit.policy_scope!(user_context, _model_class)
12-
end
13-
end
14-
15-
def records_for(association_name)
16-
record_or_records = @model.public_send(association_name)
17-
relationship = fetch_relationship(association_name)
18-
19-
case relationship
20-
when JSONAPI::Relationship::ToOne
21-
record_or_records
22-
when JSONAPI::Relationship::ToMany
23-
user_context = JSONAPI::Authorization.configuration.user_context(context)
24-
::Pundit.policy_scope!(user_context, record_or_records)
25-
else
26-
raise "Unknown relationship type #{relationship.inspect}"
70+
::Pundit.policy_scope!(user_context, super)
2771
end
28-
end
2972

30-
private
31-
32-
def fetch_relationship(association_name)
33-
relationships = self.class._relationships.select do |_k, v|
34-
v.relation_name(context: context) == association_name
35-
end
36-
if relationships.empty?
37-
nil
38-
else
39-
relationships.values.first
73+
def apply_joins(records, join_manager, options)
74+
records = super
75+
join_manager.join_details.each do |k, v|
76+
next if k == '' || v[:join_type] == :root
77+
v[:join_options][:relationship_details][:resource_klasses].each_key do |klass|
78+
next unless klass.included_modules.include?(PunditScopedResource)
79+
records = records.where(v[:alias] => { klass._primary_key => klass.records(options)})
80+
end
81+
end
82+
records
4083
end
4184
end
4285
end

0 commit comments

Comments
 (0)