Commit 95a3e2da authored by Rémy Coutable's avatar Rémy Coutable

Merge branch 'fix/import-group-members' into 'master'

Fix missing group members from Import/Export

Closes #25124

See merge request !8923
parents f88672b0 8409340b
......@@ -26,7 +26,7 @@ module Projects
end
def project_tree_saver
Gitlab::ImportExport::ProjectTreeSaver.new(project: project, shared: @shared)
Gitlab::ImportExport::ProjectTreeSaver.new(project: project, current_user: @current_user, shared: @shared)
end
def uploads_saver
......
---
title: Add ability to export project inherited group members to Import/Export
merge_request: 8923
author:
......@@ -14,6 +14,11 @@
> raketask.
> - The exports are stored in a temporary [shared directory][tmp] and are deleted
> every 24 hours by a specific worker.
> - Group members will get exported as project members, as long as the user has
> master or admin access to the group where the exported project lives. An admin
> in the import side is required to map the users, based on email or username.
> Otherwise, a supplementary comment is left to mention the original author and
> the MRs, notes or issues will be owned by the importer.
Existing projects running on any GitLab instance or GitLab.com can be exported
with all their related data and be moved into a new GitLab instance.
......@@ -22,7 +27,7 @@ with all their related data and be moved into a new GitLab instance.
| GitLab version | Import/Export version |
| -------- | -------- |
| 8.16.2 to current | 0.1.6 |
| 8.17.0 to current | 0.1.6 |
| 8.13.0 | 0.1.5 |
| 8.12.0 | 0.1.4 |
| 8.10.3 | 0.1.3 |
......
......@@ -32,6 +32,10 @@ module Gitlab
@user.id
end
def include?(old_author_id)
map.keys.include?(old_author_id) && map[old_author_id] != default_user_id
end
private
def missing_keys_tracking_hash
......
......@@ -5,8 +5,9 @@ module Gitlab
attr_reader :full_path
def initialize(project:, shared:)
def initialize(project:, current_user:, shared:)
@project = project
@current_user = current_user
@shared = shared
@full_path = File.join(@shared.export_path, ImportExport.project_filename)
end
......@@ -24,7 +25,29 @@ module Gitlab
private
def project_json_tree
@project.to_json(Gitlab::ImportExport::Reader.new(shared: @shared).project_tree)
project_json['project_members'] += group_members_json
project_json.to_json
end
def project_json
@project_json ||= @project.as_json(reader.project_tree)
end
def reader
@reader ||= Gitlab::ImportExport::Reader.new(shared: @shared)
end
def group_members_json
group_members.as_json(reader.group_members_tree).each do |group_member|
group_member['source_type'] = 'Project' # Make group members project members of the future import
end
end
def group_members
return [] unless @current_user.can?(:admin_group, @project.group)
MembersFinder.new(@project.project_members, @project.group).execute(@current_user)
end
end
end
......
......@@ -21,6 +21,10 @@ module Gitlab
false
end
def group_members_tree
@attributes_finder.find_included(:project_members).merge(include: @attributes_finder.find(:user))
end
private
# Builds a hash in the format described here: http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html
......
......@@ -89,7 +89,7 @@ module Gitlab
end
def has_author?(old_author_id)
admin_user? && @members_mapper.map.keys.include?(old_author_id)
admin_user? && @members_mapper.include?(old_author_id)
end
def missing_author_note(updated_at, author_name)
......
......@@ -116,5 +116,27 @@ describe Gitlab::ImportExport::MembersMapper, services: true do
expect(members_mapper.map[exported_user_id]).to eq(user2.id)
end
end
context 'importing group members' do
let(:group) { create(:group) }
let(:project) { create(:empty_project, namespace: group) }
let(:members_mapper) do
described_class.new(
exported_members: exported_members, user: user, project: project)
end
before do
group.add_users([user, user2], GroupMember::DEVELOPER)
user.update(email: 'invite@test.com')
end
it 'maps the importer' do
expect(members_mapper.map[-1]).to eq(user.id)
end
it 'maps the group member' do
expect(members_mapper.map[exported_user_id]).to eq(user2.id)
end
end
end
end
......@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
describe 'saves the project tree into a json object' do
let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) }
let(:project_tree_saver) { described_class.new(project: project, shared: shared) }
let(:project_tree_saver) { described_class.new(project: project, current_user: user, shared: shared) }
let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" }
let(:user) { create(:user) }
let(:project) { setup_project }
......@@ -92,7 +92,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
end
it 'has pipeline builds' do
expect(saved_project_json['pipelines'].first['statuses'].count { |hash| hash['type'] == 'Ci::Build'}).to eq(1)
expect(saved_project_json['pipelines'].first['statuses'].count { |hash| hash['type'] == 'Ci::Build' }).to eq(1)
end
it 'has pipeline commits' do
......@@ -112,13 +112,13 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
end
it 'has project and group labels' do
label_types = saved_project_json['issues'].first['label_links'].map { |link| link['label']['type']}
label_types = saved_project_json['issues'].first['label_links'].map { |link| link['label']['type'] }
expect(label_types).to match_array(['ProjectLabel', 'GroupLabel'])
end
it 'has priorities associated to labels' do
priorities = saved_project_json['issues'].first['label_links'].map { |link| link['label']['priorities']}
priorities = saved_project_json['issues'].first['label_links'].map { |link| link['label']['priorities'] }
expect(priorities.flatten).not_to be_empty
end
......@@ -140,6 +140,51 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
expect(project_tree_saver.save).to be true
end
context 'group members' do
let(:user2) { create(:user, email: 'group@member.com') }
let(:member_emails) do
saved_project_json['project_members'].map do |pm|
pm['user']['email']
end
end
before do
Group.first.add_developer(user2)
end
it 'does not export group members if it has no permission' do
Group.first.add_developer(user)
expect(member_emails).not_to include('group@member.com')
end
it 'does not export group members as master' do
Group.first.add_master(user)
expect(member_emails).not_to include('group@member.com')
end
it 'exports group members as group owner' do
Group.first.add_owner(user)
expect(member_emails).to include('group@member.com')
end
context 'as admin' do
let(:user) { create(:admin) }
it 'exports group members as admin' do
expect(member_emails).to include('group@member.com')
end
it 'exports group members as project members' do
member_types = saved_project_json['project_members'].map { |pm| pm['source_type'] }
expect(member_types).to all(eq('Project'))
end
end
end
end
end
......
......@@ -86,6 +86,10 @@ describe Gitlab::ImportExport::Reader, lib: true do
expect(described_class.new(shared: shared).project_tree).to match(include: [{ issues: { methods: [:name] } }])
end
it 'generates the correct hash for group members' do
expect(described_class.new(shared: shared).group_members_tree).to match({ include: { user: { only: [:email] } } })
end
def setup_yaml(hash)
allow(YAML).to receive(:load_file).with(test_config).and_return(hash)
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment