| 
									
										
										
										
											2011-08-04 22:32:00 +00:00
										 |  |  | # Copyright 2010 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/api_client/errors' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module Google | 
					
						
							|  |  |  |   class APIClient | 
					
						
							|  |  |  |     ## | 
					
						
							|  |  |  |     # A method that has been described by a discovery document. | 
					
						
							|  |  |  |     class Method | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ## | 
					
						
							|  |  |  |       # Creates a description of a particular method. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @param [Addressable::URI] method_base | 
					
						
							|  |  |  |       #   The base URI for the service. | 
					
						
							|  |  |  |       # @param [String] method_name | 
					
						
							|  |  |  |       #   The identifier for the method. | 
					
						
							|  |  |  |       # @param [Hash] method_description | 
					
						
							|  |  |  |       #   The section of the discovery document that applies to this method. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @return [Google::APIClient::Method] The constructed method object. | 
					
						
							| 
									
										
										
										
											2011-08-05 03:36:36 +00:00
										 |  |  |       def initialize(api, method_base, method_name, discovery_document) | 
					
						
							|  |  |  |         @api = api | 
					
						
							| 
									
										
										
										
											2011-08-04 22:32:00 +00:00
										 |  |  |         @method_base = method_base | 
					
						
							|  |  |  |         @name = method_name | 
					
						
							|  |  |  |         @discovery_document = discovery_document | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ## | 
					
						
							|  |  |  |       # Returns the identifier for the method. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @return [String] The method identifier. | 
					
						
							|  |  |  |       attr_reader :name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ## | 
					
						
							|  |  |  |       # Returns the parsed section of the discovery document that applies to | 
					
						
							|  |  |  |       # this method. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @return [Hash] The method description. | 
					
						
							|  |  |  |       attr_reader :description | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ## | 
					
						
							|  |  |  |       # Returns the base URI for the method. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @return [Addressable::URI] | 
					
						
							|  |  |  |       #   The base URI that this method will be joined to. | 
					
						
							|  |  |  |       attr_reader :method_base | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ## | 
					
						
							|  |  |  |       # Updates the method with the new base. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @param [Addressable::URI, #to_str, String] new_base | 
					
						
							|  |  |  |       #   The new base URI to use for the method. | 
					
						
							|  |  |  |       def method_base=(new_method_base) | 
					
						
							|  |  |  |         @method_base = Addressable::URI.parse(new_method_base) | 
					
						
							|  |  |  |         @uri_template = nil | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ## | 
					
						
							|  |  |  |       # Returns the method ID. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @return [String] The method identifier. | 
					
						
							|  |  |  |       def id | 
					
						
							|  |  |  |         return @discovery_document['id'] | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ## | 
					
						
							|  |  |  |       # Returns the HTTP method or 'GET' if none is specified. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @return [String] The HTTP method that will be used in the request. | 
					
						
							|  |  |  |       def http_method | 
					
						
							|  |  |  |         return @discovery_document['httpMethod'] || 'GET' | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ## | 
					
						
							|  |  |  |       # Returns the URI template for the method.  A parameter list can be | 
					
						
							|  |  |  |       # used to expand this into a URI. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @return [Addressable::Template] The URI template. | 
					
						
							|  |  |  |       def uri_template | 
					
						
							|  |  |  |         # TODO(bobaman) We shouldn't be calling #to_s here, this should be | 
					
						
							|  |  |  |         # a join operation on a URI, but we have to treat these as Strings | 
					
						
							|  |  |  |         # because of the way the discovery document provides the URIs. | 
					
						
							|  |  |  |         # This should be fixed soon. | 
					
						
							|  |  |  |         return @uri_template ||= Addressable::Template.new( | 
					
						
							|  |  |  |           self.method_base + @discovery_document['path'] | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-05 03:36:36 +00:00
										 |  |  |       ## | 
					
						
							|  |  |  |       # Returns the Schema object for the method's request, if any. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @return [Google::APIClient::Schema] The request schema. | 
					
						
							|  |  |  |       def request_schema | 
					
						
							|  |  |  |         if @discovery_document['request'] | 
					
						
							|  |  |  |           schema_name = @discovery_document['request']['$ref'] | 
					
						
							|  |  |  |           return @api.schemas[schema_name] | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           return nil | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ## | 
					
						
							|  |  |  |       # Returns the Schema object for the method's response, if any. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @return [Google::APIClient::Schema] The response schema. | 
					
						
							|  |  |  |       def response_schema | 
					
						
							|  |  |  |         if @discovery_document['response'] | 
					
						
							|  |  |  |           schema_name = @discovery_document['response']['$ref'] | 
					
						
							|  |  |  |           return @api.schemas[schema_name] | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           return nil | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-04 22:32:00 +00:00
										 |  |  |       ## | 
					
						
							|  |  |  |       # Normalizes parameters, converting to the appropriate types. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @param [Hash, Array] parameters | 
					
						
							|  |  |  |       #   The parameters to normalize. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @return [Hash] The normalized parameters. | 
					
						
							|  |  |  |       def normalize_parameters(parameters={}) | 
					
						
							|  |  |  |         # Convert keys to Strings when appropriate | 
					
						
							|  |  |  |         if parameters.kind_of?(Hash) || parameters.kind_of?(Array) | 
					
						
							|  |  |  |           # Is a Hash or an Array a better return type?  Do we ever need to | 
					
						
							|  |  |  |           # worry about the same parameter being sent twice with different | 
					
						
							|  |  |  |           # values? | 
					
						
							|  |  |  |           parameters = parameters.inject({}) do |accu, (k, v)| | 
					
						
							|  |  |  |             k = k.to_s if k.kind_of?(Symbol) | 
					
						
							|  |  |  |             k = k.to_str if k.respond_to?(:to_str) | 
					
						
							|  |  |  |             unless k.kind_of?(String) | 
					
						
							|  |  |  |               raise TypeError, "Expected String, got #{k.class}." | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |             accu[k] = v | 
					
						
							|  |  |  |             accu | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           raise TypeError, | 
					
						
							|  |  |  |             "Expected Hash or Array, got #{parameters.class}." | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |         return parameters | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ## | 
					
						
							|  |  |  |       # Expands the method's URI template using a parameter list. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @param [Hash, Array] parameters | 
					
						
							|  |  |  |       #   The parameter list to use. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @return [Addressable::URI] The URI after expansion. | 
					
						
							|  |  |  |       def generate_uri(parameters={}) | 
					
						
							|  |  |  |         parameters = self.normalize_parameters(parameters) | 
					
						
							|  |  |  |         self.validate_parameters(parameters) | 
					
						
							|  |  |  |         template_variables = self.uri_template.variables | 
					
						
							|  |  |  |         uri = self.uri_template.expand(parameters) | 
					
						
							|  |  |  |         query_parameters = parameters.reject do |k, v| | 
					
						
							|  |  |  |           template_variables.include?(k) | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |         if query_parameters.size > 0
 | 
					
						
							|  |  |  |           uri.query_values = (uri.query_values || {}).merge(query_parameters) | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |         # Normalization is necessary because of undesirable percent-escaping | 
					
						
							|  |  |  |         # during URI template expansion | 
					
						
							|  |  |  |         return uri.normalize | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ## | 
					
						
							|  |  |  |       # Generates an HTTP request for this method. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @param [Hash, Array] parameters | 
					
						
							|  |  |  |       #   The parameters to send. | 
					
						
							|  |  |  |       # @param [String, StringIO] body The body for the HTTP request. | 
					
						
							|  |  |  |       # @param [Hash, Array] headers The HTTP headers for the request. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @return [Array] The generated HTTP request. | 
					
						
							|  |  |  |       def generate_request(parameters={}, body='', headers=[]) | 
					
						
							|  |  |  |         if body.respond_to?(:string) | 
					
						
							|  |  |  |           body = body.string | 
					
						
							|  |  |  |         elsif body.respond_to?(:to_str) | 
					
						
							|  |  |  |           body = body.to_str | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           raise TypeError, "Expected String or StringIO, got #{body.class}." | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |         if !headers.kind_of?(Array) && !headers.kind_of?(Hash) | 
					
						
							|  |  |  |           raise TypeError, "Expected Hash or Array, got #{headers.class}." | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |         method = self.http_method | 
					
						
							|  |  |  |         uri = self.generate_uri(parameters) | 
					
						
							|  |  |  |         headers = headers.to_a if headers.kind_of?(Hash) | 
					
						
							|  |  |  |         return [method, uri.to_str, headers, [body]] | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ## | 
					
						
							|  |  |  |       # Returns a <code>Hash</code> of the parameter descriptions for | 
					
						
							|  |  |  |       # this method. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @return [Hash] The parameter descriptions. | 
					
						
							|  |  |  |       def parameter_descriptions | 
					
						
							|  |  |  |         @parameter_descriptions ||= ( | 
					
						
							|  |  |  |           @discovery_document['parameters'] || {} | 
					
						
							|  |  |  |         ).inject({}) { |h,(k,v)| h[k]=v; h } | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ## | 
					
						
							|  |  |  |       # Returns an <code>Array</code> of the parameters for this method. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @return [Array] The parameters. | 
					
						
							|  |  |  |       def parameters | 
					
						
							|  |  |  |         @parameters ||= (( | 
					
						
							|  |  |  |           @discovery_document['parameters'] || {} | 
					
						
							|  |  |  |         ).inject({}) { |h,(k,v)| h[k]=v; h }).keys | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ## | 
					
						
							|  |  |  |       # Returns an <code>Array</code> of the required parameters for this | 
					
						
							|  |  |  |       # method. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @return [Array] The required parameters. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @example | 
					
						
							|  |  |  |       #   # A list of all required parameters. | 
					
						
							|  |  |  |       #   method.required_parameters | 
					
						
							|  |  |  |       def required_parameters | 
					
						
							|  |  |  |         @required_parameters ||= ((self.parameter_descriptions.select do |k, v| | 
					
						
							|  |  |  |           v['required'] | 
					
						
							|  |  |  |         end).inject({}) { |h,(k,v)| h[k]=v; h }).keys | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ## | 
					
						
							|  |  |  |       # Returns an <code>Array</code> of the optional parameters for this | 
					
						
							|  |  |  |       # method. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @return [Array] The optional parameters. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @example | 
					
						
							|  |  |  |       #   # A list of all optional parameters. | 
					
						
							|  |  |  |       #   method.optional_parameters | 
					
						
							|  |  |  |       def optional_parameters | 
					
						
							|  |  |  |         @optional_parameters ||= ((self.parameter_descriptions.reject do |k, v| | 
					
						
							|  |  |  |           v['required'] | 
					
						
							|  |  |  |         end).inject({}) { |h,(k,v)| h[k]=v; h }).keys | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ## | 
					
						
							|  |  |  |       # Verifies that the parameters are valid for this method.  Raises an | 
					
						
							|  |  |  |       # exception if validation fails. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @param [Hash, Array] parameters | 
					
						
							|  |  |  |       #   The parameters to verify. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @return [NilClass] <code>nil</code> if validation passes. | 
					
						
							|  |  |  |       def validate_parameters(parameters={}) | 
					
						
							|  |  |  |         parameters = self.normalize_parameters(parameters) | 
					
						
							|  |  |  |         required_variables = ((self.parameter_descriptions.select do |k, v| | 
					
						
							|  |  |  |           v['required'] | 
					
						
							|  |  |  |         end).inject({}) { |h,(k,v)| h[k]=v; h }).keys | 
					
						
							|  |  |  |         missing_variables = required_variables - parameters.keys | 
					
						
							|  |  |  |         if missing_variables.size > 0
 | 
					
						
							|  |  |  |           raise ArgumentError, | 
					
						
							|  |  |  |             "Missing required parameters: #{missing_variables.join(', ')}." | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |         parameters.each do |k, v| | 
					
						
							|  |  |  |           if self.parameter_descriptions[k] | 
					
						
							|  |  |  |             enum = self.parameter_descriptions[k]['enum'] | 
					
						
							|  |  |  |             if enum && !enum.include?(v) | 
					
						
							|  |  |  |               raise ArgumentError, | 
					
						
							|  |  |  |                 "Parameter '#{k}' has an invalid value: #{v}. " + | 
					
						
							|  |  |  |                 "Must be one of #{enum.inspect}." | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |             pattern = self.parameter_descriptions[k]['pattern'] | 
					
						
							|  |  |  |             if pattern | 
					
						
							|  |  |  |               regexp = Regexp.new("^#{pattern}$") | 
					
						
							|  |  |  |               if v !~ regexp | 
					
						
							|  |  |  |                 raise ArgumentError, | 
					
						
							|  |  |  |                   "Parameter '#{k}' has an invalid value: #{v}. " + | 
					
						
							|  |  |  |                   "Must match: /^#{pattern}$/." | 
					
						
							|  |  |  |               end | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |         return nil | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       ## | 
					
						
							|  |  |  |       # Returns a <code>String</code> representation of the method's state. | 
					
						
							|  |  |  |       # | 
					
						
							|  |  |  |       # @return [String] The method's state, as a <code>String</code>. | 
					
						
							|  |  |  |       def inspect | 
					
						
							|  |  |  |         sprintf( | 
					
						
							|  |  |  |           "#<%s:%#0x ID:%s>", | 
					
						
							|  |  |  |           self.class.to_s, self.object_id, self.id | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |