gitlab-org--gitlab-foss/app/services/jira/requests/base.rb

128 lines
4.5 KiB
Ruby

# frozen_string_literal: true
module Jira
module Requests
class Base
include ProjectServicesLoggable
JIRA_API_VERSION = 2
# Limit the size of the JSON error message we will attempt to parse, as the JSON is external input.
JIRA_ERROR_JSON_SIZE_LIMIT = 5_000
ERRORS = {
connection: [Errno::ECONNRESET, Errno::ECONNREFUSED],
jira_ruby: JIRA::HTTPError,
ssl: OpenSSL::SSL::SSLError,
timeout: [Timeout::Error, Errno::ETIMEDOUT],
uri: [URI::InvalidURIError, SocketError]
}.freeze
ALL_ERRORS = ERRORS.values.flatten.freeze
def initialize(jira_integration, params = {})
@project = jira_integration&.project
@jira_integration = jira_integration
end
def execute
return ServiceResponse.error(message: _('Jira service not configured.')) unless jira_integration&.active?
request
end
private
attr_reader :jira_integration, :project
# We have to add the context_path here because the Jira client is not taking it into account
def base_api_url
"#{context_path}/rest/api/#{api_version}"
end
def context_path
client.options[:context_path].to_s
end
# override this method in the specific request class implementation if a differnt API version is required
def api_version
JIRA_API_VERSION
end
def client
@client ||= jira_integration.client
end
def request
response = client.get(url)
build_service_response(response)
rescue *ALL_ERRORS => e
log_error('Error sending message',
client_url: client.options[:site],
error: {
exception_class: e.class.name,
exception_message: e.message,
exception_backtrace: Gitlab::BacktraceCleaner.clean_backtrace(e.backtrace)
}
)
ServiceResponse.error(message: error_message(e))
end
def error_message(error)
reportable_error_message(error) ||
s_('JiraRequest|An error occurred while requesting data from Jira. Check your Jira integration configuration and try again.')
end
# Returns a user-facing error message if possible, otherwise `nil`.
def reportable_error_message(error)
case error
when ERRORS[:jira_ruby]
reportable_jira_ruby_error_message(error)
when ERRORS[:ssl]
s_('JiraRequest|An SSL error occurred while connecting to Jira: %{message}. Try your request again.') % { message: error.message }
when *ERRORS[:uri]
s_('JiraRequest|The Jira API URL for connecting to Jira is not valid. Check your Jira integration API URL and try again.')
when *ERRORS[:timeout]
s_('JiraRequest|A timeout error occurred while connecting to Jira. Try your request again.')
when *ERRORS[:connection]
s_('JiraRequest|A connection error occurred while connecting to Jira. Try your request again.')
end
end
# Returns a user-facing error message for a `JIRA::HTTPError` if possible,
# otherwise `nil`.
def reportable_jira_ruby_error_message(error)
case error.message
when 'Unauthorized'
s_('JiraRequest|The credentials for accessing Jira are not valid. Check your Jira integration credentials and try again.')
when 'Forbidden'
s_('JiraRequest|The credentials for accessing Jira are not allowed to access the data. Check your Jira integration credentials and try again.')
when 'Bad Request'
s_('JiraRequest|An error occurred while requesting data from Jira. Check your Jira integration configuration and try again.')
when /errorMessages/
jira_ruby_json_error_message(error.message)
end
end
def jira_ruby_json_error_message(error_message)
return if error_message.length > JIRA_ERROR_JSON_SIZE_LIMIT
begin
messages = Gitlab::Json.parse(error_message)['errorMessages']&.to_sentence
messages = Rails::Html::FullSanitizer.new.sanitize(messages).presence
return unless messages
s_('JiraRequest|An error occurred while requesting data from Jira: %{messages}. Check your Jira integration configuration and try again.') % { messages: messages }
rescue JSON::ParserError
end
end
def url
raise NotImplementedError
end
def build_service_response(response)
raise NotImplementedError
end
end
end
end