Mostly doc updates, +remove support for method as string

This commit is contained in:
Steven Bazyl 2012-09-28 12:07:11 -07:00
parent 9bbc3224ff
commit 4d3c1801b7
11 changed files with 404 additions and 158 deletions

View File

@ -341,7 +341,7 @@ module Google
# Returns the method object for a given RPC name and service version. # Returns the method object for a given RPC name and service version.
# #
# @param [String, Symbol] rpc_name The RPC name of the desired method. # @param [String, Symbol] rpc_name The RPC name of the desired method.
# @param [String, Symbol] rpc_name The API the method is within. # @param [String, Symbol] api The API the method is within.
# @param [String] version The desired version of the API. # @param [String] version The desired version of the API.
# #
# @return [Google::APIClient::Method] The method object. # @return [Google::APIClient::Method] The method object.
@ -439,7 +439,7 @@ module Google
## ##
# Generates a request. # Generates a request.
# #
# @option options [Google::APIClient::Method, String] :api_method # @option options [Google::APIClient::Method] :api_method
# The method object or the RPC name of the method being executed. # The method object or the RPC name of the method being executed.
# @option options [Hash, Array] :parameters # @option options [Hash, Array] :parameters
# The parameters to send to the method. # The parameters to send to the method.
@ -480,7 +480,7 @@ module Google
# If a Hash, the below parameters are handled. If an Array, the # If a Hash, the below parameters are handled. If an Array, the
# parameters are assumed to be in the below order: # parameters are assumed to be in the below order:
# #
# - (Google::APIClient::Method, String) api_method: # - (Google::APIClient::Method) api_method:
# The method object or the RPC name of the method being executed. # The method object or the RPC name of the method being executed.
# - (Hash, Array) parameters: # - (Hash, Array) parameters:
# The parameters to send to the method. # The parameters to send to the method.
@ -502,8 +502,9 @@ module Google
# result = client.execute(batch_request) # result = client.execute(batch_request)
# #
# @example # @example
# plus = client.discovered_api('plus')
# result = client.execute( # result = client.execute(
# :api_method => 'plus.activities.list', # :api_method => plus.activities.list,
# :parameters => {'collection' => 'public', 'userId' => 'me'} # :parameters => {'collection' => 'public', 'userId' => 'me'}
# ) # )
# #
@ -532,17 +533,14 @@ module Google
request = self.generate_request(options) request = self.generate_request(options)
end end
request.headers['User-Agent'] ||= '' + self.user_agent unless self.user_agent.nil?
request.parameters['key'] ||= self.key unless self.key.nil?
request.parameters['userIp'] ||= self.user_ip unless self.user_ip.nil?
connection = options[:connection] || Faraday.default_connection connection = options[:connection] || Faraday.default_connection
request.authorization = options[:authorization] || self.authorization unless options[:authenticated] == false request.authorization = options[:authorization] || self.authorization unless options[:authenticated] == false
result = request.send(connection) result = request.send(connection)
if request.upload_type == 'resumable'
upload = result.resumable_upload
unless upload.complete?
result = upload.send(connection)
end
end
return result return result
end end
@ -571,38 +569,12 @@ module Google
return result return result
end end
##
# Ensures API method names specified as strings resolve to
# discovered method instances
def resolve_method(method, version)
version ||= 'v1'
if method.kind_of?(Google::APIClient::Method) || method == nil
return method
elsif method.respond_to?(:to_str) || method.kind_of?(Symbol)
# This method of guessing the API is unreliable. This will fail for
# APIs where the first segment of the RPC name does not match the
# service name. However, this is a fallback mechanism anyway.
# Developers should be passing in a reference to the method, rather
# than passing in a string or symbol. This should raise an error
# in the case of a mismatch.
method = method.to_s
api = method[/^([^.]+)\./, 1]
api_method = self.discovered_method(method, api, version)
if api_method.nil?
raise ArgumentError, "API method could not be found."
end
return api_method
else
raise TypeError,
"Expected Google::APIClient::Method, got #{method.class}."
end
end
protected protected
## ##
# Resolves a URI template against the client's configured base. # Resolves a URI template against the client's configured base.
# #
# @api private
# @param [String, Addressable::URI, Addressable::Template] template # @param [String, Addressable::URI, Addressable::Template] template
# The template to resolve. # The template to resolve.
# @param [Hash] mapping The mapping that corresponds to the template. # @param [Hash] mapping The mapping that corresponds to the template.

View File

