Class: SystemNoteService

Inherits:
Object
  • Object
show all
Defined in:
app/services/system_note_service.rb

Overview

SystemNoteService

Used for creating system notes (e.g., when a user references a merge request from an issue, an issue's assignee changes, an issue is closed, etc.)

Class Method Summary collapse

Class Method Details

.add_commits(noteable, project, author, new_commits, existing_commits = [], oldrev = nil) ⇒ Object

Called when commits are added to a Merge Request

noteable - Noteable object project - Project owning noteable author - User performing the change new_commits - Array of Commits added since last push existing_commits - Array of Commits added in a previous push oldrev - Optional String SHA of a previous Commit

See new_commit_summary and existing_commit_summary.

Returns the created Note object


18
19
20
21
22
23
24
25
26
27
# File 'app/services/system_note_service.rb', line 18

def self.add_commits(noteable, project, author, new_commits, existing_commits = [], oldrev = nil)
  total_count  = new_commits.length + existing_commits.length
  commits_text = "#{total_count} commit".pluralize(total_count)

  body = "Added #{commits_text}:\n\n"
  body << existing_commit_summary(noteable, existing_commits, oldrev)
  body << new_commit_summary(new_commits).join("\n")

  create_note(noteable: noteable, project: project, author: author, note: body)
end

.add_merge_request_wip(noteable, project, author) ⇒ Object


153
154
155
156
157
# File 'app/services/system_note_service.rb', line 153

def self.add_merge_request_wip(noteable, project, author)
  body = 'Marked this merge request as a **Work In Progress**'

  create_note(noteable: noteable, project: project, author: author, note: body)
end

.cancel_merge_when_build_succeeds(noteable, project, author) ⇒ Object

Called when 'merge when build succeeds' is canceled


141
142
143
144
145
# File 'app/services/system_note_service.rb', line 141

def self.cancel_merge_when_build_succeeds(noteable, project, author)
  body = "Canceled the automatic merge"

  create_note(noteable: noteable, project: project, author: author, note: body)
end

.change_assignee(noteable, project, author, assignee) ⇒ Object

Called when the assignee of a Noteable is changed or removed

noteable - Noteable object project - Project owning noteable author - User performing the change assignee - User being assigned, or nil

Example Note text:

"Assignee removed"

"Reassigned to @rspeicher"

Returns the created Note object


43
44
45
46
47
# File 'app/services/system_note_service.rb', line 43

def self.change_assignee(noteable, project, author, assignee)
  body = assignee.nil? ? 'Assignee removed' : "Reassigned to #{assignee.to_reference}"

  create_note(noteable: noteable, project: project, author: author, note: body)
end

.change_branch(noteable, project, author, branch_type, old_branch, new_branch) ⇒ Object

Called when a branch in Noteable is changed

noteable - Noteable object project - Project owning noteable author - User performing the change branch_type - 'source' or 'target' old_branch - old branch name new_branch - new branch nmae

Example Note text:

"Target branch changed from `Old` to `New`"

Returns the created Note object


192
193
194
195
# File 'app/services/system_note_service.rb', line 192

def self.change_branch(noteable, project, author, branch_type, old_branch, new_branch)
  body = "#{branch_type} branch changed from `#{old_branch}` to `#{new_branch}`".capitalize
  create_note(noteable: noteable, project: project, author: author, note: body)
end

.change_branch_presence(noteable, project, author, branch_type, branch, presence) ⇒ Object

Called when a branch in Noteable is added or deleted

noteable - Noteable object project - Project owning noteable author - User performing the change branch_type - :source or :target branch - branch name presence - :add or :delete

Example Note text:

"Restored target branch `feature`"

Returns the created Note object


211
212
213
214
215
216
217
218
219
220
# File 'app/services/system_note_service.rb', line 211

def self.change_branch_presence(noteable, project, author, branch_type, branch, presence)
  verb =
    if presence == :add
      'restored'
    else
      'deleted'
    end
  body = "#{verb} #{branch_type.to_s} branch `#{branch}`".capitalize
  create_note(noteable: noteable, project: project, author: author, note: body)
end

.change_label(noteable, project, author, added_labels, removed_labels) ⇒ Object

Called when one or more labels on a Noteable are added and/or removed

noteable - Noteable object project - Project owning noteable author - User performing the change added_labels - Array of Labels added removed_labels - Array of Labels removed

Example Note text:

"Added ~1 and removed ~2 ~3 labels"

"Added ~4 label"

"Removed ~5 label"

Returns the created Note object


66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'app/services/system_note_service.rb', line 66

def self.change_label(noteable, project, author, added_labels, removed_labels)
  labels_count = added_labels.count + removed_labels.count

  references     = ->(label) { label.to_reference(format: :id) }
  added_labels   = added_labels.map(&references).join(' ')
  removed_labels = removed_labels.map(&references).join(' ')

  body = ''

  if added_labels.present?
    body << "added #{added_labels}"
    body << ' and ' if removed_labels.present?
  end

  if removed_labels.present?
    body << "removed #{removed_labels}"
  end

  body << ' ' << 'label'.pluralize(labels_count)
  body = "#{body.capitalize}"

  create_note(noteable: noteable, project: project, author: author, note: body)
end

.change_milestone(noteable, project, author, milestone) ⇒ Object

Called when the milestone of a Noteable is changed

noteable - Noteable object project - Project owning noteable author - User performing the change milestone - Milestone being assigned, or nil

Example Note text:

"Milestone removed"

"Miletone changed to 7.11"

Returns the created Note object


104
105
106
107
108
109
# File 'app/services/system_note_service.rb', line 104

def self.change_milestone(noteable, project, author, milestone)
  body = 'Milestone '
  body += milestone.nil? ? 'removed' : "changed to #{milestone.to_reference(project)}"

  create_note(noteable: noteable, project: project, author: author, note: body)
end

.change_status(noteable, project, author, status, source) ⇒ Object

Called when the status of a Noteable is changed

noteable - Noteable object project - Project owning noteable author - User performing the change status - String status source - Mentionable performing the change, or nil

Example Note text:

"Status changed to merged"

"Status changed to closed by bc17db76"

Returns the created Note object


126
127
128
129
130
131
# File 'app/services/system_note_service.rb', line 126

def self.change_status(noteable, project, author, status, source)
  body = "Status changed to #{status}"
  body += " by #{source.gfm_reference(project)}" if source

  create_note(noteable: noteable, project: project, author: author, note: body)
end

.change_title(noteable, project, author, old_title) ⇒ Object

Called when the title of a Noteable is changed

noteable - Noteable object that responds to `title` project - Project owning noteable author - User performing the change old_title - Previous String title

Example Note text:

"Title changed from **Old** to **New**"

Returns the created Note object


171
172
173
174
175
176
# File 'app/services/system_note_service.rb', line 171

def self.change_title(noteable, project, author, old_title)
  return unless noteable.respond_to?(:title)

  body = "Title changed from **#{old_title}** to **#{noteable.title}**"
  create_note(noteable: noteable, project: project, author: author, note: body)
end

.cross_reference(noteable, mentioner, author) ⇒ Object

Called when a Mentionable references a Noteable

noteable - Noteable object being referenced mentioner - Mentionable object author - User performing the reference

Example Note text:

"mentioned in #1"

"mentioned in !2"

"mentioned in 54f7727c"

See cross_reference_note_content.

Returns the created Note object


251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'app/services/system_note_service.rb', line 251

def self.cross_reference(noteable, mentioner, author)
  return if cross_reference_disallowed?(noteable, mentioner)

  gfm_reference = mentioner.gfm_reference(noteable.project)

  note_options = {
    project: noteable.project,
    author:  author,
    note:    cross_reference_note_content(gfm_reference)
  }

  if noteable.kind_of?(Commit)
    note_options.merge!(noteable_type: 'Commit', commit_id: noteable.id)
  else
    note_options.merge!(noteable: noteable)
  end

  if noteable.is_a?(ExternalIssue)
    noteable.project.issues_tracker.create_cross_reference_note(noteable, mentioner, author)
  else
    create_note(note_options)
  end
end

.cross_reference?(note_text) ⇒ Boolean

Returns:

  • (Boolean)

276
277
278
# File 'app/services/system_note_service.rb', line 276

def self.cross_reference?(note_text)
  note_text.start_with?(cross_reference_note_prefix)
end

.cross_reference_disallowed?(noteable, mentioner) ⇒ Boolean

Check if a cross-reference is disallowed

This method prevents adding a “mentioned in !1” note on every single commit in a merge request. Additionally, it prevents the creation of references to external issues (which would fail).

noteable - Noteable object being referenced mentioner - Mentionable object

Returns Boolean

Returns:

  • (Boolean)

290
291
292
293
294
295
296
# File 'app/services/system_note_service.rb', line 290

def self.cross_reference_disallowed?(noteable, mentioner)
  return true if noteable.is_a?(ExternalIssue) && !noteable.project.jira_tracker_active?
  return false unless mentioner.is_a?(MergeRequest)
  return false unless noteable.is_a?(Commit)

  mentioner.commits.include?(noteable)
end

.cross_reference_exists?(noteable, mentioner) ⇒ Boolean

Check if a cross reference to a noteable from a mentioner already exists

This method is used to prevent multiple notes being created for a mention when a issue is updated, for example. The method also calls notes_for_mentioner to check if the mentioner is a commit, and return matches only on commit hash instead of project + commit, to avoid repeated mentions from forks.

noteable - Noteable object being referenced mentioner - Mentionable object

Returns Boolean

Returns:

  • (Boolean)

310
311
312
313
314
315
316
317
318
319
320
321
322
# File 'app/services/system_note_service.rb', line 310

def self.cross_reference_exists?(noteable, mentioner)
  # Initial scope should be system notes of this noteable type
  notes = Note.system.where(noteable_type: noteable.class)

  if noteable.is_a?(Commit)
    # Commits have non-integer IDs, so they're stored in `commit_id`
    notes = notes.where(commit_id: noteable.id)
  else
    notes = notes.where(noteable_id: noteable.id)
  end

  notes_for_mentioner(mentioner, noteable, notes).count > 0
end

.merge_when_build_succeeds(noteable, project, author, last_commit) ⇒ Object

Called when 'merge when build succeeds' is executed


134
135
136
137
138
# File 'app/services/system_note_service.rb', line 134

def self.merge_when_build_succeeds(noteable, project, author, last_commit)
  body = "Enabled an automatic merge when the build for #{last_commit.to_reference(project)} succeeds"

  create_note(noteable: noteable, project: project, author: author, note: body)
end

.new_issue_branch(issue, project, author, branch) ⇒ Object

Called when a branch is created from the 'new branch' button on a issue Example note text:

"Started branch `201-issue-branch-button`"

226
227
228
229
230
231
232
# File 'app/services/system_note_service.rb', line 226

def self.new_issue_branch(issue, project, author, branch)
  h = Gitlab::Routing.url_helpers
  link = h.namespace_project_compare_url(project.namespace, project, from: project.default_branch, to: branch)

  body = "Started branch [`#{branch}`](#{link})"
  create_note(noteable: issue, project: project, author: author, note: body)
end

.remove_merge_request_wip(noteable, project, author) ⇒ Object


147
148
149
150
151
# File 'app/services/system_note_service.rb', line 147

def self.remove_merge_request_wip(noteable, project, author)
  body = 'Unmarked this merge request as a Work In Progress'

  create_note(noteable: noteable, project: project, author: author, note: body)
end