Commit a09d218f authored by http://jneen.net/'s avatar http://jneen.net/

add builder helpers with levels and start to separate out #filter!

parent f7e059af
...@@ -20,13 +20,35 @@ class NotificationRecipientService ...@@ -20,13 +20,35 @@ class NotificationRecipientService
@project = project @project = project
end end
class Recipient
attr_reader :user, :type
def initialize(builder, user, type)
@builder = builder
@user = user
@type = type
end
def notification_setting
@notification_setting ||=
NotificationRecipientService.notification_setting_for_user_project(user, @builder.project)
end
def notification_level
notification_setting&.level&.to_sym
end
end
module Builder module Builder
class Base class Base
def initialize(*) def initialize(*)
raise 'abstract' raise 'abstract'
end end
def build def build!
raise 'abstract'
end
def filter!
raise 'abstract' raise 'abstract'
end end
...@@ -38,13 +60,22 @@ class NotificationRecipientService ...@@ -38,13 +60,22 @@ class NotificationRecipientService
@recipients ||= [] @recipients ||= []
end end
def to_a def <<(arg)
return recipients if @already_built users, type = arg
@already_built = true users = Array(users)
build users.compact!
recipients.uniq! recipients.concat(users.map { |u| Recipient.new(self, u, type) })
recipients.freeze end
recipients
def recipient_users
@recipient_users ||=
begin
build!
filter!
users = recipients.map(&:user)
users.uniq!
users.freeze
end
end end
# Remove users with disabled notifications from array # Remove users with disabled notifications from array
...@@ -53,12 +84,22 @@ class NotificationRecipientService ...@@ -53,12 +84,22 @@ class NotificationRecipientService
reject_users(:disabled) reject_users(:disabled)
end end
def read_ability
@read_ability ||=
case target
when Issuable
:"read_#{target.to_ability_name}"
when Ci::Pipeline
:read_build # We have build trace in pipeline emails
end
end
protected protected
def add_participants(user) def add_participants(user)
return unless target.respond_to?(:participants) return unless target.respond_to?(:participants)
recipients.concat(target.participants(user)) self << [target.participants(user), :participating]
end end
# Get project/group users with CUSTOM notification level # Get project/group users with CUSTOM notification level
...@@ -76,12 +117,11 @@ class NotificationRecipientService ...@@ -76,12 +117,11 @@ class NotificationRecipientService
global_users_ids = user_ids_with_project_level_global.concat(user_ids_with_group_level_global) global_users_ids = user_ids_with_project_level_global.concat(user_ids_with_group_level_global)
user_ids += user_ids_with_global_level_custom(global_users_ids, action) user_ids += user_ids_with_global_level_custom(global_users_ids, action)
recipients.concat(User.find(user_ids)) self << [User.find(user_ids), :watch]
end end
def add_project_watchers def add_project_watchers
recipients.concat(project_watchers) self << [project_watchers, :watch]
recipients.compact!
end end
# Get project users with WATCH notification level # Get project users with WATCH notification level
...@@ -101,13 +141,17 @@ class NotificationRecipientService ...@@ -101,13 +141,17 @@ class NotificationRecipientService
# Remove users with notification level 'Mentioned' # Remove users with notification level 'Mentioned'
def reject_mention_users def reject_mention_users
reject_users(:mention) recipients.select! do |r|
next true if r.type == :mention
next true if r.type == :subscription
r.notification_level != :mention
end
end end
def add_subscribed_users def add_subscribed_users
return unless target.respond_to? :subscribers return unless target.respond_to? :subscribers
recipients.concat(target.subscribers(project)) self << [target.subscribers(project), :subscription]
end end
def user_ids_notifiable_on(resource, notification_level = nil, action = nil) def user_ids_notifiable_on(resource, notification_level = nil, action = nil)
...@@ -188,10 +232,9 @@ class NotificationRecipientService ...@@ -188,10 +232,9 @@ class NotificationRecipientService
raise 'Invalid notification level' raise 'Invalid notification level'
end end
recipients.compact! recipients.reject! do |recipient|
recipients.uniq! user = recipient.user
recipients.reject! do |user|
setting = NotificationRecipientService.notification_setting_for_user_project(user, project) setting = NotificationRecipientService.notification_setting_for_user_project(user, project)
setting.present? && setting.level == level setting.present? && setting.level == level
end end
...@@ -200,34 +243,34 @@ class NotificationRecipientService ...@@ -200,34 +243,34 @@ class NotificationRecipientService
def reject_unsubscribed_users def reject_unsubscribed_users
return unless target.respond_to? :subscriptions return unless target.respond_to? :subscriptions
recipients.reject! do |user| recipients.reject! do |recipient|
user = recipient.user
subscription = target.subscriptions.find_by_user_id(user.id) subscription = target.subscriptions.find_by_user_id(user.id)
subscription && !subscription.subscribed subscription && !subscription.subscribed
end end
end end
def reject_users_without_access def reject_users_without_access
recipients.select! { |u| u.can?(:receive_notifications) } recipients.select! { |r| r.user.can?(:receive_notifications) }
ability = case target
when Issuable
:"read_#{target.to_ability_name}"
when Ci::Pipeline
:read_build # We have build trace in pipeline emails
end
return unless ability return unless read_ability
recipients.select! do |user| DeclarativePolicy.subject_scope do
user.can?(ability, target) recipients.select! do |recipient|
recipient.user.can?(read_ability, target)
end
end end
end end
def reject_user(user)
recipients.reject! { |r| r.user == user }
end
def add_labels_subscribers(labels: nil) def add_labels_subscribers(labels: nil)
return unless target.respond_to? :labels return unless target.respond_to? :labels
(labels || target.labels).each do |label| (labels || target.labels).each do |label|
recipients.concat(label.subscribers(project)) self << [label.subscribers(project), :subscription]
end end
end end
end end
...@@ -248,7 +291,7 @@ class NotificationRecipientService ...@@ -248,7 +291,7 @@ class NotificationRecipientService
@skip_current_user = skip_current_user @skip_current_user = skip_current_user
end end
def build def build!
add_participants(current_user) add_participants(current_user)
add_project_watchers add_project_watchers
add_custom_notifications(custom_action) add_custom_notifications(custom_action)
...@@ -259,12 +302,12 @@ class NotificationRecipientService ...@@ -259,12 +302,12 @@ class NotificationRecipientService
# the "on mention" notification level # the "on mention" notification level
case custom_action case custom_action
when :reassign_merge_request when :reassign_merge_request
recipients << previous_assignee if previous_assignee self << [previous_assignee, :mention]
recipients << target.assignee self << [target.assignee, :mention]
when :reassign_issue when :reassign_issue
previous_assignees = Array(previous_assignee) previous_assignees = Array(previous_assignee)
recipients.concat(previous_assignees) self << [previous_assignees, :mention]
recipients.concat(target.assignees) self << [target.assignees, :mention]
end end
reject_muted_users reject_muted_users
...@@ -273,13 +316,16 @@ class NotificationRecipientService ...@@ -273,13 +316,16 @@ class NotificationRecipientService
if [:new_issue, :new_merge_request].include?(custom_action) if [:new_issue, :new_merge_request].include?(custom_action)
add_labels_subscribers add_labels_subscribers
end end
end
def filter!
reject_unsubscribed_users reject_unsubscribed_users
reject_users_without_access reject_users_without_access
recipients.delete(current_user) if skip_current_user && !current_user.notified_of_own_activity? reject_user(current_user) if skip_current_user && !current_user.notified_of_own_activity?
end end
private
# Build event key to search on custom notification level # Build event key to search on custom notification level
# Check NotificationSetting::EMAIL_EVENTS # Check NotificationSetting::EMAIL_EVENTS
def custom_action def custom_action
...@@ -299,7 +345,7 @@ class NotificationRecipientService ...@@ -299,7 +345,7 @@ class NotificationRecipientService
@action = action @action = action
end end
def build def build!
return [] unless current_user return [] unless current_user
custom_action = custom_action =
...@@ -318,7 +364,10 @@ class NotificationRecipientService ...@@ -318,7 +364,10 @@ class NotificationRecipientService
return if (notification_setting.watch? || notification_setting.participating?) && NotificationSetting::EXCLUDED_WATCHER_EVENTS.include?(custom_action) return if (notification_setting.watch? || notification_setting.participating?) && NotificationSetting::EXCLUDED_WATCHER_EVENTS.include?(custom_action)
recipients << current_user self << [current_user, :subscriber]
end
def filter!
reject_users_without_access reject_users_without_access
end end
end end
...@@ -335,11 +384,14 @@ class NotificationRecipientService ...@@ -335,11 +384,14 @@ class NotificationRecipientService
@labels = labels @labels = labels
end end
def build def build!
add_labels_subscribers(labels: labels) add_labels_subscribers(labels: labels)
end
def filter!
reject_unsubscribed_users reject_unsubscribed_users
reject_users_without_access reject_users_without_access
recipients.delete(current_user) unless current_user.notified_of_own_activity? reject_user(current_user) unless current_user.notified_of_own_activity?
end end
end end
...@@ -353,18 +405,22 @@ class NotificationRecipientService ...@@ -353,18 +405,22 @@ class NotificationRecipientService
@target = note.noteable @target = note.noteable
end end
def build def read_ability
ability, subject = if note.for_personal_snippet? @read_ability ||=
[:read_personal_snippet, note.noteable] case target
else when Commit then nil
[:read_project, note.project] else :"read_#{target.class.model_name.name.underscore}"
end end
end
mentioned_users = note.mentioned_users.select { |user| user.can?(ability, subject) } def subject
note.for_personal_snippet? ? note.noteable : note.project
end
def build!
# Add all users participating in the thread (author, assignee, comment authors) # Add all users participating in the thread (author, assignee, comment authors)
add_participants(note.author) add_participants(note.author)
recipients.concat(mentioned_users) if recipients.empty? self << [note.mentioned_users, :mention] if recipients.empty?
unless note.for_personal_snippet? unless note.for_personal_snippet?
# Merge project watchers # Merge project watchers
...@@ -376,32 +432,35 @@ class NotificationRecipientService ...@@ -376,32 +432,35 @@ class NotificationRecipientService
# Reject users with Mention notification level, except those mentioned in _this_ note. # Reject users with Mention notification level, except those mentioned in _this_ note.
reject_mention_users reject_mention_users
recipients.concat(mentioned_users) self << [note.mentioned_users, :mention]
reject_muted_users reject_muted_users
add_subscribed_users add_subscribed_users
end
def filter!
reject_unsubscribed_users reject_unsubscribed_users
reject_users_without_access reject_users_without_access
recipients.delete(note.author) unless note.author.notified_of_own_activity? reject_user(note.author) unless note.author.notified_of_own_activity?
end end
end end
end end
def build_recipients(*a) def build_recipients(*a)
Builder::Default.new(@project, *a).to_a Builder::Default.new(@project, *a).recipient_users
end end
def build_pipeline_recipients(*a) def build_pipeline_recipients(*a)
Builder::Pipeline.new(@project, *a).to_a Builder::Pipeline.new(@project, *a).recipient_users
end end
def build_relabeled_recipients(*a) def build_relabeled_recipients(*a)
Builder::Relabeled.new(@project, *a).to_a Builder::Relabeled.new(@project, *a).recipient_users
end end
def build_new_note_recipients(*a) def build_new_note_recipients(*a)
Builder::NewNote.new(@project, *a).to_a Builder::NewNote.new(@project, *a).recipient_users
end end
end end
...@@ -309,6 +309,11 @@ describe NotificationService do ...@@ -309,6 +309,11 @@ describe NotificationService do
before do before do
build_team(note.project) build_team(note.project)
# make sure these users can read the project snippet!
project.add_guest(@u_guest_watcher)
project.add_guest(@u_guest_custom)
note.project.add_master(note.author) note.project.add_master(note.author)
reset_delivered_emails! reset_delivered_emails!
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