summaryrefslogtreecommitdiff
path: root/app/services/topics/merge_service.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/services/topics/merge_service.rb')
-rw-r--r--app/services/topics/merge_service.rb64
1 files changed, 64 insertions, 0 deletions
diff --git a/app/services/topics/merge_service.rb b/app/services/topics/merge_service.rb
new file mode 100644
index 00000000000..0d256579fe0
--- /dev/null
+++ b/app/services/topics/merge_service.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+module Topics
+ class MergeService
+ attr_accessor :source_topic, :target_topic
+
+ def initialize(source_topic, target_topic)
+ @source_topic = source_topic
+ @target_topic = target_topic
+ end
+
+ def execute
+ validate_parameters!
+
+ ::Projects::ProjectTopic.transaction do
+ move_project_topics
+ refresh_target_topic_counters
+ delete_source_topic
+ end
+ end
+
+ private
+
+ def validate_parameters!
+ raise ArgumentError, 'The source topic is not a topic.' unless source_topic.is_a?(Projects::Topic)
+ raise ArgumentError, 'The target topic is not a topic.' unless target_topic.is_a?(Projects::Topic)
+ raise ArgumentError, 'The source topic and the target topic are identical.' if source_topic == target_topic
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def move_project_topics
+ project_ids_for_projects_currently_using_source_and_target = ::Projects::ProjectTopic
+ .where(topic_id: target_topic).select(:project_id)
+ # Only update for projects that exclusively use the source topic
+ ::Projects::ProjectTopic.where(topic_id: source_topic.id)
+ .where.not(project_id: project_ids_for_projects_currently_using_source_and_target)
+ .update_all(topic_id: target_topic.id)
+
+ # Delete source topic for projects that were using source and target
+ ::Projects::ProjectTopic.where(topic_id: source_topic.id).delete_all
+ end
+
+ def refresh_target_topic_counters
+ target_topic.update!(
+ total_projects_count: total_projects_count(target_topic.id),
+ non_private_projects_count: non_private_projects_count(target_topic.id)
+ )
+ end
+
+ def delete_source_topic
+ source_topic.destroy!
+ end
+
+ def total_projects_count(topic_id)
+ ::Projects::ProjectTopic.where(topic_id: topic_id).count
+ end
+
+ def non_private_projects_count(topic_id)
+ ::Projects::ProjectTopic.joins(:project).where(topic_id: topic_id).where('projects.visibility_level in (10, 20)')
+ .count
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+end