@ -19,18 +19,48 @@ require 'uuidtools'
module Google module Google
class APIClient class APIClient
##
# Helper class to contain a response to an individual batched call. # Helper class to contain a response to an individual batched call.
#
# @api private
class BatchedCallResponse class BatchedCallResponse
attr_reader :call_id attr_reader :call_id
attr_accessor :status, :headers, :body attr_accessor :status
attr_accessor :headers
attr_accessor :body
##
# Initialize the call response
#
# @param [String] call_id
# UUID of the original call
# @param [Integer] status
# HTTP status
# @param [Hash] headers
# HTTP response headers
# @param [#read, #to_str] body
# Response body
def initialize(call_id, status = nil, headers = nil, body = nil) def initialize(call_id, status = nil, headers = nil, body = nil)
@call_id, @status, @headers, @body = call_id, status, headers, body @call_id, @status, @headers, @body = call_id, status, headers, body
end end
end end
##
# Wraps multiple API calls into a single over-the-wire HTTP request. # Wraps multiple API calls into a single over-the-wire HTTP request.
#
# @example
#
# client = Google::APIClient.new
# urlshortener = client.discovered_api('urlshortner')
# batch = Google::APIClient::BatchRequest.new do |result|
# puts result.data
# end
#
# batch.add(:api_method=>urlshortener.url.insert, :body_object => { 'longUrl' => 'http://example.com/foo' })
# batch.add(:api_method=>urlshortener.url.insert, :body_object => { 'longUrl' => 'http://example.com/bar' })
#
# client.execute(batch)
#
class BatchRequest < Request class BatchRequest < Request
BATCH_BOUNDARY = "-----------RubyApiBatchRequest".freeze BATCH_BOUNDARY = "-----------RubyApiBatchRequest".freeze
@ -40,12 +70,16 @@ module Google
# Creates a new batch request. # Creates a new batch request.
# #
# @param [Hash] options # @param [Hash] options
# Set of options for this request # Set of options for this request.
# @param [Proc] block # @param [Proc] block
# Callback for every call's response. Won't be called if a call defined # Callback for every call's response. Won't be called if a call defined
# a callback of its own. # a callback of its own.
# #
# @return [Google::APIClient::BatchRequest] The constructed object. # @return [Google::APIClient::BatchRequest]
# The constructed object.
#
# @yield [Google::APIClient::Result]
# block to be called when result ready
def initialize(options = {}, &block) def initialize(options = {}, &block)
@calls = [] @calls = []
@global_callback = block if block_given? @global_callback = block if block_given?
@ -66,11 +100,18 @@ module Google
# automatically be generated, avoiding collisions. If duplicate call IDs # automatically be generated, avoiding collisions. If duplicate call IDs
# are provided, an error will be thrown. # are provided, an error will be thrown.
# #
# @param [Hash, Google::APIClient::Request] call: the call to be added. # @param [Hash, Google::APIClient::Request] call
# @param [String] call_id: the ID to be used for this call. Must be unique # the call to be added.
# @param [Proc] block: callback for this call's response. # @param [String] call_id
# the ID to be used for this call. Must be unique
# @param [Proc] block
# callback for this call's response.
# #
# @return [Google::APIClient::BatchRequest] The BatchRequest, for chaining # @return [Google::APIClient::BatchRequest]
# the BatchRequest, for chaining
#
# @yield [Google::APIClient::Result]
# block to be called when result ready
def add(call, call_id = nil, &block) def add(call, call_id = nil, &block)
unless call.kind_of?(Google::APIClient::Reference) unless call.kind_of?(Google::APIClient::Reference)
call = Google::APIClient::Reference.new(call) call = Google::APIClient::Reference.new(call)
@ -88,7 +129,10 @@ module Google
## ##
# Processes the HTTP response to the batch request, issuing callbacks. # Processes the HTTP response to the batch request, issuing callbacks.
# #
# @param [Faraday::Response] response: the HTTP response. # @api private
#
# @param [Faraday::Response] response
# the HTTP response.
def process_http_response(response) def process_http_response(response)
content_type = find_header('Content-Type', response.headers) content_type = find_header('Content-Type', response.headers)
boundary = /.*boundary=(.+)/.match(content_type)[1] boundary = /.*boundary=(.+)/.match(content_type)[1]
@ -105,7 +149,10 @@ module Google
## ##
# Return the request body for the BatchRequest's HTTP request. # Return the request body for the BatchRequest's HTTP request.
# #
# @return [String] The request body. # @api private
#
# @return [String]
# the request body.
def to_http_request def to_http_request
if @calls.nil? || @calls.empty? if @calls.nil? || @calls.empty?
raise BatchError, 'Cannot make an empty batch request' raise BatchError, 'Cannot make an empty batch request'
@ -121,10 +168,15 @@ module Google
## ##
# Helper method to find a header from its name, regardless of case. # Helper method to find a header from its name, regardless of case.
# #
# @param [String] name: The name of the header to find. # @api private
# @param [Hash] headers: The hash of headers and their values.
# #
# @return [String] The value of the desired header. # @param [String] name
# the name of the header to find.
# @param [Hash] headers
# the hash of headers and their values.
#
# @return [String]
# the value of the desired header.
def find_header(name, headers) def find_header(name, headers)
_, header = headers.detect do |h, v| _, header = headers.detect do |h, v|
h.downcase == name.downcase h.downcase == name.downcase
@ -135,7 +187,10 @@ module Google
## ##
# Create a new call ID. Uses an auto-incrementing, conflict-avoiding ID. # Create a new call ID. Uses an auto-incrementing, conflict-avoiding ID.
# #
# @return [String] the new, unique ID. # @api private
#
# @return [String]
# the new, unique ID.
def new_id def new_id
@last_auto_id += 1 @last_auto_id += 1
while @calls.assoc(@last_auto_id) while @calls.assoc(@last_auto_id)
@ -144,15 +199,17 @@ module Google
return @last_auto_id.to_s return @last_auto_id.to_s
end end
## ##
# Convert a Content-ID header value to an id. Presumes the Content-ID # Convert a Content-ID header value to an id. Presumes the Content-ID
# header conforms to the format that id_to_header() returns. # header conforms to the format that id_to_header() returns.
# #
# @param [String] header: Content-ID header value. # @api private
# #
# @return [String] The extracted ID value. # @param [String] header
# Content-ID header value.
#
# @return [String]
# The extracted ID value.
def header_to_id(header) def header_to_id(header)
if !header.start_with?('<') || !header.end_with?('>') || if !header.start_with?('<') || !header.end_with?('>') ||
!header.include?('+') !header.include?('+')
@ -166,9 +223,13 @@ module Google
## ##
# Auxiliary method to split the headers from the body in an HTTP response. # Auxiliary method to split the headers from the body in an HTTP response.
# #
# @param [String] response: the response to parse. # @api private
# #
# @return [Array<Hash>, String] The headers and the body, separately. # @param [String] response
# the response to parse.
#
# @return [Array<Hash>, String]
# the headers and the body, separately.
def split_headers_and_body(response) def split_headers_and_body(response)
headers = {} headers = {}
payload = response.lstrip payload = response.lstrip
@ -189,10 +250,13 @@ module Google
## ##
# Convert a single batched response into a BatchedCallResponse object. # Convert a single batched response into a BatchedCallResponse object.
# #
# @param [Google::APIClient::Reference] response: # @api private
#
# @param [String] call_response
# the request to deserialize. # the request to deserialize.
# #
# @return [BatchedCallResponse] The parsed and converted response. # @return [Google::APIClient::BatchedCallResponse]
# the parsed and converted response.
def deserialize_call_response(call_response) def deserialize_call_response(call_response)
outer_headers, outer_body = split_headers_and_body(call_response) outer_headers, outer_body = split_headers_and_body(call_response)
status_line, payload = outer_body.split("\n", 2) status_line, payload = outer_body.split("\n", 2)
@ -205,13 +269,16 @@ module Google
end end
## ##
# Convert a single batched call into a string. # Serialize a single batched call for assembling the multipart message
# #
# @param [Google::APIClient::Reference] call: the call to serialize. # @api private
# #
# @return [StringIO] The request as a string in application/http format. # @param [Google::APIClient::Request] call
# the call to serialize.
#
# @return [Faraday::UploadIO]
# the serialized request
def serialize_call(call_id, call) def serialize_call(call_id, call)
call.api_client = self.api_client
method, uri, headers, body = call.to_http_request method, uri, headers, body = call.to_http_request
request = "#{method.to_s.upcase} #{Addressable::URI.parse(uri).path} HTTP/1.1" request = "#{method.to_s.upcase} #{Addressable::URI.parse(uri).path} HTTP/1.1"
headers.each do |header, value| headers.each do |header, value|
@ -232,7 +299,10 @@ module Google
## ##
# Convert an id to a Content-ID header value. # Convert an id to a Content-ID header value.
# #
# @param [String] call_id: identifier of individual call. # @api private
#
# @param [String] call_id
# identifier of individual call.
# #
# @return [String] # @return [String]
# A Content-ID header with the call_id encoded into it. A UUID is # A Content-ID header with the call_id encoded into it. A UUID is

