summaryrefslogtreecommitdiff
path: root/app/models/preloaders/user_max_access_level_in_groups_preloader.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/preloaders/user_max_access_level_in_groups_preloader.rb')
-rw-r--r--app/models/preloaders/user_max_access_level_in_groups_preloader.rb63
1 files changed, 58 insertions, 5 deletions
diff --git a/app/models/preloaders/user_max_access_level_in_groups_preloader.rb b/app/models/preloaders/user_max_access_level_in_groups_preloader.rb
index 8df986b47a2..0c747ad9c84 100644
--- a/app/models/preloaders/user_max_access_level_in_groups_preloader.rb
+++ b/app/models/preloaders/user_max_access_level_in_groups_preloader.rb
@@ -32,11 +32,12 @@ module Preloaders
end
def preload_with_traversal_ids
- max_access_levels = GroupMember.active_without_invites_and_requests
- .where(user: @user)
- .joins("INNER JOIN (#{traversal_join_sql}) as hierarchy ON members.source_id = hierarchy.traversal_id")
- .group('hierarchy.id')
- .maximum(:access_level)
+ # Diagrammatic representation of this step:
+ # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/111157#note_1271550140
+ max_access_levels = GroupMember.from_union(all_memberships)
+ .joins("INNER JOIN (#{traversal_join_sql}) as hierarchy ON members.source_id = hierarchy.traversal_id")
+ .group('hierarchy.id')
+ .maximum(:access_level)
@groups.each do |group|
max_access_level = max_access_levels[group.id] || Gitlab::Access::NO_ACCESS
@@ -44,6 +45,58 @@ module Preloaders
end
end
+ def all_memberships
+ if Feature.enabled?(:include_memberships_from_group_shares_in_preloader)
+ [
+ direct_memberships.select(*GroupMember.cached_column_list),
+ memberships_from_group_shares
+ ]
+ else
+ [direct_memberships]
+ end
+ end
+
+ def direct_memberships
+ GroupMember.active_without_invites_and_requests.where(user: @user)
+ end
+
+ def memberships_from_group_shares
+ alter_direct_memberships_to_make_it_act_like_memberships_in_shared_groups
+ end
+
+ def alter_direct_memberships_to_make_it_act_like_memberships_in_shared_groups
+ group_group_link_table = GroupGroupLink.arel_table
+ group_member_table = GroupMember.arel_table
+
+ altered_columns = GroupMember.attribute_names.map do |column_name|
+ case column_name
+ when 'access_level'
+ # Consider the limiting effect of group share's access level
+ smallest_value_arel([group_group_link_table[:group_access], group_member_table[:access_level]], 'access_level')
+ when 'source_id'
+ # Alter the `source_id` of the `Member` record that is currently pointing to the `shared_with_group`
+ # such that this record would now behave like a `Member` record of this user pointing to the `shared_group` group.
+ Arel::Nodes::As.new(group_group_link_table[:shared_group_id], Arel::Nodes::SqlLiteral.new('source_id'))
+ else
+ group_member_table[column_name]
+ end
+ end
+
+ direct_memberships_in_groups_that_have_been_shared_with_other_groups.select(*altered_columns)
+ end
+
+ def direct_memberships_in_groups_that_have_been_shared_with_other_groups
+ direct_memberships.joins(
+ "INNER JOIN group_group_links ON members.source_id = group_group_links.shared_with_group_id"
+ )
+ end
+
+ def smallest_value_arel(args, column_alias)
+ Arel::Nodes::As.new(
+ Arel::Nodes::NamedFunction.new('LEAST', args),
+ Arel::Nodes::SqlLiteral.new(column_alias))
+ end
+
def traversal_join_sql
Namespace.select('id, unnest(traversal_ids) as traversal_id').where(id: @groups.map(&:id)).to_sql
end