1# frozen_string_literal: true
2
3module SystemNotes
4  class MergeRequestsService < ::SystemNotes::BaseService
5    # Called when 'merge when pipeline succeeds' is executed
6    def merge_when_pipeline_succeeds(sha)
7      body = "enabled an automatic merge when the pipeline for #{sha} succeeds"
8
9      create_note(NoteSummary.new(noteable, project, author, body, action: 'merge'))
10    end
11
12    # Called when 'merge when pipeline succeeds' is canceled
13    def cancel_merge_when_pipeline_succeeds
14      body = 'canceled the automatic merge'
15
16      create_note(NoteSummary.new(noteable, project, author, body, action: 'merge'))
17    end
18
19    # Called when 'merge when pipeline succeeds' is aborted
20    def abort_merge_when_pipeline_succeeds(reason)
21      body = "aborted the automatic merge because #{reason}"
22
23      ##
24      # TODO: Abort message should be sent by the system, not a particular user.
25      # See https://gitlab.com/gitlab-org/gitlab-foss/issues/63187.
26      create_note(NoteSummary.new(noteable, project, author, body, action: 'merge'))
27    end
28
29    def handle_merge_request_draft
30      action = noteable.work_in_progress? ? "draft" : "ready"
31
32      body = "marked this merge request as **#{action}**"
33
34      create_note(NoteSummary.new(noteable, project, author, body, action: 'title'))
35    end
36
37    def add_merge_request_draft_from_commit(commit)
38      body = "marked this merge request as **draft** from #{commit.to_reference(project)}"
39
40      create_note(NoteSummary.new(noteable, project, author, body, action: 'title'))
41    end
42
43    def resolve_all_discussions
44      body = "resolved all threads"
45
46      create_note(NoteSummary.new(noteable, project, author, body, action: 'discussion'))
47    end
48
49    def discussion_continued_in_issue(discussion, issue)
50      body = "created #{issue.to_reference} to continue this discussion"
51      note_attributes = discussion.reply_attributes.merge(project: project, author: author, note: body)
52
53      Note.create(note_attributes.merge(system: true, created_at: issue.system_note_timestamp)).tap do |note|
54        note.system_note_metadata = SystemNoteMetadata.new(action: 'discussion')
55      end
56    end
57
58    def diff_discussion_outdated(discussion, change_position)
59      merge_request = discussion.noteable
60      diff_refs = change_position.diff_refs
61      version_index = merge_request.merge_request_diffs.viewable.count
62      position_on_text = change_position.on_text?
63      text_parts = ["changed this #{position_on_text ? 'line' : 'file'} in"]
64
65      if version_params = merge_request.version_params_for(diff_refs)
66        repository = project.repository
67        anchor = position_on_text ? change_position.line_code(repository) : change_position.file_hash
68        url = url_helpers.diffs_project_merge_request_path(project, merge_request, version_params.merge(anchor: anchor))
69
70        text_parts << "[version #{version_index} of the diff](#{url})"
71      else
72        text_parts << "version #{version_index} of the diff"
73      end
74
75      body = text_parts.join(' ')
76      note_attributes = discussion.reply_attributes.merge(project: project, author: author, note: body)
77
78      Note.create(note_attributes.merge(system: true)).tap do |note|
79        note.system_note_metadata = SystemNoteMetadata.new(action: 'outdated')
80      end
81    end
82
83    # Called when a branch in Noteable is changed
84    #
85    # branch_type - 'source' or 'target'
86    # event_type  - the source of event: 'update' or 'delete'
87    # old_branch  - old branch name
88    # new_branch  - new branch name
89
90    # Example Note text is based on event_type:
91    #
92    #   update: "changed target branch from `Old` to `New`"
93    #   delete: "deleted the `Old` branch. This merge request now targets the `New` branch"
94    #
95    # Returns the created Note object
96    def change_branch(branch_type, event_type, old_branch, new_branch)
97      body =
98        case event_type.to_s
99        when 'delete'
100          "deleted the `#{old_branch}` branch. This merge request now targets the `#{new_branch}` branch"
101        when 'update'
102          "changed #{branch_type} branch from `#{old_branch}` to `#{new_branch}`"
103        else
104          raise ArgumentError, "invalid value for event_type: #{event_type}"
105        end
106
107      create_note(NoteSummary.new(noteable, project, author, body, action: 'branch'))
108    end
109
110    # Called when a branch in Noteable is added or deleted
111    #
112    # branch_type - :source or :target
113    # branch      - branch name
114    # presence    - :add or :delete
115    #
116    # Example Note text:
117    #
118    #   "restored target branch `feature`"
119    #
120    # Returns the created Note object
121    def change_branch_presence(branch_type, branch, presence)
122      verb =
123        if presence == :add
124          'restored'
125        else
126          'deleted'
127        end
128
129      body = "#{verb} #{branch_type} branch `#{branch}`"
130
131      create_note(NoteSummary.new(noteable, project, author, body, action: 'branch'))
132    end
133
134    # Called when a branch is created from the 'new branch' button on a issue
135    # Example note text:
136    #
137    #   "created branch `201-issue-branch-button`"
138    def new_issue_branch(branch, branch_project: nil)
139      branch_project ||= project
140      link = url_helpers.project_compare_path(branch_project, from: branch_project.default_branch, to: branch)
141
142      body = "created branch [`#{branch}`](#{link}) to address this issue"
143
144      create_note(NoteSummary.new(noteable, project, author, body, action: 'branch'))
145    end
146
147    def new_merge_request(merge_request)
148      body = "created merge request #{merge_request.to_reference(project)} to address this issue"
149
150      create_note(NoteSummary.new(noteable, project, author, body, action: 'merge'))
151    end
152
153    def picked_into_branch(branch_name, pick_commit)
154      link = url_helpers.project_tree_path(project, branch_name)
155
156      body = "picked the changes into the branch [`#{branch_name}`](#{link}) with commit #{pick_commit}"
157
158      summary = NoteSummary.new(noteable, project, author, body, action: 'cherry_pick')
159      summary.note[:commit_id] = pick_commit
160
161      create_note(summary)
162    end
163
164    # Called when the merge request is approved by user
165    #
166    # Example Note text:
167    #
168    #   "approved this merge request"
169    #
170    # Returns the created Note object
171    def approve_mr
172      body = "approved this merge request"
173
174      create_note(NoteSummary.new(noteable, project, author, body, action: 'approved'))
175    end
176
177    def unapprove_mr
178      body = "unapproved this merge request"
179
180      create_note(NoteSummary.new(noteable, project, author, body, action: 'unapproved'))
181    end
182  end
183end
184