summaryrefslogtreecommitdiff
path: root/app/services/issuable/clone
diff options
context:
space:
mode:
Diffstat (limited to 'app/services/issuable/clone')
-rw-r--r--app/services/issuable/clone/attributes_rewriter.rb62
-rw-r--r--app/services/issuable/clone/base_service.rb60
-rw-r--r--app/services/issuable/clone/content_rewriter.rb65
3 files changed, 187 insertions, 0 deletions
diff --git a/app/services/issuable/clone/attributes_rewriter.rb b/app/services/issuable/clone/attributes_rewriter.rb
new file mode 100644
index 00000000000..0300cc0d8d3
--- /dev/null
+++ b/app/services/issuable/clone/attributes_rewriter.rb
@@ -0,0 +1,62 @@
+# 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
+ new_entity.update(milestone: cloneable_milestone, labels: cloneable_labels)
+ copy_resource_label_events
+ end
+
+ private
+
+ def cloneable_milestone
+ title = original_entity.milestone&.title
+ return unless title
+
+ 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
+ original_entity.resource_label_events.find_in_batches do |batch|
+ events = batch.map do |event|
+ entity_key = new_entity.is_a?(Issue) ? 'issue_id' : 'epic_id'
+ # rubocop: disable CodeReuse/ActiveRecord
+ event.attributes
+ .except('id', 'reference', 'reference_html')
+ .merge(entity_key => new_entity.id, 'action' => ResourceLabelEvent.actions[event.action])
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+
+ Gitlab::Database.bulk_insert(ResourceLabelEvent.table_name, events)
+ end
+ end
+
+ def entity_key
+ new_entity.class.name.parameterize('_').foreign_key
+ end
+ end
+ end
+end
diff --git a/app/services/issuable/clone/base_service.rb b/app/services/issuable/clone/base_service.rb
new file mode 100644
index 00000000000..42dd9c666f5
--- /dev/null
+++ b/app/services/issuable/clone/base_service.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+module Issuable
+ module Clone
+ class BaseService < IssuableBaseService
+ attr_reader :original_entity, :new_entity
+
+ alias_method :old_project, :project
+
+ def execute(original_entity, new_project = nil)
+ @original_entity = original_entity
+
+ # Using transaction because of a high resources footprint
+ # on rewriting notes (unfolding references)
+ #
+ ActiveRecord::Base.transaction do
+ @new_entity = create_new_entity
+
+ update_new_entity
+ update_old_entity
+ create_notes
+ end
+ end
+
+ private
+
+ def update_new_entity
+ rewriters = [ContentRewriter, AttributesRewriter]
+
+ rewriters.each do |rewriter|
+ rewriter.new(current_user, original_entity, new_entity).execute
+ end
+ end
+
+ def update_old_entity
+ close_issue
+ end
+
+ def create_notes
+ add_note_from
+ add_note_to
+ end
+
+ def close_issue
+ close_service = Issues::CloseService.new(old_project, current_user)
+ close_service.execute(original_entity, notifications: false, system_note: false)
+ end
+
+ def new_parent
+ new_entity.project ? new_entity.project : new_entity.group
+ end
+
+ def group
+ if new_entity.project&.group && current_user.can?(:read_group, new_entity.project.group)
+ new_entity.project.group
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/issuable/clone/content_rewriter.rb b/app/services/issuable/clone/content_rewriter.rb
new file mode 100644
index 00000000000..e1e0b75085d
--- /dev/null
+++ b/app/services/issuable/clone/content_rewriter.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+module Issuable
+ module Clone
+ class ContentRewriter < ::Issuable::Clone::BaseService
+ def initialize(current_user, original_entity, new_entity)
+ @current_user = current_user
+ @original_entity = original_entity
+ @new_entity = new_entity
+ @project = original_entity.project
+ end
+
+ def execute
+ rewrite_description
+ rewrite_award_emoji(original_entity, new_entity)
+ rewrite_notes
+ end
+
+ private
+
+ def rewrite_description
+ new_entity.update(description: rewrite_content(original_entity.description))
+ end
+
+ def rewrite_notes
+ original_entity.notes_with_associations.find_each do |note|
+ new_note = note.dup
+ new_params = {
+ project: new_entity.project, noteable: new_entity,
+ note: rewrite_content(new_note.note),
+ created_at: note.created_at,
+ updated_at: note.updated_at
+ }
+
+ if note.system_note_metadata
+ new_params[:system_note_metadata] = note.system_note_metadata.dup
+ end
+
+ new_note.update(new_params)
+
+ rewrite_award_emoji(note, new_note)
+ 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_parent)
+ end
+ 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
+ end
+ end
+end