137 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			137 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Ruby
		
	
	
	
| # Copyright 2015 Google Inc.
 | |
| #
 | |
| # Licensed under the Apache License, Version 2.0 (the "License");
 | |
| # you may not use this file except in compliance with the License.
 | |
| # You may obtain a copy of the License at
 | |
| #
 | |
| #      http://www.apache.org/licenses/LICENSE-2.0
 | |
| #
 | |
| # Unless required by applicable law or agreed to in writing, software
 | |
| # distributed under the License is distributed on an "AS IS" BASIS,
 | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| # See the License for the specific language governing permissions and
 | |
| # limitations under the License.
 | |
| 
 | |
| require 'addressable/uri'
 | |
| require 'addressable/template'
 | |
| require 'google/apis/core/http_command'
 | |
| require 'google/apis/errors'
 | |
| require 'json'
 | |
| require 'retriable'
 | |
| 
 | |
| module Google
 | |
|   module Apis
 | |
|     module Core
 | |
|       # Command for executing most basic API request with JSON requests/responses
 | |
|       class ApiCommand < HttpCommand
 | |
|         JSON_CONTENT_TYPE = 'application/json'
 | |
|         FIELDS_PARAM = 'fields'
 | |
|         RATE_LIMIT_ERRORS = %w(rateLimitExceeded userRateLimitExceeded)
 | |
| 
 | |
|         # JSON serializer for request objects
 | |
|         # @return [Google::Apis::Core::JsonRepresentation]
 | |
|         attr_accessor :request_representation
 | |
| 
 | |
|         # Request body to serialize
 | |
|         # @return [Object]
 | |
|         attr_accessor :request_object
 | |
| 
 | |
|         # JSON serializer for response objects
 | |
|         # @return [Google::Apis::Core::JsonRepresentation]
 | |
|         attr_accessor :response_representation
 | |
| 
 | |
|         # Class to instantiate when de-serializing responses
 | |
|         # @return [Object]
 | |
|         attr_accessor :response_class
 | |
| 
 | |
|         # Serialize the request body
 | |
|         #
 | |
|         # @return [void]
 | |
|         def prepare!
 | |
|           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
 | |
|             self.body = request_representation.new(request_object).to_json(skip_undefined: true)
 | |
|           end
 | |
|           super
 | |
|         end
 | |
| 
 | |
|         # Deserialize the response body if present
 | |
|         #
 | |
|         # @param [String] content_type
 | |
|         #  Content type of body
 | |
|         # @param [String, #read] body
 | |
|         #  Response body
 | |
|         # @return [Object]
 | |
|         #   Response object
 | |
|         # noinspection RubyUnusedLocalVariable
 | |
|         def decode_response_body(content_type, body)
 | |
|           return super unless response_representation
 | |
|           return super if content_type.nil?
 | |
|           return nil unless content_type.start_with?(JSON_CONTENT_TYPE)
 | |
|           instance = response_class.new
 | |
|           response_representation.new(instance).from_json(body, unwrap: response_class)
 | |
|           instance
 | |
|         end
 | |
| 
 | |
|         # Check the response and raise error if needed
 | |
|         #
 | |
|         # @param [Fixnum] status
 | |
|         #   HTTP status code of response
 | |
|         # @param [Hurley::Header] header
 | |
|         #   HTTP response headers
 | |
|         # @param [String] body
 | |
|         #   HTTP response body
 | |
|         # @param [String] message
 | |
|         #   Error message text
 | |
|         # @return [void]
 | |
|         # @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
 | |
|         # @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
 | |
|         # @raise [Google::Apis::AuthorizationError] Authorization is required
 | |
|         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 Google::Apis::RateLimitError.new(message,
 | |
|                                                      status_code: status,
 | |
|                                                      header: header,
 | |
|                                                      body: body) if RATE_LIMIT_ERRORS.include?(error['reason'])
 | |
|             end
 | |
|             super(status, header, body, message)
 | |
|           else
 | |
|             super(status, header, body, message)
 | |
|           end
 | |
|         end
 | |
| 
 | |
|         private
 | |
| 
 | |
|         # Attempt to parse a JSON error message, returning the first found error
 | |
|         # @param [String] body
 | |
|         #  HTTP response body
 | |
|         # @return [Hash]
 | |
|         def parse_error(body)
 | |
|           hash = JSON.load(body)
 | |
|           hash['error']['errors'].first
 | |
|         rescue
 | |
|           nil
 | |
|         end
 | |
| 
 | |
|         # Convert field names from ruby conventions to original names in JSON
 | |
|         #
 | |
|         # @param [String] fields
 | |
|         #   Value of 'fields' param
 | |
|         # @return [String]
 | |
|         #   Updated header value
 | |
|         def normalize_fields_param(fields)
 | |
|           # TODO: Generate map of parameter names during code gen. Small possibility that camelization fails
 | |
|           fields.gsub(/:/, '').gsub(/\w+/) do |str|
 | |
|             str.gsub(/(?:^|_)([a-z])/){ Regexp.last_match.begin(0) == 0 ? $1 : $1.upcase }
 | |
|           end
 | |
|         end
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 |