Commit f6e544b
Changed files (4)
app/jobs/download_from_drive_job.rb
@@ -1,11 +1,29 @@
class DownloadFromDriveJob < ActiveJob::Base
queue_as :default
+ require 'google/api_client'
+
+ CLIENT_ID = "241601222378-kscpfqhpmc6059704mfcq8ckcp799dvn.apps.googleusercontent.com"
+ CLIENT_SECRET = ENV['GOOGLE_CLIENT_SECRET']
+ REDIRECT_URI = ENV['GOOGLE_REDIRECT_URI']
+ SCOPES = [
+ 'https://www.googleapis.com/auth/drive',
+ 'https://www.googleapis.com/auth/drive.install',
+ 'https://www.googleapis.com/auth/drive.file',
+ 'https://www.googleapis.com/auth/drive.readonly',
+ 'https://www.googleapis.com/auth/drive.apps.readonly',
+ ]
def perform(params)
puts params.inspect
- Dir.mktmpdir do |dir|
- download_path = File.join(dir, params[:title])
- `wget -O #{download_path} #{params[:downloadUrl]}`
- end
+
+ drive = GoogleDrive.new
+ drive.build_client(drive.get_credentials())
+
+ #Dir.mktmpdir do |dir|
+ #download_path = File.join(dir, params[:title])
+ #`wget -O #{download_path} #{params[:downloadUrl]}`
+ #end
end
end
+
+
app/models/google_drive.rb
@@ -0,0 +1,183 @@
+##
+# Error raised when an error occurred while retrieving credentials.
+class GetCredentialsError < StandardError
+ ##
+ # Initialize a NoRefreshTokenError instance.
+ #
+ # @param [String] authorize_url
+ # Authorization URL to redirect the user to in order to in order to request
+ # offline access.
+ def initialize(authorization_url)
+ @authorization_url = authorization_url
+ end
+
+ def authorization_url=(authorization_url)
+ @authorization_url = authorization_url
+ end
+
+ def authorization_url
+ return @authorization_url
+ end
+end
+
+##
+# Error raised when a code exchange has failed.
+class CodeExchangeError < GetCredentialsError
+end
+
+##
+# Error raised when no refresh token has been found.
+class NoRefreshTokenError < GetCredentialsError
+end
+
+##
+# Error raised when no user ID could be retrieved.
+class NoUserIdError < StandardError
+end
+
+class GoogleDrive
+ ##
+ # Retrieved stored credentials for the provided user ID.
+ #
+ # @param [String] user_id
+ # User's ID.
+ # @return [Signet::OAuth2::Client]
+ # Stored OAuth 2.0 credentials if found, nil otherwise.
+ def get_stored_credentials(user_id)
+ raise NotImplementedError, 'get_stored_credentials is not implemented.'
+ end
+
+ ##
+ # Store OAuth 2.0 credentials in the application's database.
+ #
+ # @param [String] user_id
+ # User's ID.
+ # @param [Signet::OAuth2::Client] credentials
+ # OAuth 2.0 credentials to store.
+ def store_credentials(user_id, credentials)
+ raise NotImplementedError, 'store_credentials is not implemented.'
+ end
+
+ ##
+ # Exchange an authorization code for OAuth 2.0 credentials.
+ #
+ # @param [String] auth_code
+ # Authorization code to exchange for OAuth 2.0 credentials.
+ # @return [Signet::OAuth2::Client]
+ # OAuth 2.0 credentials.
+ def exchange_code(authorization_code)
+ client = Google::APIClient.new
+ client.authorization.client_id = CLIENT_ID
+ client.authorization.client_secret = CLIENT_SECRET
+ client.authorization.code = authorization_code
+ client.authorization.redirect_uri = REDIRECT_URI
+
+ begin
+ client.authorization.fetch_access_token!
+ return client.authorization
+ rescue Signet::AuthorizationError
+ raise CodeExchangeError.new(nil)
+ end
+ end
+
+ ##
+ # Send a request to the UserInfo API to retrieve the user's information.
+ #
+ # @param [Signet::OAuth2::Client] credentials
+ # OAuth 2.0 credentials to authorize the request.
+ # @return [Google::APIClient::Schema::Oauth2::V2::Userinfo]
+ # User's information.
+ def get_user_info(credentials)
+ client = Google::APIClient.new
+ client.authorization = credentials
+ oauth2 = client.discovered_api('oauth2', 'v2')
+ result = client.execute!(:api_method => oauth2.userinfo.get)
+ user_info = nil
+ if result.status == 200
+ user_info = result.data
+ else
+ puts "An error occurred: #{result.data['error']['message']}"
+ end
+ if user_info != nil && user_info.id != nil
+ return user_info
+ end
+ raise NoUserIdError, 'Unable to retrieve the e-mail address.'
+ end
+
+ ##
+ # Retrieve authorization URL.
+ #
+ # @param [String] email_address
+ # User's e-mail address.
+ # @param [String] state
+ # State for the authorization URL.
+ # @return [String]
+ # Authorization URL to redirect the user to.
+ def get_authorization_url(email_address, state)
+ client = Google::APIClient.new
+ client.authorization.client_id = CLIENT_ID
+ client.authorization.redirect_uri = REDIRECT_URI
+ client.authorization.scope = SCOPES
+
+ return client.authorization.authorization_uri(
+ :approval_prompt => :force,
+ :access_type => :offline,
+ :user_id => email_address,
+ :state => state
+ ).to_s
+ end
+
+ ##
+ # Retrieve credentials using the provided authorization code.
+ #
+ # This function exchanges the authorization code for an access token and queries
+ # the UserInfo API to retrieve the user's e-mail address.
+ # If a refresh token has been retrieved along with an access token, it is stored
+ # in the application database using the user's e-mail address as key.
+ # If no refresh token has been retrieved, the function checks in the application
+ # database for one and returns it if found or raises a NoRefreshTokenError
+ # with an authorization URL to redirect the user to.
+ #
+ # @param [String] auth_code
+ # Authorization code to use to retrieve an access token.
+ # @param [String] state
+ # State to set to the authorization URL in case of error.
+ # @return [Signet::OAuth2::Client]
+ # OAuth 2.0 credentials containing an access and refresh token.
+ def get_credentials(authorization_code, state)
+ email_address = ''
+ begin
+ credentials = exchange_code(authorization_code)
+ user_info = get_user_info(credentials)
+ email_address = user_info.email
+ user_id = user_info.id
+ if credentials.refresh_token != nil
+ store_credentials(user_id, credentials)
+ return credentials
+ else
+ credentials = get_stored_credentials(user_id)
+ if credentials != nil && credentials.refresh_token != nil
+ return credentials
+ end
+ end
+ rescue CodeExchangeError => error
+ print 'An error occurred during code exchange.'
+ # Drive apps should try to retrieve the user and credentials for the current
+ # session.
+ # If none is available, redirect the user to the authorization URL.
+ error.authorization_url = get_authorization_url(email_address, state)
+ raise error
+ rescue NoUserIdError
+ print 'No user ID could be retrieved.'
+ end
+ authorization_url = get_authorization_url(email_address, state)
+ raise NoRefreshTokenError.new(authorization_url)
+ end
+
+ def build_client(credentials)
+ client = Google::APIClient.new
+ client.authorization = credentials
+ client = client.discovered_api('drive', 'v2')
+ client
+ end
+end
Gemfile
@@ -38,6 +38,7 @@ gem 'chartkick'
gem 'groupdate'
gem 'dalli'
gem 'sequel'
+gem 'google-api-client'
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
Gemfile.lock
@@ -36,7 +36,12 @@ GEM
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
+ addressable (2.3.8)
arel (6.0.0)
+ autoparse (0.3.3)
+ addressable (>= 2.3.1)
+ extlib (>= 0.9.15)
+ multi_json (>= 1.0.0)
axiom-types (0.1.1)
descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0)
@@ -113,11 +118,14 @@ GEM
equalizer (0.0.11)
erubis (2.7.0)
execjs (2.5.2)
+ extlib (0.9.16)
factory_girl (4.5.0)
activesupport (>= 3.0.0)
factory_girl_rails (4.5.0)
factory_girl (~> 4.5.0)
railties (>= 3.0.0)
+ faraday (0.9.1)
+ multipart-post (>= 1.2, < 3)
fast_stack (0.1.0)
rake
rake-compiler
@@ -131,6 +139,24 @@ GEM
sass (>= 3.3.0, < 3.5)
globalid (0.3.5)
activesupport (>= 4.1.0)
+ google-api-client (0.8.6)
+ activesupport (>= 3.2)
+ addressable (~> 2.3)
+ autoparse (~> 0.3)
+ extlib (~> 0.9)
+ faraday (~> 0.9)
+ googleauth (~> 0.3)
+ launchy (~> 2.4)
+ multi_json (~> 1.10)
+ retriable (~> 1.4)
+ signet (~> 0.6)
+ googleauth (0.4.1)
+ faraday (~> 0.9)
+ jwt (~> 1.4)
+ logging (~> 2.0)
+ memoist (~> 0.12)
+ multi_json (= 1.11)
+ signet (~> 0.6)
groupdate (2.4.0)
activesupport (>= 3)
highline (1.7.2)
@@ -154,11 +180,19 @@ GEM
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (1.8.3)
+ jwt (1.5.0)
+ launchy (2.4.3)
+ addressable (~> 2.3)
libv8 (3.16.14.7)
+ little-plugger (1.1.3)
+ logging (2.0.0)
+ little-plugger (~> 1.1)
+ multi_json (~> 1.10)
loofah (2.0.2)
nokogiri (>= 1.5.9)
mail (2.6.3)
mime-types (>= 1.16, < 3)
+ memoist (0.12.0)
meta_request (0.3.4)
callsite (~> 0.0, >= 0.0.11)
rack-contrib (~> 1.1)
@@ -167,6 +201,7 @@ GEM
mini_portile (0.6.2)
minitest (5.7.0)
multi_json (1.11.0)
+ multipart-post (2.0.0)
net-scp (1.2.1)
net-ssh (>= 2.6.5)
net-ssh (2.9.2)
@@ -232,6 +267,7 @@ GEM
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 3.0)
netrc (~> 0.7)
+ retriable (1.4.1)
rspec-core (3.2.3)
rspec-support (~> 3.2.0)
rspec-expectations (3.2.1)
@@ -264,6 +300,12 @@ GEM
json (~> 1.7, >= 1.7.7)
rdoc (~> 4.0)
sequel (4.23.0)
+ signet (0.6.1)
+ addressable (~> 2.3)
+ extlib (~> 0.9)
+ faraday (~> 0.9)
+ jwt (~> 1.5)
+ multi_json (~> 1.10)
simplecov (0.10.0)
docile (~> 1.1.0)
json (~> 1.8)
@@ -351,6 +393,7 @@ DEPENDENCIES
flamegraph
foreman
foundation-rails
+ google-api-client
groupdate
i18n-tasks
jbuilder (~> 2.0)