summaryrefslogtreecommitdiff
path: root/app/services/notification_recipient_service.rb
diff options
context:
space:
mode:
authorhttp://jneen.net/ <jneen@jneen.net>2017-08-01 12:55:13 -0700
committerhttp://jneen.net/ <jneen@jneen.net>2017-08-03 09:06:15 -0700
commit943baa13922a5a2b7aab5a46765846996f23486b (patch)
tree62a38ab64fec701ddd76c30bc2aea68daa14efb9 /app/services/notification_recipient_service.rb
parentb05e6efd535990c8a54f470faa319e4b59ec12a3 (diff)
downloadgitlab-ce-943baa13922a5a2b7aab5a46765846996f23486b.tar.gz
move filtering logic into Recipient class
Diffstat (limited to 'app/services/notification_recipient_service.rb')
-rw-r--r--app/services/notification_recipient_service.rb201
1 files changed, 102 insertions, 99 deletions
diff --git a/app/services/notification_recipient_service.rb b/app/services/notification_recipient_service.rb
index 8d15202ef1c..3d3ace29c16 100644
--- a/app/services/notification_recipient_service.rb
+++ b/app/services/notification_recipient_service.rb
@@ -33,9 +33,69 @@ class NotificationRecipientService
NotificationRecipientService.notification_setting_for_user_project(user, @builder.project)
end
- def notification_level
+ def raw_notification_level
notification_setting&.level&.to_sym
end
+
+ def notification_level
+ # custom is treated the same as watch if it's enabled - otherwise it's
+ # as :disabled.
+ @notification_level ||=
+ case raw_notification_level
+ when :custom
+ notification_setting.event_enabled?(@builder.custom_action) ? :watch : :custom
+ else
+ raw_notification_level
+ end
+ end
+
+ def notifiable?
+ return false unless has_access?
+ return false if own_activity?
+
+ return true if @type == :subscription
+
+ return false if notification_level.nil? || notification_level == :disabled
+
+ return %i[participating mention].include?(@type) if notification_level == :custom
+
+ return false if %i[watch participating].include?(notification_level) && excluded_watcher_action?
+
+ return false unless NotificationSetting.levels[notification_level] <= NotificationSetting.levels[type]
+
+ return false if unsubscribed?
+
+ true
+ end
+
+ def unsubscribed?
+ return false unless @builder.target.respond_to?(:subscriptions)
+
+ subscription = @builder.target.subscriptions.find_by_user_id(@user.id)
+ subscription && !subscription.subscribed
+ end
+
+ def own_activity?
+ return false unless @builder.acting_user
+ return false if @builder.acting_user.notified_of_own_activity?
+
+ user == @builder.acting_user
+ end
+
+ def has_access?
+ return false unless user.can?(:receive_notifications)
+ return true unless @builder.read_ability
+
+ DeclarativePolicy.subject_scope do
+ user.can?(@builder.read_ability, @builder.target)
+ end
+ end
+
+ def excluded_watcher_action?
+ return false if raw_notification_level == :custom
+
+ NotificationSetting::EXCLUDED_WATCHER_EVENTS.include?(@builder.custom_action)
+ end
end
module Builder
@@ -49,7 +109,11 @@ class NotificationRecipientService
end
def filter!
- raise 'abstract'
+ recipients.select!(&:notifiable?)
+ end
+
+ def acting_user
+ current_user
end
def target
@@ -78,12 +142,6 @@ class NotificationRecipientService
end
end
- # Remove users with disabled notifications from array
- # Also remove duplications and nil recipients
- def reject_muted_users
- reject_users(:disabled)
- end
-
def read_ability
@read_ability ||=
case target
@@ -99,23 +157,23 @@ class NotificationRecipientService
def add_participants(user)
return unless target.respond_to?(:participants)
- self << [target.participants(user), :participating]
+ self << [target.participants(user), :watch]
end
# Get project/group users with CUSTOM notification level
- def add_custom_notifications(action)
+ def add_custom_notifications
user_ids = []
# Users with a notification setting on group or project
- user_ids += user_ids_notifiable_on(project, :custom, action)
- user_ids += user_ids_notifiable_on(project.group, :custom, action)
+ user_ids += user_ids_notifiable_on(project, :custom)
+ user_ids += user_ids_notifiable_on(project.group, :custom)
# Users with global level custom
user_ids_with_project_level_global = user_ids_notifiable_on(project, :global)
user_ids_with_group_level_global = user_ids_notifiable_on(project.group, :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, custom_action)
self << [User.find(user_ids), :watch]
end
@@ -139,31 +197,22 @@ class NotificationRecipientService
User.where(id: user_ids_with_project_setting.concat(user_ids_with_group_setting).uniq).to_a
end
- # Remove users with notification level 'Mentioned'
- def reject_mention_users
- recipients.select! do |r|
- next true if r.type == :mention
- next true if r.type == :subscription
- r.notification_level != :mention
- end
- end
-
def add_subscribed_users
return unless target.respond_to? :subscribers
self << [target.subscribers(project), :subscription]
end
- def user_ids_notifiable_on(resource, notification_level = nil, action = nil)
+ def user_ids_notifiable_on(resource, notification_level = nil)
return [] unless resource
+ scope = resource.notification_settings
+
if notification_level
- settings = resource.notification_settings.where(level: NotificationSetting.levels[notification_level])
- settings = settings.select { |setting| setting.event_enabled?(action) } if action.present?
- settings.map(&:user_id)
- else
- resource.notification_settings.pluck(:user_id)
+ scope = scope.where(level: NotificationSetting.levels[notification_level])
end
+
+ scope.pluck(:user_id)
end
# Build a list of user_ids based on project notification settings
@@ -220,26 +269,6 @@ class NotificationRecipientService
)
end
- # Reject users which has certain notification level
- #
- # Example:
- # reject_users(:watch, project)
- #
- def reject_users(level)
- level = level.to_s
-
- unless NotificationSetting.levels.keys.include?(level)
- raise 'Invalid notification level'
- end
-
- recipients.reject! do |recipient|
- user = recipient.user
-
- setting = NotificationRecipientService.notification_setting_for_user_project(user, project)
- setting.present? && setting.level == level
- end
- end
-
def reject_unsubscribed_users
return unless target.respond_to? :subscriptions
@@ -294,12 +323,9 @@ class NotificationRecipientService
def build!
add_participants(current_user)
add_project_watchers
- add_custom_notifications(custom_action)
- reject_mention_users
+ add_custom_notifications
- # Re-assign is considered as a mention of the new assignee so we add the
- # new assignee to the list of recipients after we rejected users with
- # the "on mention" notification level
+ # Re-assign is considered as a mention of the new assignee
case custom_action
when :reassign_merge_request
self << [previous_assignee, :mention]
@@ -310,7 +336,6 @@ class NotificationRecipientService
self << [target.assignees, :mention]
end
- reject_muted_users
add_subscribed_users
if [:new_issue, :new_merge_request].include?(custom_action)
@@ -318,14 +343,10 @@ class NotificationRecipientService
end
end
- def filter!
- reject_unsubscribed_users
- reject_users_without_access
-
- reject_user(current_user) if skip_current_user && !current_user.notified_of_own_activity?
+ def acting_user
+ current_user if skip_current_user
end
- private
# Build event key to search on custom notification level
# Check NotificationSetting::EMAIL_EVENTS
def custom_action
@@ -345,30 +366,23 @@ class NotificationRecipientService
@action = action
end
- def build!
- return [] unless current_user
-
- custom_action =
- case action.to_s
- when 'failed'
- :failed_pipeline
- when 'success'
- :success_pipeline
- end
-
- notification_setting = NotificationRecipientService.notification_setting_for_user_project(current_user, target.project)
-
- return if notification_setting.mention? || notification_setting.disabled?
-
- return if notification_setting.custom? && !notification_setting.event_enabled?(custom_action)
-
- return if (notification_setting.watch? || notification_setting.participating?) && NotificationSetting::EXCLUDED_WATCHER_EVENTS.include?(custom_action)
+ def acting_user
+ nil
+ end
- self << [current_user, :subscriber]
+ def custom_action
+ case action.to_s
+ when 'failed'
+ :failed_pipeline
+ when 'success'
+ :success_pipeline
+ end
end
- def filter!
- reject_users_without_access
+ def build!
+ return [] unless current_user
+
+ self << [current_user, :watch]
end
end
@@ -387,12 +401,6 @@ class NotificationRecipientService
def build!
add_labels_subscribers(labels: labels)
end
-
- def filter!
- reject_unsubscribed_users
- reject_users_without_access
- reject_user(current_user) unless current_user.notified_of_own_activity?
- end
end
class NewNote < Base
@@ -420,30 +428,25 @@ class NotificationRecipientService
def build!
# Add all users participating in the thread (author, assignee, comment authors)
add_participants(note.author)
- self << [note.mentioned_users, :mention] if recipients.empty?
+ self << [note.mentioned_users, :mention]
unless note.for_personal_snippet?
# Merge project watchers
add_project_watchers
# Merge project with custom notification
- add_custom_notifications(:new_note)
+ add_custom_notifications
end
- # Reject users with Mention notification level, except those mentioned in _this_ note.
- reject_mention_users
- self << [note.mentioned_users, :mention]
-
- reject_muted_users
-
add_subscribed_users
end
- def filter!
- reject_unsubscribed_users
- reject_users_without_access
+ def custom_action
+ :new_note
+ end
- reject_user(note.author) unless note.author.notified_of_own_activity?
+ def acting_user
+ note.author
end
end
end