Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-04-20 06:09:33 +00:00
parent d306693dd6
commit 93f77228e3
11 changed files with 229 additions and 26 deletions

View File

@ -86,25 +86,7 @@ module Namespaces
raise UnboundedSearch.new('Must bound search by a top') unless top
without_sti_condition
.traversal_ids_contains(latest_traversal_ids(top))
end
# traversal_ids are a cached value.
#
# The traversal_ids value in a loaded object can become stale when compared
# to the database value. For example, if you load a hierarchy and then move
# a group, any previously loaded descendant objects will have out of date
# traversal_ids.
#
# To solve this problem, we never depend on the object's traversal_ids
# value. We always query the database first with a sub-select for the
# latest traversal_ids.
#
# Note that ActiveRecord will cache query results. You can avoid this by
# using `Model.uncached { ... }`
def latest_traversal_ids(namespace = self)
without_sti_condition.where('id = (?)', namespace)
.select('traversal_ids as latest_traversal_ids')
.traversal_ids_contains("{#{top.id}}")
end
end
end

View File

@ -1,6 +1,8 @@
# frozen_string_literal: true
class NotificationSetting < ApplicationRecord
include FromUnion
enum level: { global: 3, watch: 2, participating: 1, mention: 4, disabled: 0, custom: 5 }
default_value_for :level, NotificationSetting.levels[:global]

View File

@ -100,6 +100,8 @@ module NotificationRecipients
# Get project/group users with CUSTOM notification level
# rubocop: disable CodeReuse/ActiveRecord
def add_custom_notifications
return new_add_custom_notifications if Feature.enabled?(:notification_setting_recipient_refactor, project)
user_ids = []
# Users with a notification setting on group or project
@ -115,6 +117,48 @@ module NotificationRecipients
add_recipients(user_scope.where(id: user_ids), :custom, nil)
end
def new_add_custom_notifications
notification_by_sources = related_notification_settings_sources(:custom)
return if notification_by_sources.blank?
user_ids = NotificationSetting.from_union(notification_by_sources).select(:user_id)
add_recipients(user_scope.where(id: user_ids), :custom, nil)
end
def related_notification_settings_sources(level)
sources = [project, group].compact
sources.map do |source|
source
.notification_settings
.where(source_or_global_setting_by_level_query(level)).select(:user_id)
end
end
def global_setting_by_level_query(level)
table = NotificationSetting.arel_table
aliased_table = table.alias
table
.project('true')
.from(aliased_table)
.where(
aliased_table[:user_id].eq(table[:user_id])
.and(aliased_table[:source_id].eq(nil))
.and(aliased_table[:source_type].eq(nil))
.and(aliased_table[:level].eq(level))
).exists
end
def source_or_global_setting_by_level_query(level)
table = NotificationSetting.arel_table
table.grouping(
table[:level].eq(:global).and(global_setting_by_level_query(level))
).or(table[:level].eq(level))
end
# rubocop: enable CodeReuse/ActiveRecord
def add_project_watchers

View File

@ -0,0 +1,8 @@
---
name: notification_setting_recipient_refactor
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57688
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/327303
milestone: '13.11'
type: development
group: group::code review
default_enabled: false

View File

@ -12,7 +12,7 @@ To enable the Atlassian OmniAuth provider for passwordless authentication you mu
## Atlassian application registration
1. Go to <https://developer.atlassian.com/apps/> and sign-in with the Atlassian
account that will administer the application.
account to administer the application with.
1. Click **Create a new app**.

View File

