ghost_google-api-client 0.4.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 (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'