diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/gitlab/import_export/base_relation_factory.rb | 3 | ||||
-rw-r--r-- | lib/gitlab/import_export/group_object_builder.rb | 46 | ||||
-rw-r--r-- | lib/gitlab/import_export/group_relation_factory.rb | 40 | ||||
-rw-r--r-- | lib/gitlab/import_export/group_tree_restorer.rb | 105 | ||||
-rw-r--r-- | lib/gitlab/import_export/members_mapper.rb | 10 |
5 files changed, 202 insertions, 2 deletions
diff --git a/lib/gitlab/import_export/base_relation_factory.rb b/lib/gitlab/import_export/base_relation_factory.rb index 562b549f6a1..d3c8802bcce 100644 --- a/lib/gitlab/import_export/base_relation_factory.rb +++ b/lib/gitlab/import_export/base_relation_factory.rb @@ -24,7 +24,8 @@ module Gitlab last_edited_by_id merge_user_id resolved_by_id - closed_by_id owner_id + closed_by_id + owner_id ].freeze TOKEN_RESET_MODELS = %i[Project Namespace Group Ci::Trigger Ci::Build Ci::Runner ProjectHook].freeze diff --git a/lib/gitlab/import_export/group_object_builder.rb b/lib/gitlab/import_export/group_object_builder.rb new file mode 100644 index 00000000000..fa426e32b60 --- /dev/null +++ b/lib/gitlab/import_export/group_object_builder.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module Gitlab + module ImportExport + # Given a class, it finds or creates a new object at group level. + # + # Example: + # `GroupObjectBuilder.build(Label, label_attributes)` + # finds or initializes a label with the given attributes. + class GroupObjectBuilder < BaseObjectBuilder + def self.build(*args) + Group.transaction do + super + end + end + + def initialize(klass, attributes) + super + + @group = @attributes['group'] + end + + private + + attr_reader :group + + def where_clauses + [ + where_clause_base, + where_clause_for_title, + where_clause_for_description, + where_clause_for_created_at + ].compact + end + + # Returns Arel clause `"{table_name}"."group_id" = {group.id}` + def where_clause_base + table[:group_id].in(group_and_ancestor_ids) + end + + def group_and_ancestor_ids + group.ancestors.map(&:id) << group.id + end + end + end +end diff --git a/lib/gitlab/import_export/group_relation_factory.rb b/lib/gitlab/import_export/group_relation_factory.rb new file mode 100644 index 00000000000..e3597af44d2 --- /dev/null +++ b/lib/gitlab/import_export/group_relation_factory.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Gitlab + module ImportExport + class GroupRelationFactory < BaseRelationFactory + OVERRIDES = { + labels: :group_labels, + priorities: :label_priorities, + label: :group_label, + parent: :epic + }.freeze + + EXISTING_OBJECT_RELATIONS = %i[ + epic + epics + milestone + milestones + label + labels + group_label + group_labels + ].freeze + + private + + def setup_models + setup_note if @relation_name == :notes + + update_group_references + end + + def update_group_references + return unless self.class.existing_object_relations.include?(@relation_name) + return unless @relation_hash['group_id'] + + @relation_hash['group_id'] = @importable.id + end + end + end +end diff --git a/lib/gitlab/import_export/group_tree_restorer.rb b/lib/gitlab/import_export/group_tree_restorer.rb new file mode 100644 index 00000000000..8230e4ff128 --- /dev/null +++ b/lib/gitlab/import_export/group_tree_restorer.rb @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +module Gitlab + module ImportExport + class GroupTreeRestorer + attr_reader :user + attr_reader :shared + attr_reader :group + + def initialize(user:, shared:, group:, group_hash:) + @path = File.join(shared.export_path, 'group.json') + @user = user + @shared = shared + @group = group + @group_hash = group_hash + end + + def restore + @tree_hash = @group_hash || read_tree_hash + @group_members = @tree_hash.delete('members') + @children = @tree_hash.delete('children') + + if members_mapper.map && restorer.restore + @children&.each do |group_hash| + group = create_group(group_hash: group_hash, parent_group: @group) + shared = Gitlab::ImportExport::Shared.new(group) + + self.class.new( + user: @user, + shared: shared, + group: group, + group_hash: group_hash + ).restore + end + end + + return false if @shared.errors.any? + + true + rescue => e + @shared.error(e) + false + end + + private + + def read_tree_hash + json = IO.read(@path) + ActiveSupport::JSON.decode(json) + rescue => e + @shared.logger.error( + group_id: @group.id, + group_name: @group.name, + message: "Import/Export error: #{e.message}" + ) + + raise Gitlab::ImportExport::Error.new('Incorrect JSON format') + end + + def restorer + @relation_tree_restorer ||= RelationTreeRestorer.new( + user: @user, + shared: @shared, + importable: @group, + tree_hash: @tree_hash.except('name', 'path'), + members_mapper: members_mapper, + object_builder: object_builder, + relation_factory: relation_factory, + reader: reader + ) + end + + def create_group(group_hash:, parent_group:) + group_params = { + name: group_hash['name'], + path: group_hash['path'], + parent_id: parent_group&.id + } + + ::Groups::CreateService.new(@user, group_params).execute + end + + def members_mapper + @members_mapper ||= Gitlab::ImportExport::MembersMapper.new(exported_members: @group_members, user: @user, importable: @group) + end + + def relation_factory + Gitlab::ImportExport::GroupRelationFactory + end + + def object_builder + Gitlab::ImportExport::GroupObjectBuilder + end + + def reader + @reader ||= Gitlab::ImportExport::Reader.new( + shared: @shared, + config: Gitlab::ImportExport::Config.new( + config: Gitlab::ImportExport.group_config_file + ).to_h + ) + end + end + end +end diff --git a/lib/gitlab/import_export/members_mapper.rb b/lib/gitlab/import_export/members_mapper.rb index d2e27388b51..68d484d5087 100644 --- a/lib/gitlab/import_export/members_mapper.rb +++ b/lib/gitlab/import_export/members_mapper.rb @@ -9,7 +9,7 @@ module Gitlab @importable = importable # This needs to run first, as second call would be from #map - # which means project members already exist. + # which means Project/Group members already exist. ensure_default_member! end @@ -47,6 +47,8 @@ module Gitlab end def ensure_default_member! + return if user_already_member? + @importable.members.destroy_all # rubocop: disable DestroyAll relation_class.create!(user: @user, access_level: relation_class::MAINTAINER, source_id: @importable.id, importing: true) @@ -54,6 +56,12 @@ module Gitlab raise e, "Error adding importer user to #{@importable.class} members. #{e.message}" end + def user_already_member? + member = @importable.members&.first + + member&.user == @user && member.access_level >= relation_class::MAINTAINER + end + def add_team_member(member, existing_user = nil) member['user'] = existing_user |