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