lib/github_api/error/service_error.rb
# encoding: utf-8
require 'json'
require_relative '../error'
module Github
# Raised when GitHub returns any of the HTTP status codes
module Error
class ServiceError < GithubError
# Add http status code method to error type
#
# @param [Integer] code
# the status code
#
# @api public
def self.http_status_code(code)
define_method(:http_status_code) { code }
end
# A mapping of status codes and error types
#
# @return [Hash[Integer, Object]]
#
# @api public
def self.error_mapping
@error_mapping ||= Hash[
descendants.map do |klass|
[klass.new({}).http_status_code, klass]
end
]
end
MIN_BODY_LENGTH = 2
# Create a ServiceError
#
# @param [Hash[Symbol]] response
#
# @api public
def initialize(response)
@headers = response[:response_headers]
@body = response[:body]
@status = response[:status]
@response_headers = @headers
@response_message = @body
super(create_message(response))
end
# Expose response payload as JSON object if possible
#
# @return [Hash[Symbol]|String]
#
# @api public
def data
@data ||= decode_data(@body)
end
# Stores error message(s) returned in response body
#
# @return [Array[Hash[Symbol]]]
# the array of hash error objects
#
# @api public
def error_messages
@error_messages ||= begin
data[:errors] ? data[:errors] : [data]
end
end
private
# Create full error message
#
# @param [Hash[Symbol]] response
# the http response
#
# @return [String]
# the error message
#
# @api private
def create_message(response)
return if response.nil?
message = "#{response[:method].to_s.upcase} "
message << "#{response[:url]}: "
message << "#{@status} - #{format_response}"
message
end
# Decode body information if in JSON format
#
# @param [String] body
# the response body
#
# @api private
def decode_data(body)
if body.respond_to?(:to_str) &&
body.length >= MIN_BODY_LENGTH &&
@headers[:content_type] =~ /json/
JSON.parse(body, symbolize_names: true)
else
body
end
end
# Read response body and convert to human friendly format
#
# @return [String]
#
# @api private
def format_response
return '' if data.nil? || data.empty?
case data
when Hash
message = data[:message] ? data[:message] : ' '
docs = data[:documentation_url]
error = create_error_summary
message << error if error
message << "\nSee: #{docs}" if docs
message
when String
data
end
end
# Create error summary from response body
#
# @return [String]
#
# @api private
def create_error_summary
if data[:error]
"\nError: #{data[:error]}"
elsif data[:errors]
message = "\nErrors:\n"
message << data[:errors].map do |error|
case error
when Hash
"Error: #{error.map { |k, v| "#{k}: #{v}" }.join(', ')}"
else
"Error: #{error}"
end
end.join("\n")
end
end
end # ServiceError
# Raised when Github returns the HTTP status code 400
class BadRequest < ServiceError
http_status_code 400
end
# Raised when GitHub returns the HTTP status code 401
class Unauthorized < ServiceError
http_status_code 401
end
# Raised when Github returns the HTTP status code 403
class Forbidden < ServiceError
http_status_code 403
end
# Raised when Github returns the HTTP status code 404
class NotFound < ServiceError
http_status_code 404
end
# Raised when Github returns the HTTP status code 405
class MethodNotAllowed < ServiceError
http_status_code 405
end
# Raised when Github returns the HTTP status code 406
class NotAcceptable < ServiceError
http_status_code 406
end
# Raised when GitHub returns the HTTP status code 409
class Conflict < ServiceError
http_status_code 409
end
# Raised when GitHub returns the HTTP status code 414
class UnsupportedMediaType < ServiceError
http_status_code 414
end
# Raised when GitHub returns the HTTP status code 422
class UnprocessableEntity < ServiceError
http_status_code 422
end
# Raised when GitHub returns the HTTP status code 451
class UnavailableForLegalReasons < ServiceError
http_status_code 451
end
# Raised when Github returns the HTTP status code 500
class InternalServerError < ServiceError
http_status_code 500
end
# Raised when Github returns the HTTP status code 501
class NotImplemented < ServiceError
http_status_code 501
end
# Raised when Github returns the HTTP status code 502
class BadGateway < ServiceError
http_status_code 502
end
# Raised when GitHub returns the HTTP status code 503
class ServiceUnavailable < ServiceError
http_status_code 503
end
end # Error
end # Github