Commit 14435c99 authored by Dylan Griffith's avatar Dylan Griffith

Merge branch '341422-gitlab-migration-add-gitlab-version-check-for-project-migration' into 'master'

Resolve "GitLab Migration - Add GitLab version check for Project Migration"

See merge request gitlab-org/gitlab!71423
parents 1aff227c 44c33c0c
...@@ -22,13 +22,16 @@ class Import::BulkImportsController < ApplicationController ...@@ -22,13 +22,16 @@ class Import::BulkImportsController < ApplicationController
def status def status
respond_to do |format| respond_to do |format|
format.json do format.json do
data = importable_data data = ::BulkImports::GetImportableDataService.new(params, query_params, credentials).execute
pagination_headers.each do |header| pagination_headers.each do |header|
response.set_header(header, data.headers[header]) response.set_header(header, data[:response].headers[header])
end end
render json: { importable_data: serialized_data(data.parsed_response) } json_response = { importable_data: serialized_data(data[:response].parsed_response) }
json_response[:version_validation] = data[:version_validation]
render json: json_response
end end
format.html do format.html do
@source_url = session[url_key] @source_url = session[url_key]
...@@ -66,10 +69,6 @@ class Import::BulkImportsController < ApplicationController ...@@ -66,10 +69,6 @@ class Import::BulkImportsController < ApplicationController
@serializer ||= BaseSerializer.new(current_user: current_user) @serializer ||= BaseSerializer.new(current_user: current_user)
end end
def importable_data
client.get('groups', query_params)
end
# Default query string params used to fetch groups from GitLab source instance # Default query string params used to fetch groups from GitLab source instance
# #
# top_level_only: fetch only top level groups (subgroups are fetched during import itself) # top_level_only: fetch only top level groups (subgroups are fetched during import itself)
...@@ -85,15 +84,6 @@ class Import::BulkImportsController < ApplicationController ...@@ -85,15 +84,6 @@ class Import::BulkImportsController < ApplicationController
query_params query_params
end end
def client
@client ||= BulkImports::Clients::HTTP.new(
url: session[url_key],
token: session[access_token_key],
per_page: params[:per_page],
page: params[:page]
)
end
def configure_params def configure_params
params.permit(access_token_key, url_key) params.permit(access_token_key, url_key)
end end
......
...@@ -4,7 +4,8 @@ ...@@ -4,7 +4,8 @@
# projects to a GitLab instance. It associates the import with the responsible # projects to a GitLab instance. It associates the import with the responsible
# user. # user.
class BulkImport < ApplicationRecord class BulkImport < ApplicationRecord
MINIMUM_GITLAB_MAJOR_VERSION = 14 MIN_MAJOR_VERSION = 14
MIN_MINOR_VERSION_FOR_PROJECT = 4
belongs_to :user, optional: false belongs_to :user, optional: false
...@@ -34,6 +35,14 @@ class BulkImport < ApplicationRecord ...@@ -34,6 +35,14 @@ class BulkImport < ApplicationRecord
end end
end end
def source_version_info
Gitlab::VersionInfo.parse(source_version)
end
def self.min_gl_version_for_project_migration
Gitlab::VersionInfo.new(MIN_MAJOR_VERSION, MIN_MINOR_VERSION_FOR_PROJECT)
end
def self.all_human_statuses def self.all_human_statuses
state_machine.states.map(&:human_name) state_machine.states.map(&:human_name)
end end
......
...@@ -83,9 +83,9 @@ class BulkImports::Entity < ApplicationRecord ...@@ -83,9 +83,9 @@ class BulkImports::Entity < ApplicationRecord
def pipelines def pipelines
@pipelines ||= case source_type @pipelines ||= case source_type
when 'group_entity' when 'group_entity'
BulkImports::Groups::Stage.pipelines BulkImports::Groups::Stage.new(bulk_import).pipelines
when 'project_entity' when 'project_entity'
BulkImports::Projects::Stage.pipelines BulkImports::Projects::Stage.new(bulk_import).pipelines
end end
end end
......
...@@ -52,7 +52,11 @@ module BulkImports ...@@ -52,7 +52,11 @@ module BulkImports
def create_bulk_import def create_bulk_import
BulkImport.transaction do BulkImport.transaction do
bulk_import = BulkImport.create!(user: current_user, source_type: 'gitlab') bulk_import = BulkImport.create!(
user: current_user,
source_type: 'gitlab',
source_version: client.instance_version
)
bulk_import.create_configuration!(credentials.slice(:url, :access_token)) bulk_import.create_configuration!(credentials.slice(:url, :access_token))
params.each do |entity| params.each do |entity|
...@@ -68,5 +72,12 @@ module BulkImports ...@@ -68,5 +72,12 @@ module BulkImports
bulk_import bulk_import
end end
end end
def client
@client ||= BulkImports::Clients::HTTP.new(
url: @credentials[:url],
token: @credentials[:access_token]
)
end
end end
end end
# frozen_string_literal: true
module BulkImports
class GetImportableDataService
def initialize(params, query_params, credentials)
@params = params
@query_params = query_params
@credentials = credentials
end
def execute
{
version_validation: version_validation,
response: importables
}
end
private
def importables
client.get('groups', @query_params)
end
def version_validation
{
features: {
project_migration: {
available: client.compatible_for_project_migration?,
min_version: BulkImport.min_gl_version_for_project_migration.to_s
},
source_instance_version: client.instance_version.to_s
}
}
end
def client
@client ||= BulkImports::Clients::HTTP.new(
url: @credentials[:url],
token: @credentials[:access_token],
per_page: @params[:per_page],
page: @params[:page]
)
end
end
end
# frozen_string_literal: true
class AddSourceVersionToBulkImports < Gitlab::Database::Migration[1.0]
def change
add_column :bulk_imports, :source_version, :text # rubocop:disable Migration/AddLimitToTextColumns
end
end
# frozen_string_literal: true
class AddTextLimitToBulkImportsSourceVersion < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
add_text_limit :bulk_imports, :source_version, 63
end
def down
remove_text_limit :bulk_imports, :source_version
end
end
23be5444bb11f731e98edc9b6aad814d02fd0f3f6be9abdea9060898cc2b95f1
\ No newline at end of file
3482e5c12f1603cb67d24aee14f003345ef2a5c350c7dccafdea6554db04c4cc
\ No newline at end of file
...@@ -11216,7 +11216,9 @@ CREATE TABLE bulk_imports ( ...@@ -11216,7 +11216,9 @@ CREATE TABLE bulk_imports (
source_type smallint NOT NULL, source_type smallint NOT NULL,
status smallint NOT NULL, status smallint NOT NULL,
created_at timestamp with time zone NOT NULL, created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL updated_at timestamp with time zone NOT NULL,
source_version text,
CONSTRAINT check_ea4e58775a CHECK ((char_length(source_version) <= 63))
); );
CREATE SEQUENCE bulk_imports_id_seq CREATE SEQUENCE bulk_imports_id_seq
...@@ -20,6 +20,12 @@ RSpec.describe BulkImports::Groups::Stage do ...@@ -20,6 +20,12 @@ RSpec.describe BulkImports::Groups::Stage do
] ]
end end
subject do
bulk_import = build(:bulk_import)
described_class.new(bulk_import)
end
describe '#each' do describe '#each' do
it 'iterates over all pipelines with the stage number' do it 'iterates over all pipelines with the stage number' do
expect(subject.pipelines).to match_array(pipelines) expect(subject.pipelines).to match_array(pipelines)
......
...@@ -57,7 +57,7 @@ module BulkImports ...@@ -57,7 +57,7 @@ module BulkImports
response = client.execute('{ metadata { version } }') response = client.execute('{ metadata { version } }')
version = Gitlab::VersionInfo.parse(response.data.metadata.version) version = Gitlab::VersionInfo.parse(response.data.metadata.version)
if version.major < BulkImport::MINIMUM_GITLAB_MAJOR_VERSION if version.major < BulkImport::MIN_MAJOR_VERSION
raise ::BulkImports::Error.unsupported_gitlab_version raise ::BulkImports::Error.unsupported_gitlab_version
else else
@compatible_instance_version = true @compatible_instance_version = true
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
module BulkImports module BulkImports
module Clients module Clients
class HTTP class HTTP
include Gitlab::Utils::StrongMemoize
API_VERSION = 'v4' API_VERSION = 'v4'
DEFAULT_PAGE = 1 DEFAULT_PAGE = 1
DEFAULT_PER_PAGE = 30 DEFAULT_PER_PAGE = 30
...@@ -52,24 +54,32 @@ module BulkImports ...@@ -52,24 +54,32 @@ module BulkImports
Gitlab::Utils.append_path(api_url, resource) Gitlab::Utils.append_path(api_url, resource)
end end
def validate_instance_version! def instance_version
return if @compatible_instance_version strong_memoize(:instance_version) do
response = with_error_handling do response = with_error_handling do
Gitlab::HTTP.get(resource_url(:version), default_options) Gitlab::HTTP.get(resource_url(:version), default_options)
end end
version = Gitlab::VersionInfo.parse(response.parsed_response['version']) Gitlab::VersionInfo.parse(response.parsed_response['version'])
end
end
def compatible_for_project_migration?
instance_version >= BulkImport.min_gl_version_for_project_migration
end
if version.major < BulkImport::MINIMUM_GITLAB_MAJOR_VERSION private
def validate_instance_version!
return if @compatible_instance_version
if instance_version.major < BulkImport::MIN_MAJOR_VERSION
raise ::BulkImports::Error.unsupported_gitlab_version raise ::BulkImports::Error.unsupported_gitlab_version
else else
@compatible_instance_version = true @compatible_instance_version = true
end end
end end
private
# rubocop:disable GitlabSecurity/PublicSend # rubocop:disable GitlabSecurity/PublicSend
def request(method, resource, options = {}, &block) def request(method, resource, options = {}, &block)
validate_instance_version! validate_instance_version!
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
module BulkImports module BulkImports
class Error < StandardError class Error < StandardError
def self.unsupported_gitlab_version def self.unsupported_gitlab_version
self.new("Unsupported GitLab Version. Minimum Supported Gitlab Version #{BulkImport::MINIMUM_GITLAB_MAJOR_VERSION}.") self.new("Unsupported GitLab Version. Minimum Supported Gitlab Version #{BulkImport::MIN_MAJOR_VERSION}.")
end end
end end
end end
...@@ -47,7 +47,7 @@ module BulkImports ...@@ -47,7 +47,7 @@ module BulkImports
end end
def project_entities_pipeline def project_entities_pipeline
if ::Feature.enabled?(:bulk_import_projects, default_enabled: :yaml) if project_pipeline_available? && ::Feature.enabled?(:bulk_import_projects, default_enabled: :yaml)
{ {
project_entities: { project_entities: {
pipeline: BulkImports::Groups::Pipelines::ProjectEntitiesPipeline, pipeline: BulkImports::Groups::Pipelines::ProjectEntitiesPipeline,
...@@ -58,6 +58,10 @@ module BulkImports ...@@ -58,6 +58,10 @@ module BulkImports
{} {}
end end
end end
def project_pipeline_available?
@bulk_import.source_version_info >= BulkImport.min_gl_version_for_project_migration
end
end end
end end
end end
......
...@@ -2,8 +2,10 @@ ...@@ -2,8 +2,10 @@
module BulkImports module BulkImports
class Stage class Stage
def self.pipelines def initialize(bulk_import)
new.pipelines raise(ArgumentError, 'Expected an argument of type ::BulkImport') unless bulk_import.is_a?(::BulkImport)
@bulk_import = bulk_import
end end
def pipelines def pipelines
......
...@@ -51,62 +51,87 @@ RSpec.describe Import::BulkImportsController do ...@@ -51,62 +51,87 @@ RSpec.describe Import::BulkImportsController do
end end
describe 'GET status' do describe 'GET status' do
def get_status(params_override = {})
params = { page: 1, per_page: 20, filter: '' }.merge(params_override)
get :status,
params: params,
format: :json,
session: {
bulk_import_gitlab_url: 'https://gitlab.example.com',
bulk_import_gitlab_access_token: 'demo-pat'
}
end
include_context 'bulk imports requests context', 'https://gitlab.example.com'
let(:client) { BulkImports::Clients::HTTP.new(url: 'http://gitlab.example', token: 'token') } let(:client) { BulkImports::Clients::HTTP.new(url: 'http://gitlab.example', token: 'token') }
let(:version) { "#{BulkImport::MIN_MAJOR_VERSION}.#{BulkImport::MIN_MINOR_VERSION_FOR_PROJECT}.0" }
let(:version_response) { double(code: 200, success?: true, parsed_response: { 'version' => version }) }
describe 'serialized group data' do describe 'serialized group data' do
let(:client_response) do let(:expected_response) do
double( double(
parsed_response: [ parsed_response: [
{ 'id' => 1, 'full_name' => 'group1', 'full_path' => 'full/path/group1', 'web_url' => 'http://demo.host/full/path/group1' }, {
{ 'id' => 2, 'full_name' => 'group2', 'full_path' => 'full/path/group2', 'web_url' => 'http://demo.host/full/path/group1' } "full_name" => "Stub",
"full_path" => "stub-group",
"id" => 2595438,
"web_url" => "https://gitlab.com/groups/auto-breakfast"
}
], ],
headers: { headers: {
'x-next-page' => '2', 'x-next-page' => '2',
'x-page' => '1', 'x-page' => '1',
'x-per-page' => '20', 'x-per-page' => '20',
'x-total' => '37', 'x-total' => '42',
'x-total-pages' => '2' 'x-total-pages' => '2'
} }
) )
end end
let(:client_params) do it 'returns serialized group data' do
{ get_status
top_level_only: true,
min_access_level: Gitlab::Access::OWNER version_validation = {
"features" => {
"project_migration" => {
"available" => true,
"min_version" => BulkImport.min_gl_version_for_project_migration.to_s
},
"source_instance_version" => version
}
} }
end
before do expect(json_response).to include("importable_data" => expected_response.parsed_response, "version_validation" => hash_including(version_validation))
allow(controller).to receive(:client).and_return(client)
allow(client).to receive(:get).with('groups', client_params).and_return(client_response)
end end
it 'returns serialized group data' do it 'forwards pagination headers' do
get :status, format: :json get_status
expect(json_response).to eq({ importable_data: client_response.parsed_response }.as_json) expect(response.headers['x-per-page']).to eq expected_response.headers['x-per-page']
expect(response.headers['x-page']).to eq expected_response.headers['x-page']
expect(response.headers['x-next-page']).to eq expected_response.headers['x-next-page']
expect(response.headers['x-prev-page']).to eq expected_response.headers['x-prev-page']
expect(response.headers['x-total']).to eq expected_response.headers['x-total']
expect(response.headers['x-total-pages']).to eq expected_response.headers['x-total-pages']
end end
it 'forwards pagination headers' do context 'when filtering' do
get :status, format: :json let_it_be(:filter) { 'test' }
expect(response.headers['x-per-page']).to eq client_response.headers['x-per-page'] let(:client_params) do
expect(response.headers['x-page']).to eq client_response.headers['x-page'] {
expect(response.headers['x-next-page']).to eq client_response.headers['x-next-page'] top_level_only: true,
expect(response.headers['x-prev-page']).to eq client_response.headers['x-prev-page'] min_access_level: Gitlab::Access::OWNER,
expect(response.headers['x-total']).to eq client_response.headers['x-total'] search: filter
expect(response.headers['x-total-pages']).to eq client_response.headers['x-total-pages'] }
end end
context 'when filtering' do
it 'returns filtered result' do it 'returns filtered result' do
filter = 'test' get_status(filter: filter)
search_params = client_params.merge(search: filter)
expect(client).to receive(:get).with('groups', search_params).and_return(client_response)
get :status, format: :json, params: { filter: filter } expect(json_response['importable_data'].first['full_name']).to eq('Test')
end end
end end
end end
...@@ -148,18 +173,19 @@ RSpec.describe Import::BulkImportsController do ...@@ -148,18 +173,19 @@ RSpec.describe Import::BulkImportsController do
context 'when connection error occurs' do context 'when connection error occurs' do
before do before do
allow(controller).to receive(:client).and_return(client) allow_next_instance_of(BulkImports::Clients::HTTP) do |instance|
allow(client).to receive(:get).and_raise(BulkImports::Error) allow(instance).to receive(:get).and_raise(BulkImports::Error)
end
end end
it 'returns 422' do it 'returns 422' do
get :status, format: :json get_status
expect(response).to have_gitlab_http_status(:unprocessable_entity) expect(response).to have_gitlab_http_status(:unprocessable_entity)
end end
it 'clears session' do it 'clears session' do
get :status, format: :json get_status
expect(session[:gitlab_url]).to be_nil expect(session[:gitlab_url]).to be_nil
expect(session[:gitlab_access_token]).to be_nil expect(session[:gitlab_access_token]).to be_nil
......
...@@ -4,6 +4,7 @@ FactoryBot.define do ...@@ -4,6 +4,7 @@ FactoryBot.define do
factory :bulk_import, class: 'BulkImport' do factory :bulk_import, class: 'BulkImport' do
user user
source_type { :gitlab } source_type { :gitlab }
source_version { BulkImport.min_gl_version_for_project_migration.to_s }
trait :created do trait :created do
status { 0 } status { 0 }
......
...@@ -19,34 +19,12 @@ RSpec.describe 'Import/Export - Connect to another instance', :js do ...@@ -19,34 +19,12 @@ RSpec.describe 'Import/Export - Connect to another instance', :js do
end end
context 'when the user provides valid credentials' do context 'when the user provides valid credentials' do
it 'successfully connects to remote instance' do
source_url = 'https://gitlab.com' source_url = 'https://gitlab.com'
include_context 'bulk imports requests context', source_url
it 'successfully connects to remote instance' do
pat = 'demo-pat' pat = 'demo-pat'
stub_path = 'stub-group'
total = 37
stub_request(:get, "%{url}/api/v4/groups?page=1&per_page=20&top_level_only=true&min_access_level=50&search=" % { url: source_url }).to_return(
body: [{
id: 2595438,
web_url: 'https://gitlab.com/groups/auto-breakfast',
name: 'Stub',
path: stub_path,
full_name: 'Stub',
full_path: stub_path
}].to_json,
headers: {
'Content-Type' => 'application/json',
'X-Next-Page' => 2,
'X-Page' => 1,
'X-Per-Page' => 20,
'X-Total' => total,
'X-Total-Pages' => 2
}
)
allow_next_instance_of(BulkImports::Clients::HTTP) do |client|
allow(client).to receive(:validate_instance_version!).and_return(true)
end
expect(page).to have_content 'Import groups from another instance of GitLab' expect(page).to have_content 'Import groups from another instance of GitLab'
expect(page).to have_content 'Not all related objects are migrated' expect(page).to have_content 'Not all related objects are migrated'
...@@ -56,8 +34,8 @@ RSpec.describe 'Import/Export - Connect to another instance', :js do ...@@ -56,8 +34,8 @@ RSpec.describe 'Import/Export - Connect to another instance', :js do
click_on 'Connect instance' click_on 'Connect instance'
expect(page).to have_content 'Showing 1-1 of %{total} groups from %{url}' % { url: source_url, total: total } expect(page).to have_content 'Showing 1-1 of 42 groups from %{url}' % { url: source_url }
expect(page).to have_content stub_path expect(page).to have_content 'stub-group'
visit '/' visit '/'
......
...@@ -34,7 +34,7 @@ RSpec.describe BulkImports::Clients::Graphql do ...@@ -34,7 +34,7 @@ RSpec.describe BulkImports::Clients::Graphql do
let(:version) { '13.0.0' } let(:version) { '13.0.0' }
it 'raises an error' do it 'raises an error' do
expect { subject.execute('test') }.to raise_error(::BulkImports::Error, "Unsupported GitLab Version. Minimum Supported Gitlab Version #{BulkImport::MINIMUM_GITLAB_MAJOR_VERSION}.") expect { subject.execute('test') }.to raise_error(::BulkImports::Error, "Unsupported GitLab Version. Minimum Supported Gitlab Version #{BulkImport::MIN_MAJOR_VERSION}.")
end end
end end
end end
......
...@@ -8,7 +8,7 @@ RSpec.describe BulkImports::Clients::HTTP do ...@@ -8,7 +8,7 @@ RSpec.describe BulkImports::Clients::HTTP do
let(:url) { 'http://gitlab.example' } let(:url) { 'http://gitlab.example' }
let(:token) { 'token' } let(:token) { 'token' }
let(:resource) { 'resource' } let(:resource) { 'resource' }
let(:version) { "#{BulkImport::MINIMUM_GITLAB_MAJOR_VERSION}.0.0" } let(:version) { "#{BulkImport::MIN_MAJOR_VERSION}.0.0" }
let(:response_double) { double(code: 200, success?: true, parsed_response: {}) } let(:response_double) { double(code: 200, success?: true, parsed_response: {}) }
let(:version_response) { double(code: 200, success?: true, parsed_response: { 'version' => version }) } let(:version_response) { double(code: 200, success?: true, parsed_response: { 'version' => version }) }
...@@ -176,6 +176,28 @@ RSpec.describe BulkImports::Clients::HTTP do ...@@ -176,6 +176,28 @@ RSpec.describe BulkImports::Clients::HTTP do
end end
end end
describe '#instance_version' do
it 'returns version as an instance of Gitlab::VersionInfo' do
expect(subject.instance_version).to eq(Gitlab::VersionInfo.parse(version))
end
end
describe '#compatible_for_project_migration?' do
context 'when instance version is lower the the expected minimum' do
it 'returns false' do
expect(subject.compatible_for_project_migration?).to be false
end
end
context 'when instance version is at least the expected minimum' do
let(:version) { "14.4.4" }
it 'returns true' do
expect(subject.compatible_for_project_migration?).to be true
end
end
end
context 'when source instance is incompatible' do context 'when source instance is incompatible' do
let(:version) { '13.0.0' } let(:version) { '13.0.0' }
...@@ -183,7 +205,7 @@ RSpec.describe BulkImports::Clients::HTTP do ...@@ -183,7 +205,7 @@ RSpec.describe BulkImports::Clients::HTTP do
expect { subject.get(resource) } expect { subject.get(resource) }
.to raise_error( .to raise_error(
::BulkImports::Error, ::BulkImports::Error,
"Unsupported GitLab Version. Minimum Supported Gitlab Version #{BulkImport::MINIMUM_GITLAB_MAJOR_VERSION}." "Unsupported GitLab Version. Minimum Supported Gitlab Version #{BulkImport::MIN_MAJOR_VERSION}."
) )
end end
end end
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe BulkImports::Groups::Stage do RSpec.describe BulkImports::Groups::Stage do
let(:bulk_import) { build(:bulk_import) }
let(:pipelines) do let(:pipelines) do
[ [
[0, BulkImports::Groups::Pipelines::GroupPipeline], [0, BulkImports::Groups::Pipelines::GroupPipeline],
...@@ -16,23 +18,27 @@ RSpec.describe BulkImports::Groups::Stage do ...@@ -16,23 +18,27 @@ RSpec.describe BulkImports::Groups::Stage do
] ]
end end
it 'raises error when initialized without a BulkImport' do
expect { described_class.new({}) }.to raise_error(ArgumentError, 'Expected an argument of type ::BulkImport')
end
describe '.pipelines' do describe '.pipelines' do
it 'list all the pipelines with their stage number, ordered by stage' do it 'list all the pipelines with their stage number, ordered by stage' do
expect(described_class.pipelines & pipelines).to eq(pipelines) expect(described_class.new(bulk_import).pipelines & pipelines).to eq(pipelines)
expect(described_class.pipelines.last.last).to eq(BulkImports::Common::Pipelines::EntityFinisher) expect(described_class.new(bulk_import).pipelines.last.last).to eq(BulkImports::Common::Pipelines::EntityFinisher)
end end
it 'includes project entities pipeline' do it 'includes project entities pipeline' do
stub_feature_flags(bulk_import_projects: true) stub_feature_flags(bulk_import_projects: true)
expect(described_class.pipelines).to include([1, BulkImports::Groups::Pipelines::ProjectEntitiesPipeline]) expect(described_class.new(bulk_import).pipelines).to include([1, BulkImports::Groups::Pipelines::ProjectEntitiesPipeline])
end end
context 'when bulk_import_projects feature flag is disabled' do context 'when bulk_import_projects feature flag is disabled' do
it 'does not include project entities pipeline' do it 'does not include project entities pipeline' do
stub_feature_flags(bulk_import_projects: false) stub_feature_flags(bulk_import_projects: false)
expect(described_class.pipelines.flatten).not_to include(BulkImports::Groups::Pipelines::ProjectEntitiesPipeline) expect(described_class.new(bulk_import).pipelines.flatten).not_to include(BulkImports::Groups::Pipelines::ProjectEntitiesPipeline)
end end
end end
end end
......
...@@ -13,9 +13,15 @@ RSpec.describe BulkImports::Projects::Stage do ...@@ -13,9 +13,15 @@ RSpec.describe BulkImports::Projects::Stage do
] ]
end end
describe '.pipelines' do subject do
bulk_import = build(:bulk_import)
described_class.new(bulk_import)
end
describe '#pipelines' do
it 'list all the pipelines with their stage number, ordered by stage' do it 'list all the pipelines with their stage number, ordered by stage' do
expect(described_class.pipelines).to eq(pipelines) expect(subject.pipelines).to eq(pipelines)
end end
end end
end end
...@@ -21,4 +21,18 @@ RSpec.describe BulkImport, type: :model do ...@@ -21,4 +21,18 @@ RSpec.describe BulkImport, type: :model do
expect(described_class.all_human_statuses).to contain_exactly('created', 'started', 'finished', 'failed') expect(described_class.all_human_statuses).to contain_exactly('created', 'started', 'finished', 'failed')
end end
end end
describe '.min_gl_version_for_project' do
it { expect(described_class.min_gl_version_for_project_migration).to be_a(Gitlab::VersionInfo) }
it { expect(described_class.min_gl_version_for_project_migration.to_s).to eq('14.4.0') }
end
describe '#source_version_info' do
it 'returns source_version as Gitlab::VersionInfo' do
bulk_import = build(:bulk_import, source_version: '9.13.2')
expect(bulk_import.source_version_info).to be_a(Gitlab::VersionInfo)
expect(bulk_import.source_version_info.to_s).to eq(bulk_import.source_version)
end
end
end end
...@@ -179,7 +179,7 @@ RSpec.describe BulkImports::Entity, type: :model do ...@@ -179,7 +179,7 @@ RSpec.describe BulkImports::Entity, type: :model do
entity = create(:bulk_import_entity, :group_entity) entity = create(:bulk_import_entity, :group_entity)
entity.create_pipeline_trackers! entity.create_pipeline_trackers!
expect(entity.trackers.count).to eq(BulkImports::Groups::Stage.pipelines.count) expect(entity.trackers.count).to eq(BulkImports::Groups::Stage.new(entity.bulk_import).pipelines.count)
expect(entity.trackers.map(&:pipeline_name)).to include(BulkImports::Groups::Pipelines::GroupPipeline.to_s) expect(entity.trackers.map(&:pipeline_name)).to include(BulkImports::Groups::Pipelines::GroupPipeline.to_s)
end end
end end
...@@ -189,7 +189,7 @@ RSpec.describe BulkImports::Entity, type: :model do ...@@ -189,7 +189,7 @@ RSpec.describe BulkImports::Entity, type: :model do
entity = create(:bulk_import_entity, :project_entity) entity = create(:bulk_import_entity, :project_entity)
entity.create_pipeline_trackers! entity.create_pipeline_trackers!
expect(entity.trackers.count).to eq(BulkImports::Projects::Stage.pipelines.count) expect(entity.trackers.count).to eq(BulkImports::Projects::Stage.new(entity.bulk_import).pipelines.count)
expect(entity.trackers.map(&:pipeline_name)).to include(BulkImports::Projects::Pipelines::ProjectPipeline.to_s) expect(entity.trackers.map(&:pipeline_name)).to include(BulkImports::Projects::Pipelines::ProjectPipeline.to_s)
end end
end end
......
...@@ -66,7 +66,8 @@ RSpec.describe BulkImports::Tracker, type: :model do ...@@ -66,7 +66,8 @@ RSpec.describe BulkImports::Tracker, type: :model do
describe '#pipeline_class' do describe '#pipeline_class' do
it 'returns the pipeline class' do it 'returns the pipeline class' do
pipeline_class = BulkImports::Groups::Stage.pipelines.first[1] bulk_import = create(:bulk_import)
pipeline_class = BulkImports::Groups::Stage.new(bulk_import).pipelines.first[1]
tracker = create(:bulk_import_tracker, pipeline_name: pipeline_class) tracker = create(:bulk_import_tracker, pipeline_name: pipeline_class)
expect(tracker.pipeline_class).to eq(pipeline_class) expect(tracker.pipeline_class).to eq(pipeline_class)
......
...@@ -21,6 +21,15 @@ RSpec.describe API::BulkImports do ...@@ -21,6 +21,15 @@ RSpec.describe API::BulkImports do
end end
describe 'POST /bulk_imports' do describe 'POST /bulk_imports' do
before do
allow_next_instance_of(BulkImports::Clients::HTTP) do |instance|
allow(instance)
.to receive(:instance_version)
.and_return(
Gitlab::VersionInfo.new(::BulkImport::MIN_MAJOR_VERSION, ::BulkImport::MIN_MINOR_VERSION_FOR_PROJECT))
end
end
it 'starts a new migration' do it 'starts a new migration' do
post api('/bulk_imports', user), params: { post api('/bulk_imports', user), params: {
configuration: { configuration: {
......
...@@ -31,8 +31,25 @@ RSpec.describe BulkImports::CreateService do ...@@ -31,8 +31,25 @@ RSpec.describe BulkImports::CreateService do
subject { described_class.new(user, params, credentials) } subject { described_class.new(user, params, credentials) }
describe '#execute' do describe '#execute' do
let_it_be(:source_version) do
Gitlab::VersionInfo.new(::BulkImport::MIN_MAJOR_VERSION,
::BulkImport::MIN_MINOR_VERSION_FOR_PROJECT)
end
before do
allow_next_instance_of(BulkImports::Clients::HTTP) do |instance|
allow(instance).to receive(:instance_version).and_return(source_version)
end
end
it 'creates bulk import' do it 'creates bulk import' do
expect { subject.execute }.to change { BulkImport.count }.by(1) expect { subject.execute }.to change { BulkImport.count }.by(1)
last_bulk_import = BulkImport.last
expect(last_bulk_import.user).to eq(user)
expect(last_bulk_import.source_version).to eq(source_version.to_s)
expect(last_bulk_import.user).to eq(user)
end end
it 'creates bulk import entities' do it 'creates bulk import entities' do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe BulkImports::GetImportableDataService do
describe '#execute' do
include_context 'bulk imports requests context', 'https://gitlab.example.com'
let_it_be(:params) { { per_page: 20, page: 1 } }
let_it_be(:query_params) { { top_level_only: true, min_access_level: 50, search: '' } }
let_it_be(:credentials) { { url: 'https://gitlab.example.com', access_token: 'demo-pat' } }
let_it_be(:expected_version_validation) do
{
features: {
project_migration: {
available: true,
min_version: BulkImport.min_gl_version_for_project_migration.to_s
},
'source_instance_version': BulkImport.min_gl_version_for_project_migration.to_s
}
}
end
let_it_be(:expected_parsed_response) do
[
{
'id' => 2595438,
'web_url' => 'https://gitlab.com/groups/auto-breakfast',
'name' => 'Stub',
'path' => 'stub-group',
'full_name' => 'Stub',
'full_path' => 'stub-group'
}
]
end
subject do
described_class.new(params, query_params, credentials).execute
end
it 'returns version_validation and a response' do
expect(subject[:version_validation]).to eq(expected_version_validation)
expect(subject[:response].parsed_response).to eq(expected_parsed_response)
end
end
end
# frozen_string_literal: true
RSpec.shared_context 'bulk imports requests context' do |url|
let(:page_response_headers) do
{
'Content-Type' => 'application/json',
'X-Next-Page' => 2,
'X-Page' => 1,
'X-Per-Page' => 20,
'X-Total' => 42,
'X-Total-Pages' => 2
}
end
let(:request_headers) { { 'Authorization' => 'Bearer demo-pat', 'Content-Type' => 'application/json' } }
before do
stub_request(:get, "#{url}/api/v4/version")
.with(headers: request_headers)
.to_return(
status: 200,
body: { version: ::BulkImport.min_gl_version_for_project_migration.to_s }.to_json,
headers: { 'Content-Type' => 'application/json' })
stub_request(:get, "https://gitlab.example.com/api/v4/groups?min_access_level=50&page=1&per_page=20&search=test&top_level_only=true")
.with(headers: request_headers)
.to_return(status: 200,
body: [{
id: 2595440,
web_url: 'https://gitlab.com/groups/test',
name: 'Test',
path: 'stub-test-group',
full_name: 'Test',
full_path: 'stub-test-group'
}].to_json,
headers: page_response_headers
)
stub_request(:get, "%{url}/api/v4/groups?page=1&per_page=20&top_level_only=true&min_access_level=50&search=" % { url: url })
.to_return(
body: [{
id: 2595438,
web_url: 'https://gitlab.com/groups/auto-breakfast',
name: 'Stub',
path: 'stub-group',
full_name: 'Stub',
full_path: 'stub-group'
}].to_json,
headers: page_response_headers
)
end
end
...@@ -84,7 +84,7 @@ RSpec.describe BulkImportWorker do ...@@ -84,7 +84,7 @@ RSpec.describe BulkImportWorker do
expect { subject.perform(bulk_import.id) } expect { subject.perform(bulk_import.id) }
.to change(BulkImports::Tracker, :count) .to change(BulkImports::Tracker, :count)
.by(BulkImports::Groups::Stage.pipelines.size * 2) .by(BulkImports::Groups::Stage.new(bulk_import).pipelines.size * 2)
expect(entity_1.trackers).not_to be_empty expect(entity_1.trackers).not_to be_empty
expect(entity_2.trackers).not_to be_empty expect(entity_2.trackers).not_to be_empty
......
...@@ -22,10 +22,11 @@ RSpec.describe BulkImports::PipelineWorker do ...@@ -22,10 +22,11 @@ RSpec.describe BulkImports::PipelineWorker do
before do before do
stub_const('FakePipeline', pipeline_class) stub_const('FakePipeline', pipeline_class)
allow(BulkImports::Groups::Stage) allow_next_instance_of(BulkImports::Groups::Stage) do |instance|
.to receive(:pipelines) allow(instance).to receive(:pipelines)
.and_return([[0, pipeline_class]]) .and_return([[0, pipeline_class]])
end end
end
shared_examples 'successfully runs the pipeline' do shared_examples 'successfully runs the pipeline' do
it 'runs the given pipeline successfully' do it 'runs the given pipeline successfully' do
...@@ -206,10 +207,11 @@ RSpec.describe BulkImports::PipelineWorker do ...@@ -206,10 +207,11 @@ RSpec.describe BulkImports::PipelineWorker do
before do before do
stub_const('NdjsonPipeline', ndjson_pipeline) stub_const('NdjsonPipeline', ndjson_pipeline)
allow(BulkImports::Groups::Stage) allow_next_instance_of(BulkImports::Groups::Stage) do |instance|
.to receive(:pipelines) allow(instance).to receive(:pipelines)
.and_return([[0, ndjson_pipeline]]) .and_return([[0, ndjson_pipeline]])
end end
end
it 'runs the pipeline successfully' do it 'runs the pipeline successfully' do
allow_next_instance_of(BulkImports::ExportStatus) do |status| allow_next_instance_of(BulkImports::ExportStatus) do |status|
......
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