google-api-client 0.4.7 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +11 -0
- data/Gemfile +6 -1
- data/Gemfile.lock +80 -0
- data/README.md +152 -45
- data/Rakefile +2 -2
- data/bin/google-api +2 -9
- data/lib/compat/multi_json.rb +2 -3
- data/lib/google/api_client.rb +87 -278
- data/lib/google/api_client/auth/jwt_asserter.rb +139 -0
- data/lib/google/api_client/auth/pkcs12.rb +48 -0
- data/lib/google/api_client/batch.rb +164 -136
- data/lib/google/api_client/client_secrets.rb +45 -1
- data/lib/google/api_client/discovery/api.rb +7 -8
- data/lib/google/api_client/discovery/method.rb +20 -27
- data/lib/google/api_client/discovery/resource.rb +16 -10
- data/lib/google/api_client/discovery/schema.rb +2 -0
- data/lib/google/api_client/media.rb +76 -64
- data/lib/google/api_client/reference.rb +7 -285
- data/lib/google/api_client/request.rb +336 -0
- data/lib/google/api_client/result.rb +147 -55
- data/lib/google/api_client/service_account.rb +2 -120
- data/lib/google/api_client/version.rb +2 -3
- data/spec/google/api_client/batch_spec.rb +9 -10
- data/spec/google/api_client/discovery_spec.rb +184 -114
- data/spec/google/api_client/media_spec.rb +27 -39
- data/spec/google/api_client/result_spec.rb +30 -11
- data/spec/google/api_client/service_account_spec.rb +38 -6
- data/spec/google/api_client_spec.rb +48 -32
- data/spec/spec_helper.rb +46 -0
- data/tasks/gem.rake +1 -0
- metadata +36 -70
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
# 0.5.0
|
2
|
+
|
3
|
+
* Beta candidate, potential incompatible changes with how requests are processed.
|
4
|
+
* All requests should be made using execute() or execute!()
|
5
|
+
* :api_method in request can no longer be a string
|
6
|
+
* Deprecated ResumableUpload.send_* methods.
|
7
|
+
* Reduce memory utilization when uploading large files
|
8
|
+
* Automatic refresh of OAuth 2 credentials & retry of request when 401 errors
|
9
|
+
are returned
|
10
|
+
* Simplify internal request processing.
|
11
|
+
|
1
12
|
# 0.4.7
|
2
13
|
|
3
14
|
* Added the ability to convert client secrets to an authorization object
|
data/Gemfile
CHANGED
@@ -9,12 +9,17 @@ gem 'autoparse', '>= 0.3.2'
|
|
9
9
|
gem 'faraday', '~> 0.8.1'
|
10
10
|
gem 'multi_json', '>= 1.0.0'
|
11
11
|
gem 'extlib', '>= 0.9.15'
|
12
|
+
gem 'jwt', '~> 0.1.5'
|
12
13
|
gem 'jruby-openssl', :platforms => :jruby
|
13
14
|
|
14
15
|
group :development do
|
15
16
|
gem 'launchy', '>= 2.1.1'
|
16
17
|
gem 'yard'
|
17
|
-
|
18
|
+
if File.exist?('/usr/bin/gcc-4.2')
|
19
|
+
# Not a critically important gem to have around.
|
20
|
+
# If it's not going to build, skip it.
|
21
|
+
gem 'redcarpet'
|
22
|
+
end
|
18
23
|
end
|
19
24
|
|
20
25
|
group :examples do
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
google-api-client (0.5.0)
|
5
|
+
addressable (>= 2.3.2)
|
6
|
+
autoparse (>= 0.3.2)
|
7
|
+
extlib (>= 0.9.15)
|
8
|
+
faraday (~> 0.8.1)
|
9
|
+
jwt (>= 0.1.5)
|
10
|
+
launchy (>= 2.1.1)
|
11
|
+
multi_json (>= 1.0.0)
|
12
|
+
signet (>= 0.4.1)
|
13
|
+
uuidtools (>= 2.1.0)
|
14
|
+
|
15
|
+
GEM
|
16
|
+
remote: http://rubygems.org/
|
17
|
+
specs:
|
18
|
+
addressable (2.3.2)
|
19
|
+
autoparse (0.3.2)
|
20
|
+
addressable (>= 2.3.1)
|
21
|
+
extlib (>= 0.9.15)
|
22
|
+
multi_json (>= 1.0.0)
|
23
|
+
diff-lcs (1.1.3)
|
24
|
+
extlib (0.9.15)
|
25
|
+
faraday (0.8.4)
|
26
|
+
multipart-post (~> 1.1)
|
27
|
+
idn (0.0.2)
|
28
|
+
jwt (0.1.5)
|
29
|
+
multi_json (>= 1.0)
|
30
|
+
launchy (2.1.2)
|
31
|
+
addressable (~> 2.3)
|
32
|
+
multi_json (1.3.6)
|
33
|
+
multipart-post (1.1.5)
|
34
|
+
rack (1.4.1)
|
35
|
+
rack-protection (1.2.0)
|
36
|
+
rack
|
37
|
+
rake (0.9.2.2)
|
38
|
+
rcov (1.0.0)
|
39
|
+
rspec (2.11.0)
|
40
|
+
rspec-core (~> 2.11.0)
|
41
|
+
rspec-expectations (~> 2.11.0)
|
42
|
+
rspec-mocks (~> 2.11.0)
|
43
|
+
rspec-core (2.11.1)
|
44
|
+
rspec-expectations (2.11.3)
|
45
|
+
diff-lcs (~> 1.1.3)
|
46
|
+
rspec-mocks (2.11.3)
|
47
|
+
signet (0.4.3)
|
48
|
+
addressable (>= 2.2.3)
|
49
|
+
faraday (~> 0.8.1)
|
50
|
+
jwt (>= 0.1.5)
|
51
|
+
multi_json (>= 1.0.0)
|
52
|
+
sinatra (1.3.3)
|
53
|
+
rack (~> 1.3, >= 1.3.6)
|
54
|
+
rack-protection (~> 1.2)
|
55
|
+
tilt (~> 1.3, >= 1.3.3)
|
56
|
+
tilt (1.3.3)
|
57
|
+
uuidtools (2.1.3)
|
58
|
+
yard (0.8.3)
|
59
|
+
|
60
|
+
PLATFORMS
|
61
|
+
ruby
|
62
|
+
|
63
|
+
DEPENDENCIES
|
64
|
+
addressable (>= 2.3.2)
|
65
|
+
autoparse (>= 0.3.2)
|
66
|
+
extlib (>= 0.9.15)
|
67
|
+
faraday (~> 0.8.1)
|
68
|
+
google-api-client!
|
69
|
+
idn
|
70
|
+
jruby-openssl
|
71
|
+
jwt (~> 0.1.5)
|
72
|
+
launchy (>= 2.1.1)
|
73
|
+
multi_json (>= 1.0.0)
|
74
|
+
rake (>= 0.9.0)
|
75
|
+
rcov (>= 0.9.9)
|
76
|
+
rspec (>= 2.11.0)
|
77
|
+
signet (>= 0.4.1)
|
78
|
+
sinatra
|
79
|
+
uuidtools (>= 2.1.0)
|
80
|
+
yard
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
#
|
1
|
+
# Google API Client
|
2
2
|
|
3
3
|
<dl>
|
4
4
|
<dt>Homepage</dt><dd><a href="http://code.google.com/p/google-api-ruby-client">http://code.google.com/p/google-api-ruby-client</a></dd>
|
5
|
-
<dt>
|
5
|
+
<dt>Authoris</dt><dd>Bob Aman, <a href="mailto:sbazyl@google.com">Steven Bazyl</a></dd>
|
6
6
|
<dt>Copyright</dt><dd>Copyright © 2011 Google, Inc.</dd>
|
7
7
|
<dt>License</dt><dd>Apache 2.0</dd>
|
8
8
|
</dl>
|
@@ -10,62 +10,169 @@
|
|
10
10
|
[![Build Status](https://secure.travis-ci.org/google/google-api-ruby-client.png)](http://travis-ci.org/google/google-api-ruby-client)
|
11
11
|
[![Dependency Status](https://gemnasium.com/google/google-api-ruby-client.png)](https://gemnasium.com/google/google-api-ruby-client)
|
12
12
|
|
13
|
-
|
13
|
+
## Description
|
14
14
|
|
15
15
|
The Google API Ruby Client makes it trivial to discover and access supported
|
16
16
|
APIs.
|
17
17
|
|
18
|
-
|
18
|
+
## Install
|
19
19
|
|
20
|
-
|
20
|
+
Be sure `http://rubygems.org/` is in your gem sources.
|
21
|
+
|
22
|
+
For normal client usage, this is sufficient:
|
23
|
+
|
24
|
+
$ sudo gem install google-api-client
|
25
|
+
|
26
|
+
The command line interface, the example applications, and the test suite
|
27
|
+
require additional dependencies. These may be obtained with:
|
28
|
+
|
29
|
+
$ sudo gem install google-api-client --development --force --no-rdoc --no-ri
|
30
|
+
|
31
|
+
## Example Usage
|
32
|
+
|
33
|
+
# Initialize the client & Google+ API
|
21
34
|
require 'google/api_client'
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
:authorization_uri =>
|
30
|
-
'https://www.google.com/buzz/api/auth/OAuthAuthorizeToken',
|
31
|
-
:token_credential_uri =>
|
32
|
-
'https://www.google.com/accounts/OAuthGetAccessToken',
|
33
|
-
:client_credential_key => 'anonymous',
|
34
|
-
:client_credential_secret => 'anonymous'
|
35
|
-
)
|
36
|
-
)
|
37
|
-
client.authorization.fetch_temporary_credential!(
|
38
|
-
:additional_parameters => {
|
39
|
-
'scope' => 'https://www.googleapis.com/auth/buzz'
|
40
|
-
}
|
41
|
-
)
|
42
|
-
redirect_uri = client.authorization.authorization_uri(
|
43
|
-
:additional_parameters => {
|
44
|
-
'domain' => client.authorization.client_credential_key,
|
45
|
-
'scope' => 'https://www.googleapis.com/auth/buzz'
|
46
|
-
}
|
47
|
-
)
|
48
|
-
# Redirect user here
|
49
|
-
client.authorization.fetch_token_credential!(:verifier => '12345')
|
50
|
-
|
51
|
-
# Discover available methods
|
52
|
-
method_names = client.discovered_api('plus').to_h.keys
|
35
|
+
client = Google::APIClient.new
|
36
|
+
plus = client.discovered_api('plus')
|
37
|
+
|
38
|
+
# Initialize OAuth 2.0 client
|
39
|
+
client.authorization.client_id = '<CLIENT_ID_FROM_API_CONSOLE>'
|
40
|
+
client.authorization.client_secret = '<CLIENT_SECRET>'
|
41
|
+
client.authorization.redirect_uri = '<YOUR_REDIRECT_URI>'
|
53
42
|
|
43
|
+
client.authorization.scope = 'https://www.googleapis.com/auth/plus.me'
|
44
|
+
|
45
|
+
# Request authorization
|
46
|
+
redirect_uri = client.authorization.authorization_uri
|
47
|
+
|
48
|
+
# Wait for authorization code then exchange for token
|
49
|
+
client.authorization.code = '....'
|
50
|
+
client.authorization.fetch_access_token!
|
51
|
+
|
54
52
|
# Make an API call
|
55
53
|
result = client.execute(
|
56
|
-
|
57
|
-
{'collection' => 'public', 'userId' => 'me'}
|
54
|
+
:api_method => plus.activities.list,
|
55
|
+
:parameters => {'collection' => 'public', 'userId' => 'me'}
|
58
56
|
)
|
59
57
|
|
60
|
-
|
58
|
+
puts result.data
|
61
59
|
|
62
|
-
|
60
|
+
## API Features
|
63
61
|
|
64
|
-
|
62
|
+
### API Discovery
|
63
|
+
|
64
|
+
To take full advantage of the client, load API definitions prior to use. To load an API:
|
65
|
+
|
66
|
+
urlshortener = client.discovered_api('urlshortener')
|
67
|
+
|
68
|
+
Specific versions of the API can be loaded as well:
|
69
|
+
|
70
|
+
drive = client.discovered_api('drive', 'v2')
|
71
|
+
|
72
|
+
Locally cached discovery documents may be used as well. To load an API from a local file:
|
73
|
+
|
74
|
+
doc = File.read('my-api.json')
|
75
|
+
my_api = client.register_discovery_document('myapi', 'v1', doc)
|
76
|
+
|
77
|
+
### Authorization
|
78
|
+
|
79
|
+
Most interactions with Google APIs require users to authorize applications via OAuth 2.0. The client library uses [Signet](https://github.com/google/signet) to handle most aspects of authorization. For additional details about Google's OAuth support, see [Google Developers](https://developers.google.com/accounts/docs/OAuth2).
|
80
|
+
|
81
|
+
Credentials can be managed at the connection level, as shown, or supplied on a per-request basis when calling `execute`.
|
82
|
+
|
83
|
+
For server-to-server interactions, like those between a web application and Google Cloud Storage, Prediction, or BigQuery APIs, use service accounts. Assertions for service accounts are made with `Google::APIClient::JWTAsserter`.
|
84
|
+
|
85
|
+
client = Google::APIClient.new
|
86
|
+
key = Google::APIClient::PKCS12.load_key('client.p12', 'notasecret')
|
87
|
+
service_account = Google::APIClient::JWTAsserter(
|
88
|
+
'123456-abcdef@developer.gserviceaccount.com',
|
89
|
+
'https://www.googleapis.com/auth/prediction',
|
90
|
+
key)
|
91
|
+
client.authorization = service_account.authorize
|
92
|
+
|
93
|
+
### Batching Requests
|
94
|
+
|
95
|
+
Some Google APIs support batching requests into a single HTTP request. Use `Google::APIClient::BatchRequest`
|
96
|
+
to bundle multiple requests together.
|
97
|
+
|
98
|
+
Example:
|
99
|
+
|
100
|
+
client = Google::APIClient.new
|
101
|
+
urlshortener = client.discovered_api('urlshortner')
|
102
|
+
|
103
|
+
batch = Google::APIClient::BatchRequest.new do |result|
|
104
|
+
puts result.data
|
105
|
+
end
|
106
|
+
|
107
|
+
batch.add(:api_method=>urlshortener.url.insert,
|
108
|
+
:body_object => { 'longUrl' => 'http://example.com/foo' })
|
109
|
+
batch.add(:api_method=>urlshortener.url.insert,
|
110
|
+
:body_object => { 'longUrl' => 'http://example.com/bar' })
|
111
|
+
client.execute(batch)
|
112
|
+
|
113
|
+
Blocks for handling responses can be specified either at the batch level or when adding an individual API call. For example:
|
114
|
+
|
115
|
+
batch.add(:api_method=>urlshortener.url.insert, :body_object => { 'longUrl' => 'http://example.com/bar' }) do |result|
|
116
|
+
puts result.data
|
117
|
+
end
|
118
|
+
|
119
|
+
### Media Upload
|
120
|
+
|
121
|
+
For APIs that support file uploads, use `Google::APIClient::UploadIO` to load the stream. Both multipart and resumable
|
122
|
+
uploads can be used. For example, to upload a file to Google Drive using multipart
|
123
|
+
|
124
|
+
drive = client.discovered_api('drive', 'v2')
|
125
|
+
|
126
|
+
media = Google::APIClient::UploadIO.new('mymovie.m4v', 'video/mp4')
|
127
|
+
metadata = {
|
128
|
+
'title' => 'My movie',
|
129
|
+
'description' => 'The best home movie ever made'
|
130
|
+
}
|
131
|
+
client.execute(:api_method => drive.files.insert,
|
132
|
+
:parameters => { 'uploadType' => 'multipart' },
|
133
|
+
:body_object => metadata,
|
134
|
+
:media => media )
|
135
|
+
|
136
|
+
To use resumable uploads, change the `uploadType` parameter to `resumable`. To check the status of the upload
|
137
|
+
and continue if necessary, check `result.resumable_upload`.
|
138
|
+
|
139
|
+
client.execute(:api_method => drive.files.insert,
|
140
|
+
:parameters => { 'uploadType' => 'resumable' },
|
141
|
+
:body_object => metadata,
|
142
|
+
:media => media )
|
143
|
+
upload = result.resumable_upload
|
144
|
+
|
145
|
+
# Resume if needed
|
146
|
+
if upload.resumable?
|
147
|
+
client.execute(upload)
|
148
|
+
end
|
149
|
+
|
150
|
+
## Command Line
|
151
|
+
|
152
|
+
Included with the gem is a command line interface for working with Google APIs.
|
153
|
+
|
154
|
+
# Log in
|
155
|
+
google-api oauth-2-login --client-id='...' --client-secret='...' --scope="https://www.googleapis.com/auth/plus.me"
|
156
|
+
|
157
|
+
# List the signed-in user's activities
|
158
|
+
google-api execute plus.activities.list --api=plus -- userId="me" collection="public"
|
159
|
+
|
160
|
+
# Start an interactive API session
|
161
|
+
google-api irb
|
162
|
+
>> plus = $client.discovered_api('plus')
|
163
|
+
>> $client.execute(plus.activities.list, {'userId' => 'me', 'collection' => 'public'})
|
164
|
+
=> # returns a response from the API
|
165
|
+
|
166
|
+
For more information, use `google-api --help`
|
167
|
+
|
168
|
+
## Samples
|
169
|
+
|
170
|
+
See the full list of [samples on Google Code](http://code.google.com/p/google-api-ruby-client/source/browse?repo=samples).
|
171
|
+
|
172
|
+
|
173
|
+
## Support
|
174
|
+
|
175
|
+
Please [report bugs at the project on Google Code](http://code.google.com/p/google-api-ruby-client/issues/entry). Don't hesitate to [ask questions](http://stackoverflow.com/questions/tagged/google-api) about the client or APIs on [StackOverflow](http://stackoverflow.com).
|
65
176
|
|
66
|
-
$ sudo gem install google-api-client
|
67
177
|
|
68
|
-
The command line interface, the example applications, and the test suite
|
69
|
-
require additional dependencies. These may be obtained with:
|
70
178
|
|
71
|
-
$ sudo gem install google-api-client --development --force --no-rdoc --no-ri
|
data/Rakefile
CHANGED
@@ -15,8 +15,8 @@ PKG_HOMEPAGE = 'http://code.google.com/p/google-api-ruby-client/'
|
|
15
15
|
|
16
16
|
RELEASE_NAME = "REL #{PKG_VERSION}"
|
17
17
|
|
18
|
-
PKG_AUTHOR = "Bob Aman"
|
19
|
-
PKG_AUTHOR_EMAIL = "
|
18
|
+
PKG_AUTHOR = ["Bob Aman", "Steve Bazyl"]
|
19
|
+
PKG_AUTHOR_EMAIL = "sbazyl@google.com"
|
20
20
|
PKG_SUMMARY = 'Package Summary'
|
21
21
|
PKG_DESCRIPTION = <<-TEXT
|
22
22
|
The Google API Ruby Client makes it trivial to discover and access supported
|
data/bin/google-api
CHANGED
@@ -10,11 +10,8 @@ OAUTH_SERVER_PORT = 12736
|
|
10
10
|
|
11
11
|
require 'rubygems'
|
12
12
|
require 'optparse'
|
13
|
-
|
14
|
-
gem 'faraday', '~> 0.8.1'
|
15
13
|
require 'faraday'
|
16
14
|
require 'faraday/utils'
|
17
|
-
|
18
15
|
require 'webrick'
|
19
16
|
require 'google/api_client/version'
|
20
17
|
require 'google/api_client'
|
@@ -187,7 +184,6 @@ HTML
|
|
187
184
|
end
|
188
185
|
|
189
186
|
def client
|
190
|
-
gem 'signet', '~> 0.4.0'
|
191
187
|
require 'signet/oauth_1/client'
|
192
188
|
require 'yaml'
|
193
189
|
require 'irb'
|
@@ -281,7 +277,6 @@ HTML
|
|
281
277
|
]
|
282
278
|
|
283
279
|
def oauth_1_login
|
284
|
-
gem 'signet', '~> 0.4.0'
|
285
280
|
require 'signet/oauth_1/client'
|
286
281
|
require 'launchy'
|
287
282
|
require 'yaml'
|
@@ -349,7 +344,6 @@ HTML
|
|
349
344
|
end
|
350
345
|
|
351
346
|
def oauth_2_login
|
352
|
-
gem 'signet', '~> 0.4.0'
|
353
347
|
require 'signet/oauth_2/client'
|
354
348
|
require 'launchy'
|
355
349
|
require 'yaml'
|
@@ -471,9 +465,8 @@ HTML
|
|
471
465
|
method = options[:http_method]
|
472
466
|
method ||= request_body == '' ? 'GET' : 'POST'
|
473
467
|
method.upcase!
|
474
|
-
|
475
|
-
|
476
|
-
response = client.transmit(request)
|
468
|
+
response = client.execute(:http_method => method, :uri => uri.to_str,
|
469
|
+
:headers => headers, :body => request_body)
|
477
470
|
puts response.body
|
478
471
|
exit(0)
|
479
472
|
else
|
data/lib/compat/multi_json.rb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
|
-
gem 'multi_json', '>= 1.0.0'
|
2
1
|
require 'multi_json'
|
3
2
|
|
4
|
-
|
3
|
+
if !MultiJson.respond_to?(:load) || MultiJson.method(:load).owner == Kernel
|
5
4
|
module MultiJson
|
6
5
|
class <<self
|
7
6
|
alias :load :decode
|
8
7
|
end
|
9
8
|
end
|
10
9
|
end
|
11
|
-
|
10
|
+
if !MultiJson.respond_to?(:dump)
|
12
11
|
module MultiJson
|
13
12
|
class <<self
|
14
13
|
alias :dump :encode
|
data/lib/google/api_client.rb
CHANGED
@@ -13,7 +13,6 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
|
16
|
-
gem 'faraday', '~> 0.8.1'
|
17
16
|
require 'faraday'
|
18
17
|
require 'faraday/utils'
|
19
18
|
require 'multi_json'
|
@@ -24,6 +23,7 @@ require 'google/api_client/version'
|
|
24
23
|
require 'google/api_client/errors'
|
25
24
|
require 'google/api_client/environment'
|
26
25
|
require 'google/api_client/discovery'
|
26
|
+
require 'google/api_client/request'
|
27
27
|
require 'google/api_client/reference'
|
28
28
|
require 'google/api_client/result'
|
29
29
|
require 'google/api_client/media'
|
@@ -31,9 +31,6 @@ require 'google/api_client/service_account'
|
|
31
31
|
require 'google/api_client/batch'
|
32
32
|
|
33
33
|
module Google
|
34
|
-
# TODO(bobaman): Document all this stuff.
|
35
|
-
|
36
|
-
|
37
34
|
##
|
38
35
|
# This class manages APIs communication.
|
39
36
|
class APIClient
|
@@ -67,23 +64,23 @@ module Google
|
|
67
64
|
def initialize(options={})
|
68
65
|
# Normalize key to String to allow indifferent access.
|
69
66
|
options = options.inject({}) do |accu, (key, value)|
|
70
|
-
accu[key.
|
67
|
+
accu[key.to_sym] = value
|
71
68
|
accu
|
72
69
|
end
|
73
70
|
# Almost all API usage will have a host of 'www.googleapis.com'.
|
74
|
-
self.host = options[
|
75
|
-
self.port = options[
|
76
|
-
self.discovery_path = options[
|
71
|
+
self.host = options[:host] || 'www.googleapis.com'
|
72
|
+
self.port = options[:port] || 443
|
73
|
+
self.discovery_path = options[:discovery_path] || '/discovery/v1'
|
77
74
|
|
78
75
|
# Most developers will want to leave this value alone and use the
|
79
76
|
# application_name option.
|
80
77
|
application_string = (
|
81
|
-
options[
|
82
|
-
"#{options[
|
83
|
-
"#{options[
|
78
|
+
options[:application_name] ? (
|
79
|
+
"#{options[:application_name]}/" +
|
80
|
+
"#{options[:application_version] || '0.0.0'}"
|
84
81
|
) : ""
|
85
82
|
)
|
86
|
-
self.user_agent = options[
|
83
|
+
self.user_agent = options[:user_agent] || (
|
87
84
|
"#{application_string} " +
|
88
85
|
"google-api-ruby-client/#{VERSION::STRING} " +
|
89
86
|
ENV::OS_VERSION
|
@@ -91,9 +88,9 @@ module Google
|
|
91
88
|
# The writer method understands a few Symbols and will generate useful
|
92
89
|
# default authentication mechanisms.
|
93
90
|
self.authorization =
|
94
|
-
options.key?(
|
95
|
-
self.key = options[
|
96
|
-
self.user_ip = options[
|
91
|
+
options.key?(:authorization) ? options[:authorization] : :oauth_2
|
92
|
+
self.key = options[:key]
|
93
|
+
self.user_ip = options[:user_ip]
|
97
94
|
@discovery_uris = {}
|
98
95
|
@discovery_documents = {}
|
99
96
|
@discovered_apis = {}
|
@@ -114,7 +111,6 @@ module Google
|
|
114
111
|
def authorization=(new_authorization)
|
115
112
|
case new_authorization
|
116
113
|
when :oauth_1, :oauth
|
117
|
-
gem 'signet', '~> 0.4.0'
|
118
114
|
require 'signet/oauth_1/client'
|
119
115
|
# NOTE: Do not rely on this default value, as it may change
|
120
116
|
new_authorization = Signet::OAuth1::Client.new(
|
@@ -128,7 +124,6 @@ module Google
|
|
128
124
|
:client_credential_secret => 'anonymous'
|
129
125
|
)
|
130
126
|
when :two_legged_oauth_1, :two_legged_oauth
|
131
|
-
gem 'signet', '~> 0.4.0'
|
132
127
|
require 'signet/oauth_1/client'
|
133
128
|
# NOTE: Do not rely on this default value, as it may change
|
134
129
|
new_authorization = Signet::OAuth1::Client.new(
|
@@ -137,7 +132,6 @@ module Google
|
|
137
132
|
:two_legged => true
|
138
133
|
)
|
139
134
|
when :oauth_2
|
140
|
-
gem 'signet', '~> 0.4.0'
|
141
135
|
require 'signet/oauth_2/client'
|
142
136
|
# NOTE: Do not rely on this default value, as it may change
|
143
137
|
new_authorization = Signet::OAuth2::Client.new(
|
@@ -199,31 +193,6 @@ module Google
|
|
199
193
|
# The base path. Should almost always be '/discovery/v1'.
|
200
194
|
attr_accessor :discovery_path
|
201
195
|
|
202
|
-
##
|
203
|
-
# Resolves a URI template against the client's configured base.
|
204
|
-
#
|
205
|
-
# @param [String, Addressable::URI, Addressable::Template] template
|
206
|
-
# The template to resolve.
|
207
|
-
# @param [Hash] mapping The mapping that corresponds to the template.
|
208
|
-
# @return [Addressable::URI] The expanded URI.
|
209
|
-
def resolve_uri(template, mapping={})
|
210
|
-
@base_uri ||= Addressable::URI.new(
|
211
|
-
:scheme => 'https',
|
212
|
-
:host => self.host,
|
213
|
-
:port => self.port
|
214
|
-
).normalize
|
215
|
-
template = if template.kind_of?(Addressable::Template)
|
216
|
-
template.pattern
|
217
|
-
elsif template.respond_to?(:to_str)
|
218
|
-
template.to_str
|
219
|
-
else
|
220
|
-
raise TypeError,
|
221
|
-
"Expected String, Addressable::URI, or Addressable::Template, " +
|
222
|
-
"got #{template.class}."
|
223
|
-
end
|
224
|
-
return Addressable::Template.new(@base_uri + template).expand(mapping)
|
225
|
-
end
|
226
|
-
|
227
196
|
##
|
228
197
|
# Returns the URI for the directory document.
|
229
198
|
#
|
@@ -293,27 +262,12 @@ module Google
|
|
293
262
|
# @return [Hash] The parsed JSON from the directory document.
|
294
263
|
def directory_document
|
295
264
|
return @directory_document ||= (begin
|
296
|
-
|
265
|
+
response = self.execute!(
|
297
266
|
:http_method => :get,
|
298
267
|
:uri => self.directory_uri,
|
299
268
|
:authenticated => false
|
300
269
|
)
|
301
|
-
response
|
302
|
-
if response.status >= 200 && response.status < 300
|
303
|
-
MultiJson.load(response.body)
|
304
|
-
elsif response.status >= 400
|
305
|
-
case response.status
|
306
|
-
when 400...500
|
307
|
-
exception_type = ClientError
|
308
|
-
when 500...600
|
309
|
-
exception_type = ServerError
|
310
|
-
else
|
311
|
-
exception_type = TransmissionError
|
312
|
-
end
|
313
|
-
url = request.to_env(Faraday.default_connection)[:url]
|
314
|
-
raise exception_type,
|
315
|
-
"Could not retrieve directory document at: #{url}"
|
316
|
-
end
|
270
|
+
response.data
|
317
271
|
end)
|
318
272
|
end
|
319
273
|
|
@@ -327,27 +281,12 @@ module Google
|
|
327
281
|
api = api.to_s
|
328
282
|
version = version || 'v1'
|
329
283
|
return @discovery_documents["#{api}:#{version}"] ||= (begin
|
330
|
-
|
284
|
+
response = self.execute!(
|
331
285
|
:http_method => :get,
|
332
286
|
:uri => self.discovery_uri(api, version),
|
333
287
|
:authenticated => false
|
334
288
|
)
|
335
|
-
response
|
336
|
-
if response.status >= 200 && response.status < 300
|
337
|
-
MultiJson.load(response.body)
|
338
|
-
elsif response.status >= 400
|
339
|
-
case response.status
|
340
|
-
when 400...500
|
341
|
-
exception_type = ClientError
|
342
|
-
when 500...600
|
343
|
-
exception_type = ServerError
|
344
|
-
else
|
345
|
-
exception_type = TransmissionError
|
346
|
-
end
|
347
|
-
url = request.to_env(Faraday.default_connection)[:url]
|
348
|
-
raise exception_type,
|
349
|
-
"Could not retrieve discovery document at: #{url}"
|
350
|
-
end
|
289
|
+
response.data
|
351
290
|
end)
|
352
291
|
end
|
353
292
|
|
@@ -403,7 +342,7 @@ module Google
|
|
403
342
|
# Returns the method object for a given RPC name and service version.
|
404
343
|
#
|
405
344
|
# @param [String, Symbol] rpc_name The RPC name of the desired method.
|
406
|
-
# @param [String, Symbol]
|
345
|
+
# @param [String, Symbol] api The API the method is within.
|
407
346
|
# @param [String] version The desired version of the API.
|
408
347
|
#
|
409
348
|
# @return [Google::APIClient::Method] The method object.
|
@@ -449,7 +388,6 @@ module Google
|
|
449
388
|
# an ID token supplied by an untrusted client-side mechanism is valid.
|
450
389
|
# Raises an error if the token is invalid or missing.
|
451
390
|
def verify_id_token!
|
452
|
-
gem 'jwt', '~> 0.1.4'
|
453
391
|
require 'jwt'
|
454
392
|
require 'openssl'
|
455
393
|
@certificates ||= {}
|
@@ -479,31 +417,16 @@ module Google
|
|
479
417
|
if check_cached_certs.call()
|
480
418
|
return true
|
481
419
|
end
|
482
|
-
|
420
|
+
response = self.execute!(
|
483
421
|
:http_method => :get,
|
484
422
|
:uri => 'https://www.googleapis.com/oauth2/v1/certs',
|
485
423
|
:authenticated => false
|
486
424
|
)
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
end]
|
493
|
-
)
|
494
|
-
elsif response.status >= 400
|
495
|
-
case response.status
|
496
|
-
when 400...500
|
497
|
-
exception_type = ClientError
|
498
|
-
when 500...600
|
499
|
-
exception_type = ServerError
|
500
|
-
else
|
501
|
-
exception_type = TransmissionError
|
502
|
-
end
|
503
|
-
url = request.to_env(Faraday.default_connection)[:url]
|
504
|
-
raise exception_type,
|
505
|
-
"Could not retrieve certificates from: #{url}"
|
506
|
-
end
|
425
|
+
@certificates.merge!(
|
426
|
+
Hash[MultiJson.load(response.body).map do |key, cert|
|
427
|
+
[key, OpenSSL::X509::Certificate.new(cert)]
|
428
|
+
end]
|
429
|
+
)
|
507
430
|
if check_cached_certs.call()
|
508
431
|
return true
|
509
432
|
else
|
@@ -517,7 +440,7 @@ module Google
|
|
517
440
|
##
|
518
441
|
# Generates a request.
|
519
442
|
#
|
520
|
-
# @option options [Google::APIClient::Method
|
443
|
+
# @option options [Google::APIClient::Method] :api_method
|
521
444
|
# The method object or the RPC name of the method being executed.
|
522
445
|
# @option options [Hash, Array] :parameters
|
523
446
|
# The parameters to send to the method.
|
@@ -532,7 +455,7 @@ module Google
|
|
532
455
|
# `true` if the request must be signed or somehow
|
533
456
|
# authenticated, `false` otherwise.
|
534
457
|
#
|
535
|
-
# @return [
|
458
|
+
# @return [Google::APIClient::Reference] The generated request.
|
536
459
|
#
|
537
460
|
# @example
|
538
461
|
# request = client.generate_request(
|
@@ -541,153 +464,30 @@ module Google
|
|
541
464
|
# {'collection' => 'public', 'userId' => 'me'}
|
542
465
|
# )
|
543
466
|
def generate_request(options={})
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
options={
|
548
|
-
:version => 'v1',
|
549
|
-
:authorization => self.authorization,
|
550
|
-
:key => self.key,
|
551
|
-
:user_ip => self.user_ip,
|
552
|
-
:connection => Faraday.default_connection
|
467
|
+
options = {
|
468
|
+
:api_client => self
|
553
469
|
}.merge(options)
|
554
|
-
|
555
|
-
# The Reference object is going to need this to do method ID lookups.
|
556
|
-
options[:client] = self
|
557
|
-
# The default value for the :authenticated option depends on whether an
|
558
|
-
# authorization mechanism has been set.
|
559
|
-
if options[:authorization]
|
560
|
-
options = {:authenticated => true}.merge(options)
|
561
|
-
else
|
562
|
-
options = {:authenticated => false}.merge(options)
|
563
|
-
end
|
564
|
-
reference = Google::APIClient::Reference.new(options)
|
565
|
-
request = reference.to_request
|
566
|
-
if options[:authenticated]
|
567
|
-
request = options[:authorization].generate_authenticated_request(
|
568
|
-
:request => request,
|
569
|
-
:connection => options[:connection]
|
570
|
-
)
|
571
|
-
end
|
572
|
-
return request
|
573
|
-
end
|
574
|
-
|
575
|
-
##
|
576
|
-
# Signs a request using the current authorization mechanism.
|
577
|
-
#
|
578
|
-
# @param [Hash] options a customizable set of options
|
579
|
-
#
|
580
|
-
# @return [Faraday::Request] The signed or otherwise authenticated request.
|
581
|
-
# @deprecated No longer used internally
|
582
|
-
def generate_authenticated_request(options={})
|
583
|
-
return authorization.generate_authenticated_request(options)
|
584
|
-
end
|
585
|
-
|
586
|
-
##
|
587
|
-
# Transmits the request using the current HTTP adapter.
|
588
|
-
#
|
589
|
-
# @option options [Array, Faraday::Request] :request
|
590
|
-
# The HTTP request to transmit.
|
591
|
-
# @option options [String, Symbol] :method
|
592
|
-
# The method for the HTTP request.
|
593
|
-
# @option options [String, Addressable::URI] :uri
|
594
|
-
# The URI for the HTTP request.
|
595
|
-
# @option options [Array, Hash] :headers
|
596
|
-
# The headers for the HTTP request.
|
597
|
-
# @option options [String] :body
|
598
|
-
# The body for the HTTP request.
|
599
|
-
# @option options [Faraday::Connection] :connection
|
600
|
-
# The HTTP connection to use.
|
601
|
-
#
|
602
|
-
# @return [Faraday::Response] The response from the server.
|
603
|
-
def transmit(options={})
|
604
|
-
options[:connection] ||= Faraday.default_connection
|
605
|
-
if options[:request]
|
606
|
-
if options[:request].kind_of?(Array)
|
607
|
-
method, uri, headers, body = options[:request]
|
608
|
-
elsif options[:request].kind_of?(Faraday::Request)
|
609
|
-
unless options[:connection]
|
610
|
-
raise ArgumentError,
|
611
|
-
"Faraday::Request used, requires a connection to be provided."
|
612
|
-
end
|
613
|
-
method = options[:request].method.to_s.downcase.to_sym
|
614
|
-
uri = options[:connection].build_url(
|
615
|
-
options[:request].path, options[:request].params
|
616
|
-
)
|
617
|
-
headers = options[:request].headers || {}
|
618
|
-
body = options[:request].body || ''
|
619
|
-
end
|
620
|
-
else
|
621
|
-
method = options[:method] || :get
|
622
|
-
uri = options[:uri]
|
623
|
-
headers = options[:headers] || []
|
624
|
-
body = options[:body] || ''
|
625
|
-
end
|
626
|
-
headers = headers.to_a if headers.kind_of?(Hash)
|
627
|
-
request_components = {
|
628
|
-
:method => method,
|
629
|
-
:uri => uri,
|
630
|
-
:headers => headers,
|
631
|
-
:body => body
|
632
|
-
}
|
633
|
-
# Verify that we have all pieces required to transmit an HTTP request
|
634
|
-
request_components.each do |(key, value)|
|
635
|
-
unless value
|
636
|
-
raise ArgumentError, "Missing :#{key} parameter."
|
637
|
-
end
|
638
|
-
end
|
639
|
-
|
640
|
-
if self.user_agent != nil
|
641
|
-
# If there's no User-Agent header, set one.
|
642
|
-
unless headers.kind_of?(Enumerable)
|
643
|
-
# We need to use some Enumerable methods, relying on the presence of
|
644
|
-
# the #each method.
|
645
|
-
class << headers
|
646
|
-
include Enumerable
|
647
|
-
end
|
648
|
-
end
|
649
|
-
if self.user_agent.kind_of?(String)
|
650
|
-
unless headers.any? { |k, v| k.downcase == 'User-Agent'.downcase }
|
651
|
-
headers = headers.to_a.insert(0, ['User-Agent', self.user_agent])
|
652
|
-
end
|
653
|
-
elsif self.user_agent != nil
|
654
|
-
raise TypeError,
|
655
|
-
"Expected User-Agent to be String, got #{self.user_agent.class}"
|
656
|
-
end
|
657
|
-
end
|
658
|
-
|
659
|
-
request = options[:connection].build_request(
|
660
|
-
method.to_s.downcase.to_sym
|
661
|
-
) do |req|
|
662
|
-
req.url(Addressable::URI.parse(uri).normalize.to_s)
|
663
|
-
req.headers = Faraday::Utils::Headers.new(headers)
|
664
|
-
req.body = body
|
665
|
-
end
|
666
|
-
request_env = request.to_env(options[:connection])
|
667
|
-
response = options[:connection].app.call(request_env)
|
668
|
-
return response
|
470
|
+
return Google::APIClient::Request.new(options)
|
669
471
|
end
|
670
472
|
|
671
473
|
##
|
672
474
|
# Executes a request, wrapping it in a Result object.
|
673
475
|
#
|
674
|
-
# @param [Google::APIClient::
|
675
|
-
# Either a Google::APIClient::
|
476
|
+
# @param [Google::APIClient::Request, Hash, Array] params
|
477
|
+
# Either a Google::APIClient::Request, a Hash, or an Array.
|
676
478
|
#
|
677
|
-
# If a Google::APIClient::
|
479
|
+
# If a Google::APIClient::Request, no other parameters are expected.
|
678
480
|
#
|
679
481
|
# If a Hash, the below parameters are handled. If an Array, the
|
680
482
|
# parameters are assumed to be in the below order:
|
681
483
|
#
|
682
|
-
# - (Google::APIClient::Method
|
484
|
+
# - (Google::APIClient::Method) api_method:
|
683
485
|
# The method object or the RPC name of the method being executed.
|
684
486
|
# - (Hash, Array) parameters:
|
685
487
|
# The parameters to send to the method.
|
686
488
|
# - (String) body: The body of the request.
|
687
489
|
# - (Hash, Array) headers: The HTTP headers for the request.
|
688
490
|
# - (Hash) options: A set of options for the request, of which:
|
689
|
-
# - (String) :version (default: "v1") -
|
690
|
-
# The service version. Only used if `api_method` is a `String`.
|
691
491
|
# - (#generate_authenticated_request) :authorization (default: true) -
|
692
492
|
# The authorization mechanism for the response. Used only if
|
693
493
|
# `:authenticated` is `true`.
|
@@ -701,50 +501,18 @@ module Google
|
|
701
501
|
# result = client.execute(batch_request)
|
702
502
|
#
|
703
503
|
# @example
|
504
|
+
# plus = client.discovered_api('plus')
|
704
505
|
# result = client.execute(
|
705
|
-
# :api_method =>
|
506
|
+
# :api_method => plus.activities.list,
|
706
507
|
# :parameters => {'collection' => 'public', 'userId' => 'me'}
|
707
508
|
# )
|
708
509
|
#
|
709
510
|
# @see Google::APIClient#generate_request
|
710
511
|
def execute(*params)
|
711
|
-
if params.last.kind_of?(Google::APIClient::
|
512
|
+
if params.last.kind_of?(Google::APIClient::Request) &&
|
712
513
|
params.size == 1
|
713
|
-
|
714
|
-
options =
|
715
|
-
options[:connection] ||= Faraday.default_connection
|
716
|
-
http_request = batch.to_http_request
|
717
|
-
request = nil
|
718
|
-
|
719
|
-
if @authorization
|
720
|
-
method, uri, headers, body = http_request
|
721
|
-
method = method.to_s.downcase.to_sym
|
722
|
-
|
723
|
-
faraday_request = options[:connection].build_request(
|
724
|
-
method.to_s.downcase.to_sym
|
725
|
-
) do |req|
|
726
|
-
req.url(Addressable::URI.parse(uri).normalize.to_s)
|
727
|
-
req.headers = Faraday::Utils::Headers.new(headers)
|
728
|
-
req.body = body
|
729
|
-
end
|
730
|
-
|
731
|
-
request = {
|
732
|
-
:request => self.generate_authenticated_request(
|
733
|
-
:request => faraday_request,
|
734
|
-
:connection => options[:connection]
|
735
|
-
),
|
736
|
-
:connection => options[:connection]
|
737
|
-
}
|
738
|
-
else
|
739
|
-
request = {
|
740
|
-
:request => http_request,
|
741
|
-
:connection => options[:connection]
|
742
|
-
}
|
743
|
-
end
|
744
|
-
|
745
|
-
response = self.transmit(request)
|
746
|
-
batch.process_response(response)
|
747
|
-
return nil
|
514
|
+
request = params.pop
|
515
|
+
options = {}
|
748
516
|
else
|
749
517
|
# This block of code allows us to accept multiple parameter passing
|
750
518
|
# styles, and maintaining some backwards compatibility.
|
@@ -760,16 +528,28 @@ module Google
|
|
760
528
|
options[:parameters] = params.shift if params.size > 0
|
761
529
|
options[:body] = params.shift if params.size > 0
|
762
530
|
options[:headers] = params.shift if params.size > 0
|
763
|
-
options
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
531
|
+
options.update(params.shift) if params.size > 0
|
532
|
+
request = self.generate_request(options)
|
533
|
+
end
|
534
|
+
|
535
|
+
request.headers['User-Agent'] ||= '' + self.user_agent unless self.user_agent.nil?
|
536
|
+
request.parameters['key'] ||= self.key unless self.key.nil?
|
537
|
+
request.parameters['userIp'] ||= self.user_ip unless self.user_ip.nil?
|
538
|
+
|
539
|
+
connection = options[:connection] || Faraday.default_connection
|
540
|
+
request.authorization = options[:authorization] || self.authorization unless options[:authenticated] == false
|
541
|
+
|
542
|
+
result = request.send(connection)
|
543
|
+
if result.status == 401 && authorization.respond_to?(:refresh_token)
|
544
|
+
begin
|
545
|
+
authorization.fetch_access_token!
|
546
|
+
result = request.send(connection)
|
547
|
+
rescue Signet::AuthorizationError
|
548
|
+
# Ignore since we want the original error
|
549
|
+
end
|
772
550
|
end
|
551
|
+
|
552
|
+
return result
|
773
553
|
end
|
774
554
|
|
775
555
|
##
|
@@ -796,6 +576,35 @@ module Google
|
|
796
576
|
end
|
797
577
|
return result
|
798
578
|
end
|
579
|
+
|
580
|
+
protected
|
581
|
+
|
582
|
+
##
|
583
|
+
# Resolves a URI template against the client's configured base.
|
584
|
+
#
|
585
|
+
# @api private
|
586
|
+
# @param [String, Addressable::URI, Addressable::Template] template
|
587
|
+
# The template to resolve.
|
588
|
+
# @param [Hash] mapping The mapping that corresponds to the template.
|
589
|
+
# @return [Addressable::URI] The expanded URI.
|
590
|
+
def resolve_uri(template, mapping={})
|
591
|
+
@base_uri ||= Addressable::URI.new(
|
592
|
+
:scheme => 'https',
|
593
|
+
:host => self.host,
|
594
|
+
:port => self.port
|
595
|
+
).normalize
|
596
|
+
template = if template.kind_of?(Addressable::Template)
|
597
|
+
template.pattern
|
598
|
+
elsif template.respond_to?(:to_str)
|
599
|
+
template.to_str
|
600
|
+
else
|
601
|
+
raise TypeError,
|
602
|
+
"Expected String, Addressable::URI, or Addressable::Template, " +
|
603
|
+
"got #{template.class}."
|
604
|
+
end
|
605
|
+
return Addressable::Template.new(@base_uri + template).expand(mapping)
|
606
|
+
end
|
607
|
+
|
799
608
|
end
|
800
609
|
end
|
801
610
|
|