From 6dd88e090e94f7f36fafd3e35c35a2868f89eebe Mon Sep 17 00:00:00 2001
From: Douglas Barbosa Alexandre <dbalexandre@gmail.com>
Date: Thu, 21 Jan 2016 16:09:32 -0200
Subject: [PATCH] Extract Projects::ImportService service from
 RepositoryImportWorker

---
 app/services/projects/import_service.rb       |  71 ++++++++++++
 app/workers/repository_import_worker.rb       |  46 ++------
 spec/services/projects/import_service_spec.rb | 106 ++++++++++++++++++
 3 files changed, 184 insertions(+), 39 deletions(-)
 create mode 100644 app/services/projects/import_service.rb
 create mode 100644 spec/services/projects/import_service_spec.rb

diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
new file mode 100644
index 00000000000..7a9508ef085
--- /dev/null
+++ b/app/services/projects/import_service.rb
@@ -0,0 +1,71 @@
+module Projects
+  class ImportService < BaseService
+    include Gitlab::ShellAdapter
+
+    class Error < StandardError; end
+
+    ALLOWED_TYPES = [
+      'bitbucket',
+      'fogbugz',
+      'gitlab',
+      'github',
+      'google_code'
+    ]
+
+    def execute
+      if unknown_url?
+        # In this case, we only want to import issues, not a repository.
+        create_repository
+      else
+        import_repository
+      end
+
+      import_data
+
+      success
+    rescue Error => e
+      error(e.message)
+    end
+
+    private
+
+    def create_repository
+      unless project.create_repository
+        raise Error, 'The repository could not be created.'
+      end
+    end
+
+    def import_repository
+      begin
+        gitlab_shell.import_repository(project.path_with_namespace, project.import_url)
+      rescue Gitlab::Shell::Error => e
+        raise Error, e.message
+      end
+    end
+
+    def import_data
+      return unless has_importer?
+
+      unless importer.execute
+        raise Error, 'The remote data could not be imported.'
+      end
+
+      if project.import_type == 'bitbucket'
+        Gitlab::BitbucketImport::KeyDeleter.new(project).execute
+      end
+    end
+
+    def has_importer?
+      ALLOWED_TYPES.include?(project.import_type)
+    end
+
+    def importer
+      class_name = "Gitlab::#{project.import_type.camelize}Import::Importer"
+      class_name.constantize.new(project)
+    end
+
+    def unknown_url?
+      project.import_url == Project::UNKNOWN_IMPORT_URL
+    end
+  end
+end
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index d18c0706b30..e295a9ddd14 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -4,52 +4,20 @@ class RepositoryImportWorker
 
   sidekiq_options queue: :gitlab_shell
 
-  def perform(project_id)
-    project = Project.find(project_id)
+  attr_accessor :project, :current_user
 
-    if project.import_url == Project::UNKNOWN_IMPORT_URL
-      # In this case, we only want to import issues, not a repository.
-      unless project.create_repository
-        project.update(import_error: "The repository could not be created.")
-        project.import_fail
-        return
-      end
-    else
-      begin
-        gitlab_shell.import_repository(project.path_with_namespace, project.import_url)
-      rescue Gitlab::Shell::Error => e
-        project.update(import_error: e.message)
-        project.import_fail
-        return
-      end
-    end
+  def perform(project_id)
+    @project = Project.find(project_id)
+    @current_user = @project.creator
 
-    data_import_result =
-      case project.import_type
-      when 'github'
-        Gitlab::GithubImport::Importer.new(project).execute
-      when 'gitlab'
-        Gitlab::GitlabImport::Importer.new(project).execute
-      when 'bitbucket'
-        Gitlab::BitbucketImport::Importer.new(project).execute
-      when 'google_code'
-        Gitlab::GoogleCodeImport::Importer.new(project).execute
-      when 'fogbugz'
-        Gitlab::FogbugzImport::Importer.new(project).execute
-      else
-        true
-      end
+    result = Projects::ImportService.new(project, current_user).execute
 
-    unless data_import_result
-      project.update(import_error: "The remote issue data could not be imported.")
+    if result[:status] == :error
+      project.update(import_error: result[:message])
       project.import_fail
       return
     end
 
-    if project.import_type == 'bitbucket'
-      Gitlab::BitbucketImport::KeyDeleter.new(project).execute
-    end
-
     project.import_finish
   end
 end
diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb
new file mode 100644
index 00000000000..04f474c736c
--- /dev/null
+++ b/spec/services/projects/import_service_spec.rb
@@ -0,0 +1,106 @@
+require 'spec_helper'
+
+describe Projects::ImportService, services: true do
+  let!(:project) { create(:empty_project) }
+  let(:user) { project.creator }
+
+  subject { described_class.new(project, user) }
+
+  describe '#execute' do
+    context 'with unknown url' do
+      before do
+        project.import_url = Project::UNKNOWN_IMPORT_URL
+      end
+
+      it 'succeeds if repository is created successfully' do
+        expect(project).to receive(:create_repository).and_return(true)
+
+        result = subject.execute
+
+        expect(result[:status]).to eq :success
+      end
+
+      it 'fails if repository creation fails' do
+        expect(project).to receive(:create_repository).and_return(false)
+
+        result = subject.execute
+
+        expect(result[:status]).to eq :error
+        expect(result[:message]).to eq 'The repository could not be created.'
+      end
+    end
+
+    context 'with known url' do
+      before do
+        project.import_url = 'https://github.com/vim/vim.git'
+      end
+
+      it 'succeeds if repository import is successfully' do
+        expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).with(project.path_with_namespace, project.import_url).and_return(true)
+
+        result = subject.execute
+
+        expect(result[:status]).to eq :success
+      end
+
+      it 'fails if repository import fails' do
+        expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).with(project.path_with_namespace, project.import_url).and_raise(Gitlab::Shell::Error.new('Failed to import the repository'))
+
+        result = subject.execute
+
+        expect(result[:status]).to eq :error
+        expect(result[:message]).to eq 'Failed to import the repository'
+      end
+    end
+
+    context 'with valid importer' do
+      before do
+        stub_github_omniauth_provider
+
+        project.import_url = 'https://github.com/vim/vim.git'
+        project.import_type = 'github'
+
+        allow(project).to receive(:import_data).and_return(double.as_null_object)
+      end
+
+      it 'succeeds if importer succeeds' do
+        expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).with(project.path_with_namespace, project.import_url).and_return(true)
+        expect_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).and_return(true)
+
+        result = subject.execute
+
+        expect(result[:status]).to eq :success
+      end
+
+      it 'fails if importer fails' do
+        expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).with(project.path_with_namespace, project.import_url).and_return(true)
+        expect_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).and_return(false)
+
+        result = subject.execute
+
+        expect(result[:status]).to eq :error
+        expect(result[:message]).to eq 'The remote data could not be imported.'
+      end
+
+      it 'fails if importer raise an error' do
+        expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).with(project.path_with_namespace, project.import_url).and_return(true)
+        expect_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).and_raise(Projects::ImportService::Error.new('Github: failed to connect API'))
+
+        result = subject.execute
+
+        expect(result[:status]).to eq :error
+        expect(result[:message]).to eq 'Github: failed to connect API'
+      end
+    end
+
+    def stub_github_omniauth_provider
+      provider = OpenStruct.new(
+        name: 'github',
+        app_id: 'asd123',
+        app_secret: 'asd123'
+      )
+
+      Gitlab.config.omniauth.providers << provider
+    end
+  end
+end
-- 
GitLab