diff --git a/lib/google/apis/core/api_command.rb b/lib/google/apis/core/api_command.rb index 1df7d6240bf..9262f87dc00 100644 --- a/lib/google/apis/core/api_command.rb +++ b/lib/google/apis/core/api_command.rb @@ -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 @@ -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 @@ -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 diff --git a/lib/google/apis/core/json_representation.rb b/lib/google/apis/core/json_representation.rb index bfc1e49d91e..9b7e5444185 100644 --- a/lib/google/apis/core/json_representation.rb +++ b/lib/google/apis/core/json_representation.rb @@ -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 diff --git a/lib/google/apis/options.rb b/lib/google/apis/options.rb index 83f6def9eda..69be662a687 100644 --- a/lib/google/apis/options.rb +++ b/lib/google/apis/options.rb @@ -30,7 +30,8 @@ module Apis :header, :normalize_unicode, :skip_serialization, - :skip_deserialization) + :skip_deserialization, + :api_format_version) # General client options class ClientOptions @@ -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] @@ -85,7 +88,7 @@ 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' @@ -93,5 +96,6 @@ def merge(options) RequestOptions.default.normalize_unicode = false RequestOptions.default.skip_serialization = false RequestOptions.default.skip_deserialization = false + RequestOptions.default.api_format_version = nil end end diff --git a/spec/google/apis/core/api_command_spec.rb b/spec/google/apis/core/api_command_spec.rb index c14634169c4..6b57863bb15 100644 --- a/spec/google/apis/core/api_command_spec.rb +++ b/spec/google/apis/core/api_command_spec.rb @@ -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 = < '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