summaryrefslogtreecommitdiff
path: root/app/services/issuable/clone/attributes_rewriter.rb
blob: a78e191c85f29cd5a57e3177d1279b40c5c18e77 (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
# frozen_string_literal: true

module Issuable
  module Clone
    class AttributesRewriter < ::Issuable::Clone::BaseService
      def initialize(current_user, original_entity, new_entity)
        @current_user = current_user
        @original_entity = original_entity
        @new_entity = new_entity
      end

      def execute
        update_attributes = { labels: cloneable_labels }

        milestone = matching_milestone(original_entity.milestone&.title)
        update_attributes[:milestone] = milestone if milestone.present?

        new_entity.update(update_attributes)

        copy_resource_label_events
        copy_resource_weight_events
        copy_resource_milestone_events
        copy_resource_state_events
      end

      private

      def matching_milestone(title)
        return if title.blank? || !new_entity.supports_milestone?

        params = { title: title, project_ids: new_entity.project&.id, group_ids: group&.id }

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

      def cloneable_labels
        params = {
          project_id: new_entity.project&.id,
          group_id: group&.id,
          title: original_entity.labels.select(:title),
          include_ancestor_groups: true
        }

        params[:only_group_labels] = true if new_parent.is_a?(Group)

        LabelsFinder.new(current_user, params).execute
      end

      def copy_resource_label_events
        copy_events(ResourceLabelEvent.table_name, original_entity.resource_label_events) do |event|
          event.attributes
            .except('id', 'reference', 'reference_html')
            .merge(entity_key => new_entity.id, 'action' => ResourceLabelEvent.actions[event.action])
        end
      end

      def copy_resource_weight_events
        return unless original_entity.respond_to?(:resource_weight_events)

        copy_events(ResourceWeightEvent.table_name, original_entity.resource_weight_events) do |event|
          event.attributes
            .except('id', 'reference', 'reference_html')
            .merge('issue_id' => new_entity.id)
        end
      end

      def copy_resource_milestone_events
        return unless milestone_events_supported?

        copy_events(ResourceMilestoneEvent.table_name, original_entity.resource_milestone_events) do |event|
          if event.remove?
            event_attributes_with_milestone(event, nil)
          else
            matching_destination_milestone = matching_milestone(event.milestone_title)

            event_attributes_with_milestone(event, matching_destination_milestone) if matching_destination_milestone.present?
          end
        end
      end

      def copy_resource_state_events
        return unless state_events_supported?

        copy_events(ResourceStateEvent.table_name, original_entity.resource_state_events) do |event|
          event.attributes
            .except('id')
            .merge(entity_key => new_entity.id,
                   'state' => ResourceStateEvent.states[event.state])
        end
      end

      def event_attributes_with_milestone(event, milestone)
        event.attributes
          .except('id')
          .merge(entity_key => new_entity.id,
                 'milestone_id' => milestone&.id,
                 'action' => ResourceMilestoneEvent.actions[event.action],
                 'state' => ResourceMilestoneEvent.states[event.state])
      end

      def copy_events(table_name, events_to_copy)
        events_to_copy.find_in_batches do |batch|
          events = batch.map do |event|
            yield(event)
          end.compact

          Gitlab::Database.bulk_insert(table_name, events)
        end
      end

      def entity_key
        new_entity.class.name.underscore.foreign_key
      end

      def milestone_events_supported?
        both_respond_to?(:resource_milestone_events)
      end

      def state_events_supported?
        both_respond_to?(:resource_state_events)
      end

      def both_respond_to?(method)
        original_entity.respond_to?(method) &&
          new_entity.respond_to?(method)
      end
    end
  end
end