From 4d3c1801b7dccf5f2938b2a9ddbbb88da0e32e96 Mon Sep 17 00:00:00 2001 From: Steven Bazyl Date: Fri, 28 Sep 2012 12:07:11 -0700 Subject: [PATCH] Mostly doc updates, +remove support for method as string --- lib/google/api_client.rb | 48 ++----- lib/google/api_client/batch.rb | 124 ++++++++++++++---- lib/google/api_client/client_secrets.rb | 46 ++++++- lib/google/api_client/discovery/api.rb | 12 +- lib/google/api_client/discovery/method.rb | 7 +- lib/google/api_client/discovery/resource.rb | 8 +- lib/google/api_client/discovery/schema.rb | 2 + lib/google/api_client/media.rb | 40 ++++-- lib/google/api_client/reference.rb | 137 ++++++++++++++++++-- lib/google/api_client/result.rb | 108 +++++++++++---- spec/google/api_client/discovery_spec.rb | 30 +---- 11 files changed, 404 insertions(+), 158 deletions(-) diff --git a/lib/google/api_client.rb b/lib/google/api_client.rb index 4a9678d10..4856bde04 100644 --- a/lib/google/api_client.rb +++ b/lib/google/api_client.rb @@ -341,7 +341,7 @@ module Google # 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 API the method is within. + # @param [String, Symbol] api The API the method is within. # @param [String] version The desired version of the API. # # @return [Google::APIClient::Method] The method object. @@ -439,7 +439,7 @@ module Google ## # 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. # @option options [Hash, Array] :parameters # 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 # 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. # - (Hash, Array) parameters: # The parameters to send to the method. @@ -502,8 +502,9 @@ module Google # result = client.execute(batch_request) # # @example + # plus = client.discovered_api('plus') # result = client.execute( - # :api_method => 'plus.activities.list', + # :api_method => plus.activities.list, # :parameters => {'collection' => 'public', 'userId' => 'me'} # ) # @@ -532,17 +533,14 @@ module Google request = self.generate_request(options) 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 request.authorization = options[:authorization] || self.authorization unless options[:authenticated] == false result = request.send(connection) - - if request.upload_type == 'resumable' - upload = result.resumable_upload - unless upload.complete? - result = upload.send(connection) - end - end return result end @@ -571,38 +569,12 @@ module Google return result 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 ## # Resolves a URI template against the client's configured base. # + # @api private # @param [String, Addressable::URI, Addressable::Template] template # The template to resolve. # @param [Hash] mapping The mapping that corresponds to the template. diff --git a/lib/google/api_client/batch.rb b/lib/google/api_client/batch.rb index 789285e51..03804821f 100644 --- a/lib/google/api_client/batch.rb +++ b/lib/google/api_client/batch.rb @@ -19,18 +19,48 @@ require 'uuidtools' module Google class APIClient + ## # Helper class to contain a response to an individual batched call. + # + # @api private class BatchedCallResponse 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) @call_id, @status, @headers, @body = call_id, status, headers, body end end - ## # 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 BATCH_BOUNDARY = "-----------RubyApiBatchRequest".freeze @@ -40,12 +70,16 @@ module Google # Creates a new batch request. # # @param [Hash] options - # Set of options for this request + # Set of options for this request. # @param [Proc] block # Callback for every call's response. Won't be called if a call defined # 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) @calls = [] @global_callback = block if block_given? @@ -66,11 +100,18 @@ module Google # automatically be generated, avoiding collisions. If duplicate call IDs # are provided, an error will be thrown. # - # @param [Hash, Google::APIClient::Request] call: the call to be added. - # @param [String] call_id: the ID to be used for this call. Must be unique - # @param [Proc] block: callback for this call's response. + # @param [Hash, Google::APIClient::Request] call + # the call to be added. + # @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) unless call.kind_of?(Google::APIClient::Reference) call = Google::APIClient::Reference.new(call) @@ -88,7 +129,10 @@ module Google ## # 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) content_type = find_header('Content-Type', response.headers) boundary = /.*boundary=(.+)/.match(content_type)[1] @@ -105,7 +149,10 @@ module Google ## # 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 if @calls.nil? || @calls.empty? 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. # - # @param [String] name: The name of the header to find. - # @param [Hash] headers: The hash of headers and their values. + # @api private # - # @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) _, header = headers.detect do |h, v| h.downcase == name.downcase @@ -135,7 +187,10 @@ module Google ## # 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 @last_auto_id += 1 while @calls.assoc(@last_auto_id) @@ -144,15 +199,17 @@ module Google return @last_auto_id.to_s end - - ## # Convert a Content-ID header value to an id. Presumes the Content-ID # 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) if !header.start_with?('<') || !header.end_with?('>') || !header.include?('+') @@ -166,9 +223,13 @@ module Google ## # Auxiliary method to split the headers from the body in an HTTP response. # - # @param [String] response: the response to parse. + # @api private # - # @return [Array, String] The headers and the body, separately. + # @param [String] response + # the response to parse. + # + # @return [Array, String] + # the headers and the body, separately. def split_headers_and_body(response) headers = {} payload = response.lstrip @@ -189,10 +250,13 @@ module Google ## # Convert a single batched response into a BatchedCallResponse object. # - # @param [Google::APIClient::Reference] response: + # @api private + # + # @param [String] call_response # 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) outer_headers, outer_body = split_headers_and_body(call_response) status_line, payload = outer_body.split("\n", 2) @@ -205,13 +269,16 @@ module Google 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) - call.api_client = self.api_client method, uri, headers, body = call.to_http_request request = "#{method.to_s.upcase} #{Addressable::URI.parse(uri).path} HTTP/1.1" headers.each do |header, value| @@ -232,7 +299,10 @@ module Google ## # 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] # A Content-ID header with the call_id encoded into it. A UUID is diff --git a/lib/google/api_client/client_secrets.rb b/lib/google/api_client/client_secrets.rb index 27b71087c..c682d26db 100644 --- a/lib/google/api_client/client_secrets.rb +++ b/lib/google/api_client/client_secrets.rb @@ -20,8 +20,42 @@ require 'compat/multi_json' module Google 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 + + ## + # 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) if filename && File.directory?(filename) search_path = File.expand_path(filename) @@ -44,6 +78,11 @@ module Google return self.new(data) end + ## + # Intialize OAuth client settings. + # + # @param [Hash] options + # Parsed client secrets files def initialize(options={}) # Client auth configuration @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 ) + ## + # Serialize back to the original JSON form + # + # @return [String] + # JSON def to_json return MultiJson.dump({ self.flow => ({ diff --git a/lib/google/api_client/discovery/api.rb b/lib/google/api_client/discovery/api.rb index 58346d386..6d9b07d2a 100644 --- a/lib/google/api_client/discovery/api.rb +++ b/lib/google/api_client/discovery/api.rb @@ -29,13 +29,9 @@ module Google ## # Creates a description of a particular version of a service. # - # @param [String] api - # The identifier for the service. Note that while this frequently - # matches the first segment of all of the service's RPC names, this - # should not be assumed. There is no requirement that these match. - # @param [String] version - # The identifier for the service version. - # @param [Hash] api_description + # @param [String] document_base + # Base URI for the service + # @param [Hash] discovery_document # The section of the discovery document that applies to this service # version. # @@ -159,7 +155,7 @@ module Google ## # 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. def method_base=(new_method_base) @method_base = Addressable::URI.parse(new_method_base) diff --git a/lib/google/api_client/discovery/method.rb b/lib/google/api_client/discovery/method.rb index af6013d0b..057a1ef0c 100644 --- a/lib/google/api_client/discovery/method.rb +++ b/lib/google/api_client/discovery/method.rb @@ -34,7 +34,7 @@ module Google # The base URI for the service. # @param [String] method_name # The identifier for the method. - # @param [Hash] method_description + # @param [Hash] discovery_document # The section of the discovery document that applies to this method. # # @return [Google::APIClient::Method] The constructed method object. @@ -74,7 +74,7 @@ module Google ## # 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. def method_base=(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. # + # @api private # @param [Hash, Array] parameters # The parameter list to use. # @@ -214,6 +215,7 @@ module Google ## # Generates an HTTP request for this method. # + # @api private # @param [Hash, Array] parameters # The parameters to send. # @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 # exception if validation fails. # + # @api private # @param [Hash, Array] parameters # The parameters to verify. # diff --git a/lib/google/api_client/discovery/resource.rb b/lib/google/api_client/discovery/resource.rb index d8a3affae..71515a513 100644 --- a/lib/google/api_client/discovery/resource.rb +++ b/lib/google/api_client/discovery/resource.rb @@ -28,11 +28,13 @@ module Google ## # 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. # @param [String] resource_name # The identifier for the resource. - # @param [Hash] resource_description + # @param [Hash] discovery_document # The section of the discovery document that applies to this resource. # # @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. # - # @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. def method_base=(new_method_base) @method_base = Addressable::URI.parse(new_method_base) diff --git a/lib/google/api_client/discovery/schema.rb b/lib/google/api_client/discovery/schema.rb index a62e6f4d6..34755190f 100644 --- a/lib/google/api_client/discovery/schema.rb +++ b/lib/google/api_client/discovery/schema.rb @@ -27,6 +27,8 @@ require 'google/api_client/errors' module Google class APIClient + ## + # @api private module Schema def self.parse(api, schema_data) # This method is super-long, but hard to break up due to the diff --git a/lib/google/api_client/media.rb b/lib/google/api_client/media.rb index 826637948..312429ea0 100644 --- a/lib/google/api_client/media.rb +++ b/lib/google/api_client/media.rb @@ -24,6 +24,7 @@ module Google class UploadIO < Faraday::UploadIO ## # Get the length of the stream + # # @return [Integer] # Length of stream, in bytes def length @@ -40,12 +41,8 @@ module Google ## # Creates a new uploader. # - # @param [Google::APIClient::Result] result - # Result of the initial request that started the upload - # @param [Google::APIClient::UploadIO] media - # Media to upload - # @param [String] location - # URL to upload to + # @param [Hash] options + # Request options def initialize(options={}) super options self.uri = options[:uri] @@ -58,6 +55,8 @@ module Google ## # Sends all remaining chunks to the server # + # @deprecated Pass the instance to {Google::APIClient#execute} instead + # # @param [Google::APIClient] api_client # API Client instance to use for sending def send_all(api_client) @@ -73,6 +72,8 @@ module Google ## # Sends the next chunk to the server # + # @deprecated Pass the instance to {Google::APIClient#execute} instead + # # @param [Google::APIClient] api_client # API Client instance to use for sending def send_chunk(api_client) @@ -98,6 +99,13 @@ module Google return @expired 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 if @complete raise Google::APIClient::ClientError, "Upload already complete" @@ -121,16 +129,17 @@ module Google super end - def to_hash - super.merge(:offset => @offset) - end - ## # Check the result from the server, updating the offset and/or location # if available. # - # @param [Faraday::Response] r - # Result of a chunk upload or range query + # @api private + # + # @param [Faraday::Response] response + # HTTP response + # + # @return [Google::APIClient::Result] + # Processed API response def process_http_response(response) case response.status when 200...299 @@ -151,7 +160,12 @@ module Google @offset = nil end return Google::APIClient::Result.new(self, response) - end + end + + def to_hash + super.merge(:offset => @offset) + end + end end end \ No newline at end of file diff --git a/lib/google/api_client/reference.rb b/lib/google/api_client/reference.rb index a30a7db04..db2e10724 100644 --- a/lib/google/api_client/reference.rb +++ b/lib/google/api_client/reference.rb @@ -23,19 +23,43 @@ require 'google/api_client/discovery' module Google class APIClient + ## + # Represents an API request. class Request MULTIPART_BOUNDARY = "-----------RubyApiMultipartPost".freeze - attr_reader :parameters, :headers - attr_accessor :api_client, :connection, :api_method, :version ,:media, :authorization, :authenticated, :body + attr_reader :parameters, :headers, :api_method + 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={}) @parameters = Hash[options[:parameters] || {}] @headers = Faraday::Utils::Headers.new - self.api_client = options[:api_client] self.headers.merge!(options[:headers]) unless options[:headers].nil? self.api_method = options[:api_method] - self.version = options[:version] self.authenticated = options[:authenticated] self.authorization = options[:authorization] @@ -80,6 +104,15 @@ module Google 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 return @uri ||= self.api_method.generate_uri(self.parameters) end @@ -89,18 +122,37 @@ module Google @parameters.update(@uri.query_values) unless @uri.query_values.nil? 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) - response = connection.app.call(self.to_env(connection)) - self.process_http_response(response) + http_response = connection.app.call(self.to_env(connection)) + 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 + # 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 - 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 = ( if self.uri unless self.parameters.empty? @@ -112,6 +164,10 @@ module Google end) end + ## + # Hashified verison of the API request + # + # @return [Hash] def to_hash options = {} if self.api_method @@ -130,6 +186,17 @@ module Google return options 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) method, uri, headers, body = self.to_http_request http_request = connection.build_request(method) do |req| @@ -148,12 +215,37 @@ module Google request_env = http_request.to_env(connection) 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) Result.new(self, response) end 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) self.media = options[:media] case self.upload_type @@ -182,6 +274,17 @@ module Google 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) env = { :request_headers => {'Content-Type' => "#{mime_type};boundary=#{boundary}"}, @@ -192,6 +295,16 @@ module Google self.headers.update(env[:request_headers]) end + ## + # Serialize body object to JSON + # + # @api private + # + # @param [#to_json,#to_hash] body + # object to serialize + # + # @return [String] + # JSON def serialize_body(body) return body.to_json if body.respond_to?(:to_json) return MultiJson.dump(options[:body_object].to_hash) if body.respond_to?(:to_hash) diff --git a/lib/google/api_client/result.rb b/lib/google/api_client/result.rb index 32c22120e..97d5bcec5 100644 --- a/lib/google/api_client/result.rb +++ b/lib/google/api_client/result.rb @@ -18,27 +18,32 @@ module Google ## # This class wraps a result returned by an API call. class Result - def initialize(reference, response) - @reference = reference + extend Forwardable + + ## + # 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 @media_upload = reference if reference.kind_of?(ResumableUpload) end - attr_reader :reference - + attr_reader :request attr_reader :response + alias_method :reference, :request # For compatibility with pre-beta clients - def status - return @response.status - end - - def headers - return @response.headers - end - - def body - return @response.body - end + # @!attribute [r] status + # @return [Integer] HTTP status code + # @!attribute [r] headers + # @return [Hash] HTTP response headers + # @!attribute [r] body + # @return [String] HTTP response body + def_delegators :@response, :status, :headers, :body def resumable_upload @media_upload ||= ( @@ -50,6 +55,11 @@ module Google ) end + ## + # Get the content type of the response + # + # @return [String] + # Value of content-type header def media_type _, content_type = self.headers.detect do |h, v| h.downcase == 'Content-Type'.downcase @@ -57,14 +67,29 @@ module Google content_type[/^([^;]*);?.*$/, 1].strip.downcase end + ## + # Check if request failed + # + # @return [TrueClass, FalseClass] + # true if result of operation is an error def error? return self.response.status >= 400 end + ## + # Check if request was successful + # + # @return [TrueClass, FalseClass] + # true if result of operation was successful def success? return !self.error? end + ## + # Extracts error messages from the response body + # + # @return [String] + # error message, if available def error_message if self.data? if self.data.respond_to?(:error) && @@ -78,11 +103,21 @@ module Google end return self.body end - + + ## + # Check for parsable data in response + # + # @return [TrueClass, FalseClass] + # true if body can be parsed def data? self.media_type == 'application/json' 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 return @data ||= (begin media_type = self.media_type @@ -96,10 +131,10 @@ module Google raise ArgumentError, "Content-Type not supported for parsing: #{media_type}" 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 # response of this API method. - data = @reference.api_method.response_schema.new(data) + data = @request.api_method.response_schema.new(data) data else # Otherwise, return the raw unparsed value. @@ -109,14 +144,11 @@ module Google end) end - def pagination_type - return :token - end - - def page_token_param - return "pageToken" - end - + ## + # Get the token used for requesting the next page of data + # + # @return [String] + # next page token def next_page_token if self.data.respond_to?(:next_page_token) return self.data.next_page_token @@ -127,6 +159,11 @@ module Google 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 merged_parameters = Hash[self.reference.parameters].merge({ self.page_token_param => self.next_page_token @@ -139,6 +176,11 @@ module Google ) end + ## + # Get the token used for requesting the previous page of data + # + # @return [String] + # previous page token def prev_page_token if self.data.respond_to?(:prev_page_token) return self.data.prev_page_token @@ -149,6 +191,11 @@ module Google 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 merged_parameters = Hash[self.reference.parameters].merge({ self.page_token_param => self.prev_page_token @@ -160,6 +207,15 @@ module Google Hash[self.reference].merge(:parameters => merged_parameters) ) end + + def pagination_type + return :token + end + + def page_token_param + return "pageToken" + end + end end end diff --git a/spec/google/api_client/discovery_spec.rb b/spec/google/api_client/discovery_spec.rb index 6db322d85..b459a617c 100644 --- a/spec/google/api_client/discovery_spec.rb +++ b/spec/google/api_client/discovery_spec.rb @@ -554,19 +554,6 @@ describe Google::APIClient do CLIENT.discovered_api('moderator').batch_path.should_not be_nil 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 conn = stub_connection do |stub| 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 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 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 conn = stub_connection do |stub| 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 result = CLIENT.execute( - :api_method => 'adsense.adclients.list', + :api_method => @adsense.adclients.list, :authenticated => false ) result.response.status.should == 401