Created
October 30, 2014 20:07
-
-
Save ctweney/584ea5f6fd869faf5469 to your computer and use it in GitHub Desktop.
Batch API calls don't observe auto_refresh_token setting
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rubygems' | |
require 'google/api_client' | |
require 'google/api_client/client_secrets' | |
require 'google/api_client/auth/file_storage' | |
require 'sinatra' | |
require 'logger' | |
enable :sessions | |
CREDENTIAL_STORE_FILE = "#{$0}-oauth2.json" | |
def logger; settings.logger end | |
def api_client; settings.api_client; end | |
def calendar_api; settings.calendar; end | |
def user_credentials | |
# Build a per-request oauth credential based on token stored in session | |
# which allows us to use a shared API client. | |
@authorization ||= ( | |
auth = api_client.authorization.dup | |
auth.redirect_uri = to('/oauth2callback') | |
auth.update_token!(session) | |
auth | |
) | |
end | |
configure do | |
log_file = File.open('calendar.log', 'a+') | |
log_file.sync = true | |
logger = Logger.new(log_file) | |
logger.level = Logger::DEBUG | |
client = Google::APIClient.new( | |
:application_name => 'Ruby Calendar sample', | |
:application_version => '1.0.0', | |
:auto_refresh_token => true, | |
:retries => 3 | |
) | |
file_storage = Google::APIClient::FileStorage.new(CREDENTIAL_STORE_FILE) | |
if file_storage.authorization.nil? | |
client_secrets = Google::APIClient::ClientSecrets.load | |
client.authorization = client_secrets.to_authorization | |
client.authorization.scope = 'https://www.googleapis.com/auth/calendar' | |
else | |
client.authorization = file_storage.authorization | |
end | |
# Since we're saving the API definition to the settings, we're only retrieving | |
# it once (on server start) and saving it between requests. | |
# If this is still an issue, you could serialize the object and load it on | |
# subsequent runs. | |
calendar = client.discovered_api('calendar', 'v3') | |
set :logger, logger | |
set :api_client, client | |
set :calendar, calendar | |
end | |
before do | |
# Ensure user has authorized the app | |
unless user_credentials.access_token || request.path_info =~ /\A\/oauth2/ | |
redirect to('/oauth2authorize') | |
end | |
end | |
after do | |
# Serialize the access/refresh token to the session and credential store. | |
session[:access_token] = user_credentials.access_token | |
session[:refresh_token] = user_credentials.refresh_token | |
session[:expires_in] = user_credentials.expires_in | |
session[:issued_at] = user_credentials.issued_at | |
file_storage = Google::APIClient::FileStorage.new(CREDENTIAL_STORE_FILE) | |
file_storage.write_credentials(user_credentials) | |
end | |
get '/oauth2authorize' do | |
# Request authorization | |
redirect user_credentials.authorization_uri.to_s, 303 | |
end | |
get '/oauth2callback' do | |
# Exchange token | |
user_credentials.code = params[:code] if params[:code] | |
user_credentials.fetch_access_token! | |
redirect to('/') | |
end | |
# single API requests with auto_refresh_token:true will work even when the access token has expired. | |
get '/single' do | |
old_token = api_client.authorization.access_token | |
puts "Token before = #{api_client.authorization.access_token}; expires_in #{api_client.authorization.expires_in}; issued_at #{api_client.authorization.issued_at}" | |
result = api_client.execute(:api_method => calendar_api.events.list, | |
:parameters => {'calendarId' => 'primary'}, | |
:authorization => user_credentials) | |
puts "Token changed? #{old_token != api_client.authorization.access_token}" | |
puts "Token after = #{api_client.authorization.access_token}; expires_in #{api_client.authorization.expires_in}; issued_at #{api_client.authorization.issued_at}" | |
[result.status, {'Content-Type' => 'application/json'}, result.data.to_json] | |
end | |
# batched API requests with auto_refresh_token:true will NOT work when the access token has expired. | |
get '/batch' do | |
old_token = api_client.authorization.access_token | |
puts "Token before = #{api_client.authorization.access_token}; expires_in #{api_client.authorization.expires_in}; issued_at #{api_client.authorization.issued_at}" | |
results_json = [] | |
batch = Google::APIClient::BatchRequest.new do |result| | |
puts "Batch response status: #{result.response.status}" | |
puts "Token currently = #{api_client.authorization.access_token}, changed? #{old_token != api_client.authorization.access_token}" | |
results_json << result.data.to_json | |
end | |
batch.add(:api_method => calendar_api.events.get, | |
:parameters => {'eventId' => 'jfu2jg6495nbbp43q3832ln65k', 'calendarId' => 'primary'}, | |
:authorization => user_credentials) | |
batch.add(:api_method => calendar_api.events.get, | |
:parameters => {'eventId' => 'dpdni6vbvbnopr6dbu8r7iq7vg', 'calendarId' => 'primary'}, | |
:authorization => user_credentials) | |
batch.add(:api_method => calendar_api.events.get, | |
:parameters => {'eventId' => 'abcdef', 'calendarId' => 'primary'}, | |
:authorization => user_credentials) | |
api_client.execute batch | |
puts "Token changed? #{old_token != api_client.authorization.access_token}" | |
puts "Token after = #{api_client.authorization.access_token}; expires_in #{api_client.authorization.expires_in}; issued_at #{api_client.authorization.issued_at}" | |
[200, {'Content-Type' => 'application/json'}, results_json] | |
end | |
# explicitly updating the token with fetch_access_token! makes batched API requests work correctly, even with an expired token. | |
get '/batch_refreshing' do | |
old_token = api_client.authorization.access_token | |
puts "Token before = #{api_client.authorization.access_token}; expires_in #{api_client.authorization.expires_in}; issued_at #{api_client.authorization.issued_at}" | |
# now refresh the token | |
api_client.authorization.fetch_access_token! | |
puts "Token after refreshing, before batch = #{api_client.authorization.access_token}; expires_in #{api_client.authorization.expires_in}; issued_at #{api_client.authorization.issued_at}" | |
results_json = [] | |
batch = Google::APIClient::BatchRequest.new do |result| | |
puts "Batch response status: #{result.response.status}" | |
puts "Token currently = #{api_client.authorization.access_token}, changed? #{old_token != api_client.authorization.access_token}" | |
results_json << result.data.to_json | |
end | |
batch.add(:api_method => calendar_api.events.get, | |
:parameters => {'eventId' => 'jfu2jg6495nbbp43q3832ln65k', 'calendarId' => 'primary'}, | |
:authorization => user_credentials) | |
batch.add(:api_method => calendar_api.events.get, | |
:parameters => {'eventId' => 'dpdni6vbvbnopr6dbu8r7iq7vg', 'calendarId' => 'primary'}, | |
:authorization => user_credentials) | |
batch.add(:api_method => calendar_api.events.get, | |
:parameters => {'eventId' => 'abcdef', 'calendarId' => 'primary'}, | |
:authorization => user_credentials) | |
api_client.execute batch | |
puts "Token changed? #{old_token != api_client.authorization.access_token}" | |
puts "Token after batch = #{api_client.authorization.access_token}; expires_in #{api_client.authorization.expires_in}; issued_at #{api_client.authorization.issued_at}" | |
[200, {'Content-Type' => 'application/json'}, results_json] | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
FWIW, if you're using the same credentials for each individual request you can just specify the credentials either at the connection (api_client.authorization) or as an option to api_client.execute. In either of those two cases refresh will work as expected.
Handling refresh/retries for the individual requests can get very complicated quickly...