From f9eb3fc0aa3c99fd36d3f344dc95f6b4edad5e07 Mon Sep 17 00:00:00 2001
From: James Lopez <james@jameslopez.es>
Date: Mon, 7 Mar 2016 18:26:15 +0100
Subject: [PATCH] WIP - initial export service and spec

---
 app/services/projects/import_export.rb        | 23 +++++
 .../projects/import_export/export_service.rb  | 15 ++++
 .../import_export/project_tree_saver.rb       | 42 ++++++++++
 .../import_export/project_tree_saver_spec.rb  | 83 +++++++++++++++++++
 4 files changed, 163 insertions(+)
 create mode 100644 app/services/projects/import_export.rb
 create mode 100644 app/services/projects/import_export/export_service.rb
 create mode 100644 app/services/projects/import_export/project_tree_saver.rb
 create mode 100644 spec/services/projects/import_export/project_tree_saver_spec.rb

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
-- 
GitLab