Class: Issues::UpdateService

Inherits:
BaseService show all
Includes:
SpamCheckMethods
Defined in:
app/services/issues/update_service.rb

Constant Summary

Constants inherited from BaseService

BaseService::NO_REBALANCING_NEEDED

Constants included from Gitlab::Utils::UsageData

Gitlab::Utils::UsageData::FALLBACK

Instance Attribute Summary

Attributes inherited from BaseService

#current_user, #params, #project

Instance Method Summary collapse

Methods included from SpamCheckMethods

#filter_spam_check_params, #spam_check

Methods inherited from BaseService

#close_service, #hook_data, #rebalance_if_needed, #reopen_service

Methods included from IncidentManagement::UsageData

#track_event, #track_incident_action

Methods included from Gitlab::Utils::UsageData

#alt_usage_data, #count, #distinct_count, #measure_duration, #redis_usage_data, #track_usage_event, #with_finished_at, #with_prometheus_client

Methods inherited from BaseService

#initialize

Methods included from BaseServiceUtility

#deny_visibility_level, #event_service, #log_error, #log_info, #notification_service, #system_hook_service, #todo_service, #visibility_level

Methods included from Gitlab::Allowable

#can?

Instance Method Details

#after_update(issue) ⇒ Object


24
25
26
# File 'app/services/issues/update_service.rb', line 24

def after_update(issue)
  IssuesChannel.broadcast_to(issue, event: 'updated') if Gitlab::ActionCable::Config.in_app? || Feature.enabled?(:broadcast_issue_updates, issue.project)
end

#before_update(issue, skip_spam_check: false) ⇒ Object


20
21
22
# File 'app/services/issues/update_service.rb', line 20

def before_update(issue, skip_spam_check: false)
  spam_check(issue, current_user, action: :update) unless skip_spam_check
end

#change_issue_duplicate(issue) ⇒ Object

rubocop: disable CodeReuse/ActiveRecord


92
93
94
95
96
97
98
99
100
101
# File 'app/services/issues/update_service.rb', line 92

def change_issue_duplicate(issue)
  canonical_issue_id = params.delete(:canonical_issue_id)
  return unless canonical_issue_id

  canonical_issue = IssuesFinder.new(current_user).find_by(id: canonical_issue_id)

  if canonical_issue
    Issues::DuplicateService.new(project, current_user).execute(issue, canonical_issue)
  end
end

#execute(issue) ⇒ Object


7
8
9
10
11
12
# File 'app/services/issues/update_service.rb', line 7

def execute(issue)
  handle_move_between_ids(issue)
  filter_spam_check_params
  change_issue_duplicate(issue)
  move_issue_to_new_project(issue) || update_task_event(issue) || update(issue)
end

#handle_changes(issue, options) ⇒ Object


28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'app/services/issues/update_service.rb', line 28

def handle_changes(issue, options)
  old_associations = options.fetch(:old_associations, {})
  old_labels = old_associations.fetch(:labels, [])
  old_mentioned_users = old_associations.fetch(:mentioned_users, [])
  old_assignees = old_associations.fetch(:assignees, [])

  if has_changes?(issue, old_labels: old_labels, old_assignees: old_assignees)
    todo_service.resolve_todos_for_target(issue, current_user)
  end

  if issue.previous_changes.include?('title') ||
      issue.previous_changes.include?('description')
    todo_service.update_issue(issue, current_user, old_mentioned_users)
  end

  if issue.assignees != old_assignees
    create_assignee_note(issue, old_assignees)
    notification_service.async.reassigned_issue(issue, current_user, old_assignees)
    todo_service.reassigned_assignable(issue, current_user, old_assignees)
    track_incident_action(current_user, issue, :incident_assigned)
  end

  if issue.previous_changes.include?('confidential')
    # don't enqueue immediately to prevent todos removal in case of a mistake
    TodosDestroyer::ConfidentialIssueWorker.perform_in(Todo::WAIT_FOR_DELETE, issue.id) if issue.confidential?
    create_confidentiality_note(issue)
    track_usage_event(:incident_management_incident_change_confidential, current_user)
  end

  added_labels = issue.labels - old_labels

  if added_labels.present?
    notification_service.async.relabeled_issue(issue, added_labels, current_user)
  end

  handle_milestone_change(issue)

  added_mentions = issue.mentioned_users(current_user) - old_mentioned_users

  if added_mentions.present?
    notification_service.async.new_mentions_in_issue(issue, added_mentions, current_user)
  end
end

#handle_move_between_ids(issue) ⇒ Object

Raises:

  • (ActiveRecord::RecordNotFound)

77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'app/services/issues/update_service.rb', line 77

def handle_move_between_ids(issue)
  return unless params[:move_between_ids]

  after_id, before_id = params.delete(:move_between_ids)
  board_group_id = params.delete(:board_group_id)

  issue_before = get_issue_if_allowed(before_id, board_group_id)
  issue_after = get_issue_if_allowed(after_id, board_group_id)
  raise ActiveRecord::RecordNotFound unless issue_before || issue_after

  issue.move_between(issue_before, issue_after)
  rebalance_if_needed(issue)
end

#handle_task_changes(issuable) ⇒ Object


72
73
74
75
# File 'app/services/issues/update_service.rb', line 72

def handle_task_changes(issuable)
  todo_service.resolve_todos_for_target(issuable, current_user)
  todo_service.update_issue(issuable, current_user)
end

#move_issue_to_new_project(issue) ⇒ Object

rubocop: enable CodeReuse/ActiveRecord


104
105
106
107
108
109
110
111
112
113
# File 'app/services/issues/update_service.rb', line 104

def move_issue_to_new_project(issue)
  target_project = params.delete(:target_project)

  return unless target_project &&
      issue.can_move?(current_user, target_project) &&
      target_project != issue.project

  update(issue)
  Issues::MoveService.new(project, current_user).execute(issue, target_project)
end

#update(issue) ⇒ Object


14
15
16
17
18
# File 'app/services/issues/update_service.rb', line 14

def update(issue)
  create_merge_request_from_quick_action

  super
end