304 lines
8.1 KiB
Ruby
304 lines
8.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Gitlab
|
|
module FogbugzImport
|
|
class Importer
|
|
attr_reader :project, :repo
|
|
|
|
def initialize(project)
|
|
@project = project
|
|
|
|
import_data = project.import_data.try(:data)
|
|
repo_data = import_data['repo'] if import_data
|
|
if repo_data
|
|
@repo = FogbugzImport::Repository.new(repo_data)
|
|
@known_labels = Set.new
|
|
else
|
|
raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}"
|
|
end
|
|
end
|
|
|
|
def execute
|
|
return true unless repo.valid?
|
|
|
|
client = Gitlab::FogbugzImport::Client.new(token: fb_session[:token], uri: fb_session[:uri])
|
|
|
|
@cases = client.cases(@repo.id.to_i)
|
|
@categories = client.categories
|
|
|
|
import_cases
|
|
|
|
true
|
|
end
|
|
|
|
private
|
|
|
|
def fb_session
|
|
@import_data_credentials ||= project.import_data.credentials[:fb_session] if project.import_data && project.import_data.credentials
|
|
end
|
|
|
|
def user_map
|
|
@user_map ||= begin
|
|
user_map = {}
|
|
import_data = project.import_data.try(:data)
|
|
stored_user_map = import_data['user_map'] if import_data
|
|
user_map.update(stored_user_map) if stored_user_map
|
|
|
|
user_map
|
|
end
|
|
end
|
|
|
|
def import_labels
|
|
@categories['categories']['category'].each do |label|
|
|
create_label(label['sCategory'])
|
|
@known_labels << name
|
|
end
|
|
end
|
|
|
|
def nice_label_color(name)
|
|
case name
|
|
when 'Blocker'
|
|
'#ff0000'
|
|
when 'Crash'
|
|
'#ffcfcf'
|
|
when 'Major'
|
|
'#deffcf'
|
|
when 'Minor'
|
|
'#cfe9ff'
|
|
when 'Bug'
|
|
'#d9534f'
|
|
when 'Feature'
|
|
'#44ad8e'
|
|
when 'Technical Task'
|
|
'#4b6dd0'
|
|
else
|
|
'#e2e2e2'
|
|
end
|
|
end
|
|
|
|
def create_label(name)
|
|
params = { title: name, color: nice_label_color(name) }
|
|
::Labels::FindOrCreateService.new(nil, project, params).execute(skip_authorization: true)
|
|
end
|
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
|
def user_info(person_id)
|
|
user_hash = user_map[person_id.to_s]
|
|
|
|
user_name = ''
|
|
gitlab_id = nil
|
|
|
|
unless user_hash.nil?
|
|
user_name = user_hash['name']
|
|
if user = User.find_by(id: user_hash['gitlab_user'])
|
|
user_name = "@#{user.username}"
|
|
gitlab_id = user.id
|
|
end
|
|
end
|
|
|
|
{ name: user_name, gitlab_id: gitlab_id }
|
|
end
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
|
def import_cases
|
|
return unless @cases
|
|
|
|
while bug = @cases.shift
|
|
author = user_info(bug['ixPersonOpenedBy'])[:name]
|
|
date = DateTime.parse(bug['dtOpened'])
|
|
|
|
comments = bug['events']['event']
|
|
|
|
content = format_content(opened_content(comments))
|
|
body = format_issue_body(author, date, content)
|
|
|
|
labels = []
|
|
[bug['sCategory'], bug['sPriority']].each do |label|
|
|
unless label.blank?
|
|
labels << label
|
|
|
|
unless @known_labels.include?(label)
|
|
create_label(label)
|
|
@known_labels << label
|
|
end
|
|
end
|
|
end
|
|
|
|
assignee_id = user_info(bug['ixPersonAssignedTo'])[:gitlab_id]
|
|
author_id = user_info(bug['ixPersonOpenedBy'])[:gitlab_id] || project.creator_id
|
|
|
|
issue = Issue.create!(
|
|
iid: bug['ixBug'],
|
|
project_id: project.id,
|
|
title: bug['sTitle'],
|
|
description: body,
|
|
author_id: author_id,
|
|
assignee_ids: [assignee_id],
|
|
state: bug['fOpen'] == 'true' ? 'opened' : 'closed',
|
|
created_at: date,
|
|
updated_at: DateTime.parse(bug['dtLastUpdated'])
|
|
)
|
|
|
|
issue_labels = ::LabelsFinder.new(nil, project_id: project.id, title: labels).execute(skip_authorization: true)
|
|
issue.update_attribute(:label_ids, issue_labels.pluck(:id))
|
|
|
|
import_issue_comments(issue, comments)
|
|
end
|
|
end
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
|
|
|
def opened_content(comments)
|
|
while comment = comments.shift
|
|
if comment['sVerb'] == 'Opened'
|
|
return comment['s']
|
|
end
|
|
end
|
|
''
|
|
end
|
|
|
|
def import_issue_comments(issue, comments)
|
|
Note.transaction do
|
|
while comment = comments.shift
|
|
verb = comment['sVerb']
|
|
|
|
next if verb == 'Opened'
|
|
|
|
content = format_content(comment['s'])
|
|
attachments = format_attachments(comment['rgAttachments'])
|
|
updates = format_updates(comment)
|
|
|
|
next if content.blank? && attachments.empty? && updates.empty?
|
|
|
|
author = user_info(comment['ixPerson'])[:name]
|
|
author_id = user_info(comment['ixPerson'])[:gitlab_id] || project.creator_id
|
|
date = DateTime.parse(comment['dt'])
|
|
|
|
body = format_issue_comment_body(
|
|
comment['ixBugEvent'],
|
|
author,
|
|
date,
|
|
content,
|
|
attachments,
|
|
updates
|
|
)
|
|
|
|
note = Note.create!(
|
|
project_id: project.id,
|
|
noteable_type: "Issue",
|
|
noteable_id: issue.id,
|
|
author_id: author_id,
|
|
note: body
|
|
)
|
|
|
|
note.update_attribute(:created_at, date)
|
|
note.update_attribute(:updated_at, date)
|
|
end
|
|
end
|
|
end
|
|
|
|
def linkify_issues(str)
|
|
str = str.gsub(/([Ii]ssue) ([0-9]+)/, '\1 #\2')
|
|
str.gsub(/([Cc]ase) ([0-9]+)/, '\1 #\2')
|
|
end
|
|
|
|
def escape_for_markdown(str)
|
|
str = str.gsub(/^#/, "\\#")
|
|
str = str.gsub(/^-/, "\\-")
|
|
str = str.gsub("`", "\\~")
|
|
str = str.delete("\r")
|
|
str.gsub("\n", " \n")
|
|
end
|
|
|
|
def format_content(raw_content)
|
|
return raw_content if raw_content.nil?
|
|
|
|
linkify_issues(escape_for_markdown(raw_content))
|
|
end
|
|
|
|
def format_attachments(raw_attachments)
|
|
return [] unless raw_attachments
|
|
|
|
attachments = case raw_attachments['attachment']
|
|
when Array
|
|
raw_attachments['attachment']
|
|
when Hash
|
|
[raw_attachments['attachment']]
|
|
else
|
|
[]
|
|
end
|
|
|
|
attachments.map! { |a| format_attachment(a) }
|
|
attachments.compact
|
|
end
|
|
|
|
def format_attachment(attachment)
|
|
link = build_attachment_url(attachment['sURL'])
|
|
|
|
res = ::Projects::DownloadService.new(project, link).execute
|
|
|
|
return if res.nil?
|
|
|
|
res[:markdown]
|
|
end
|
|
|
|
def build_attachment_url(rel_url)
|
|
uri = fb_session[:uri]
|
|
token = fb_session[:token]
|
|
"#{uri}/#{rel_url}&token=#{token}"
|
|
end
|
|
|
|
def format_updates(comment)
|
|
updates = []
|
|
|
|
if comment['sChanges']
|
|
updates << "*Changes: #{linkify_issues(comment['sChanges'].chomp)}*"
|
|
end
|
|
|
|
if comment['evtDescription']
|
|
updates << "*#{comment['evtDescription']}*"
|
|
end
|
|
|
|
updates
|
|
end
|
|
|
|
def format_issue_body(author, date, content)
|
|
body = []
|
|
body << "*By #{author} on #{date} (imported from FogBugz)*"
|
|
body << '---'
|
|
|
|
if content.blank?
|
|
content = '*(No description has been entered for this issue)*'
|
|
end
|
|
|
|
body << content
|
|
|
|
body.join("\n\n")
|
|
end
|
|
|
|
def format_issue_comment_body(id, author, date, content, attachments, updates)
|
|
body = []
|
|
body << "*By #{author} on #{date} (imported from FogBugz)*"
|
|
body << '---'
|
|
|
|
if content.blank?
|
|
content = "*(No comment has been entered for this change)*"
|
|
end
|
|
|
|
body << content
|
|
|
|
if updates.any?
|
|
body << '---'
|
|
body += updates
|
|
end
|
|
|
|
if attachments.any?
|
|
body << '---'
|
|
body += attachments
|
|
end
|
|
|
|
body.join("\n\n")
|
|
end
|
|
end
|
|
end
|
|
end
|