google-api-client 0.7.0.rc2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c9fdb163b06a237193bbfdb00789efa0477f9f25
4
- data.tar.gz: f69ac3dfb9612faee06a4621a68f8cc76cdac706
3
+ metadata.gz: 25f7fed5190421b20ce39568ea716d2a5a52627e
4
+ data.tar.gz: f511d420725a7e69600d7654090d7794743c8591
5
5
  SHA512:
6
- metadata.gz: f26c7cc3ab8beab47c95b2763c8f0c5bc5df4ea04e6daae3ad5f5e9f8c24e3358a295d642ec3c315e269d5a2837ab83fc02122723604fe9c467740b6689e8f0c
7
- data.tar.gz: 1432deab452fc436913508288081f4febf770d995e8dcdbd9ce0f5bf1145e894c4b44ba4a4cf7839e6e48d21d77c80a0c5a90f892ecce341f946118f11072966
6
+ metadata.gz: 4a889315a2b6d96b6d56ac808880203c7dbe6c108a214c610440182e784162a93c753367d49d9cc16b22a50a8d2668e507590d45f6665c3227fe31faed8adf88
7
+ data.tar.gz: 49493d0cb5c148bd88962b1eaca02acba2f21bfc04ef115a6e75958f2677a386a6945698f3ea2054b2f3e635be08fc441441aef149f60aa880503809b7618d90
data/CHANGELOG.md CHANGED
@@ -1,8 +1,11 @@
1
- # 0.7.0.rc2
1
+ # 0.7.0
2
+ * Remove CLI
3
+ * SUpport for automatic retires & backoff. Off by default, enable by setting `retries` on `APIClient`
4
+ * Experimental new interface (see `Google::APIClient::Service`)
2
5
  * Fix warnings when using Faraday separately
3
6
  * Support Google Compute Engine service accounts
4
7
  * Enable gzip compression for responses
5
- * Upgrade to Faraday 0.9.x. Resolves multiple issues with query parameter encodings.
8
+ * Upgrade to Faraday 0.9.0. Resolves multiple issues with query parameter encodings.
6
9
  * Use bundled root certificates for verifying SSL certificates
7
10
  * Rewind media when retrying uploads
8
11
 
data/Gemfile CHANGED
@@ -6,10 +6,11 @@ gem 'signet', '>= 0.5.0'
6
6
  gem 'addressable', '>= 2.3.2'
7
7
  gem 'uuidtools', '>= 2.1.0'
8
8
  gem 'autoparse', '>= 0.3.3'
9
- gem 'faraday', '>= 0.9.0.rc5'
9
+ gem 'faraday', '>= 0.9.0'
10
10
  gem 'multi_json', '>= 1.0.0'
11
11
  gem 'extlib', '>= 0.9.15'
12
12
  gem 'jwt', '~> 0.1.5'
13
+ gem 'retriable', '>= 1.4'
13
14
  gem 'jruby-openssl', :platforms => :jruby
14
15
 
15
16
  group :development do
@@ -18,6 +19,13 @@ group :development do
18
19
  gem 'kramdown'
19
20
  end
20
21
 
22
+
23
+ platforms :rbx do
24
+ gem 'rubysl', '~> 2.0'
25
+ gem 'psych'
26
+ end
27
+
28
+
21
29
  group :examples do
22
30
  gem 'sinatra'
23
31
  end
@@ -29,4 +37,5 @@ group :test, :development do
29
37
  gem 'rcov', '>= 0.9.9', :platform => :mri_18
30
38
  end
31
39
 
40
+
32
41
  gem 'idn', :platform => :mri_18
data/README.md CHANGED
@@ -107,6 +107,16 @@ client.authorization.fetch_access_token!
107
107
  client.execute(...)
