From b4745890f5277e4181377208ef924ac8cdf8e86e Mon Sep 17 00:00:00 2001 From: Bob Aman Date: Thu, 4 Aug 2011 18:32:00 -0400 Subject: [PATCH] Separated out the discovery classes into individual files. --- lib/google/api_client/discovery.rb | 573 +------------------- lib/google/api_client/discovery/api.rb | 198 +++++++ lib/google/api_client/discovery/method.rb | 286 ++++++++++ lib/google/api_client/discovery/resource.rb | 146 +++++ 4 files changed, 633 insertions(+), 570 deletions(-) create mode 100644 lib/google/api_client/discovery/api.rb create mode 100644 lib/google/api_client/discovery/method.rb create mode 100644 lib/google/api_client/discovery/resource.rb diff --git a/lib/google/api_client/discovery.rb b/lib/google/api_client/discovery.rb index 45f348bbe..4a72acf60 100644 --- a/lib/google/api_client/discovery.rb +++ b/lib/google/api_client/discovery.rb @@ -13,573 +13,6 @@ # limitations under the License. -require 'json' -require 'addressable/uri' -require 'addressable/template' - -require 'google/inflection' -require 'google/api_client/errors' - -module Google - class APIClient - ## - # A service that has been described by a discovery document. - class API - - ## - # 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 - # The section of the discovery document that applies to this service - # version. - # - # @return [Google::APIClient::API] The constructed service object. - def initialize(document_base, discovery_document) - @document_base = Addressable::URI.parse(document_base) - @discovery_document = discovery_document - metaclass = (class <String representation of the service's state. - # - # @return [String] The service's state, as a String. - def inspect - sprintf( - "#<%s:%#0x ID:%s>", self.class.to_s, self.object_id, self.id - ) - end - end - - ## - # A resource that has been described by a discovery document. - class Resource - - ## - # Creates a description of a particular version of a resource. - # - # @param [Addressable::URI] base - # The base URI for the service. - # @param [String] resource_name - # The identifier for the resource. - # @param [Hash] resource_description - # The section of the discovery document that applies to this resource. - # - # @return [Google::APIClient::Resource] The constructed resource object. - def initialize(method_base, resource_name, discovery_document) - @method_base = method_base - @name = resource_name - @discovery_document = discovery_document - metaclass = (class <String representation of the resource's state. - # - # @return [String] The resource's state, as a String. - def inspect - sprintf( - "#<%s:%#0x NAME:%s>", self.class.to_s, self.object_id, self.name - ) - end - end - - ## - # 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. - def initialize(method_base, method_name, discovery_document) - @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 - - ## - # 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 Hash 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 Array 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 Array 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 Array 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] nil 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 String representation of the method's state. - # - # @return [String] The method's state, as a String. - def inspect - sprintf( - "#<%s:%#0x ID:%s>", - self.class.to_s, self.object_id, self.id - ) - end - end - end -end +require 'google/api_client/discovery/api' +require 'google/api_client/discovery/resource' +require 'google/api_client/discovery/method' diff --git a/lib/google/api_client/discovery/api.rb b/lib/google/api_client/discovery/api.rb new file mode 100644 index 000000000..8d20310a9 --- /dev/null +++ b/lib/google/api_client/discovery/api.rb @@ -0,0 +1,198 @@ +# 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 'google/inflection' +require 'google/api_client/discovery/resource' +require 'google/api_client/discovery/method' + +module Google + class APIClient + ## + # A service that has been described by a discovery document. + class API + + ## + # 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 + # The section of the discovery document that applies to this service + # version. + # + # @return [Google::APIClient::API] The constructed service object. + def initialize(document_base, discovery_document) + @document_base = Addressable::URI.parse(document_base) + @discovery_document = discovery_document + metaclass = (class <String representation of the service's state. + # + # @return [String] The service's state, as a String. + def inspect + sprintf( + "#<%s:%#0x ID:%s>", self.class.to_s, self.object_id, self.id + ) + end + end + end +end diff --git a/lib/google/api_client/discovery/method.rb b/lib/google/api_client/discovery/method.rb new file mode 100644 index 000000000..de79bf752 --- /dev/null +++ b/lib/google/api_client/discovery/method.rb @@ -0,0 +1,286 @@ +# 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. + def initialize(method_base, method_name, discovery_document) + @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 + + ## + # 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 Hash 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 Array 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 Array 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 Array 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] nil 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 String representation of the method's state. + # + # @return [String] The method's state, as a String. + def inspect + sprintf( + "#<%s:%#0x ID:%s>", + self.class.to_s, self.object_id, self.id + ) + end + end + end +end diff --git a/lib/google/api_client/discovery/resource.rb b/lib/google/api_client/discovery/resource.rb new file mode 100644 index 000000000..6d2d6bddb --- /dev/null +++ b/lib/google/api_client/discovery/resource.rb @@ -0,0 +1,146 @@ +# 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 'google/inflection' +require 'google/api_client/discovery/method' + +module Google + class APIClient + ## + # A resource that has been described by a discovery document. + class Resource + + ## + # Creates a description of a particular version of a resource. + # + # @param [Addressable::URI] base + # The base URI for the service. + # @param [String] resource_name + # The identifier for the resource. + # @param [Hash] resource_description + # The section of the discovery document that applies to this resource. + # + # @return [Google::APIClient::Resource] The constructed resource object. + def initialize(method_base, resource_name, discovery_document) + @method_base = method_base + @name = resource_name + @discovery_document = discovery_document + metaclass = (class <String representation of the resource's state. + # + # @return [String] The resource's state, as a String. + def inspect + sprintf( + "#<%s:%#0x NAME:%s>", self.class.to_s, self.object_id, self.name + ) + end + end + end +end