Unit test updates + ensure auth retry only done once per execute

This commit is contained in:
Doug Henderson 2014-03-13 13:13:44 -07:00
parent 2bed0748ab
commit 17092bb129
2 changed files with 92 additions and 11 deletions

View File

@ -109,6 +109,7 @@ module Google
self.key = options[:key] self.key = options[:key]
self.user_ip = options[:user_ip] self.user_ip = options[:user_ip]
self.retries = options.fetch(:retries) { 0 } self.retries = options.fetch(:retries) { 0 }
self.expired_auth_retry = options.fetch(:expired_auth_retry) { true }
@discovery_uris = {} @discovery_uris = {}
@discovery_documents = {} @discovery_documents = {}
@discovered_apis = {} @discovered_apis = {}
@ -239,6 +240,13 @@ module Google
# Number of retries # Number of retries
attr_accessor :retries attr_accessor :retries
##
# Whether or not an expired auth token should be re-acquired
# (and the operation retried) regardless of retries setting
# @return [Boolean]
# Auto retry on auth expiry
attr_accessor :expired_auth_retry
## ##
# Returns the URI for the directory document. # Returns the URI for the directory document.
# #
@ -593,16 +601,20 @@ module Google
request.authorization = options[:authorization] || self.authorization unless options[:authenticated] == false request.authorization = options[:authorization] || self.authorization unless options[:authenticated] == false
tries = 1 + (options[:retries] || self.retries) tries = 1 + (options[:retries] || self.retries)
attempt = 0
Retriable.retriable :tries => tries, Retriable.retriable :tries => tries,
:on => [TransmissionError], :on => [TransmissionError],
:on_retry => client_error_handler,
:interval => lambda {|attempts| (2 ** attempts) + rand} do :interval => lambda {|attempts| (2 ** attempts) + rand} do
attempt += 1
# This 2nd level retriable only catches auth errors, and supports 1 retry, which allows # This 2nd level retriable only catches auth errors, and supports 1 retry, which allows
# auth to be re-attempted without having to retry all sorts of other failures like # auth to be re-attempted without having to retry all sorts of other failures like
# NotFound, etc # NotFound, etc
Retriable.retriable :tries => 2, Retriable.retriable :tries => ((expired_auth_retry || tries > 1) && attempt == 1) ? 2 : 1,
:on => [AuthorizationError], :on => [AuthorizationError],
:on_retry => client_error_handler(request.authorization), :on_retry => authorization_error_handler(request.authorization),
:interval => lambda {|attempts| (2 ** attempts) + rand} do :interval => lambda {|attempts| (2 ** attempts) + rand} do
result = request.send(connection, true) result = request.send(connection, true)
@ -671,18 +683,17 @@ module Google
## ##
# Returns on proc for special processing of retries as not all client errors # Returns on proc for special processing of retries for authorization errors
# are recoverable. Only 401s should be retried and only if the credentials # Only 401s should be retried and only if the credentials are refreshable
# are refreshable
# #
# @param [#fetch_access_token!] authorization # @param [#fetch_access_token!] authorization
# OAuth 2 credentials # OAuth 2 credentials
# @return [Proc] # @return [Proc]
def client_error_handler(authorization) def authorization_error_handler(authorization)
can_refresh = authorization.respond_to?(:refresh_token) && auto_refresh_token can_refresh = authorization.respond_to?(:refresh_token) && auto_refresh_token
Proc.new do |exception, tries| Proc.new do |exception, tries|
next unless exception.kind_of?(ClientError) next unless exception.kind_of?(AuthorizationError)
if exception.result.status == 401 && can_refresh && tries == 1 if can_refresh
begin begin
logger.debug("Attempting refresh of access token & retry of request") logger.debug("Attempting refresh of access token & retry of request")
authorization.fetch_access_token! authorization.fetch_access_token!
@ -694,6 +705,17 @@ module Google
end end
end end
##
# Returns on proc for special processing of retries as not all client errors
# are recoverable. Only 401s should be retried (via authorization_error_handler)
#
# @return [Proc]
def client_error_handler
Proc.new do |exception, tries|
raise exception if exception.kind_of?(ClientError)
end
end
end end
end end

View File

@ -194,7 +194,7 @@ describe Google::APIClient do
) )
end end
it 'should refresh tokens on 401 tokens' do it 'should refresh tokens on 401 errors' do
client.authorization.access_token = '12345' client.authorization.access_token = '12345'
expect(client.authorization).to receive(:fetch_access_token!) expect(client.authorization).to receive(:fetch_access_token!)
@ -283,5 +283,64 @@ describe Google::APIClient do
count.should == 3 count.should == 3
end end
end end
describe 'when retries disabled and expired_auth_retry on (default)' do
before do
client.retries = 0
end
after do
@connection.verify
end
it 'should refresh tokens on 401 errors' do
client.authorization.access_token = '12345'
expect(client.authorization).to receive(:fetch_access_token!)
@connection = stub_connection do |stub|
stub.get('/foo') do |env|
[401, {}, '{}']
end
stub.get('/foo') do |env|
[200, {}, '{}']
end
end
client.execute(
:uri => 'https://www.gogole.com/foo',
:connection => @connection
)
end
end
describe 'when retries disabled and expired_auth_retry off' do
before do
client.retries = 0
client.expired_auth_retry = false
end
it 'should not refresh tokens on 401 errors' do
client.authorization.access_token = '12345'
expect(client.authorization).not_to receive(:fetch_access_token!)
@connection = stub_connection do |stub|
stub.get('/foo') do |env|
[401, {}, '{}']
end
stub.get('/foo') do |env|
[200, {}, '{}']
end
end
resp = client.execute(
:uri => 'https://www.gogole.com/foo',
:connection => @connection
)
resp.response.status.should == 401
end
end
end end