jomz-google-api-client 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/CHANGELOG.md +144 -0
  2. data/CONTRIBUTING.md +32 -0
  3. data/Gemfile +41 -0
  4. data/LICENSE +202 -0
  5. data/README.md +192 -0
  6. data/Rakefile +46 -0
  7. data/lib/cacerts.pem +2183 -0
  8. data/lib/compat/multi_json.rb +16 -0
  9. data/lib/google/api_client.rb +672 -0
  10. data/lib/google/api_client/auth/compute_service_account.rb +28 -0
  11. data/lib/google/api_client/auth/file_storage.rb +87 -0
  12. data/lib/google/api_client/auth/installed_app.rb +122 -0
  13. data/lib/google/api_client/auth/jwt_asserter.rb +126 -0
  14. data/lib/google/api_client/auth/key_utils.rb +93 -0
  15. data/lib/google/api_client/auth/pkcs12.rb +41 -0
  16. data/lib/google/api_client/batch.rb +323 -0
  17. data/lib/google/api_client/client_secrets.rb +176 -0
  18. data/lib/google/api_client/discovery.rb +19 -0
  19. data/lib/google/api_client/discovery/api.rb +300 -0
  20. data/lib/google/api_client/discovery/media.rb +77 -0
  21. data/lib/google/api_client/discovery/method.rb +363 -0
  22. data/lib/google/api_client/discovery/resource.rb +156 -0
  23. data/lib/google/api_client/discovery/schema.rb +121 -0
  24. data/lib/google/api_client/environment.rb +42 -0
  25. data/lib/google/api_client/errors.rb +60 -0
  26. data/lib/google/api_client/gzip.rb +28 -0
  27. data/lib/google/api_client/logging.rb +32 -0
  28. data/lib/google/api_client/media.rb +259 -0
  29. data/lib/google/api_client/railtie.rb +16 -0
  30. data/lib/google/api_client/reference.rb +27 -0
  31. data/lib/google/api_client/request.rb +351 -0
  32. data/lib/google/api_client/result.rb +253 -0
  33. data/lib/google/api_client/service.rb +233 -0
  34. data/lib/google/api_client/service/batch.rb +103 -0
  35. data/lib/google/api_client/service/request.rb +144 -0
  36. data/lib/google/api_client/service/resource.rb +40 -0
  37. data/lib/google/api_client/service/result.rb +162 -0
  38. data/lib/google/api_client/service/simple_file_store.rb +151 -0
  39. data/lib/google/api_client/service/stub_generator.rb +59 -0
  40. data/lib/google/api_client/service_account.rb +18 -0
  41. data/lib/google/api_client/version.rb +31 -0
  42. data/lib/google/inflection.rb +28 -0
  43. data/spec/fixtures/files/privatekey.p12 +0 -0
  44. data/spec/fixtures/files/sample.txt +33 -0
  45. data/spec/fixtures/files/secret.pem +19 -0
  46. data/spec/google/api_client/batch_spec.rb +249 -0
  47. data/spec/google/api_client/discovery_spec.rb +652 -0
  48. data/spec/google/api_client/gzip_spec.rb +86 -0
  49. data/spec/google/api_client/media_spec.rb +179 -0
  50. data/spec/google/api_client/request_spec.rb +30 -0
  51. data/spec/google/api_client/result_spec.rb +203 -0
  52. data/spec/google/api_client/service_account_spec.rb +164 -0
  53. data/spec/google/api_client/service_spec.rb +586 -0
  54. data/spec/google/api_client/simple_file_store_spec.rb +137 -0
  55. data/spec/google/api_client_spec.rb +253 -0
  56. data/spec/spec_helper.rb +56 -0
  57. data/tasks/gem.rake +97 -0
  58. data/tasks/git.rake +45 -0
  59. data/tasks/metrics.rake +22 -0
  60. data/tasks/spec.rake +57 -0
  61. data/tasks/wiki.rake +82 -0
  62. data/tasks/yard.rake +29 -0
  63. metadata +309 -0
