2021-01-29 00:20:46 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
module Issuable
|
|
|
|
module ImportCsv
|
|
|
|
class BaseService
|
|
|
|
def initialize(user, project, csv_io)
|
|
|
|
@user = user
|
|
|
|
@project = project
|
|
|
|
@csv_io = csv_io
|
|
|
|
@results = { success: 0, error_lines: [], parse_error: false }
|
|
|
|
end
|
|
|
|
|
|
|
|
def execute
|
|
|
|
process_csv
|
|
|
|
email_results_to_user
|
|
|
|
|
|
|
|
@results
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def process_csv
|
|
|
|
with_csv_lines.each do |row, line_no|
|
2022-08-27 11:52:29 +05:30
|
|
|
attributes = issuable_attributes_for(row)
|
2021-01-29 00:20:46 +05:30
|
|
|
|
2022-11-25 23:54:43 +05:30
|
|
|
if create_issuable(attributes)&.persisted?
|
2021-01-29 00:20:46 +05:30
|
|
|
@results[:success] += 1
|
|
|
|
else
|
|
|
|
@results[:error_lines].push(line_no)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
rescue ArgumentError, CSV::MalformedCSVError
|
|
|
|
@results[:parse_error] = true
|
|
|
|
end
|
|
|
|
|
2022-08-27 11:52:29 +05:30
|
|
|
def issuable_attributes_for(row)
|
|
|
|
{
|
|
|
|
title: row[:title],
|
|
|
|
description: row[:description],
|
|
|
|
due_date: row[:due_date]
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2021-01-29 00:20:46 +05:30
|
|
|
def with_csv_lines
|
|
|
|
csv_data = @csv_io.open(&:read).force_encoding(Encoding::UTF_8)
|
2021-02-22 17:27:13 +05:30
|
|
|
validate_headers_presence!(csv_data.lines.first)
|
2021-01-29 00:20:46 +05:30
|
|
|
|
2021-02-22 17:27:13 +05:30
|
|
|
CSV.new(
|
|
|
|
csv_data,
|
2021-01-29 00:20:46 +05:30
|
|
|
col_sep: detect_col_sep(csv_data.lines.first),
|
|
|
|
headers: true,
|
|
|
|
header_converters: :symbol
|
2021-02-22 17:27:13 +05:30
|
|
|
).each.with_index(2)
|
2021-01-29 00:20:46 +05:30
|
|
|
end
|
|
|
|
|
2021-02-22 17:27:13 +05:30
|
|
|
def validate_headers_presence!(headers)
|
|
|
|
headers.downcase! if headers
|
|
|
|
return if headers && headers.include?('title') && headers.include?('description')
|
2021-01-29 00:20:46 +05:30
|
|
|
|
|
|
|
raise CSV::MalformedCSVError
|
|
|
|
end
|
|
|
|
|
|
|
|
def detect_col_sep(header)
|
|
|
|
if header.include?(",")
|
|
|
|
","
|
|
|
|
elsif header.include?(";")
|
|
|
|
";"
|
|
|
|
elsif header.include?("\t")
|
|
|
|
"\t"
|
|
|
|
else
|
|
|
|
raise CSV::MalformedCSVError
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def create_issuable(attributes)
|
2021-09-30 23:02:18 +05:30
|
|
|
# NOTE: CSV imports are performed by workers, so we do not have a request context in order
|
|
|
|
# to create a SpamParams object to pass to the issuable create service.
|
|
|
|
spam_params = nil
|
2021-11-18 22:05:49 +05:30
|
|
|
create_service = create_issuable_class.new(project: @project, current_user: @user, params: attributes, spam_params: spam_params)
|
|
|
|
|
|
|
|
# For now, if create_issuable_class prepends RateLimitedService let's bypass rate limiting
|
|
|
|
if create_issuable_class < RateLimitedService
|
|
|
|
create_service.execute_without_rate_limiting
|
|
|
|
else
|
|
|
|
create_service.execute
|
|
|
|
end
|
2021-01-29 00:20:46 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
def email_results_to_user
|
|
|
|
# defined in ImportCsvService
|
|
|
|
end
|
|
|
|
|
|
|
|
def create_issuable_class
|
|
|
|
# defined in ImportCsvService
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|