jomz-google-api-client 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,16 @@
1
+ require 'multi_json'
2
+
3
+ if !MultiJson.respond_to?(:load)
4
+ module MultiJson
5
+ class <<self
6
+ alias :load :decode
7
+ end
8
+ end
9
+ end
10
+ if !MultiJson.respond_to?(:dump)
11
+ module MultiJson
12
+ class <<self
13
+ alias :dump :encode
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,672 @@
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
+ require 'faraday'
17
+ require 'multi_json'
18
+ require 'compat/multi_json'
19
+ require 'stringio'
20
+ require 'retriable'
21
+
22
+ require 'google/api_client/version'
23
+ require 'google/api_client/logging'
24
+ require 'google/api_client/errors'
25
+ require 'google/api_client/environment'
26
+ require 'google/api_client/discovery'
27
+ require 'google/api_client/request'
28
+ require 'google/api_client/reference'
29
+ require 'google/api_client/result'
30
+ require 'google/api_client/media'
31
+ require 'google/api_client/service_account'
32
+ require 'google/api_client/batch'
33
+ require 'google/api_client/gzip'
34
+ require 'google/api_client/client_secrets'
35
+ require 'google/api_client/railtie' if defined?(Rails::Railtie)
36
+
37
+ module Google
38
+
39
+ ##
40
+ # This class manages APIs communication.
41
+ class APIClient
42
+ include Google::APIClient::Logging
43
+
44
+ ##
45
+ # Creates a new Google API client.
46
+ #
47
+ # @param [Hash] options The configuration parameters for the client.
48
+ # @option options [Symbol, #generate_authenticated_request] :authorization
49
+ # (:oauth_1)
50
+ # The authorization mechanism used by the client. The following
51
+ # mechanisms are supported out-of-the-box:
52
+ # <ul>
53
+ # <li><code>:two_legged_oauth_1</code></li>
54
+ # <li><code>:oauth_1</code></li>
55
+ # <li><code>:oauth_2</code></li>
56
+ # </ul>
57
+ # @option options [Boolean] :auto_refresh_token (true)
58
+ # The setting that controls whether or not the api client attempts to
59
+ # refresh authorization when a 401 is hit in #execute. If the token does
60
+ # not support it, this option is ignored.
61
+ # @option options [String] :application_name
62
+ # The name of the application using the client.
63
+ # @option options [String] :application_version
64
+ # The version number of the application using the client.
65
+ # @option options [String] :user_agent
66
+ # ("{app_name} google-api-ruby-client/{version} {os_name}/{os_version}")
67
+ # The user agent used by the client. Most developers will want to
68
+ # leave this value alone and use the `:application_name` option instead.
69
+ # @option options [String] :host ("www.googleapis.com")
70
+ # The API hostname used by the client. This rarely needs to be changed.
71
+ # @option options [String] :port (443)
72
+ # The port number used by the client. This rarely needs to be changed.
73
+ # @option options [String] :discovery_path ("/discovery/v1")
74
+ # The discovery base path. This rarely needs to be changed.
75
+ # @option options [String] :ca_file
76
+ # Optional set of root certificates to use when validating SSL connections.
77
+ # By default, a bundled set of trusted roots will be used.
78
+ def initialize(options={})
79
+ logger.debug { "#{self.class} - Initializing client with options #{options}" }
80
+
81
+ # Normalize key to String to allow indifferent access.
82
+ options = options.inject({}) do |accu, (key, value)|
83
+ accu[key.to_sym] = value
84
+ accu
85
+ end
86
+ # Almost all API usage will have a host of 'www.googleapis.com'.
87
+ self.host = options[:host] || 'www.googleapis.com'
88
+ self.port = options[:port] || 443
89
+ self.discovery_path = options[:discovery_path] || '/discovery/v1'
90
+
91
+ # Most developers will want to leave this value alone and use the
92
+ # application_name option.
93
+ if options[:application_name]
94
+ app_name = options[:application_name]
95
+ app_version = options[:application_version]
96
+ application_string = "#{app_name}/#{app_version || '0.0.0'}"
97
+ else
98
+ logger.warn { "#{self.class} - Please provide :application_name and :application_version when initializing the client" }
99
+ end
100
+ self.user_agent = options[:user_agent] || (
101
+ "#{application_string} " +
102
+ "google-api-ruby-client/#{Google::APIClient::VERSION::STRING} #{ENV::OS_VERSION} (gzip)"
103
+ ).strip
104
+ # The writer method understands a few Symbols and will generate useful
105
+ # default authentication mechanisms.
106
+ self.authorization =
107
+ options.key?(:authorization) ? options[:authorization] : :oauth_2
108
+ self.auto_refresh_token = options.fetch(:auto_refresh_token) { true }
109
+ self.key = options[:key]
110
+ self.user_ip = options[:user_ip]
111
+ self.retries = options.fetch(:retries) { 0 }
112
+ @discovery_uris = {}
113
+ @discovery_documents = {}
114
+ @discovered_apis = {}
115
+ ca_file = options[:ca_file] || File.expand_path('../../cacerts.pem', __FILE__)
116
+ self.connection = Faraday.new do |faraday|
117
+ faraday.response :gzip
118
+ faraday.options.params_encoder = Faraday::FlatParamsEncoder
119
+ faraday.ssl.ca_file = ca_file
120
+ faraday.ssl.verify = true
121
+ faraday.adapter Faraday.default_adapter
122
+ end
123
+ return self
124
+ end
125
+
126
+ ##
127
+ # Returns the authorization mechanism used by the client.
128
+ #
129
+ # @return [#generate_authenticated_request] The authorization mechanism.
130
+ attr_reader :authorization
131
+
132
+ ##
133
+ # Sets the authorization mechanism used by the client.
134
+ #
135
+ # @param [#generate_authenticated_request] new_authorization
136
+ # The new authorization mechanism.
137
+ def authorization=(new_authorization)
138
+ case new_authorization
139
+ when :oauth_1, :oauth
140
+ require 'signet/oauth_1/client'
141
+ # NOTE: Do not rely on this default value, as it may change
142
+ new_authorization = Signet::OAuth1::Client.new(
143
+ :temporary_credential_uri =>
144
+ 'https://www.google.com/accounts/OAuthGetRequestToken',
145
+ :authorization_uri =>
146
+ 'https://www.google.com/accounts/OAuthAuthorizeToken',
147
+ :token_credential_uri =>
148
+ 'https://www.google.com/accounts/OAuthGetAccessToken',
149
+ :client_credential_key => 'anonymous',
150
+ :client_credential_secret => 'anonymous'
151
+ )
152
+ when :two_legged_oauth_1, :two_legged_oauth
153
+ require 'signet/oauth_1/client'
154
+ # NOTE: Do not rely on this default value, as it may change
155
+ new_authorization = Signet::OAuth1::Client.new(
156
+ :client_credential_key => nil,
157
+ :client_credential_secret => nil,
158
+ :two_legged => true
159
+ )
160
+ when :oauth_2
161
+ require 'signet/oauth_2/client'
162
+ # NOTE: Do not rely on this default value, as it may change
163
+ new_authorization = Signet::OAuth2::Client.new(
164
+ :authorization_uri =>
165
+ 'https://accounts.google.com/o/oauth2/auth',
166
+ :token_credential_uri =>
167
+ 'https://accounts.google.com/o/oauth2/token'
168
+ )
169
+ when nil
170
+ # No authorization mechanism
171
+ else
172
+ if !new_authorization.respond_to?(:generate_authenticated_request)
173
+ raise TypeError,
174
+ 'Expected authorization mechanism to respond to ' +
175
+ '#generate_authenticated_request.'
176
+ end
177
+ end
178
+ @authorization = new_authorization
179
+ return @authorization
180
+ end
181
+
182
+ ##
183
+ # Default Faraday/HTTP connection.
184
+ #
185
+ # @return [Faraday::Connection]
186
+ attr_accessor :connection
187
+
188
+ ##
189
+ # The setting that controls whether or not the api client attempts to
190
+ # refresh authorization when a 401 is hit in #execute.
191
+ #
192
+ # @return [Boolean]
193
+ attr_accessor :auto_refresh_token
194
+
195
+ ##
196
+ # The application's API key issued by the API console.
197
+ #
198
+ # @return [String] The API key.
199
+ attr_accessor :key
200
+
201
+ ##
202
+ # The IP address of the user this request is being performed on behalf of.
203
+ #
204
+ # @return [String] The user's IP address.
205
+ attr_accessor :user_ip
206
+
207
+ ##
208
+ # The user agent used by the client.
209
+ #
210
+ # @return [String]
211
+ # The user agent string used in the User-Agent header.
212
+ attr_accessor :user_agent
213
+
214
+ ##
215
+ # The API hostname used by the client.
216
+ #
217
+ # @return [String]
218
+ # The API hostname. Should almost always be 'www.googleapis.com'.
219
+ attr_accessor :host
220
+
221
+ ##
222
+ # The port number used by the client.
223
+ #
224
+ # @return [String]
225
+ # The port number. Should almost always be 443.
226
+ attr_accessor :port
227
+
228
+ ##
229
+ # The base path used by the client for discovery.
230
+ #
231
+ # @return [String]
232
+ # The base path. Should almost always be '/discovery/v1'.
233
+ attr_accessor :discovery_path
234
+
235
+ ##
236
+ # Number of times to retry on recoverable errors
237
+ #
238
+ # @return [FixNum]
239
+ # Number of retries
240
+ attr_accessor :retries
241
+
242
+ ##
243
+ # Returns the URI for the directory document.
244
+ #
245
+ # @return [Addressable::URI] The URI of the directory document.
246
+ def directory_uri
247
+ return resolve_uri(self.discovery_path + '/apis')
248
+ end
249
+
250
+ ##
251
+ # Manually registers a URI as a discovery document for a specific version
252
+ # of an API.
253
+ #
254
+ # @param [String, Symbol] api The API name.
255
+ # @param [String] version The desired version of the API.
256
+ # @param [Addressable::URI] uri The URI of the discovery document.
257
+ def register_discovery_uri(api, version, uri)
258
+ api = api.to_s
259
+ version = version || 'v1'
260
+ @discovery_uris["#{api}:#{version}"] = uri
261
+ end
262
+
263
+ ##
264
+ # Returns the URI for the discovery document.
265
+ #
266
+ # @param [String, Symbol] api The API name.
267
+ # @param [String] version The desired version of the API.
268
+ # @return [Addressable::URI] The URI of the discovery document.
269
+ def discovery_uri(api, version=nil)
270
+ api = api.to_s
271
+ version = version || 'v1'
272
+ return @discovery_uris["#{api}:#{version}"] ||= (
273
+ resolve_uri(
274
+ self.discovery_path + '/apis/{api}/{version}/rest',
275
+ 'api' => api,
276
+ 'version' => version
277
+ )
278
+ )
279
+ end
280
+
281
+ ##
282
+ # Manually registers a pre-loaded discovery document for a specific version
283
+ # of an API.
284
+ #
285
+ # @param [String, Symbol] api The API name.
286
+ # @param [String] version The desired version of the API.
287
+ # @param [String, StringIO] discovery_document
288
+ # The contents of the discovery document.
289
+ def register_discovery_document(api, version, discovery_document)
290
+ api = api.to_s
291
+ version = version || 'v1'
292
+ if discovery_document.kind_of?(StringIO)
293
+ discovery_document.rewind
294
+ discovery_document = discovery_document.string
295
+ elsif discovery_document.respond_to?(:to_str)
296
+ discovery_document = discovery_document.to_str
297
+ else
298
+ raise TypeError,
299
+ "Expected String or StringIO, got #{discovery_document.class}."
300
+ end
301
+ @discovery_documents["#{api}:#{version}"] =
302
+ MultiJson.load(discovery_document)
303
+ end
304
+
305
+ ##
306
+ # Returns the parsed directory document.
307
+ #
308
+ # @return [Hash] The parsed JSON from the directory document.
309
+ def directory_document
310
+ return @directory_document ||= (begin
311
+ response = self.execute!(
312
+ :http_method => :get,
313
+ :uri => self.directory_uri,
314
+ :authenticated => false
315
+ )
316
+ response.data
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
+ response = self.execute!(
331
+ :http_method => :get,
332
+ :uri => self.discovery_uri(api, version),
333
+ :authenticated => false
334
+ )
335
+ response.data
336
+ end)
337
+ end
338
+
339
+ ##
340
+ # Returns all APIs published in the directory document.
341
+ #
342
+ # @return [Array] The list of available APIs.
343
+ def discovered_apis
344
+ @directory_apis ||= (begin
345
+ document_base = self.directory_uri
346
+ if self.directory_document && self.directory_document['items']
347
+ self.directory_document['items'].map do |discovery_document|
348
+ Google::APIClient::API.new(
349
+ document_base,
350
+ discovery_document
351
+ )
352
+ end
353
+ else
354
+ []
355
+ end
356
+ end)
357
+ end
358
+
359
+ ##
360
+ # Returns the service object for a given service name and service version.
361
+ #
362
+ # @param [String, Symbol] api The API name.
363
+ # @param [String] version The desired version of the API.
364
+ #
365
+ # @return [Google::APIClient::API] The service object.
366
+ def discovered_api(api, version=nil)
367
+ if !api.kind_of?(String) && !api.kind_of?(Symbol)
368
+ raise TypeError,
369
+ "Expected String or Symbol, got #{api.class}."
370
+ end
371
+ api = api.to_s
372
+ version = version || 'v1'
373
+ return @discovered_apis["#{api}:#{version}"] ||= begin
374
+ document_base = self.discovery_uri(api, version)
375
+ discovery_document = self.discovery_document(api, version)
376
+ if document_base && discovery_document
377
+ Google::APIClient::API.new(
378
+ document_base,
379
+ discovery_document
380
+ )
381
+ else
382
+ nil
383
+ end
384
+ end
385
+ end
386
+
387
+ ##
388
+ # Returns the method object for a given RPC name and service version.
389
+ #
390
+ # @param [String, Symbol] rpc_name The RPC name of the desired method.
391
+ # @param [String, Symbol] api The API the method is within.
392
+ # @param [String] version The desired version of the API.
393
+ #
394
+ # @return [Google::APIClient::Method] The method object.
395
+ def discovered_method(rpc_name, api, version=nil)
396
+ if !rpc_name.kind_of?(String) && !rpc_name.kind_of?(Symbol)
397
+ raise TypeError,
398
+ "Expected String or Symbol, got #{rpc_name.class}."
399
+ end
400
+ rpc_name = rpc_name.to_s
401
+ api = api.to_s
402
+ version = version || 'v1'
403
+ service = self.discovered_api(api, version)
404
+ if service.to_h[rpc_name]
405
+ return service.to_h[rpc_name]
406
+ else
407
+ return nil
408
+ end
409
+ end
410
+
411
+ ##
412
+ # Returns the service object with the highest version number.
413
+ #
414
+ # @note <em>Warning</em>: This method should be used with great care.
415
+ # As APIs are updated, minor differences between versions may cause
416
+ # incompatibilities. Requesting a specific version will avoid this issue.
417
+ #
418
+ # @param [String, Symbol] api The name of the service.
419
+ #
420
+ # @return [Google::APIClient::API] The service object.
421
+ def preferred_version(api)
422
+ if !api.kind_of?(String) && !api.kind_of?(Symbol)
423
+ raise TypeError,
424
+ "Expected String or Symbol, got #{api.class}."
425
+ end
426
+ api = api.to_s
427
+ return self.discovered_apis.detect do |a|
428
+ a.name == api && a.preferred == true
429
+ end
430
+ end
431
+
432
+ ##
433
+ # Verifies an ID token against a server certificate. Used to ensure that
434
+ # an ID token supplied by an untrusted client-side mechanism is valid.
435
+ # Raises an error if the token is invalid or missing.
436
+ #
437
+ # @deprecated Use the google-id-token gem for verifying JWTs
438
+ def verify_id_token!
439
+ require 'jwt'
440
+ require 'openssl'
441
+ @certificates ||= {}
442
+ if !self.authorization.respond_to?(:id_token)
443
+ raise ArgumentError, (
444
+ "Current authorization mechanism does not support ID tokens: " +
445
+ "#{self.authorization.class.to_s}"
446
+ )
447
+ elsif !self.authorization.id_token
448
+ raise ArgumentError, (
449
+ "Could not verify ID token, ID token missing. " +
450
+ "Scopes were: #{self.authorization.scope.inspect}"
451
+ )
452
+ else
453
+ check_cached_certs = lambda do
454
+ valid = false
455
+ for key, cert in @certificates
456
+ begin
457
+ self.authorization.decoded_id_token(cert.public_key)
458
+ valid = true
459
+ rescue JWT::DecodeError, Signet::UnsafeOperationError
460
+ # Expected exception. Ignore, ID token has not been validated.
461
+ end
462
+ end
463
+ valid
464
+ end
465
+ if check_cached_certs.call()
466
+ return true
467
+ end
468
+ response = self.execute!(
469
+ :http_method => :get,
470
+ :uri => 'https://www.googleapis.com/oauth2/v1/certs',
471
+ :authenticated => false
472
+ )
473
+ @certificates.merge!(
474
+ Hash[MultiJson.load(response.body).map do |key, cert|
475
+ [key, OpenSSL::X509::Certificate.new(cert)]
476
+ end]
477
+ )
478
+ if check_cached_certs.call()
479
+ return true
480
+ else
481
+ raise InvalidIDTokenError,
482
+ "Could not verify ID token against any available certificate."
483
+ end
484
+ end
485
+ return nil
486
+ end
487
+
488
+ ##
489
+ # Generates a request.
490
+ #
491
+ # @option options [Google::APIClient::Method] :api_method
492
+ # The method object or the RPC name of the method being executed.
493
+ # @option options [Hash, Array] :parameters
494
+ # The parameters to send to the method.
495
+ # @option options [Hash, Array] :headers The HTTP headers for the request.
496
+ # @option options [String] :body The body of the request.
497
+ # @option options [String] :version ("v1")
498
+ # The service version. Only used if `api_method` is a `String`.
499
+ # @option options [#generate_authenticated_request] :authorization
500
+ # The authorization mechanism for the response. Used only if
501
+ # `:authenticated` is `true`.
502
+ # @option options [TrueClass, FalseClass] :authenticated (true)
503
+ # `true` if the request must be signed or somehow
504
+ # authenticated, `false` otherwise.
505
+ #
506
+ # @return [Google::APIClient::Reference] The generated request.
507
+ #
508
+ # @example
509
+ # request = client.generate_request(
510
+ # :api_method => 'plus.activities.list',
511
+ # :parameters =>
512
+ # {'collection' => 'public', 'userId' => 'me'}
513
+ # )
514
+ def generate_request(options={})
515
+ options = {
516
+ :api_client => self
517
+ }.merge(options)
518
+ return Google::APIClient::Request.new(options)
519
+ end
520
+
521
+ ##
522
+ # Executes a request, wrapping it in a Result object.
523
+ #
524
+ # @param [Google::APIClient::Request, Hash, Array] params
525
+ # Either a Google::APIClient::Request, a Hash, or an Array.
526
+ #
527
+ # If a Google::APIClient::Request, no other parameters are expected.
528
+ #
529
+ # If a Hash, the below parameters are handled. If an Array, the
530
+ # parameters are assumed to be in the below order:
531
+ #
532
+ # - (Google::APIClient::Method) api_method:
533
+ # The method object or the RPC name of the method being executed.
534
+ # - (Hash, Array) parameters:
535
+ # The parameters to send to the method.
536
+ # - (String) body: The body of the request.
537
+ # - (Hash, Array) headers: The HTTP headers for the request.
538
+ # - (Hash) options: A set of options for the request, of which:
539
+ # - (#generate_authenticated_request) :authorization (default: true) -
540
+ # The authorization mechanism for the response. Used only if
541
+ # `:authenticated` is `true`.
542
+ # - (TrueClass, FalseClass) :authenticated (default: true) -
543
+ # `true` if the request must be signed or somehow
544
+ # authenticated, `false` otherwise.
545
+ # - (TrueClass, FalseClass) :gzip (default: true) -
546
+ # `true` if gzip enabled, `false` otherwise.
547
+ # - (FixNum) :retries -
548
+ # # of times to retry on recoverable errors
549
+ #
550
+ # @return [Google::APIClient::Result] The result from the API, nil if batch.
551
+ #
552
+ # @example
553
+ # result = client.execute(batch_request)
554
+ #
555
+ # @example
556
+ # plus = client.discovered_api('plus')
557
+ # result = client.execute(
558
+ # :api_method => plus.activities.list,
559
+ # :parameters => {'collection' => 'public', 'userId' => 'me'}
560
+ # )
561
+ #
562
+ # @see Google::APIClient#generate_request
563
+ def execute!(*params)
564
+ if params.first.kind_of?(Google::APIClient::Request)
565
+ request = params.shift
566
+ options = params.shift || {}
567
+ else
568
+ # This block of code allows us to accept multiple parameter passing
569
+ # styles, and maintaining some backwards compatibility.
570
+ #
571
+ # Note: I'm extremely tempted to deprecate this style of execute call.
572
+ if params.last.respond_to?(:to_hash) && params.size == 1
573
+ options = params.pop
574
+ else
575
+ options = {}
576
+ end
577
+
578
+ options[:api_method] = params.shift if params.size > 0
579
+ options[:parameters] = params.shift if params.size > 0
580
+ options[:body] = params.shift if params.size > 0
581
+ options[:headers] = params.shift if params.size > 0
582
+ options.update(params.shift) if params.size > 0
583
+ request = self.generate_request(options)
584
+ end
585
+
586
+ request.headers['User-Agent'] ||= '' + self.user_agent unless self.user_agent.nil?
587
+ request.headers['Accept-Encoding'] ||= 'gzip' unless options[:gzip] == false
588
+ request.headers['Content-Type'] ||= ''
589
+ request.parameters['key'] ||= self.key unless self.key.nil?
590
+ request.parameters['userIp'] ||= self.user_ip unless self.user_ip.nil?
591
+
592
+ connection = options[:connection] || self.connection
593
+ request.authorization = options[:authorization] || self.authorization unless options[:authenticated] == false
594
+ tries = 1 + (options[:retries] || self.retries)
595
+ Retriable.retriable :tries => tries,
596
+ :on => [TransmissionError],
597
+ :interval => lambda {|attempts| (2 ** attempts) + rand} do
598
+ result = request.send(connection, true)
599
+
600
+ case result.status
601
+ when 200...300
602
+ result
603
+ when 301, 302, 303, 307
604
+ request = generate_request(request.to_hash.merge({
605
+ :uri => result.headers['location'],
606
+ :api_method => nil
607
+ }))
608
+ raise RedirectError.new(result.headers['location'], result)
609
+ when 400...500
610
+ if result.status == 401 && request.authorization.respond_to?(:refresh_token) && auto_refresh_token
611
+ begin
612
+ logger.debug("Attempting refresh of access token & retry of request")
613
+ request.authorization.fetch_access_token!
614
+ rescue Signet::AuthorizationError
615
+ # Ignore since we want the original error
616
+ end
617
+ end
618
+ raise ClientError.new(result.error_message || "A client error has occurred", result)
619
+ when 500...600
620
+ raise ServerError.new(result.error_message || "A server error has occurred", result)
621
+ else
622
+ raise TransmissionError.new(result.error_message || "A transmission error has occurred", result)
623
+ end
624
+ end
625
+ end
626
+
627
+ ##
628
+ # Same as Google::APIClient#execute!, but does not raise an exception for
629
+ # normal API errros.
630
+ #
631
+ # @see Google::APIClient#execute
632
+ def execute(*params)
633
+ begin
634
+ return self.execute!(*params)
635
+ rescue TransmissionError => e
636
+ return e.result
637
+ end
638
+ end
639
+
640
+ protected
641
+
642
+ ##
643
+ # Resolves a URI template against the client's configured base.
644
+ #
645
+ # @api private
646
+ # @param [String, Addressable::URI, Addressable::Template] template
647
+ # The template to resolve.
648
+ # @param [Hash] mapping The mapping that corresponds to the template.
649
+ # @return [Addressable::URI] The expanded URI.
650
+ def resolve_uri(template, mapping={})
651
+ @base_uri ||= Addressable::URI.new(
652
+ :scheme => 'https',
653
+ :host => self.host,
654
+ :port => self.port
655
+ ).normalize
656
+ template = if template.kind_of?(Addressable::Template)
657
+ template.pattern
658
+ elsif template.respond_to?(:to_str)
659
+ template.to_str
660
+ else
661
+ raise TypeError,
662
+ "Expected String, Addressable::URI, or Addressable::Template, " +
663
+ "got #{template.class}."
664
+ end
665
+ return Addressable::Template.new(@base_uri + template).expand(mapping)
666
+ end
667
+
668
+ end
669
+
670
+ end
671
+
672
+ require 'google/api_client/version'