From 3bd7056e8683add7c6279fac5e7741d9af3b736e Mon Sep 17 00:00:00 2001 From: Bob Aman Date: Tue, 12 Oct 2010 20:39:09 +0000 Subject: [PATCH] Updated version sorting code and added authorization shortcuts. git-svn-id: https://google-api-ruby-client.googlecode.com/svn/trunk@60 c1d61fac-ed7f-fcc1-18f7-ff78120a04ef --- README | 4 +- lib/google/api_client.rb | 65 ++++++++++++++++-------- lib/google/api_client/discovery.rb | 38 ++++++++++++++ spec/google/api_client/discovery_spec.rb | 41 ++++++--------- spec/google/api_client_spec.rb | 18 +++++++ 5 files changed, 116 insertions(+), 50 deletions(-) diff --git a/README b/README index 6ac35e13a..fa287b97a 100644 --- a/README +++ b/README @@ -17,6 +17,7 @@ APIs. require 'signet/oauth_1/client' client = Google::APIClient.new( :service => 'buzz', + # Buzz has API-specific endpoints :authorization => Signet::OAuth1::Client.new( :temporary_credential_uri => 'https://www.google.com/accounts/OAuthGetRequestToken', @@ -48,8 +49,7 @@ APIs. # Make an API call response = client.execute( 'chili.activities.list', - {'scope' => '@self', 'userId' => '@me', 'alt' => 'json'}, - '', [], {:signed => true} + {'scope' => '@self', 'userId' => '@me', 'alt' => 'json'} ) status, headers, body = response diff --git a/lib/google/api_client.rb b/lib/google/api_client.rb index 1f51324bf..dcf30007d 100644 --- a/lib/google/api_client.rb +++ b/lib/google/api_client.rb @@ -33,11 +33,11 @@ module Google @options = { # TODO: What configuration options need to go here? }.merge(options) - if !self.authorization.respond_to?(:generate_authenticated_request) - raise TypeError, - 'Expected authorization mechanism to respond to ' + - '#generate_authenticated_request.' - end + # Force immediate type-checking and short-cut resolution + self.parser + self.authorization + self.http_adapter + return self end ## @@ -53,8 +53,11 @@ module Google ## # Returns the authorization mechanism used by the client. + # + # @return [#generate_authenticated_request] The authorization mechanism. def authorization - unless @options[:authorization] + case @options[:authorization] + when :oauth_1, :oauth require 'signet/oauth_1/client' # NOTE: Do not rely on this default value, as it may change @options[:authorization] = Signet::OAuth1::Client.new( @@ -67,10 +70,29 @@ module Google :client_credential_key => 'anonymous', :client_credential_secret => 'anonymous' ) + when nil + # No authorization mechanism + else + if !@options[:authorization].respond_to?( + :generate_authenticated_request) + raise TypeError, + 'Expected authorization mechanism to respond to ' + + '#generate_authenticated_request.' + end end return @options[:authorization] end + ## + # Sets the authorization mechanism used by the client. + # + # @param [#generate_authenticated_request] new_authorization + # The new authorization mechanism. + def authorization=(new_authorization) + @options[:authorization] = new_authorization + return self.authorization + end + ## # Returns the HTTP adapter used by the client. def http_adapter @@ -204,21 +226,15 @@ module Google # @param [String, Symbol] service_name The name of the service. # # @return [Google::APIClient::Service] The service object. - def latest_service(service_name) + def latest_service_version(service_name) if !service_name.kind_of?(String) && !service_name.kind_of?(Symbol) raise TypeError, "Expected String or Symbol, got #{service_name.class}." end service_name = service_name.to_s - versions = {} - for service in self.discovered_services - next if service.name != service_name - sortable_version = service.version.gsub(/^v/, '').split('.').map do |v| - v.to_i - end - versions[sortable_version] = service - end - return versions[versions.keys.sort.last] + return (self.discovered_services.select do |service| + service.name == service_name + end).sort.last end ## @@ -242,25 +258,31 @@ module Google # :signed is true. # - :signed — # true if the request must be signed, false - # otherwise. Defaults to true. + # otherwise. Defaults to true if an authorization + # mechanism has been set, false otherwise. # # @return [Array] The generated request. # # @example # request = client.generate_request( # 'chili.activities.list', - # {'scope' => '@self', 'userId' => '@me', 'alt' => 'json'}, - # '', [], {:signed => true} + # {'scope' => '@self', 'userId' => '@me', 'alt' => 'json'} # ) # method, uri, headers, body = request def generate_request( api_method, parameters={}, body='', headers=[], options={}) options={ - :signed => true, :parser => self.parser, :service_version => 'v1', :authorization => self.authorization }.merge(options) + # The default value for the :signed option depends on whether an + # authorization mechanism has been set. + if options[:authorization] + options = {:signed => true}.merge(options) + else + options = {:signed => false}.merge(options) + end if api_method.kind_of?(String) || api_method.kind_of?(Symbol) api_method = self.discovered_method( api_method.to_s, options[:service_version] @@ -310,8 +332,7 @@ module Google # @example # response = client.execute( # 'chili.activities.list', - # {'scope' => '@self', 'userId' => '@me', 'alt' => 'json'}, - # '', [], {:signed => true} + # {'scope' => '@self', 'userId' => '@me', 'alt' => 'json'} # ) # status, headers, body = response def execute(api_method, parameters={}, body='', headers=[], options={}) diff --git a/lib/google/api_client/discovery.rb b/lib/google/api_client/discovery.rb index 8976d2f9a..5191e3f34 100644 --- a/lib/google/api_client/discovery.rb +++ b/lib/google/api_client/discovery.rb @@ -138,6 +138,44 @@ module Google end) end + ## + # Compares two versions of a service. + # + # @param [Object] other The service to compare. + # + # @return [Integer] + # -1 if the service is older than other. + # 0 if the service is the same as other. + # 1 if the service is newer than other. + # nil if the service cannot be compared to + # other. + def <=>(other) + # We can only compare versions of the same service + if other.kind_of?(self.class) && self.name == other.name + split_version = lambda do |version| + dotted_version = version[/^v?(\d+(.\d+)*)-?(.*?)?$/, 1] + suffix = version[/^v?(\d+(.\d+)*)-?(.*?)?$/, 3] + [dotted_version.split('.').map { |v| v.to_i }, suffix] + end + self_sortable, self_suffix = split_version.call(self.version) + other_sortable, other_suffix = split_version.call(other.version) + result = self_sortable <=> other_sortable + if result != 0 + return result + # If the dotted versions are equal, check the suffix. + # An omitted suffix should be sorted after an included suffix. + elsif self_suffix == '' + return 1 + elsif other_suffix == '' + return -1 + else + return self_suffix <=> other_suffix + end + else + return nil + end + end + ## # Returns a String representation of the service's state. # diff --git a/spec/google/api_client/discovery_spec.rb b/spec/google/api_client/discovery_spec.rb index 2b6f77ad3..305d2c9e3 100644 --- a/spec/google/api_client/discovery_spec.rb +++ b/spec/google/api_client/discovery_spec.rb @@ -113,13 +113,13 @@ describe Google::APIClient, 'configured for the prediction API' do end it 'should correctly determine the latest version' do - @client.latest_service('prediction').version.should_not == 'v1' - @client.latest_service(:prediction).version.should_not == 'v1' + @client.latest_service_version('prediction').version.should_not == 'v1' + @client.latest_service_version(:prediction).version.should_not == 'v1' end it 'should raise an error for bogus services' do (lambda do - @client.latest_service(42) + @client.latest_service_version(42) end).should raise_error(TypeError) end @@ -138,7 +138,7 @@ describe Google::APIClient, 'configured for the prediction API' do Google::APIClient::Service.new('magic', 'v2.1', {}) @client.discovered_services << Google::APIClient::Service.new('magic', 'v10.0', {}) - @client.latest_service('magic').version.should == 'v10.1' + @client.latest_service_version('magic').version.should == 'v10.1' end it 'should correctly determine the latest version' do @@ -152,21 +152,20 @@ describe Google::APIClient, 'configured for the prediction API' do Google::APIClient::Service.new('two', 'v1.1-r1c3', {}) @client.discovered_services << Google::APIClient::Service.new('two', 'v2', {}) - @client.latest_service('two').version.should == 'v2' + @client.discovered_services << + Google::APIClient::Service.new('two', 'v2beta1', {}) + @client.latest_service_version('two').version.should == 'v2' end it 'should return nil for bogus service names' do # Sanity check the algorithm - @client.latest_service('bogus').should == nil + @client.latest_service_version('bogus').should == nil end it 'should generate valid requests' do request = @client.generate_request( 'prediction.training.insert', - {'query' => '12345'}, - '', - [], - {:signed => false} + {'query' => '12345'} ) method, uri, headers, body = request method.should == 'POST' @@ -179,10 +178,7 @@ describe Google::APIClient, 'configured for the prediction API' do it 'should generate requests against the correct URIs' do request = @client.generate_request( :'prediction.training.insert', - {'query' => '12345'}, - '', - [], - {:signed => false} + {'query' => '12345'} ) method, uri, headers, body = request uri.should == @@ -193,10 +189,7 @@ describe Google::APIClient, 'configured for the prediction API' do prediction = @client.discovered_service('prediction', 'v1') request = @client.generate_request( prediction.training.insert, - {'query' => '12345'}, - '', - [], - {:signed => false} + {'query' => '12345'} ) method, uri, headers, body = request uri.should == @@ -204,14 +197,12 @@ describe Google::APIClient, 'configured for the prediction API' do end it 'should generate signed requests' do + @client.authorization = :oauth_1 @client.authorization.token_credential_key = '12345' @client.authorization.token_credential_secret = '12345' request = @client.generate_request( 'prediction.training.insert', - {'query' => '12345'}, - '', - [], - {:signed => true} + {'query' => '12345'} ) method, uri, headers, body = request headers = Hash[headers] @@ -220,14 +211,12 @@ describe Google::APIClient, 'configured for the prediction API' do end it 'should not be able to execute improperly authorized requests' do + @client.authorization = :oauth_1 @client.authorization.token_credential_key = '12345' @client.authorization.token_credential_secret = '12345' response = @client.execute( 'prediction.training.insert', - {'query' => '12345'}, - '', - [], - {:signed => true} + {'query' => '12345'} ) status, headers, body = response status.should == 401 diff --git a/spec/google/api_client_spec.rb b/spec/google/api_client_spec.rb index e76c10e0e..50036478c 100644 --- a/spec/google/api_client_spec.rb +++ b/spec/google/api_client_spec.rb @@ -34,6 +34,24 @@ describe Google::APIClient, 'with default configuration' do @client.parser.should be(Google::APIClient::JSONParser) end + it 'should not use an authorization mechanism' do + @client.authorization.should be_nil + end +end + +describe Google::APIClient, 'with default oauth configuration' do + before do + @client = Google::APIClient.new(:authorization => :oauth_1) + end + + it 'should make its version number available' do + ::Google::APIClient::VERSION::STRING.should be_instance_of(String) + end + + it 'should use the default JSON parser' do + @client.parser.should be(Google::APIClient::JSONParser) + end + it 'should use the default OAuth1 client configuration' do @client.authorization.temporary_credential_uri.to_s.should == 'https://www.google.com/accounts/OAuthGetRequestToken'