summaryrefslogtreecommitdiff
path: root/app/services/issues/move_service.rb
blob: 841bce9949e73afb7877a2bc2d8a55ccc37f497c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# frozen_string_literal: true

module Issues
  class MoveService < Issues::BaseService
    MoveError = Class.new(StandardError)

    def execute(issue, new_project)
      @old_issue = issue
      @old_project = @project
      @new_project = new_project

      unless issue.can_move?(current_user, new_project)
        raise MoveError, 'Cannot move issue due to insufficient permissions!'
      end

      if @project == new_project
        raise MoveError, 'Cannot move issue to project it originates from!'
      end

      # Using transaction because of a high resources footprint
      # on rewriting notes (unfolding references)
      #
      ActiveRecord::Base.transaction do
        @new_issue = create_new_issue

        update_new_issue
        update_old_issue
      end

      notify_participants

      @new_issue
    end

    private

    def update_new_issue
      rewrite_notes
      rewrite_issue_award_emoji
      add_note_moved_from
    end

    def update_old_issue
      add_note_moved_to
      close_issue
      mark_as_moved
    end

    def create_new_issue
      new_params = { id: nil, iid: nil, label_ids: cloneable_label_ids,
                     milestone_id: cloneable_milestone_id,
                     project: @new_project, author: @old_issue.author,
                     description: rewrite_content(@old_issue.description),
                     assignee_ids: @old_issue.assignee_ids }

      new_params = @old_issue.serializable_hash.symbolize_keys.merge(new_params)
      CreateService.new(@new_project, @current_user, new_params).execute
    end

    def cloneable_label_ids
      params = {
        project_id: @new_project.id,
        title: @old_issue.labels.pluck(:title),
        include_ancestor_groups: true
      }

      LabelsFinder.new(current_user, params).execute.pluck(:id)
    end

    def cloneable_milestone_id
      title = @old_issue.milestone&.title
      return unless title

      if @new_project.group && can?(current_user, :read_group, @new_project.group)
        group_id = @new_project.group.id
      end

      params =
        { title: title, project_ids: @new_project.id, group_ids: group_id }

      milestones = MilestonesFinder.new(params).execute
      milestones.first&.id
    end

    def rewrite_notes
      @old_issue.notes_with_associations.find_each do |note|
        new_note = note.dup
        new_params = { project: @new_project, noteable: @new_issue,
                       note: rewrite_content(new_note.note),
                       created_at: note.created_at,
                       updated_at: note.updated_at }

        new_note.update(new_params)

        rewrite_award_emoji(note, new_note)
      end
    end

    def rewrite_issue_award_emoji
      rewrite_award_emoji(@old_issue, @new_issue)
    end

    def rewrite_award_emoji(old_awardable, new_awardable)
      old_awardable.award_emoji.each do |award|
        new_award = award.dup
        new_award.awardable = new_awardable
        new_award.save
      end
    end

    def rewrite_content(content)
      return unless content

      rewriters = [Gitlab::Gfm::ReferenceRewriter,
                   Gitlab::Gfm::UploadsRewriter]

      rewriters.inject(content) do |text, klass|
        rewriter = klass.new(text, @old_project, @current_user)
        rewriter.rewrite(@new_project)
      end
    end

    def close_issue
      close_service = CloseService.new(@old_project, @current_user)
      close_service.execute(@old_issue, notifications: false, system_note: false)
    end

    def add_note_moved_from
      SystemNoteService.noteable_moved(@new_issue, @new_project,
                                       @old_issue, @current_user,
                                       direction: :from)
    end

    def add_note_moved_to
      SystemNoteService.noteable_moved(@old_issue, @old_project,
                                       @new_issue, @current_user,
                                       direction: :to)
    end

    def mark_as_moved
      @old_issue.update(moved_to: @new_issue)
    end

    def notify_participants
      notification_service.async.issue_moved(@old_issue, @new_issue, @current_user)
    end
  end
end