Google sheets API is not retrieving correct values - ruby

I'm using Google sheets API (v4) in a ruby script to retrieve the values of a particular columns and this script is running using cron job on a EC2 Linux machine. The script is running fine and fetching the values from the sheet specified. If the existing columns was updated with new values, sometimes the script is retrieved the older values while invoking through cron and sometimes the updated values. But when I run this script standalone, it is always picking up the latest values.
Can someone help in debugging this? Is it an issue with my code or with the cron invocation
Below is the piece of code which retrieves Google sheet values
​def authorize
client_id = Google::Auth::ClientId.from_file CREDENTIALS_PATH
token_store = Google::Auth::Stores::FileTokenStore.new file: TOKEN_PATH
authorizer = Google::Auth::UserAuthorizer.new client_id, SCOPE, token_store
user_id = "default"
credentials = authorizer.get_credentials user_id
if credentials.nil?
url = authorizer.get_authorization_url base_url: OOB_URI
puts "Open the following URL in the browser and enter the " \
"resulting code in the console after authorization:\n" + url
code = gets
credentials = authorizer.get_and_store_credentials_from_code(
user_id: user_id, code: code, base_url: OOB_URI
)
end
credentials
end
def get_budget_spreadsheet_data
# Initialize the API
service = Google::Apis::SheetsV4::SheetsService.new
service.client_options.application_name = APPLICATION_NAME
service.authorization = authorize
spreadsheet_id = "XXXXXX"
range = "Sheet1!A2:Z"
begin
response = service.get_spreadsheet_values spreadsheet_id, range
rescue Exception => e
puts "Error occured while retrieving budget values. #{e.message}"
exit
end
if response.values.empty?
puts "No data found."
exit
end
puts response
end

Related

dailyLimitExceededUnreg: Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup

I set up a small script to export the first tab of a Google Sheet as csv via the Google Drive API. I can execute the script once and it works. During that execution I'm provided with a URL that let's me authenticate online and gives me a code. That code gets stored in a token.yaml file. Then the script successfully exports my sheet.
When I execute the script a second time, I get the error
dailyLimitExceededUnreg: Daily Limit for Unauthenticated Use Exceeded.
Continued use requires signup. (Google::Apis::ClientError)
When I delete the token.yaml file that was created during my first (successful) attempt, I can execute the script again, incl. authentication as described above. When I execute the script another time, I'm back to the error.
require 'google/apis/drive_v3'
require 'googleauth'
require 'googleauth/stores/file_token_store'
require 'fileutils'
OOB_URI = 'urn:ietf:wg:oauth:2.0:oob'.freeze
APPLICATION_NAME = 'Maker'.freeze
CREDENTIALS_PATH = 'credentials.json'.freeze
FILE_ID = '<my-file-id>'
# The file token.yaml stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
#
# copied from https://developers.google.com/drive/api/v3/quickstart/ruby
TOKEN_PATH = 'token.yaml'.freeze
SCOPE = Google::Apis::DriveV3::AUTH_DRIVE
##
# Ensure valid credentials, either by restoring from the saved credentials
# files or initiating an OAuth2 authorization. If authorization is required,
# the user's default browser will be launched to approve the request.
#
# #return [Google::Auth::UserRefreshCredentials] OAuth2 credentials
#
# copied from https://developers.google.com/drive/api/v3/quickstart/ruby
def authorize
client_id = Google::Auth::ClientId.from_file CREDENTIALS_PATH
token_store = Google::Auth::Stores::FileTokenStore.new file: TOKEN_PATH
authorizer = Google::Auth::UserAuthorizer.new client_id, SCOPE, token_store
user_id = 'default'
credentials = authorizer.get_credentials user_id
create_credentials(authorizer, user_id) if credentials.nil?
end
def create_credentials(authorizer, user_id)
url = authorizer.get_authorization_url base_url: OOB_URI
puts 'Open the following URL in the browser and enter the ' \
"resulting code after authorization:\n" + url
code = STDIN.gets
authorizer.get_and_store_credentials_from_code user_id: user_id,
code: code,
base_url: OOB_URI
end
# copied from https://developers.google.com/drive/api/v3/about-auth
def download_csv
service = Google::Apis::DriveV3::DriveService.new
service.client_options.application_name = APPLICATION_NAME
service.authorization = authorize
service.export_file(FILE_ID, 'text/csv') #only downloading first sheet
end
puts download_csv
Google's quickstart example ( https://developers.google.com/drive/api/v3/quickstart/ruby ) referenced their API version 0.8, which is outdated. There is a newer version 0.11. When I installed that, everything worked flawlessly.