View File

@ -20,8 +20,42 @@ require 'compat/multi_json'
module Google module Google
class APIClient class APIClient
## ##
# Manages the persistence of client configuration data and secrets. # Manages the persistence of client configuration data and secrets. Format
# inspired by the Google API Python client.
#
# @see https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
#
# @example
# {
# "web": {
# "client_id": "asdfjasdljfasdkjf",
# "client_secret": "1912308409123890",
# "redirect_uris": ["https://www.example.com/oauth2callback"],
# "auth_uri": "https://accounts.google.com/o/oauth2/auth",
# "token_uri": "https://accounts.google.com/o/oauth2/token"
# }
# }
#
# @example
# {
# "installed": {
# "client_id": "837647042410-75ifg...usercontent.com",
# "client_secret":"asdlkfjaskd",
# "redirect_uris": ["http://localhost", "urn:ietf:oauth:2.0:oob"],
# "auth_uri": "https://accounts.google.com/o/oauth2/auth",
# "token_uri": "https://accounts.google.com/o/oauth2/token"
# }
# }
class ClientSecrets class ClientSecrets
##
# Reads client configuration from a file
#
# @param [String] filename
# Path to file to load
#
# @return [Google::APIClient::ClientSecrets]
# OAuth client settings
def self.load(filename=nil) def self.load(filename=nil)
if filename && File.directory?(filename) if filename && File.directory?(filename)
search_path = File.expand_path(filename) search_path = File.expand_path(filename)
@ -44,6 +78,11 @@ module Google
return self.new(data) return self.new(data)
end end
##
# Intialize OAuth client settings.
#
# @param [Hash] options
# Parsed client secrets files
def initialize(options={}) def initialize(options={})
# Client auth configuration # Client auth configuration
@flow = options[:flow] || options.keys.first.to_s || 'web' @flow = options[:flow] || options.keys.first.to_s || 'web'
@ -77,6 +116,11 @@ module Google
:refresh_token, :id_token, :expires_in, :expires_at, :issued_at :refresh_token, :id_token, :expires_in, :expires_at, :issued_at
) )
##
# Serialize back to the original JSON form
#
# @return [String]
# JSON
def to_json def to_json
return MultiJson.dump({ return MultiJson.dump({
self.flow => ({ self.flow => ({

View File

@ -29,13 +29,9 @@ module Google
## ##
# Creates a description of a particular version of a service. # Creates a description of a particular version of a service.
# #
# @param [String] api # @param [String] document_base
# The identifier for the service. Note that while this frequently # Base URI for the service
# matches the first segment of all of the service's RPC names, this # @param [Hash] discovery_document
# should not be assumed. There is no requirement that these match.
# @param [String] version
# The identifier for the service version.
# @param [Hash] api_description
# The section of the discovery document that applies to this service # The section of the discovery document that applies to this service
# version. # version.
# #
@ -159,7 +155,7 @@ module Google
## ##
# Updates the hierarchy of resources and methods with the new base. # Updates the hierarchy of resources and methods with the new base.
# #
# @param [Addressable::URI, #to_str, String] new_base # @param [Addressable::URI, #to_str, String] new_method_base
# The new base URI to use for the service. # The new base URI to use for the service.
def method_base=(new_method_base) def method_base=(new_method_base)
@method_base = Addressable::URI.parse(new_method_base) @method_base = Addressable::URI.parse(new_method_base)

View File

@ -34,7 +34,7 @@ module Google
# The base URI for the service. # The base URI for the service.
# @param [String] method_name # @param [String] method_name
# The identifier for the method. # The identifier for the method.
# @param [Hash] method_description # @param [Hash] discovery_document
# The section of the discovery document that applies to this method. # The section of the discovery document that applies to this method.
# #
# @return [Google::APIClient::Method] The constructed method object. # @return [Google::APIClient::Method] The constructed method object.
@ -74,7 +74,7 @@ module Google
## ##
# Updates the method with the new base. # Updates the method with the new base.
# #
# @param [Addressable::URI, #to_str, String] new_base # @param [Addressable::URI, #to_str, String] new_method_base
# The new base URI to use for the method. # The new base URI to use for the method.
def method_base=(new_method_base) def method_base=(new_method_base)
@method_base = Addressable::URI.parse(new_method_base) @method_base = Addressable::URI.parse(new_method_base)
@ -176,6 +176,7 @@ module Google
## ##
# Expands the method's URI template using a parameter list. # Expands the method's URI template using a parameter list.
# #
# @api private
# @param [Hash, Array] parameters # @param [Hash, Array] parameters
# The parameter list to use. # The parameter list to use.
# #
@ -214,6 +215,7 @@ module Google
## ##
# Generates an HTTP request for this method. # Generates an HTTP request for this method.
# #
# @api private
# @param [Hash, Array] parameters # @param [Hash, Array] parameters
# The parameters to send. # The parameters to send.
# @param [String, StringIO] body The body for the HTTP request. # @param [String, StringIO] body The body for the HTTP request.
@ -288,6 +290,7 @@ module Google
# Verifies that the parameters are valid for this method. Raises an # Verifies that the parameters are valid for this method. Raises an
# exception if validation fails. # exception if validation fails.
# #
# @api private
# @param [Hash, Array] parameters # @param [Hash, Array] parameters
# The parameters to verify. # The parameters to verify.
# #

View File

@ -28,11 +28,13 @@ module Google
## ##
# Creates a description of a particular version of a resource. # Creates a description of a particular version of a resource.
# #
# @param [Addressable::URI] base # @param [Google::APIClient::API] api
# The API this resource belongs to.
# @param [Addressable::URI] method_base
# The base URI for the service. # The base URI for the service.
# @param [String] resource_name # @param [String] resource_name
# The identifier for the resource. # The identifier for the resource.
# @param [Hash] resource_description # @param [Hash] discovery_document
# The section of the discovery document that applies to this resource. # The section of the discovery document that applies to this resource.
# #
# @return [Google::APIClient::Resource] The constructed resource object. # @return [Google::APIClient::Resource] The constructed resource object.
@ -78,7 +80,7 @@ module Google
## ##
# Updates the hierarchy of resources and methods with the new base. # Updates the hierarchy of resources and methods with the new base.
# #
# @param [Addressable::URI, #to_str, String] new_base # @param [Addressable::URI, #to_str, String] new_method_base
# The new base URI to use for the resource. # The new base URI to use for the resource.
def method_base=(new_method_base) def method_base=(new_method_base)
@method_base = Addressable::URI.parse(new_method_base) @method_base = Addressable::URI.parse(new_method_base)

View File

@ -27,6 +27,8 @@ require 'google/api_client/errors'
module Google module Google
class APIClient class APIClient
##
# @api private
module Schema module Schema
def self.parse(api, schema_data) def self.parse(api, schema_data)
# This method is super-long, but hard to break up due to the # This method is super-long, but hard to break up due to the

View File

@ -24,6 +24,7 @@ module Google
class UploadIO < Faraday::UploadIO class UploadIO < Faraday::UploadIO
## ##
# Get the length of the stream # Get the length of the stream
#
# @return [Integer] # @return [Integer]
# Length of stream, in bytes # Length of stream, in bytes
def length def length
@ -40,12 +41,8 @@ module Google
## ##
# Creates a new uploader. # Creates a new uploader.
# #
# @param [Google::APIClient::Result] result # @param [Hash] options
# Result of the initial request that started the upload # Request options
# @param [Google::APIClient::UploadIO] media
# Media to upload
# @param [String] location
# URL to upload to
def initialize(options={}) def initialize(options={})
super options super options
self.uri = options[:uri] self.uri = options[:uri]
@ -58,6 +55,8 @@ module Google
## ##
# Sends all remaining chunks to the server # Sends all remaining chunks to the server
# #
# @deprecated Pass the instance to {Google::APIClient#execute} instead
#
# @param [Google::APIClient] api_client # @param [Google::APIClient] api_client
# API Client instance to use for sending # API Client instance to use for sending
def send_all(api_client) def send_all(api_client)
@ -73,6 +72,8 @@ module Google
## ##
# Sends the next chunk to the server # Sends the next chunk to the server
# #
# @deprecated Pass the instance to {Google::APIClient#execute} instead
#
# @param [Google::APIClient] api_client # @param [Google::APIClient] api_client
# API Client instance to use for sending # API Client instance to use for sending
def send_chunk(api_client) def send_chunk(api_client)
@ -98,6 +99,13 @@ module Google
return @expired return @expired
end end
##
# Convert to an HTTP request. Returns components in order of method, URI,
# request headers, and body
#
# @api private
#
# @return [Array<(Symbol, Addressable::URI, Hash, [#read,#to_str])>]
def to_http_request def to_http_request
if @complete if @complete
raise Google::APIClient::ClientError, "Upload already complete" raise Google::APIClient::ClientError, "Upload already complete"
@ -121,16 +129,17 @@ module Google
super super
end end
def to_hash
super.merge(:offset => @offset)
end
## ##
# Check the result from the server, updating the offset and/or location # Check the result from the server, updating the offset and/or location
# if available. # if available.
# #
# @param [Faraday::Response] r # @api private
# Result of a chunk upload or range query #
# @param [Faraday::Response] response
# HTTP response
#
# @return [Google::APIClient::Result]
# Processed API response
def process_http_response(response) def process_http_response(response)
case response.status case response.status
when 200...299 when 200...299
@ -152,6 +161,11 @@ module Google
end end
return Google::APIClient::Result.new(self, response) return Google::APIClient::Result.new(self, response)
end end
def to_hash
super.merge(:offset => @offset)
end
end end
end end
end end

View File

@ -23,19 +23,43 @@ require 'google/api_client/discovery'
module Google module Google
class APIClient class APIClient
##
# Represents an API request.
class Request class Request
MULTIPART_BOUNDARY = "-----------RubyApiMultipartPost".freeze MULTIPART_BOUNDARY = "-----------RubyApiMultipartPost".freeze
attr_reader :parameters, :headers attr_reader :parameters, :headers, :api_method
attr_accessor :api_client, :connection, :api_method, :version ,:media, :authorization, :authenticated, :body attr_accessor :connection, :media, :authorization, :authenticated, :body
##
# Build a request
#
# @param [Hash] options
# @option options [Hash, Array] :parameters
# Request parameters for the API method.
# @option options [Google::APIClient::Method] :api_method
# API method to invoke. Either :api_method or :uri must be specified
# @option options [TrueClass, FalseClass] :authenticated
# True if request should include credentials. Implicitly true if
# unspecified and :authorization present
# @option options [#generate_signed_request] :authorization
# OAuth credentials
# @option options [Google::APIClient::UploadIO] :media
# File to upload, if media upload request
# @option options [#to_json, #to_hash] :body_object
# Main body of the API request. Typically hash or object that can
# be serialized to JSON
# @option options [#read, #to_str] :body
# Raw body to send in POST/PUT requests
# @option options [String, Addressable::URI] :uri
# URI to request. Either :api_method or :uri must be specified
# @option options [String, Symbol] :http_method
# HTTP method when requesting a URI
def initialize(options={}) def initialize(options={})
@parameters = Hash[options[:parameters] || {}] @parameters = Hash[options[:parameters] || {}]
@headers = Faraday::Utils::Headers.new @headers = Faraday::Utils::Headers.new
self.api_client = options[:api_client]
self.headers.merge!(options[:headers]) unless options[:headers].nil? self.headers.merge!(options[:headers]) unless options[:headers].nil?
self.api_method = options[:api_method] self.api_method = options[:api_method]
self.version = options[:version]
self.authenticated = options[:authenticated] self.authenticated = options[:authenticated]
self.authorization = options[:authorization] self.authorization = options[:authorization]
@ -80,6 +104,15 @@ module Google
end end
end end
def api_method=(new_api_method)
if new_api_method.nil? || new_api_method.kind_of?(Google::APIClient::Method)
@api_method = new_api_method
else
raise TypeError,
"Expected Google::APIClient::Method, got #{new_api_method.class}."
end
end
def uri def uri
return @uri ||= self.api_method.generate_uri(self.parameters) return @uri ||= self.api_method.generate_uri(self.parameters)
end end
@ -89,18 +122,37 @@ module Google
@parameters.update(@uri.query_values) unless @uri.query_values.nil? @parameters.update(@uri.query_values) unless @uri.query_values.nil?
end end
# Transmits the request with the given connection
#
# @api private
#
# @param [Faraday::Connection] connection
# the connection to transmit with
#
# @return [Google::APIClient::Result]
# result of API request
def send(connection) def send(connection)
response = connection.app.call(self.to_env(connection)) http_response = connection.app.call(self.to_env(connection))
self.process_http_response(response) result = self.process_http_response(http_response)
# Resumamble slightly different than other upload protocols in that it requires at least
# 2 requests.
if self.upload_type == 'resumable'
upload = result.resumable_upload
unless upload.complete?
result = upload.send(connection)
end
end
return result
end end
# Convert to an HTTP request. Returns components in order of method, URI,
# request headers, and body
#
# @api private
#
# @return [Array<(Symbol, Addressable::URI, Hash, [#read,#to_str])>]
def to_http_request def to_http_request
if self.api_client
self.headers['User-Agent'] ||= '' + self.api_client.user_agent unless self.api_client.user_agent.nil?
self.parameters['key'] ||= self.api_client.key unless self.api_client.key.nil?
self.parameters['userIp'] ||= self.api_client.user_ip unless self.api_client.user_ip.nil?
self.api_method = self.api_client.resolve_method(self.api_method, self.version) unless self.api_method.nil?
end
request = ( request = (
if self.uri if self.uri
unless self.parameters.empty? unless self.parameters.empty?
@ -112,6 +164,10 @@ module Google
end) end)
end end
##
# Hashified verison of the API request
#
# @return [Hash]
def to_hash def to_hash
options = {} options = {}
if self.api_method if self.api_method
@ -130,6 +186,17 @@ module Google
return options return options
end end
##
# Prepares the request for execution, building a hash of parts
# suitable for sending to Faraday::Connection.
#
# @api private
#
# @param [Faraday::Connection] connection
# Connection for building the request
#
# @return [Hash]
# Encoded request
def to_env(connection) def to_env(connection)
method, uri, headers, body = self.to_http_request method, uri, headers, body = self.to_http_request
http_request = connection.build_request(method) do |req| http_request = connection.build_request(method) do |req|
@ -148,12 +215,37 @@ module Google
request_env = http_request.to_env(connection) request_env = http_request.to_env(connection)
end end
##
# Convert HTTP response to an API Result
#
# @api private
#
# @param [Faraday::Response] response
# HTTP response
#
# @return [Google::APIClient::Result]
# Processed API response
def process_http_response(response) def process_http_response(response)
Result.new(self, response) Result.new(self, response)
end end
protected protected
##
# Adjust headers & body for media uploads
#
# @api private
#
# @param [Hash] options
# @option options [Hash, Array] :parameters
# Request parameters for the API method.
# @option options [Google::APIClient::UploadIO] :media
# File to upload, if media upload request
# @option options [#to_json, #to_hash] :body_object
# Main body of the API request. Typically hash or object that can
# be serialized to JSON
# @option options [#read, #to_str] :body
# Raw body to send in POST/PUT requests
def initialize_media_upload(options) def initialize_media_upload(options)
self.media = options[:media] self.media = options[:media]
case self.upload_type case self.upload_type
@ -182,6 +274,17 @@ module Google
end end
end end
##
# Assemble a multipart message from a set of parts
#
# @api private
#
# @param [Array<[#read,#to_str]>] parts
# Array of parts to encode.
# @param [String] mime_type
# MIME type of the message
# @param [String] boundary
# Boundary for separating each part of the message
def build_multipart(parts, mime_type = 'multipart/related', boundary = MULTIPART_BOUNDARY) def build_multipart(parts, mime_type = 'multipart/related', boundary = MULTIPART_BOUNDARY)
env = { env = {
:request_headers => {'Content-Type' => "#{mime_type};boundary=#{boundary}"}, :request_headers => {'Content-Type' => "#{mime_type};boundary=#{boundary}"},
@ -192,6 +295,16 @@ module Google
self.headers.update(env[:request_headers]) self.headers.update(env[:request_headers])
end end
##
# Serialize body object to JSON
#
# @api private
#
# @param [#to_json,#to_hash] body
# object to serialize
#
# @return [String]
# JSON
def serialize_body(body) def serialize_body(body)
return body.to_json if body.respond_to?(:to_json) return body.to_json if body.respond_to?(:to_json)
return MultiJson.dump(options[:body_object].to_hash) if body.respond_to?(:to_hash) return MultiJson.dump(options[:body_object].to_hash) if body.respond_to?(:to_hash)

View File

@ -18,27 +18,32 @@ module Google
## ##
# This class wraps a result returned by an API call. # This class wraps a result returned by an API call.
class Result class Result
def initialize(reference, response) extend Forwardable
@reference = reference
##
# Init the result
#
# @param [Google::APIClient::Request] request
# The original request
# @param [Faraday::Response] response
# Raw HTTP Response
def initialize(request, response)
@request = request
@response = response @response = response
@media_upload = reference if reference.kind_of?(ResumableUpload) @media_upload = reference if reference.kind_of?(ResumableUpload)
end end
attr_reader :reference attr_reader :request
attr_reader :response attr_reader :response
alias_method :reference, :request # For compatibility with pre-beta clients
def status # @!attribute [r] status
return @response.status # @return [Integer] HTTP status code
end # @!attribute [r] headers
# @return [Hash] HTTP response headers
def headers # @!attribute [r] body
return @response.headers # @return [String] HTTP response body
end def_delegators :@response, :status, :headers, :body
def body
return @response.body
end
def resumable_upload def resumable_upload
@media_upload ||= ( @media_upload ||= (
@ -50,6 +55,11 @@ module Google
) )
end end
##
# Get the content type of the response
#
# @return [String]
# Value of content-type header
def media_type def media_type
_, content_type = self.headers.detect do |h, v| _, content_type = self.headers.detect do |h, v|
h.downcase == 'Content-Type'.downcase h.downcase == 'Content-Type'.downcase
@ -57,14 +67,29 @@ module Google
content_type[/^([^;]*);?.*$/, 1].strip.downcase content_type[/^([^;]*);?.*$/, 1].strip.downcase
end end
##
# Check if request failed
#
# @return [TrueClass, FalseClass]
# true if result of operation is an error
def error? def error?
return self.response.status >= 400 return self.response.status >= 400
end end
##
# Check if request was successful
#
# @return [TrueClass, FalseClass]
# true if result of operation was successful
def success? def success?
return !self.error? return !self.error?
end end
##
# Extracts error messages from the response body
#
# @return [String]
# error message, if available
def error_message def error_message
if self.data? if self.data?
if self.data.respond_to?(:error) && if self.data.respond_to?(:error) &&
@ -79,10 +104,20 @@ module Google
return self.body return self.body
end end
##
# Check for parsable data in response
#
# @return [TrueClass, FalseClass]
# true if body can be parsed
def data? def data?
self.media_type == 'application/json' self.media_type == 'application/json'
end end
##
# Return parsed version of the response body.
#
# @return [Object, Hash, String]
# Object if body parsable from API schema, Hash if JSON, raw body if unable to parse
def data def data
return @data ||= (begin return @data ||= (begin
media_type = self.media_type media_type = self.media_type
@ -96,10 +131,10 @@ module Google
raise ArgumentError, raise ArgumentError,
"Content-Type not supported for parsing: #{media_type}" "Content-Type not supported for parsing: #{media_type}"
end end
if @reference.api_method && @reference.api_method.response_schema if @request.api_method && @request.api_method.response_schema
# Automatically parse using the schema designated for the # Automatically parse using the schema designated for the
# response of this API method. # response of this API method.
data = @reference.api_method.response_schema.new(data) data = @request.api_method.response_schema.new(data)
data data
else else
# Otherwise, return the raw unparsed value. # Otherwise, return the raw unparsed value.
@ -109,14 +144,11 @@ module Google
end) end)
end end
def pagination_type ##
return :token # Get the token used for requesting the next page of data
end #
# @return [String]
def page_token_param # next page token
return "pageToken"
end
def next_page_token def next_page_token
if self.data.respond_to?(:next_page_token) if self.data.respond_to?(:next_page_token)
return self.data.next_page_token return self.data.next_page_token
@ -127,6 +159,11 @@ module Google
end end
end end
##
# Build a request for fetching the next page of data
#
# @return [Google::APIClient::Request]
# API request for retrieving next page
def next_page def next_page
merged_parameters = Hash[self.reference.parameters].merge({ merged_parameters = Hash[self.reference.parameters].merge({
self.page_token_param => self.next_page_token self.page_token_param => self.next_page_token
@ -139,6 +176,11 @@ module Google
) )
end end
##
# Get the token used for requesting the previous page of data
#
# @return [String]
# previous page token
def prev_page_token def prev_page_token
if self.data.respond_to?(:prev_page_token) if self.data.respond_to?(:prev_page_token)
return self.data.prev_page_token return self.data.prev_page_token
@ -149,6 +191,11 @@ module Google
end end
end end
##
# Build a request for fetching the previous page of data
#
# @return [Google::APIClient::Request]
# API request for retrieving previous page
def prev_page def prev_page
merged_parameters = Hash[self.reference.parameters].merge({ merged_parameters = Hash[self.reference.parameters].merge({
self.page_token_param => self.prev_page_token self.page_token_param => self.prev_page_token
@ -160,6 +207,15 @@ module Google
Hash[self.reference].merge(:parameters => merged_parameters) Hash[self.reference].merge(:parameters => merged_parameters)
) )
end end
def pagination_type
return :token
end
def page_token_param
return "pageToken"
end
end end
end end
end end

View File

@ -554,19 +554,6 @@ describe Google::APIClient do
CLIENT.discovered_api('moderator').batch_path.should_not be_nil CLIENT.discovered_api('moderator').batch_path.should_not be_nil
end end
it 'should generate requests against the correct URIs' do
conn = stub_connection do |stub|
stub.get('/moderator/v1/profiles/@me') do |env|
end
end
request = CLIENT.execute(
:api_method => 'moderator.profiles.get',
:authenticated => false,
:connection => conn
)
conn.verify
end
it 'should generate requests against the correct URIs' do it 'should generate requests against the correct URIs' do
conn = stub_connection do |stub| conn = stub_connection do |stub|
stub.get('/moderator/v1/profiles/@me') do |env| stub.get('/moderator/v1/profiles/@me') do |env|
@ -582,7 +569,7 @@ describe Google::APIClient do
it 'should not be able to execute requests without authorization' do it 'should not be able to execute requests without authorization' do
result = CLIENT.execute( result = CLIENT.execute(
'moderator.profiles.get', @moderator.profiles.get,
{}, {},
'', '',
[], [],
@ -622,19 +609,6 @@ describe Google::APIClient do
CLIENT.discovered_method('adsense.bogus', 'adsense').should == nil CLIENT.discovered_method('adsense.bogus', 'adsense').should == nil
end end
it 'should generate requests against the correct URIs' do
conn = stub_connection do |stub|
stub.get('/adsense/v1/adclients') do |env|
end
end
request = CLIENT.execute(
:api_method => 'adsense.adclients.list',
:authenticated => false,
:connection => conn
)
conn.verify
end
it 'should generate requests against the correct URIs' do it 'should generate requests against the correct URIs' do
conn = stub_connection do |stub| conn = stub_connection do |stub|
stub.get('/adsense/v1/adclients') do |env| stub.get('/adsense/v1/adclients') do |env|
@ -650,7 +624,7 @@ describe Google::APIClient do
it 'should not be able to execute requests without authorization' do it 'should not be able to execute requests without authorization' do
result = CLIENT.execute( result = CLIENT.execute(
:api_method => 'adsense.adclients.list', :api_method => @adsense.adclients.list,
:authenticated => false :authenticated => false
) )
result.response.status.should == 401 result.response.status.should == 401