A significant update of the client functionality.
* updated to use v0.3 of the discovery API * updated to use httpadapter 1.0.0 * added OAuth 2 support to the command line tool * renamed some switches in the command line tool * added additional configuration capabilities git-svn-id: https://google-api-ruby-client.googlecode.com/svn/trunk@128 c1d61fac-ed7f-fcc1-18f7-ff78120a04ef
This commit is contained in:
		
							parent
							
								
									1dee705828
								
							
						
					
					
						commit
						286a7152f2
					
				|  | @ -1,3 +1,11 @@ | ||||||
|  | == 0.2.0 | ||||||
|  | 
 | ||||||
|  | * updated to use v0.3 of the discovery API | ||||||
|  | * updated to use httpadapter 1.0.0 | ||||||
|  | * added OAuth 2 support to the command line tool | ||||||
|  | * renamed some switches in the command line tool | ||||||
|  | * added additional configuration capabilities | ||||||
|  | 
 | ||||||
| == 0.1.3 | == 0.1.3 | ||||||
| 
 | 
 | ||||||
| * added support for manual overrides of the discovery URI | * added support for manual overrides of the discovery URI | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								README
								
								
								
								
							
							
						
						
									
										2
									
								
								README
								
								
								
								
							|  | @ -44,7 +44,7 @@ APIs. | ||||||
|   client.authorization.fetch_token_credential!(:verifier => '12345') |   client.authorization.fetch_token_credential!(:verifier => '12345') | ||||||
| 
 | 
 | ||||||
|   # Discover available methods |   # Discover available methods | ||||||
|   method_names = client.discovered_service('buzz').to_h.keys |   method_names = client.discovered_api('buzz').to_h.keys | ||||||
| 
 | 
 | ||||||
