Commit 7fd7221b authored by Mark Chao's avatar Mark Chao

Merge branch '329587-reuse-bulkmemberaccessload-for-group' into 'master'

Reuse BulkMemberAccessLoad on groups [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!62226
parents 8375fd59 daa4c18e
...@@ -25,7 +25,6 @@ class Groups::GroupMembersController < Groups::ApplicationController ...@@ -25,7 +25,6 @@ class Groups::GroupMembersController < Groups::ApplicationController
helper_method :can_manage_members? helper_method :can_manage_members?
def index def index
preload_max_access
@sort = params[:sort].presence || sort_value_name @sort = params[:sort].presence || sort_value_name
@members = GroupMembersFinder @members = GroupMembersFinder
...@@ -54,14 +53,6 @@ class Groups::GroupMembersController < Groups::ApplicationController ...@@ -54,14 +53,6 @@ class Groups::GroupMembersController < Groups::ApplicationController
private private
def preload_max_access
return unless current_user
# this allows the can? against admin type queries in this action to
# only perform the query once, even if it is cached
current_user.max_access_for_group[@group.id] = @group.max_member_access(current_user)
end
def can_manage_members? def can_manage_members?
strong_memoize(:can_manage_members) do strong_memoize(:can_manage_members) do
can?(current_user, :admin_group_member, @group) can?(current_user, :admin_group_member, @group)
......
...@@ -17,6 +17,7 @@ class Group < Namespace ...@@ -17,6 +17,7 @@ class Group < Namespace
include GroupAPICompatibility include GroupAPICompatibility
include EachBatch include EachBatch
include HasTimelogsReport include HasTimelogsReport
include BulkMemberAccessLoad
ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT = 10 ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT = 10
...@@ -569,24 +570,8 @@ class Group < Namespace ...@@ -569,24 +570,8 @@ class Group < Namespace
def max_member_access_for_user(user, only_concrete_membership: false) def max_member_access_for_user(user, only_concrete_membership: false)
return GroupMember::NO_ACCESS unless user return GroupMember::NO_ACCESS unless user
return GroupMember::OWNER if user.can_admin_all_resources? && !only_concrete_membership return GroupMember::OWNER if user.can_admin_all_resources? && !only_concrete_membership
# Use the preloaded value that exists instead of performing the db query again(cached or not).
# Groups::GroupMembersController#preload_max_access makes use of this by
# calling Group#max_member_access. This helps when we have a process
# that may query this multiple times from the outside through a policy query
# like the GroupPolicy#lookup_access_level! does as a condition for any role
return user.max_access_for_group[id] if user.max_access_for_group[id]
max_member_access(user) max_member_access([user.id])[user.id]
end
def max_member_access(user)
max_member_access = members_with_parents
.where(user_id: user)
.reorder(access_level: :desc)
.first
&.access_level
max_member_access || GroupMember::NO_ACCESS
end end
def mattermost_team_params def mattermost_team_params
...@@ -730,6 +715,12 @@ class Group < Namespace ...@@ -730,6 +715,12 @@ class Group < Namespace
private private
def max_member_access(user_ids)
max_member_access_for_resource_ids(User, user_ids) do |user_ids|
members_with_parents.where(user_id: user_ids).group(:user_id).maximum(:access_level)
end
end
def update_two_factor_requirement def update_two_factor_requirement
return unless saved_change_to_require_two_factor_authentication? || saved_change_to_two_factor_grace_period? return unless saved_change_to_require_two_factor_authentication? || saved_change_to_two_factor_grace_period?
......
...@@ -99,12 +99,6 @@ class User < ApplicationRecord ...@@ -99,12 +99,6 @@ class User < ApplicationRecord
# Virtual attribute for impersonator # Virtual attribute for impersonator
attr_accessor :impersonator attr_accessor :impersonator
attr_writer :max_access_for_group
def max_access_for_group
@max_access_for_group ||= {}
end
# #
# Relations # Relations
# #
......
...@@ -20,15 +20,15 @@ RSpec.describe Groups::GroupMembersController do ...@@ -20,15 +20,15 @@ RSpec.describe Groups::GroupMembersController do
let!(:invited) { create(:group_member, :invited, :developer, group: group) } let!(:invited) { create(:group_member, :invited, :developer, group: group) }
let!(:requested) { create(:group_member, :access_request, group: group) } let!(:requested) { create(:group_member, :access_request, group: group) }
it 'records queries', :use_sql_query_cache do it 'records queries', :request_store, :use_sql_query_cache do
get :index, params: { group_id: group } get :index, params: { group_id: group }
control = ActiveRecord::QueryRecorder.new(skip_cached: false) { get :index, params: { group_id: group } } control = ActiveRecord::QueryRecorder.new(skip_cached: false) { get :index, params: { group_id: group } }
create_list(:group_member, 5, group: group, created_by: user) create_list(:group_member, 5, group: group, created_by: user)
create_list(:group_member, 5, :invited, group: group, created_by: user) create_list(:group_member, 5, :invited, group: group, created_by: user)
create_list(:group_member, 5, :access_request, group: group) create_list(:group_member, 5, :access_request, group: group)
# locally 47 vs 52 GDK vs 57 CI # locally 39 vs 43 GDK vs 48 CI
unresolved_n_plus_ones = 5 # still have a few queries created by can_update/can_remove that should be reduced unresolved_n_plus_ones = 4 # still have a few queries created by can_update/can_remove that could be reduced
multiple_members_threshold = 5 # GDK vs CI difference multiple_members_threshold = 5 # GDK vs CI difference
expect do expect do
......
...@@ -32,14 +32,6 @@ RSpec.describe Groups::GroupMembersController do ...@@ -32,14 +32,6 @@ RSpec.describe Groups::GroupMembersController do
sign_in(user) sign_in(user)
end end
it 'assigns max_access_for_group' do
allow(controller).to receive(:current_user).and_return(user)
get :index, params: { group_id: group }
expect(user.max_access_for_group[group.id]).to eq(Gitlab::Access::OWNER)
end
it 'assigns invited members' do it 'assigns invited members' do
get :index, params: { group_id: group } get :index, params: { group_id: group }
......
...@@ -1093,10 +1093,9 @@ RSpec.describe Group do ...@@ -1093,10 +1093,9 @@ RSpec.describe Group do
it { expect(subject.parent).to be_kind_of(described_class) } it { expect(subject.parent).to be_kind_of(described_class) }
end end
context "with member access" do describe '#max_member_access_for_user' do
let_it_be(:group_user) { create(:user) } let_it_be(:group_user) { create(:user) }
describe '#max_member_access_for_user' do
context 'with user in the group' do context 'with user in the group' do
before do before do
group.add_owner(group_user) group.add_owner(group_user)
...@@ -1134,20 +1133,6 @@ RSpec.describe Group do ...@@ -1134,20 +1133,6 @@ RSpec.describe Group do
end end
end end
context 'when max_access_for_group is set' do
let(:max_member_access) { 111 }
before do
group_user.max_access_for_group[group.id] = max_member_access
end
it 'uses the cached value' do
expect(group.max_member_access_for_user(group_user)).to eq(max_member_access)
end
end
end
describe '#max_member_access' do
context 'group shared with another group' do context 'group shared with another group' do
let_it_be(:parent_group_user) { create(:user) } let_it_be(:parent_group_user) { create(:user) }
let_it_be(:child_group_user) { create(:user) } let_it_be(:child_group_user) { create(:user) }
...@@ -1172,9 +1157,9 @@ RSpec.describe Group do ...@@ -1172,9 +1157,9 @@ RSpec.describe Group do
context 'with user in the group' do context 'with user in the group' do
it 'returns correct access level' do it 'returns correct access level' do
expect(shared_group_parent.max_member_access(group_user)).to eq(Gitlab::Access::NO_ACCESS) expect(shared_group_parent.max_member_access_for_user(group_user)).to eq(Gitlab::Access::NO_ACCESS)
expect(shared_group.max_member_access(group_user)).to eq(Gitlab::Access::DEVELOPER) expect(shared_group.max_member_access_for_user(group_user)).to eq(Gitlab::Access::DEVELOPER)
expect(shared_group_child.max_member_access(group_user)).to eq(Gitlab::Access::DEVELOPER) expect(shared_group_child.max_member_access_for_user(group_user)).to eq(Gitlab::Access::DEVELOPER)
end end
context 'with lower group access level than max access level for share' do context 'with lower group access level than max access level for share' do
...@@ -1183,26 +1168,26 @@ RSpec.describe Group do ...@@ -1183,26 +1168,26 @@ RSpec.describe Group do
it 'returns correct access level' do it 'returns correct access level' do
group.add_reporter(user) group.add_reporter(user)
expect(shared_group_parent.max_member_access(user)).to eq(Gitlab::Access::NO_ACCESS) expect(shared_group_parent.max_member_access_for_user(user)).to eq(Gitlab::Access::NO_ACCESS)
expect(shared_group.max_member_access(user)).to eq(Gitlab::Access::REPORTER) expect(shared_group.max_member_access_for_user(user)).to eq(Gitlab::Access::REPORTER)
expect(shared_group_child.max_member_access(user)).to eq(Gitlab::Access::REPORTER) expect(shared_group_child.max_member_access_for_user(user)).to eq(Gitlab::Access::REPORTER)
end end
end end
end end
context 'with user in the parent group' do context 'with user in the parent group' do
it 'returns correct access level' do it 'returns correct access level' do
expect(shared_group_parent.max_member_access(parent_group_user)).to eq(Gitlab::Access::NO_ACCESS) expect(shared_group_parent.max_member_access_for_user(parent_group_user)).to eq(Gitlab::Access::NO_ACCESS)
expect(shared_group.max_member_access(parent_group_user)).to eq(Gitlab::Access::NO_ACCESS) expect(shared_group.max_member_access_for_user(parent_group_user)).to eq(Gitlab::Access::NO_ACCESS)
expect(shared_group_child.max_member_access(parent_group_user)).to eq(Gitlab::Access::NO_ACCESS) expect(shared_group_child.max_member_access_for_user(parent_group_user)).to eq(Gitlab::Access::NO_ACCESS)
end end
end end
context 'with user in the child group' do context 'with user in the child group' do
it 'returns correct access level' do it 'returns correct access level' do
expect(shared_group_parent.max_member_access(child_group_user)).to eq(Gitlab::Access::NO_ACCESS) expect(shared_group_parent.max_member_access_for_user(child_group_user)).to eq(Gitlab::Access::NO_ACCESS)
expect(shared_group.max_member_access(child_group_user)).to eq(Gitlab::Access::NO_ACCESS) expect(shared_group.max_member_access_for_user(child_group_user)).to eq(Gitlab::Access::NO_ACCESS)
expect(shared_group_child.max_member_access(child_group_user)).to eq(Gitlab::Access::NO_ACCESS) expect(shared_group_child.max_member_access_for_user(child_group_user)).to eq(Gitlab::Access::NO_ACCESS)
end end
end end
...@@ -1213,9 +1198,9 @@ RSpec.describe Group do ...@@ -1213,9 +1198,9 @@ RSpec.describe Group do
let(:user) { unrelated_project.owner } let(:user) { unrelated_project.owner }
it 'returns correct access level' do it 'returns correct access level' do
expect(shared_group_parent.max_member_access(user)).to eq(Gitlab::Access::NO_ACCESS) expect(shared_group_parent.max_member_access_for_user(user)).to eq(Gitlab::Access::NO_ACCESS)
expect(shared_group.max_member_access(user)).to eq(Gitlab::Access::NO_ACCESS) expect(shared_group.max_member_access_for_user(user)).to eq(Gitlab::Access::NO_ACCESS)
expect(shared_group_child.max_member_access(user)).to eq(Gitlab::Access::NO_ACCESS) expect(shared_group_child.max_member_access_for_user(user)).to eq(Gitlab::Access::NO_ACCESS)
end end
end end
...@@ -1227,9 +1212,9 @@ RSpec.describe Group do ...@@ -1227,9 +1212,9 @@ RSpec.describe Group do
end end
it 'returns correct access level' do it 'returns correct access level' do
expect(shared_group_parent.max_member_access(user)).to eq(Gitlab::Access::NO_ACCESS) expect(shared_group_parent.max_member_access_for_user(user)).to eq(Gitlab::Access::NO_ACCESS)
expect(shared_group.max_member_access(user)).to eq(Gitlab::Access::NO_ACCESS) expect(shared_group.max_member_access_for_user(user)).to eq(Gitlab::Access::NO_ACCESS)
expect(shared_group_child.max_member_access(user)).to eq(Gitlab::Access::NO_ACCESS) expect(shared_group_child.max_member_access_for_user(user)).to eq(Gitlab::Access::NO_ACCESS)
end end
end end
end end
...@@ -1252,8 +1237,7 @@ RSpec.describe Group do ...@@ -1252,8 +1237,7 @@ RSpec.describe Group do
end end
it 'returns correct access level' do it 'returns correct access level' do
expect(shared_group.max_member_access(user)).to eq(Gitlab::Access::MAINTAINER) expect(shared_group.max_member_access_for_user(user)).to eq(Gitlab::Access::MAINTAINER)
end
end end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment