Allow using v2 error messages + fix to_json signature to allow arbitrary args

This commit is contained in:
Steven Bazyl 2017-05-19 17:16:22 -06:00
parent 271f0c86e3
commit 6a4e9184e7
4 changed files with 98 additions and 13 deletions

View File

@ -52,6 +52,9 @@ module Google
# #
# @return [void] # @return [void]
def prepare! def prepare!
if options && options.api_format_version
header['X-Goog-Api-Format-Version'] = options.api_format_version.to_s
end
query[FIELDS_PARAM] = normalize_fields_param(query[FIELDS_PARAM]) if query.key?(FIELDS_PARAM) query[FIELDS_PARAM] = normalize_fields_param(query[FIELDS_PARAM]) if query.key?(FIELDS_PARAM)
if request_representation && request_object if request_representation && request_object
header['Content-Type'] ||= JSON_CONTENT_TYPE header['Content-Type'] ||= JSON_CONTENT_TYPE
@ -100,15 +103,15 @@ module Google
def check_status(status, header = nil, body = nil, message = nil) def check_status(status, header = nil, body = nil, message = nil)
case status case status
when 400, 402...500 when 400, 402...500
error = parse_error(body) reason, message = parse_error(body)
if error if reason
message = sprintf('%s: %s', error['reason'], error['message']) message = sprintf('%s: %s', reason, message)
raise ERROR_REASON_MAPPING[error['reason']].new( raise ERROR_REASON_MAPPING[reason].new(
message, message,
status_code: status, status_code: status,
header: header, header: header,
body: body body: body
) if ERROR_REASON_MAPPING.key?(error['reason']) ) if ERROR_REASON_MAPPING.key?(reason)
end end
super(status, header, body, message) super(status, header, body, message)
else else
@ -122,15 +125,45 @@ module Google
private private
# Attempt to parse a JSON error message, returning the first found error # Attempt to parse a JSON error message
# @param [String] body # @param [String] body
# HTTP response body # HTTP response body
# @return [Hash] # @return [Array<(String, String)>]
# Error reason and message
def parse_error(body) def parse_error(body)
hash = JSON.load(body) obj = JSON.load(body)
hash['error']['errors'].first error = obj['error']
if error['details']
return extract_v2_error_details(error)
elsif error['errors']
return extract_v1_error_details(error)
else
fail 'Can not parse error message. No "details" or "errors" detected'
end
rescue rescue
nil return [nil, nil]
end
# Extracts details from a v1 error message
# @param [Hash] error
# Parsed JSON
# @return [Array<(String, String)>]
# Error reason and message
def extract_v1_error_details(error)
reason = error['errors'].first['reason']
message = error['message']
return [reason, message]
end
# Extracts details from a v2error message
# @param [Hash] error
# Parsed JSON
# @return [Array<(String, String)>]
# Error reason and message
def extract_v2_error_details(error)
reason = error['status']
message = error['message']
return [reason, message]
end end
# Convert field names from ruby conventions to original names in JSON # Convert field names from ruby conventions to original names in JSON

View File

@ -139,7 +139,7 @@ module Google
end end
end end
def to_json def to_json(*a)
representation = self.class.const_get(:Representation) representation = self.class.const_get(:Representation)
representation.new(self).to_json(user_options: { skip_undefined: true }) representation.new(self).to_json(user_options: { skip_undefined: true })
end end

View File

@ -30,7 +30,8 @@ module Google
:header, :header,
:normalize_unicode, :normalize_unicode,
:skip_serialization, :skip_serialization,
:skip_deserialization) :skip_deserialization,
:api_format_version)
# General client options # General client options
class ClientOptions class ClientOptions
@ -67,6 +68,8 @@ module Google
# @return [Boolean] True if body object should be treated as raw text instead of an object. # @return [Boolean] True if body object should be treated as raw text instead of an object.
# @!attribute [rw] skip_deserialization # @!attribute [rw] skip_deserialization
# @return [Boolean] True if response should be returned in raw form instead of deserialized. # @return [Boolean] True if response should be returned in raw form instead of deserialized.
# @!attribute [rw] api_format_version
# @return [Fixnum] Version of the error format to request/expect.
# Get the default options # Get the default options
# @return [Google::Apis::RequestOptions] # @return [Google::Apis::RequestOptions]
@ -93,5 +96,6 @@ module Google
RequestOptions.default.normalize_unicode = false RequestOptions.default.normalize_unicode = false
RequestOptions.default.skip_serialization = false RequestOptions.default.skip_serialization = false
RequestOptions.default.skip_deserialization = false RequestOptions.default.skip_deserialization = false
RequestOptions.default.api_format_version = nil
end end
end end

View File

@ -280,4 +280,52 @@ EOF
expect { command.execute(client) }.to raise_error(/Invalid request/) expect { command.execute(client) }.to raise_error(/Invalid request/)
end end
end end
context('with v2 error messages') do
let(:command) do
cmd = Google::Apis::Core::ApiCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
cmd.options.api_format_version = 2
cmd
end
before(:example) do
json = <<EOF
{
"error": {
"code": 400,
"message": "Illegal character ':' in log name",
"status": "INVALID_ARGUMENT",
"details": [
{
"@type": "type.googleapis.com/google.logging.v2.WriteLogEntriesPartialErrors",
"logEntryErrors": {
"0": {
"code": 3,
"message": "Illegal character ':' in log name"
},
"1": {
"code": 7,
"message": "User not authorized."
}
}
}
]
}
}
EOF
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.with(headers: {'X-Goog-Api-Format-Version' => '2'})
.to_return(status: [400, 'Invalid Argument'], headers: { 'Content-Type' => 'application/json' }, body: json)
end
it 'should raise client error' do
expect { command.execute(client) }.to raise_error(Google::Apis::ClientError)
end
it 'should raise an error with the reason and message' do
expect { command.execute(client) }.to raise_error(
/INVALID_ARGUMENT: Illegal character ':' in log name/)
end
end
end end