How to link OmniAuth identification (working) and Google Api Calls

After a day trying, testing, googling ... I ask for some help
I try to use Omniauth and Google Calendar. OmniAuth is working like a charm but I just can't link it with Google API
I think I read almost everything, I still get this error message :
dailyLimitExceededUnreg: Daily Limit for Unauthenticated Use Exceeded.
Continued use requires sign up.
It means that my calls are not properly 'connected' to my auth, which seems to be valid. My tokens are in databases but I this point I would like to login / identify / call and have something else that an error message.
client_id = Google::Auth::ClientId.from_file('.....googleusercontent.com.json')
scopes = ['userinfo.email,calendar']
token_store = Google::Auth::MyTokenStore.new()
authorizer = Google::Auth::WebUserAuthorizer.new(
client_id,
scopes,
token_store,
'http://localhost:3000'
)
# credentials = Google::Auth::UserAuthorizer.new( . # Anotheir test
# client_id,
# scopes,
# token_store,
# 'http://localhost:3000'
# )
#
# authorizer = credentials.get_credentials_from_code(
# GoogleUser.find(session[:user_id]) # I tested token ... notking worked
# )
calendar = Google::Apis::CalendarV3::CalendarService.new
calendar.authorization = authorizer
calendar_id = 'primary'
#result = calendar.list_events(calendar_id,
max_results: 10,
single_events: true,
order_by: 'startTime',
time_min: Time.now.iso8601)
and my token storage , I don't understand why but never called
class MyTokenStore
class << self
attr_accessor :default
end
def load(_id)
puts "********** load ************"
return GoogleUser.find(session[:user_id]).access_token
end
def store(_id, _token)
puts "********** store ************"
#user.access_token = _token
end
def delete(_id)
puts "********** delete ************"
#user.access_token = nil
end
end
end
end
For future readers : I took a different technic, after reading an excellent article here : http://gmile.me/simple-google-auth/
I followed it and use signet, it is working like a charm

Refresh Token Google Auth in Ruby

I have a code in ruby that calls to a google apps script that create a spreadsheet report. But this generation will take more than 20 minutes( a lot of info) but after a couple of minutes I receive the message Error calling API! execution expired.
I am using the following code:
OOB_URI = 'urn:ietf:wg:oauth:2.0:oob'
APPLICATION_NAME = 'GoogleService'
SCOPE = ['https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/spreadsheets']
CERT_PATH = Gem.loaded_specs['google-api-client'].full_gem_path+'/lib/cacerts.pem'
ENV['SSL_CERT_FILE'] = CERT_PATH
def self.authorize
dir_path = File.dirname(__FILE__)
client_id = Google::Auth::ClientId.from_file("#{dir_path}/client_secret.json")
token_store = Google::Auth::Stores::FileTokenStore.new(file: "#{dir_path}/google_service.yaml")
authorizer = Google::Auth::UserAuthorizer.new(
client_id, SCOPE, token_store)
user_id = 'default'
credentials = authorizer.get_credentials(user_id)
if credentials.nil?
url = authorizer.get_authorization_url(
base_url: OOB_URI)
p "Open the following URL in the browser and enter the " +
"resulting code after authorization"
p url
code = gets
credentials = authorizer.get_and_store_credentials_from_code(
user_id: user_id, code: code, base_url: OOB_URI)
end
credentials
end
def self.create_spreadsheet(environment, root_path)
id_spreadsheet = nil
scrip_id = 'MerlVgs0vXonNfecv1XNrXTOzpl-Lo'
# Initialize the API
service = Google::Apis::ScriptV1::ScriptService.new
service.client_options.application_name = APPLICATION_NAME
service.authorization = self.authorize
# Create an execution request object.
request = Google::Apis::ScriptV1::ExecutionRequest.new
request.function = 'createSpreadsheet'
request.parameters = [environment, timestamp]
begin
# Make the API request.
resp = service.run_script(scrip_id, request)
if resp.error
# The API executed, but the script returned an error.
# Extract the first (and only) set of error details. The values of this
# object are the script's 'errorMessage' and 'errorType', and an array of
# stack trace elements.
error = resp.error.details[0]
puts "Script error message: #{error['errorMessage']}"
if error['scriptStackTraceElements']
# There may not be a stacktrace if the script didn't start executing.
puts "Script error stacktrace:"
error['scriptStackTraceElements'].each do |trace|
puts "\t#{trace['function']}: #{trace['lineNumber']}"
end
end
else
id_spreadsheet = resp.response['result']
end
rescue Exception => e
# The API encountered a problem before the script started executing.
puts "Error calling API!"
puts e.message
end
id_spreadsheet
end
How should I modify my code in order that the token don't expire?
I will appreciate any guide to do this thanks
Regards,
Walter