108
108
  ```
109
109
 
110
+ Service accounts are also used for delegation in Google Apps domains. The target user for impersonation is specified by setting the `:person` parameter to the user's email address
111
+ in the credentials. Detailed instructions on how to enable delegation for your domain can be found at [developers.google.com](https://developers.google.com/drive/delegation).
112
+
113
+ ### Automatic Retries & Backoff
114
+
115
+ The API client can automatically retry requests for recoverable errors. To enable retries, set the `client.retries` property to
116
+ the number of additional attempts. To avoid flooding servers, retries invovle a 1 second delay that increases on each subsequent retry.
117
+
118
+ The default value for retries is 0, but will be enabled by default in future releases.
119
+
110
120
  ### Batching Requests
111
121
 
112
122
  Some Google APIs support batching requests into a single HTTP request. Use `Google::APIClient::BatchRequest`
@@ -194,7 +204,7 @@ For more information, use `google-api --help`
194
204
 
195
205
  ## Samples
196
206
 
197
- See the full list of [samples on Google Code](http://code.google.com/p/google-api-ruby-client/source/browse?repo=samples).
207
+ See the full list of [samples on Github](https://github.com/google/google-api-ruby-client-samples).
198
208
 
199
209
 
200
210
  ## Support
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ PKG_DISPLAY_NAME = 'Google API Client'
11
11
  PKG_NAME = PKG_DISPLAY_NAME.downcase.gsub(/\s/, '-')
12
12
  PKG_VERSION = Google::APIClient::VERSION::STRING
13
13
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
14
- PKG_HOMEPAGE = 'http://code.google.com/p/google-api-ruby-client/'
14
+ PKG_HOMEPAGE = 'https://github.com/google/google-api-ruby-client'
15
15
 
16
16
  RELEASE_NAME = "REL #{PKG_VERSION}"
17
17
 
@@ -17,6 +17,7 @@ require 'faraday'
17
17
  require 'multi_json'
18
18
  require 'compat/multi_json'
19
19
  require 'stringio'
20
+ require 'retriable'
20
21
 
21
22
  require 'google/api_client/version'
22
23
  require 'google/api_client/logging'
@@ -107,6 +108,7 @@ module Google
107
108
  self.auto_refresh_token = options.fetch(:auto_refresh_token) { true }
108
109
  self.key = options[:key]
109
110
  self.user_ip = options[:user_ip]
111
+ self.retries = options.fetch(:retries) { 0 }
110
112
  @discovery_uris = {}
111
113
  @discovery_documents = {}
112
114
  @discovered_apis = {}
@@ -230,6 +232,13 @@ module Google
230
232
  # The base path. Should almost always be '/discovery/v1'.
231
233
  attr_accessor :discovery_path
232
234
 
235
+ ##
236
+ # Number of times to retry on recoverable errors
237
+ #
238
+ # @return [FixNum]
239
+ # Number of retries
240
+ attr_accessor :retries
241
+
233
242
  ##
234
243
  # Returns the URI for the directory document.
235
244
  #
@@ -424,6 +433,8 @@ module Google
424
433
  # Verifies an ID token against a server certificate. Used to ensure that
425
434
  # an ID token supplied by an untrusted client-side mechanism is valid.
426
435
  # Raises an error if the token is invalid or missing.
436
+ #
437
+ # @deprecated Use the google-id-token gem for verifying JWTs
427
438
  def verify_id_token!
428
439
  require 'jwt'
429
440
  require 'openssl'
@@ -533,6 +544,8 @@ module Google
533
544
  # authenticated, `false` otherwise.
534
545
  # - (TrueClass, FalseClass) :gzip (default: true) -
535
546
  # `true` if gzip enabled, `false` otherwise.
547
+ # - (FixNum) :retries -
548
+ # # of times to retry on recoverable errors
536
549
  #
537
550
  # @return [Google::APIClient::Result] The result from the API, nil if batch.
538
551
  #
@@ -547,7 +560,7 @@ module Google
547
560
  # )
548
561
  #
549
562
  # @see Google::APIClient#generate_request
550
- def execute(*params)
563
+ def execute!(*params)
551
564
  if params.first.kind_of?(Google::APIClient::Request)
552
565
  request = params.shift
553
566
  options = params.shift || {}
@@ -572,53 +585,60 @@ module Google
572
585
 
573
586
  request.headers['User-Agent'] ||= '' + self.user_agent unless self.user_agent.nil?
574
587
  request.headers['Accept-Encoding'] ||= 'gzip' unless options[:gzip] == false
588
+ request.headers['Content-Type'] ||= ''
575
589
  request.parameters['key'] ||= self.key unless self.key.nil?
576
590
  request.parameters['userIp'] ||= self.user_ip unless self.user_ip.nil?
577
591
 
578
592
  connection = options[:connection] || self.connection
579
593
  request.authorization = options[:authorization] || self.authorization unless options[:authenticated] == false
580
-
581
- result = request.send(connection)
582
- if result.status == 401 && request.authorization.respond_to?(:refresh_token) && auto_refresh_token
583
- begin
584
- logger.debug("Attempting refresh of access token & retry of request")
585
- request.authorization.fetch_access_token!
586
- result = request.send(connection, true)
587
- rescue Signet::AuthorizationError
588
- # Ignore since we want the original error
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)
589
623
  end
590
624
  end
591
-
592
- return result
593
625
  end
594
626
 
595
627
  ##
596
- # Same as Google::APIClient#execute, but raises an exception if there was
597
- # an error.
628
+ # Same as Google::APIClient#execute!, but does not raise an exception for
629
+ # normal API errros.
598
630
  #
599
631
  # @see Google::APIClient#execute
600
- def execute!(*params)
601
- result = self.execute(*params)
602
- if result.error?
603
- error_message = result.error_message
604
- case result.response.status
605
- when 400...500
606
- exception_type = ClientError
607
- error_message ||= "A client error has occurred."
608
- when 500...600
609
- exception_type = ServerError
610
- error_message ||= "A server error has occurred."
611
- else
612
- exception_type = TransmissionError
613
- error_message ||= "A transmission error has occurred."
614
- end
615
- raise exception_type, error_message
632
+ def execute(*params)
633
+ begin
634
+ return self.execute!(*params)
635
+ rescue TransmissionError => e
636
+ return e.result
616
637
  end
617
- return result
618
638
  end
619
-
639
+
620
640
  protected
621
-
641
+
622
642
  ##
623
643
  # Resolves a URI template against the client's configured base.
624
644
  #
@@ -646,6 +666,7 @@ module Google
646
666
  end
647
667
 
648
668
  end
669
+
649
670
  end
650
671
 
651
672
  require 'google/api_client/version'
@@ -19,6 +19,17 @@ module Google
19
19
  # An error which is raised when there is an unexpected response or other
20
20
  # transport error that prevents an operation from succeeding.
21
21
  class TransmissionError < StandardError
22
+ attr_reader :result
23
+ def initialize(message = nil, result = nil)
24
+ super(message)
25
+ @result = result
26
+ end
27
+ end
28
+
29
+ ##
30
+ # An exception that is raised if a redirect is required
31
+ #
32
+ class RedirectError < TransmissionError
22
33
  end
23
34
 
24
35
  ##
@@ -287,6 +287,7 @@ module Google
287
287
  raise ArgumentError, "Can not specify body & body object for simple uploads"
288
288
  end
289
289
  self.headers['Content-Type'] ||= self.media.content_type
290
+ self.headers['Content-Length'] ||= self.media.length.to_s
290
291
  self.body = self.media
291
292
  when "multipart"
292
293
  unless options[:body_object]
@@ -0,0 +1,233 @@
1
+ # Copyright 2013 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
+ require 'google/api_client'
16
+ require 'google/api_client/service/stub_generator'
17
+ require 'google/api_client/service/resource'
18
+ require 'google/api_client/service/request'
19
+ require 'google/api_client/service/result'
20
+ require 'google/api_client/service/batch'
21
+ require 'google/api_client/service/simple_file_store'
22
+
23
+ module Google
24
+ class APIClient
25
+
26
+ ##
27
+ # Experimental new programming interface at the API level.
28
+ # Hides Google::APIClient. Designed to be easier to use, with less code.
29
+ #
30
+ # @example
31
+ # calendar = Google::APIClient::Service.new('calendar', 'v3')
32
+ # result = calendar.events.list('calendarId' => 'primary').execute()
33
+ class Service
34
+ include Google::APIClient::Service::StubGenerator
35
+ extend Forwardable
36
+
37
+ DEFAULT_CACHE_FILE = 'discovery.cache'
38
+
39
+ # Cache for discovered APIs.
40
+ @@discovered = {}
41
+
42
+ ##
43
+ # Creates a new Service.
44
+ #
45
+ # @param [String, Symbol] api_name
46
+ # The name of the API this service will access.
47
+ # @param [String, Symbol] api_version
48
+ # The version of the API this service will access.
49
+ # @param [Hash] options
50
+ # The configuration parameters for the service.
51
+ # @option options [Symbol, #generate_authenticated_request] :authorization
52
+ # (:oauth_1)
53
+ # The authorization mechanism used by the client. The following
54
+ # mechanisms are supported out-of-the-box:
55
+ # <ul>
56
+ # <li><code>:two_legged_oauth_1</code></li>
57
+ # <li><code>:oauth_1</code></li>
58
+ # <li><code>:oauth_2</code></li>
59
+ # </ul>
60
+ # @option options [Boolean] :auto_refresh_token (true)
61
+ # The setting that controls whether or not the api client attempts to
62
+ # refresh authorization when a 401 is hit in #execute. If the token does
63
+ # not support it, this option is ignored.
64
+ # @option options [String] :application_name
65
+ # The name of the application using the client.
66
+ # @option options [String] :application_version
67
+ # The version number of the application using the client.
68
+ # @option options [String] :host ("www.googleapis.com")
69
+ # The API hostname used by the client. This rarely needs to be changed.
70
+ # @option options [String] :port (443)
71
+ # The port number used by the client. This rarely needs to be changed.
72
+ # @option options [String] :discovery_path ("/discovery/v1")
73
+ # The discovery base path. This rarely needs to be changed.
74
+ # @option options [String] :ca_file
75
+ # Optional set of root certificates to use when validating SSL connections.
76
+ # By default, a bundled set of trusted roots will be used.
77
+ # @option options [#generate_authenticated_request] :authorization
78
+ # The authorization mechanism for requests. Used only if
79
+ # `:authenticated` is `true`.
80
+ # @option options [TrueClass, FalseClass] :authenticated (default: true)
81
+ # `true` if requests must be signed or somehow
82
+ # authenticated, `false` otherwise.
83
+ # @option options [TrueClass, FalseClass] :gzip (default: true)
84
+ # `true` if gzip enabled, `false` otherwise.
85
+ # @option options [Faraday::Connection] :connection
86
+ # A custom connection to be used for all requests.
87
+ # @option options [ActiveSupport::Cache::Store, :default] :discovery_cache
88
+ # A cache store to place the discovery documents for loaded APIs.
89
+ # Avoids unnecessary roundtrips to the discovery service.
90
+ # :default loads the default local file cache store.
91
+ def initialize(api_name, api_version, options = {})
92
+ @api_name = api_name.to_s
93
+ if api_version.nil?
94
+ raise ArgumentError,
95
+ "API version must be set"
96
+ end
97
+ @api_version = api_version.to_s
98
+ if options && !options.respond_to?(:to_hash)
99
+ raise ArgumentError,
100
+ "expected options Hash, got #{options.class}"
101
+ end
102
+
103
+ params = {}
104
+ [:application_name, :application_version, :authorization, :host, :port,
105
+ :discovery_path, :auto_refresh_token, :key, :user_ip,
106
+ :ca_file].each do |option|
107
+ if options.include? option
108
+ params[option] = options[option]
109
+ end
110
+ end
111
+
112
+ @client = Google::APIClient.new(params)
113
+
114
+ @connection = options[:connection] || @client.connection
115
+
116
+ @options = options
117
+
118
+ # Initialize cache store. Default to SimpleFileStore if :cache_store
119
+ # is not provided and we have write permissions.
120
+ if options.include? :cache_store
121
+ @cache_store = options[:cache_store]
122
+ else
123
+ cache_exists = File.exist?(DEFAULT_CACHE_FILE)
124
+ if (cache_exists && File.writable?(DEFAULT_CACHE_FILE)) ||
125
+ (!cache_exists && File.writable?(Dir.pwd))
126
+ @cache_store = Google::APIClient::Service::SimpleFileStore.new(
127
+ DEFAULT_CACHE_FILE)
128
+ end
129
+ end
130
+
131
+ # Attempt to read API definition from memory cache.
132
+ # Not thread-safe, but the worst that can happen is a cache miss.
133
+ unless @api = @@discovered[[api_name, api_version]]
134
+ # Attempt to read API definition from cache store, if there is one.
135
+ # If there's a miss or no cache store, call discovery service.
136
+ if !@cache_store.nil?
137
+ @api = @cache_store.fetch("%s/%s" % [api_name, api_version]) do
138
+ @client.discovered_api(api_name, api_version)
139
+ end
140
+ else
141
+ @api = @client.discovered_api(api_name, api_version)
142
+ end
143
+ @@discovered[[api_name, api_version]] = @api
144
+ end
145
+
146
+ generate_call_stubs(self, @api)
147
+ end
148
+
149
+ ##
150
+ # Returns the authorization mechanism used by the service.
151
+ #
152
+ # @return [#generate_authenticated_request] The authorization mechanism.
153
+ def_delegators :@client, :authorization, :authorization=
154
+
155
+ ##
156
+ # The setting that controls whether or not the service attempts to
157
+ # refresh authorization when a 401 is hit during an API call.
158
+ #
159
+ # @return [Boolean]
160
+ def_delegators :@client, :auto_refresh_token, :auto_refresh_token=
161
+
162
+ ##
163
+ # The application's API key issued by the API console.
164
+ #
165
+ # @return [String] The API key.
166
+ def_delegators :@client, :key, :key=
167
+
168
+ ##
169
+ # The Faraday/HTTP connection used by this service.
170
+ #
171
+ # @return [Faraday::Connection]
172
+ attr_accessor :connection
173
+
174
+ ##
175
+ # The cache store used for storing discovery documents.
176
+ #
177
+ # @return [ActiveSupport::Cache::Store,
178
+ # Google::APIClient::Service::SimpleFileStore,
179
+ # nil]
180
+ attr_reader :cache_store
181
+
182
+ ##
183
+ # Prepares a Google::APIClient::BatchRequest object to make batched calls.
184
+ # @param [Array] calls
185
+ # Optional array of Google::APIClient::Service::Request to initialize
186
+ # the batch request with.
187
+ # @param [Proc] block
188
+ # Callback for every call's response. Won't be called if a call defined
189
+ # a callback of its own.
190
+ #
191
+ # @yield [Google::APIClient::Service::Result]
192
+ # block to be called when result ready
193
+ def batch(calls = nil, &block)
194
+ Google::APIClient::Service::BatchRequest.new(self, calls, &block)
195
+ end
196
+
197
+ ##
198
+ # Executes an API request.
199
+ # Do not call directly; this method is only used by Request objects when
200
+ # executing.
201
+ #
202
+ # @param [Google::APIClient::Service::Request,
203
+ # Google::APIClient::Service::BatchCall] request
204
+ # The request to be executed.
205
+ def execute(request)
206
+ if request.instance_of? Google::APIClient::Service::Request
207
+ params = {:api_method => request.method,
208
+ :parameters => request.parameters,
209
+ :connection => @connection}
210
+ if request.respond_to? :body
211
+ if request.body.respond_to? :to_hash
212
+ params[:body_object] = request.body
213
+ else
214
+ params[:body] = request.body
215
+ end
216
+ end
217
+ if request.respond_to? :media
218
+ params[:media] = request.media
219
+ end
220
+ [:authenticated, :gzip].each do |option|
221
+ if @options.include? option
222
+ params[option] = @options[option]
223
+ end
224
+ end
225
+ result = @client.execute(params)
226
+ return Google::APIClient::Service::Result.new(request, result)
227
+ elsif request.instance_of? Google::APIClient::Service::BatchRequest
228
+ @client.execute(request.base_batch)
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end