Commit 9d4a6597 authored by Francisco Javier López's avatar Francisco Javier López Committed by Nikola Milojevic

User linear version UserGroupNotificationSettingsFinder#execute

In this commit we're removing the ff
`linear_user_group_notification_settings_finder_ancestors_scopes` and
enabling the linear version of
`UserGroupNotificationSettingsFinder#execute`.

Changelog: changed
parent 6c3646d6
...@@ -7,15 +7,7 @@ class UserGroupNotificationSettingsFinder ...@@ -7,15 +7,7 @@ class UserGroupNotificationSettingsFinder
end end
def execute def execute
# rubocop: disable CodeReuse/ActiveRecord groups_with_ancestors = groups.self_and_ancestors
selected_groups = Group.where(id: groups.select(:id))
groups_with_ancestors = if Feature.enabled?(:linear_user_group_notification_settings_finder_ancestors_scopes, user, default_enabled: :yaml)
selected_groups.self_and_ancestors
else
Gitlab::ObjectHierarchy.new(selected_groups).base_and_ancestors
end
# rubocop: enable CodeReuse/ActiveRecord
@loaded_groups_with_ancestors = groups_with_ancestors.index_by(&:id) @loaded_groups_with_ancestors = groups_with_ancestors.index_by(&:id)
@loaded_notification_settings = user.notification_settings_for_groups(groups_with_ancestors).preload_source_route.index_by(&:source_id) @loaded_notification_settings = user.notification_settings_for_groups(groups_with_ancestors).preload_source_route.index_by(&:source_id)
......
---
name: linear_user_group_notification_settings_finder_ancestors_scopes
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74606
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345792
milestone: '14.6'
type: development
group: group::access
default_enabled: false
...@@ -11,167 +11,155 @@ RSpec.describe UserGroupNotificationSettingsFinder do ...@@ -11,167 +11,155 @@ RSpec.describe UserGroupNotificationSettingsFinder do
subject.map(&proc).uniq subject.map(&proc).uniq
end end
shared_examples 'user group notifications settings tests' do context 'when the groups have no existing notification settings' do
context 'when the groups have no existing notification settings' do context 'when the groups have no ancestors' do
context 'when the groups have no ancestors' do let_it_be(:groups) { create_list(:group, 3) }
let_it_be(:groups) { create_list(:group, 3) }
it 'will be a default Global notification setting', :aggregate_failures do
it 'will be a default Global notification setting', :aggregate_failures do expect(subject.count).to eq(3)
expect(subject.count).to eq(3) expect(attributes(&:notification_email)).to match_array([nil])
expect(attributes(&:notification_email)).to match_array([nil]) expect(attributes(&:level)).to match_array(['global'])
expect(attributes(&:level)).to match_array(['global'])
end
end end
end
context 'when the groups have ancestors' do context 'when the groups have ancestors' do
context 'when an ancestor has a level other than Global' do context 'when an ancestor has a level other than Global' do
let_it_be(:ancestor_a) { create(:group) } let_it_be(:ancestor_a) { create(:group) }
let_it_be(:group_a) { create(:group, parent: ancestor_a) } let_it_be(:group_a) { create(:group, parent: ancestor_a) }
let_it_be(:ancestor_b) { create(:group) } let_it_be(:ancestor_b) { create(:group) }
let_it_be(:group_b) { create(:group, parent: ancestor_b) } let_it_be(:group_b) { create(:group, parent: ancestor_b) }
let_it_be(:email) { create(:email, :confirmed, email: 'ancestor@example.com', user: user) } let_it_be(:email) { create(:email, :confirmed, email: 'ancestor@example.com', user: user) }
let_it_be(:groups) { [group_a, group_b] }
before do let_it_be(:groups) { [group_a, group_b] }
create(:notification_setting, user: user, source: ancestor_a, level: 'participating', notification_email: email.email)
create(:notification_setting, user: user, source: ancestor_b, level: 'participating', notification_email: email.email)
end
it 'has the same level set' do before do
expect(attributes(&:level)).to match_array(['participating']) create(:notification_setting, user: user, source: ancestor_a, level: 'participating', notification_email: email.email)
end create(:notification_setting, user: user, source: ancestor_b, level: 'participating', notification_email: email.email)
end
it 'has the same email set' do it 'has the same level set' do
expect(attributes(&:notification_email)).to match_array(['ancestor@example.com']) expect(attributes(&:level)).to match_array(['participating'])
end end
it 'only returns the two queried groups' do it 'has the same email set' do
expect(subject.count).to eq(2) expect(attributes(&:notification_email)).to match_array(['ancestor@example.com'])
end
end end
context 'when an ancestor has a Global level but has an email set' do it 'only returns the two queried groups' do
let_it_be(:grand_ancestor) { create(:group) } expect(subject.count).to eq(2)
let_it_be(:ancestor) { create(:group, parent: grand_ancestor) }
let_it_be(:group) { create(:group, parent: ancestor) }
let_it_be(:ancestor_email) { create(:email, :confirmed, email: 'ancestor@example.com', user: user) }
let_it_be(:grand_email) { create(:email, :confirmed, email: 'grand@example.com', user: user) }
let_it_be(:groups) { [group] }
before do
create(:notification_setting, user: user, source: grand_ancestor, level: 'participating', notification_email: grand_email.email)
create(:notification_setting, user: user, source: ancestor, level: 'global', notification_email: ancestor_email.email)
end
it 'has the same email and level set', :aggregate_failures do
expect(subject.count).to eq(1)
expect(attributes(&:level)).to match_array(['global'])
expect(attributes(&:notification_email)).to match_array(['ancestor@example.com'])
end
end end
end
context 'when the group has parent_id set but that does not belong to any group' do context 'when an ancestor has a Global level but has an email set' do
let_it_be(:group) { create(:group) } let_it_be(:grand_ancestor) { create(:group) }
let_it_be(:groups) { [group] } let_it_be(:ancestor) { create(:group, parent: grand_ancestor) }
let_it_be(:group) { create(:group, parent: ancestor) }
let_it_be(:ancestor_email) { create(:email, :confirmed, email: 'ancestor@example.com', user: user) }
let_it_be(:grand_email) { create(:email, :confirmed, email: 'grand@example.com', user: user) }
before do let_it_be(:groups) { [group] }
# Let's set a parent_id for a group that definitely doesn't exist
group.update_columns(parent_id: 19283746)
end
it 'returns a default Global notification setting' do before do
expect(subject.count).to eq(1) create(:notification_setting, user: user, source: grand_ancestor, level: 'participating', notification_email: grand_email.email)
expect(attributes(&:level)).to match_array(['global']) create(:notification_setting, user: user, source: ancestor, level: 'global', notification_email: ancestor_email.email)
expect(attributes(&:notification_email)).to match_array([nil])
end
end end
context 'when the group has a private parent' do it 'has the same email and level set', :aggregate_failures do
let_it_be(:ancestor) { create(:group, :private) } expect(subject.count).to eq(1)
let_it_be(:group) { create(:group, :private, parent: ancestor) } expect(attributes(&:level)).to match_array(['global'])
let_it_be(:ancestor_email) { create(:email, :confirmed, email: 'ancestor@example.com', user: user) } expect(attributes(&:notification_email)).to match_array(['ancestor@example.com'])
let_it_be(:groups) { [group] }
before do
group.add_reporter(user)
# Adding the user creates a NotificationSetting, so we remove it here
user.notification_settings.where(source: group).delete_all
create(:notification_setting, user: user, source: ancestor, level: 'participating', notification_email: ancestor_email.email)
end
it 'still inherits the notification settings' do
expect(subject.count).to eq(1)
expect(attributes(&:level)).to match_array(['participating'])
expect(attributes(&:notification_email)).to match_array([ancestor_email.email])
end
end end
end
it 'does not cause an N+1', :aggregate_failures do context 'when the group has parent_id set but that does not belong to any group' do
parent = create(:group) let_it_be(:group) { create(:group) }
child = create(:group, parent: parent) let_it_be(:groups) { [group] }
control = ActiveRecord::QueryRecorder.new do
described_class.new(user, Group.where(id: child.id)).execute
end
other_parent = create(:group)
other_children = create_list(:group, 2, parent: other_parent)
result = nil
expect do before do
result = described_class.new(user, Group.where(id: other_children.append(child).map(&:id))).execute # Let's set a parent_id for a group that definitely doesn't exist
end.not_to exceed_query_limit(control) group.update_columns(parent_id: 19283746)
end
expect(result.count).to eq(3) it 'returns a default Global notification setting' do
expect(subject.count).to eq(1)
expect(attributes(&:level)).to match_array(['global'])
expect(attributes(&:notification_email)).to match_array([nil])
end end
end end
end
context 'preloading `emails_disabled`' do context 'when the group has a private parent' do
let_it_be(:root_group) { create(:group) } let_it_be(:ancestor) { create(:group, :private) }
let_it_be(:sub_group) { create(:group, parent: root_group) } let_it_be(:group) { create(:group, :private, parent: ancestor) }
let_it_be(:sub_sub_group) { create(:group, parent: sub_group) } let_it_be(:ancestor_email) { create(:email, :confirmed, email: 'ancestor@example.com', user: user) }
let_it_be(:groups) { [group] }
let_it_be(:another_root_group) { create(:group) } before do
let_it_be(:sub_group_with_emails_disabled) { create(:group, emails_disabled: true, parent: another_root_group) } group.add_reporter(user)
let_it_be(:another_sub_sub_group) { create(:group, parent: sub_group_with_emails_disabled) } # Adding the user creates a NotificationSetting, so we remove it here
user.notification_settings.where(source: group).delete_all
let_it_be(:root_group_with_emails_disabled) { create(:group, emails_disabled: true) } create(:notification_setting, user: user, source: ancestor, level: 'participating', notification_email: ancestor_email.email)
let_it_be(:group) { create(:group, parent: root_group_with_emails_disabled) } end
let(:groups) { Group.where(id: [sub_sub_group, another_sub_sub_group, group]) }
before do it 'still inherits the notification settings' do
described_class.new(user, groups).execute expect(subject.count).to eq(1)
expect(attributes(&:level)).to match_array(['participating'])
expect(attributes(&:notification_email)).to match_array([ancestor_email.email])
end
end end
it 'preloads the `group.emails_disabled` method' do it 'does not cause an N+1', :aggregate_failures do
recorder = ActiveRecord::QueryRecorder.new do parent = create(:group)
groups.each(&:emails_disabled?) child = create(:group, parent: parent)
control = ActiveRecord::QueryRecorder.new do
described_class.new(user, Group.where(id: child.id)).execute
end end
expect(recorder.count).to eq(0) other_parent = create(:group)
end other_children = create_list(:group, 2, parent: other_parent)
it 'preloads the `group.emails_disabled` method correctly' do result = nil
groups.each do |group|
expect(group.emails_disabled?).to eq(Group.find(group.id).emails_disabled?) # compare the memoized and the freshly loaded value expect do
end result = described_class.new(user, Group.where(id: other_children.append(child).map(&:id))).execute
end.not_to exceed_query_limit(control)
expect(result.count).to eq(3)
end end
end end
end end
it_behaves_like 'user group notifications settings tests' context 'preloading `emails_disabled`' do
let_it_be(:root_group) { create(:group) }
let_it_be(:sub_group) { create(:group, parent: root_group) }
let_it_be(:sub_sub_group) { create(:group, parent: sub_group) }
let_it_be(:another_root_group) { create(:group) }
let_it_be(:sub_group_with_emails_disabled) { create(:group, emails_disabled: true, parent: another_root_group) }
let_it_be(:another_sub_sub_group) { create(:group, parent: sub_group_with_emails_disabled) }
let_it_be(:root_group_with_emails_disabled) { create(:group, emails_disabled: true) }
let_it_be(:group) { create(:group, parent: root_group_with_emails_disabled) }
let(:groups) { Group.where(id: [sub_sub_group, another_sub_sub_group, group]) }
context 'when feature flag :linear_user_group_notification_settings_finder_ancestors_scopes is disabled' do
before do before do
stub_feature_flags(linear_user_group_notification_settings_finder_ancestors_scopes: false) described_class.new(user, groups).execute
end
it 'preloads the `group.emails_disabled` method' do
recorder = ActiveRecord::QueryRecorder.new do
groups.each(&:emails_disabled?)
end
expect(recorder.count).to eq(0)
end end
it_behaves_like 'user group notifications settings tests' it 'preloads the `group.emails_disabled` method correctly' do
groups.each do |group|
expect(group.emails_disabled?).to eq(Group.find(group.id).emails_disabled?) # compare the memoized and the freshly loaded value
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