Unify processing of api/resumable/batch requests
[arvados.git] / spec / google / api_client / discovery_spec.rb
1 # encoding:utf-8
2
3 # Copyright 2010 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
18 require 'spec_helper'
19
20 require 'faraday'
21 require 'faraday/utils'
22 require 'multi_json'
23 require 'compat/multi_json'
24 require 'signet/oauth_1/client'
25 require 'google/api_client'
26 require 'google/api_client/version'
27
28 describe Google::APIClient do
29   CLIENT = Google::APIClient.new unless defined?(CLIENT)
30
31   after do
32     # Reset client to not-quite-pristine state
33     CLIENT.key = nil
34     CLIENT.user_ip = nil
35   end
36
37   it 'should raise a type error for bogus authorization' do
38     (lambda do
39       Google::APIClient.new(:authorization => 42)
40     end).should raise_error(TypeError)
41   end
42
43   it 'should not be able to retrieve the discovery document for a bogus API' do
44     (lambda do
45       CLIENT.discovery_document('bogus')
46     end).should raise_error(Google::APIClient::TransmissionError)
47     (lambda do
48       CLIENT.discovered_api('bogus')
49     end).should raise_error(Google::APIClient::TransmissionError)
50   end
51
52   it 'should raise an error for bogus services' do
53     (lambda do
54       CLIENT.discovered_api(42)
55     end).should raise_error(TypeError)
56   end
57
58   it 'should raise an error for bogus services' do
59     (lambda do
60       CLIENT.preferred_version(42)
61     end).should raise_error(TypeError)
62   end
63
64   it 'should raise an error for bogus methods' do
65     (lambda do
66       CLIENT.generate_request(42)
67     end).should raise_error(TypeError)
68   end
69
70   it 'should not return a preferred version for bogus service names' do
71     CLIENT.preferred_version('bogus').should == nil
72   end
73
74   describe 'with the prediction API' do
75     before do
76       CLIENT.authorization = nil
77       # The prediction API no longer exposes a v1, so we have to be
78       # careful about looking up the wrong API version.
79       @prediction = CLIENT.discovered_api('prediction', 'v1.2')
80     end
81
82     it 'should correctly determine the discovery URI' do
83       CLIENT.discovery_uri('prediction').should ===
84         'https://www.googleapis.com/discovery/v1/apis/prediction/v1/rest'
85     end
86
87     it 'should correctly determine the discovery URI if :user_ip is set' do
88       CLIENT.user_ip = '127.0.0.1'
89       request = CLIENT.generate_request(
90         :http_method => 'GET',
91         :uri => CLIENT.discovery_uri('prediction', 'v1.2'),
92         :authenticated => false
93       )
94       request.to_http_request.to_env(Faraday.default_connection)[:url].to_s.should === (
95         'https://www.googleapis.com/discovery/v1/apis/prediction/v1.2/rest' +
96         '?userIp=127.0.0.1'
97       )
98     end
99
100     it 'should correctly determine the discovery URI if :key is set' do
101       CLIENT.key = 'qwerty'
102       request = CLIENT.generate_request(
103         :http_method => 'GET',
104         :uri => CLIENT.discovery_uri('prediction', 'v1.2'),
105         :authenticated => false
106       )
107       request.to_http_request.to_env(Faraday.default_connection)[:url].to_s.should === (
108         'https://www.googleapis.com/discovery/v1/apis/prediction/v1.2/rest' +
109         '?key=qwerty'
110       )
111     end
112
113     it 'should correctly determine the discovery URI if both are set' do
114       CLIENT.key = 'qwerty'
115       CLIENT.user_ip = '127.0.0.1'
116       request = CLIENT.generate_request(
117         :http_method => 'GET',
118         :uri => CLIENT.discovery_uri('prediction', 'v1.2'),
119         :authenticated => false
120       )
121       Addressable::URI.parse(
122         request.to_http_request.to_env(Faraday.default_connection)[:url]
123       ).query_values.should == {
124         'key' => 'qwerty',
125         'userIp' => '127.0.0.1'
126       }
127     end
128
129     it 'should correctly generate API objects' do
130       CLIENT.discovered_api('prediction', 'v1.2').name.should == 'prediction'
131       CLIENT.discovered_api('prediction', 'v1.2').version.should == 'v1.2'
132       CLIENT.discovered_api(:prediction, 'v1.2').name.should == 'prediction'
133       CLIENT.discovered_api(:prediction, 'v1.2').version.should == 'v1.2'
134     end
135
136     it 'should discover methods' do
137       CLIENT.discovered_method(
138         'prediction.training.insert', 'prediction', 'v1.2'
139       ).name.should == 'insert'
140       CLIENT.discovered_method(
141         :'prediction.training.insert', :prediction, 'v1.2'
142       ).name.should == 'insert'
143       CLIENT.discovered_method(
144         'prediction.training.delete', 'prediction', 'v1.2'
145       ).name.should == 'delete'
146     end
147
148     it 'should define the origin API in discovered methods' do
149       CLIENT.discovered_method(
150         'prediction.training.insert', 'prediction', 'v1.2'
151       ).api.name.should == 'prediction'
152     end
153
154     it 'should not find methods that are not in the discovery document' do
155       CLIENT.discovered_method(
156         'prediction.bogus', 'prediction', 'v1.2'
157       ).should == nil
158     end
159
160     it 'should raise an error for bogus methods' do
161       (lambda do
162         CLIENT.discovered_method(42, 'prediction', 'v1.2')
163       end).should raise_error(TypeError)
164     end
165
166     it 'should raise an error for bogus methods' do
167       (lambda do
168         CLIENT.generate_request(CLIENT.discovered_api('prediction', 'v1.2'))
169       end).should raise_error(TypeError)
170     end
171
172     it 'should correctly determine the preferred version' do
173       CLIENT.preferred_version('prediction').version.should_not == 'v1'
174       CLIENT.preferred_version(:prediction).version.should_not == 'v1'
175     end
176
177     it 'should return a batch path' do
178       CLIENT.discovered_api('prediction', 'v1.2').batch_path.should_not be_nil
179     end
180
181     it 'should generate valid requests' do
182       request = CLIENT.generate_request(
183         :api_method => @prediction.training.insert,
184         :parameters => {'data' => '12345'}
185       )
186       request.to_http_request.method.should == :post
187       request.to_http_request.to_env(Faraday.default_connection)[:url].to_s.should ===
188         'https://www.googleapis.com/prediction/v1.2/training?data=12345'
189       request.headers.should be_empty
190       request.body.should == ''
191     end
192
193     it 'should generate valid requests when repeated parameters are passed' do
194       pending("This is caused by Faraday's encoding of query parameters.")
195       request = CLIENT.generate_request(
196         :api_method => @prediction.training.insert,
197         :parameters => [['data', '1'], ['data','2']]
198       )
199       request.to_http_request.method.should == :post
200       request.to_http_request.to_env(Faraday.default_connection)[:url].to_s.should ===
201         'https://www.googleapis.com/prediction/v1.2/training?data=1&data=2'
202     end
203
204     it 'should generate requests against the correct URIs' do
205       request = CLIENT.generate_request(
206         :api_method => @prediction.training.insert,
207         :parameters => {'data' => '12345'}
208       )
209       request.to_http_request.to_env(Faraday.default_connection)[:url].to_s.should ===
210         'https://www.googleapis.com/prediction/v1.2/training?data=12345'
211     end
212
213     it 'should generate requests against the correct URIs' do
214       request = CLIENT.generate_request(
215         :api_method => @prediction.training.insert,
216         :parameters => {'data' => '12345'}
217       )
218       request.to_http_request.to_env(Faraday.default_connection)[:url].to_s.should ===
219         'https://www.googleapis.com/prediction/v1.2/training?data=12345'
220     end
221
222     it 'should allow modification to the base URIs for testing purposes' do
223       # Using a new client instance here to avoid caching rebased discovery doc
224       prediction_rebase =
225         Google::APIClient.new.discovered_api('prediction', 'v1.2')
226       prediction_rebase.method_base =
227         'https://testing-domain.example.com/prediction/v1.2/'
228       request = CLIENT.generate_request(
229         :api_method => prediction_rebase.training.insert,
230         :parameters => {'data' => '123'}
231       )
232       request.to_http_request.to_env(Faraday.default_connection)[:url].to_s.should === (
233         'https://testing-domain.example.com/' +
234         'prediction/v1.2/training?data=123'
235       )
236     end
237
238     it 'should generate OAuth 1 requests' do
239       CLIENT.authorization = :oauth_1
240       CLIENT.authorization.token_credential_key = '12345'
241       CLIENT.authorization.token_credential_secret = '12345'
242       request = CLIENT.generate_request(
243         :api_method => @prediction.training.insert,
244         :parameters => {'data' => '12345'}
245       )
246       http_request = request.to_http_request
247       http_request.headers.should have_key('Authorization')
248       http_request.headers['Authorization'].should =~ /^OAuth/
249     end
250
251     it 'should generate OAuth 2 requests' do
252       CLIENT.authorization = :oauth_2
253       CLIENT.authorization.access_token = '12345'
254       request = CLIENT.generate_request(
255         :api_method => @prediction.training.insert,
256         :parameters => {'data' => '12345'}
257       )
258       http_request = request.to_http_request
259       http_request.headers.should have_key('Authorization')
260       http_request.headers['Authorization'].should =~ /^Bearer/
261     end
262
263     it 'should not be able to execute improperly authorized requests' do
264       CLIENT.authorization = :oauth_1
265       CLIENT.authorization.token_credential_key = '12345'
266       CLIENT.authorization.token_credential_secret = '12345'
267       result = CLIENT.execute(
268         @prediction.training.insert,
269         {'data' => '12345'}
270       )
271       result.response.status.should == 401
272     end
273
274     it 'should not be able to execute improperly authorized requests' do
275       CLIENT.authorization = :oauth_2
276       CLIENT.authorization.access_token = '12345'
277       result = CLIENT.execute(
278         @prediction.training.insert,
279         {'data' => '12345'}
280       )
281       result.response.status.should == 401
282     end
283
284     it 'should not be able to execute improperly authorized requests' do
285       (lambda do
286         CLIENT.authorization = :oauth_1
287         CLIENT.authorization.token_credential_key = '12345'
288         CLIENT.authorization.token_credential_secret = '12345'
289         result = CLIENT.execute!(
290           @prediction.training.insert,
291           {'data' => '12345'}
292         )
293       end).should raise_error(Google::APIClient::ClientError)
294     end
295
296     it 'should not be able to execute improperly authorized requests' do
297       (lambda do
298         CLIENT.authorization = :oauth_2
299         CLIENT.authorization.access_token = '12345'
300         result = CLIENT.execute!(
301           @prediction.training.insert,
302           {'data' => '12345'}
303         )
304       end).should raise_error(Google::APIClient::ClientError)
305     end
306
307     it 'should correctly handle unnamed parameters' do
308       CLIENT.authorization = :oauth_2
309       CLIENT.authorization.access_token = '12345'
310       result = CLIENT.execute(
311         @prediction.training.insert,
312         {},
313         MultiJson.dump({"id" => "bucket/object"}),
314         {'Content-Type' => 'application/json'}
315       )
316       result.reference.to_http_request.headers['Content-Type'].should == 'application/json'
317     end
318   end
319
320   describe 'with the plus API' do
321     before do
322       CLIENT.authorization = nil
323       @plus = CLIENT.discovered_api('plus')
324     end
325
326     it 'should correctly determine the discovery URI' do
327       CLIENT.discovery_uri('plus').should ===
328         'https://www.googleapis.com/discovery/v1/apis/plus/v1/rest'
329     end
330
331     it 'should find APIs that are in the discovery document' do
332       CLIENT.discovered_api('plus').name.should == 'plus'
333       CLIENT.discovered_api('plus').version.should == 'v1'
334       CLIENT.discovered_api(:plus).name.should == 'plus'
335       CLIENT.discovered_api(:plus).version.should == 'v1'
336     end
337
338     it 'should find methods that are in the discovery document' do
339       # TODO(bobaman) Fix this when the RPC names are correct
340       CLIENT.discovered_method(
341         'plus.activities.list', 'plus'
342       ).name.should == 'list'
343     end
344
345     it 'should define the origin API in discovered methods' do
346       CLIENT.discovered_method(
347         'plus.activities.list', 'plus'
348       ).api.name.should == 'plus'
349     end
350
351     it 'should not find methods that are not in the discovery document' do
352       CLIENT.discovered_method('plus.bogus', 'plus').should == nil
353     end
354
355     it 'should generate requests against the correct URIs' do
356       request = CLIENT.generate_request(
357         :api_method => @plus.activities.list,
358         :parameters => {
359           'userId' => '107807692475771887386', 'collection' => 'public'
360         },
361         :authenticated => false
362       )
363       request.to_http_request.to_env(Faraday.default_connection)[:url].to_s.should === (
364         'https://www.googleapis.com/plus/v1/' +
365         'people/107807692475771887386/activities/public'
366       )
367     end
368
369     it 'should correctly validate parameters' do
370       (lambda do
371         CLIENT.generate_request(
372           :api_method => @plus.activities.list,
373           :parameters => {'alt' => 'json'},
374           :authenticated => false
375         ).to_http_request
376       end).should raise_error(ArgumentError)
377     end
378
379     it 'should correctly validate parameters' do
380       (lambda do
381         CLIENT.generate_request(
382           :api_method => @plus.activities.list,
383           :parameters => {
384             'userId' => '107807692475771887386', 'collection' => 'bogus'
385           },
386           :authenticated => false
387         ).to_http_request
388       end).should raise_error(ArgumentError)
389     end
390   end
391   
392 =begin
393   describe 'with the latitude API' do
394     before do
395       CLIENT.authorization = nil
396       @latitude = CLIENT.discovered_api('latitude')
397     end
398
399     it 'should correctly determine the discovery URI' do
400       CLIENT.discovery_uri('latitude').should ===
401         'https://www.googleapis.com/discovery/v1/apis/latitude/v1/rest'
402     end
403
404     it 'should find APIs that are in the discovery document' do
405       CLIENT.discovered_api('latitude').name.should == 'latitude'
406       CLIENT.discovered_api('latitude').version.should == 'v1'
407     end
408
409     it 'should return a batch path' do
410       CLIENT.discovered_api('latitude').batch_path.should_not be_nil
411     end
412
413     it 'should find methods that are in the discovery document' do
414       CLIENT.discovered_method(
415         'latitude.currentLocation.get', 'latitude'
416       ).name.should == 'get'
417     end
418
419     it 'should define the origin API in discovered methods' do
420       CLIENT.discovered_method(
421         'latitude.currentLocation.get', 'latitude'
422       ).api.name.should == 'latitude'
423     end
424
425     it 'should not find methods that are not in the discovery document' do
426       CLIENT.discovered_method('latitude.bogus', 'latitude').should == nil
427     end
428
429     it 'should generate requests against the correct URIs' do
430       request = CLIENT.generate_request(
431         :api_method => 'latitude.currentLocation.get',
432         :authenticated => false
433       )
434       request.to_http_request.to_env(Faraday.default_connection)[:url].to_s.should ===
435         'https://www.googleapis.com/latitude/v1/currentLocation'
436     end
437
438     it 'should generate requests against the correct URIs' do
439       request = CLIENT.generate_request(
440         :api_method => @latitude.current_location.get,
441         :authenticated => false
442       )
443       request.to_http_request.to_env(Faraday.default_connection)[:url].to_s.should ===
444         'https://www.googleapis.com/latitude/v1/currentLocation'
445     end
446
447     it 'should not be able to execute requests without authorization' do
448       result = CLIENT.execute(
449         :api_method => 'latitude.currentLocation.get',
450         :authenticated => false
451       )
452       result.response.status.should == 401
453     end
454   end
455 =end
456
457   describe 'with the moderator API' do
458     before do
459       CLIENT.authorization = nil
460       @moderator = CLIENT.discovered_api('moderator')
461     end
462
463     it 'should correctly determine the discovery URI' do
464       CLIENT.discovery_uri('moderator').should ===
465         'https://www.googleapis.com/discovery/v1/apis/moderator/v1/rest'
466     end
467
468     it 'should find APIs that are in the discovery document' do
469       CLIENT.discovered_api('moderator').name.should == 'moderator'
470       CLIENT.discovered_api('moderator').version.should == 'v1'
471     end
472
473     it 'should find methods that are in the discovery document' do
474       CLIENT.discovered_method(
475         'moderator.profiles.get', 'moderator'
476       ).name.should == 'get'
477     end
478
479     it 'should define the origin API in discovered methods' do
480       CLIENT.discovered_method(
481         'moderator.profiles.get', 'moderator'
482       ).api.name.should == 'moderator'
483     end
484
485     it 'should not find methods that are not in the discovery document' do
486       CLIENT.discovered_method('moderator.bogus', 'moderator').should == nil
487     end
488
489     it 'should return a batch path' do
490       CLIENT.discovered_api('moderator').batch_path.should_not be_nil
491     end
492
493     it 'should generate requests against the correct URIs' do
494       request = CLIENT.generate_request(
495         :api_method => 'moderator.profiles.get',
496         :authenticated => false
497       )
498       request.to_http_request.to_env(Faraday.default_connection)[:url].to_s.should ===
499         'https://www.googleapis.com/moderator/v1/profiles/@me'
500     end
501
502     it 'should generate requests against the correct URIs' do
503       request = CLIENT.generate_request(
504         :api_method => @moderator.profiles.get,
505         :authenticated => false
506       )
507       request.to_http_request.to_env(Faraday.default_connection)[:url].to_s.should ===
508         'https://www.googleapis.com/moderator/v1/profiles/@me'
509     end
510
511     it 'should not be able to execute requests without authorization' do
512       result = CLIENT.execute(
513         'moderator.profiles.get',
514         {},
515         '',
516         [],
517         {:authenticated => false}
518       )
519       result.response.status.should == 401
520     end
521   end
522
523   describe 'with the adsense API' do
524     before do
525       CLIENT.authorization = nil
526       @adsense = CLIENT.discovered_api('adsense', 'v1')
527     end
528
529     it 'should correctly determine the discovery URI' do
530       CLIENT.discovery_uri('adsense').should ===
531         'https://www.googleapis.com/discovery/v1/apis/adsense/v1/rest'
532     end
533
534     it 'should find APIs that are in the discovery document' do
535       CLIENT.discovered_api('adsense').name.should == 'adsense'
536       CLIENT.discovered_api('adsense').version.should == 'v1'
537     end
538
539     it 'should return a batch path' do
540       CLIENT.discovered_api('adsense').batch_path.should_not be_nil
541     end
542
543     it 'should find methods that are in the discovery document' do
544       CLIENT.discovered_method(
545         'adsense.reports.generate', 'adsense'
546       ).name.should == 'generate'
547     end
548
549     it 'should not find methods that are not in the discovery document' do
550       CLIENT.discovered_method('adsense.bogus', 'adsense').should == nil
551     end
552
553     it 'should generate requests against the correct URIs' do
554       request = CLIENT.generate_request(
555         :api_method => 'adsense.adclients.list',
556         :authenticated => false
557       )
558       request.to_http_request.to_env(Faraday.default_connection)[:url].to_s.should ===
559         'https://www.googleapis.com/adsense/v1/adclients'
560     end
561
562     it 'should generate requests against the correct URIs' do
563       request = CLIENT.generate_request(
564         :api_method => @adsense.adclients.list,
565         :authenticated => false
566       )
567       request.to_http_request.to_env(Faraday.default_connection)[:url].to_s.should ===
568         'https://www.googleapis.com/adsense/v1/adclients'
569     end
570
571     it 'should not be able to execute requests without authorization' do
572       result = CLIENT.execute(
573         :api_method => 'adsense.adclients.list',
574         :authenticated => false
575       )
576       result.response.status.should == 401
577     end
578
579     it 'should fail when validating missing required parameters' do
580       (lambda do
581         CLIENT.generate_request(
582           :api_method => @adsense.reports.generate,
583           :authenticated => false
584         ).to_http_request
585       end).should raise_error(ArgumentError)
586     end
587
588     it 'should succeed when validating parameters in a correct call' do
589       (lambda do
590         CLIENT.generate_request(
591           :api_method => @adsense.reports.generate,
592           :parameters => {
593             'startDate' => '2000-01-01',
594             'endDate' => '2010-01-01',
595             'dimension' => 'DATE',
596             'metric' => 'PAGE_VIEWS'
597           },
598           :authenticated => false
599         ).to_http_request
600       end).should_not raise_error
601     end
602
603     it 'should fail when validating parameters with invalid values' do
604       (lambda do
605         CLIENT.generate_request(
606           :api_method => @adsense.reports.generate,
607           :parameters => {
608             'startDate' => '2000-01-01',
609             'endDate' => '2010-01-01',
610             'dimension' => 'BAD_CHARACTERS=-&*(£&',
611             'metric' => 'PAGE_VIEWS'
612           },
613           :authenticated => false
614         ).to_http_request
615       end).should raise_error(ArgumentError)
616     end
617
618     it 'should succeed when validating repeated parameters in a correct call' do
619       (lambda do
620         CLIENT.generate_request(
621           :api_method => @adsense.reports.generate,
622           :parameters => {
623             'startDate' => '2000-01-01',
624             'endDate' => '2010-01-01',
625             'dimension' => ['DATE', 'PRODUCT_CODE'],
626             'metric' => ['PAGE_VIEWS', 'CLICKS']
627           },
628           :authenticated => false
629         )
630       end).should_not raise_error
631     end
632
633     it 'should fail when validating incorrect repeated parameters' do
634       (lambda do
635         CLIENT.generate_request(
636           :api_method => @adsense.reports.generate,
637           :parameters => {
638             'startDate' => '2000-01-01',
639             'endDate' => '2010-01-01',
640             'dimension' => ['DATE', 'BAD_CHARACTERS=-&*(£&'],
641             'metric' => ['PAGE_VIEWS', 'CLICKS']
642           },
643           :authenticated => false
644         ).to_http_request
645       end).should raise_error(ArgumentError)
646     end
647   end
648
649   describe 'with the Drive API' do
650     before do
651       CLIENT.authorization = nil
652       @drive = CLIENT.discovered_api('drive', 'v1')
653     end
654
655     it 'should include media upload info methods' do
656       @drive.files.insert.media_upload.should_not == nil
657     end
658
659     it 'should include accepted media types' do
660       @drive.files.insert.media_upload.accepted_types.should_not be_empty
661     end
662
663     it 'should have an upload path' do
664       @drive.files.insert.media_upload.uri_template.should_not == nil
665     end
666
667     it 'should have a max file size' do
668       @drive.files.insert.media_upload.max_size.should_not == nil
669     end
670   end
671 end