2018-07-17 12:50:37 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2017-10-31 11:03:52 -04:00
|
|
|
module Milestones
|
|
|
|
class PromoteService < Milestones::BaseService
|
|
|
|
PromoteMilestoneError = Class.new(StandardError)
|
|
|
|
|
|
|
|
def execute(milestone)
|
|
|
|
check_project_milestone!(milestone)
|
|
|
|
|
|
|
|
Milestone.transaction do
|
|
|
|
group_milestone = clone_project_milestone(milestone)
|
|
|
|
|
|
|
|
move_children_to_group_milestone(group_milestone)
|
|
|
|
|
2017-11-20 09:44:10 -05:00
|
|
|
# Destroy all milestones with same title across projects
|
|
|
|
destroy_old_milestones(milestone)
|
|
|
|
|
|
|
|
# Rollback if milestone is not valid
|
2017-10-31 11:03:52 -04:00
|
|
|
unless group_milestone.valid?
|
|
|
|
raise_error(group_milestone.errors.full_messages.to_sentence)
|
|
|
|
end
|
|
|
|
|
|
|
|
group_milestone
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2017-10-31 11:03:52 -04:00
|
|
|
def milestone_ids_for_merge(group_milestone)
|
|
|
|
# Pluck need to be used here instead of select so the array of ids
|
|
|
|
# is persistent after old milestones gets deleted.
|
|
|
|
@milestone_ids_for_merge ||= begin
|
|
|
|
search_params = { title: group_milestone.title, project_ids: group_project_ids, state: 'all' }
|
|
|
|
milestones = MilestonesFinder.new(search_params).execute
|
|
|
|
milestones.pluck(:id)
|
|
|
|
end
|
|
|
|
end
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2017-10-31 11:03:52 -04:00
|
|
|
|
|
|
|
def move_children_to_group_milestone(group_milestone)
|
2017-11-20 09:44:10 -05:00
|
|
|
milestone_ids_for_merge(group_milestone).in_groups_of(100, false) do |milestone_ids|
|
2017-10-31 11:03:52 -04:00
|
|
|
update_children(group_milestone, milestone_ids)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def check_project_milestone!(milestone)
|
|
|
|
raise_error('Only project milestones can be promoted.') unless milestone.project_milestone?
|
|
|
|
end
|
|
|
|
|
|
|
|
def clone_project_milestone(milestone)
|
|
|
|
params = milestone.slice(:title, :description, :start_date, :due_date, :state_event)
|
|
|
|
|
|
|
|
create_service = CreateService.new(group, current_user, params)
|
|
|
|
|
2017-11-20 09:44:10 -05:00
|
|
|
milestone = create_service.execute
|
|
|
|
|
|
|
|
# milestone won't be valid here because of duplicated title
|
|
|
|
milestone.save(validate: false)
|
|
|
|
|
|
|
|
milestone
|
2017-10-31 11:03:52 -04:00
|
|
|
end
|
|
|
|
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2017-10-31 11:03:52 -04:00
|
|
|
def update_children(group_milestone, milestone_ids)
|
|
|
|
issues = Issue.where(project_id: group_project_ids, milestone_id: milestone_ids)
|
|
|
|
merge_requests = MergeRequest.where(source_project_id: group_project_ids, milestone_id: milestone_ids)
|
|
|
|
|
|
|
|
[issues, merge_requests].each do |issuable_collection|
|
|
|
|
issuable_collection.update_all(milestone_id: group_milestone.id)
|
|
|
|
end
|
|
|
|
end
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2017-10-31 11:03:52 -04:00
|
|
|
|
|
|
|
def group
|
|
|
|
@group ||= parent.group || raise_error('Project does not belong to a group.')
|
|
|
|
end
|
|
|
|
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2017-11-20 09:44:10 -05:00
|
|
|
def destroy_old_milestones(milestone)
|
2018-08-16 08:46:40 -04:00
|
|
|
Milestone.where(id: milestone_ids_for_merge(milestone)).destroy_all # rubocop: disable DestroyAll
|
2017-10-31 11:03:52 -04:00
|
|
|
end
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2017-10-31 11:03:52 -04:00
|
|
|
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2017-10-31 11:03:52 -04:00
|
|
|
def group_project_ids
|
2017-11-20 09:44:10 -05:00
|
|
|
@group_project_ids ||= group.projects.pluck(:id)
|
2017-10-31 11:03:52 -04:00
|
|
|
end
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2017-10-31 11:03:52 -04:00
|
|
|
|
|
|
|
def raise_error(message)
|
|
|
|
raise PromoteMilestoneError, "Promotion failed - #{message}"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|