Commit ffa71b6a authored by James Lopez's avatar James Lopez

Merge branch 'feature/project-import' of gitlab.com:gitlab-org/gitlab-ce into...

Merge branch 'feature/project-import' of gitlab.com:gitlab-org/gitlab-ce into feature/project-export-ui-experimental
parents f6c9883d 25a1c654
...@@ -2,4 +2,4 @@ module Gitlab ...@@ -2,4 +2,4 @@ module Gitlab
module ImportExport module ImportExport
class Error < StandardError; end class Error < StandardError; end
end end
end end
\ No newline at end of file
# Model relationships to be included in the project import/export # Model relationships to be included in the project import/export
project_tree: project_tree:
- issues: - issues:
- :notes - notes:
:author
- :labels - :labels
- :milestones - :milestones
- :snippets - :snippets
...@@ -10,8 +11,9 @@ project_tree: ...@@ -10,8 +11,9 @@ project_tree:
- project_members: - project_members:
- :user - :user
- merge_requests: - merge_requests:
- notes:
:author
- :merge_request_diff - :merge_request_diff
- :notes
- ci_commits: - ci_commits:
- :statuses - :statuses
...@@ -31,6 +33,8 @@ included_attributes: ...@@ -31,6 +33,8 @@ included_attributes:
- :id - :id
- :email - :email
- :username - :username
author:
- :name
# Do not include the following attributes for the models specified. # Do not include the following attributes for the models specified.
excluded_attributes: excluded_attributes:
......
...@@ -15,7 +15,7 @@ module Gitlab ...@@ -15,7 +15,7 @@ module Gitlab
def project_tree def project_tree
@attributes_parser.find_included(:project).merge(include: build_hash(@tree)) @attributes_parser.find_included(:project).merge(include: build_hash(@tree))
rescue => e rescue => e
@shared.error(e.message) @shared.error(e)
end end
private private
...@@ -39,10 +39,22 @@ module Gitlab ...@@ -39,10 +39,22 @@ module Gitlab
@attributes_parser.parse(current_key) { |hash| @json_config_hash[current_key] ||= hash } @attributes_parser.parse(current_key) { |hash| @json_config_hash[current_key] ||= hash }
handle_model_object(current_key, model_object) handle_model_object(current_key, model_object)
process_sub_model(current_key, model_object) if model_object.is_a?(Hash)
end end
@json_config_hash @json_config_hash
end end
def process_sub_model(current_key, model_object)
sub_model_json = build_json_config_hash(model_object).dup
@json_config_hash.slice!(current_key)
if @json_config_hash[current_key] && @json_config_hash[current_key][:include]
@json_config_hash[current_key][:include] << sub_model_json
else
@json_config_hash[current_key] = { include: sub_model_json }
end
end
def handle_model_object(current_key, model_object) def handle_model_object(current_key, model_object)
if @json_config_hash[current_key] if @json_config_hash[current_key]
add_model_value(current_key, model_object) add_model_value(current_key, model_object)
...@@ -55,7 +67,7 @@ module Gitlab ...@@ -55,7 +67,7 @@ module Gitlab
parsed_hash = { include: value } parsed_hash = { include: value }
@attributes_parser.parse(value) do |hash| @attributes_parser.parse(value) do |hash|
parsed_hash = { include: hash_or_merge(value, hash) } parsed_hash = { include: hash_or_merge(value, hash) }
end end
@json_config_hash[current_key] = parsed_hash @json_config_hash[current_key] = parsed_hash
end end
......
...@@ -16,7 +16,7 @@ module Gitlab ...@@ -16,7 +16,7 @@ module Gitlab
FileUtils.mkdir_p(@shared.export_path) FileUtils.mkdir_p(@shared.export_path)
decompress_archive decompress_archive
rescue => e rescue => e
@shared.error(e.message) @shared.error(e)
false false
end end
......
...@@ -2,18 +2,25 @@ module Gitlab ...@@ -2,18 +2,25 @@ module Gitlab
module ImportExport module ImportExport
class MembersMapper class MembersMapper
def self.map(*args) attr_reader :map, :note_member_list
new(*args).map
end
def initialize(exported_members:, user:, project_id:) def initialize(exported_members:, user:, project_id:)
@exported_members = exported_members @exported_members = exported_members
@user = user @user = user
@project_id = project_id @project_id = project_id
@note_member_list = []
@project_member_map = Hash.new do |_, key|
@note_member_list << key
default_project_member
end
@map = generate_map
end end
def map private
@project_member_map = Hash.new(default_project_member)
def generate_map
@exported_members.each do |member| @exported_members.each do |member|
existing_user = User.where(find_project_user_query(member)).first existing_user = User.where(find_project_user_query(member)).first
assign_member(existing_user, member) if existing_user assign_member(existing_user, member) if existing_user
...@@ -21,8 +28,6 @@ module Gitlab ...@@ -21,8 +28,6 @@ module Gitlab
@project_member_map @project_member_map
end end
private
def assign_member(existing_user, member) def assign_member(existing_user, member)
old_user_id = member['user']['id'] old_user_id = member['user']['id']
member['user'] = existing_user member['user'] = existing_user
......
...@@ -16,7 +16,7 @@ module Gitlab ...@@ -16,7 +16,7 @@ module Gitlab
@project_members = @tree_hash.delete('project_members') @project_members = @tree_hash.delete('project_members')
create_relations create_relations
rescue => e rescue => e
@shared.error(e.message) @shared.error(e)
false false
end end
...@@ -26,9 +26,10 @@ module Gitlab ...@@ -26,9 +26,10 @@ module Gitlab
private private
def members_map def members_mapper
@members ||= Gitlab::ImportExport::MembersMapper.map( @members_mapper ||= Gitlab::ImportExport::MembersMapper.new(exported_members: @project_members,
exported_members: @project_members, user: @user, project_id: project.id) user: @user,
project_id: project.id)
end end
def create_relations(relation_list = default_relation_list, tree_hash = @tree_hash) def create_relations(relation_list = default_relation_list, tree_hash = @tree_hash)
...@@ -61,11 +62,18 @@ module Gitlab ...@@ -61,11 +62,18 @@ module Gitlab
end end
def create_sub_relations(relation, tree_hash) def create_sub_relations(relation, tree_hash)
tree_hash[relation.keys.first.to_s].each do |relation_item| relation_key = relation.keys.first.to_s
tree_hash[relation_key].each do |relation_item|
relation.values.flatten.each do |sub_relation| relation.values.flatten.each do |sub_relation|
relation_hash = relation_item[sub_relation.to_s]
next if relation_hash.blank? if sub_relation.is_a?(Hash)
process_sub_relation(relation_hash, relation_item, sub_relation) relation_hash = relation_item[sub_relation.keys.first.to_s]
sub_relation = sub_relation.keys.first
else
relation_hash = relation_item[sub_relation.to_s]
end
process_sub_relation(relation_hash, relation_item, sub_relation) unless relation_hash.blank?
end end
end end
end end
...@@ -87,7 +95,7 @@ module Gitlab ...@@ -87,7 +95,7 @@ module Gitlab
def relation_from_factory(relation, relation_hash) def relation_from_factory(relation, relation_hash)
Gitlab::ImportExport::RelationFactory.create( Gitlab::ImportExport::RelationFactory.create(
relation_sym: relation.to_sym, relation_hash: relation_hash.merge('project_id' => project.id), members_map: members_map) relation_sym: relation.to_sym, relation_hash: relation_hash.merge('project_id' => project.id), members_mapper: members_mapper)
end end
end end
end end
......
...@@ -14,7 +14,7 @@ module Gitlab ...@@ -14,7 +14,7 @@ module Gitlab
File.write(full_path, project_json_tree) File.write(full_path, project_json_tree)
true true
rescue => e rescue => e
@shared.error(e.message) @shared.error(e)
false false
end end
......
...@@ -6,11 +6,12 @@ module Gitlab ...@@ -6,11 +6,12 @@ module Gitlab
OVERRIDES = { snippets: :project_snippets, ci_commits: 'Ci::Commit', statuses: 'commit_status' }.freeze OVERRIDES = { snippets: :project_snippets, ci_commits: 'Ci::Commit', statuses: 'commit_status' }.freeze
USER_REFERENCES = %w(author_id assignee_id updated_by_id).freeze USER_REFERENCES = %w(author_id assignee_id updated_by_id).freeze
def create(relation_sym:, relation_hash:, members_map:) def create(relation_sym:, relation_hash:, members_mapper:)
relation_sym = parse_relation_sym(relation_sym) relation_sym = parse_relation_sym(relation_sym)
klass = parse_relation(relation_hash, relation_sym) klass = parse_relation(relation_hash, relation_sym)
update_user_references(relation_hash, members_map) update_missing_author(relation_hash, members_mapper) if relation_sym == :notes
update_user_references(relation_hash, members_mapper.map)
update_project_references(relation_hash, klass) update_project_references(relation_hash, klass)
imported_object(klass, relation_hash) imported_object(klass, relation_hash)
...@@ -26,6 +27,21 @@ module Gitlab ...@@ -26,6 +27,21 @@ module Gitlab
end end
end end
def update_missing_author(relation_hash, members_map)
old_author_id = relation_hash['author_id'].dup
relation_hash['author_id'] = members_map.map[old_author_id]
return unless members_map.note_member_list.include?(old_author_id)
relation_hash['note'] = ('*Blank note*') if relation_hash['note'].blank?
relation_hash['note'].join(missing_author_note(relation_hash['updated_at'],
relation_hash['author']['name']))
relation_hash.delete('author')
end
def missing_author_note(updated_at, author_name)
"\n\n *By #{author_name} on #{updated_at} (imported from GitLab project)*"
end
def update_project_references(relation_hash, klass) def update_project_references(relation_hash, klass)
project_id = relation_hash.delete('project_id') project_id = relation_hash.delete('project_id')
......
...@@ -22,7 +22,7 @@ module Gitlab ...@@ -22,7 +22,7 @@ module Gitlab
FileUtils.mkdir_p(@shared.export_path) FileUtils.mkdir_p(@shared.export_path)
git_bundle(repo_path: path_to_repo, bundle_path: @full_path) git_bundle(repo_path: path_to_repo, bundle_path: @full_path)
rescue => e rescue => e
@shared.error(e.message) @shared.error(e)
false false
end end
......
...@@ -17,7 +17,7 @@ module Gitlab ...@@ -17,7 +17,7 @@ module Gitlab
git_unbundle(repo_path: path_to_repo, bundle_path: @path_to_bundle) git_unbundle(repo_path: path_to_repo, bundle_path: @path_to_bundle)
rescue => e rescue => e
@shared.error(e.message) @shared.error(e)
false false
end end
......
...@@ -20,7 +20,7 @@ module Gitlab ...@@ -20,7 +20,7 @@ module Gitlab
false false
end end
rescue => e rescue => e
@shared.error(e.message) @shared.error(e)
false false
end end
......
...@@ -13,9 +13,11 @@ module Gitlab ...@@ -13,9 +13,11 @@ module Gitlab
@export_path ||= Gitlab::ImportExport.export_path(relative_path: @opts[:relative_path]) @export_path ||= Gitlab::ImportExport.export_path(relative_path: @opts[:relative_path])
end end
def error(message) def error(error)
error_out(message, caller[0].dup) error_out(error.message, caller[0].dup)
@errors << message @errors << error.message
# Debug:
Rails.logger.error(error.backtrace)
end end
private private
......
...@@ -12,7 +12,7 @@ module Gitlab ...@@ -12,7 +12,7 @@ module Gitlab
FileUtils.mkdir_p(@shared.export_path) FileUtils.mkdir_p(@shared.export_path)
git_bundle(repo_path: path_to_repo, bundle_path: @full_path) git_bundle(repo_path: path_to_repo, bundle_path: @full_path)
rescue => e rescue => e
@shared.error(e.message) @shared.error(e)
false false
end end
......
...@@ -42,9 +42,11 @@ describe Gitlab::ImportExport::MembersMapper, services: true do ...@@ -42,9 +42,11 @@ describe Gitlab::ImportExport::MembersMapper, services: true do
it 'defaults to importer project member if it does not exist' do it 'defaults to importer project member if it does not exist' do
expect(members_mapper.map[-1]).to eq(user.id) expect(members_mapper.map[-1]).to eq(user.id)
end end
end
def project_member_user_id(id) it 'updates missing author IDs on missing project member' do
members_mapper.map[id] members_mapper.map[-1]
expect(members_mapper.note_member_list.first).to eq(-1)
end
end end
end end
...@@ -7,12 +7,9 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do ...@@ -7,12 +7,9 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
let(:namespace) { create(:namespace, owner: user) } let(:namespace) { create(:namespace, owner: user) }
let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: "../../../spec/lib/gitlab/import_export/", project_path: 'path') } let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: "../../../spec/lib/gitlab/import_export/", project_path: 'path') }
let(:project_tree_restorer) { described_class.new(user: user, shared: shared, namespace_id: namespace.id) } let(:project_tree_restorer) { described_class.new(user: user, shared: shared, namespace_id: namespace.id) }
let!(:restored_project_json) { project_tree_restorer.restore }
context 'JSON' do context 'JSON' do
let(:restored_project_json) do
project_tree_restorer.restore
end
it 'restores models based on JSON' do it 'restores models based on JSON' do
expect(restored_project_json).to be true expect(restored_project_json).to be true
end end
......
...@@ -65,6 +65,10 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do ...@@ -65,6 +65,10 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
expect(saved_project_json['issues'].first['notes']).not_to be_empty expect(saved_project_json['issues'].first['notes']).not_to be_empty
end end
it 'has author on issue comments' do
expect(saved_project_json['issues'].first['notes'].first['author']).not_to be_empty
end
it 'has project members' do it 'has project members' do
expect(saved_project_json['project_members']).not_to be_empty expect(saved_project_json['project_members']).not_to be_empty
end end
...@@ -77,6 +81,9 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do ...@@ -77,6 +81,9 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
expect(saved_project_json['merge_requests'].first['notes']).not_to be_empty expect(saved_project_json['merge_requests'].first['notes']).not_to be_empty
end end
it 'has author on merge requests comments' do
expect(saved_project_json['merge_requests'].first['notes'].first['author']).not_to be_empty
end
it 'has commit statuses' do it 'has commit statuses' do
expect(saved_project_json['ci_commits'].first['statuses']).not_to be_empty expect(saved_project_json['ci_commits'].first['statuses']).not_to be_empty
end 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