ghost_google-api-client 0.4.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGELOG.md +77 -0
  2. data/Gemfile +30 -0
  3. data/Gemfile.lock +80 -0
  4. data/LICENSE +202 -0
  5. data/README.md +71 -0
  6. data/Rakefile +42 -0
  7. data/bin/google-api +545 -0
  8. data/lib/compat/multi_json.rb +17 -0
  9. data/lib/google/api_client.rb +802 -0
  10. data/lib/google/api_client/batch.rb +296 -0
  11. data/lib/google/api_client/client_secrets.rb +106 -0
  12. data/lib/google/api_client/discovery.rb +19 -0
  13. data/lib/google/api_client/discovery/api.rb +287 -0
  14. data/lib/google/api_client/discovery/media.rb +77 -0
  15. data/lib/google/api_client/discovery/method.rb +369 -0
  16. data/lib/google/api_client/discovery/resource.rb +150 -0
  17. data/lib/google/api_client/discovery/schema.rb +119 -0
  18. data/lib/google/api_client/environment.rb +42 -0
  19. data/lib/google/api_client/errors.rb +49 -0
  20. data/lib/google/api_client/media.rb +172 -0
  21. data/lib/google/api_client/reference.rb +305 -0
  22. data/lib/google/api_client/result.rb +161 -0
  23. data/lib/google/api_client/service_account.rb +134 -0
  24. data/lib/google/api_client/version.rb +31 -0
  25. data/lib/google/inflection.rb +28 -0
  26. data/spec/fixtures/files/sample.txt +33 -0
  27. data/spec/google/api_client/batch_spec.rb +241 -0
  28. data/spec/google/api_client/discovery_spec.rb +670 -0
  29. data/spec/google/api_client/media_spec.rb +143 -0
  30. data/spec/google/api_client/result_spec.rb +185 -0
  31. data/spec/google/api_client/service_account_spec.rb +58 -0
  32. data/spec/google/api_client_spec.rb +139 -0
  33. data/spec/spec_helper.rb +7 -0
  34. data/tasks/gem.rake +97 -0
  35. data/tasks/git.rake +45 -0
  36. data/tasks/metrics.rake +22 -0
  37. data/tasks/spec.rake +57 -0
  38. data/tasks/wiki.rake +82 -0
  39. data/tasks/yard.rake +29 -0
  40. metadata +253 -0