@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
To enable the Authentiq OmniAuth provider for passwordless authentication you must register an application with Authentiq.
Authentiq will generate a Client ID and the accompanying Client Secret for you to use.
Authentiq generates a Client ID and the accompanying Client Secret for you to use.
1. Get your Client credentials (Client ID and Client Secret) at [Authentiq](https://www.authentiq.com/developers).

View File

@ -62,6 +62,7 @@ Before you push your changes, Lefthook automatically runs the following checks:
- SCSS lint: Run `yarn lint:stylelint` checks (with the [`.stylelintrc`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.stylelintrc) configuration) on the modified `*.scss{,.css}` files. Tags: `stylesheet`, `css`, `style`.
- RuboCop: Run `bundle exec rubocop` checks (with the [`.rubocop.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.rubocop.yml) configuration) on the modified `*.rb` files. Tags: `backend`, `style`.
- Vale: Run `vale` checks (with the [`.vale.ini`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.vale.ini) configuration) on the modified `*.md` files. Tags: `documentation`, `style`.
- Documentation metadata: Run checks for the absence of [documentation metadata](../documentation/index.md#metadata).
In addition to the default configuration, you can define a [local configuration](https://github.com/Arkweid/lefthook/blob/master/docs/full_guide.md#local-config).

View File

@ -38,3 +38,8 @@ pre-push:
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
glob: 'doc/*.md'
run: if command -v vale 2> /dev/null; then vale --config .vale.ini --minAlertLevel error {files}; else echo "Vale not found. Install Vale"; fi
docs-metadata: # https://docs.gitlab.com/ee/development/documentation/#metadata
tags: documentation style
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
glob: '*.md'
run: scripts/lint-docs-metadata.sh "{files}"

View File

@ -19,6 +19,7 @@ then
fi
# Documentation pages need front matter for tracking purposes.
# See also lint-docs-metadata.sh
echo '=> Checking documentation for front matter...'
echo
no_frontmatter=$(find doc -name "*.md" -exec head -n1 {} \; | grep -v --count -- ---)

24
scripts/lint-docs-metadata.sh Executable file
View File

@ -0,0 +1,24 @@
#!/usr/bin/env bash
# Used in `lefthook.yml`
(( counter=0 ))
for file in $1
do
if [ "$(head -n1 "$file")" != "---" ]
then
printf "%sDocumentation metadata missing in %s%s\n" "$(tput setaf 1)" "$file" "$(tput sgr0)"
(( counter++ ))
else
printf "Documentation metadata found in %s.\n" "$file"
fi
done
if [ "$counter" -gt 0 ]
then
printf "\n%sDocumentation metadata is missing in changed documentation files.%s For more information, see https://docs.gitlab.com/ee/development/documentation/#metadata.\n" "$(tput setaf 1)" "$(tput sgr0)"
false
else
printf "\n%sDocumentation metadata found in all changed documentation files.%s\n" "$(tput setaf 2)" "$(tput sgr0)"
true
fi

View File

@ -6,7 +6,7 @@ RSpec.describe NotificationRecipients::Builder::Default do
describe '#build!' do
let_it_be(:group) { create(:group, :public) }
let_it_be(:project) { create(:project, :public, group: group).tap { |p| p.add_developer(project_watcher) } }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:target) { create(:issue, project: project) }
let_it_be(:current_user) { create(:user) }
let_it_be(:other_user) { create(:user) }
@ -17,11 +17,11 @@ RSpec.describe NotificationRecipients::Builder::Default do
let_it_be(:notification_setting_project_w) { create(:notification_setting, source: project, user: project_watcher, level: 2) }
let_it_be(:notification_setting_group_w) { create(:notification_setting, source: group, user: group_watcher, level: 2) }
subject { described_class.new(issue, current_user, action: :new).tap { |s| s.build! } }
subject { described_class.new(target, current_user, action: :new).tap { |s| s.build! } }
context 'participants and project watchers' do
before do
expect(issue).to receive(:participants).and_return([participant, current_user])
expect(target).to receive(:participants).and_return([participant, current_user])
end
it 'adds all participants and watchers' do
@ -34,11 +34,147 @@ RSpec.describe NotificationRecipients::Builder::Default do
it 'adds all subscribers' do
subscriber = create(:user)
non_subscriber = create(:user)
create(:subscription, project: project, user: subscriber, subscribable: issue, subscribed: true)
create(:subscription, project: project, user: non_subscriber, subscribable: issue, subscribed: false)
create(:subscription, project: project, user: subscriber, subscribable: target, subscribed: true)
create(:subscription, project: project, user: non_subscriber, subscribable: target, subscribed: false)
expect(subject.recipients.map(&:user)).to include(subscriber)
end
end
context 'custom notifications' do
shared_examples 'custom notification recipients' do
let_it_be(:custom_notification_user) { create(:user) }
let_it_be(:another_group) { create(:group) }
let_it_be(:another_project) { create(:project, namespace: another_group) }
context 'with project custom notification setting' do
before do
create(:notification_setting, source: project, user: custom_notification_user, level: :custom)
end
it 'adds the user to the recipients' do
expect(subject.recipients.map(&:user)).to include(custom_notification_user)
end
end
context 'with the project custom notification setting in another project' do
before do
create(:notification_setting, source: another_project, user: custom_notification_user, level: :custom)
end
it 'does not add the user to the recipients' do
expect(subject.recipients.map(&:user)).not_to include(custom_notification_user)
end
end
context 'with group custom notification setting' do
before do
create(:notification_setting, source: group, user: custom_notification_user, level: :custom)
end
it 'adds the user to the recipients' do
expect(subject.recipients.map(&:user)).to include(custom_notification_user)
end
end
context 'with the group custom notification setting in another group' do
before do
create(:notification_setting, source: another_group, user: custom_notification_user, level: :custom)
end
it 'does not add the user to the recipients' do
expect(subject.recipients.map(&:user)).not_to include(custom_notification_user)
end
end
context 'with project global custom notification setting' do
before do
create(:notification_setting, source: project, user: custom_notification_user, level: :global)
end
context 'with global custom notification setting' do
before do
create(:notification_setting, source: nil, user: custom_notification_user, level: :custom)
end
it 'adds the user to the recipients' do
expect(subject.recipients.map(&:user)).to include(custom_notification_user)
end
end
context 'without global custom notification setting' do
it 'does not add the user to the recipients' do
expect(subject.recipients.map(&:user)).not_to include(custom_notification_user)
end
end
end
context 'with group global custom notification setting' do
before do
create(:notification_setting, source: group, user: custom_notification_user, level: :global)
end
context 'with global custom notification setting' do
before do
create(:notification_setting, source: nil, user: custom_notification_user, level: :custom)
end
it 'adds the user to the recipients' do
expect(subject.recipients.map(&:user)).to include(custom_notification_user)
end
end
context 'without global custom notification setting' do
it 'does not add the user to the recipients' do
expect(subject.recipients.map(&:user)).not_to include(custom_notification_user)
end
end
end
context 'with group custom notification setting in deeply nested parent group' do
let(:grand_parent_group) { create(:group, :public) }
let(:parent_group) { create(:group, :public, parent: grand_parent_group) }
let(:group) { create(:group, :public, parent: parent_group) }
let(:project) { create(:project, :public, group: group).tap { |p| p.add_developer(project_watcher) } }
let(:target) { create(:issue, project: project) }
before do
create(:notification_setting, source: grand_parent_group, user: custom_notification_user, level: :custom)
end
it 'adds the user to the recipients' do
expect(subject.recipients.map(&:user)).to include(custom_notification_user)
end
end
context 'without a project or group' do
let(:target) { create(:snippet) }
before do
create(:notification_setting, source: nil, user: custom_notification_user, level: :custom)
end
it 'does not add the user to the recipients' do
expect(subject.recipients.map(&:user)).not_to include(custom_notification_user)
end
end
end
before do
stub_feature_flags(notification_setting_recipient_refactor: enabled)
end
context 'with notification_setting_recipient_refactor enabled' do
let(:enabled) { true }
it_behaves_like 'custom notification recipients'
end
context 'with notification_setting_recipient_refactor disabled' do
let(:enabled) { false }
it_behaves_like 'custom notification recipients'
end
end
end
end