Commit 1d291312 authored by Illya Klymov's avatar Illya Klymov Committed by David O'Regan

Remove Google Code importer

Remove relevant backend parts and tests
parent 19b26b12
import $ from 'jquery';
import { escape } from 'lodash';
import { __, sprintf } from './locale';
import axios from './lib/utils/axios_utils';
import { deprecatedCreateFlash as flash } from './flash';
import { parseBoolean, spriteIcon } from './lib/utils/common_utils';
class ImporterStatus {
constructor({ jobsUrl, importUrl, ciCdOnly }) {
this.jobsUrl = jobsUrl;
this.importUrl = importUrl;
this.ciCdOnly = ciCdOnly;
this.initStatusPage();
this.setAutoUpdate();
}
initStatusPage() {
$('.js-add-to-import')
.off('click')
.on('click', this.addToImport.bind(this));
$('.js-import-all')
.off('click')
.on('click', function onClickImportAll() {
const $btn = $(this);
$btn.disable().addClass('is-loading');
return $('.js-add-to-import').each(function triggerAddImport() {
return $(this).trigger('click');
});
});
}
addToImport(event) {
const $btn = $(event.currentTarget);
const $tr = $btn.closest('tr');
const $targetField = $tr.find('.import-target');
const $namespaceInput = $targetField.find('.js-select-namespace option:selected');
const repoData = $tr.data();
const id = repoData.id || $tr.attr('id').replace('repo_', '');
let targetNamespace;
let newName;
if ($namespaceInput.length > 0) {
targetNamespace = $namespaceInput[0].innerHTML;
newName = $targetField.find('#path').prop('value');
$targetField.empty().append(`${targetNamespace}/${newName}`);
}
$btn.disable().addClass('is-loading');
this.id = id;
let attributes = {
repo_id: id,
target_namespace: targetNamespace,
new_name: newName,
ci_cd_only: this.ciCdOnly,
};
if (repoData) {
attributes = Object.assign(repoData, attributes);
}
return axios
.post(this.importUrl, attributes)
.then(({ data }) => {
const job = $tr;
job.attr('id', `project_${data.id}`);
job.find('.import-target').html(`<a href="${data.full_path}">${data.full_path}</a>`);
$('table.import-jobs tbody').prepend(job);
job.addClass('table-active');
const connectingVerb = this.ciCdOnly ? __('connecting') : __('importing');
job.find('.import-actions').html(
sprintf(
escape(__('%{loadingIcon} Started')),
{
loadingIcon: `<i class="fa fa-spinner fa-spin" aria-label="${escape(
connectingVerb,
)}"></i>`,
},
false,
),
);
})
.catch(error => {
let details = error;
const $statusField = $tr.find('.job-status');
$statusField.text(__('Failed'));
if (error.response && error.response.data && error.response.data.errors) {
details = error.response.data.errors;
}
flash(sprintf(__('An error occurred while importing project: %{details}'), { details }));
});
}
autoUpdate() {
return axios.get(this.jobsUrl).then(({ data = [] }) => {
data.forEach(job => {
const jobItem = $(`#project_${job.id}`);
const statusField = jobItem.find('.job-status');
const spinner = '<i class="fa fa-spinner fa-spin"></i>';
switch (job.import_status) {
case 'finished':
jobItem.removeClass('table-active').addClass('table-success');
statusField.html(`<span>${spriteIcon('check', 's16')} ${__('Done')}</span>`);
break;
case 'scheduled':
statusField.html(`${spinner} ${__('Scheduled')}`);
break;
case 'started':
statusField.html(`${spinner} ${__('Started')}`);
break;
case 'failed':
statusField.html(__('Failed'));
break;
default:
statusField.html(job.import_status);
break;
}
});
});
}
setAutoUpdate() {
setInterval(this.autoUpdate.bind(this), 4000);
}
}
// eslint-disable-next-line consistent-return
function initImporterStatus() {
const importerStatus = document.querySelector('.js-importer-status');
if (importerStatus) {
const data = importerStatus.dataset;
return new ImporterStatus({
jobsUrl: data.jobsImportPath,
importUrl: data.importPath,
ciCdOnly: parseBoolean(data.ciCdOnly),
});
}
}
export { initImporterStatus as default, ImporterStatus };
...@@ -23,7 +23,6 @@ import { getLocationHash, visitUrl } from './lib/utils/url_utility'; ...@@ -23,7 +23,6 @@ import { getLocationHash, visitUrl } from './lib/utils/url_utility';
// everything else // everything else
import { deprecatedCreateFlash as Flash, removeFlashClickListener } from './flash'; import { deprecatedCreateFlash as Flash, removeFlashClickListener } from './flash';
import initTodoToggle from './header'; import initTodoToggle from './header';
import initImporterStatus from './importer_status';
import initLayoutNav from './layout_nav'; import initLayoutNav from './layout_nav';
import initAlertHandler from './alert_handler'; import initAlertHandler from './alert_handler';
import './feature_highlight/feature_highlight_options'; import './feature_highlight/feature_highlight_options';
...@@ -107,7 +106,6 @@ function deferredInitialisation() { ...@@ -107,7 +106,6 @@ function deferredInitialisation() {
const $body = $('body'); const $body = $('body');
initBreadcrumbs(); initBreadcrumbs();
initImporterStatus();
initTodoToggle(); initTodoToggle();
initLogoAnimation(); initLogoAnimation();
initUsagePingConsent(); initUsagePingConsent();
......
...@@ -39,15 +39,3 @@ ...@@ -39,15 +39,3 @@
.import-projects-loading-icon { .import-projects-loading-icon {
margin-top: $gl-padding-32; margin-top: $gl-padding-32;
} }
.btn-import {
.loading-icon {
display: none;
}
&.is-loading {
.loading-icon {
display: inline-block;
}
}
}
...@@ -61,8 +61,7 @@ class ApplicationController < ActionController::Base ...@@ -61,8 +61,7 @@ class ApplicationController < ActionController::Base
:gitea_import_enabled?, :github_import_configured?, :gitea_import_enabled?, :github_import_configured?,
:gitlab_import_enabled?, :gitlab_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?,
:bitbucket_import_enabled?, :bitbucket_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?,
:bitbucket_server_import_enabled?, :bitbucket_server_import_enabled?, :fogbugz_import_enabled?,
:google_code_import_enabled?, :fogbugz_import_enabled?,
:git_import_enabled?, :gitlab_project_import_enabled?, :git_import_enabled?, :gitlab_project_import_enabled?,
:manifest_import_enabled?, :phabricator_import_enabled? :manifest_import_enabled?, :phabricator_import_enabled?
...@@ -434,10 +433,6 @@ class ApplicationController < ActionController::Base ...@@ -434,10 +433,6 @@ class ApplicationController < ActionController::Base
Gitlab::Auth::OAuth::Provider.enabled?(:bitbucket) Gitlab::Auth::OAuth::Provider.enabled?(:bitbucket)
end end
def google_code_import_enabled?
Gitlab::CurrentSettings.import_sources.include?('google_code')
end
def fogbugz_import_enabled? def fogbugz_import_enabled?
Gitlab::CurrentSettings.import_sources.include?('fogbugz') Gitlab::CurrentSettings.import_sources.include?('fogbugz')
end end
......
# frozen_string_literal: true
class Import::GoogleCodeController < Import::BaseController
before_action :verify_google_code_import_enabled
before_action :user_map, only: [:new_user_map, :create_user_map]
def new
end
def callback
dump_file = params[:dump_file]
unless dump_file.respond_to?(:read)
return redirect_back_or_default(options: { alert: _("You need to upload a Google Takeout archive.") })
end
begin
dump = Gitlab::Json.parse(dump_file.read)
rescue
return redirect_back_or_default(options: { alert: _("The uploaded file is not a valid Google Takeout archive.") })
end
client = Gitlab::GoogleCodeImport::Client.new(dump)
unless client.valid?
return redirect_back_or_default(options: { alert: _("The uploaded file is not a valid Google Takeout archive.") })
end
session[:google_code_dump] = dump
if params[:create_user_map] == "1"
redirect_to new_user_map_import_google_code_path
else
redirect_to status_import_google_code_path
end
end
def new_user_map
end
def create_user_map
user_map_json = params[:user_map]
user_map_json = "{}" if user_map_json.blank?
begin
user_map = Gitlab::Json.parse(user_map_json)
rescue
flash.now[:alert] = _("The entered user map is not a valid JSON user map.")
return render "new_user_map"
end
unless user_map.is_a?(Hash) && user_map.all? { |k, v| k.is_a?(String) && v.is_a?(String) }
flash.now[:alert] = _("The entered user map is not a valid JSON user map.")
return render "new_user_map"
end
# This is the default, so let's not save it into the database.
user_map.reject! do |key, value|
value == Gitlab::GoogleCodeImport::Client.mask_email(key)
end
session[:google_code_user_map] = user_map
flash[:notice] = _("The user map has been saved. Continue by selecting the projects you want to import.")
redirect_to status_import_google_code_path
end
# rubocop: disable CodeReuse/ActiveRecord
def status
unless client.valid?
return redirect_to new_import_google_code_path
end
@repos = client.repos
@incompatible_repos = client.incompatible_repos
@already_added_projects = find_already_added_projects('google_code')
already_added_projects_names = @already_added_projects.pluck(:import_source)
@repos.reject! { |repo| already_added_projects_names.include? repo.name }
end
# rubocop: enable CodeReuse/ActiveRecord
def jobs
render json: find_jobs('google_code')
end
def create
repo = client.repo(params[:repo_id])
user_map = session[:google_code_user_map]
project = Gitlab::GoogleCodeImport::ProjectCreator.new(repo, current_user.namespace, current_user, user_map).execute
if project.persisted?
render json: ProjectSerializer.new.represent(project)
else
render json: { errors: project_save_error(project) }, status: :unprocessable_entity
end
end
private
def client
@client ||= Gitlab::GoogleCodeImport::Client.new(session[:google_code_dump])
end
def verify_google_code_import_enabled
render_404 unless google_code_import_enabled?
end
def user_map
@user_map ||= begin
user_map = client.user_map
stored_user_map = session[:google_code_user_map]
user_map.update(stored_user_map) if stored_user_map
Hash[user_map.sort]
end
end
end
- page_title _("Google Code import")
- header_title _("Projects"), root_path
%h3.page-title.gl-display-flex
.gl-display-flex.gl-align-items-center.gl-justify-content-center
= sprite_icon('google', css_class: 'gl-mr-2')
= _('Import projects from Google Code')
%hr
= form_tag callback_import_google_code_path, multipart: true do
%p
= _('Follow the steps below to export your Google Code project data.')
= _("In the next step, you'll be able to select the projects you want to import.")
%ol
%li
%p
- link_to_google_takeout = link_to(_("Google Takeout"), "https://www.google.com/settings/takeout", target: '_blank', rel: 'noopener noreferrer')
= _("Go to %{link_to_google_takeout}.").html_safe % { link_to_google_takeout: link_to_google_takeout }
%li
%p
= _("Make sure you're logged into the account that owns the projects you'd like to import.")
%li
%p
= html_escape(_('Click the %{strong_open}Select none%{strong_close} button on the right, since we only need "Google Code Project Hosting".')) % { strong_open: '<strong>'.html_safe, strong_close: '</strong>'.html_safe }
%li
%p
= html_escape(_('Scroll down to %{strong_open}Google Code Project Hosting%{strong_close} and enable the switch on the right.')) % { strong_open: '<strong>'.html_safe, strong_close: '</strong>'.html_safe }
%li
%p
= html_escape(_('Choose %{strong_open}Next%{strong_close} at the bottom of the page.')) % { strong_open: '<strong>'.html_safe, strong_close: '</strong>'.html_safe }
%li
%p
= _('Leave the "File type" and "Delivery method" options on their default values.')
%li
%p
= html_escape(_('Choose %{strong_open}Create archive%{strong_close} and wait for archiving to complete.')) % { strong_open: '<strong>'.html_safe, strong_close: '</strong>'.html_safe }
%li
%p
= html_escape(_('Click the %{strong_open}Download%{strong_close} button and wait for downloading to complete.')) % { strong_open: '<strong>'.html_safe, strong_close: '</strong>'.html_safe }
%li
%p
= _('Find the downloaded ZIP file and decompress it.')
%li
%p
= html_escape(_('Find the newly extracted %{code_open}Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json%{code_close} file.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
%li
%p
= html_escape(_('Upload %{code_open}GoogleCodeProjectHosting.json%{code_close} here:')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
%p
%input{ type: "file", name: "dump_file", id: "dump_file" }
%li
%p
= _('Do you want to customize how Google Code email addresses and usernames are imported into GitLab?')
%p
= label_tag :create_user_map_0 do
= radio_button_tag :create_user_map, 0, true
= _('No, directly import the existing email addresses and usernames.')
%p
= label_tag :create_user_map_1 do
= radio_button_tag :create_user_map, 1, false
= _('Yes, let me map Google Code users to full names or GitLab users.')
%span
= submit_tag _('Continue to the next step'), class: "btn btn-success"
- page_title _("User map"), _("Google Code import")
- header_title _("Projects"), root_path
%h3.page-title.gl-display-flex
.gl-display-flex.gl-align-items-center.gl-justify-content-center
= sprite_icon('google', css_class: 'gl-mr-2')
= _('Import projects from Google Code')
%hr
= form_tag create_user_map_import_google_code_path do
%p
= _("Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import.")
%p
= html_escape(_("The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of %{code_open}:%{code_close}. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side.")) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
%ul
%li
%strong= _("Default: Directly import the Google Code email address or username")
%p
= html_escape(_('%{code_open}"johnsmith@example.com": "johnsm...@example.com"%{code_close} will add "By johnsm...@example.com" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user\'s privacy.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
%li
%strong= _("Map a Google Code user to a GitLab user")
%p
= html_escape(_('%{code_open}"johnsmith@example.com": "@johnsmith"%{code_close} will add "By %{link_open}@johnsmith%{link_close}" to all issues and comments originally created by johnsmith@example.com, and will set %{link_open}@johnsmith%{link_close} as the assignee on all issues originally assigned to johnsmith@example.com.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe, link_open: '<a href="#">'.html_safe, link_close: '</a>'.html_safe }
%li
%strong= _("Map a Google Code user to a full name")
%p
= html_escape(_('%{code_open}"johnsmith@example.com": "John Smith"%{code_close} will add "By John Smith" to all issues and comments originally created by johnsmith@example.com.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
%li
%strong= _("Map a Google Code user to a full email address")
%p
= html_escape(_('%{code_open}"johnsmith@example.com": "johnsmith@example.com"%{code_close} will add "By %{link_open}johnsmith@example.com%{link_close}" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user\'s privacy. Use this option if you want to show the full email address.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe, link_open: '<a href="#">'.html_safe, link_close: '</a>'.html_safe }
.form-group.row
.col-sm-12
= text_area_tag :user_map, Gitlab::Json.pretty_generate(@user_map), class: 'form-control', rows: 15
.form-actions
= submit_tag _('Continue to the next step'), class: "btn btn-success"
- page_title _("Google Code import")
- header_title _("Projects"), root_path
%h3.page-title.gl-display-flex
.gl-display-flex.gl-align-items-center.gl-justify-content-center
= sprite_icon('google', css_class: 'gl-mr-2')
= _('Import projects from Google Code')
- if @repos.any?
%p.light
= _('Select projects you want to import.')
%p.light
- link_to_customize = link_to(_("customize"), new_user_map_import_google_code_path)
= _("Optionally, you can %{link_to_customize} how Google Code email addresses and usernames are imported into GitLab.").html_safe % { link_to_customize: link_to_customize }
%hr
%p
- if @incompatible_repos.any?
= button_tag class: "btn btn-import btn-success js-import-all" do
= _("Import all compatible projects")
= loading_icon(css_class: 'loading-icon')
- else
= button_tag class: "btn btn-import btn-success js-import-all" do
= _("Import all projects")
= loading_icon(css_class: 'loading-icon')
.table-responsive
%table.table.import-jobs
%colgroup.import-jobs-from-col
%colgroup.import-jobs-to-col
%colgroup.import-jobs-status-col
%thead
%tr
%th= _("From Google Code")
%th= _("To GitLab")
%th= _("Status")
%tbody
- @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
%td
= link_to project.import_source, "https://code.google.com/p/#{project.import_source}", target: "_blank", rel: 'noopener noreferrer'
%td
= link_to project.full_path, project
%td.job-status
- case project.import_status
- when 'finished'
%span
= sprite_icon('check')
= _("done")
- when 'started'
= loading_icon
= _("started")
- else
= project.human_import_status_name
- @repos.each do |repo|
%tr{ id: "repo_#{repo.id}" }
%td
= link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank", rel: 'noopener noreferrer'
%td.import-target
#{current_user.username}/#{repo.name}
%td.import-actions.job-status
= button_tag class: "btn btn-import js-add-to-import" do
= _("Import")
= loading_icon(css_class: 'loading-icon')
- @incompatible_repos.each do |repo|
%tr{ id: "repo_#{repo.id}" }
%td
= link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank", rel: 'noopener noreferrer'
%td.import-target
%td.import-actions-job-status
= label_tag _("Incompatible Project"), nil, class: "label badge-danger"
- if @incompatible_repos.any?
%p
= _("One or more of your Google Code projects cannot be imported into GitLab directly because they use Subversion or Mercurial for version control, rather than Git.")
- link_to_import_flow = link_to(_("import flow"), new_import_google_code_path)
= _("Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again.").html_safe % { link_to_import_flow: link_to_import_flow }
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_google_code_path}", import_path: "#{import_google_code_path}" } }
...@@ -41,12 +41,6 @@ ...@@ -41,12 +41,6 @@
- unless gitlab_import_configured? - unless gitlab_import_configured?
= render 'projects/gitlab_import_modal' = render 'projects/gitlab_import_modal'
- if google_code_import_enabled?
%div
= link_to new_import_google_code_path, class: 'btn import_google_code', **tracking_attrs(track_label, 'click_button', 'google_code') do
= sprite_icon('google')
Google Code
- if fogbugz_import_enabled? - if fogbugz_import_enabled?
%div %div
= link_to new_import_fogbugz_path, class: 'btn import_fogbugz', **tracking_attrs(track_label, 'click_button', 'fogbugz') do = link_to new_import_fogbugz_path, class: 'btn import_fogbugz', **tracking_attrs(track_label, 'click_button', 'fogbugz') do
......
---
title: "Remove Google Code importer"
merge_request: 48139
author: Getulio Valentin Sánchez
type: removed
...@@ -42,15 +42,6 @@ namespace :import do ...@@ -42,15 +42,6 @@ namespace :import do
get :realtime_changes get :realtime_changes
end end
resource :google_code, only: [:create, :new], controller: :google_code do
get :status
post :callback
get :jobs
get :new_user_map, path: :user_map
post :create_user_map, path: :user_map
end
resource :fogbugz, only: [:create, :new], controller: :fogbugz do resource :fogbugz, only: [:create, :new], controller: :fogbugz do
get :status get :status
post :callback post :callback
......
...@@ -292,7 +292,7 @@ listed in the descriptions of the relevant settings. ...@@ -292,7 +292,7 @@ listed in the descriptions of the relevant settings.
| `housekeeping_gc_period` | integer | required by: `housekeeping_enabled` | Number of Git pushes after which `git gc` is run. | | `housekeeping_gc_period` | integer | required by: `housekeeping_enabled` | Number of Git pushes after which `git gc` is run. |
| `housekeeping_incremental_repack_period` | integer | required by: `housekeeping_enabled` | Number of Git pushes after which an incremental `git repack` is run. | | `housekeeping_incremental_repack_period` | integer | required by: `housekeeping_enabled` | Number of Git pushes after which an incremental `git repack` is run. |
| `html_emails_enabled` | boolean | no | Enable HTML emails. | | `html_emails_enabled` | boolean | no | Enable HTML emails. |
| `import_sources` | array of strings | no | Sources to allow project import from, possible values: `github`, `bitbucket`, `bitbucket_server`, `gitlab`, `google_code`, `fogbugz`, `git`, `gitlab_project`, `gitea`, `manifest`, and `phabricator`. | | `import_sources` | array of strings | no | Sources to allow project import from, possible values: `github`, `bitbucket`, `bitbucket_server`, `gitlab`, `fogbugz`, `git`, `gitlab_project`, `gitea`, `manifest`, and `phabricator`. |
| `issues_create_limit` | integer | no | Max number of issue creation requests per minute per user. Disabled by default.| | `issues_create_limit` | integer | no | Max number of issue creation requests per minute per user. Disabled by default.|
| `local_markdown_version` | integer | no | Increase this value when any cached Markdown should be invalidated. | | `local_markdown_version` | integer | no | Increase this value when any cached Markdown should be invalidated. |
| `maintenance_mode_message` | string | no | **(PREMIUM)** Message displayed when instance is in maintenance mode | | `maintenance_mode_message` | string | no | **(PREMIUM)** Message displayed when instance is in maintenance mode |
......
...@@ -88,7 +88,7 @@ module API ...@@ -88,7 +88,7 @@ module API
end end
optional :html_emails_enabled, type: Boolean, desc: 'By default GitLab sends emails in HTML and plain text formats so mail clients can choose what format to use. Disable this option if you only want to send emails in plain text format.' optional :html_emails_enabled, type: Boolean, desc: 'By default GitLab sends emails in HTML and plain text formats so mail clients can choose what format to use. Disable this option if you only want to send emails in plain text format.'
optional :import_sources, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, optional :import_sources, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce,
values: %w[github bitbucket bitbucket_server gitlab google_code fogbugz git gitlab_project gitea manifest phabricator], values: %w[github bitbucket bitbucket_server gitlab fogbugz git gitlab_project gitea manifest phabricator],
desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com' desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com'
optional :max_artifacts_size, type: Integer, desc: "Set the maximum file size for each job's artifacts" optional :max_artifacts_size, type: Integer, desc: "Set the maximum file size for each job's artifacts"
optional :max_attachment_size, type: Integer, desc: 'Maximum attachment size in MB' optional :max_attachment_size, type: Integer, desc: 'Maximum attachment size in MB'
......
# frozen_string_literal: true
module Gitlab
module GoogleCodeImport
class Client
attr_reader :raw_data
def self.mask_email(author)
parts = author.split("@", 2)
parts[0] = "#{parts[0][0...-3]}..."
parts.join("@")
end
def initialize(raw_data)
@raw_data = raw_data
end
def valid?
raw_data.is_a?(Hash) && raw_data["kind"] == "projecthosting#user" && raw_data.key?("projects")
end
def repos
@repos ||= raw_data["projects"].map { |raw_repo| GoogleCodeImport::Repository.new(raw_repo) }.select(&:git?)
end
def incompatible_repos
@incompatible_repos ||= raw_data["projects"].map { |raw_repo| GoogleCodeImport::Repository.new(raw_repo) }.reject(&:git?)
end
def repo(id)
repos.find { |repo| repo.id == id }
end
def user_map
user_map = Hash.new { |hash, user| hash[user] = self.class.mask_email(user) }
repos.each do |repo|
next unless repo.valid? && repo.issues
repo.issues.each do |raw_issue|
# Touching is enough to add the entry and masked email.
user_map[raw_issue["author"]["name"]]
raw_issue["comments"]["items"].each do |raw_comment|
user_map[raw_comment["author"]["name"]]
end
end
end
Hash[user_map.sort]
end
end
end
end
This diff is collapsed.
# frozen_string_literal: true
module Gitlab
module GoogleCodeImport
class ProjectCreator
attr_reader :repo, :namespace, :current_user, :user_map
def initialize(repo, namespace, current_user, user_map = nil)
@repo = repo
@namespace = namespace
@current_user = current_user
@user_map = user_map
end
def execute
::Projects::CreateService.new(
current_user,
name: repo.name,
path: repo.name,
description: repo.summary,
namespace: namespace,
creator: current_user,
visibility_level: Gitlab::VisibilityLevel::PUBLIC,
import_type: "google_code",
import_source: repo.name,
import_url: repo.import_url,
import_data: { data: { 'repo' => repo.raw_data, 'user_map' => user_map } }
).execute
end
end
end
end
# frozen_string_literal: true
module Gitlab
module GoogleCodeImport
class Repository
attr_accessor :raw_data
def initialize(raw_data)
@raw_data = raw_data
end
def valid?
raw_data.is_a?(Hash) && raw_data["kind"] == "projecthosting#project"
end
def id
raw_data["externalId"]
end
def name
raw_data["name"]
end
def summary
raw_data["summary"]
end
def description
raw_data["description"]
end
def git?
raw_data["versionControlSystem"] == "git"
end
def import_url
raw_data["repositoryUrls"].first
end
def issues
raw_data["issues"] && raw_data["issues"]["items"]
end
end
end
end
...@@ -15,7 +15,6 @@ module Gitlab ...@@ -15,7 +15,6 @@ module Gitlab
ImportSource.new('bitbucket', 'Bitbucket Cloud', Gitlab::BitbucketImport::Importer), ImportSource.new('bitbucket', 'Bitbucket Cloud', Gitlab::BitbucketImport::Importer),
ImportSource.new('bitbucket_server', 'Bitbucket Server', Gitlab::BitbucketServerImport::Importer), ImportSource.new('bitbucket_server', 'Bitbucket Server', Gitlab::BitbucketServerImport::Importer),
ImportSource.new('gitlab', 'GitLab.com', Gitlab::GitlabImport::Importer), ImportSource.new('gitlab', 'GitLab.com', Gitlab::GitlabImport::Importer),
ImportSource.new('google_code', 'Google Code', Gitlab::GoogleCodeImport::Importer),
ImportSource.new('fogbugz', 'FogBugz', Gitlab::FogbugzImport::Importer), ImportSource.new('fogbugz', 'FogBugz', Gitlab::FogbugzImport::Importer),
ImportSource.new('git', 'Repo by URL', nil), ImportSource.new('git', 'Repo by URL', nil),
ImportSource.new('gitlab_project', 'GitLab export', Gitlab::ImportExport::Importer), ImportSource.new('gitlab_project', 'GitLab export', Gitlab::ImportExport::Importer),
......
This diff is collapsed.
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Import::GoogleCodeController do
include ImportSpecHelper
let(:user) { create(:user) }
let(:dump_file) { fixture_file_upload('spec/fixtures/GoogleCodeProjectHosting.json', 'application/json') }
before do
sign_in(user)
end
describe "POST callback" do
it "stores Google Takeout dump list in session" do
post :callback, params: { dump_file: dump_file }
expect(session[:google_code_dump]).to be_a(Hash)
expect(session[:google_code_dump]["kind"]).to eq("projecthosting#user")
expect(session[:google_code_dump]).to have_key("projects")
end
end
describe "GET status" do
before do
@repo = OpenStruct.new(name: 'vim')
stub_client(valid?: true)
end
it "assigns variables" do
@project = create(:project, import_type: 'google_code', creator_id: user.id)
stub_client(repos: [@repo], incompatible_repos: [])
get :status
expect(assigns(:already_added_projects)).to eq([@project])
expect(assigns(:repos)).to eq([@repo])
expect(assigns(:incompatible_repos)).to eq([])
end
it "does not show already added project" do
@project = create(:project, import_type: 'google_code', creator_id: user.id, import_source: 'vim')
stub_client(repos: [@repo], incompatible_repos: [])
get :status
expect(assigns(:already_added_projects)).to eq([@project])
expect(assigns(:repos)).to eq([])
end
it "does not show any invalid projects" do
stub_client(repos: [], incompatible_repos: [@repo])
get :status
expect(assigns(:repos)).to be_empty
expect(assigns(:incompatible_repos)).to eq([@repo])
end
end
describe "POST create" do
it_behaves_like 'project import rate limiter'
end
end
...@@ -56,7 +56,6 @@ RSpec.describe 'New project', :js do ...@@ -56,7 +56,6 @@ RSpec.describe 'New project', :js do
expect(page).to have_link('GitHub') expect(page).to have_link('GitHub')
expect(page).to have_link('Bitbucket') expect(page).to have_link('Bitbucket')
expect(page).to have_link('GitLab.com') expect(page).to have_link('GitLab.com')
expect(page).to have_link('Google Code')
expect(page).to have_button('Repo by URL') expect(page).to have_button('Repo by URL')
expect(page).to have_link('GitLab export') expect(page).to have_link('GitLab export')
end end
...@@ -292,17 +291,6 @@ RSpec.describe 'New project', :js do ...@@ -292,17 +291,6 @@ RSpec.describe 'New project', :js do
end end
end end
context 'from Google Code' do
before do
first('.import_google_code').click
end
it 'shows import instructions' do
expect(page).to have_content('Import projects from Google Code')
expect(current_path).to eq new_import_google_code_path
end
end
context 'from manifest file' do context 'from manifest file' do
before do before do
first('.import_manifest').click first('.import_manifest').click
......
import MockAdapter from 'axios-mock-adapter';
import { ImporterStatus } from '~/importer_status';
import axios from '~/lib/utils/axios_utils';
describe('Importer Status', () => {
let instance;
let mock;
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
});
describe('addToImport', () => {
const importUrl = '/import_url';
const fixtures = `
<table>
<tr id="repo_123">
<td class="import-target"></td>
<td class="import-actions job-status">
<button name="button" type="submit" class="btn btn-import js-add-to-import">
</button>
</td>
</tr>
</table>
`;
beforeEach(() => {
setFixtures(fixtures);
jest.spyOn(ImporterStatus.prototype, 'initStatusPage').mockImplementation(() => {});
jest.spyOn(ImporterStatus.prototype, 'setAutoUpdate').mockImplementation(() => {});
instance = new ImporterStatus({
jobsUrl: '',
importUrl,
});
});
it('sets table row to active after post request', done => {
mock.onPost(importUrl).reply(200, {
id: 1,
full_path: '/full_path',
});
instance
.addToImport({
currentTarget: document.querySelector('.js-add-to-import'),
})
.then(() => {
expect(document.querySelector('tr').classList.contains('table-active')).toEqual(true);
done();
})
.catch(done.fail);
});
it('shows error message after failed POST request', done => {
setFixtures(`${fixtures}<div class="flash-container"></div>`);
mock.onPost(importUrl).reply(422, {
errors: 'You forgot your lunch',
});
instance
.addToImport({
currentTarget: document.querySelector('.js-add-to-import'),
})
.then(() => {
const flashMessage = document.querySelector('.flash-text');
expect(flashMessage.textContent.trim()).toEqual(
'An error occurred while importing project: You forgot your lunch',
);
done();
})
.catch(done.fail);
});
});
describe('autoUpdate', () => {
const jobsUrl = '/jobs_url';
beforeEach(() => {
const div = document.createElement('div');
div.innerHTML = `
<div id="project_1">
<div class="job-status">
</div>
</div>
`;
document.body.appendChild(div);
jest.spyOn(ImporterStatus.prototype, 'initStatusPage').mockImplementation(() => {});
jest.spyOn(ImporterStatus.prototype, 'setAutoUpdate').mockImplementation(() => {});
instance = new ImporterStatus({
jobsUrl,
});
});
function setupMock(importStatus) {
mock.onGet(jobsUrl).reply(200, [
{
id: 1,
import_status: importStatus,
},
]);
}
function expectJobStatus(done, status) {
instance
.autoUpdate()
.then(() => {
expect(document.querySelector('#project_1').innerText.trim()).toEqual(status);
done();
})
.catch(done.fail);
}
it('sets the job status to done', done => {
setupMock('finished');
expectJobStatus(done, 'Done');
});
it('sets the job status to scheduled', done => {
setupMock('scheduled');
expectJobStatus(done, 'Scheduled');
});
it('sets the job status to started', done => {
setupMock('started');
expectJobStatus(done, 'Started');
});
it('sets the job status to custom status', done => {
setupMock('custom status');
expectJobStatus(done, 'custom status');
});
});
});
# frozen_string_literal: true
require "spec_helper"
RSpec.describe Gitlab::GoogleCodeImport::Client do
let(:raw_data) { Gitlab::Json.parse(fixture_file("GoogleCodeProjectHosting.json")) }
subject { described_class.new(raw_data) }
describe "#valid?" do
context "when the data is valid" do
it "returns true" do
expect(subject).to be_valid
end
end
context "when the data is invalid" do
let(:raw_data) { "No clue" }
it "returns true" do
expect(subject).not_to be_valid
end
end
end
describe "#repos" do
it "returns only Git repositories" do
expect(subject.repos.length).to eq(1)
expect(subject.incompatible_repos.length).to eq(1)
end
end
describe "#repo" do
it "returns the referenced repository" do
expect(subject.repo("tint2").name).to eq("tint2")
end
end
end
# frozen_string_literal: true
require "spec_helper"
RSpec.describe Gitlab::GoogleCodeImport::Importer do
let(:mapped_user) { create(:user, username: "thilo123") }
let(:raw_data) { Gitlab::Json.parse(fixture_file("GoogleCodeProjectHosting.json")) }
let(:client) { Gitlab::GoogleCodeImport::Client.new(raw_data) }
let(:import_data) do
{
'repo' => client.repo('tint2').raw_data,
'user_map' => { 'thilo...' => "@#{mapped_user.username}" }
}
end
let(:project) { create(:project) }
subject { described_class.new(project) }
before do
project.add_maintainer(project.creator)
project.create_import_data(data: import_data)
end
describe "#execute" do
it "imports status labels" do
subject.execute
%w(New NeedInfo Accepted Wishlist Started Fixed Invalid Duplicate WontFix Incomplete).each do |status|
expect(project.labels.find_by(name: "Status: #{status}")).not_to be_nil
end
end
it "imports labels" do
subject.execute
%w(
Type-Defect Type-Enhancement Type-Task Type-Review Type-Other Milestone-0.12 Priority-Critical
Priority-High Priority-Medium Priority-Low OpSys-All OpSys-Windows OpSys-Linux OpSys-OSX Security
Performance Usability Maintainability Component-Panel Component-Taskbar Component-Battery
Component-Systray Component-Clock Component-Launcher Component-Tint2conf Component-Docs Component-New
).each do |label|
label = label.sub("-", ": ")
expect(project.labels.find_by(name: label)).not_to be_nil
end
end
it "imports issues" do
subject.execute
issue = project.issues.first
expect(issue).not_to be_nil
expect(issue.iid).to eq(169)
expect(issue.author).to eq(project.creator)
expect(issue.assignees).to eq([mapped_user])
expect(issue.state).to eq("closed")
expect(issue.label_names).to include("Priority: Medium")
expect(issue.label_names).to include("Status: Fixed")
expect(issue.label_names).to include("Type: Enhancement")
expect(issue.title).to eq("Scrolling through tasks")
expect(issue.state).to eq("closed")
expect(issue.description).to include("schattenpr\\.\\.\\.")
expect(issue.description).to include("November 18, 2009 00:20")
expect(issue.description).to include("Google Code")
expect(issue.description).to include('I like to scroll through the tasks with my scrollwheel (like in fluxbox).')
expect(issue.description).to include('Patch is attached that adds two new mouse-actions (next_task+prev_task)')
expect(issue.description).to include('that can be used for exactly that purpose.')
expect(issue.description).to include('all the best!')
expect(issue.description).to include('[tint2_task_scrolling.diff](https://storage.googleapis.com/google-code-attachments/tint2/issue-169/comment-0/tint2_task_scrolling.diff)')
expect(issue.description).to include('![screenshot.png](https://storage.googleapis.com/google-code-attachments/tint2/issue-169/comment-0/screenshot.png)')
expect(issue.description).to include('![screenshot1.PNG](https://storage.googleapis.com/google-code-attachments/tint2/issue-169/comment-0/screenshot1.PNG)')
end
it "imports issue comments" do
subject.execute
note = project.issues.first.notes.first
expect(note).not_to be_nil
expect(note.note).to include("Comment 1")
expect(note.note).to include("@#{mapped_user.username}")
expect(note.note).to include("November 18, 2009 05:14")
expect(note.note).to include("applied, thanks.")
expect(note.note).to include("Status: Fixed")
expect(note.note).to include("~~Type: Defect~~")
expect(note.note).to include("Type: Enhancement")
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::GoogleCodeImport::ProjectCreator do
let(:user) { create(:user) }
let(:repo) do
Gitlab::GoogleCodeImport::Repository.new(
"name" => 'vim',
"summary" => 'VI Improved',
"repositoryUrls" => ["https://vim.googlecode.com/git/"]
)
end
let(:namespace) { create(:group) }
before do
namespace.add_owner(user)
end
it 'creates project' do
expect_next_instance_of(Project) do |project|
expect(project).to receive(:add_import_job)
end
project_creator = described_class.new(repo, namespace, user)
project = project_creator.execute
expect(project.import_url).to eq("https://vim.googlecode.com/git/")
expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
end
end
...@@ -11,7 +11,6 @@ RSpec.describe Gitlab::ImportSources do ...@@ -11,7 +11,6 @@ RSpec.describe Gitlab::ImportSources do
'Bitbucket Cloud' => 'bitbucket', 'Bitbucket Cloud' => 'bitbucket',
'Bitbucket Server' => 'bitbucket_server', 'Bitbucket Server' => 'bitbucket_server',
'GitLab.com' => 'gitlab', 'GitLab.com' => 'gitlab',
'Google Code' => 'google_code',
'FogBugz' => 'fogbugz', 'FogBugz' => 'fogbugz',
'Repo by URL' => 'git', 'Repo by URL' => 'git',
'GitLab export' => 'gitlab_project', 'GitLab export' => 'gitlab_project',
...@@ -32,7 +31,6 @@ RSpec.describe Gitlab::ImportSources do ...@@ -32,7 +31,6 @@ RSpec.describe Gitlab::ImportSources do
bitbucket bitbucket
bitbucket_server bitbucket_server
gitlab gitlab
google_code
fogbugz fogbugz
git git
gitlab_project gitlab_project
...@@ -53,7 +51,6 @@ RSpec.describe Gitlab::ImportSources do ...@@ -53,7 +51,6 @@ RSpec.describe Gitlab::ImportSources do
bitbucket bitbucket
bitbucket_server bitbucket_server
gitlab gitlab
google_code
fogbugz fogbugz
gitlab_project gitlab_project
gitea gitea
...@@ -70,7 +67,6 @@ RSpec.describe Gitlab::ImportSources do ...@@ -70,7 +67,6 @@ RSpec.describe Gitlab::ImportSources do
'bitbucket' => Gitlab::BitbucketImport::Importer, 'bitbucket' => Gitlab::BitbucketImport::Importer,
'bitbucket_server' => Gitlab::BitbucketServerImport::Importer, 'bitbucket_server' => Gitlab::BitbucketServerImport::Importer,
'gitlab' => Gitlab::GitlabImport::Importer, 'gitlab' => Gitlab::GitlabImport::Importer,
'google_code' => Gitlab::GoogleCodeImport::Importer,
'fogbugz' => Gitlab::FogbugzImport::Importer, 'fogbugz' => Gitlab::FogbugzImport::Importer,
'git' => nil, 'git' => nil,
'gitlab_project' => Gitlab::ImportExport::Importer, 'gitlab_project' => Gitlab::ImportExport::Importer,
...@@ -92,7 +88,6 @@ RSpec.describe Gitlab::ImportSources do ...@@ -92,7 +88,6 @@ RSpec.describe Gitlab::ImportSources do
'bitbucket' => 'Bitbucket Cloud', 'bitbucket' => 'Bitbucket Cloud',
'bitbucket_server' => 'Bitbucket Server', 'bitbucket_server' => 'Bitbucket Server',
'gitlab' => 'GitLab.com', 'gitlab' => 'GitLab.com',
'google_code' => 'Google Code',
'fogbugz' => 'FogBugz', 'fogbugz' => 'FogBugz',
'git' => 'Repo by URL', 'git' => 'Repo by URL',
'gitlab_project' => 'GitLab export', 'gitlab_project' => 'GitLab export',
......
...@@ -126,32 +126,6 @@ RSpec.describe Import::BitbucketServerController, 'routing' do ...@@ -126,32 +126,6 @@ RSpec.describe Import::BitbucketServerController, 'routing' do
end end
end end
# status_import_google_code GET /import/google_code/status(.:format) import/google_code#status
# callback_import_google_code POST /import/google_code/callback(.:format) import/google_code#callback
# jobs_import_google_code GET /import/google_code/jobs(.:format) import/google_code#jobs
# new_user_map_import_google_code GET /import/google_code/user_map(.:format) import/google_code#new_user_map
# create_user_map_import_google_code POST /import/google_code/user_map(.:format) import/google_code#create_user_map
# import_google_code POST /import/google_code(.:format) import/google_code#create
# new_import_google_code GET /import/google_code/new(.:format) import/google_code#new
RSpec.describe Import::GoogleCodeController, 'routing' do
it_behaves_like 'importer routing' do
let(:except_actions) { [:callback] }
let(:provider) { 'google_code' }
end
it 'to #callback' do
expect(post("/import/google_code/callback")).to route_to("import/google_code#callback")
end
it 'to #new_user_map' do
expect(get('/import/google_code/user_map')).to route_to('import/google_code#new_user_map')
end
it 'to #create_user_map' do
expect(post('/import/google_code/user_map')).to route_to('import/google_code#create_user_map')
end
end
# status_import_fogbugz GET /import/fogbugz/status(.:format) import/fogbugz#status # status_import_fogbugz GET /import/fogbugz/status(.:format) import/fogbugz#status
# callback_import_fogbugz POST /import/fogbugz/callback(.:format) import/fogbugz#callback # callback_import_fogbugz POST /import/fogbugz/callback(.:format) import/fogbugz#callback
# realtime_changes_import_fogbugz GET /import/fogbugz/realtime_changes(.:format) import/fogbugz#realtime_changes # realtime_changes_import_fogbugz GET /import/fogbugz/realtime_changes(.:format) import/fogbugz#realtime_changes
......
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