119 lines
3.8 KiB
Ruby
119 lines
3.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Groups
|
|
class TransferService < Groups::BaseService
|
|
ERROR_MESSAGES = {
|
|
database_not_supported: s_('TransferGroup|Database is not supported.'),
|
|
namespace_with_same_path: s_('TransferGroup|The parent group already has a subgroup with the same path.'),
|
|
group_is_already_root: s_('TransferGroup|Group is already a root group.'),
|
|
same_parent_as_current: s_('TransferGroup|Group is already associated to the parent group.'),
|
|
invalid_policies: s_("TransferGroup|You don't have enough permissions."),
|
|
group_contains_images: s_('TransferGroup|Cannot update the path because there are projects under this group that contain Docker images in their Container Registry. Please remove the images from your projects first and try again.')
|
|
}.freeze
|
|
|
|
TransferError = Class.new(StandardError)
|
|
|
|
attr_reader :error, :new_parent_group
|
|
|
|
def initialize(group, user, params = {})
|
|
super
|
|
@error = nil
|
|
end
|
|
|
|
def execute(new_parent_group)
|
|
@new_parent_group = new_parent_group
|
|
ensure_allowed_transfer
|
|
proceed_to_transfer
|
|
|
|
rescue TransferError, ActiveRecord::RecordInvalid, Gitlab::UpdatePathError => e
|
|
@group.errors.clear
|
|
@error = s_("TransferGroup|Transfer failed: %{error_message}") % { error_message: e.message }
|
|
false
|
|
end
|
|
|
|
private
|
|
|
|
def proceed_to_transfer
|
|
Group.transaction do
|
|
update_group_attributes
|
|
ensure_ownership
|
|
end
|
|
|
|
true
|
|
end
|
|
|
|
def ensure_allowed_transfer
|
|
raise_transfer_error(:group_is_already_root) if group_is_already_root?
|
|
raise_transfer_error(:same_parent_as_current) if same_parent?
|
|
raise_transfer_error(:invalid_policies) unless valid_policies?
|
|
raise_transfer_error(:namespace_with_same_path) if namespace_with_same_path?
|
|
raise_transfer_error(:group_contains_images) if group_projects_contain_registry_images?
|
|
end
|
|
|
|
def group_is_already_root?
|
|
!@new_parent_group && !@group.has_parent?
|
|
end
|
|
|
|
def same_parent?
|
|
@new_parent_group && @new_parent_group.id == @group.parent_id
|
|
end
|
|
|
|
def valid_policies?
|
|
return false unless can?(current_user, :admin_group, @group)
|
|
|
|
if @new_parent_group
|
|
can?(current_user, :create_subgroup, @new_parent_group)
|
|
else
|
|
can?(current_user, :create_group)
|
|
end
|
|
end
|
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
|
def namespace_with_same_path?
|
|
Namespace.exists?(path: @group.path, parent: @new_parent_group)
|
|
end
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
|
|
|
def group_projects_contain_registry_images?
|
|
@group.has_container_repositories?
|
|
end
|
|
|
|
def update_group_attributes
|
|
if @new_parent_group && @new_parent_group.visibility_level < @group.visibility_level
|
|
update_children_and_projects_visibility
|
|
@group.visibility_level = @new_parent_group.visibility_level
|
|
end
|
|
|
|
@group.parent = @new_parent_group
|
|
@group.save!
|
|
end
|
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
|
def update_children_and_projects_visibility
|
|
descendants = @group.descendants.where("visibility_level > ?", @new_parent_group.visibility_level)
|
|
|
|
Group
|
|
.where(id: descendants.select(:id))
|
|
.update_all(visibility_level: @new_parent_group.visibility_level)
|
|
|
|
@group
|
|
.all_projects
|
|
.where("visibility_level > ?", @new_parent_group.visibility_level)
|
|
.update_all(visibility_level: @new_parent_group.visibility_level)
|
|
end
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
|
|
|
def ensure_ownership
|
|
return if @new_parent_group
|
|
return unless @group.owners.empty?
|
|
|
|
@group.add_owner(current_user)
|
|
end
|
|
|
|
def raise_transfer_error(message)
|
|
raise TransferError, ERROR_MESSAGES[message]
|
|
end
|
|
end
|
|
end
|
|
|
|
Groups::TransferService.prepend_if_ee('EE::Groups::TransferService')
|