@@ -0,0 +1,17 @@
1
+ gem 'multi_json', '>= 1.0.0'
2
+ require 'multi_json'
3
+
4
+ unless MultiJson.respond_to?(:load)
5
+ module MultiJson
6
+ class <<self
7
+ alias :load :decode
8
+ end
9
+ end
10
+ end
11
+ unless MultiJson.respond_to?(:dump)
12
+ module MultiJson
13
+ class <<self
14
+ alias :dump :encode
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,802 @@
1
+ # Copyright 2010 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
+
16
+ gem 'faraday', '~> 0.8.1'
17
+ require 'faraday'
18
+ require 'faraday/utils'
19
+ require 'multi_json'
20
+ require 'compat/multi_json'
21
+ require 'stringio'
22
+
23
+ require 'google/api_client/version'
24
+ require 'google/api_client/errors'
25
+ require 'google/api_client/environment'
26
+ require 'google/api_client/discovery'
27
+ require 'google/api_client/reference'
28
+ require 'google/api_client/result'
29
+ require 'google/api_client/media'
30
+ require 'google/api_client/service_account'
31
+ require 'google/api_client/batch'
32
+
33
+ module Google
34
+ # TODO(bobaman): Document all this stuff.
35
+
36
+
37
+ ##
38
+ # This class manages APIs communication.
39
+ class APIClient
40
+ ##
41
+ # Creates a new Google API client.
42
+ #
43
+ # @param [Hash] options The configuration parameters for the client.
44
+ # @option options [Symbol, #generate_authenticated_request] :authorization
45
+ # (:oauth_1)
46
+ # The authorization mechanism used by the client. The following
47
+ # mechanisms are supported out-of-the-box:
48
+ # <ul>
49
+ # <li><code>:two_legged_oauth_1</code></li>
50
+ # <li><code>:oauth_1</code></li>
51
+ # <li><code>:oauth_2</code></li>
52
+ # </ul>
53
+ # @option options [String] :application_name
54
+ # The name of the application using the client.
55
+ # @option options [String] :application_version
56
+ # The version number of the application using the client.
57
+ # @option options [String] :user_agent
58
+ # ("{app_name} google-api-ruby-client/{version} {os_name}/{os_version}")
59
+ # The user agent used by the client. Most developers will want to
60
+ # leave this value alone and use the `:application_name` option instead.
61
+ # @option options [String] :host ("www.googleapis.com")
62
+ # The API hostname used by the client. This rarely needs to be changed.
63
+ # @option options [String] :port (443)
64
+ # The port number used by the client. This rarely needs to be changed.
65
+ # @option options [String] :discovery_path ("/discovery/v1")
66
+ # The discovery base path. This rarely needs to be changed.
67
+ def initialize(options={})
68
+ # Normalize key to String to allow indifferent access.
69
+ options = options.inject({}) do |accu, (key, value)|
70
+ accu[key.to_s] = value
71
+ accu
72
+ end
73
+ # Almost all API usage will have a host of 'www.googleapis.com'.
74
+ self.host = options["host"] || 'www.googleapis.com'
75
+ self.port = options["port"] || 443
76
+ self.discovery_path = options["discovery_path"] || '/discovery/v1'
77
+
78
+ # Most developers will want to leave this value alone and use the
79
+ # application_name option.
80
+ application_string = (
81
+ options["application_name"] ? (
82
+ "#{options["application_name"]}/" +
83
+ "#{options["application_version"] || '0.0.0'}"
84
+ ) : ""
85
+ )
86
+ self.user_agent = options["user_agent"] || (
87
+ "#{application_string} " +
88
+ "google-api-ruby-client/#{VERSION::STRING} " +
89
+ ENV::OS_VERSION
90
+ ).strip
91
+ # The writer method understands a few Symbols and will generate useful
92
+ # default authentication mechanisms.
93
+ self.authorization =
94
+ options.key?("authorization") ? options["authorization"] : :oauth_2
95
+ self.key = options["key"]
96
+ self.user_ip = options["user_ip"]
97
+ @discovery_uris = {}
98
+ @discovery_documents = {}
99
+ @discovered_apis = {}
100
+ return self
101
+ end
102
+
103
+ ##
104
+ # Returns the authorization mechanism used by the client.
105
+ #
106
+ # @return [#generate_authenticated_request] The authorization mechanism.
107
+ attr_reader :authorization
108
+
109
+ ##
110
+ # Sets the authorization mechanism used by the client.
111
+ #
112
+ # @param [#generate_authenticated_request] new_authorization
113
+ # The new authorization mechanism.
114
+ def authorization=(new_authorization)
115
+ case new_authorization
116
+ when :oauth_1, :oauth
117
+ gem 'signet', '~> 0.4.0'
118
+ require 'signet/oauth_1/client'
119
+ # NOTE: Do not rely on this default value, as it may change
120
+ new_authorization = Signet::OAuth1::Client.new(
121
+ :temporary_credential_uri =>
122
+ 'https://www.google.com/accounts/OAuthGetRequestToken',
123
+ :authorization_uri =>
124
+ 'https://www.google.com/accounts/OAuthAuthorizeToken',
125
+ :token_credential_uri =>
126
+ 'https://www.google.com/accounts/OAuthGetAccessToken',
127
+ :client_credential_key => 'anonymous',
128
+ :client_credential_secret => 'anonymous'
129
+ )
130
+ when :two_legged_oauth_1, :two_legged_oauth
131
+ gem 'signet', '~> 0.4.0'
132
+ require 'signet/oauth_1/client'
133
+ # NOTE: Do not rely on this default value, as it may change
134
+ new_authorization = Signet::OAuth1::Client.new(
135
+ :client_credential_key => nil,
136
+ :client_credential_secret => nil,
137
+ :two_legged => true
138
+ )
139
+ when :oauth_2
140
+ gem 'signet', '~> 0.4.0'
141
+ require 'signet/oauth_2/client'
142
+ # NOTE: Do not rely on this default value, as it may change
143
+ new_authorization = Signet::OAuth2::Client.new(
144
+ :authorization_uri =>
145
+ 'https://accounts.google.com/o/oauth2/auth',
146
+ :token_credential_uri =>
147
+ 'https://accounts.google.com/o/oauth2/token'
148
+ )
149
+ when nil
150
+ # No authorization mechanism
151
+ else
152
+ if !new_authorization.respond_to?(:generate_authenticated_request)
153
+ raise TypeError,
154
+ 'Expected authorization mechanism to respond to ' +
155
+ '#generate_authenticated_request.'
156
+ end
157
+ end
158
+ @authorization = new_authorization
159
+ return @authorization
160
+ end
161
+
162
+ ##
163
+ # The application's API key issued by the API console.
164
+ #
165
+ # @return [String] The API key.
166
+ attr_accessor :key
167
+
168
+ ##
169
+ # The IP address of the user this request is being performed on behalf of.
170
+ #
171
+ # @return [String] The user's IP address.
172
+ attr_accessor :user_ip
173
+
174
+ ##
175
+ # The user agent used by the client.
176
+ #
177
+ # @return [String]
178
+ # The user agent string used in the User-Agent header.
179
+ attr_accessor :user_agent
180
+
181
+ ##
182
+ # The API hostname used by the client.
183
+ #
184
+ # @return [String]
185
+ # The API hostname. Should almost always be 'www.googleapis.com'.
186
+ attr_accessor :host
187
+
188
+ ##
189
+ # The port number used by the client.
190
+ #
191
+ # @return [String]
192
+ # The port number. Should almost always be 443.
193
+ attr_accessor :port
194
+
195
+ ##
196
+ # The base path used by the client for discovery.
197
+ #
198
+ # @return [String]
199
+ # The base path. Should almost always be '/discovery/v1'.
200
+ attr_accessor :discovery_path
201
+
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
+ ##
228
+ # Returns the URI for the directory document.
229
+ #
230
+ # @return [Addressable::URI] The URI of the directory document.
231
+ def directory_uri
232
+ return resolve_uri(self.discovery_path + '/apis')
233
+ end
234
+
235
+ ##
236
+ # Manually registers a URI as a discovery document for a specific version
237
+ # of an API.
238
+ #
239
+ # @param [String, Symbol] api The API name.
240
+ # @param [String] version The desired version of the API.
241
+ # @param [Addressable::URI] uri The URI of the discovery document.
242
+ def register_discovery_uri(api, version, uri)
243
+ api = api.to_s
244
+ version = version || 'v1'
245
+ @discovery_uris["#{api}:#{version}"] = uri
246
+ end
247
+
248
+ ##
249
+ # Returns the URI for the discovery document.
250
+ #
251
+ # @param [String, Symbol] api The API name.
252
+ # @param [String] version The desired version of the API.
253
+ # @return [Addressable::URI] The URI of the discovery document.
254
+ def discovery_uri(api, version=nil)
255
+ api = api.to_s
256
+ version = version || 'v1'
257
+ return @discovery_uris["#{api}:#{version}"] ||= (
258
+ resolve_uri(
259
+ self.discovery_path + '/apis/{api}/{version}/rest',
260
+ 'api' => api,
261
+ 'version' => version
262
+ )
263
+ )
264
+ end
265
+
266
+ ##
267
+ # Manually registers a pre-loaded discovery document for a specific version
268
+ # of an API.
269
+ #
270
+ # @param [String, Symbol] api The API name.
271
+ # @param [String] version The desired version of the API.
272
+ # @param [String, StringIO] discovery_document
273
+ # The contents of the discovery document.
274
+ def register_discovery_document(api, version, discovery_document)
275
+ api = api.to_s
276
+ version = version || 'v1'
277
+ if discovery_document.kind_of?(StringIO)
278
+ discovery_document.rewind
279
+ discovery_document = discovery_document.string
280
+ elsif discovery_document.respond_to?(:to_str)
281
+ discovery_document = discovery_document.to_str
282
+ else
283
+ raise TypeError,
284
+ "Expected String or StringIO, got #{discovery_document.class}."
285
+ end
286
+ @discovery_documents["#{api}:#{version}"] =
287
+ MultiJson.load(discovery_document)
288
+ end
289
+
290
+ ##
291
+ # Returns the parsed directory document.
292
+ #
293
+ # @return [Hash] The parsed JSON from the directory document.
294
+ def directory_document
295
+ return @directory_document ||= (begin
296
+ request = self.generate_request(
297
+ :http_method => :get,
298
+ :uri => self.directory_uri,
299
+ :authenticated => false
300
+ )
301
+ response = self.transmit(:request => request)
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
317
+ end)
318
+ end
319
+
320
+ ##
321
+ # Returns the parsed discovery document.
322
+ #
323
+ # @param [String, Symbol] api The API name.
324
+ # @param [String] version The desired version of the API.
325
+ # @return [Hash] The parsed JSON from the discovery document.
326
+ def discovery_document(api, version=nil)
327
+ api = api.to_s
328
+ version = version || 'v1'
329
+ return @discovery_documents["#{api}:#{version}"] ||= (begin
330
+ request = self.generate_request(
331
+ :http_method => :get,
332
+ :uri => self.discovery_uri(api, version),
333
+ :authenticated => false
334
+ )
335
+ response = self.transmit(:request => request)
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
351
+ end)
352
+ end
353
+
354
+ ##
355
+ # Returns all APIs published in the directory document.
356
+ #
357
+ # @return [Array] The list of available APIs.
358
+ def discovered_apis
359
+ @directory_apis ||= (begin
360
+ document_base = self.directory_uri
361
+ if self.directory_document && self.directory_document['items']
362
+ self.directory_document['items'].map do |discovery_document|
363
+ Google::APIClient::API.new(
364
+ document_base,
365
+ discovery_document
366
+ )
367
+ end
368
+ else
369
+ []
370
+ end
371
+ end)
372
+ end
373
+
374
+ ##
375
+ # Returns the service object for a given service name and service version.
376
+ #
377
+ # @param [String, Symbol] api The API name.
378
+ # @param [String] version The desired version of the API.
379
+ #
380
+ # @return [Google::APIClient::API] The service object.
381
+ def discovered_api(api, version=nil)
382
+ if !api.kind_of?(String) && !api.kind_of?(Symbol)
383
+ raise TypeError,
384
+ "Expected String or Symbol, got #{api.class}."
385
+ end
386
+ api = api.to_s
387
+ version = version || 'v1'
388
+ return @discovered_apis["#{api}:#{version}"] ||= begin
389
+ document_base = self.discovery_uri(api, version)
390
+ discovery_document = self.discovery_document(api, version)
391
+ if document_base && discovery_document
392
+ Google::APIClient::API.new(
393
+ document_base,
394
+ discovery_document
395
+ )
396
+ else
397
+ nil
398
+ end
399
+ end
400
+ end
401
+
402
+ ##
403
+ # Returns the method object for a given RPC name and service version.
404
+ #
405
+ # @param [String, Symbol] rpc_name The RPC name of the desired method.
406
+ # @param [String, Symbol] rpc_name The API the method is within.
407
+ # @param [String] version The desired version of the API.
408
+ #
409
+ # @return [Google::APIClient::Method] The method object.
410
+ def discovered_method(rpc_name, api, version=nil)
411
+ if !rpc_name.kind_of?(String) && !rpc_name.kind_of?(Symbol)
412
+ raise TypeError,
413
+ "Expected String or Symbol, got #{rpc_name.class}."
414
+ end
415
+ rpc_name = rpc_name.to_s
416
+ api = api.to_s
417
+ version = version || 'v1'
418
+ service = self.discovered_api(api, version)
419
+ if service.to_h[rpc_name]
420
+ return service.to_h[rpc_name]
421
+ else
422
+ return nil
423
+ end
424
+ end
425
+
426
+ ##
427
+ # Returns the service object with the highest version number.
428
+ #
429
+ # @note <em>Warning</em>: This method should be used with great care.
430
+ # As APIs are updated, minor differences between versions may cause
431
+ # incompatibilities. Requesting a specific version will avoid this issue.
432
+ #
433
+ # @param [String, Symbol] api The name of the service.
434
+ #
435
+ # @return [Google::APIClient::API] The service object.
436
+ def preferred_version(api)
437
+ if !api.kind_of?(String) && !api.kind_of?(Symbol)
438
+ raise TypeError,
439
+ "Expected String or Symbol, got #{api.class}."
440
+ end
441
+ api = api.to_s
442
+ return self.discovered_apis.detect do |a|
443
+ a.name == api && a.preferred == true
444
+ end
445
+ end
446
+
447
+ ##
448
+ # Verifies an ID token against a server certificate. Used to ensure that
449
+ # an ID token supplied by an untrusted client-side mechanism is valid.
450
+ # Raises an error if the token is invalid or missing.
451
+ def verify_id_token!
452
+ gem 'jwt', '~> 0.1.4'
453
+ require 'jwt'
454
+ require 'openssl'
455
+ @certificates ||= {}
456
+ if !self.authorization.respond_to?(:id_token)
457
+ raise ArgumentError, (
458
+ "Current authorization mechanism does not support ID tokens: " +
459
+ "#{self.authorization.class.to_s}"
460
+ )
461
+ elsif !self.authorization.id_token
462
+ raise ArgumentError, (
463
+ "Could not verify ID token, ID token missing. " +
464
+ "Scopes were: #{self.authorization.scope.inspect}"
465
+ )
466
+ else
467
+ check_cached_certs = lambda do
468
+ valid = false
469
+ for key, cert in @certificates
470
+ begin
471
+ self.authorization.decoded_id_token(cert.public_key)
472
+ valid = true
473
+ rescue JWT::DecodeError, Signet::UnsafeOperationError
474
+ # Expected exception. Ignore, ID token has not been validated.
475
+ end
476
+ end
477
+ valid
478
+ end
479
+ if check_cached_certs.call()
480
+ return true
481
+ end
482
+ request = self.generate_request(
483
+ :http_method => :get,
484
+ :uri => 'https://www.googleapis.com/oauth2/v1/certs',
485
+ :authenticated => false
486
+ )
487
+ response = self.transmit(:request => request)
488
+ if response.status >= 200 && response.status < 300
489
+ @certificates.merge!(
490
+ Hash[MultiJson.load(response.body).map do |key, cert|
491
+ [key, OpenSSL::X509::Certificate.new(cert)]
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
507
+ if check_cached_certs.call()
508
+ return true
509
+ else
510
+ raise InvalidIDTokenError,
511
+ "Could not verify ID token against any available certificate."
512
+ end
513
+ end
514
+ return nil
515
+ end
516
+
517
+ ##
518
+ # Generates a request.
519
+ #
520
+ # @option options [Google::APIClient::Method, String] :api_method
521
+ # The method object or the RPC name of the method being executed.
522
+ # @option options [Hash, Array] :parameters
523
+ # The parameters to send to the method.
524
+ # @option options [Hash, Array] :headers The HTTP headers for the request.
525
+ # @option options [String] :body The body of the request.
526
+ # @option options [String] :version ("v1")
527
+ # The service version. Only used if `api_method` is a `String`.
528
+ # @option options [#generate_authenticated_request] :authorization
529
+ # The authorization mechanism for the response. Used only if
530
+ # `:authenticated` is `true`.
531
+ # @option options [TrueClass, FalseClass] :authenticated (true)
532
+ # `true` if the request must be signed or somehow
533
+ # authenticated, `false` otherwise.
534
+ #
535
+ # @return [Faraday::Request] The generated request.
536
+ #
537
+ # @example
538
+ # request = client.generate_request(
539
+ # :api_method => 'plus.activities.list',
540
+ # :parameters =>
541
+ # {'collection' => 'public', 'userId' => 'me'}
542
+ # )
543
+ def generate_request(options={})
544
+ # Note: The merge method on a Hash object will coerce an API Reference
545
+ # object into a Hash and merge with the default options.
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
553
+ }.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
669
+ end
670
+
671
+ ##
672
+ # Executes a request, wrapping it in a Result object.
673
+ #
674
+ # @param [Google::APIClient::BatchRequest, Hash, Array] params
675
+ # Either a Google::APIClient::BatchRequest, a Hash, or an Array.
676
+ #
677
+ # If a Google::APIClient::BatchRequest, no other parameters are expected.
678
+ #
679
+ # If a Hash, the below parameters are handled. If an Array, the
680
+ # parameters are assumed to be in the below order:
681
+ #
682
+ # - (Google::APIClient::Method, String) api_method:
683
+ # The method object or the RPC name of the method being executed.
684
+ # - (Hash, Array) parameters:
685
+ # The parameters to send to the method.
686
+ # - (String) body: The body of the request.
687
+ # - (Hash, Array) headers: The HTTP headers for the request.
688
+ # - (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
+ # - (#generate_authenticated_request) :authorization (default: true) -
692
+ # The authorization mechanism for the response. Used only if
693
+ # `:authenticated` is `true`.
694
+ # - (TrueClass, FalseClass) :authenticated (default: true) -
695
+ # `true` if the request must be signed or somehow
696
+ # authenticated, `false` otherwise.
697
+ #
698
+ # @return [Google::APIClient::Result] The result from the API, nil if batch.
699
+ #
700
+ # @example
701
+ # result = client.execute(batch_request)
702
+ #
703
+ # @example
704
+ # result = client.execute(
705
+ # :api_method => 'plus.activities.list',
706
+ # :parameters => {'collection' => 'public', 'userId' => 'me'}
707
+ # )
708
+ #
709
+ # @see Google::APIClient#generate_request
710
+ def execute(*params)
711
+ if params.last.kind_of?(Google::APIClient::BatchRequest) &&
712
+ params.size == 1
713
+ batch = params.pop
714
+ options = batch.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
748
+ else
749
+ # This block of code allows us to accept multiple parameter passing
750
+ # styles, and maintaining some backwards compatibility.
751
+ #
752
+ # Note: I'm extremely tempted to deprecate this style of execute call.
753
+ if params.last.respond_to?(:to_hash) && params.size == 1
754
+ options = params.pop
755
+ else
756
+ options = {}
757
+ end
758
+
759
+ options[:api_method] = params.shift if params.size > 0
760
+ options[:parameters] = params.shift if params.size > 0
761
+ options[:body] = params.shift if params.size > 0
762
+ options[:headers] = params.shift if params.size > 0
763
+ options[:client] = self
764
+ options[:connection] ||= Faraday.default_connection
765
+ reference = Google::APIClient::Reference.new(options)
766
+ request = self.generate_request(reference)
767
+ response = self.transmit(
768
+ :request => request,
769
+ :connection => options[:connection]
770
+ )
771
+ return Google::APIClient::Result.new(reference, request, response)
772
+ end
773
+ end
774
+
775
+ ##
776
+ # Same as Google::APIClient#execute, but raises an exception if there was
777
+ # an error.
778
+ #
779
+ # @see Google::APIClient#execute
780
+ def execute!(*params)
781
+ result = self.execute(*params)
782
+ if result.error?
783
+ error_message = result.error_message
784
+ case result.response.status
785
+ when 400...500
786
+ exception_type = ClientError
787
+ error_message ||= "A client error has occurred."
788
+ when 500...600
789
+ exception_type = ServerError
790
+ error_message ||= "A server error has occurred."
791
+ else
792
+ exception_type = TransmissionError
793
+ error_message ||= "A transmission error has occurred."
794
+ end
795
+ raise exception_type, error_message
796
+ end
797
+ return result
798
+ end
799
+ end
800
+ end
801
+
802
+ require 'google/api_client/version'