User ParticipantService linear ancestor scopes

In this commit we're replacing the recursive ancestors scope in
the `ParticipantService` class, for their linear version.

Changelog: performance
parent 532e3e3e
......@@ -36,14 +36,17 @@ module Projects
private
def project_members_through_invited_groups
groups_with_ancestors_ids = Gitlab::ObjectHierarchy
.new(visible_groups)
.base_and_ancestors
.pluck_primary_key
groups_with_ancestors = if ::Feature.enabled?(:linear_participants_service_ancestor_scopes, current_user, default_enabled: :yaml)
visible_groups.self_and_ancestors
else
Gitlab::ObjectHierarchy
.new(visible_groups)
.base_and_ancestors
end
GroupMember
.active_without_invites_and_requests
.with_source_id(groups_with_ancestors_ids)
.with_source_id(groups_with_ancestors.pluck_primary_key)
end
def visible_groups
......
---
name: linear_participants_service_ancestor_scopes
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70684
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341348
milestone: '14.4'
type: development
group: group::access
default_enabled: false
......@@ -104,104 +104,116 @@ RSpec.describe Projects::ParticipantsService do
describe '#project_members' do
subject(:usernames) { service.project_members.map { |member| member[:username] } }
context 'when there is a project in group namespace' do
let_it_be(:public_group) { create(:group, :public) }
let_it_be(:public_project) { create(:project, :public, namespace: public_group)}
shared_examples 'return project members' do
context 'when there is a project in group namespace' do
let_it_be(:public_group) { create(:group, :public) }
let_it_be(:public_project) { create(:project, :public, namespace: public_group)}
let_it_be(:public_group_owner) { create(:user) }
let_it_be(:public_group_owner) { create(:user) }
let(:service) { described_class.new(public_project, create(:user)) }
let(:service) { described_class.new(public_project, create(:user)) }
before do
public_group.add_owner(public_group_owner)
end
before do
public_group.add_owner(public_group_owner)
end
it 'returns members of a group' do
expect(usernames).to include(public_group_owner.username)
it 'returns members of a group' do
expect(usernames).to include(public_group_owner.username)
end
end
end
context 'when there is a private group and a public project' do
let_it_be(:public_group) { create(:group, :public) }
let_it_be(:private_group) { create(:group, :private, :nested) }
let_it_be(:public_project) { create(:project, :public, namespace: public_group)}
let_it_be(:project_issue) { create(:issue, project: public_project)}
context 'when there is a private group and a public project' do
let_it_be(:public_group) { create(:group, :public) }
let_it_be(:private_group) { create(:group, :private, :nested) }
let_it_be(:public_project) { create(:project, :public, namespace: public_group)}
let_it_be(:public_group_owner) { create(:user) }
let_it_be(:private_group_member) { create(:user) }
let_it_be(:public_project_maintainer) { create(:user) }
let_it_be(:private_group_owner) { create(:user) }
let_it_be(:project_issue) { create(:issue, project: public_project)}
let_it_be(:group_ancestor_owner) { create(:user) }
let_it_be(:public_group_owner) { create(:user) }
let_it_be(:private_group_member) { create(:user) }
let_it_be(:public_project_maintainer) { create(:user) }
let_it_be(:private_group_owner) { create(:user) }
before_all do
public_group.add_owner public_group_owner
private_group.add_developer private_group_member
public_project.add_maintainer public_project_maintainer
let_it_be(:group_ancestor_owner) { create(:user) }
private_group.add_owner private_group_owner
private_group.parent.add_owner group_ancestor_owner
end
context 'when the private group is invited to the public project' do
before_all do
create(:project_group_link, group: private_group, project: public_project)
end
public_group.add_owner public_group_owner
private_group.add_developer private_group_member
public_project.add_maintainer public_project_maintainer
context 'when a user who is outside the public project and the private group is signed in' do
let(:service) { described_class.new(public_project, create(:user)) }
private_group.add_owner private_group_owner
private_group.parent.add_owner group_ancestor_owner
end
it 'does not return the private group' do
expect(usernames).not_to include(private_group.name)
context 'when the private group is invited to the public project' do
before_all do
create(:project_group_link, group: private_group, project: public_project)
end
it 'does not return private group members' do
expect(usernames).not_to include(private_group_member.username)
end
context 'when a user who is outside the public project and the private group is signed in' do
let(:service) { described_class.new(public_project, create(:user)) }
it 'returns the project maintainer' do
expect(usernames).to include(public_project_maintainer.username)
end
it 'does not return the private group' do
expect(usernames).not_to include(private_group.name)
end
it 'returns project members from an invited public group' do
invited_public_group = create(:group, :public)
invited_public_group.add_owner create(:user)
it 'does not return private group members' do
expect(usernames).not_to include(private_group_member.username)
end
create(:project_group_link, group: invited_public_group, project: public_project)
it 'returns the project maintainer' do
expect(usernames).to include(public_project_maintainer.username)
end
expect(usernames).to include(invited_public_group.users.first.username)
end
it 'returns project members from an invited public group' do
invited_public_group = create(:group, :public)
invited_public_group.add_owner create(:user)
it 'does not return ancestors of the private group' do
expect(usernames).not_to include(group_ancestor_owner.username)
end
end
create(:project_group_link, group: invited_public_group, project: public_project)
context 'when private group owner is signed in' do
let(:service) { described_class.new(public_project, private_group_owner) }
expect(usernames).to include(invited_public_group.users.first.username)
end
it 'returns private group members' do
expect(usernames).to include(private_group_member.username)
it 'does not return ancestors of the private group' do
expect(usernames).not_to include(group_ancestor_owner.username)
end
end
it 'returns ancestors of the the private group' do
expect(usernames).to include(group_ancestor_owner.username)
end
end
context 'when private group owner is signed in' do
let(:service) { described_class.new(public_project, private_group_owner) }
context 'when the namespace owner of the public project is signed in' do
let(:service) { described_class.new(public_project, public_group_owner) }
it 'returns private group members' do
expect(usernames).to include(private_group_member.username)
end
it 'returns private group members' do
expect(usernames).to include(private_group_member.username)
it 'returns ancestors of the the private group' do
expect(usernames).to include(group_ancestor_owner.username)
end
end
it 'does not return members of the ancestral groups of the private group' do
expect(usernames).to include(group_ancestor_owner.username)
context 'when the namespace owner of the public project is signed in' do
let(:service) { described_class.new(public_project, public_group_owner) }
it 'returns private group members' do
expect(usernames).to include(private_group_member.username)
end
it 'does not return members of the ancestral groups of the private group' do
expect(usernames).to include(group_ancestor_owner.username)
end
end
end
end
end
it_behaves_like 'return project members'
context 'when feature flag :linear_participants_service_ancestor_scopes is disabled' do
before do
stub_feature_flags(linear_participants_service_ancestor_scopes: false)
end
it_behaves_like 'return project members'
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