Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow using v2 error messages + fix to_json signature #583

Merged
merged 1 commit into from
May 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 43 additions & 10 deletions lib/google/apis/core/api_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ class ApiCommand < HttpCommand
#
# @return [void]
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)
if request_representation && request_object
header['Content-Type'] ||= JSON_CONTENT_TYPE
Expand Down Expand Up @@ -100,15 +103,15 @@ def decode_response_body(content_type, body)
def check_status(status, header = nil, body = nil, message = nil)
case status
when 400, 402...500
error = parse_error(body)
if error
message = sprintf('%s: %s', error['reason'], error['message'])
raise ERROR_REASON_MAPPING[error['reason']].new(
reason, message = parse_error(body)
if reason
message = sprintf('%s: %s', reason, message)
raise ERROR_REASON_MAPPING[reason].new(
message,
status_code: status,
header: header,
body: body
) if ERROR_REASON_MAPPING.key?(error['reason'])
) if ERROR_REASON_MAPPING.key?(reason)
end
super(status, header, body, message)
else
Expand All @@ -122,15 +125,45 @@ def allow_form_encoding?

private

# Attempt to parse a JSON error message, returning the first found error
# Attempt to parse a JSON error message
# @param [String] body
# HTTP response body
# @return [Hash]
# @return [Array<(String, String)>]
# Error reason and message
def parse_error(body)
hash = JSON.load(body)
hash['error']['errors'].first
obj = JSON.load(body)
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
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

# Convert field names from ruby conventions to original names in JSON
Expand Down
2 changes: 1 addition & 1 deletion lib/google/apis/core/json_representation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def from_json(json)
end
end

def to_json
def to_json(*a)
representation = self.class.const_get(:Representation)
representation.new(self).to_json(user_options: { skip_undefined: true })
end
Expand Down
8 changes: 6 additions & 2 deletions lib/google/apis/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ module Apis
:header,
:normalize_unicode,
:skip_serialization,
:skip_deserialization)
:skip_deserialization,
:api_format_version)

# General client options
class ClientOptions
Expand Down Expand Up @@ -67,6 +68,8 @@ class RequestOptions
# @return [Boolean] True if body object should be treated as raw text instead of an object.
# @!attribute [rw] skip_deserialization
# @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
# @return [Google::Apis::RequestOptions]
Expand All @@ -85,13 +88,14 @@ def merge(options)
new_options
end
end

ClientOptions.default.log_http_requests = false
ClientOptions.default.application_name = 'unknown'
ClientOptions.default.application_version = '0.0.0'
RequestOptions.default.retries = 0
RequestOptions.default.normalize_unicode = false
RequestOptions.default.skip_serialization = false
RequestOptions.default.skip_deserialization = false
RequestOptions.default.api_format_version = nil
end
end
48 changes: 48 additions & 0 deletions spec/google/apis/core/api_command_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -280,4 +280,52 @@
expect { command.execute(client) }.to raise_error(/Invalid request/)
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