@@ -0,0 +1,164 @@
1
+ # Copyright 2012 Google Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'spec_helper'
16
+
17
+ require 'google/api_client'
18
+
19
+ fixtures_path = File.expand_path('../../../fixtures', __FILE__)
20
+
21
+ describe Google::APIClient::KeyUtils do
22
+ it 'should read PKCS12 files from the filesystem' do
23
+ pending "Reading from PKCS12 not supported on jruby" if RUBY_PLATFORM == 'java'
24
+ path = File.expand_path('files/privatekey.p12', fixtures_path)
25
+ key = Google::APIClient::KeyUtils.load_from_pkcs12(path, 'notasecret')
26
+ key.should_not == nil
27
+ end
28
+
29
+ it 'should read PKCS12 files from loaded files' do
30
+ pending "Reading from PKCS12 not supported on jruby" if RUBY_PLATFORM == 'java'
31
+ path = File.expand_path('files/privatekey.p12', fixtures_path)
32
+ content = File.read(path)
33
+ key = Google::APIClient::KeyUtils.load_from_pkcs12(content, 'notasecret')
34
+ key.should_not == nil
35
+ end
36
+
37
+ it 'should read PEM files from the filesystem' do
38
+ path = File.expand_path('files/secret.pem', fixtures_path)
39
+ key = Google::APIClient::KeyUtils.load_from_pem(path, 'notasecret')
40
+ key.should_not == nil
41
+ end
42
+
43
+ it 'should read PEM files from loaded files' do
44
+ path = File.expand_path('files/secret.pem', fixtures_path)
45
+ content = File.read(path)
46
+ key = Google::APIClient::KeyUtils.load_from_pem(content, 'notasecret')
47
+ key.should_not == nil
48
+ end
49
+
50
+ end
51
+
52
+ describe Google::APIClient::JWTAsserter do
53
+ include ConnectionHelpers
54
+
55
+ before do
56
+ @key = OpenSSL::PKey::RSA.new 2048
57
+ end
58
+
59
+ it 'should generate valid JWTs' do
60
+ asserter = Google::APIClient::JWTAsserter.new('client1', 'scope1 scope2', @key)
61
+ jwt = asserter.to_authorization.to_jwt
62
+ jwt.should_not == nil
63
+
64
+ claim = JWT.decode(jwt, @key.public_key, true)
65
+ claim["iss"].should == 'client1'
66
+ claim["scope"].should == 'scope1 scope2'
67
+ end
68
+
69
+ it 'should allow impersonation' do
70
+ conn = stub_connection do |stub|
71
+ stub.post('/o/oauth2/token') do |env|
72
+ params = Addressable::URI.form_unencode(env[:body])
73
+ JWT.decode(params.assoc("assertion").last, @key.public_key)
74
+ params.assoc("grant_type").should == ['grant_type','urn:ietf:params:oauth:grant-type:jwt-bearer']
75
+ [200, {}, '{
76
+ "access_token" : "1/abcdef1234567890",
77
+ "token_type" : "Bearer",
78
+ "expires_in" : 3600
79
+ }']
80
+ end
81
+ end
82
+ asserter = Google::APIClient::JWTAsserter.new('client1', 'scope1 scope2', @key)
83
+ auth = asserter.authorize('user1@email.com', { :connection => conn })
84
+ auth.should_not == nil?
85
+ auth.person.should == 'user1@email.com'
86
+ conn.verify
87
+ end
88
+
89
+ it 'should send valid access token request' do
90
+ conn = stub_connection do |stub|
91
+ stub.post('/o/oauth2/token') do |env|
92
+ params = Addressable::URI.form_unencode(env[:body])
93
+ JWT.decode(params.assoc("assertion").last, @key.public_key)
94
+ params.assoc("grant_type").should == ['grant_type','urn:ietf:params:oauth:grant-type:jwt-bearer']
95
+ [200, {}, '{
96
+ "access_token" : "1/abcdef1234567890",
97
+ "token_type" : "Bearer",
98
+ "expires_in" : 3600
99
+ }']
100
+ end
101
+ end
102
+ asserter = Google::APIClient::JWTAsserter.new('client1', 'scope1 scope2', @key)
103
+ auth = asserter.authorize(nil, { :connection => conn })
104
+ auth.should_not == nil?
105
+ auth.access_token.should == "1/abcdef1234567890"
106
+ conn.verify
107
+ end
108
+
109
+ it 'should be refreshable' do
110
+ conn = stub_connection do |stub|
111
+ stub.post('/o/oauth2/token') do |env|
112
+ params = Addressable::URI.form_unencode(env[:body])
113
+ JWT.decode(params.assoc("assertion").last, @key.public_key)
114
+ params.assoc("grant_type").should == ['grant_type','urn:ietf:params:oauth:grant-type:jwt-bearer']
115
+ [200, {}, '{
116
+ "access_token" : "1/abcdef1234567890",
117
+ "token_type" : "Bearer",
118
+ "expires_in" : 3600
119
+ }']
120
+ end
121
+ stub.post('/o/oauth2/token') do |env|
122
+ params = Addressable::URI.form_unencode(env[:body])
123
+ JWT.decode(params.assoc("assertion").last, @key.public_key)
124
+ params.assoc("grant_type").should == ['grant_type','urn:ietf:params:oauth:grant-type:jwt-bearer']
125
+ [200, {}, '{
126
+ "access_token" : "1/0987654321fedcba",
127
+ "token_type" : "Bearer",
128
+ "expires_in" : 3600
129
+ }']
130
+ end
131
+ end
132
+ asserter = Google::APIClient::JWTAsserter.new('client1', 'scope1 scope2', @key)
133
+ auth = asserter.authorize(nil, { :connection => conn })
134
+ auth.should_not == nil?
135
+ auth.access_token.should == "1/abcdef1234567890"
136
+
137
+ auth.fetch_access_token!(:connection => conn)
138
+ auth.access_token.should == "1/0987654321fedcba"
139
+
140
+ conn.verify
141
+ end
142
+ end
143
+
144
+ describe Google::APIClient::ComputeServiceAccount do
145
+ include ConnectionHelpers
146
+
147
+ it 'should query metadata server' do
148
+ conn = stub_connection do |stub|
149
+ stub.get('/computeMetadata/v1beta1/instance/service-accounts/default/token') do |env|
150
+ env.url.host.should == 'metadata'
151
+ [200, {}, '{
152
+ "access_token" : "1/abcdef1234567890",
153
+ "token_type" : "Bearer",
154
+ "expires_in" : 3600
155
+ }']
156
+ end
157
+ end
158
+ service_account = Google::APIClient::ComputeServiceAccount.new
159
+ auth = service_account.fetch_access_token!({ :connection => conn })
160
+ auth.should_not == nil?
161
+ auth["access_token"].should == "1/abcdef1234567890"
162
+ conn.verify
163
+ end
164
+ end
@@ -0,0 +1,586 @@
1
+ # encoding:utf-8
2
+
3
+ # Copyright 2013 Google Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'spec_helper'
18
+
19
+ require 'google/api_client'
20
+ require 'google/api_client/service'
21
+
22
+ fixtures_path = File.expand_path('../../../fixtures', __FILE__)
23
+
24
+ describe Google::APIClient::Service do
25
+ include ConnectionHelpers
26
+
27
+ APPLICATION_NAME = 'API Client Tests'
28
+
29
+ it 'should error out when called without an API name or version' do
30
+ (lambda do
31
+ Google::APIClient::Service.new
32
+ end).should raise_error(ArgumentError)
33
+ end
34
+
35
+ it 'should error out when called without an API version' do
36
+ (lambda do
37
+ Google::APIClient::Service.new('foo')
38
+ end).should raise_error(ArgumentError)
39
+ end
40
+
41
+ it 'should error out when the options hash is not a hash' do
42
+ (lambda do
43
+ Google::APIClient::Service.new('foo', 'v1', 42)
44
+ end).should raise_error(ArgumentError)
45
+ end
46
+
47
+ describe 'with the AdSense Management API' do
48
+
49
+ it 'should make a valid call for a method with no parameters' do
50
+ conn = stub_connection do |stub|
51
+ stub.get('/adsense/v1.3/adclients') do |env|
52
+ [200, {}, '{}']
53
+ end
54
+ end
55
+ adsense = Google::APIClient::Service.new(
56
+ 'adsense',
57
+ 'v1.3',
58
+ {
59
+ :application_name => APPLICATION_NAME,
60
+ :authenticated => false,
61
+ :connection => conn,
62
+ :cache_store => nil
63
+ }
64
+ )
65
+
66
+ req = adsense.adclients.list.execute()
67
+ conn.verify
68
+ end
69
+
70
+ it 'should make a valid call for a method with parameters' do
71
+ conn = stub_connection do |stub|
72
+ stub.get('/adsense/v1.3/adclients/1/adunits') do |env|
73
+ [200, {}, '{}']
74
+ end
75
+ end
76
+ adsense = Google::APIClient::Service.new(
77
+ 'adsense',
78
+ 'v1.3',
79
+ {
80
+ :application_name => APPLICATION_NAME,
81
+ :authenticated => false,
82
+ :connection => conn,
83
+ :cache_store => nil
84
+ }
85
+ )
86
+ req = adsense.adunits.list(:adClientId => '1').execute()
87
+ end
88
+
89
+ it 'should make a valid call for a deep method' do
90
+ conn = stub_connection do |stub|
91
+ stub.get('/adsense/v1.3/accounts/1/adclients') do |env|
92
+ [200, {}, '{}']
93
+ end
94
+ end
95
+ adsense = Google::APIClient::Service.new(
96
+ 'adsense',
97
+ 'v1.3',
98
+ {
99
+ :application_name => APPLICATION_NAME,
100
+ :authenticated => false,
101
+ :connection => conn,
102
+ :cache_store => nil
103
+ }
104
+ )
105
+ req = adsense.accounts.adclients.list(:accountId => '1').execute()
106
+ end
107
+
108
+ describe 'with no connection' do
109
+ before do
110
+ @adsense = Google::APIClient::Service.new('adsense', 'v1.3',
111
+ {:application_name => APPLICATION_NAME, :cache_store => nil})
112
+ end
113
+
114
+ it 'should return a resource when using a valid resource name' do
115
+ @adsense.accounts.should be_a(Google::APIClient::Service::Resource)
116
+ end
117
+
118
+ it 'should throw an error when using an invalid resource name' do
119
+ (lambda do
120
+ @adsense.invalid_resource
121
+ end).should raise_error
122
+ end
123
+
124
+ it 'should return a request when using a valid method name' do
125
+ req = @adsense.adclients.list
126
+ req.should be_a(Google::APIClient::Service::Request)
127
+ req.method.id.should == 'adsense.adclients.list'
128
+ req.parameters.should be_nil
129
+ end
130
+
131
+ it 'should throw an error when using an invalid method name' do
132
+ (lambda do
133
+ @adsense.adclients.invalid_method
134
+ end).should raise_error
135
+ end
136
+
137
+ it 'should return a valid request with parameters' do
138
+ req = @adsense.adunits.list(:adClientId => '1')
139
+ req.should be_a(Google::APIClient::Service::Request)
140
+ req.method.id.should == 'adsense.adunits.list'
141
+ req.parameters.should_not be_nil
142
+ req.parameters[:adClientId].should == '1'
143
+ end
144
+ end
145
+ end
146
+
147
+ describe 'with the Prediction API' do
148
+
149
+ it 'should make a valid call with an object body' do
150
+ conn = stub_connection do |stub|
151
+ stub.post('/prediction/v1.5/trainedmodels?project=1') do |env|
152
+ env.body.should == '{"id":"1"}'
153
+ [200, {}, '{}']
154
+ end
155
+ end
156
+ prediction = Google::APIClient::Service.new(
157
+ 'prediction',
158
+ 'v1.5',
159
+ {
160
+ :application_name => APPLICATION_NAME,
161
+ :authenticated => false,
162
+ :connection => conn,
163
+ :cache_store => nil
164
+ }
165
+ )
166
+ req = prediction.trainedmodels.insert(:project => '1').body({'id' => '1'}).execute()
167
+ conn.verify
168
+ end
169
+
170
+ it 'should make a valid call with a text body' do
171
+ conn = stub_connection do |stub|
172
+ stub.post('/prediction/v1.5/trainedmodels?project=1') do |env|
173
+ env.body.should == '{"id":"1"}'
174
+ [200, {}, '{}']
175
+ end
176
+ end
177
+ prediction = Google::APIClient::Service.new(
178
+ 'prediction',
179
+ 'v1.5',
180
+ {
181
+ :application_name => APPLICATION_NAME,
182
+ :authenticated => false,
183
+ :connection => conn,
184
+ :cache_store => nil
185
+ }
186
+ )
187
+ req = prediction.trainedmodels.insert(:project => '1').body('{"id":"1"}').execute()
188
+ conn.verify
189
+ end
190
+
191
+ describe 'with no connection' do
192
+ before do
193
+ @prediction = Google::APIClient::Service.new('prediction', 'v1.5',
194
+ {:application_name => APPLICATION_NAME, :cache_store => nil})
195
+ end
196
+
197
+ it 'should return a valid request with a body' do
198
+ req = @prediction.trainedmodels.insert(:project => '1').body({'id' => '1'})
199
+ req.should be_a(Google::APIClient::Service::Request)
200
+ req.method.id.should == 'prediction.trainedmodels.insert'
201
+ req.body.should == {'id' => '1'}
202
+ req.parameters.should_not be_nil
203
+ req.parameters[:project].should == '1'
204
+ end
205
+
206
+ it 'should return a valid request with a body when using resource name' do
207
+ req = @prediction.trainedmodels.insert(:project => '1').training({'id' => '1'})
208
+ req.should be_a(Google::APIClient::Service::Request)
209
+ req.method.id.should == 'prediction.trainedmodels.insert'
210
+ req.training.should == {'id' => '1'}
211
+ req.parameters.should_not be_nil
212
+ req.parameters[:project].should == '1'
213
+ end
214
+ end
215
+ end
216
+
217
+ describe 'with the Drive API' do
218
+
219
+ before do
220
+ @metadata = {
221
+ 'title' => 'My movie',
222
+ 'description' => 'The best home movie ever made'
223
+ }
224
+ @file = File.expand_path('files/sample.txt', fixtures_path)
225
+ @media = Google::APIClient::UploadIO.new(@file, 'text/plain')
226
+ end
227
+
228
+ it 'should make a valid call with an object body and media upload' do
229
+ conn = stub_connection do |stub|
230
+ stub.post('/upload/drive/v1/files?uploadType=multipart') do |env|
231
+ env.body.should be_a Faraday::CompositeReadIO
232
+ [200, {}, '{}']
233
+ end
234
+ end
235
+ drive = Google::APIClient::Service.new(
236
+ 'drive',
237
+ 'v1',
238
+ {
239
+ :application_name => APPLICATION_NAME,
240
+ :authenticated => false,
241
+ :connection => conn,
242
+ :cache_store => nil
243
+ }
244
+ )
245
+ req = drive.files.insert(:uploadType => 'multipart').body(@metadata).media(@media).execute()
246
+ conn.verify
247
+ end
248
+
249
+ describe 'with no connection' do
250
+ before do
251
+ @drive = Google::APIClient::Service.new('drive', 'v1',
252
+ {:application_name => APPLICATION_NAME, :cache_store => nil})
253
+ end
254
+
255
+ it 'should return a valid request with a body and media upload' do
256
+ req = @drive.files.insert(:uploadType => 'multipart').body(@metadata).media(@media)
257
+ req.should be_a(Google::APIClient::Service::Request)
258
+ req.method.id.should == 'drive.files.insert'
259
+ req.body.should == @metadata
260
+ req.media.should == @media
261
+ req.parameters.should_not be_nil
262
+ req.parameters[:uploadType].should == 'multipart'
263
+ end
264
+
265
+ it 'should return a valid request with a body and media upload when using resource name' do
266
+ req = @drive.files.insert(:uploadType => 'multipart').file(@metadata).media(@media)
267
+ req.should be_a(Google::APIClient::Service::Request)
268
+ req.method.id.should == 'drive.files.insert'
269
+ req.file.should == @metadata
270
+ req.media.should == @media
271
+ req.parameters.should_not be_nil
272
+ req.parameters[:uploadType].should == 'multipart'
273
+ end
274
+ end
275
+ end
276
+
277
+ describe 'with the Discovery API' do
278
+ it 'should make a valid end-to-end request' do
279
+ discovery = Google::APIClient::Service.new('discovery', 'v1',
280
+ {:application_name => APPLICATION_NAME, :authenticated => false,
281
+ :cache_store => nil})
282
+ result = discovery.apis.get_rest(:api => 'discovery', :version => 'v1').execute
283
+ result.should_not be_nil
284
+ result.data.name.should == 'discovery'
285
+ result.data.version.should == 'v1'
286
+ end
287
+ end
288
+ end
289
+
290
+
291
+ describe Google::APIClient::Service::Result do
292
+
293
+ describe 'with the plus API' do
294
+ before do
295
+ @plus = Google::APIClient::Service.new('plus', 'v1',
296
+ {:application_name => APPLICATION_NAME, :cache_store => nil})
297
+ @reference = Google::APIClient::Reference.new({
298
+ :api_method => @plus.activities.list.method,
299
+ :parameters => {
300
+ 'userId' => 'me',
301
+ 'collection' => 'public',
302
+ 'maxResults' => 20
303
+ }
304
+ })
305
+ @request = @plus.activities.list(:userId => 'me', :collection => 'public',
306
+ :maxResults => 20)
307
+
308
+ # Response double
309
+ @response = double("response")
310
+ @response.stub(:status).and_return(200)
311
+ @response.stub(:headers).and_return({
312
+ 'etag' => '12345',
313
+ 'x-google-apiary-auth-scopes' =>
314
+ 'https://www.googleapis.com/auth/plus.me',
315
+ 'content-type' => 'application/json; charset=UTF-8',
316
+ 'date' => 'Mon, 23 Apr 2012 00:00:00 GMT',
317
+ 'cache-control' => 'private, max-age=0, must-revalidate, no-transform',
318
+ 'server' => 'GSE',
319
+ 'connection' => 'close'
320
+ })
321
+ end
322
+
323
+ describe 'with a next page token' do
324
+ before do
325
+ @body = <<-END_OF_STRING
326
+ {
327
+ "kind": "plus#activityFeed",
328
+ "etag": "FOO",
329
+ "nextPageToken": "NEXT+PAGE+TOKEN",
330
+ "selfLink": "https://www.googleapis.com/plus/v1/people/foo/activities/public?",
331
+ "nextLink": "https://www.googleapis.com/plus/v1/people/foo/activities/public?maxResults=20&pageToken=NEXT%2BPAGE%2BTOKEN",
332
+ "title": "Plus Public Activity Feed for ",
333
+ "updated": "2012-04-23T00:00:00.000Z",
334
+ "id": "123456790",
335
+ "items": []
336
+ }
337
+ END_OF_STRING
338
+ @response.stub(:body).and_return(@body)
339
+ base_result = Google::APIClient::Result.new(@reference, @response)
340
+ @result = Google::APIClient::Service::Result.new(@request, base_result)
341
+ end
342
+
343
+ it 'should indicate a successful response' do
344
+ @result.error?.should be_false
345
+ end
346
+
347
+ it 'should return the correct next page token' do
348
+ @result.next_page_token.should == 'NEXT+PAGE+TOKEN'
349
+ end
350
+
351
+ it 'generate a correct request when calling next_page' do
352
+ next_page_request = @result.next_page
353
+ next_page_request.parameters.should include('pageToken')
354
+ next_page_request.parameters['pageToken'].should == 'NEXT+PAGE+TOKEN'
355
+ @request.parameters.each_pair do |param, value|
356
+ next_page_request.parameters[param].should == value
357
+ end
358
+ end
359
+
360
+ it 'should return content type correctly' do
361
+ @result.media_type.should == 'application/json'
362
+ end
363
+
364
+ it 'should return the body correctly' do
365
+ @result.body.should == @body
366
+ end
367
+
368
+ it 'should return the result data correctly' do
369
+ @result.data?.should be_true
370
+ @result.data.class.to_s.should ==
371
+ 'Google::APIClient::Schema::Plus::V1::ActivityFeed'
372
+ @result.data.kind.should == 'plus#activityFeed'
373
+ @result.data.etag.should == 'FOO'
374
+ @result.data.nextPageToken.should == 'NEXT+PAGE+TOKEN'
375
+ @result.data.selfLink.should ==
376
+ 'https://www.googleapis.com/plus/v1/people/foo/activities/public?'
377
+ @result.data.nextLink.should ==
378
+ 'https://www.googleapis.com/plus/v1/people/foo/activities/public?' +
379
+ 'maxResults=20&pageToken=NEXT%2BPAGE%2BTOKEN'
380
+ @result.data.title.should == 'Plus Public Activity Feed for '
381
+ @result.data.id.should == "123456790"
382
+ @result.data.items.should be_empty
383
+ end
384
+ end
385
+
386
+ describe 'without a next page token' do
387
+ before do
388
+ @body = <<-END_OF_STRING
389
+ {
390
+ "kind": "plus#activityFeed",
391
+ "etag": "FOO",
392
+ "selfLink": "https://www.googleapis.com/plus/v1/people/foo/activities/public?",
393
+ "title": "Plus Public Activity Feed for ",
394
+ "updated": "2012-04-23T00:00:00.000Z",
395
+ "id": "123456790",
396
+ "items": []
397
+ }
398
+ END_OF_STRING
399
+ @response.stub(:body).and_return(@body)
400
+ base_result = Google::APIClient::Result.new(@reference, @response)
401
+ @result = Google::APIClient::Service::Result.new(@request, base_result)
402
+ end
403
+
404
+ it 'should not return a next page token' do
405
+ @result.next_page_token.should == nil
406
+ end
407
+
408
+ it 'should return content type correctly' do
409
+ @result.media_type.should == 'application/json'
410
+ end
411
+
412
+ it 'should return the body correctly' do
413
+ @result.body.should == @body
414
+ end
415
+
416
+ it 'should return the result data correctly' do
417
+ @result.data?.should be_true
418
+ @result.data.class.to_s.should ==
419
+ 'Google::APIClient::Schema::Plus::V1::ActivityFeed'
420
+ @result.data.kind.should == 'plus#activityFeed'
421
+ @result.data.etag.should == 'FOO'
422
+ @result.data.selfLink.should ==
423
+ 'https://www.googleapis.com/plus/v1/people/foo/activities/public?'
424
+ @result.data.title.should == 'Plus Public Activity Feed for '
425
+ @result.data.id.should == "123456790"
426
+ @result.data.items.should be_empty
427
+ end
428
+ end
429
+
430
+ describe 'with JSON error response' do
431
+ before do
432
+ @body = <<-END_OF_STRING
433
+ {
434
+ "error": {
435
+ "errors": [
436
+ {
437
+ "domain": "global",
438
+ "reason": "parseError",
439
+ "message": "Parse Error"
440
+ }
441
+ ],
442
+ "code": 400,
443
+ "message": "Parse Error"
444
+ }
445
+ }
446
+ END_OF_STRING
447
+ @response.stub(:body).and_return(@body)
448
+ @response.stub(:status).and_return(400)
449
+ base_result = Google::APIClient::Result.new(@reference, @response)
450
+ @result = Google::APIClient::Service::Result.new(@request, base_result)
451
+ end
452
+
453
+ it 'should return error status correctly' do
454
+ @result.error?.should be_true
455
+ end
456
+
457
+ it 'should return the correct error message' do
458
+ @result.error_message.should == 'Parse Error'
459
+ end
460
+
461
+ it 'should return the body correctly' do
462
+ @result.body.should == @body
463
+ end
464
+ end
465
+
466
+ describe 'with 204 No Content response' do
467
+ before do
468
+ @response.stub(:body).and_return('')
469
+ @response.stub(:status).and_return(204)
470
+ @response.stub(:headers).and_return({})
471
+ base_result = Google::APIClient::Result.new(@reference, @response)
472
+ @result = Google::APIClient::Service::Result.new(@request, base_result)
473
+ end
474
+
475
+ it 'should indicate no data is available' do
476
+ @result.data?.should be_false
477
+ end
478
+
479
+ it 'should return nil for data' do
480
+ @result.data.should == nil
481
+ end
482
+
483
+ it 'should return nil for media_type' do
484
+ @result.media_type.should == nil
485
+ end
486
+ end
487
+ end
488
+ end
489
+
490
+ describe Google::APIClient::Service::BatchRequest do
491
+ describe 'with the discovery API' do
492
+ before do
493
+ @discovery = Google::APIClient::Service.new('discovery', 'v1',
494
+ {:application_name => APPLICATION_NAME, :authorization => nil,
495
+ :cache_store => nil})
496
+ end
497
+
498
+ describe 'with two valid requests' do
499
+ before do
500
+ @calls = [
501
+ @discovery.apis.get_rest(:api => 'plus', :version => 'v1'),
502
+ @discovery.apis.get_rest(:api => 'discovery', :version => 'v1')
503
+ ]
504
+ end
505
+
506
+ it 'should execute both when using a global callback' do
507
+ block_called = 0
508
+ batch = @discovery.batch(@calls) do |result|
509
+ block_called += 1
510
+ result.status.should == 200
511
+ end
512
+
513
+ batch.execute
514
+ block_called.should == 2
515
+ end
516
+
517
+ it 'should execute both when using individual callbacks' do
518
+ call1_returned, call2_returned = false, false
519
+ batch = @discovery.batch
520
+
521
+ batch.add(@calls[0]) do |result|
522
+ call1_returned = true
523
+ result.status.should == 200
524
+ result.call_index.should == 0
525
+ end
526
+
527
+ batch.add(@calls[1]) do |result|
528
+ call2_returned = true
529
+ result.status.should == 200
530
+ result.call_index.should == 1
531
+ end
532
+
533
+ batch.execute
534
+ call1_returned.should == true
535
+ call2_returned.should == true
536
+ end
537
+ end
538
+
539
+ describe 'with a valid request and an invalid one' do
540
+ before do
541
+ @calls = [
542
+ @discovery.apis.get_rest(:api => 'plus', :version => 'v1'),
543
+ @discovery.apis.get_rest(:api => 'invalid', :version => 'invalid')
544
+ ]
545
+ end
546
+
547
+ it 'should execute both when using a global callback' do
548
+ block_called = 0
549
+ batch = @discovery.batch(@calls) do |result|
550
+ block_called += 1
551
+ if result.call_index == 0
552
+ result.status.should == 200
553
+ else
554
+ result.status.should >= 400
555
+ result.status.should < 500
556
+ end
557
+ end
558
+
559
+ batch.execute
560
+ block_called.should == 2
561
+ end
562
+
563
+ it 'should execute both when using individual callbacks' do
564
+ call1_returned, call2_returned = false, false
565
+ batch = @discovery.batch
566
+
567
+ batch.add(@calls[0]) do |result|
568
+ call1_returned = true
569
+ result.status.should == 200
570
+ result.call_index.should == 0
571
+ end
572
+
573
+ batch.add(@calls[1]) do |result|
574
+ call2_returned = true
575
+ result.status.should >= 400
576
+ result.status.should < 500
577
+ result.call_index.should == 1
578
+ end
579
+
580
+ batch.execute
581
+ call1_returned.should == true
582
+ call2_returned.should == true
583
+ end
584
+ end
585
+ end
586
+ end