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