diff --git a/app/services/projects/import_export.rb b/app/services/projects/import_export.rb new file mode 100644 index 00000000000..f87b10390a5 --- /dev/null +++ b/app/services/projects/import_export.rb @@ -0,0 +1,23 @@ +module Projects + module ImportExport + extend self + + def export_path(project_name:) + File.join(storage_path, "#{Time.now.strftime('%Y-%m-%d_%H-%M-%3N')}_gitlab_export_#{project_name}") + end + + def project_atts + %i(id name path description issues_enabled wall_enabled merge_requests_enabled wiki_enabled snippets_enabled visibility_level archived) + end + + def project_tree + %i(issues merge_requests labels milestones snippets releases events commit_statuses) + end + + private + + def storage_path + File.join(Settings.shared['path'], 'tmp/project_exports') + end + end +end diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb new file mode 100644 index 00000000000..c89dd3d12ed --- /dev/null +++ b/app/services/projects/import_export/export_service.rb @@ -0,0 +1,15 @@ +module Projects + module ImportExport + class ExportService < BaseService + def execute(options = {}) + save_project_tree + end + + private + + def save_project_tree + Projects::ImportExport::ProjectTreeSaver.save(project: project) + end + end + end +end diff --git a/app/services/projects/import_export/project_tree_saver.rb b/app/services/projects/import_export/project_tree_saver.rb new file mode 100644 index 00000000000..4801c791ffe --- /dev/null +++ b/app/services/projects/import_export/project_tree_saver.rb @@ -0,0 +1,42 @@ +module Projects + module ImportExport + class ProjectTreeSaver + attr_reader :full_path + + def initialize(project: ) + @project = project + end + + def save + @full_path = File.join(export_path, project_filename) + save_to_disk + end + + private + + def save_to_disk + FileUtils.mkdir_p(export_path) + File.write(full_path, project_json_tree) + true + rescue + #TODO: handle error + false + end + + def export_path + @export_path ||= ImportExport.export_path(@project.name) + end + + def project_filename + # TODO sanitize name + "#{@project.name}.json" + end + + def project_json_tree + # TODO confirm children, also add subchildren (i.e comments) + # TODO confirm atts for children + @project.to_json(only: ImportExport.project_atts, include: ImportExport.project_tree) + end + end + end +end diff --git a/spec/services/projects/import_export/project_tree_saver_spec.rb b/spec/services/projects/import_export/project_tree_saver_spec.rb new file mode 100644 index 00000000000..00e44a9f335 --- /dev/null +++ b/spec/services/projects/import_export/project_tree_saver_spec.rb @@ -0,0 +1,83 @@ +require 'spec_helper' + +describe Projects::ImportExport::ProjectTreeSaver, services: true do + describe :save do + + let(:user) { create(:user) } + let!(:project) { create(:project, :public, name: 'searchable_project') } + let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" } + let(:project_tree_saver) { Projects::ImportExport::ProjectTreeSaver.new(project: project) } + + before(:each) do + project.team << [user, :master] + allow_any_instance_of(Projects::ImportExport::ProjectTreeSaver).to receive(:export_path).and_return(export_path) + end + + after(:each) do + FileUtils.rm_rf(export_path) + end + + it 'saves project successfully' do + expect(project_tree_saver.save).to be true + end + + it 'saves the correct json' do + project_tree_saver.save + expect(project_json(project_tree_saver.full_path)).to include({ "name" => project.name }) + end + end + + def project_json(filename) + JSON.parse(IO.read(filename)) + end + + # TODO: Remove this. Current JSON pretty printed: + # { + # "id": 1, + # "name": "searchable_project", + # "path": "gitlabhq", + # "description": null, + # "issues_enabled": true, + # "wall_enabled": false, + # "merge_requests_enabled": true, + # "wiki_enabled": true, + # "snippets_enabled": true, + # "visibility_level": 20, + # "archived": false, + # "issues": [ + # + # ], + # "merge_requests": [ + # + # ], + # "labels": [ + # + # ], + # "milestones": [ + # + # ], + # "snippets": [ + # + # ], + # "releases": [ + # + # ], + # "events": [ + # { + # "id": 1, + # "target_type": null, + # "target_id": null, + # "title": null, + # "data": null, + # "project_id": 1, + # "created_at": "2016-03-07T17:05:20.926Z", + # "updated_at": "2016-03-07T17:05:20.926Z", + # "action": 8, + # "author_id": 3 + # } + # ], + # "commit_statuses": [ + # + # ] + # } +end