Google Drive Ruby API won't return spreadsheet contents as csv

I am trying to write a Ruby script to automatically download a spreadsheet from Google Drive in csv format. The resulting file is always empty.
Interestingly enough, if I download it in .xlsx format, I get a file that almost works. (Excel will open the file, but must repair it first.)
Does anybody see a bug in my code? (The code below is the 'quickstart.rb' sample code provided by Google with a few modifications at the very end.)
require 'google/apis/drive_v3'
require 'googleauth'
require 'googleauth/stores/file_token_store'
require 'fileutils'
OOB_URI = 'urn:ietf:wg:oauth:2.0:oob'
APPLICATION_NAME = 'Drive API Ruby Quickstart'
CLIENT_SECRETS_PATH = 'client_secret.json'
CREDENTIALS_PATH = File.join(Dir.home, '.credentials',
"drive-ruby-quickstart.yaml")
SCOPE = Google::Apis::DriveV3::AUTH_DRIVE
##
# Ensure valid credentials, either by restoring from the saved credentials
# files or intitiating an OAuth2 authorization. If authorization is required,
# the user's default browser will be launched to approve the request.
#
# #return [Google::Auth::UserRefreshCredentials] OAuth2 credentials
def authorize
FileUtils.mkdir_p(File.dirname(CREDENTIALS_PATH))
client_id = Google::Auth::ClientId.from_file(CLIENT_SECRETS_PATH)
token_store = Google::Auth::Stores::FileTokenStore.new(file: CREDENTIALS_PATH)
authorizer = Google::Auth::UserAuthorizer.new(
client_id, SCOPE, token_store)
user_id = 'default'
credentials = authorizer.get_credentials(user_id)
if credentials.nil?
url = authorizer.get_authorization_url(
base_url: OOB_URI)
puts "Open the following URL in the browser and enter the " +
"resulting code after authorization"
puts url
code = gets
credentials = authorizer.get_and_store_credentials_from_code(
user_id: user_id, code: code, base_url: OOB_URI)
end
credentials
end
# Initialize the API
service = Google::Apis::DriveV3::DriveService.new
service.client_options.application_name = APPLICATION_NAME
service.authorization = authorize
response = service.list_files(page_size: 1000, fields: 'nextPageToken, files(id, name)')
budget_sheet = response.files.find {|file| file.name == 'Budget 2012'}
# Make sure the file is found
puts "#{budget_sheet.name} (#{budget_sheet.id})"
# This almost works. (File foo.xlsx contains data, but Excel complains that it has errors)
service.export_file(budget_sheet.id, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', download_dest: '/tmp/foo.xlsx')
# This does not work. It always generates an empty file
service.export_file(budget_sheet.id, 'text/csv', download_dest: '/tmp/foo.csv')
I'm not sure about the library you're using, but based on the sample in their documentation, the file resource should include exportLinks with the corresponding
download_url = file['exportLinks']['application/pdf']
Hope this helps!

Ruby 2.2.0 Google Drive OAuth refresh

I am trying to setup a command line backup app in ruby which accesses Google Drive using Oauth. I have set everything up in the account, created my Client ID and Secret. I seem to remember doing this before but cannot find the code. I used this answer before I think: Ruby google_drive gem oAuth2 saving
I have made a class to handle the Google Drive stuff then there is the applications main file which if given "hard" as an argument will do the initial setup where you have to copy and paste the link into the web browser in order to get a code which you can then paste into the CLI to get the initial access token and refresh token. This works and following these steps my list method (when not commented out) correctly lists everything in Google Drive. When I do the initial setup I am manually copying the access and refresh tokens to .access_token and .refresh_token, these are loading in the code fine.
What is not working, is refreshing the token which I understand I need to do otherwise it will expire, meaning I will have to go through the initial setup again which is obviously a pain (and not suitable for a CRON job). I am getting the following error when I run #auth.refresh!
/home/user/.rvm/gems/ruby-2.2.0/gems/signet-0.6.0/lib/signet/oauth_2/client.rb:947:in `fetch_access_token': Authorization failed. Server message: (Signet::AuthorizationError)
{
"error" : "invalid_grant"
}
from /home/user/.rvm/gems/ruby-2.2.0/gems/signet-0.6.0/lib/signet/oauth_2/client.rb:964:in `fetch_access_token!'
from /home/user/.rvm/gems/ruby-2.2.0/gems/signet-0.6.0/lib/signet/oauth_2/client.rb:981:in `refresh!'
from /home/user/Development/BackupBadger/Sources/Mechanisms/GoogleDriveMechanism.rb:62:in `connect'
from BackupBadger.rb:9:in `<main>'
I have had a look to see what it might be but am for the moment stuck on why this error is triggering when I can seemingly authenticate (since I can list all files on the drive)
My main file
$root=File.join('/home/user/Development/BackupBadger')
$sources=File.join($root,'Sources')
require File.join($sources,'Mechanisms','GoogleDriveMechanism.rb')
badger = BackupBadger::GoogleDriveMechanism.new
if ARGV[0] == "hard" then
badger.hard_setup
else
badger.connect
#badger.list
end
My class Google Drive
module BackupBadger
require 'google/api_client'
require 'google_drive'
require 'pp'
require File.join($sources,'Mechanism.rb')
class GoogleDriveMechanism
def initialize()
#client = Google::APIClient.new(
:application_name => 'Backup Badger',
:application_version => '0.0.1'
)
#access_token_path = File.join($root,'.access_token')
#refresh_token_path = File.join($root,'.refresh_token')
#auth = nil
#access_token = File.open(#access_token_path, "rb").read
#refresh_token = File.open(#refresh_token_path, "rb").read
#session = nil
#client_id = 'CLIENT_ID'
#client_secret = 'CLIENT_SECRET'
#redirect_uri = 'urn:ietf:wg:oauth:2.0:oob'
#scope = "https://www.googleapis.com/auth/drive " +
"https://spreadsheets.google.com/feeds/"
end
# Call this to do the initial setup, which requires pasting a url into a web broswer
def hard_setup
#auth = #client.authorization
#auth.client_id = #client_id
#auth.client_secret = #client_secret
#auth.scope = #scope
#auth.redirect_uri = #redirect_uri
print("1. Open this page:\n%s\n\n" % #auth.authorization_uri)
print("2. Enter the authorization code shown in the page: ")
#auth.code = $stdin.gets.chomp
#auth.fetch_access_token!
#access_token = #auth.access_token
system'clear'
print "Save your access token\n\n"
print #access_token
print "\nSave your refresh token\n\n"
print #auth.refresh_token
end
def connect
#auth = #client.authorization
#auth.client_id = #client_id
#auth.client_secret = #client_secret
#auth.scope = #scope
#auth.redirect_uri = #redirect_uri
#auth.refresh_token = #refresh_token
puts #access_token
puts #refresh_token
# Error is here
#auth.refresh!
#refresh_token = #auth.refresh_token
#access_token = #auth.access_token
File.write(#refresh_token_path, #refresh_token) if #refresh_token
File.write(#access_token_path, #access_token) if #access_token
puts #access_token
puts #refresh_token
end
def list
#session = GoogleDrive.login_with_oauth(#access_token)
for file in #session.files
p file.title
end
end
end
end
If the tl;dr is simply "how do I use a refresh token to get a new access token", then the answer is https://developers.google.com/accounts/docs/OAuth2WebServer#refresh
I won't paste the code coz it's likely to change, but in essence you simply POST to a Google URL and the JSON response is a shiny new access token.

Resources