Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/services')
-rw-r--r--app/services/auth/container_registry_authentication_service.rb7
-rw-r--r--app/services/ci/register_job_service.rb24
-rw-r--r--app/services/delete_merged_branches_service.rb2
-rw-r--r--app/services/git_operation_service.rb2
-rw-r--r--app/services/issuable_base_service.rb7
-rw-r--r--app/services/merge_requests/create_service.rb7
-rw-r--r--app/services/notification_recipient_service.rb471
-rw-r--r--app/services/notification_service.rb69
-rw-r--r--app/services/projects/autocomplete_service.rb10
-rw-r--r--app/services/projects/create_from_template_service.rb15
-rw-r--r--app/services/projects/create_service.rb4
-rw-r--r--app/services/projects/destroy_service.rb6
-rw-r--r--app/services/projects/gitlab_projects_import_service.rb36
-rw-r--r--app/services/projects/import_export/export_service.rb2
-rw-r--r--app/services/projects/import_service.rb12
-rw-r--r--app/services/projects/transfer_service.rb4
-rw-r--r--app/services/projects/update_pages_service.rb18
-rw-r--r--app/services/quick_actions/interpret_service.rb7
-rw-r--r--app/services/submit_usage_ping_service.rb20
-rw-r--r--app/services/system_hooks_service.rb8
-rw-r--r--app/services/system_note_service.rb3
-rw-r--r--app/services/todo_service.rb16
-rw-r--r--app/services/web_hook_service.rb8
-rw-r--r--app/services/wiki_pages/update_service.rb2
24 files changed, 428 insertions, 332 deletions
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb
index 5e151b0f044..7dae5880931 100644
--- a/app/services/auth/container_registry_authentication_service.rb
+++ b/app/services/auth/container_registry_authentication_service.rb
@@ -103,6 +103,8 @@ module Auth
build_can_pull?(requested_project) || user_can_pull?(requested_project)
when 'push'
build_can_push?(requested_project) || user_can_push?(requested_project)
+ when '*'
+ user_can_admin?(requested_project)
else
false
end
@@ -120,6 +122,11 @@ module Auth
(requested_project == project || can?(current_user, :build_read_container_image, requested_project))
end
+ def user_can_admin?(requested_project)
+ has_authentication_ability?(:admin_container_image) &&
+ can?(current_user, :admin_container_image, requested_project)
+ end
+
def user_can_pull?(requested_project)
has_authentication_ability?(:read_container_image) &&
can?(current_user, :read_container_image, requested_project)
diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb
index b951e8d0c9f..fc87bd6a659 100644
--- a/app/services/ci/register_job_service.rb
+++ b/app/services/ci/register_job_service.rb
@@ -30,6 +30,7 @@ module Ci
# with StateMachines::InvalidTransition or StaleObjectError when doing run! or save method.
build.runner_id = runner.id
build.run!
+ register_success(build)
return Result.new(build, true)
rescue StateMachines::InvalidTransition, ActiveRecord::StaleObjectError
@@ -46,6 +47,7 @@ module Ci
end
end
+ register_failure
Result.new(nil, valid)
end
@@ -81,5 +83,27 @@ module Ci
def shared_runner_build_limits_feature_enabled?
ENV['DISABLE_SHARED_RUNNER_BUILD_MINUTES_LIMIT'].to_s != 'true'
end
+
+ def register_failure
+ failed_attempt_counter.increase
+ attempt_counter.increase
+ end
+
+ def register_success(job)
+ job_queue_duration_seconds.observe({ shared_runner: @runner.shared? }, Time.now - job.created_at)
+ attempt_counter.increase
+ end
+
+ def failed_attempt_counter
+ @failed_attempt_counter ||= Gitlab::Metrics.counter(:job_register_attempts_failed_total, "Counts the times a runner tries to register a job")
+ end
+
+ def attempt_counter
+ @attempt_counter ||= Gitlab::Metrics.counter(:job_register_attempts_total, "Counts the times a runner tries to register a job")
+ end
+
+ def job_queue_duration_seconds
+ @job_queue_duration_seconds ||= Gitlab::Metrics.histogram(:job_queue_duration_seconds, 'Request handling execution time')
+ end
end
end
diff --git a/app/services/delete_merged_branches_service.rb b/app/services/delete_merged_branches_service.rb
index 5c9e2a16c71..ff11bd59d29 100644
--- a/app/services/delete_merged_branches_service.rb
+++ b/app/services/delete_merged_branches_service.rb
@@ -11,7 +11,7 @@ class DeleteMergedBranchesService < BaseService
# Prevent deletion of branches relevant to open merge requests
branches -= merge_request_branch_names
# Prevent deletion of protected branches
- branches -= project.protected_branches.pluck(:name)
+ branches = branches.reject { |branch| project.protected_for?(branch) }
branches.each do |branch|
DeleteBranchService.new(project, current_user).execute(branch)
diff --git a/app/services/git_operation_service.rb b/app/services/git_operation_service.rb
index 32925e9c1f2..545ca0742e4 100644
--- a/app/services/git_operation_service.rb
+++ b/app/services/git_operation_service.rb
@@ -60,7 +60,7 @@ class GitOperationService
start_branch_name = nil if start_repository.empty_repo?
if start_branch_name && !start_repository.branch_exists?(start_branch_name)
- raise ArgumentError, "Cannot find branch #{start_branch_name} in #{start_repository.path_with_namespace}"
+ raise ArgumentError, "Cannot find branch #{start_branch_name} in #{start_repository.full_path}"
end
update_branch_with_hooks(branch_name) do
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 0e25c555136..b84a6fd2b7d 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -2,11 +2,8 @@ class IssuableBaseService < BaseService
private
def create_milestone_note(issuable)
- milestone = issuable.milestone
- return if milestone && milestone.is_group_milestone?
-
SystemNoteService.change_milestone(
- issuable, issuable.project, current_user, milestone)
+ issuable, issuable.project, current_user, issuable.milestone)
end
def create_labels_note(issuable, old_labels)
@@ -287,7 +284,7 @@ class IssuableBaseService < BaseService
todo_service.mark_todo(issuable, current_user)
when 'done'
todo = TodosFinder.new(current_user).execute.find_by(target: issuable)
- todo_service.mark_todos_as_done([todo], current_user) if todo
+ todo_service.mark_todos_as_done_by_ids(todo, current_user) if todo
end
end
diff --git a/app/services/merge_requests/create_service.rb b/app/services/merge_requests/create_service.rb
index eb04048b748..7d539fa49e6 100644
--- a/app/services/merge_requests/create_service.rb
+++ b/app/services/merge_requests/create_service.rb
@@ -12,7 +12,6 @@ module MergeRequests
merge_request.source_project = source_project
merge_request.source_branch = params[:source_branch]
merge_request.merge_params['force_remove_source_branch'] = params.delete(:force_remove_source_branch)
- merge_request.head_pipeline = head_pipeline_for(merge_request)
create(merge_request)
end
@@ -28,10 +27,16 @@ module MergeRequests
event_service.open_mr(issuable, current_user)
todo_service.new_merge_request(issuable, current_user)
issuable.cache_merge_request_closes_issues!(current_user)
+ update_merge_requests_head_pipeline(issuable)
end
private
+ def update_merge_requests_head_pipeline(merge_request)
+ pipeline = head_pipeline_for(merge_request)
+ merge_request.update(head_pipeline_id: pipeline.id) if pipeline
+ end
+
def head_pipeline_for(merge_request)
return unless merge_request.source_project
diff --git a/app/services/notification_recipient_service.rb b/app/services/notification_recipient_service.rb
index 9ac561e4bd2..21c9c314a2a 100644
--- a/app/services/notification_recipient_service.rb
+++ b/app/services/notification_recipient_service.rb
@@ -1,331 +1,288 @@
#
# Used by NotificationService to determine who should receive notification
#
-class NotificationRecipientService
- attr_reader :project
-
- def initialize(project)
- @project = project
+module NotificationRecipientService
+ def self.notifiable_users(users, *args)
+ users.compact.map { |u| NotificationRecipient.new(u, *args) }.select(&:notifiable?).map(&:user)
end
- def build_recipients(target, current_user, action:, previous_assignee: nil, skip_current_user: true)
- custom_action = build_custom_key(action, target)
-
- recipients = participants(target, current_user)
- recipients = add_project_watchers(recipients)
- recipients = add_custom_notifications(recipients, custom_action)
- recipients = reject_mention_users(recipients)
-
- # 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
- case custom_action
- when :reassign_merge_request
- recipients << previous_assignee if previous_assignee
- recipients << target.assignee
- when :reassign_issue
- previous_assignees = Array(previous_assignee)
- recipients.concat(previous_assignees)
- recipients.concat(target.assignees)
- end
-
- recipients = reject_muted_users(recipients)
- recipients = add_subscribed_users(recipients, target)
-
- if [:new_issue, :new_merge_request].include?(custom_action)
- recipients = add_labels_subscribers(recipients, target)
- end
-
- recipients = reject_unsubscribed_users(recipients, target)
- recipients = reject_users_without_access(recipients, target)
+ def self.notifiable?(user, *args)
+ NotificationRecipient.new(user, *args).notifiable?
+ end
- recipients.delete(current_user) if skip_current_user && !current_user.notified_of_own_activity?
+ def self.build_recipients(*a)
+ Builder::Default.new(*a).recipient_users
+ end
- recipients.uniq
+ def self.build_new_note_recipients(*a)
+ Builder::NewNote.new(*a).recipient_users
end
- def build_pipeline_recipients(target, current_user, action:)
- return [] unless current_user
+ module Builder
+ class Base
+ def initialize(*)
+ raise 'abstract'
+ end
- custom_action =
- case action.to_s
- when 'failed'
- :failed_pipeline
- when 'success'
- :success_pipeline
+ def build!
+ raise 'abstract'
end
- notification_setting = notification_setting_for_user_project(current_user, target.project)
+ def filter!
+ recipients.select!(&:notifiable?)
+ end
- return [] if notification_setting.mention? || notification_setting.disabled?
+ def acting_user
+ current_user
+ end
- return [] if notification_setting.custom? && !notification_setting.event_enabled?(custom_action)
+ def target
+ raise 'abstract'
+ end
- return [] if (notification_setting.watch? || notification_setting.participating?) && NotificationSetting::EXCLUDED_WATCHER_EVENTS.include?(custom_action)
+ # rubocop:disable Rails/Delegate
+ def project
+ target.project
+ end
- reject_users_without_access([current_user], target)
- end
+ def recipients
+ @recipients ||= []
+ end
- def build_relabeled_recipients(target, current_user, labels:)
- recipients = add_labels_subscribers([], target, labels: labels)
- recipients = reject_unsubscribed_users(recipients, target)
- recipients = reject_users_without_access(recipients, target)
- recipients.delete(current_user) unless current_user.notified_of_own_activity?
- recipients.uniq
- end
+ def <<(pair)
+ users, type = pair
- def build_new_note_recipients(note)
- target = note.noteable
+ if users.is_a?(ActiveRecord::Relation)
+ users = users.includes(:notification_settings)
+ end
- ability, subject = if note.for_personal_snippet?
- [:read_personal_snippet, note.noteable]
- else
- [:read_project, note.project]
- end
+ users = Array(users)
+ users.compact!
+ recipients.concat(users.map { |u| make_recipient(u, type) })
+ end
- mentioned_users = note.mentioned_users.select { |user| user.can?(ability, subject) }
+ def user_scope
+ User.includes(:notification_settings)
+ end
- # Add all users participating in the thread (author, assignee, comment authors)
- recipients = participants(target, note.author) || mentioned_users
+ def make_recipient(user, type)
+ NotificationRecipient.new(
+ user, type,
+ project: project,
+ custom_action: custom_action,
+ target: target,
+ acting_user: acting_user
+ )
+ end
- unless note.for_personal_snippet?
- # Merge project watchers
- recipients = add_project_watchers(recipients)
+ def recipient_users
+ @recipient_users ||=
+ begin
+ build!
+ filter!
+ users = recipients.map(&:user)
+ users.uniq!
+ users.freeze
+ end
+ end
- # Merge project with custom notification
- recipients = add_custom_notifications(recipients, :new_note)
- end
+ def custom_action
+ nil
+ end
- # Reject users with Mention notification level, except those mentioned in _this_ note.
- recipients = reject_mention_users(recipients - mentioned_users)
- recipients = recipients + mentioned_users
+ protected
- recipients = reject_muted_users(recipients)
+ def add_participants(user)
+ return unless target.respond_to?(:participants)
- recipients = add_subscribed_users(recipients, note.noteable)
- recipients = reject_unsubscribed_users(recipients, note.noteable)
- recipients = reject_users_without_access(recipients, note.noteable)
+ self << [target.participants(user), :watch]
+ end
- recipients.delete(note.author) unless note.author.notified_of_own_activity?
- recipients.uniq
- end
+ # Get project/group users with CUSTOM notification level
+ def add_custom_notifications
+ user_ids = []
- # Remove users with disabled notifications from array
- # Also remove duplications and nil recipients
- def reject_muted_users(users)
- reject_users(users, :disabled)
- end
+ # Users with a notification setting on group or project
+ user_ids += user_ids_notifiable_on(project, :custom)
+ user_ids += user_ids_notifiable_on(project.group, :custom)
- protected
+ # 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)
- # Ensure that if we modify this array, we aren't modifying the memoised
- # participants on the target.
- def participants(target, user)
- return unless target.respond_to?(:participants)
+ 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, custom_action)
- target.participants(user).dup
- end
+ self << [user_scope.where(id: user_ids), :watch]
+ end
- # Get project/group users with CUSTOM notification level
- def add_custom_notifications(recipients, action)
- user_ids = []
+ def add_project_watchers
+ self << [project_watchers, :watch]
+ end
- # 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)
+ # Get project users with WATCH notification level
+ def project_watchers
+ project_members_ids = user_ids_notifiable_on(project)
- # 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)
+ user_ids_with_project_global = user_ids_notifiable_on(project, :global)
+ user_ids_with_group_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_watch((user_ids_with_project_global + user_ids_with_group_global).uniq)
- recipients.concat(User.find(user_ids))
- end
+ user_ids_with_project_setting = select_project_members_ids(user_ids_with_project_global, user_ids)
+ user_ids_with_group_setting = select_group_members_ids(project.group, project_members_ids, user_ids_with_group_global, user_ids)
- def add_project_watchers(recipients)
- recipients.concat(project_watchers).compact
- end
+ user_scope.where(id: user_ids_with_project_setting.concat(user_ids_with_group_setting).uniq)
+ end
- # Get project users with WATCH notification level
- def project_watchers
- project_members_ids = user_ids_notifiable_on(project)
+ def add_subscribed_users
+ return unless target.respond_to? :subscribers
- user_ids_with_project_global = user_ids_notifiable_on(project, :global)
- user_ids_with_group_global = user_ids_notifiable_on(project.group, :global)
+ self << [target.subscribers(project), :subscription]
+ end
- user_ids = user_ids_with_global_level_watch((user_ids_with_project_global + user_ids_with_group_global).uniq)
+ def user_ids_notifiable_on(resource, notification_level = nil)
+ return [] unless resource
- user_ids_with_project_setting = select_project_members_ids(project, user_ids_with_project_global, user_ids)
- user_ids_with_group_setting = select_group_members_ids(project.group, project_members_ids, user_ids_with_group_global, user_ids)
+ scope = resource.notification_settings
- User.where(id: user_ids_with_project_setting.concat(user_ids_with_group_setting).uniq).to_a
- end
+ if notification_level
+ scope = scope.where(level: NotificationSetting.levels[notification_level])
+ end
- # Remove users with notification level 'Mentioned'
- def reject_mention_users(users)
- reject_users(users, :mention)
- end
+ scope.pluck(:user_id)
+ end
- def add_subscribed_users(recipients, target)
- return recipients unless target.respond_to? :subscribers
+ # Build a list of user_ids based on project notification settings
+ def select_project_members_ids(global_setting, user_ids_global_level_watch)
+ user_ids = user_ids_notifiable_on(project, :watch)
- recipients + target.subscribers(project)
- end
+ # If project setting is global, add to watch list if global setting is watch
+ user_ids + (global_setting & user_ids_global_level_watch)
+ end
- def user_ids_notifiable_on(resource, notification_level = nil, action = nil)
- return [] unless resource
+ # Build a list of user_ids based on group notification settings
+ def select_group_members_ids(group, project_members, global_setting, user_ids_global_level_watch)
+ uids = user_ids_notifiable_on(group, :watch)
- 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)
- end
- end
+ # Group setting is global, add to user_ids list if global setting is watch
+ uids + (global_setting & user_ids_global_level_watch) - project_members
+ end
- # Build a list of user_ids based on project notification settings
- def select_project_members_ids(project, global_setting, user_ids_global_level_watch)
- user_ids = user_ids_notifiable_on(project, :watch)
+ def user_ids_with_global_level_watch(ids)
+ settings_with_global_level_of(:watch, ids).pluck(:user_id)
+ end
- # If project setting is global, add to watch list if global setting is watch
- global_setting.each do |user_id|
- if user_ids_global_level_watch.include?(user_id)
- user_ids << user_id
+ def user_ids_with_global_level_custom(ids, action)
+ settings_with_global_level_of(:custom, ids).pluck(:user_id)
end
- end
- user_ids
- end
+ def settings_with_global_level_of(level, ids)
+ NotificationSetting.where(
+ user_id: ids,
+ source_type: nil,
+ level: NotificationSetting.levels[level]
+ )
+ end
- # Build a list of user_ids based on group notification settings
- def select_group_members_ids(group, project_members, global_setting, user_ids_global_level_watch)
- uids = user_ids_notifiable_on(group, :watch)
+ def add_labels_subscribers(labels: nil)
+ return unless target.respond_to? :labels
- # Group setting is watch, add to user_ids list if user is not project member
- user_ids = []
- uids.each do |user_id|
- if project_members.exclude?(user_id)
- user_ids << user_id
+ (labels || target.labels).each do |label|
+ self << [label.subscribers(project), :subscription]
+ end
end
end
- # Group setting is global, add to user_ids list if global setting is watch
- global_setting.each do |user_id|
- if project_members.exclude?(user_id) && user_ids_global_level_watch.include?(user_id)
- user_ids << user_id
+ class Default < Base
+ attr_reader :target
+ attr_reader :current_user
+ attr_reader :action
+ attr_reader :previous_assignee
+ attr_reader :skip_current_user
+ def initialize(target, current_user, action:, previous_assignee: nil, skip_current_user: true)
+ @target = target
+ @current_user = current_user
+ @action = action
+ @previous_assignee = previous_assignee
+ @skip_current_user = skip_current_user
end
- end
-
- user_ids
- end
-
- def user_ids_with_global_level_watch(ids)
- settings_with_global_level_of(:watch, ids).pluck(:user_id)
- end
-
- def user_ids_with_global_level_custom(ids, action)
- settings = settings_with_global_level_of(:custom, ids)
- settings = settings.select { |setting| setting.event_enabled?(action) }
- settings.map(&:user_id)
- end
- def settings_with_global_level_of(level, ids)
- NotificationSetting.where(
- user_id: ids,
- source_type: nil,
- level: NotificationSetting.levels[level]
- )
- end
+ def build!
+ add_participants(current_user)
+ add_project_watchers
+ add_custom_notifications
+
+ # Re-assign is considered as a mention of the new assignee
+ case custom_action
+ when :reassign_merge_request
+ self << [previous_assignee, :mention]
+ self << [target.assignee, :mention]
+ when :reassign_issue
+ previous_assignees = Array(previous_assignee)
+ self << [previous_assignees, :mention]
+ self << [target.assignees, :mention]
+ end
+
+ add_subscribed_users
+
+ if [:new_issue, :new_merge_request].include?(custom_action)
+ add_labels_subscribers
+ end
+ end
- # Reject users which has certain notification level
- #
- # Example:
- # reject_users(users, :watch, project)
- #
- def reject_users(users, level)
- level = level.to_s
+ def acting_user
+ current_user if skip_current_user
+ end
- unless NotificationSetting.levels.keys.include?(level)
- raise 'Invalid notification level'
+ # Build event key to search on custom notification level
+ # Check NotificationSetting::EMAIL_EVENTS
+ def custom_action
+ @custom_action ||= "#{action}_#{target.class.model_name.name.underscore}".to_sym
+ end
end
- users = users.to_a.compact.uniq
- users = users.select { |u| u.can?(:receive_notifications) }
-
- users.reject do |user|
- global_notification_setting = user.global_notification_setting
-
- next global_notification_setting.level == level unless project
-
- setting = user.notification_settings_for(project)
-
- if project.group && (setting.nil? || setting.global?)
- setting = user.notification_settings_for(project.group)
+ class NewNote < Base
+ attr_reader :note
+ def initialize(note)
+ @note = note
end
- # reject users who globally set mention notification and has no setting per project/group
- next global_notification_setting.level == level unless setting
-
- # reject users who set mention notification in project
- next true if setting.level == level
-
- # reject users who have mention level in project and disabled in global settings
- setting.global? && global_notification_setting.level == level
- end
- end
+ def target
+ note.noteable
+ end
- def reject_unsubscribed_users(recipients, target)
- return recipients unless target.respond_to? :subscriptions
+ # NOTE: may be nil, in the case of a PersonalSnippet
+ #
+ # (this is okay because NotificationRecipient is written
+ # to handle nil projects)
+ def project
+ note.project
+ end
- recipients.reject do |user|
- subscription = target.subscriptions.find_by_user_id(user.id)
- subscription && !subscription.subscribed
- end
- end
+ def build!
+ # Add all users participating in the thread (author, assignee, comment authors)
+ add_participants(note.author)
+ self << [note.mentioned_users, :mention]
- def reject_users_without_access(recipients, target)
- ability = case target
- when Issuable
- :"read_#{target.to_ability_name}"
- when Ci::Pipeline
- :read_build # We have build trace in pipeline emails
- end
+ unless note.for_personal_snippet?
+ # Merge project watchers
+ add_project_watchers
- return recipients unless ability
+ # Merge project with custom notification
+ add_custom_notifications
+ end
- recipients.select do |user|
- user.can?(ability, target)
- end
- end
+ add_subscribed_users
+ end
- def add_labels_subscribers(recipients, target, labels: nil)
- return recipients unless target.respond_to? :labels
+ def custom_action
+ :new_note
+ end
- (labels || target.labels).each do |label|
- recipients += label.subscribers(project)
+ def acting_user
+ note.author
+ end
end
-
- recipients
- end
-
- # Build event key to search on custom notification level
- # Check NotificationSetting::EMAIL_EVENTS
- def build_custom_key(action, object)
- "#{action}_#{object.class.model_name.name.underscore}".to_sym
- end
-
- def notification_setting_for_user_project(user, project)
- project_setting = user.notification_settings_for(project)
-
- return project_setting unless project_setting.global?
-
- group_setting = user.notification_settings_for(project.group)
-
- return group_setting unless group_setting.global?
-
- user.global_notification_setting
end
end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index b94921d2a08..df04b1a4fe3 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -42,7 +42,7 @@ class NotificationService
# * users with custom level checked with "new issue"
#
def new_issue(issue, current_user)
- new_resource_email(issue, issue.project, :new_issue_email)
+ new_resource_email(issue, :new_issue_email)
end
# When issue text is updated, we should send an email to:
@@ -52,7 +52,6 @@ class NotificationService
def new_mentions_in_issue(issue, new_mentioned_users, current_user)
new_mentions_in_resource_email(
issue,
- issue.project,
new_mentioned_users,
current_user,
:new_mention_in_issue_email
@@ -67,7 +66,7 @@ class NotificationService
# * users with custom level checked with "close issue"
#
def close_issue(issue, current_user)
- close_resource_email(issue, issue.project, current_user, :closed_issue_email)
+ close_resource_email(issue, current_user, :closed_issue_email)
end
# When we reassign an issue we should send an email to:
@@ -77,7 +76,7 @@ class NotificationService
# * users with custom level checked with "reassign issue"
#
def reassigned_issue(issue, current_user, previous_assignees = [])
- recipients = NotificationRecipientService.new(issue.project).build_recipients(
+ recipients = NotificationRecipientService.build_recipients(
issue,
current_user,
action: "reassign",
@@ -102,7 +101,7 @@ class NotificationService
# * watchers of the issue's labels
#
def relabeled_issue(issue, added_labels, current_user)
- relabeled_resource_email(issue, issue.project, added_labels, current_user, :relabeled_issue_email)
+ relabeled_resource_email(issue, added_labels, current_user, :relabeled_issue_email)
end
# When create a merge request we should send an email to:
@@ -113,7 +112,7 @@ class NotificationService
# * users with custom level checked with "new merge request"
#
def new_merge_request(merge_request, current_user)
- new_resource_email(merge_request, merge_request.target_project, :new_merge_request_email)
+ new_resource_email(merge_request, :new_merge_request_email)
end
# When merge request text is updated, we should send an email to:
@@ -123,7 +122,6 @@ class NotificationService
def new_mentions_in_merge_request(merge_request, new_mentioned_users, current_user)
new_mentions_in_resource_email(
merge_request,
- merge_request.target_project,
new_mentioned_users,
current_user,
:new_mention_in_merge_request_email
@@ -137,7 +135,7 @@ class NotificationService
# * users with custom level checked with "reassign merge request"
#
def reassigned_merge_request(merge_request, current_user)
- reassign_resource_email(merge_request, merge_request.target_project, current_user, :reassigned_merge_request_email)
+ reassign_resource_email(merge_request, current_user, :reassigned_merge_request_email)
end
# When we add labels to a merge request we should send an email to:
@@ -145,21 +143,20 @@ class NotificationService
# * watchers of the mr's labels
#
def relabeled_merge_request(merge_request, added_labels, current_user)
- relabeled_resource_email(merge_request, merge_request.target_project, added_labels, current_user, :relabeled_merge_request_email)
+ relabeled_resource_email(merge_request, added_labels, current_user, :relabeled_merge_request_email)
end
def close_mr(merge_request, current_user)
- close_resource_email(merge_request, merge_request.target_project, current_user, :closed_merge_request_email)
+ close_resource_email(merge_request, current_user, :closed_merge_request_email)
end
def reopen_issue(issue, current_user)
- reopen_resource_email(issue, issue.project, current_user, :issue_status_changed_email, 'reopened')
+ reopen_resource_email(issue, current_user, :issue_status_changed_email, 'reopened')
end
def merge_mr(merge_request, current_user)
close_resource_email(
merge_request,
- merge_request.target_project,
current_user,
:merged_merge_request_email,
skip_current_user: !merge_request.merge_when_pipeline_succeeds?
@@ -169,7 +166,6 @@ class NotificationService
def reopen_mr(merge_request, current_user)
reopen_resource_email(
merge_request,
- merge_request.target_project,
current_user,
:merge_request_status_email,
'reopened'
@@ -177,7 +173,7 @@ class NotificationService
end
def resolve_all_discussions(merge_request, current_user)
- recipients = NotificationRecipientService.new(merge_request.target_project).build_recipients(
+ recipients = NotificationRecipientService.build_recipients(
merge_request,
current_user,
action: "resolve_all_discussions")
@@ -202,7 +198,7 @@ class NotificationService
notify_method = "note_#{note.to_ability_name}_email".to_sym
- recipients = NotificationRecipientService.new(note.project).build_new_note_recipients(note)
+ recipients = NotificationRecipientService.build_new_note_recipients(note)
recipients.each do |recipient|
mailer.send(notify_method, recipient.id, note.id).deliver_later
end
@@ -270,8 +266,7 @@ class NotificationService
end
def project_was_moved(project, old_path_with_namespace)
- recipients = project.team.members
- recipients = NotificationRecipientService.new(project).reject_muted_users(recipients)
+ recipients = NotificationRecipientService.notifiable_users(project.team.members, :mention, project: project)
recipients.each do |recipient|
mailer.project_was_moved_email(
@@ -283,7 +278,7 @@ class NotificationService
end
def issue_moved(issue, new_issue, current_user)
- recipients = NotificationRecipientService.new(issue.project).build_recipients(issue, current_user, action: 'moved')
+ recipients = NotificationRecipientService.build_recipients(issue, current_user, action: 'moved')
recipients.map do |recipient|
email = mailer.issue_moved_email(recipient, issue, new_issue, current_user)
@@ -305,10 +300,10 @@ class NotificationService
return unless mailer.respond_to?(email_template)
- recipients ||= NotificationRecipientService.new(pipeline.project).build_pipeline_recipients(
- pipeline,
- pipeline.user,
- action: pipeline.status
+ recipients ||= NotificationRecipientService.notifiable_users(
+ [pipeline.user], :watch,
+ custom_action: :"#{pipeline.status}_pipeline",
+ target: pipeline
).map(&:notification_email)
if recipients.any?
@@ -318,16 +313,16 @@ class NotificationService
protected
- def new_resource_email(target, project, method)
- recipients = NotificationRecipientService.new(project).build_recipients(target, target.author, action: "new")
+ def new_resource_email(target, method)
+ recipients = NotificationRecipientService.build_recipients(target, target.author, action: "new")
recipients.each do |recipient|
mailer.send(method, recipient.id, target.id).deliver_later
end
end
- def new_mentions_in_resource_email(target, project, new_mentioned_users, current_user, method)
- recipients = NotificationRecipientService.new(project).build_recipients(target, current_user, action: "new")
+ def new_mentions_in_resource_email(target, new_mentioned_users, current_user, method)
+ recipients = NotificationRecipientService.build_recipients(target, current_user, action: "new")
recipients = recipients & new_mentioned_users
recipients.each do |recipient|
@@ -335,10 +330,10 @@ class NotificationService
end
end
- def close_resource_email(target, project, current_user, method, skip_current_user: true)
+ def close_resource_email(target, current_user, method, skip_current_user: true)
action = method == :merged_merge_request_email ? "merge" : "close"
- recipients = NotificationRecipientService.new(project).build_recipients(
+ recipients = NotificationRecipientService.build_recipients(
target,
current_user,
action: action,
@@ -350,11 +345,11 @@ class NotificationService
end
end
- def reassign_resource_email(target, project, current_user, method)
+ def reassign_resource_email(target, current_user, method)
previous_assignee_id = previous_record(target, 'assignee_id')
previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
- recipients = NotificationRecipientService.new(project).build_recipients(
+ recipients = NotificationRecipientService.build_recipients(
target,
current_user,
action: "reassign",
@@ -372,8 +367,14 @@ class NotificationService
end
end
- def relabeled_resource_email(target, project, labels, current_user, method)
- recipients = NotificationRecipientService.new(project).build_relabeled_recipients(target, current_user, labels: labels)
+ def relabeled_resource_email(target, labels, current_user, method)
+ recipients = labels.flat_map { |l| l.subscribers(target.project) }
+ recipients = NotificationRecipientService.notifiable_users(
+ recipients, :subscription,
+ target: target,
+ acting_user: current_user
+ )
+
label_names = labels.map(&:name)
recipients.each do |recipient|
@@ -381,8 +382,8 @@ class NotificationService
end
end
- def reopen_resource_email(target, project, current_user, method, status)
- recipients = NotificationRecipientService.new(project).build_recipients(target, current_user, action: "reopen")
+ def reopen_resource_email(target, current_user, method, status)
+ recipients = NotificationRecipientService.build_recipients(target, current_user, action: "reopen")
recipients.each do |recipient|
mailer.send(method, recipient.id, target.id, status, current_user.id).deliver_later
diff --git a/app/services/projects/autocomplete_service.rb b/app/services/projects/autocomplete_service.rb
index fc85f398935..724a77c873a 100644
--- a/app/services/projects/autocomplete_service.rb
+++ b/app/services/projects/autocomplete_service.rb
@@ -5,7 +5,15 @@ module Projects
end
def milestones
- @project.milestones.active.reorder(due_date: :asc, title: :asc).select([:iid, :title])
+ finder_params = {
+ project_ids: [@project.id],
+ state: :active,
+ order: { due_date: :asc, title: :asc }
+ }
+
+ finder_params[:group_ids] = [@project.group.id] if @project.group
+
+ MilestonesFinder.new(finder_params).execute.select([:iid, :title])
end
def merge_requests
diff --git a/app/services/projects/create_from_template_service.rb b/app/services/projects/create_from_template_service.rb
new file mode 100644
index 00000000000..87d9ed7a0e6
--- /dev/null
+++ b/app/services/projects/create_from_template_service.rb
@@ -0,0 +1,15 @@
+module Projects
+ class CreateFromTemplateService < BaseService
+ def initialize(user, params)
+ @current_user, @params = user, params.dup
+ end
+
+ def execute
+ params[:file] = Gitlab::ProjectTemplate.find(params[:template_name]).file
+
+ GitlabProjectsImportService.new(@current_user, @params).execute
+ ensure
+ params[:file]&.close
+ end
+ end
+end
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index e874a2d8789..48578b6d9e5 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -5,6 +5,10 @@ module Projects
end
def execute
+ if @params[:template_name]&.present?
+ return ::Projects::CreateFromTemplateService.new(current_user, params).execute
+ end
+
forked_from_project_id = params.delete(:forked_from_project_id)
import_data = params.delete(:import_data)
@skip_wiki = params.delete(:skip_wiki)
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index f6e8b6655f2..11ad4838471 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -9,7 +9,7 @@ module Projects
def async_execute
project.update_attribute(:pending_delete, true)
job_id = ProjectDestroyWorker.perform_async(project.id, current_user.id, params)
- Rails.logger.info("User #{current_user.id} scheduled destruction of project #{project.path_with_namespace} with job ID #{job_id}")
+ Rails.logger.info("User #{current_user.id} scheduled destruction of project #{project.full_path} with job ID #{job_id}")
end
def execute
@@ -40,7 +40,7 @@ module Projects
private
def repo_path
- project.path_with_namespace
+ project.disk_path
end
def wiki_path
@@ -127,7 +127,7 @@ module Projects
def flush_caches(project)
project.repository.before_delete
- Repository.new(wiki_path, project).before_delete
+ Repository.new(wiki_path, project, disk_path: repo_path).before_delete
end
end
end
diff --git a/app/services/projects/gitlab_projects_import_service.rb b/app/services/projects/gitlab_projects_import_service.rb
new file mode 100644
index 00000000000..4ca6414b73b
--- /dev/null
+++ b/app/services/projects/gitlab_projects_import_service.rb
@@ -0,0 +1,36 @@
+# This service is an adapter used to for the GitLab Import feature, and
+# creating a project from a template.
+# The latter will under the hood just import an archive supplied by GitLab.
+module Projects
+ class GitlabProjectsImportService
+ attr_reader :current_user, :params
+
+ def initialize(user, params)
+ @current_user, @params = user, params.dup
+ end
+
+ def execute
+ FileUtils.mkdir_p(File.dirname(import_upload_path))
+ FileUtils.copy_entry(file.path, import_upload_path)
+
+ Gitlab::ImportExport::ProjectCreator.new(params[:namespace_id],
+ current_user,
+ import_upload_path,
+ params[:path]).execute
+ end
+
+ private
+
+ def import_upload_path
+ @import_upload_path ||= Gitlab::ImportExport.import_upload_path(filename: tmp_filename)
+ end
+
+ def tmp_filename
+ "#{SecureRandom.hex}_#{params[:path]}"
+ end
+
+ def file
+ params[:file]
+ end
+ end
+end
diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb
index 535da706159..fe4e8ea10bf 100644
--- a/app/services/projects/import_export/export_service.rb
+++ b/app/services/projects/import_export/export_service.rb
@@ -2,7 +2,7 @@ module Projects
module ImportExport
class ExportService < BaseService
def execute(_options = {})
- @shared = Gitlab::ImportExport::Shared.new(relative_path: File.join(project.path_with_namespace, 'work'))
+ @shared = Gitlab::ImportExport::Shared.new(relative_path: File.join(project.disk_path, 'work'))
save_all
end
diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
index eea17e24903..c3bf0031409 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -11,7 +11,7 @@ module Projects
success
rescue => e
- error("Error importing repository #{project.import_url} into #{project.path_with_namespace} - #{e.message}")
+ error("Error importing repository #{project.import_url} into #{project.full_path} - #{e.message}")
end
private
@@ -34,8 +34,12 @@ module Projects
def import_repository
raise Error, 'Blocked import URL.' if Gitlab::UrlBlocker.blocked_url?(project.import_url)
+ # We should return early for a GitHub import because the new GitHub
+ # importer fetch the project repositories for us.
+ return if project.github_import?
+
begin
- if project.github_import? || project.gitea_import?
+ if project.gitea_import?
fetch_repository
else
clone_repository
@@ -51,11 +55,11 @@ module Projects
end
def clone_repository
- gitlab_shell.import_repository(project.repository_storage_path, project.path_with_namespace, project.import_url)
+ gitlab_shell.import_repository(project.repository_storage_path, project.disk_path, project.import_url)
end
def fetch_repository
- project.create_repository
+ project.ensure_repository
project.repository.add_remote(project.import_type, project.import_url)
project.repository.set_remote_as_mirror(project.import_type)
project.repository.fetch_remote(project.import_type, forced: true)
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index 4bb98e5cb4e..5957f612e84 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -34,7 +34,7 @@ module Projects
private
def transfer(project)
- @old_path = project.path_with_namespace
+ @old_path = project.full_path
@old_group = project.group
@new_path = File.join(@new_namespace.try(:full_path) || '', project.path)
@old_namespace = project.namespace
@@ -61,11 +61,13 @@ module Projects
project.send_move_instructions(@old_path)
# Move main repository
+ # TODO: check storage type and NOOP when not using Legacy
unless move_repo_folder(@old_path, @new_path)
raise TransferError.new('Cannot move project')
end
# Move wiki repo also if present
+ # TODO: check storage type and NOOP when not using Legacy
move_repo_folder("#{@old_path}.wiki", "#{@new_path}.wiki")
# Move missing group labels to project
diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb
index 749a1cc56d8..5038155ca31 100644
--- a/app/services/projects/update_pages_service.rb
+++ b/app/services/projects/update_pages_service.rb
@@ -33,8 +33,10 @@ module Projects
success
end
rescue => e
+ register_failure
error(e.message)
ensure
+ register_attempt
build.erase_artifacts! unless build.has_expiring_artifacts?
end
@@ -168,5 +170,21 @@ module Projects
def sha
build.sha
end
+
+ def register_attempt
+ pages_deployments_total_counter.increase
+ end
+
+ def register_failure
+ pages_deployments_failed_total_counter.increase
+ end
+
+ def pages_deployments_total_counter
+ @pages_deployments_total_counter ||= Gitlab::Metrics.counter(:pages_deployments_total, "Counter of GitLab Pages deployments triggered")
+ end
+
+ def pages_deployments_failed_total_counter
+ @pages_deployments_failed_total_counter ||= Gitlab::Metrics.counter(:pages_deployments_failed_total, "Counter of GitLab Pages deployments which failed")
+ end
end
end
diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb
index c22bf7498bb..c7832c47e1a 100644
--- a/app/services/quick_actions/interpret_service.rb
+++ b/app/services/quick_actions/interpret_service.rb
@@ -511,7 +511,12 @@ module QuickActions
users = extract_references(params, :user)
if users.empty?
- users = User.where(username: params.split(' ').map(&:strip))
+ users =
+ if params == 'me'
+ [current_user]
+ else
+ User.where(username: params.split(' ').map(&:strip))
+ end
end
users
diff --git a/app/services/submit_usage_ping_service.rb b/app/services/submit_usage_ping_service.rb
index 17857ca62f2..14171bce782 100644
--- a/app/services/submit_usage_ping_service.rb
+++ b/app/services/submit_usage_ping_service.rb
@@ -1,6 +1,16 @@
class SubmitUsagePingService
URL = 'https://version.gitlab.com/usage_data'.freeze
+ METRICS = %w[leader_issues instance_issues percentage_issues leader_notes instance_notes
+ percentage_notes leader_milestones instance_milestones percentage_milestones
+ leader_boards instance_boards percentage_boards leader_merge_requests
+ instance_merge_requests percentage_merge_requests leader_ci_pipelines
+ instance_ci_pipelines percentage_ci_pipelines leader_environments instance_environments
+ percentage_environments leader_deployments instance_deployments percentage_deployments
+ leader_projects_prometheus_active instance_projects_prometheus_active
+ percentage_projects_prometheus_active leader_service_desk_issues instance_service_desk_issues
+ percentage_service_desk_issues].freeze
+
include Gitlab::CurrentSettings
def execute
@@ -27,15 +37,7 @@ class SubmitUsagePingService
return unless response['conv_index'].present?
ConversationalDevelopmentIndex::Metric.create!(
- response['conv_index'].slice(
- 'leader_issues', 'instance_issues', 'leader_notes', 'instance_notes',
- 'leader_milestones', 'instance_milestones', 'leader_boards', 'instance_boards',
- 'leader_merge_requests', 'instance_merge_requests', 'leader_ci_pipelines',
- 'instance_ci_pipelines', 'leader_environments', 'instance_environments',
- 'leader_deployments', 'instance_deployments', 'leader_projects_prometheus_active',
- 'instance_projects_prometheus_active', 'leader_service_desk_issues',
- 'instance_service_desk_issues'
- )
+ response['conv_index'].slice(*METRICS)
)
end
end
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index bd58a54592f..cbcd4478af6 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -24,7 +24,7 @@ class SystemHooksService
key: model.key,
id: model.id
)
-
+
if model.user
data[:username] = model.user.username
end
@@ -56,7 +56,7 @@ class SystemHooksService
when GroupMember
data.merge!(group_member_data(model))
end
-
+
data
end
@@ -79,7 +79,7 @@ class SystemHooksService
{
name: model.name,
path: model.path,
- path_with_namespace: model.path_with_namespace,
+ path_with_namespace: model.full_path,
project_id: model.id,
owner_name: owner.name,
owner_email: owner.respond_to?(:email) ? owner.email : "",
@@ -93,7 +93,7 @@ class SystemHooksService
{
project_name: project.name,
project_path: project.path,
- project_path_with_namespace: project.path_with_namespace,
+ project_path_with_namespace: project.full_path,
project_id: project.id,
user_username: model.user.username,
user_name: model.user.name,
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 2dbee9c246e..1763f64a4e4 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -142,7 +142,8 @@ module SystemNoteService
#
# Returns the created Note object
def change_milestone(noteable, project, author, milestone)
- body = milestone.nil? ? 'removed milestone' : "changed milestone to #{milestone.to_reference(project)}"
+ format = milestone&.is_group_milestone? ? :name : :iid
+ body = milestone.nil? ? 'removed milestone' : "changed milestone to #{milestone.to_reference(project, format: format)}"
create_note(NoteSummary.new(noteable, project, author, body, action: 'milestone'))
end
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index 322c6286365..6ee96d6a0f8 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -170,20 +170,22 @@ class TodoService
# When user marks some todos as done
def mark_todos_as_done(todos, current_user)
- update_todos_state_by_ids(todos.select(&:id), current_user, :done)
+ update_todos_state(todos, current_user, :done)
end
def mark_todos_as_done_by_ids(ids, current_user)
- update_todos_state_by_ids(ids, current_user, :done)
+ todos = todos_by_ids(ids, current_user)
+ mark_todos_as_done(todos, current_user)
end
# When user marks some todos as pending
def mark_todos_as_pending(todos, current_user)
- update_todos_state_by_ids(todos.select(&:id), current_user, :pending)
+ update_todos_state(todos, current_user, :pending)
end
def mark_todos_as_pending_by_ids(ids, current_user)
- update_todos_state_by_ids(ids, current_user, :pending)
+ todos = todos_by_ids(ids, current_user)
+ mark_todos_as_pending(todos, current_user)
end
# When user marks an issue as todo
@@ -198,9 +200,11 @@ class TodoService
private
- def update_todos_state_by_ids(ids, current_user, state)
- todos = current_user.todos.where(id: ids)
+ def todos_by_ids(ids, current_user)
+ current_user.todos.where(id: Array(ids))
+ end
+ def update_todos_state(todos, current_user, state)
# Only update those that are not really on that state
todos = todos.where.not(state: state)
todos_ids = todos.pluck(:id)
diff --git a/app/services/web_hook_service.rb b/app/services/web_hook_service.rb
index 27c3ba197ac..2825478926a 100644
--- a/app/services/web_hook_service.rb
+++ b/app/services/web_hook_service.rb
@@ -101,7 +101,7 @@ class WebHookService
request_headers: build_headers(hook_name),
request_data: request_data,
response_headers: format_response_headers(response),
- response_body: response.body,
+ response_body: safe_response_body(response),
response_status: response.code,
internal_error_message: error_message
)
@@ -124,4 +124,10 @@ class WebHookService
def format_response_headers(response)
response.headers.each_capitalized.to_h
end
+
+ def safe_response_body(response)
+ return '' unless response.body
+
+ response.body.encode('UTF-8', invalid: :replace, undef: :replace, replace: '')
+ end
end
diff --git a/app/services/wiki_pages/update_service.rb b/app/services/wiki_pages/update_service.rb
index c628e6781af..93cbd9a509f 100644
--- a/app/services/wiki_pages/update_service.rb
+++ b/app/services/wiki_pages/update_service.rb
@@ -1,7 +1,7 @@
module WikiPages
class UpdateService < WikiPages::BaseService
def execute(page)
- if page.update(@params[:content], format: @params[:format], message: @params[:message], last_commit_sha: @params[:last_commit_sha])
+ if page.update(@params)
execute_hooks(page, 'update')
end