From acfa0b69d5370749cee9768bf79b58daf6d916a4 Mon Sep 17 00:00:00 2001
From: James Lopez <james@jameslopez.es>
Date: Thu, 7 Apr 2016 13:19:57 +0200
Subject: [PATCH] continuing to refactor config, added spec and fixed a few
 problems

---
 .../projects/import_export/import_export.yml  |  2 +-
 .../import_export/import_export_reader.rb     | 51 ++++++++++++++++---
 .../import_export_reader_spec.rb              | 24 +++++++++
 spec/support/import_export/import_export.yml  | 20 ++++++++
 4 files changed, 89 insertions(+), 8 deletions(-)
 create mode 100644 spec/services/projects/import_export/import_export_reader_spec.rb
 create mode 100644 spec/support/import_export/import_export.yml

diff --git a/app/services/projects/import_export/import_export.yml b/app/services/projects/import_export/import_export.yml
index d02ec5df2ff..40fd98b8092 100644
--- a/app/services/projects/import_export/import_export.yml
+++ b/app/services/projects/import_export/import_export.yml
@@ -1,4 +1,4 @@
-# Class relationships to be included in the import/export
+# Class relationships to be included in the project import/export
 :project_tree:
   - :issues
   - :labels
diff --git a/app/services/projects/import_export/import_export_reader.rb b/app/services/projects/import_export/import_export_reader.rb
index af8c8c3c251..77a1abb851b 100644
--- a/app/services/projects/import_export/import_export_reader.rb
+++ b/app/services/projects/import_export/import_export_reader.rb
@@ -7,6 +7,8 @@ module Projects
         { only: atts_only[:project], include: build_hash(tree) }
       end
 
+      private
+
       def config
         @config ||= YAML.load_file('app/services/projects/import_export/import_export.yml')
       end
@@ -27,21 +29,56 @@ module Projects
         array.map { |el| el.is_a?(Hash) ? process_include(el) : el }
       end
 
-      def process_include(hash)
-        included_classes_hash = {}
+      def process_include(hash, included_classes_hash = {})
         hash.values.flatten.each do |value|
-          value = value.is_a?(Hash) ? process_include(hash) : value
-          new_hash = { :include => value }
-          new_hash.merge!(check_only(value))
-          included_classes_hash[hash.keys.first] = new_hash
+          current_key, value = process_current_class(hash, included_classes_hash, value)
+          if included_classes_hash[current_key]
+            add_class(current_key, included_classes_hash, value)
+          else
+            add_new_class(current_key, included_classes_hash, value)
+          end
         end
         included_classes_hash
       end
 
+      def process_current_class(hash, included_classes_hash, value)
+        value = value.is_a?(Hash) ? process_include(hash, included_classes_hash) : value
+        current_key = hash.keys.first
+        current_key_only = check_only_and_except(current_key)
+        included_classes_hash[current_key] ||= current_key_only unless current_key_only.empty?
+        return current_key, value
+      end
+
+      def add_new_class(current_key, included_classes_hash, value)
+        new_hash = { :include => value }
+        new_hash.merge!(check_only_and_except(value))
+        included_classes_hash[current_key] = new_hash
+      end
+
+      def add_class(current_key, included_classes_hash, value)
+        check_only_hash = check_only_and_except(value)
+        value = { value => check_only_hash } unless check_only_hash.empty?
+        old_values = included_classes_hash[current_key][:include]
+        included_classes_hash[current_key][:include] = ([old_values] + [value]).compact.flatten
+      end
+
+      def check_only_and_except(value)
+        check_only(value).merge(check_except(value))
+      end
+
       def check_only(value)
-        key = value.is_a?(Hash) ? value.keys.first : value
+        key = key_from_hash(value)
         atts_only[key].nil? ? {} : { only: atts_only[key] }
       end
+
+      def check_except(value)
+        key = key_from_hash(value)
+        atts_except[key].nil? ? {} : { except: atts_except[key] }
+      end
+
+      def key_from_hash(value)
+        value.is_a?(Hash) ? value.keys.first : value
+      end
     end
   end
 end
\ No newline at end of file
diff --git a/spec/services/projects/import_export/import_export_reader_spec.rb b/spec/services/projects/import_export/import_export_reader_spec.rb
new file mode 100644
index 00000000000..73e043e52ce
--- /dev/null
+++ b/spec/services/projects/import_export/import_export_reader_spec.rb
@@ -0,0 +1,24 @@
+require 'rspec'
+
+describe Projects::ImportExport::ImportExportReader do
+
+  let(:test_config) { 'spec/support/import_export/import_export.yml' }
+  let(:project_tree_hash) do
+    {
+      :only => [:name, :path],
+      :include => [:issues, :labels,
+                   { :merge_requests => {
+                     :only => [:id],
+                     :except => [:iid],
+                     :include => [:merge_request_diff, :merge_request_test]
+                   } },
+                   { :commit_statuses => { :include => :commit } }]
+    }
+  end
+
+  it 'should generate hash from project tree config' do
+    allow(described_class).to receive(:config).and_return(YAML.load_file(test_config))
+
+    expect(described_class.project_tree).to eq(project_tree_hash)
+  end
+end
\ No newline at end of file
diff --git a/spec/support/import_export/import_export.yml b/spec/support/import_export/import_export.yml
new file mode 100644
index 00000000000..e6985d6413c
--- /dev/null
+++ b/spec/support/import_export/import_export.yml
@@ -0,0 +1,20 @@
+# Class relationships to be included in the project import/export
+:project_tree:
+  - :issues
+  - :labels
+  - :merge_requests:
+    - :merge_request_diff
+    - :merge_request_test
+  - :commit_statuses:
+    - :commit
+
+:attributes_only:
+  :project:
+    - :name
+    - :path
+  :merge_requests:
+    - :id
+
+:attributes_except:
+  :merge_requests:
+    - :iid
\ No newline at end of file
-- 
GitLab