gitlab-org--gitlab-foss/lib/gitlab/import_export/group/tree_restorer.rb

155 lines
4.4 KiB
Ruby

# frozen_string_literal: true
module Gitlab
module ImportExport
module Group
class TreeRestorer
include Gitlab::Utils::StrongMemoize
attr_reader :user, :shared, :groups_mapping
def initialize(user:, shared:, group:)
@user = user
@shared = shared
@top_level_group = group
@groups_mapping = {}
end
def restore
group_ids = relation_reader.consume_relation('groups', '_all').map { |value, _idx| Integer(value) }
root_group_id = group_ids.delete_at(0)
process_root(root_group_id)
group_ids.each do |group_id|
process_child(group_id)
end
true
rescue StandardError => e
shared.error(e)
false
end
class GroupAttributes
attr_reader :attributes, :group_id, :id, :path
def initialize(group_id, relation_reader)
@group_id = group_id
@path = "groups/#{group_id}"
@attributes = relation_reader.consume_attributes(@path)
@id = @attributes.delete('id')
unless @id == @group_id
raise ArgumentError, "Invalid group_id for #{group_id}"
end
end
def delete_attribute(name)
attributes.delete(name)
end
def delete_attributes(*names)
names.map(&method(:delete_attribute))
end
end
private_constant :GroupAttributes
private
def process_root(group_id)
group_attributes = GroupAttributes.new(group_id, relation_reader)
# name and path are not imported on the root group to avoid conflict
# with existing groups name and/or path.
group_attributes.delete_attributes('name', 'path')
restore_group(@top_level_group, group_attributes)
end
def process_child(group_id)
group_attributes = GroupAttributes.new(group_id, relation_reader)
group = create_group(group_attributes)
restore_group(group, group_attributes)
rescue StandardError => e
import_failure_service.log_import_failure(
source: 'process_child',
relation_key: 'group',
exception: e
)
end
def create_group(group_attributes)
parent_id = group_attributes.delete_attribute('parent_id')
name = group_attributes.delete_attribute('name')
path = group_attributes.delete_attribute('path')
parent_group = @groups_mapping.fetch(parent_id) { raise(ArgumentError, 'Parent group not found') }
group = ::Groups::CreateService.new(
user,
name: name,
path: path,
parent_id: parent_group.id,
visibility_level: sub_group_visibility_level(group_attributes.attributes, parent_group)
).execute
group.validate!
group
end
def restore_group(group, group_attributes)
@groups_mapping[group_attributes.id] = group
Group::GroupRestorer.new(
user: user,
shared: shared,
group: group,
attributes: group_attributes.attributes,
importable_path: group_attributes.path,
relation_reader: relation_reader,
reader: reader
).restore
end
def relation_reader
strong_memoize(:relation_reader) do
ImportExport::Json::NdjsonReader.new(
File.join(shared.export_path, 'tree')
)
end
end
def sub_group_visibility_level(group_hash, parent_group)
original_visibility_level = group_hash['visibility_level'] || Gitlab::VisibilityLevel::PRIVATE
if parent_group && parent_group.visibility_level < original_visibility_level
Gitlab::VisibilityLevel.closest_allowed_level(parent_group.visibility_level)
else
original_visibility_level
end
end
def reader
strong_memoize(:reader) do
Gitlab::ImportExport::Reader.new(
shared: @shared,
config: Gitlab::ImportExport::Config.new(
config: Gitlab::ImportExport.group_config_file
).to_h
)
end
end
def import_failure_service
Gitlab::ImportExport::ImportFailureService.new(@top_level_group)
end
end
end
end
end