From 01236e0c0bab4206f516d811dd69078171d4c44a Mon Sep 17 00:00:00 2001 From: Rodrigo Tomonari <rtomonari@gitlab.com> Date: Mon, 4 Jul 2022 14:46:23 +0000 Subject: [PATCH] Fix group name conflict when migrating groups via BulkImport Modify BulkImport to make group names unique as a namespace can not have subgroups with the same name. Changelog: fixed --- .../group_attributes_transformer.rb | 28 ++++++-- .../group_attributes_transformer_spec.rb | 65 ++++++++++++++++--- 2 files changed, 79 insertions(+), 14 deletions(-) diff --git a/lib/bulk_imports/groups/transformers/group_attributes_transformer.rb b/lib/bulk_imports/groups/transformers/group_attributes_transformer.rb index df27275b664b1..3067e0997c243 100644 --- a/lib/bulk_imports/groups/transformers/group_attributes_transformer.rb +++ b/lib/bulk_imports/groups/transformers/group_attributes_transformer.rb @@ -7,10 +7,15 @@ class GroupAttributesTransformer def transform(context, data) import_entity = context.entity + if import_entity.destination_namespace.present? + namespace = Namespace.find_by_full_path(import_entity.destination_namespace) + end + data + .then { |data| transform_name(import_entity, namespace, data) } .then { |data| transform_path(import_entity, data) } .then { |data| transform_full_path(data) } - .then { |data| transform_parent(context, import_entity, data) } + .then { |data| transform_parent(context, import_entity, namespace, data) } .then { |data| transform_visibility_level(data) } .then { |data| transform_project_creation_level(data) } .then { |data| transform_subgroup_creation_level(data) } @@ -18,6 +23,20 @@ def transform(context, data) private + def transform_name(import_entity, namespace, data) + if namespace.present? + namespace_children_names = namespace.children.pluck(:name) # rubocop: disable CodeReuse/ActiveRecord + + if namespace_children_names.include?(data['name']) + data['name'] = Uniquify.new(1).string(-> (counter) { "#{data['name']}(#{counter})" }) do |base| + namespace_children_names.include?(base) + end + end + end + + data + end + def transform_path(import_entity, data) data['path'] = import_entity.destination_name.parameterize data @@ -28,11 +47,8 @@ def transform_full_path(data) data end - def transform_parent(context, import_entity, data) - unless import_entity.destination_namespace.blank? - namespace = Namespace.find_by_full_path(import_entity.destination_namespace) - data['parent_id'] = namespace.id - end + def transform_parent(context, import_entity, namespace, data) + data['parent_id'] = namespace.id if namespace.present? data end diff --git a/spec/lib/bulk_imports/groups/transformers/group_attributes_transformer_spec.rb b/spec/lib/bulk_imports/groups/transformers/group_attributes_transformer_spec.rb index c42ca9bef3be6..d775cf6b02679 100644 --- a/spec/lib/bulk_imports/groups/transformers/group_attributes_transformer_spec.rb +++ b/spec/lib/bulk_imports/groups/transformers/group_attributes_transformer_spec.rb @@ -4,12 +4,12 @@ RSpec.describe BulkImports::Groups::Transformers::GroupAttributesTransformer do describe '#transform' do - let_it_be(:user) { create(:user) } let_it_be(:parent) { create(:group) } - let_it_be(:bulk_import) { create(:bulk_import, user: user) } - let_it_be(:entity) do - create( + let(:bulk_import) { build_stubbed(:bulk_import) } + + let(:entity) do + build_stubbed( :bulk_import_entity, bulk_import: bulk_import, source_full_path: 'source/full/path', @@ -18,8 +18,8 @@ ) end - let_it_be(:tracker) { create(:bulk_import_tracker, entity: entity) } - let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) } + let(:tracker) { build_stubbed(:bulk_import_tracker, entity: entity) } + let(:context) { BulkImports::Pipeline::Context.new(tracker) } let(:data) do { @@ -87,14 +87,63 @@ end context 'when destination namespace is empty' do - it 'does not set parent id' do - entity.update!(destination_namespace: '') + before do + entity.destination_namespace = '' + end + it 'does not set parent id' do transformed_data = subject.transform(context, data) expect(transformed_data).not_to have_key('parent_id') end end end + + describe 'group name transformation' do + context 'when destination namespace is empty' do + before do + entity.destination_namespace = '' + end + + it 'does not transform name' do + transformed_data = subject.transform(context, data) + + expect(transformed_data['name']).to eq('Source Group Name') + end + end + + context 'when destination namespace is present' do + context 'when destination namespace does not have a group with same name' do + it 'does not transform name' do + transformed_data = subject.transform(context, data) + + expect(transformed_data['name']).to eq('Source Group Name') + end + end + + context 'when destination namespace already have a group with the same name' do + before do + create(:group, parent: parent, name: 'Source Group Name', path: 'group_1') + create(:group, parent: parent, name: 'Source Group Name(1)', path: 'group_2') + create(:group, parent: parent, name: 'Source Group Name(2)', path: 'group_3') + create(:group, parent: parent, name: 'Source Group Name(1)(1)', path: 'group_4') + end + + it 'makes the name unique by appeding a counter', :aggregate_failures do + transformed_data = subject.transform(context, data.merge('name' => 'Source Group Name')) + expect(transformed_data['name']).to eq('Source Group Name(3)') + + transformed_data = subject.transform(context, data.merge('name' => 'Source Group Name(2)')) + expect(transformed_data['name']).to eq('Source Group Name(2)(1)') + + transformed_data = subject.transform(context, data.merge('name' => 'Source Group Name(1)')) + expect(transformed_data['name']).to eq('Source Group Name(1)(2)') + + transformed_data = subject.transform(context, data.merge('name' => 'Source Group Name(1)(1)')) + expect(transformed_data['name']).to eq('Source Group Name(1)(1)(1)') + end + end + end + end end end -- GitLab