|   # Make an API call |   # Make an API call | ||||||
|   response = client.execute( |   response = client.execute( | ||||||
|  |  | ||||||
							
								
								
									
										292
									
								
								bin/google-api
								
								
								
								
							
							
						
						
									
										292
									
								
								bin/google-api
								
								
								
								
							|  | @ -26,7 +26,8 @@ module Google | ||||||
| 
 | 
 | ||||||
|         def do_GET(request, response) |         def do_GET(request, response) | ||||||
|           $verifier ||= Addressable::URI.unencode_component( |           $verifier ||= Addressable::URI.unencode_component( | ||||||
|             request.request_uri.to_s[/\?.*oauth_verifier=([^&$]+)(&|$)/, 1] |             request.request_uri.to_s[/\?.*oauth_verifier=([^&$]+)(&|$)/, 1] || | ||||||
|  |             request.request_uri.to_s[/\?.*code=([^&$]+)(&|$)/, 1] | ||||||
|           ) |           ) | ||||||
|           response.status = WEBrick::HTTPStatus::RC_ACCEPTED |           response.status = WEBrick::HTTPStatus::RC_ACCEPTED | ||||||
|           # This javascript will auto-close the tab after the |           # This javascript will auto-close the tab after the | ||||||
|  | @ -92,24 +93,24 @@ HTML | ||||||
|             options[:scope] = s |             options[:scope] = s | ||||||
|           end |           end | ||||||
|           opts.on( |           opts.on( | ||||||
|               "--client-key <key>", String, |               "--client-id <key>", String, | ||||||
|                 "Set the 2-legged OAuth key") do |k| |                 "Set the OAuth client id or key") do |k| | ||||||
|             options[:client_credential_key] = k |             options[:client_credential_key] = k | ||||||
|           end |           end | ||||||
|           opts.on( |           opts.on( | ||||||
|               "--client-secret <secret>", String, |               "--client-secret <secret>", String, | ||||||
|                 "Set the 2-legged OAuth secret") do |s| |                 "Set the OAuth client secret") do |s| | ||||||
|             options[:client_credential_secret] = s |             options[:client_credential_secret] = s | ||||||
|           end |           end | ||||||
|           opts.on( |           opts.on( | ||||||
|               "-s", "--service <name>", String, |               "--api <name>", String, | ||||||
|               "Perform discovery on service") do |s| |               "Perform discovery on API") do |s| | ||||||
|             options[:service_name] = s |             options[:api] = s | ||||||
|           end |           end | ||||||
|           opts.on( |           opts.on( | ||||||
|               "--service-version <id>", String, |               "--service-version <id>", String, | ||||||
|               "Select service version") do |id| |               "Select service version") do |id| | ||||||
|             options[:service_version] = id |             options[:version] = id | ||||||
|           end |           end | ||||||
|           opts.on( |           opts.on( | ||||||
|               "--content-type <format>", String, |               "--content-type <format>", String, | ||||||
|  | @ -162,10 +163,11 @@ HTML | ||||||
| 
 | 
 | ||||||
|           opts.separator( |           opts.separator( | ||||||
|             "\nAvailable commands:\n" + |             "\nAvailable commands:\n" + | ||||||
|             "    oauth-login   Log a user into an API\n" + |             "    oauth-1-login   Log a user into an API with OAuth 1.0a\n" + | ||||||
|             "    list          List the methods available for a service\n" + |             "    oauth-2-login   Log a user into an API with OAuth 2.0 d10\n" + | ||||||
|             "    execute       Execute a method on the API\n" + |             "    list            List the methods available for a service\n" + | ||||||
|             "    irb           Start an interactive client session" |             "    execute         Execute a method on the API\n" + | ||||||
|  |             "    irb             Start an interactive client session" | ||||||
|           ) |           ) | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
|  | @ -180,23 +182,98 @@ HTML | ||||||
|         self.send(symbol) |         self.send(symbol) | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|  |       def client | ||||||
|  |         require 'signet/oauth_1/client' | ||||||
|  |         require 'yaml' | ||||||
|  |         require 'irb' | ||||||
|  |         config_file = File.expand_path('~/.google-api.yaml') | ||||||
|  |         authorization = nil | ||||||
|  |         if File.exist?(config_file) | ||||||
|  |           config = open(config_file, 'r') { |file| YAML.load(file.read) } | ||||||
|  |         else | ||||||
|  |           config = {} | ||||||
|  |         end | ||||||
|  |         if config["mechanism"] | ||||||
|  |           authorization = config["mechanism"].to_sym | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|  |         client = Google::APIClient.new(:authorization => authorization) | ||||||
|  | 
 | ||||||
|  |         case authorization | ||||||
|  |         when :oauth_1 | ||||||
|  |           if client.authorization && | ||||||
|  |               !client.authorization.kind_of?(Signet::OAuth1::Client) | ||||||
|  |             STDERR.puts( | ||||||
|  |               "Unexpected authorization mechanism: " + | ||||||
|  |               "#{client.authorization.class}" | ||||||
|  |             ) | ||||||
|  |             exit(1) | ||||||
|  |           end | ||||||
|  |           config = open(config_file, 'r') { |file| YAML.load(file.read) } | ||||||
|  |           client.authorization.client_credential_key = | ||||||
|  |             config["client_credential_key"] | ||||||
|  |           client.authorization.client_credential_secret = | ||||||
|  |             config["client_credential_secret"] | ||||||
|  |           client.authorization.token_credential_key = | ||||||
|  |             config["token_credential_key"] | ||||||
|  |           client.authorization.token_credential_secret = | ||||||
|  |             config["token_credential_secret"] | ||||||
|  |         when :oauth_2 | ||||||
|  |           if client.authorization && | ||||||
|  |               !client.authorization.kind_of?(Signet::OAuth2::Client) | ||||||
|  |             STDERR.puts( | ||||||
|  |               "Unexpected authorization mechanism: " + | ||||||
|  |               "#{client.authorization.class}" | ||||||
|  |             ) | ||||||
|  |             exit(1) | ||||||
|  |           end | ||||||
|  |           config = open(config_file, 'r') { |file| YAML.load(file.read) } | ||||||
|  |           client.authorization.scope = options[:scope] | ||||||
|  |           client.authorization.client_id = config["client_id"] | ||||||
|  |           client.authorization.client_secret = config["client_secret"] | ||||||
|  |           client.authorization.access_token = config["access_token"] | ||||||
|  |           client.authorization.refresh_token = config["refresh_token"] | ||||||
|  |         else | ||||||
|  |           # Dunno? | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|  |         if options[:discovery_uri] | ||||||
|  |           client.discovery_uri = options[:discovery_uri] | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|  |         return client | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       def api_version(api, version) | ||||||
|  |         v = version | ||||||
|  |         if !version | ||||||
|  |           if client.preferred_version(api) | ||||||
|  |             v = client.preferred_version(api).version | ||||||
|  |           else | ||||||
|  |             v = 'v1' | ||||||
|  |           end | ||||||
|  |         end | ||||||
|  |         return v | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|       COMMANDS = [ |       COMMANDS = [ | ||||||
|         :oauth_login, |         :oauth_1_login, | ||||||
|  |         :oauth_2_login, | ||||||
|         :list, |         :list, | ||||||
|         :execute, |         :execute, | ||||||
|         :irb, |         :irb, | ||||||
|         :fuzz |         :fuzz | ||||||
|       ] |       ] | ||||||
| 
 | 
 | ||||||
|       def oauth_login |       def oauth_1_login | ||||||
|         require 'signet/oauth_1/client' |         require 'signet/oauth_1/client' | ||||||
|         require 'launchy' |         require 'launchy' | ||||||
|         require 'yaml' |         require 'yaml' | ||||||
|         if options[:client_credential_key] && |         if options[:client_credential_key] && | ||||||
|             options[:client_credential_secret] |             options[:client_credential_secret] | ||||||
|           scope = options[:scope] |  | ||||||
|           config = { |           config = { | ||||||
|             "scope" => nil, |             "mechanism" => "oauth_1", | ||||||
|  |             "scope" => options[:scope], | ||||||
|             "client_credential_key" => options[:client_credential_key], |             "client_credential_key" => options[:client_credential_key], | ||||||
|             "client_credential_secret" => options[:client_credential_secret], |             "client_credential_secret" => options[:client_credential_secret], | ||||||
|             "token_credential_key" => nil, |             "token_credential_key" => nil, | ||||||
|  | @ -229,19 +306,8 @@ HTML | ||||||
|             :client_credential_secret => 'anonymous', |             :client_credential_secret => 'anonymous', | ||||||
|             :callback => "http://localhost:#{OAUTH_SERVER_PORT}/" |             :callback => "http://localhost:#{OAUTH_SERVER_PORT}/" | ||||||
|           ) |           ) | ||||||
|           scope = options[:scope] |  | ||||||
|           # Special cases |  | ||||||
|           case scope |  | ||||||
|           when /https:\/\/www\.googleapis\.com\/auth\/buzz/, |  | ||||||
|               /https:\/\/www\.googleapis\.com\/auth\/buzz\.readonly/ |  | ||||||
|             oauth_client.authorization_uri = |  | ||||||
|               'https://www.google.com/buzz/api/auth/OAuthAuthorizeToken?' + |  | ||||||
|               "domain=#{oauth_client.client_credential_key}&" + |  | ||||||
|               "scope=#{scope}&" + |  | ||||||
|               "xoauth_displayname=Google%20API%20Client" |  | ||||||
|           end |  | ||||||
|           oauth_client.fetch_temporary_credential!(:additional_parameters => { |           oauth_client.fetch_temporary_credential!(:additional_parameters => { | ||||||
|             :scope => scope, |             :scope => options[:scope], | ||||||
|             :xoauth_displayname => 'Google API Client' |             :xoauth_displayname => 'Google API Client' | ||||||
|           }) |           }) | ||||||
| 
 | 
 | ||||||
|  | @ -251,7 +317,7 @@ HTML | ||||||
|           server.start |           server.start | ||||||
|           oauth_client.fetch_token_credential!(:verifier => $verifier) |           oauth_client.fetch_token_credential!(:verifier => $verifier) | ||||||
|           config = { |           config = { | ||||||
|             "scope" => scope, |             "scope" => options[:scope], | ||||||
|             "client_credential_key" => |             "client_credential_key" => | ||||||
|               oauth_client.client_credential_key, |               oauth_client.client_credential_key, | ||||||
|             "client_credential_secret" => |             "client_credential_secret" => | ||||||
|  | @ -267,34 +333,90 @@ HTML | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|  |       def oauth_2_login | ||||||
|  |         require 'signet/oauth_2/client' | ||||||
|  |         require 'launchy' | ||||||
|  |         require 'yaml' | ||||||
|  |         if !options[:client_credential_key] || | ||||||
|  |             !options[:client_credential_secret] | ||||||
|  |           STDERR.puts('No client ID and secret supplied.') | ||||||
|  |           exit(1) | ||||||
|  |         end | ||||||
|  |         if options[:access_token] | ||||||
|  |           config = { | ||||||
|  |             "mechanism" => "oauth_2", | ||||||
|  |             "scope" => options[:scope], | ||||||
|  |             "client_id" => options[:client_credential_key], | ||||||
|  |             "client_secret" => options[:client_credential_secret], | ||||||
|  |             "access_token" => options[:access_token], | ||||||
|  |             "refresh_token" => options[:refresh_token] | ||||||
|  |           } | ||||||
|  |           config_file = File.expand_path('~/.google-api.yaml') | ||||||
|  |           open(config_file, 'w') { |file| file.write(YAML.dump(config)) } | ||||||
|  |           exit(0) | ||||||
|  |         else | ||||||
|  |           $verifier = nil | ||||||
|  |           # TODO(bobaman): Cross-platform? | ||||||
|  |           logger = WEBrick::Log.new('/dev/null') | ||||||
|  |           server = WEBrick::HTTPServer.new( | ||||||
|  |             :Port => OAUTH_SERVER_PORT, | ||||||
|  |             :Logger => logger, | ||||||
|  |             :AccessLog => logger | ||||||
|  |           ) | ||||||
|  |           trap("INT") { server.shutdown } | ||||||
|  | 
 | ||||||
|  |           server.mount("/", OAuthVerifierServlet) | ||||||
|  | 
 | ||||||
|  |           oauth_client = Signet::OAuth2::Client.new( | ||||||
|  |             :authorization_uri => | ||||||
|  |               'https://www.google.com/accounts/o8/oauth2/authorization', | ||||||
|  |             :token_credential_uri => | ||||||
|  |               'https://www.google.com/accounts/o8/oauth2/token', | ||||||
|  |             :client_id => options[:client_credential_key], | ||||||
|  |             :client_secret => options[:client_credential_secret], | ||||||
|  |             :redirect_uri => "http://localhost:#{OAUTH_SERVER_PORT}/", | ||||||
|  |             :scope => options[:scope] | ||||||
|  |           ) | ||||||
|  | 
 | ||||||
|  |           # Launch browser | ||||||
|  |           Launchy::Browser.run(oauth_client.authorization_uri.to_s) | ||||||
|  | 
 | ||||||
|  |           server.start | ||||||
|  |           oauth_client.code = $verifier | ||||||
|  |           oauth_client.fetch_access_token! | ||||||
|  |           config = { | ||||||
|  |             "mechanism" => "oauth_2", | ||||||
|  |             "scope" => options[:scope], | ||||||
|  |             "client_id" => oauth_client.client_id, | ||||||
|  |             "client_secret" => oauth_client.client_secret, | ||||||
|  |             "access_token" => oauth_client.access_token, | ||||||
|  |             "refresh_token" => oauth_client.refresh_token | ||||||
|  |           } | ||||||
|  |           config_file = File.expand_path('~/.google-api.yaml') | ||||||
|  |           open(config_file, 'w') { |file| file.write(YAML.dump(config)) } | ||||||
|  |           exit(0) | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|       def list |       def list | ||||||
|         service_name = options[:service_name] |         api = options[:api] | ||||||
|         unless service_name |         unless api | ||||||
|           STDERR.puts('No service name supplied.') |           STDERR.puts('No service name supplied.') | ||||||
|           exit(1) |           exit(1) | ||||||
|         end |         end | ||||||
|         client = Google::APIClient.new( |         client = Google::APIClient.new(:authorization => nil) | ||||||
|           :service => service_name, |  | ||||||
|           :authorization => nil |  | ||||||
|         ) |  | ||||||
|         if options[:discovery_uri] |         if options[:discovery_uri] | ||||||
|           client.discovery_uri = options[:discovery_uri] |           client.discovery_uri = options[:discovery_uri] | ||||||
|         end |         end | ||||||
|         service_version = |         version = api_version(api, options[:version]) | ||||||
|           options[:service_version] || |         service = client.discovered_api(api, version) | ||||||
|           client.latest_service_version(service_name).version |  | ||||||
|         service = client.discovered_service(service_name, service_version) |  | ||||||
|         rpcnames = service.to_h.keys |         rpcnames = service.to_h.keys | ||||||
|         puts rpcnames.sort.join("\n") |         puts rpcnames.sort.join("\n") | ||||||
|         exit(0) |         exit(0) | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       def execute |       def execute | ||||||
|         require 'signet/oauth_1/client' |         client = self.client | ||||||
|         require 'yaml' |  | ||||||
|         config_file = File.expand_path('~/.google-api.yaml') |  | ||||||
|         signed = File.exist?(config_file) |  | ||||||
|         authorization_type = :oauth_1 |  | ||||||
| 
 | 
 | ||||||
|         # Setup HTTP request data |         # Setup HTTP request data | ||||||
|         request_body = '' |         request_body = '' | ||||||
|  | @ -308,28 +430,6 @@ HTML | ||||||
|           headers << ['Content-Type', 'application/json'] |           headers << ['Content-Type', 'application/json'] | ||||||
|         end |         end | ||||||
| 
 | 
 | ||||||
|         configure_authorization = lambda do |client| |  | ||||||
|           if !client.authorization.kind_of?(Signet::OAuth1::Client) |  | ||||||
|             STDERR.puts( |  | ||||||
|               "Unexpected authorization mechanism: " + |  | ||||||
|               "#{client.authorization.class}" |  | ||||||
|             ) |  | ||||||
|             exit(1) |  | ||||||
|           end |  | ||||||
|           config = open(config_file, 'r') { |file| YAML.load(file.read) } |  | ||||||
|           client.authorization.client_credential_key = |  | ||||||
|             config["client_credential_key"] |  | ||||||
|           client.authorization.client_credential_secret = |  | ||||||
|             config["client_credential_secret"] |  | ||||||
|           client.authorization.token_credential_key = |  | ||||||
|             config["token_credential_key"] |  | ||||||
|           client.authorization.token_credential_secret = |  | ||||||
|             config["token_credential_secret"] |  | ||||||
|           if client.authorization.token_credential == nil |  | ||||||
|             authorization_type = :two_legged_oauth_1 |  | ||||||
|           end |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         if options[:uri] |         if options[:uri] | ||||||
|           # Make request with URI manually specified |           # Make request with URI manually specified | ||||||
|           uri = Addressable::URI.parse(options[:uri]) |           uri = Addressable::URI.parse(options[:uri]) | ||||||
|  | @ -345,13 +445,8 @@ HTML | ||||||
|           method = options[:http_method] |           method = options[:http_method] | ||||||
|           method ||= request_body == '' ? 'GET' : 'POST' |           method ||= request_body == '' ? 'GET' : 'POST' | ||||||
|           method.upcase! |           method.upcase! | ||||||
|           client = Google::APIClient.new(:authorization => authorization_type) |  | ||||||
|           if options[:discovery_uri] |  | ||||||
|             client.discovery_uri = options[:discovery_uri] |  | ||||||
|           end |  | ||||||
|           configure_authorization.call(client) if signed |  | ||||||
|           request = [method, uri.to_str, headers, [request_body]] |           request = [method, uri.to_str, headers, [request_body]] | ||||||
|           request = client.sign_request(request) |           request = client.generate_authenticated_request(:request => request) | ||||||
|           response = client.transmit_request(request) |           response = client.transmit_request(request) | ||||||
|           status, headers, body = response |           status, headers, body = response | ||||||
|           puts body |           puts body | ||||||
|  | @ -362,25 +457,14 @@ HTML | ||||||
|             STDERR.puts('No rpcname supplied.') |             STDERR.puts('No rpcname supplied.') | ||||||
|             exit(1) |             exit(1) | ||||||
|           end |           end | ||||||
|           service_name = |           api = options[:api] || self.rpcname[/^([^\.]+)\./, 1] | ||||||
|             options[:service_name] || self.rpcname[/^([^\.]+)\./, 1] |           version = api_version(api, options[:version]) | ||||||
|           client = Google::APIClient.new( |           service = client.discovered_api(api, version) | ||||||
|             :service => service_name, |  | ||||||
|             :authorization => authorization_type |  | ||||||
|           ) |  | ||||||
|           if options[:discovery_uri] |  | ||||||
|             client.discovery_uri = options[:discovery_uri] |  | ||||||
|           end |  | ||||||
|           configure_authorization.call(client) if signed |  | ||||||
|           service_version = |  | ||||||
|             options[:service_version] || |  | ||||||
|             client.latest_service_version(service_name).version |  | ||||||
|           service = client.discovered_service(service_name, service_version) |  | ||||||
|           method = service.to_h[self.rpcname] |           method = service.to_h[self.rpcname] | ||||||
|           if !method |           if !method | ||||||
|             STDERR.puts( |             STDERR.puts( | ||||||
|               "Method #{self.rpcname} does not exist for " + |               "Method #{self.rpcname} does not exist for " + | ||||||
|               "#{service_name}-#{service_version}." |               "#{api}-#{version}." | ||||||
|             ) |             ) | ||||||
|             exit(1) |             exit(1) | ||||||
|           end |           end | ||||||
|  | @ -394,7 +478,7 @@ HTML | ||||||
|           end |           end | ||||||
|           begin |           begin | ||||||
|             response = client.execute( |             response = client.execute( | ||||||
|               method, parameters, request_body, headers, {:signed => signed} |               method, parameters, request_body, headers | ||||||
|             ) |             ) | ||||||
|             status, headers, body = response |             status, headers, body = response | ||||||
|             puts body |             puts body | ||||||
|  | @ -407,37 +491,7 @@ HTML | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       def irb |       def irb | ||||||
|         require 'signet/oauth_1/client' |         $client = self.client | ||||||
|         require 'yaml' |  | ||||||
|         require 'irb' |  | ||||||
|         config_file = File.expand_path('~/.google-api.yaml') |  | ||||||
|         signed = File.exist?(config_file) |  | ||||||
| 
 |  | ||||||
|         $client = Google::APIClient.new( |  | ||||||
|           :service => options[:service_name], |  | ||||||
|           :authorization => (signed ? :oauth_1 : nil) |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|         if signed |  | ||||||
|           if $client.authorization && |  | ||||||
|               !$client.authorization.kind_of?(Signet::OAuth1::Client) |  | ||||||
|             STDERR.puts( |  | ||||||
|               "Unexpected authorization mechanism: " + |  | ||||||
|               "#{$client.authorization.class}" |  | ||||||
|             ) |  | ||||||
|             exit(1) |  | ||||||
|           end |  | ||||||
|           config = open(config_file, 'r') { |file| YAML.load(file.read) } |  | ||||||
|           $client.authorization.client_credential_key = |  | ||||||
|             config["client_credential_key"] |  | ||||||
|           $client.authorization.client_credential_secret = |  | ||||||
|             config["client_credential_secret"] |  | ||||||
|           $client.authorization.token_credential_key = |  | ||||||
|             config["token_credential_key"] |  | ||||||
|           $client.authorization.token_credential_secret = |  | ||||||
|             config["token_credential_secret"] |  | ||||||
|         end |  | ||||||
| 
 |  | ||||||
|         # Otherwise IRB will misinterpret command-line options |         # Otherwise IRB will misinterpret command-line options | ||||||
|         ARGV.clear |         ARGV.clear | ||||||
|         IRB.start(__FILE__) |         IRB.start(__FILE__) | ||||||
|  |  | ||||||
|  | @ -319,7 +319,7 @@ def service(service_name, service_version) | ||||||
|   unless service_version |   unless service_version | ||||||
|     service_version = client.latest_service_version(service_name).version |     service_version = client.latest_service_version(service_name).version | ||||||
|   end |   end | ||||||
|   client.discovered_service(service_name, service_version) |   client.discovered_api(service_name, service_version) | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| get '/template/:service/:method/' do | get '/template/:service/:method/' do | ||||||
|  |  | ||||||
|  | @ -12,58 +12,125 @@ | ||||||
| # See the License for the specific language governing permissions and | # See the License for the specific language governing permissions and | ||||||
| # limitations under the License. | # limitations under the License. | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| require 'httpadapter' | require 'httpadapter' | ||||||
| require 'json' | require 'json' | ||||||
| require 'stringio' | require 'stringio' | ||||||
| 
 | 
 | ||||||
|  | require 'google/api_client/errors' | ||||||
| require 'google/api_client/discovery' | require 'google/api_client/discovery' | ||||||
| 
 | 
 | ||||||
| module Google | module Google | ||||||
|   # TODO(bobaman): Document all this stuff. |   # TODO(bobaman): Document all this stuff. | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|   ## |   ## | ||||||
|   # This class manages communication with a single API. |   # This class manages APIs communication. | ||||||
|   class APIClient |   class APIClient | ||||||
|     ## |     ## | ||||||
|     # An error which is raised when there is an unexpected response or other |     # Creates a new Google API client. | ||||||
|     # transport error that prevents an operation from succeeding. |     # | ||||||
|     class TransmissionError < StandardError |     # @param [Hash] options The configuration parameters for the client. | ||||||
|     end |     # @option options [Symbol, #generate_authenticated_request] :authorization | ||||||
| 
 |     #   (:oauth_1) | ||||||
|  |     #   The authorization mechanism used by the client.  The following | ||||||
|  |     #   mechanisms are supported out-of-the-box: | ||||||
|  |     #   <ul> | ||||||
|  |     #     <li><code>:two_legged_oauth_1</code></li> | ||||||
|  |     #     <li><code>:oauth_1</code></li> | ||||||
|  |     #     <li><code>:oauth_2</code></li> | ||||||
|  |     #   </ul> | ||||||
|  |     # @option options [String] :host ("www.googleapis.com") | ||||||
|  |     #   The API hostname used by the client.  This rarely needs to be changed. | ||||||
|  |     # @option options [String] :user_agent ("google-api-ruby-client/{version}") | ||||||
|  |     #   The user agent used by the client.  Most developers will want to | ||||||
|  |     #   leave this value alone — the API key is the primary mechanism used to | ||||||
|  |     #   identify an application. | ||||||
|     def initialize(options={}) |     def initialize(options={}) | ||||||
|       @options = { |       # Normalize key to String to allow indifferent access. | ||||||
|         :user_agent => ( |       options = options.inject({}) do |accu, (key, value)| | ||||||
|           'google-api-ruby-client/' + Google::APIClient::VERSION::STRING |         accu[key.to_s] = value | ||||||
|         ) |         accu | ||||||
|       }.merge(options) |       end | ||||||
|       # Force immediate type-checking and short-cut resolution |       # Almost all API usage will have a host of 'www.googleapis.com'. | ||||||
|       self.parser |       self.host = options["host"] || 'www.googleapis.com' | ||||||
|       self.authorization |       # Most developers will want to leave this value alone. | ||||||
|       self.http_adapter |       self.user_agent = options["user_agent"] || ( | ||||||
|  |         'google-api-ruby-client/' + Google::APIClient::VERSION::STRING | ||||||
|  |       ) | ||||||
|  |       # This is mostly a default for the sake of convenience. | ||||||
|  |       # Unlike most other options, this one may be nil, so we check for | ||||||
|  |       # the presence of the key rather than checking the value. | ||||||
|  |       if options.has_key?("parser") | ||||||
|  |         self.parser = options["parser"] | ||||||
|  |       else | ||||||
|  |         require 'google/api_client/parsers/json_parser' | ||||||
|  |         # NOTE: Do not rely on this default value, as it may change | ||||||
|  |         self.parser = Google::APIClient::JSONParser | ||||||
|  |       end | ||||||
|  |       # The writer method understands a few Symbols and will generate useful | ||||||
|  |       # default authentication mechanisms. | ||||||
|  |       self.authorization = options["authorization"] || :oauth_2 | ||||||
|  |       # The HTTP adapter controls all of the HTTP traffic the client generates. | ||||||
|  |       # By default, Net::HTTP is used, but adding support for other clients | ||||||
|  |       # is trivial. | ||||||
|  |       if options["http_adapter"] | ||||||
|  |         self.http_adapter = options["http_adapter"] | ||||||
|  |       else | ||||||
|  |         require 'httpadapter/adapters/net_http' | ||||||
|  |         # NOTE: Do not rely on this default value, as it may change | ||||||
|  |         self.http_adapter = HTTPAdapter::NetHTTPAdapter.new | ||||||
|  |       end | ||||||
|  |       @discovery_uris = {} | ||||||
|  |       @discovery_documents = {} | ||||||
|  |       @discovered_apis = {} | ||||||
|       return self |       return self | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|     ## |     ## | ||||||
|     # Returns the parser used by the client. |     # Returns the parser used by the client. | ||||||
|     def parser |     # | ||||||
|       unless @options[:parser] |     # @return [#serialize, #parse] | ||||||
|         require 'google/api_client/parsers/json_parser' |     #   The parser used by the client.  Any object that implements both a | ||||||
|         # NOTE: Do not rely on this default value, as it may change |     #   <code>#serialize</code> and a <code>#parse</code> method may be used. | ||||||
|         @options[:parser] = JSONParser |     #   If <code>nil</code>, no parsing will be done. | ||||||
|  |     attr_reader :parser | ||||||
|  | 
 | ||||||
|  |     ## | ||||||
|  |     # Sets the parser used by the client. | ||||||
|  |     # | ||||||
|  |     # @param [#serialize, #parse] new_parser | ||||||
|  |     #   The parser used by the client.  Any object that implements both a | ||||||
|  |     #   <code>#serialize</code> and a <code>#parse</code> method may be used. | ||||||
|  |     #   If <code>nil</code>, no parsing will be done. | ||||||
|  |     def parser=(new_parser) | ||||||
|  |       if new_parser && | ||||||
|  |           !new_parser.respond_to?(:serialize) && | ||||||
|  |           !new_parser.respond_to?(:parse) | ||||||
|  |         raise TypeError, | ||||||
|  |           'Expected parser object to respond to #serialize and #parse.' | ||||||
|       end |       end | ||||||
|       return @options[:parser] |       @parser = new_parser | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     ## |     ## | ||||||
|     # Returns the authorization mechanism used by the client. |     # Returns the authorization mechanism used by the client. | ||||||
|     # |     # | ||||||
|     # @return [#generate_authenticated_request] The authorization mechanism. |     # @return [#generate_authenticated_request] The authorization mechanism. | ||||||
|     def authorization |     attr_reader :authorization | ||||||
|       case @options[:authorization] | 
 | ||||||
|  |     ## | ||||||
|  |     # Sets the authorization mechanism used by the client. | ||||||
|  |     # | ||||||
|  |     # @param [#generate_authenticated_request] new_authorization | ||||||
|  |     #   The new authorization mechanism. | ||||||
|  |     def authorization=(new_authorization) | ||||||
|  |       case new_authorization | ||||||
|       when :oauth_1, :oauth |       when :oauth_1, :oauth | ||||||
|         require 'signet/oauth_1/client' |         require 'signet/oauth_1/client' | ||||||
|         # NOTE: Do not rely on this default value, as it may change |         # NOTE: Do not rely on this default value, as it may change | ||||||
|         @options[:authorization] = Signet::OAuth1::Client.new( |         new_authorization = Signet::OAuth1::Client.new( | ||||||
|           :temporary_credential_uri => |           :temporary_credential_uri => | ||||||
|             'https://www.google.com/accounts/OAuthGetRequestToken', |             'https://www.google.com/accounts/OAuthGetRequestToken', | ||||||
|           :authorization_uri => |           :authorization_uri => | ||||||
|  | @ -76,79 +143,149 @@ module Google | ||||||
|       when :two_legged_oauth_1, :two_legged_oauth |       when :two_legged_oauth_1, :two_legged_oauth | ||||||
|         require 'signet/oauth_1/client' |         require 'signet/oauth_1/client' | ||||||
|         # NOTE: Do not rely on this default value, as it may change |         # NOTE: Do not rely on this default value, as it may change | ||||||
|         @options[:authorization] = Signet::OAuth1::Client.new( |         new_authorization = Signet::OAuth1::Client.new( | ||||||
|           :client_credential_key => nil, |           :client_credential_key => nil, | ||||||
|           :client_credential_secret => nil, |           :client_credential_secret => nil, | ||||||
|           :two_legged => true |           :two_legged => true | ||||||
|         ) |         ) | ||||||
|  |       when :oauth_2 | ||||||
|  |         require 'signet/oauth_2/client' | ||||||
|  |         # NOTE: Do not rely on this default value, as it may change | ||||||
|  |         new_authorization = Signet::OAuth2::Client.new( | ||||||
|  |           :authorization_uri => | ||||||
|  |             'https://accounts.google.com/o/oauth2/auth', | ||||||
|  |           :token_credential_uri => | ||||||
|  |             'https://accounts.google.com/o/oauth2/token' | ||||||
|  |         ) | ||||||
|       when nil |       when nil | ||||||
|         # No authorization mechanism |         # No authorization mechanism | ||||||
|       else |       else | ||||||
|         if !@options[:authorization].respond_to?( |         if !new_authorization.respond_to?(:generate_authenticated_request) | ||||||
|             :generate_authenticated_request) |  | ||||||
|           raise TypeError, |           raise TypeError, | ||||||
|             'Expected authorization mechanism to respond to ' + |             'Expected authorization mechanism to respond to ' + | ||||||
|             '#generate_authenticated_request.' |             '#generate_authenticated_request.' | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
|       return @options[:authorization] |       @authorization = new_authorization | ||||||
|     end |       return @authorization | ||||||
| 
 |  | ||||||
|     ## |  | ||||||
|     # 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 |     end | ||||||
| 
 | 
 | ||||||
|     ## |     ## | ||||||
|     # Returns the HTTP adapter used by the client. |     # Returns the HTTP adapter used by the client. | ||||||
|     def http_adapter |     # | ||||||
|       return @options[:http_adapter] ||= (begin |     # @return [HTTPAdapter] | ||||||
|         require 'httpadapter/adapters/net_http' |     #   The HTTP adapter object.  The object must include the | ||||||
|         @options[:http_adapter] = HTTPAdapter::NetHTTPRequestAdapter |     #   HTTPAdapter module and conform to its interface. | ||||||
|       end) |     attr_reader :http_adapter | ||||||
|  | 
 | ||||||
|  |     ## | ||||||
|  |     # Returns the HTTP adapter used by the client. | ||||||
|  |     # | ||||||
|  |     # @return [HTTPAdapter] | ||||||
|  |     #   The HTTP adapter object.  The object must include the | ||||||
|  |     #   HTTPAdapter module and conform to its interface. | ||||||
|  |     def http_adapter=(new_http_adapter) | ||||||
|  |       if new_http_adapter.kind_of?(HTTPAdapter) | ||||||
|  |         @http_adapter = new_http_adapter | ||||||
|  |       else | ||||||
|  |         raise TypeError, "Expected HTTPAdapter, got #{new_http_adapter.class}." | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     ## | ||||||
|  |     # The API hostname used by the client. | ||||||
|  |     # | ||||||
|  |     # @return [String] | ||||||
|  |     #   The API hostname.  Should almost always be 'www.googleapis.com'. | ||||||
|  |     attr_accessor :host | ||||||
|  | 
 | ||||||
|  |     ## | ||||||
|  |     # The user agent used by the client. | ||||||
|  |     # | ||||||
|  |     # @return [String] | ||||||
|  |     #   The user agent string used in the User-Agent header. | ||||||
|  |     attr_accessor :user_agent | ||||||
|  | 
 | ||||||
|  |     ## | ||||||
|  |     # Returns the URI for the directory document. | ||||||
|  |     # | ||||||
|  |     # @return [Addressable::URI] The URI of the directory document. | ||||||
|  |     def directory_uri | ||||||
|  |       template = Addressable::Template.new( | ||||||
|  |         "https://{host}/discovery/v0.3/directory" | ||||||
|  |       ) | ||||||
|  |       return template.expand({ | ||||||
|  |         "host" => self.host | ||||||
|  |       }) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     ## | ||||||
|  |     # Manually registers a URI as a discovery document for a specific version | ||||||
|  |     # of an API. | ||||||
|  |     # | ||||||
|  |     # @param [String, Symbol] api The service name. | ||||||
|  |     # @param [String] version The desired version of the service. | ||||||
|  |     # @param [Addressable::URI] uri The URI of the discovery document. | ||||||
|  |     def register_discovery_uri(api, version, uri) | ||||||
|  |       api = api.to_s | ||||||
|  |       version = version || 'v1' | ||||||
|  |       @discovery_uris["#{api}:#{version}"] = uri | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     ## |     ## | ||||||
|     # Returns the URI for the discovery document. |     # Returns the URI for the discovery document. | ||||||
|     # |     # | ||||||
|  |     # @param [String, Symbol] api The service name. | ||||||
|  |     # @param [String] version The desired version of the service. | ||||||
|     # @return [Addressable::URI] The URI of the discovery document. |     # @return [Addressable::URI] The URI of the discovery document. | ||||||
|     def discovery_uri |     def discovery_uri(api, version=nil) | ||||||
|       return @options[:discovery_uri] ||= (begin |       api = api.to_s | ||||||
|         if @options[:service] |       version = version || 'v1' | ||||||
|           service_id = @options[:service] |       return @discovery_uris["#{api}:#{version}"] ||= (begin | ||||||
|           service_version = @options[:service_version] || 'v1' |         template = Addressable::Template.new( | ||||||
|           Addressable::URI.parse( |           "https://{host}/discovery/v0.3/describe/" + | ||||||
|             "http://www.googleapis.com/discovery/0.1/describe" + |           "{api}/{version}" | ||||||
|             "?api=#{service_id}" |         ) | ||||||
|           ) |         template.expand({ | ||||||
|         else |           "host" => self.host, | ||||||
|           raise ArgumentError, |           "api" => api, | ||||||
|             'Missing required configuration value, :discovery_uri.' |           "version" => version | ||||||
|         end |         }) | ||||||
|       end) |       end) | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     ## |     ## | ||||||
|     # Sets the discovery URI for the client. |     # Manually registers a pre-loaded discovery document for a specific version | ||||||
|  |     # of an API. | ||||||
|     # |     # | ||||||
|     # @param [Addressable::URI, #to_str, String] new_discovery_uri |     # @param [String, Symbol] api The service name. | ||||||
|     #   The new discovery URI. |     # @param [String] version The desired version of the service. | ||||||
|     def discovery_uri=(new_discovery_uri) |     # @param [String, StringIO] discovery_document | ||||||
|       @options[:discovery_uri] = Addressable::URI.parse(new_discovery_uri) |     #   The contents of the discovery document. | ||||||
|  |     def register_discovery_document(api, version, discovery_document) | ||||||
|  |       api = api.to_s | ||||||
|  |       version = version || 'v1' | ||||||
|  |       if discovery_document.kind_of?(StringIO) | ||||||
|  |         discovery_document.rewind | ||||||
|  |         discovery_document = discovery_document.string | ||||||
|  |       elsif discovery_document.respond_to?(:to_str) | ||||||
|  |         discovery_document = discovery_document.to_str | ||||||
|  |       else | ||||||
|  |         raise TypeError, | ||||||
|  |           "Expected String or StringIO, got #{discovery_document.class}." | ||||||
|  |       end | ||||||
|  |       @discovery_documents["#{api}:#{version}"] = | ||||||
|  |         JSON.parse(discovery_document) | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     ## |     ## | ||||||
|     # Returns the parsed discovery document. |     # Returns the parsed directory document. | ||||||
|     # |     # | ||||||
|     # @return [Hash] The parsed JSON from the discovery document. |     # @return [Hash] The parsed JSON from the directory document. | ||||||
|     def discovery_document |     def directory_document | ||||||
|       return @discovery_document ||= (begin |       return @directory_document ||= (begin | ||||||
|         request = ['GET', self.discovery_uri.to_s, [], []] |         request_uri = self.directory_uri | ||||||
|  |         request = ['GET', request_uri, [], []] | ||||||
|         response = self.transmit_request(request) |         response = self.transmit_request(request) | ||||||
|         status, headers, body = response |         status, headers, body = response | ||||||
|         if status == 200 # TODO(bobaman) Better status code handling? |         if status == 200 # TODO(bobaman) Better status code handling? | ||||||
|  | @ -160,124 +297,130 @@ module Google | ||||||
|           JSON.parse(merged_body.string) |           JSON.parse(merged_body.string) | ||||||
|         else |         else | ||||||
|           raise TransmissionError, |           raise TransmissionError, | ||||||
|             "Could not retrieve discovery document at: #{self.discovery_uri}" |             "Could not retrieve discovery document at: #{request_uri}" | ||||||
|         end |         end | ||||||
|       end) |       end) | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     ## |     ## | ||||||
|     # Returns a list of services this client instance has performed discovery |     # Returns the parsed discovery document. | ||||||
|     # for.  This may return multiple versions of the same service. |  | ||||||
|     # |     # | ||||||
|     # @return [Array] |     # @param [String, Symbol] api The service name. | ||||||
|     #   A list of discovered <code>Google::APIClient::Service</code> objects. |     # @param [String] version The desired version of the service. | ||||||
|     def discovered_services |     # @return [Hash] The parsed JSON from the discovery document. | ||||||
|       return @discovered_services ||= (begin |     def discovery_document(api, version=nil) | ||||||
|         service_names = self.discovery_document['data'].keys() |       api = api.to_s | ||||||
|         services = [] |       version = version || 'v1' | ||||||
|         for service_name in service_names |       return @discovery_documents["#{api}:#{version}"] ||= (begin | ||||||
|           versions = self.discovery_document['data'][service_name] |         request_uri = self.discovery_uri(api, version) | ||||||
|           for service_version in versions.keys() |         request = ['GET', request_uri, [], []] | ||||||
|             service_description = |         response = self.transmit_request(request) | ||||||
|               self.discovery_document['data'][service_name][service_version] |         status, headers, body = response | ||||||
|             services << ::Google::APIClient::Service.new( |         if status == 200 # TODO(bobaman) Better status code handling? | ||||||
|               service_name, |           merged_body = StringIO.new | ||||||
|               service_version, |           body.each do |chunk| | ||||||
|               service_description |             merged_body.write(chunk) | ||||||
|  |           end | ||||||
|  |           merged_body.rewind | ||||||
|  |           JSON.parse(merged_body.string) | ||||||
|  |         else | ||||||
|  |           raise TransmissionError, | ||||||
|  |             "Could not retrieve discovery document at: #{request_uri}" | ||||||
|  |         end | ||||||
|  |       end) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     ## | ||||||
|  |     # Returns all APIs published in the directory document. | ||||||
|  |     # | ||||||
|  |     # @return [Array] The list of available APIs. | ||||||
|  |     def discovered_apis | ||||||
|  |       @directory_apis ||= (begin | ||||||
|  |         document_base = self.directory_uri | ||||||
|  |         if self.directory_document && self.directory_document['items'] | ||||||
|  |           self.directory_document['items'].map do |discovery_document| | ||||||
|  |             ::Google::APIClient::API.new( | ||||||
|  |               document_base, | ||||||
|  |               discovery_document | ||||||
|             ) |             ) | ||||||
|           end |           end | ||||||
|  |         else | ||||||
|  |           [] | ||||||
|         end |         end | ||||||
|         services |  | ||||||
|       end) |       end) | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     ## |     ## | ||||||
|     # Returns the service object for a given service name and service version. |     # Returns the service object for a given service name and service version. | ||||||
|     # |     # | ||||||
|     # @param [String, Symbol] service_name The service name. |     # @param [String, Symbol] api The service name. | ||||||
|     # @param [String] service_version The desired version of the service. |     # @param [String] version The desired version of the service. | ||||||
|     # |     # | ||||||
|     # @return [Google::APIClient::Service] The service object. |     # @return [Google::APIClient::API] The service object. | ||||||
|     def discovered_service(service_name, service_version='v1') |     def discovered_api(api, version=nil) | ||||||
|       if !service_name.kind_of?(String) && !service_name.kind_of?(Symbol) |       if !api.kind_of?(String) && !api.kind_of?(Symbol) | ||||||
|         raise TypeError, |         raise TypeError, | ||||||
|           "Expected String or Symbol, got #{service_name.class}." |           "Expected String or Symbol, got #{api.class}." | ||||||
|       end |       end | ||||||
|       service_name = service_name.to_s |       api = api.to_s | ||||||
|       for service in self.discovered_services |       version = version || 'v1' | ||||||
|         if service.name == service_name && |       return @discovered_apis["#{api}:#{version}"] ||= begin | ||||||
|             service.version.to_s == service_version.to_s |         document_base = self.discovery_uri(api, version) | ||||||
|           return service |         discovery_document = self.discovery_document(api, version) | ||||||
|  |         if document_base && discovery_document | ||||||
|  |           ::Google::APIClient::API.new( | ||||||
|  |             document_base, | ||||||
|  |             discovery_document | ||||||
|  |           ) | ||||||
|  |         else | ||||||
|  |           nil | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
|       return nil |  | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     ## |     ## | ||||||
|     # 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] service_version The desired version of the service. |     # @param [String] version The desired version of the service. | ||||||
|     # |     # | ||||||
|     # @return [Google::APIClient::Method] The method object. |     # @return [Google::APIClient::Method] The method object. | ||||||
|     def discovered_method(rpc_name, service_version='v1') |     def discovered_method(rpc_name, api, version=nil) | ||||||
|       if !rpc_name.kind_of?(String) && !rpc_name.kind_of?(Symbol) |       if !rpc_name.kind_of?(String) && !rpc_name.kind_of?(Symbol) | ||||||
|         raise TypeError, |         raise TypeError, | ||||||
|           "Expected String or Symbol, got #{rpc_name.class}." |           "Expected String or Symbol, got #{rpc_name.class}." | ||||||
|       end |       end | ||||||
|       rpc_name = rpc_name.to_s |       rpc_name = rpc_name.to_s | ||||||
|       for service in self.discovered_services |       api = api.to_s | ||||||
|         # This looks kinda weird, but is not a real problem because there's |       version = version || 'v1' | ||||||
|         # almost always only one service, and this is memoized anyhow. |       service = self.discovered_api(api, version) | ||||||
|         if service.version.to_s == service_version.to_s |       if service.to_h[rpc_name] | ||||||
|           return service.to_h[rpc_name] if service.to_h[rpc_name] |         return service.to_h[rpc_name] | ||||||
|         end |       else | ||||||
|  |         return nil | ||||||
|       end |       end | ||||||
|       return nil |  | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     ## |     ## | ||||||
|     # Returns the service object with the highest version number. |     # Returns the service object with the highest version number. | ||||||
|     # |     # | ||||||
|     # <em>Warning</em>: This method should be used with great care. As APIs |     # @note <em>Warning</em>: This method should be used with great care. | ||||||
|     # are updated, minor differences between versions may cause |     # As APIs are updated, minor differences between versions may cause | ||||||
|     # incompatibilities. Requesting a specific version will avoid this issue. |     # incompatibilities. Requesting a specific version will avoid this issue. | ||||||
|     # |     # | ||||||
|     # @param [String, Symbol] service_name The name of the service. |     # @param [String, Symbol] api The name of the service. | ||||||
|     # |     # | ||||||
|     # @return [Google::APIClient::Service] The service object. |     # @return [Google::APIClient::API] The service object. | ||||||
|     def latest_service_version(service_name) |     def preferred_version(api) | ||||||
|       if !service_name.kind_of?(String) && !service_name.kind_of?(Symbol) |       if !api.kind_of?(String) && !api.kind_of?(Symbol) | ||||||
|         raise TypeError, |         raise TypeError, | ||||||
|           "Expected String or Symbol, got #{service_name.class}." |           "Expected String or Symbol, got #{api.class}." | ||||||
|       end |       end | ||||||
|       service_name = service_name.to_s |       api = api.to_s | ||||||
|       return (self.discovered_services.select do |service| |       # TODO(bobaman): Update to use directory API. | ||||||
|         service.name == service_name |       return self.discovered_apis.detect do |a| | ||||||
|       end).sort.last |         a.name == api && a.preferred == true | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     ## |  | ||||||
|     # Returns the user agent used by the client. |  | ||||||
|     # |  | ||||||
|     # @return [String] |  | ||||||
|     #   The user agent string used in the User-Agent header. |  | ||||||
|     def user_agent |  | ||||||
|       return @options[:user_agent] |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     ## |  | ||||||
|     # Sets the user agent used by the client. |  | ||||||
|     # |  | ||||||
|     # @param [String, #to_str] new_user_agent |  | ||||||
|     #   The new user agent string to use in the User-Agent header. |  | ||||||
|     def user_agent=(new_user_agent) |  | ||||||
|       unless new_user_agent == nil || new_user_agent.respond_to?(:to_str) |  | ||||||
|         raise TypeError, "Expected String, got #{new_user_agent.class}." |  | ||||||
|       end |       end | ||||||
|       new_user_agent = new_user_agent.to_str unless new_user_agent == nil |  | ||||||
|       @options[:user_agent] = new_user_agent |  | ||||||
|       return self.user_agent |  | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     ## |     ## | ||||||
|  | @ -291,16 +434,17 @@ module Google | ||||||
|     # @param [Hash, Array] headers The HTTP headers for the request. |     # @param [Hash, Array] headers The HTTP headers for the request. | ||||||
|     # @param [Hash] options |     # @param [Hash] options | ||||||
|     #   The configuration parameters for the request. |     #   The configuration parameters for the request. | ||||||
|     #   - <code>:service_version</code> —  |     #   - <code>:version</code> —  | ||||||
|     #     The service version.  Only used if <code>api_method</code> is a |     #     The service version.  Only used if <code>api_method</code> is a | ||||||
|     #     <code>String</code>.  Defaults to <code>'v1'</code>. |     #     <code>String</code>.  Defaults to <code>'v1'</code>. | ||||||
|     #   - <code>:parser</code> —  |     #   - <code>:parser</code> —  | ||||||
|     #     The parser for the response. |     #     The parser for the response. | ||||||
|     #   - <code>:authorization</code> —  |     #   - <code>:authorization</code> —  | ||||||
|     #     The authorization mechanism for the response.  Used only if |     #     The authorization mechanism for the response.  Used only if | ||||||
|     #     <code>:signed</code> is <code>true</code>. |     #     <code>:authenticated</code> is <code>true</code>. | ||||||
|     #   - <code>:signed</code> —  |     #   - <code>:authenticated</code> —  | ||||||
|     #     <code>true</code> if the request must be signed, <code>false</code> |     #     <code>true</code> if the request must be signed or otherwise | ||||||
|  |     #     authenticated, <code>false</code> | ||||||
|     #     otherwise.  Defaults to <code>true</code> if an authorization |     #     otherwise.  Defaults to <code>true</code> if an authorization | ||||||
|     #     mechanism has been set, <code>false</code> otherwise. |     #     mechanism has been set, <code>false</code> otherwise. | ||||||
|     # |     # | ||||||
|  | @ -316,19 +460,27 @@ module Google | ||||||
|         api_method, parameters={}, body='', headers=[], options={}) |         api_method, parameters={}, body='', headers=[], options={}) | ||||||
|       options={ |       options={ | ||||||
|         :parser => self.parser, |         :parser => self.parser, | ||||||
|         :service_version => 'v1', |         :version => 'v1', | ||||||
|         :authorization => self.authorization |         :authorization => self.authorization | ||||||
|       }.merge(options) |       }.merge(options) | ||||||
|       # The default value for the :signed option depends on whether an |       # The default value for the :authenticated option depends on whether an | ||||||
|       # authorization mechanism has been set. |       # authorization mechanism has been set. | ||||||
|       if options[:authorization] |       if options[:authorization] | ||||||
|         options = {:signed => true}.merge(options) |         options = {:authenticated => true}.merge(options) | ||||||
|       else |       else | ||||||
|         options = {:signed => false}.merge(options) |         options = {:authenticated => false}.merge(options) | ||||||
|       end |       end | ||||||
|       if api_method.kind_of?(String) || api_method.kind_of?(Symbol) |       if api_method.kind_of?(String) || api_method.kind_of?(Symbol) | ||||||
|  |         api_method = api_method.to_s | ||||||
|  |         # 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. | ||||||
|  |         api = api_method[/^([^.]+)\./, 1] | ||||||
|         api_method = self.discovered_method( |         api_method = self.discovered_method( | ||||||
|           api_method.to_s, options[:service_version] |           api_method, api, options[:version] | ||||||
|         ) |         ) | ||||||
|       elsif !api_method.kind_of?(::Google::APIClient::Method) |       elsif !api_method.kind_of?(::Google::APIClient::Method) | ||||||
|         raise TypeError, |         raise TypeError, | ||||||
|  | @ -339,8 +491,8 @@ module Google | ||||||
|         raise ArgumentError, "API method could not be found." |         raise ArgumentError, "API method could not be found." | ||||||
|       end |       end | ||||||
|       request = api_method.generate_request(parameters, body, headers) |       request = api_method.generate_request(parameters, body, headers) | ||||||
|       if options[:signed] |       if options[:authenticated] | ||||||
|         request = self.sign_request(request, options[:authorization]) |         request = self.generate_authenticated_request(:request => request) | ||||||
|       end |       end | ||||||
|       return request |       return request | ||||||
|     end |     end | ||||||
|  | @ -356,7 +508,7 @@ module Google | ||||||
|     # @param [Hash, Array] headers The HTTP headers for the request. |     # @param [Hash, Array] headers The HTTP headers for the request. | ||||||
|     # @param [Hash] options |     # @param [Hash] options | ||||||
|     #   The configuration parameters for the request. |     #   The configuration parameters for the request. | ||||||
|     #   - <code>:service_version</code> —  |     #   - <code>:version</code> —  | ||||||
|     #     The service version.  Only used if <code>api_method</code> is a |     #     The service version.  Only used if <code>api_method</code> is a | ||||||
|     #     <code>String</code>.  Defaults to <code>'v1'</code>. |     #     <code>String</code>.  Defaults to <code>'v1'</code>. | ||||||
|     #   - <code>:adapter</code> —  |     #   - <code>:adapter</code> —  | ||||||
|  | @ -365,9 +517,10 @@ module Google | ||||||
|     #     The parser for the response. |     #     The parser for the response. | ||||||
|     #   - <code>:authorization</code> —  |     #   - <code>:authorization</code> —  | ||||||
|     #     The authorization mechanism for the response.  Used only if |     #     The authorization mechanism for the response.  Used only if | ||||||
|     #     <code>:signed</code> is <code>true</code>. |     #     <code>:authenticated</code> is <code>true</code>. | ||||||
|     #   - <code>:signed</code> —  |     #   - <code>:authenticated</code> —  | ||||||
|     #     <code>true</code> if the request must be signed, <code>false</code> |     #     <code>true</code> if the request must be signed or otherwise | ||||||
|  |     #     authenticated, <code>false</code> | ||||||
|     #     otherwise.  Defaults to <code>true</code>. |     #     otherwise.  Defaults to <code>true</code>. | ||||||
|     # |     # | ||||||
|     # @return [Array] The response from the API. |     # @return [Array] The response from the API. | ||||||
|  | @ -406,25 +559,26 @@ module Google | ||||||
|             include Enumerable |             include Enumerable | ||||||
|           end |           end | ||||||
|         end |         end | ||||||
|         unless headers.any? { |k, v| k.downcase == 'user-agent' } |         if self.user_agent.kind_of?(String) | ||||||
|           headers = headers.to_a.insert(0, ['User-Agent', self.user_agent]) |           unless headers.any? { |k, v| k.downcase == 'User-Agent'.downcase } | ||||||
|  |             headers = headers.to_a.insert(0, ['User-Agent', self.user_agent]) | ||||||
|  |           end | ||||||
|  |         elsif self.user_agent != nil | ||||||
|  |           raise TypeError, | ||||||
|  |             "Expected User-Agent to be String, got #{self.user_agent.class}" | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
|       ::HTTPAdapter.transmit([method, uri, headers, body], adapter) |       adapter.transmit([method, uri, headers, body]) | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     ## |     ## | ||||||
|     # Signs a request using the current authorization mechanism. |     # Signs a request using the current authorization mechanism. | ||||||
|     # |     # | ||||||
|     # @param [Array] request The request to sign. |     # @param [Hash] options The options to pass through. | ||||||
|     # @param [#generate_authenticated_request] authorization |  | ||||||
|     #   The authorization mechanism. |  | ||||||
|     # |     # | ||||||
|     # @return [Array] The signed request. |     # @return [Array] The signed or otherwise authenticated request. | ||||||
|     def sign_request(request, authorization=self.authorization) |     def generate_authenticated_request(options={}) | ||||||
|       return authorization.generate_authenticated_request( |       return authorization.generate_authenticated_request(options) | ||||||
|         :request => request |  | ||||||
|       ) |  | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -12,42 +12,37 @@ | ||||||
| # See the License for the specific language governing permissions and | # See the License for the specific language governing permissions and | ||||||
| # limitations under the License. | # limitations under the License. | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| require 'json' | require 'json' | ||||||
| require 'addressable/uri' | require 'addressable/uri' | ||||||
| require 'addressable/template' | require 'addressable/template' | ||||||
| 
 | 
 | ||||||
| require 'google/inflection' | require 'google/inflection' | ||||||
|  | require 'google/api_client/errors' | ||||||
| 
 | 
 | ||||||
| module Google | module Google | ||||||
|   class APIClient |   class APIClient | ||||||
|     ## |  | ||||||
|     # An exception that is raised if a method is called with missing or |  | ||||||
|     # invalid parameter values. |  | ||||||
|     class ValidationError < StandardError |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     ## |     ## | ||||||
|     # A service that has been described by a discovery document. |     # A service that has been described by a discovery document. | ||||||
|     class Service |     class API | ||||||
| 
 | 
 | ||||||
|       ## |       ## | ||||||
|       # Creates a description of a particular version of a service. |       # Creates a description of a particular version of a service. | ||||||
|       # |       # | ||||||
|       # @param [String] service_name |       # @param [String] api | ||||||
|       #   The identifier for the service.  Note that while this frequently |       #   The identifier for the service.  Note that while this frequently | ||||||
|       #   matches the first segment of all of the service's RPC names, this |       #   matches the first segment of all of the service's RPC names, this | ||||||
|       #   should not be assumed.  There is no requirement that these match. |       #   should not be assumed.  There is no requirement that these match. | ||||||
|       # @param [String] service_version |       # @param [String] version | ||||||
|       #   The identifier for the service version. |       #   The identifier for the service version. | ||||||
|       # @param [Hash] service_description |       # @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. | ||||||
|       # |       # | ||||||
|       # @return [Google::APIClient::Service] The constructed service object. |       # @return [Google::APIClient::API] The constructed service object. | ||||||
|       def initialize(service_name, service_version, service_description) |       def initialize(document_base, discovery_document) | ||||||
|         @name = service_name |         @document_base = Addressable::URI.parse(document_base) | ||||||
|         @version = service_version |         @discovery_document = discovery_document | ||||||
|         @description = service_description |  | ||||||
|         metaclass = (class <<self; self; end) |         metaclass = (class <<self; self; end) | ||||||
|         self.resources.each do |resource| |         self.resources.each do |resource| | ||||||
|           method_name = Google::INFLECTOR.underscore(resource.name).to_sym |           method_name = Google::INFLECTOR.underscore(resource.name).to_sym | ||||||
|  | @ -63,31 +58,67 @@ module Google | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|  |       ## | ||||||
|  |       # Returns the id of the service. | ||||||
|  |       # | ||||||
|  |       # @return [String] The service id. | ||||||
|  |       def id | ||||||
|  |         return @discovery_document['id'] | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|       ## |       ## | ||||||
|       # Returns the identifier for the service. |       # Returns the identifier for the service. | ||||||
|       # |       # | ||||||
|       # @return [String] The service identifier. |       # @return [String] The service identifier. | ||||||
|       attr_reader :name |       def name | ||||||
|  |         return @discovery_document['name'] | ||||||
|  |       end | ||||||
| 
 | 
 | ||||||
|       ## |       ## | ||||||
|       # Returns the version of the service. |       # Returns the version of the service. | ||||||
|       # |       # | ||||||
|       # @return [String] The service version. |       # @return [String] The service version. | ||||||
|       attr_reader :version |       def version | ||||||
|  |         return @discovery_document['version'] | ||||||
|  |       end | ||||||
| 
 | 
 | ||||||
|       ## |       ## | ||||||
|       # Returns the parsed section of the discovery document that applies to |       # Returns the parsed section of the discovery document that applies to | ||||||
|       # this version of the service. |       # this version of the service. | ||||||
|       # |       # | ||||||
|       # @return [Hash] The service description. |       # @return [Hash] The service description. | ||||||
|       attr_reader :description |       def description | ||||||
|  |         return @discovery_document['description'] | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       ## | ||||||
|  |       # Returns true if this is the preferred version of this API. | ||||||
|  |       # | ||||||
|  |       # @return [TrueClass, FalseClass] | ||||||
|  |       #   Whether or not this is the preferred version of this API. | ||||||
|  |       def preferred | ||||||
|  |         return @discovery_document['preferred'] | ||||||
|  |       end | ||||||
| 
 | 
 | ||||||
|       ## |       ## | ||||||
|       # Returns the base URI for this version of the service. |       # Returns the base URI for this version of the service. | ||||||
|       # |       # | ||||||
|       # @return [Addressable::URI] The base URI that methods are joined to. |       # @return [Addressable::URI] The base URI that methods are joined to. | ||||||
|       def base |       attr_reader :document_base | ||||||
|         return @base ||= Addressable::URI.parse(self.description['baseUrl']) | 
 | ||||||
|  |       ## | ||||||
|  |       # Returns the base URI for this version of the service. | ||||||
|  |       # | ||||||
|  |       # @return [Addressable::URI] The base URI that methods are joined to. | ||||||
|  |       def rest_base | ||||||
|  |         if @discovery_document['restBasePath'] | ||||||
|  |           return @rest_base ||= ( | ||||||
|  |             self.document_base + | ||||||
|  |             Addressable::URI.parse(@discovery_document['restBasePath']) | ||||||
|  |           ).normalize | ||||||
|  |         else | ||||||
|  |           return nil | ||||||
|  |         end | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       ## |       ## | ||||||
|  | @ -95,13 +126,13 @@ module Google | ||||||
|       # |       # | ||||||
|       # @param [Addressable::URI, #to_str, String] new_base |       # @param [Addressable::URI, #to_str, String] new_base | ||||||
|       #   The new base URI to use for the service. |       #   The new base URI to use for the service. | ||||||
|       def base=(new_base) |       def rest_base=(new_rest_base) | ||||||
|         @base = Addressable::URI.parse(new_base) |         @rest_base = Addressable::URI.parse(new_rest_base) | ||||||
|         self.resources.each do |resource| |         self.resources.each do |resource| | ||||||
|           resource.base = @base |           resource.rest_base = @rest_base | ||||||
|         end |         end | ||||||
|         self.methods.each do |method| |         self.methods.each do |method| | ||||||
|           method.base = @base |           method.rest_base = @rest_base | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|  | @ -112,8 +143,8 @@ module Google | ||||||
|       # @return [Array] A list of {Google::APIClient::Resource} objects. |       # @return [Array] A list of {Google::APIClient::Resource} objects. | ||||||
|       def resources |       def resources | ||||||
|         return @resources ||= ( |         return @resources ||= ( | ||||||
|           (self.description['resources'] || []).inject([]) do |accu, (k, v)| |           (@discovery_document['resources'] || []).inject([]) do |accu, (k, v)| | ||||||
|             accu << ::Google::APIClient::Resource.new(self.base, k, v) |             accu << ::Google::APIClient::Resource.new(self.rest_base, k, v) | ||||||
|             accu |             accu | ||||||
|           end |           end | ||||||
|         ) |         ) | ||||||
|  | @ -126,8 +157,8 @@ module Google | ||||||
|       # @return [Array] A list of {Google::APIClient::Method} objects. |       # @return [Array] A list of {Google::APIClient::Method} objects. | ||||||
|       def methods |       def methods | ||||||
|         return @methods ||= ( |         return @methods ||= ( | ||||||
|           (self.description['methods'] || []).inject([]) do |accu, (k, v)| |           (@discovery_document['methods'] || []).inject([]) do |accu, (k, v)| | ||||||
|             accu << ::Google::APIClient::Method.new(self.base, k, v) |             accu << ::Google::APIClient::Method.new(self.rest_base, k, v) | ||||||
|             accu |             accu | ||||||
|           end |           end | ||||||
|         ) |         ) | ||||||
|  | @ -140,12 +171,12 @@ module Google | ||||||
|       # |       # | ||||||
|       # @example |       # @example | ||||||
|       #   # Discover available methods |       #   # Discover available methods | ||||||
|       #   method_names = client.discovered_service('buzz').to_h.keys |       #   method_names = client.discovered_api('buzz').to_h.keys | ||||||
|       def to_h |       def to_h | ||||||
|         return @hash ||= (begin |         return @hash ||= (begin | ||||||
|           methods_hash = {} |           methods_hash = {} | ||||||
|           self.methods.each do |method| |           self.methods.each do |method| | ||||||
|             methods_hash[method.rpc_name] = method |             methods_hash[method.rpc_method] = method | ||||||
|           end |           end | ||||||
|           self.resources.each do |resource| |           self.resources.each do |resource| | ||||||
|             methods_hash.merge!(resource.to_h) |             methods_hash.merge!(resource.to_h) | ||||||
|  | @ -154,48 +185,6 @@ module Google | ||||||
|         end) |         end) | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       ## |  | ||||||
|       # Compares two versions of a service. |  | ||||||
|       # |  | ||||||
|       # @param [Object] other The service to compare. |  | ||||||
|       # |  | ||||||
|       # @return [Integer] |  | ||||||
|       #   <code>-1</code> if the service is older than <code>other</code>. |  | ||||||
|       #   <code>0</code> if the service is the same as <code>other</code>. |  | ||||||
|       #   <code>1</code> if the service is newer than <code>other</code>. |  | ||||||
|       #   <code>nil</code> if the service cannot be compared to |  | ||||||
|       #   <code>other</code>. |  | ||||||
|       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] |  | ||||||
|             if dotted_version && suffix |  | ||||||
|               [dotted_version.split('.').map { |v| v.to_i }, suffix] |  | ||||||
|             else |  | ||||||
|               [[-1], version] |  | ||||||
|             end |  | ||||||
|           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 <code>String</code> representation of the service's state. |       # Returns a <code>String</code> representation of the service's state. | ||||||
|       # |       # | ||||||
|  | @ -222,10 +211,10 @@ module Google | ||||||
|       #   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. | ||||||
|       def initialize(base, resource_name, resource_description) |       def initialize(rest_base, resource_name, discovery_document) | ||||||
|         @base = base |         @rest_base = rest_base | ||||||
|         @name = resource_name |         @name = resource_name | ||||||
|         @description = resource_description |         @discovery_document = discovery_document | ||||||
|         metaclass = (class <<self; self; end) |         metaclass = (class <<self; self; end) | ||||||
|         self.resources.each do |resource| |         self.resources.each do |resource| | ||||||
|           method_name = Google::INFLECTOR.underscore(resource.name).to_sym |           method_name = Google::INFLECTOR.underscore(resource.name).to_sym | ||||||
|  | @ -258,20 +247,20 @@ module Google | ||||||
|       # Returns the base URI for this resource. |       # Returns the base URI for this resource. | ||||||
|       # |       # | ||||||
|       # @return [Addressable::URI] The base URI that methods are joined to. |       # @return [Addressable::URI] The base URI that methods are joined to. | ||||||
|       attr_reader :base |       attr_reader :rest_base | ||||||
| 
 | 
 | ||||||
|       ## |       ## | ||||||
|       # 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_base | ||||||
|       #   The new base URI to use for the resource. |       #   The new base URI to use for the resource. | ||||||
|       def base=(new_base) |       def rest_base=(new_rest_base) | ||||||
|         @base = Addressable::URI.parse(new_base) |         @rest_base = Addressable::URI.parse(new_rest_base) | ||||||
|         self.resources.each do |resource| |         self.resources.each do |resource| | ||||||
|           resource.base = @base |           resource.rest_base = @rest_base | ||||||
|         end |         end | ||||||
|         self.methods.each do |method| |         self.methods.each do |method| | ||||||
|           method.base = @base |           method.rest_base = @rest_base | ||||||
|         end |         end | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|  | @ -281,8 +270,8 @@ module Google | ||||||
|       # @return [Array] A list of {Google::APIClient::Resource} objects. |       # @return [Array] A list of {Google::APIClient::Resource} objects. | ||||||
|       def resources |       def resources | ||||||
|         return @resources ||= ( |         return @resources ||= ( | ||||||
|           (self.description['resources'] || []).inject([]) do |accu, (k, v)| |           (@discovery_document['resources'] || []).inject([]) do |accu, (k, v)| | ||||||
|             accu << ::Google::APIClient::Resource.new(self.base, k, v) |             accu << ::Google::APIClient::Resource.new(self.rest_base, k, v) | ||||||
|             accu |             accu | ||||||
|           end |           end | ||||||
|         ) |         ) | ||||||
|  | @ -294,8 +283,8 @@ module Google | ||||||
|       # @return [Array] A list of {Google::APIClient::Method} objects. |       # @return [Array] A list of {Google::APIClient::Method} objects. | ||||||
|       def methods |       def methods | ||||||
|         return @methods ||= ( |         return @methods ||= ( | ||||||
|           (self.description['methods'] || []).inject([]) do |accu, (k, v)| |           (@discovery_document['methods'] || []).inject([]) do |accu, (k, v)| | ||||||
|             accu << ::Google::APIClient::Method.new(self.base, k, v) |             accu << ::Google::APIClient::Method.new(self.rest_base, k, v) | ||||||
|             accu |             accu | ||||||
|           end |           end | ||||||
|         ) |         ) | ||||||
|  | @ -310,7 +299,7 @@ module Google | ||||||
|         return @hash ||= (begin |         return @hash ||= (begin | ||||||
|           methods_hash = {} |           methods_hash = {} | ||||||
|           self.methods.each do |method| |           self.methods.each do |method| | ||||||
|             methods_hash[method.rpc_name] = method |             methods_hash[method.rpc_method] = method | ||||||
|           end |           end | ||||||
|           self.resources.each do |resource| |           self.resources.each do |resource| | ||||||
|             methods_hash.merge!(resource.to_h) |             methods_hash.merge!(resource.to_h) | ||||||
|  | @ -337,7 +326,7 @@ module Google | ||||||
|       ## |       ## | ||||||
|       # Creates a description of a particular method. |       # Creates a description of a particular method. | ||||||
|       # |       # | ||||||
|       # @param [Addressable::URI] base |       # @param [Addressable::URI] rest_base | ||||||
|       #   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. | ||||||
|  | @ -345,10 +334,10 @@ module Google | ||||||
|       #   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. | ||||||
|       def initialize(base, method_name, method_description) |       def initialize(rest_base, method_name, discovery_document) | ||||||
|         @base = base |         @rest_base = rest_base | ||||||
|         @name = method_name |         @name = method_name | ||||||
|         @description = method_description |         @discovery_document = discovery_document | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       ## |       ## | ||||||
|  | @ -369,15 +358,15 @@ module Google | ||||||
|       # |       # | ||||||
|       # @return [Addressable::URI] |       # @return [Addressable::URI] | ||||||
|       #   The base URI that this method will be joined to. |       #   The base URI that this method will be joined to. | ||||||
|       attr_reader :base |       attr_reader :rest_base | ||||||
| 
 | 
 | ||||||
|       ## |       ## | ||||||
|       # 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_base | ||||||
|       #   The new base URI to use for the method. |       #   The new base URI to use for the method. | ||||||
|       def base=(new_base) |       def rest_base=(new_rest_base) | ||||||
|         @base = Addressable::URI.parse(new_base) |         @rest_base = Addressable::URI.parse(new_rest_base) | ||||||
|         @uri_template = nil |         @uri_template = nil | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|  | @ -385,8 +374,8 @@ module Google | ||||||
|       # Returns the RPC name for the method. |       # Returns the RPC name for the method. | ||||||
|       # |       # | ||||||
|       # @return [String] The RPC name. |       # @return [String] The RPC name. | ||||||
|       def rpc_name |       def rpc_method | ||||||
|         return self.description['rpcName'] |         return @discovery_document['rpcMethod'] | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       ## |       ## | ||||||
|  | @ -399,8 +388,9 @@ module Google | ||||||
|         # a join operation on a URI, but we have to treat these as Strings |         # a join operation on a URI, but we have to treat these as Strings | ||||||
|         # because of the way the discovery document provides the URIs. |         # because of the way the discovery document provides the URIs. | ||||||
|         # This should be fixed soon. |         # This should be fixed soon. | ||||||
|         return @uri_template ||= |         return @uri_template ||= Addressable::Template.new( | ||||||
|           Addressable::Template.new(base.to_s + self.description['pathUrl']) |           self.rest_base + @discovery_document['restPath'] | ||||||
|  |         ) | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       ## |       ## | ||||||
|  | @ -475,7 +465,7 @@ module Google | ||||||
|         if !headers.kind_of?(Array) && !headers.kind_of?(Hash) |         if !headers.kind_of?(Array) && !headers.kind_of?(Hash) | ||||||
|           raise TypeError, "Expected Hash or Array, got #{headers.class}." |           raise TypeError, "Expected Hash or Array, got #{headers.class}." | ||||||
|         end |         end | ||||||
|         method = self.description['httpMethod'] || 'GET' |         method = @discovery_document['httpMethod'] || 'GET' | ||||||
|         uri = self.generate_uri(parameters) |         uri = self.generate_uri(parameters) | ||||||
|         headers = headers.to_a if headers.kind_of?(Hash) |         headers = headers.to_a if headers.kind_of?(Hash) | ||||||
|         return [method, uri.to_str, headers, [body]] |         return [method, uri.to_str, headers, [body]] | ||||||
|  | @ -488,7 +478,7 @@ module Google | ||||||
|       # @return [Hash] The parameter descriptions. |       # @return [Hash] The parameter descriptions. | ||||||
|       def parameter_descriptions |       def parameter_descriptions | ||||||
|         @parameter_descriptions ||= ( |         @parameter_descriptions ||= ( | ||||||
|           self.description['parameters'] || {} |           @discovery_document['parameters'] || {} | ||||||
|         ).inject({}) { |h,(k,v)| h[k]=v; h } |         ).inject({}) { |h,(k,v)| h[k]=v; h } | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|  | @ -498,7 +488,7 @@ module Google | ||||||
|       # @return [Array] The parameters. |       # @return [Array] The parameters. | ||||||
|       def parameters |       def parameters | ||||||
|         @parameters ||= (( |         @parameters ||= (( | ||||||
|           self.description['parameters'] || {} |           @discovery_document['parameters'] || {} | ||||||
|         ).inject({}) { |h,(k,v)| h[k]=v; h }).keys |         ).inject({}) { |h,(k,v)| h[k]=v; h }).keys | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|  | @ -552,6 +542,12 @@ module Google | ||||||
|         end |         end | ||||||
|         parameters.each do |k, v| |         parameters.each do |k, v| | ||||||
|           if self.parameter_descriptions[k] |           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'] |             pattern = self.parameter_descriptions[k]['pattern'] | ||||||
|             if pattern |             if pattern | ||||||
|               regexp = Regexp.new("^#{pattern}$") |               regexp = Regexp.new("^#{pattern}$") | ||||||
|  | @ -572,7 +568,8 @@ module Google | ||||||
|       # @return [String] The method's state, as a <code>String</code>. |       # @return [String] The method's state, as a <code>String</code>. | ||||||
|       def inspect |       def inspect | ||||||
|         sprintf( |         sprintf( | ||||||
|           "#<%s:%#0x NAME:%s>", self.class.to_s, self.object_id, self.rpc_name |           "#<%s:%#0x NAME:%s>", | ||||||
|  |           self.class.to_s, self.object_id, self.rpc_method | ||||||
|         ) |         ) | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|  |  | ||||||
|  | @ -0,0 +1,30 @@ | ||||||
|  | # 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. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | module Google | ||||||
|  |   class APIClient | ||||||
|  |     ## | ||||||
|  |     # An error which is raised when there is an unexpected response or other | ||||||
|  |     # transport error that prevents an operation from succeeding. | ||||||
|  |     class TransmissionError < StandardError | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     ## | ||||||
|  |     # An exception that is raised if a method is called with missing or | ||||||
|  |     # invalid parameter values. | ||||||
|  |     class ValidationError < StandardError | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -12,6 +12,7 @@ | ||||||
| # See the License for the specific language governing permissions and | # See the License for the specific language governing permissions and | ||||||
| # limitations under the License. | # limitations under the License. | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| require 'json' | require 'json' | ||||||
| 
 | 
 | ||||||
| module Google | module Google | ||||||
|  |  | ||||||
|  | @ -12,12 +12,13 @@ | ||||||
| # See the License for the specific language governing permissions and | # See the License for the specific language governing permissions and | ||||||
| # limitations under the License. | # limitations under the License. | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| module Google | module Google | ||||||
|   class APIClient |   class APIClient | ||||||
|     module VERSION |     module VERSION | ||||||
|       MAJOR = 0 |       MAJOR = 0 | ||||||
|       MINOR = 1 |       MINOR = 2 | ||||||
|       TINY  = 3 |       TINY  = 0 | ||||||
| 
 | 
 | ||||||
|       STRING = [MAJOR, MINOR, TINY].join('.') |       STRING = [MAJOR, MINOR, TINY].join('.') | ||||||
|     end |     end | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ | ||||||
| # See the License for the specific language governing permissions and | # See the License for the specific language governing permissions and | ||||||
| # limitations under the License. | # limitations under the License. | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| module Google | module Google | ||||||
|   if defined?(ActiveSupport::Inflector) |   if defined?(ActiveSupport::Inflector) | ||||||
|     INFLECTOR = ActiveSupport::Inflector |     INFLECTOR = ActiveSupport::Inflector | ||||||
|  |  | ||||||
|  | @ -21,422 +21,422 @@ require 'google/api_client' | ||||||
| require 'google/api_client/version' | require 'google/api_client/version' | ||||||
| require 'google/api_client/parsers/json_parser' | require 'google/api_client/parsers/json_parser' | ||||||
| 
 | 
 | ||||||
| describe Google::APIClient, 'unconfigured' do | describe Google::APIClient do | ||||||
|   before do |   before do | ||||||
|     @client = Google::APIClient.new |     @client = Google::APIClient.new | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it 'should not be able to determine the discovery URI' do |   it 'should raise a type error for bogus authorization' do | ||||||
|     (lambda do |     (lambda do | ||||||
|       @client.discovery_uri |       Google::APIClient.new(:authorization => 42) | ||||||
|     end).should raise_error(ArgumentError) |     end).should raise_error(TypeError) | ||||||
|   end |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| describe Google::APIClient, 'configured for a bogus API' do |  | ||||||
|   before do |  | ||||||
|     @client = Google::APIClient.new(:service => 'bogus') |  | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it 'should not be able to retrieve the discovery document' do |   it 'should not be able to retrieve the discovery document for a bogus API' do | ||||||
|     (lambda do |     (lambda do | ||||||
|       @client.discovery_document |       @client.discovery_document('bogus') | ||||||
|  |     end).should raise_error(Google::APIClient::TransmissionError) | ||||||
|  |     (lambda do | ||||||
|  |       @client.discovered_api('bogus') | ||||||
|     end).should raise_error(Google::APIClient::TransmissionError) |     end).should raise_error(Google::APIClient::TransmissionError) | ||||||
|   end |   end | ||||||
| end |  | ||||||
| 
 | 
 | ||||||
| describe Google::APIClient, 'configured for bogus authorization' do |   it 'should raise an error for bogus services' do | ||||||
|   it 'should raise a type error' do |  | ||||||
|     (lambda do |     (lambda do | ||||||
|       Google::APIClient.new(:service => 'prediction', :authorization => 42) |       @client.discovered_api(42) | ||||||
|     end).should raise_error(TypeError) |     end).should raise_error(TypeError) | ||||||
|   end |   end | ||||||
| end |  | ||||||
| 
 |  | ||||||
| describe Google::APIClient, 'configured for the prediction API' do |  | ||||||
|   before do |  | ||||||
|     @client = Google::APIClient.new(:service => 'prediction') |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   it 'should correctly determine the discovery URI' do |  | ||||||
|     @client.discovery_uri.should === |  | ||||||
|       'http://www.googleapis.com/discovery/0.1/describe?api=prediction' |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   it 'should have multiple versions available' do |  | ||||||
|     @client.discovered_services.size.should > 1 |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   it 'should find APIs that are in the discovery document' do |  | ||||||
|     @client.discovered_service('prediction').name.should == 'prediction' |  | ||||||
|     @client.discovered_service('prediction').version.should == 'v1' |  | ||||||
|     @client.discovered_service(:prediction).name.should == 'prediction' |  | ||||||
|     @client.discovered_service(:prediction).version.should == 'v1' |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   it 'should find API versions that are in the discovery document' do |  | ||||||
|     @client.discovered_service('prediction', 'v1.1').version.should == 'v1.1' |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   it 'should not find APIs that are not in the discovery document' do |  | ||||||
|     @client.discovered_service('bogus').should == nil |  | ||||||
|   end |  | ||||||
| 
 | 
 | ||||||
|   it 'should raise an error for bogus services' do |   it 'should raise an error for bogus services' do | ||||||
|     (lambda do |     (lambda do | ||||||
|       @client.discovered_service(42) |       @client.preferred_version(42) | ||||||
|     end).should raise_error(TypeError) |     end).should raise_error(TypeError) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it 'should find methods that are in the discovery document' do |  | ||||||
|     @client.discovered_method('prediction.training.insert').name.should == |  | ||||||
|       'insert' |  | ||||||
|     @client.discovered_method(:'prediction.training.insert').name.should == |  | ||||||
|       'insert' |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   it 'should find methods for versions that are in the discovery document' do |  | ||||||
|     @client.discovered_method( |  | ||||||
|       'prediction.training.delete', 'v1.1' |  | ||||||
|     ).should_not == nil |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   it 'should not find methods that are not in the discovery document' do |  | ||||||
|     @client.discovered_method('prediction.training.delete', 'v1').should == nil |  | ||||||
|     @client.discovered_method('prediction.bogus').should == nil |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   it 'should raise an error for bogus methods' do |  | ||||||
|     (lambda do |  | ||||||
|       @client.discovered_method(42) |  | ||||||
|     end).should raise_error(TypeError) |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   it 'should correctly determine the latest version' do |  | ||||||
|     @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_version(42) |  | ||||||
|     end).should raise_error(TypeError) |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   it 'should correctly determine the latest version' do |  | ||||||
|     # Sanity check the algorithm |  | ||||||
|     @client.discovered_services.clear |  | ||||||
|     @client.discovered_services << |  | ||||||
|       Google::APIClient::Service.new('magic', 'v1', {}) |  | ||||||
|     @client.discovered_services << |  | ||||||
|       Google::APIClient::Service.new('magic', 'v1.1', {}) |  | ||||||
|     @client.discovered_services << |  | ||||||
|       Google::APIClient::Service.new('magic', 'v1.10', {}) |  | ||||||
|     @client.discovered_services << |  | ||||||
|       Google::APIClient::Service.new('magic', 'v10.0.1', {}) |  | ||||||
|     @client.discovered_services << |  | ||||||
|       Google::APIClient::Service.new('magic', 'v10.1', {}) |  | ||||||
|     @client.discovered_services << |  | ||||||
|       Google::APIClient::Service.new('magic', 'v2.1', {}) |  | ||||||
|     @client.discovered_services << |  | ||||||
|       Google::APIClient::Service.new('magic', 'v10.0', {}) |  | ||||||
|     @client.latest_service_version('magic').version.should == 'v10.1' |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   it 'should correctly determine the latest version' do |  | ||||||
|     # Sanity check the algorithm |  | ||||||
|     @client.discovered_services.clear |  | ||||||
|     @client.discovered_services << |  | ||||||
|       Google::APIClient::Service.new('one', 'v3', {}) |  | ||||||
|     @client.discovered_services << |  | ||||||
|       Google::APIClient::Service.new('two', 'v1', {}) |  | ||||||
|     @client.discovered_services << |  | ||||||
|       Google::APIClient::Service.new('two', 'v1.1-r1c3', {}) |  | ||||||
|     @client.discovered_services << |  | ||||||
|       Google::APIClient::Service.new('two', 'v2', {}) |  | ||||||
|     @client.discovered_services << |  | ||||||
|       Google::APIClient::Service.new('two', 'v2beta1', {}) |  | ||||||
|     @client.discovered_services << |  | ||||||
|       Google::APIClient::Service.new('two', 'test2', {}) |  | ||||||
|     @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_version('bogus').should == nil |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   it 'should generate valid requests' do |  | ||||||
|     request = @client.generate_request( |  | ||||||
|       'prediction.training.insert', |  | ||||||
|       {'query' => '12345'} |  | ||||||
|     ) |  | ||||||
|     method, uri, headers, body = request |  | ||||||
|     method.should == 'POST' |  | ||||||
|     uri.should == |  | ||||||
|       'https://www.googleapis.com/prediction/v1/training?query=12345' |  | ||||||
|     (headers.inject({}) { |h,(k,v)| h[k]=v; h }).should == {} |  | ||||||
|     body.should respond_to(:each) |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   it 'should generate requests against the correct URIs' do |  | ||||||
|     request = @client.generate_request( |  | ||||||
|       :'prediction.training.insert', |  | ||||||
|       {'query' => '12345'} |  | ||||||
|     ) |  | ||||||
|     method, uri, headers, body = request |  | ||||||
|     uri.should == |  | ||||||
|       'https://www.googleapis.com/prediction/v1/training?query=12345' |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   it 'should generate requests against the correct URIs' do |  | ||||||
|     prediction = @client.discovered_service('prediction', 'v1') |  | ||||||
|     request = @client.generate_request( |  | ||||||
|       prediction.training.insert, |  | ||||||
|       {'query' => '12345'} |  | ||||||
|     ) |  | ||||||
|     method, uri, headers, body = request |  | ||||||
|     uri.should == |  | ||||||
|       'https://www.googleapis.com/prediction/v1/training?query=12345' |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   it 'should allow modification to the base URIs for testing purposes' do |  | ||||||
|     prediction = @client.discovered_service('prediction', 'v1') |  | ||||||
|     prediction.base = 'https://testing-domain.googleapis.com/prediction/v1/' |  | ||||||
|     request = @client.generate_request( |  | ||||||
|       prediction.training.insert, |  | ||||||
|       {'query' => '123'} |  | ||||||
|     ) |  | ||||||
|     method, uri, headers, body = request |  | ||||||
|     uri.should == |  | ||||||
|       'https://testing-domain.googleapis.com/prediction/v1/training?query=123' |  | ||||||
|   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'} |  | ||||||
|     ) |  | ||||||
|     method, uri, headers, body = request |  | ||||||
|     headers = headers.inject({}) { |h,(k,v)| h[k]=v; h } |  | ||||||
|     headers.keys.should include('Authorization') |  | ||||||
|     headers['Authorization'].should =~ /^OAuth/ |  | ||||||
|   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'} |  | ||||||
|     ) |  | ||||||
|     status, headers, body = response |  | ||||||
|     status.should == 401 |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   it 'should raise an error for bogus methods' do |   it 'should raise an error for bogus methods' do | ||||||
|     (lambda do |     (lambda do | ||||||
|       @client.generate_request(42) |       @client.generate_request(42) | ||||||
|     end).should raise_error(TypeError) |     end).should raise_error(TypeError) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it 'should raise an error for bogus methods' do |   it 'should not return a preferred version for bogus service names' do | ||||||
|     (lambda do |     @client.preferred_version('bogus').should == nil | ||||||
|       @client.generate_request(@client.discovered_service('prediction')) |  | ||||||
|     end).should raise_error(TypeError) |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| describe Google::APIClient, 'configured for the buzz API' do |  | ||||||
|   before do |  | ||||||
|     @client = Google::APIClient.new(:service => 'buzz') |  | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it 'should correctly determine the discovery URI' do |   describe 'with the prediction API' do | ||||||
|     @client.discovery_uri.should === |     before do | ||||||
|       'http://www.googleapis.com/discovery/0.1/describe?api=buzz' |       @client.authorization = nil | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'should correctly determine the discovery URI' do | ||||||
|  |       @client.discovery_uri('prediction').should === | ||||||
|  |         'https://www.googleapis.com/discovery/v0.3/describe/prediction/v1' | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'should correctly generate API objects' do | ||||||
|  |       @client.discovered_api('prediction').name.should == 'prediction' | ||||||
|  |       @client.discovered_api('prediction').version.should == 'v1' | ||||||
|  |       @client.discovered_api(:prediction).name.should == 'prediction' | ||||||
|  |       @client.discovered_api(:prediction).version.should == 'v1' | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'should discover methods' do | ||||||
|  |       @client.discovered_method( | ||||||
|  |         'prediction.training.insert', 'prediction' | ||||||
|  |       ).name.should == 'insert' | ||||||
|  |       @client.discovered_method( | ||||||
|  |         :'prediction.training.insert', :prediction | ||||||
|  |       ).name.should == 'insert' | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'should discover methods' do | ||||||
|  |       @client.discovered_method( | ||||||
|  |         'prediction.training.delete', 'prediction', 'v1.1' | ||||||
|  |       ).name.should == 'delete' | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'should not find methods that are not in the discovery document' do | ||||||
|  |       @client.discovered_method( | ||||||
|  |         'prediction.training.delete', 'prediction', 'v1' | ||||||
|  |       ).should == nil | ||||||
|  |       @client.discovered_method( | ||||||
|  |         'prediction.bogus', 'prediction', 'v1' | ||||||
|  |       ).should == nil | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'should raise an error for bogus methods' do | ||||||
|  |       (lambda do | ||||||
|  |         @client.discovered_method(42, 'prediction', 'v1') | ||||||
|  |       end).should raise_error(TypeError) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'should raise an error for bogus methods' do | ||||||
|  |       (lambda do | ||||||
|  |         @client.generate_request(@client.discovered_api('prediction')) | ||||||
|  |       end).should raise_error(TypeError) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'should correctly determine the preferred version' do | ||||||
|  |       @client.preferred_version('prediction').version.should_not == 'v1' | ||||||
|  |       @client.preferred_version(:prediction).version.should_not == 'v1' | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'should generate valid requests' do | ||||||
|  |       request = @client.generate_request( | ||||||
|  |         'prediction.training.insert', | ||||||
|  |         {'data' => '12345', } | ||||||
|  |       ) | ||||||
|  |       method, uri, headers, body = request | ||||||
|  |       method.should == 'POST' | ||||||
|  |       uri.should == | ||||||
|  |         'https://www.googleapis.com/prediction/v1/training?data=12345' | ||||||
|  |       (headers.inject({}) { |h,(k,v)| h[k]=v; h }).should == {} | ||||||
|  |       body.should respond_to(:each) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'should generate requests against the correct URIs' do | ||||||
|  |       request = @client.generate_request( | ||||||
|  |         :'prediction.training.insert', | ||||||
|  |         {'data' => '12345'} | ||||||
|  |       ) | ||||||
|  |       method, uri, headers, body = request | ||||||
|  |       uri.should == | ||||||
|  |         'https://www.googleapis.com/prediction/v1/training?data=12345' | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'should generate requests against the correct URIs' do | ||||||
|  |       prediction = @client.discovered_api('prediction', 'v1') | ||||||
|  |       request = @client.generate_request( | ||||||
|  |         prediction.training.insert, | ||||||
|  |         {'data' => '12345'} | ||||||
|  |       ) | ||||||
|  |       method, uri, headers, body = request | ||||||
|  |       uri.should == | ||||||
|  |         'https://www.googleapis.com/prediction/v1/training?data=12345' | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'should allow modification to the base URIs for testing purposes' do | ||||||
|  |       prediction = @client.discovered_api('prediction', 'v1') | ||||||
|  |       prediction.rest_base = | ||||||
|  |         'https://testing-domain.googleapis.com/prediction/v1/' | ||||||
|  |       request = @client.generate_request( | ||||||
|  |         prediction.training.insert, | ||||||
|  |         {'data' => '123'} | ||||||
|  |       ) | ||||||
|  |       method, uri, headers, body = request | ||||||
|  |       uri.should == | ||||||
|  |         'https://testing-domain.googleapis.com/prediction/v1/training?data=123' | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'should generate OAuth 1 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', | ||||||
|  |         {'data' => '12345'} | ||||||
|  |       ) | ||||||
|  |       method, uri, headers, body = request | ||||||
|  |       headers = headers.inject({}) { |h,(k,v)| h[k]=v; h } | ||||||
|  |       headers.keys.should include('Authorization') | ||||||
|  |       headers['Authorization'].should =~ /^OAuth/ | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'should generate OAuth 2 requests' do | ||||||
|  |       @client.authorization = :oauth_2 | ||||||
|  |       @client.authorization.access_token = '12345' | ||||||
|  |       request = @client.generate_request( | ||||||
|  |         'prediction.training.insert', | ||||||
|  |         {'data' => '12345'} | ||||||
|  |       ) | ||||||
|  |       method, uri, headers, body = request | ||||||
|  |       headers = headers.inject({}) { |h,(k,v)| h[k]=v; h } | ||||||
|  |       headers.keys.should include('Authorization') | ||||||
|  |       headers['Authorization'].should =~ /^OAuth/ | ||||||
|  |     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', | ||||||
|  |         {'data' => '12345'} | ||||||
|  |       ) | ||||||
|  |       status, headers, body = response | ||||||
|  |       status.should == 401 | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'should not be able to execute improperly authorized requests' do | ||||||
|  |       @client.authorization = :oauth_2 | ||||||
|  |       @client.authorization.access_token = '12345' | ||||||
|  |       response = @client.execute( | ||||||
|  |         'prediction.training.insert', | ||||||
|  |         {'data' => '12345'} | ||||||
|  |       ) | ||||||
|  |       status, headers, body = response | ||||||
|  |       status.should == 401 | ||||||
|  |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it 'should find APIs that are in the discovery document' do |   describe 'with the buzz API' do | ||||||
|     @client.discovered_service('buzz').name.should == 'buzz' |     before do | ||||||
|     @client.discovered_service('buzz').version.should == 'v1' |       @client.authorization = nil | ||||||
|   end |       @buzz = @client.discovered_api('buzz') | ||||||
|  |     end | ||||||
| 
 | 
 | ||||||
|   it 'should not find APIs that are not in the discovery document' do |     it 'should correctly determine the discovery URI' do | ||||||
|     @client.discovered_service('bogus').should == nil |       @client.discovery_uri('buzz').should === | ||||||
|   end |         'https://www.googleapis.com/discovery/v0.3/describe/buzz/v1' | ||||||
|  |     end | ||||||
| 
 | 
 | ||||||
|   it 'should find methods that are in the discovery document' do |     it 'should find APIs that are in the discovery document' do | ||||||
|     # TODO(bobaman) Fix this when the RPC names are correct |       @client.discovered_api('buzz').name.should == 'buzz' | ||||||
|     @client.discovered_method('chili.activities.list').name.should == 'list' |       @client.discovered_api('buzz').version.should == 'v1' | ||||||
|   end |       @client.discovered_api(:buzz).name.should == 'buzz' | ||||||
|  |       @client.discovered_api(:buzz).version.should == 'v1' | ||||||
|  |     end | ||||||
| 
 | 
 | ||||||
|   it 'should not find methods that are not in the discovery document' do |     it 'should find methods that are in the discovery document' do | ||||||
|     @client.discovered_method('buzz.bogus').should == nil |       # TODO(bobaman) Fix this when the RPC names are correct | ||||||
|   end |       @client.discovered_method( | ||||||
|  |         'chili.activities.list', 'buzz' | ||||||
|  |       ).name.should == 'list' | ||||||
|  |     end | ||||||
| 
 | 
 | ||||||
|   it 'should generate requests against the correct URIs' do |     it 'should not find methods that are not in the discovery document' do | ||||||
|     # TODO(bobaman) Fix this when the RPC names are correct |       @client.discovered_method('buzz.bogus', 'buzz').should == nil | ||||||
|     request = @client.generate_request( |     end | ||||||
|       'chili.activities.list', |  | ||||||
|       {'userId' => 'hikingfan', 'scope' => '@public'}, |  | ||||||
|       '', |  | ||||||
|       [], |  | ||||||
|       {:signed => false} |  | ||||||
|     ) |  | ||||||
|     method, uri, headers, body = request |  | ||||||
|     uri.should == |  | ||||||
|       'https://www.googleapis.com/buzz/v1/activities/hikingfan/@public' |  | ||||||
|   end |  | ||||||
| 
 | 
 | ||||||
|   it 'should correctly validate parameters' do |     it 'should fail for string RPC names that do not match API name' do | ||||||
|     # TODO(bobaman) Fix this when the RPC names are correct |       (lambda do | ||||||
|     (lambda do |         @client.generate_request( | ||||||
|       @client.generate_request( |           'chili.activities.list', | ||||||
|         'chili.activities.list', |           {'alt' => 'json'}, | ||||||
|         {'alt' => 'json'}, |           '', | ||||||
|  |           [], | ||||||
|  |           {:signed => false} | ||||||
|  |         ) | ||||||
|  |       end).should raise_error(Google::APIClient::TransmissionError) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'should generate requests against the correct URIs' do | ||||||
|  |       request = @client.generate_request( | ||||||
|  |         @buzz.activities.list, | ||||||
|  |         {'userId' => 'hikingfan', 'scope' => '@public'}, | ||||||
|         '', |         '', | ||||||
|         [], |         [], | ||||||
|         {:signed => false} |         {:signed => false} | ||||||
|       ) |       ) | ||||||
|     end).should raise_error(ArgumentError) |       method, uri, headers, body = request | ||||||
|   end |       uri.should == | ||||||
|  |         'https://www.googleapis.com/buzz/v1/activities/hikingfan/@public' | ||||||
|  |     end | ||||||
| 
 | 
 | ||||||
|   it 'should correctly validate parameters' do |     it 'should correctly validate parameters' do | ||||||
|     # TODO(bobaman) Fix this when the RPC names are correct |       (lambda do | ||||||
|     (lambda do |         @client.generate_request( | ||||||
|       @client.generate_request( |           @buzz.activities.list, | ||||||
|         'chili.activities.list', |           {'alt' => 'json'}, | ||||||
|         {'userId' => 'hikingfan', 'scope' => '@bogus'}, |           '', | ||||||
|  |           [], | ||||||
|  |           {:signed => false} | ||||||
|  |         ) | ||||||
|  |       end).should raise_error(ArgumentError) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'should correctly validate parameters' do | ||||||
|  |       (lambda do | ||||||
|  |         @client.generate_request( | ||||||
|  |           @buzz.activities.list, | ||||||
|  |           {'userId' => 'hikingfan', 'scope' => '@bogus'}, | ||||||
|  |           '', | ||||||
|  |           [], | ||||||
|  |           {:signed => false} | ||||||
|  |         ) | ||||||
|  |       end).should raise_error(ArgumentError) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'should be able to execute requests without authorization' do | ||||||
|  |       response = @client.execute( | ||||||
|  |         @buzz.activities.list, | ||||||
|  |         {'alt' => 'json', 'userId' => 'hikingfan', 'scope' => '@public'}, | ||||||
|         '', |         '', | ||||||
|         [], |         [], | ||||||
|         {:signed => false} |         {:signed => false} | ||||||
|       ) |       ) | ||||||
|     end).should raise_error(ArgumentError) |       status, headers, body = response | ||||||
|  |       status.should == 200 | ||||||
|  |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it 'should be able to execute requests without authorization' do |   describe 'with the latitude API' do | ||||||
|     # TODO(bobaman) Fix this when the RPC names are correct |     before do | ||||||
|     response = @client.execute( |       @client.authorization = nil | ||||||
|       'chili.activities.list', |       @latitude = @client.discovered_api('latitude') | ||||||
|       {'alt' => 'json', 'userId' => 'hikingfan', 'scope' => '@public'}, |     end | ||||||
|       '', | 
 | ||||||
|       [], |     it 'should correctly determine the discovery URI' do | ||||||
|       {:signed => false} |       @client.discovery_uri('latitude').should === | ||||||
|     ) |         'https://www.googleapis.com/discovery/v0.3/describe/latitude/v1' | ||||||
|     status, headers, body = response |     end | ||||||
|     status.should == 200 | 
 | ||||||
|   end |     it 'should find APIs that are in the discovery document' do | ||||||
| end |       @client.discovered_api('latitude').name.should == 'latitude' | ||||||
| 
 |       @client.discovered_api('latitude').version.should == 'v1' | ||||||
| describe Google::APIClient, 'configured for the latitude API' do |     end | ||||||
|   before do | 
 | ||||||
|     @client = Google::APIClient.new(:service => 'latitude') |     it 'should find methods that are in the discovery document' do | ||||||
|   end |       @client.discovered_method( | ||||||
| 
 |         'latitude.currentLocation.get', 'latitude' | ||||||
|   it 'should correctly determine the discovery URI' do |       ).name.should == 'get' | ||||||
|     @client.discovery_uri.should === |     end | ||||||
|       'http://www.googleapis.com/discovery/0.1/describe?api=latitude' | 
 | ||||||
|   end |     it 'should not find methods that are not in the discovery document' do | ||||||
| 
 |       @client.discovered_method('latitude.bogus', 'latitude').should == nil | ||||||
|   it 'should find APIs that are in the discovery document' do |     end | ||||||
|     @client.discovered_service('latitude').name.should == 'latitude' | 
 | ||||||
|     @client.discovered_service('latitude').version.should == 'v1' |     it 'should generate requests against the correct URIs' do | ||||||
|   end |       request = @client.generate_request( | ||||||
| 
 |         'latitude.currentLocation.get', | ||||||
|   it 'should not find APIs that are not in the discovery document' do |         {}, | ||||||
|     @client.discovered_service('bogus').should == nil |         '', | ||||||
|   end |         [], | ||||||
| 
 |         {:signed => false} | ||||||
|   it 'should find methods that are in the discovery document' do |       ) | ||||||
|     @client.discovered_method('latitude.currentLocation.get').name.should == |       method, uri, headers, body = request | ||||||
|       'get' |       uri.should == | ||||||
|   end |         'https://www.googleapis.com/latitude/v1/currentLocation' | ||||||
| 
 |     end | ||||||
|   it 'should not find methods that are not in the discovery document' do | 
 | ||||||
|     @client.discovered_method('latitude.bogus').should == nil |     it 'should generate requests against the correct URIs' do | ||||||
|   end |       request = @client.generate_request( | ||||||
| 
 |         @latitude.current_location.get, | ||||||
|   it 'should generate requests against the correct URIs' do |         {}, | ||||||
|     request = @client.generate_request( |         '', | ||||||
|       'latitude.currentLocation.get', |         [], | ||||||
|       {}, |         {:signed => false} | ||||||
|       '', |       ) | ||||||
|       [], |       method, uri, headers, body = request | ||||||
|       {:signed => false} |       uri.should == | ||||||
|     ) |         'https://www.googleapis.com/latitude/v1/currentLocation' | ||||||
|     method, uri, headers, body = request |     end | ||||||
|     uri.should == | 
 | ||||||
|       'https://www.googleapis.com/latitude/v1/currentLocation' |     it 'should not be able to execute requests without authorization' do | ||||||
|   end |       response = @client.execute( | ||||||
| 
 |         'latitude.currentLocation.get', | ||||||
|   it 'should not be able to execute requests without authorization' do |         {}, | ||||||
|     response = @client.execute( |         '', | ||||||
|       'latitude.currentLocation.get', |         [], | ||||||
|       {}, |         {:signed => false} | ||||||
|       '', |       ) | ||||||
|       [], |       status, headers, body = response | ||||||
|       {:signed => false} |       status.should == 401 | ||||||
|     ) |     end | ||||||
|     status, headers, body = response |   end | ||||||
|     status.should == 401 | 
 | ||||||
|   end |   describe 'with the moderator API' do | ||||||
| end |     before do | ||||||
| 
 |       @client.authorization = nil | ||||||
| describe Google::APIClient, 'configured for the moderator API' do |       @moderator = @client.discovered_api('moderator') | ||||||
|   before do |     end | ||||||
|     @client = Google::APIClient.new(:service => 'moderator') | 
 | ||||||
|   end |     it 'should correctly determine the discovery URI' do | ||||||
| 
 |       @client.discovery_uri('moderator').should === | ||||||
|   it 'should correctly determine the discovery URI' do |         'https://www.googleapis.com/discovery/v0.3/describe/moderator/v1' | ||||||
|     @client.discovery_uri.should === |     end | ||||||
|       'http://www.googleapis.com/discovery/0.1/describe?api=moderator' | 
 | ||||||
|   end |     it 'should find APIs that are in the discovery document' do | ||||||
| 
 |       @client.discovered_api('moderator').name.should == 'moderator' | ||||||
|   it 'should find APIs that are in the discovery document' do |       @client.discovered_api('moderator').version.should == 'v1' | ||||||
|     @client.discovered_service('moderator').name.should == 'moderator' |     end | ||||||
|     @client.discovered_service('moderator').version.should == 'v1' | 
 | ||||||
|   end |     it 'should find methods that are in the discovery document' do | ||||||
| 
 |       @client.discovered_method( | ||||||
|   it 'should not find APIs that are not in the discovery document' do |         'moderator.profiles.get', 'moderator' | ||||||
|     @client.discovered_service('bogus').should == nil |       ).name.should == 'get' | ||||||
|   end |     end | ||||||
| 
 | 
 | ||||||
|   it 'should find methods that are in the discovery document' do |     it 'should not find methods that are not in the discovery document' do | ||||||
|     @client.discovered_method('moderator.profiles.get').name.should == |       @client.discovered_method('moderator.bogus', 'moderator').should == nil | ||||||
|       'get' |     end | ||||||
|   end | 
 | ||||||
| 
 |     it 'should generate requests against the correct URIs' do | ||||||
|   it 'should not find methods that are not in the discovery document' do |       request = @client.generate_request( | ||||||
|     @client.discovered_method('moderator.bogus').should == nil |         'moderator.profiles.get', | ||||||
|   end |         {}, | ||||||
| 
 |         '', | ||||||
|   it 'should generate requests against the correct URIs' do |         [], | ||||||
|     request = @client.generate_request( |         {:signed => false} | ||||||
|       'moderator.profiles.get', |       ) | ||||||
|       {}, |       method, uri, headers, body = request | ||||||
|       '', |       uri.should == | ||||||
|       [], |         'https://www.googleapis.com/moderator/v1/profiles/@me' | ||||||
|       {:signed => false} |     end | ||||||
|     ) | 
 | ||||||
|     method, uri, headers, body = request |     it 'should generate requests against the correct URIs' do | ||||||
|     uri.should == |       request = @client.generate_request( | ||||||
|       'https://www.googleapis.com/moderator/v1/profiles/@me' |         @moderator.profiles.get, | ||||||
|   end |         {}, | ||||||
| 
 |         '', | ||||||
|   it 'should not be able to execute requests without authorization' do |         [], | ||||||
|     response = @client.execute( |         {:signed => false} | ||||||
|       'moderator.profiles.get', |       ) | ||||||
|       {}, |       method, uri, headers, body = request | ||||||
|       '', |       uri.should == | ||||||
|       [], |         'https://www.googleapis.com/moderator/v1/profiles/@me' | ||||||
|       {:signed => false} |     end | ||||||
|     ) | 
 | ||||||
|     status, headers, body = response |     it 'should not be able to execute requests without authorization' do | ||||||
|     status.should == 401 |       response = @client.execute( | ||||||
|  |         'moderator.profiles.get', | ||||||
|  |         {}, | ||||||
|  |         '', | ||||||
|  |         [], | ||||||
|  |         {:signed => false} | ||||||
|  |       ) | ||||||
|  |       status, headers, body = response | ||||||
|  |       status.should == 401 | ||||||
|  |     end | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -33,17 +33,20 @@ shared_examples_for 'configurable user agent' do | ||||||
|     @client.user_agent.should == nil |     @client.user_agent.should == nil | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it 'should not allow the user agent to be set to bogus values' do |   it 'should not allow the user agent to be used with bogus values' do | ||||||
|     (lambda do |     (lambda do | ||||||
|       @client.user_agent = 42 |       @client.user_agent = 42 | ||||||
|  |       @client.transmit_request( | ||||||
|  |         ['GET', 'http://www.google.com/', [], []] | ||||||
|  |       ) | ||||||
|     end).should raise_error(TypeError) |     end).should raise_error(TypeError) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it 'should transmit a User-Agent header when sending requests' do |   it 'should transmit a User-Agent header when sending requests' do | ||||||
|     @client.user_agent = 'Custom User Agent/1.2.3' |     @client.user_agent = 'Custom User Agent/1.2.3' | ||||||
|     request = ['GET', 'http://www.google.com/', [], []] |     request = ['GET', 'http://www.google.com/', [], []] | ||||||
|     adapter = HTTPAdapter::MockAdapter.request_adapter do |request, connection| |     adapter = HTTPAdapter::MockAdapter.create do |request_ary, connection| | ||||||
|       method, uri, headers, body = request |       method, uri, headers, body = request_ary | ||||||
|       headers.should be_any { |k, v| k.downcase == 'user-agent' } |       headers.should be_any { |k, v| k.downcase == 'user-agent' } | ||||||
|       headers.each do |k, v| |       headers.each do |k, v| | ||||||
|         v.should == @client.user_agent if k.downcase == 'user-agent' |         v.should == @client.user_agent if k.downcase == 'user-agent' | ||||||
|  | @ -54,7 +57,7 @@ shared_examples_for 'configurable user agent' do | ||||||
|   end |   end | ||||||
| end | end | ||||||
| 
 | 
 | ||||||
| describe Google::APIClient, 'with default configuration' do | describe Google::APIClient do | ||||||
|   before do |   before do | ||||||
|     @client = Google::APIClient.new |     @client = Google::APIClient.new | ||||||
|   end |   end | ||||||
|  | @ -67,52 +70,60 @@ describe Google::APIClient, 'with default configuration' do | ||||||
|     @client.parser.should be(Google::APIClient::JSONParser) |     @client.parser.should be(Google::APIClient::JSONParser) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it 'should not use an authorization mechanism' do |   it 'should default to OAuth 2' do | ||||||
|     @client.authorization.should be_nil |     Signet::OAuth2::Client.should === @client.authorization | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it_should_behave_like 'configurable user agent' |   it_should_behave_like 'configurable user agent' | ||||||
| end |  | ||||||
| 
 | 
 | ||||||
| describe Google::APIClient, 'with default oauth configuration' do |   describe 'configured for OAuth 1' do | ||||||
|   before do |     before do | ||||||
|     @client = Google::APIClient.new(:authorization => :oauth_1) |       @client.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' |  | ||||||
|     @client.authorization.authorization_uri.to_s.should include( |  | ||||||
|       'https://www.google.com/accounts/OAuthAuthorizeToken' |  | ||||||
|     ) |  | ||||||
|     @client.authorization.token_credential_uri.to_s.should == |  | ||||||
|       'https://www.google.com/accounts/OAuthGetAccessToken' |  | ||||||
|     @client.authorization.client_credential_key.should == 'anonymous' |  | ||||||
|     @client.authorization.client_credential_secret.should == 'anonymous' |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   it_should_behave_like 'configurable user agent' |  | ||||||
| end |  | ||||||
| 
 |  | ||||||
| describe Google::APIClient, 'with custom pluggable parser' do |  | ||||||
|   before do |  | ||||||
|     class FakeJsonParser |  | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     @client = Google::APIClient.new(:parser => FakeJsonParser.new) |     it 'should use the default OAuth1 client configuration' do | ||||||
|  |       @client.authorization.temporary_credential_uri.to_s.should == | ||||||
|  |         'https://www.google.com/accounts/OAuthGetRequestToken' | ||||||
|  |       @client.authorization.authorization_uri.to_s.should include( | ||||||
|  |         'https://www.google.com/accounts/OAuthAuthorizeToken' | ||||||
|  |       ) | ||||||
|  |       @client.authorization.token_credential_uri.to_s.should == | ||||||
|  |         'https://www.google.com/accounts/OAuthGetAccessToken' | ||||||
|  |       @client.authorization.client_credential_key.should == 'anonymous' | ||||||
|  |       @client.authorization.client_credential_secret.should == 'anonymous' | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it_should_behave_like 'configurable user agent' | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it 'should use the custom parser' do |   describe 'configured for OAuth 2' do | ||||||
|     @client.parser.should be_instance_of(FakeJsonParser) |     before do | ||||||
|  |       @client.authorization = :oauth_2 | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     # TODO | ||||||
|  |     it_should_behave_like 'configurable user agent' | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   it_should_behave_like 'configurable user agent' |   describe 'with custom pluggable parser' do | ||||||
|  |     before do | ||||||
|  |       class FakeJsonParser | ||||||
|  |         def serialize(value) | ||||||
|  |           return "42" | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|  |         def parse(value) | ||||||
|  |           return 42 | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       @client.parser = FakeJsonParser.new | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'should use the custom parser' do | ||||||
|  |       @client.parser.should be_instance_of(FakeJsonParser) | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it_should_behave_like 'configurable user agent' | ||||||
|  |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -20,10 +20,10 @@ namespace :gem do | ||||||
|     s.rdoc_options.concat ['--main',  'README'] |     s.rdoc_options.concat ['--main',  'README'] | ||||||
| 
 | 
 | ||||||
|     # Dependencies used in the main library |     # Dependencies used in the main library | ||||||
|     s.add_runtime_dependency('signet', '>= 0.1.4') |     s.add_runtime_dependency('signet', '~> 0.2.1') | ||||||
|     s.add_runtime_dependency('addressable', '>= 2.2.2') |     s.add_runtime_dependency('addressable', '~> 2.2.2') | ||||||
|     s.add_runtime_dependency('httpadapter', '>= 0.2.0') |     s.add_runtime_dependency('httpadapter', '~> 1.0.0') | ||||||
|     s.add_runtime_dependency('json', '>= 1.1.9') |     s.add_runtime_dependency('json', '>= 1.5.1') | ||||||
|     s.add_runtime_dependency('extlib', '>= 0.9.15') |     s.add_runtime_dependency('extlib', '>= 0.9.15') | ||||||
| 
 | 
 | ||||||
|     # Dependencies used in the CLI |     # Dependencies used in the CLI | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue