googledriver 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3faab8a54083aaa61627247349a97ef48b725687
4
+ data.tar.gz: da7832b7385d3681c243e279c10343550bb15d47
5
+ SHA512:
6
+ metadata.gz: 03675c73e61060c1515e12a4ee1ad4521e60aaf98801965e2bd0871a61432aeb3b0090fe4a4f8e4b25fd228fe6774774ed9c1552c408a136b713ca06b9979f58
7
+ data.tar.gz: 24a986d20f2a878456bf36a21f7165ce225a1e54bcc632c13e68e95deabf733cc76f8167da6fdceb3521afc47de1282c9c7cee32dc462fa7adcba1a2c514bb9d
@@ -0,0 +1,90 @@
1
+ module Googledriver
2
+ # Authorizes a user to Google Drive and generates access tokens
3
+ class Authorizer
4
+ # Authorization scope which only allows program to manipulate files it
5
+ # created.
6
+ SCOPE = 'https://www.googleapis.com/auth/drive.file'.freeze
7
+
8
+ # Standard redirect uri for authorization.
9
+ REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'.freeze # standard redirect uri
10
+
11
+ # The access token created during authorization which is needed to perform
12
+ # uploads to Google Drive.
13
+ attr_reader :access_token
14
+
15
+ # The lifetime of an access token in seconds which dictates how often a
16
+ # token needs to be refreshed.
17
+ attr_reader :token_lifetime
18
+
19
+ # The time of birth of the current access token.
20
+ attr_reader :token_tob
21
+
22
+ # Constructs a new Authorizer by reading client data from a file and
23
+ # creating a REST resource.
24
+ def initialize(client_secrets_path)
25
+ @client_secrets_path = client_secrets_path
26
+ update_client_data
27
+ create_refresh_resource
28
+ end
29
+
30
+ # Generates an authorization url for the user in order to obtain an initial
31
+ # refresh token.
32
+ def create_refresh_token
33
+ client = OAuth2::Client.new(@client_id, @client_secret,
34
+ authorize_url: '/o/oauth2/auth',
35
+ token_url: '/o/oauth2/token',
36
+ site: 'https://accounts.google.com')
37
+ url = client.auth_code.authorize_url(redirect_uri: REDIRECT_URI,
38
+ scope: SCOPE, access_type: 'offline')
39
+ puts "Open the following link and follow the onscreen instructions #{url}"
40
+ code = gets
41
+ token = client.auth_code.get_token(code, redirect_uri: REDIRECT_URI)
42
+ @refresh_token = token.refresh_token
43
+ end
44
+
45
+ # Refreshes the access token and updates resources appropriately.
46
+ def refresh_access_token
47
+ @token_tob = Time.now
48
+
49
+ begin
50
+ refresh = @refresh_manager.post(
51
+ build_refresh_payload
52
+ )
53
+ rescue StandardError => error
54
+ warn "#{error}; METHOD #{__callee__}; RESOURCE #{@refresh_manager}"
55
+ retry
56
+ end
57
+
58
+ update_refresh_data(refresh)
59
+ end
60
+
61
+ private
62
+
63
+ def update_client_data
64
+ file_content = File.read(@client_secrets_path)
65
+ client_data = JSON.parse(file_content)['installed']
66
+ @client_id = client_data['client_id']
67
+ @client_secret = client_data['client_secret']
68
+ end
69
+
70
+ def create_refresh_resource
71
+ @refresh_manager = RestClient::Resource.new(
72
+ 'https://www.googleapis.com/oauth2/v4/token',
73
+ headers: { 'Content-Type' => 'application/x-www-form-urlencoded' }
74
+ )
75
+ end
76
+
77
+ def build_refresh_payload
78
+ payload = { 'refresh_token' => @refresh_token, 'client_id' => @client_id,
79
+ 'client_secret' => @client_secret,
80
+ 'grant_type' => 'refresh_token' }
81
+ payload
82
+ end
83
+
84
+ def update_refresh_data(refresh_data)
85
+ processed_data = JSON.parse(refresh_data)
86
+ @token_lifetime = processed_data['expires_in']
87
+ @access_token = processed_data['access_token']
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,194 @@
1
+ module Googledriver
2
+
3
+ # Uploads a filesystem to Google Drive and saves file ids
4
+ class Uploader
5
+ # Constructs a new Uploader by building an empty hash for file ids and
6
+ # making an Authorizer object to handle the creation of REST resources.
7
+ def initialize(client_secrets_path)
8
+ @file_ids = {}
9
+ @authorizer = Googledriver::Authorizer.new(client_secrets_path)
10
+ @authorizer.create_refresh_token
11
+ @authorizer.refresh_access_token
12
+ @access_token = @authorizer.access_token
13
+ @token_tob = @authorizer.token_tob
14
+ create_manager_resource
15
+ @token_lifetime = @authorizer.token_lifetime
16
+ create_uploader_resource
17
+ end
18
+
19
+ # Recursively uploads a local filesystem specified by a file path to a given
20
+ # folder in Google Drive.
21
+ def upload_filesystem(directory: '', upload_dest: 'root')
22
+ directory = "#{directory}/" unless directory[-1] == '/'
23
+
24
+ Dir[directory + '*'].each do |object|
25
+ if File.directory?(object)
26
+ folder_name = File.basename(object)
27
+ folder_id = upload_folder(folder_name, location: upload_dest)
28
+ upload_filesystem(upload_dest: folder_id,
29
+ directory: "#{directory}#{folder_name}/")
30
+ else
31
+ file_name = File.basename(object)
32
+ file_id = upload_file(object, file_name, location: upload_dest)
33
+ @file_ids[object] = file_id
34
+ end
35
+ end
36
+ end
37
+
38
+ # Uploads a local folder specified by a file path to a given folder in
39
+ # Google Drive.
40
+ def upload_folder(folder_name, location: 'root')
41
+ begin
42
+ update_refresh_token if refresh_due?
43
+ upload = @drive_manager.post(
44
+ { 'name' => folder_name,
45
+ 'mimeType' => 'application/vnd.google-apps.folder' }.to_json
46
+ )
47
+ rescue StandardError => error
48
+ warn "#{error}; METHOD #{__callee__}; RESOURCE #{folder_name}"
49
+ retry
50
+ end
51
+
52
+ folder_id = JSON.parse(upload)['id']
53
+
54
+ if location != 'root'
55
+ begin
56
+ update_refresh_token if refresh_due?
57
+ @drive_manager[folder_id + '?addParents=' + location +
58
+ '&removeParents=root&alt=json'].patch(
59
+ { 'uploadType' => 'resumable' }.to_json
60
+ )
61
+ rescue StandardError => error
62
+ warn "#{error}; METHOD #{__callee__}; RESOURCE #{folder_name}"
63
+ retry
64
+ end
65
+ end
66
+
67
+ folder_id
68
+ end
69
+
70
+ # Uploads a local file specified by a file path to a given folder in
71
+ # Google Drive.
72
+ def upload_file(file_path, file_name, location: 'root')
73
+ begin
74
+ payload = File.open(file_path)
75
+ rescue StandardError => error
76
+ warn "#{error}; METHOD #{__callee__}; RESOURCE #{file_path}"
77
+ return
78
+ end
79
+
80
+ begin
81
+ update_refresh_token if refresh_due?
82
+ upload = @drive_uploader.post(
83
+ payload
84
+ )
85
+ rescue StandardError => error
86
+ warn "#{error}; METHOD #{__callee__}; RESOURCE #{file_path}"
87
+ retry
88
+ end
89
+
90
+ file_id = JSON.parse(upload)['id']
91
+ @file_ids[file_path] = file_id
92
+
93
+ begin
94
+ update_refresh_token if refresh_due?
95
+ @drive_manager[file_id + '?addParents=' + location +
96
+ '&removeParents=root&alt=json'].patch(
97
+ { 'uploadType' => 'resumable',
98
+ 'name' => file_name }.to_json
99
+ )
100
+ rescue StandardError => error
101
+ warn "#{error}; METHOD #{__callee__}; RESOURCE #{file_path}"
102
+ retry
103
+ end
104
+
105
+ payload.close
106
+ file_id
107
+ end
108
+
109
+ # Saves the file ids hash to a json file in the working directory.
110
+ def archive_file_ids
111
+ archive = File.open('drive_file_ids.json', 'w')
112
+ File.write('drive_file_ids.json', @file_ids.to_json)
113
+ archive.close
114
+ end
115
+
116
+ # Returns the metadata of a given file.
117
+ def obtain_file_metadata(file_id)
118
+ begin
119
+ metadata = @drive_manager[file_id].get
120
+ rescue StandardError => error
121
+ warn "#{error}; METHOD #{__callee__}; RESOURCE #{file_path}"
122
+ return
123
+ end
124
+
125
+ metadata = JSON.parse(metadata)
126
+ metadata
127
+ end
128
+
129
+ # Updates a piece of metadata for a given file.
130
+ def update_file_metadata(file_id, element, new_data)
131
+ payload = { 'uploadType' => 'resumable', element => new_data }.to_json
132
+
133
+ begin
134
+ update = @drive_manager[file_id].patch(
135
+ payload
136
+ )
137
+ rescue StandardError => error
138
+ warn "#{error}; METHOD #{__callee__}; RESOURCE #{file_path}"
139
+ return
140
+ end
141
+
142
+ update
143
+ end
144
+
145
+ # Shares a given file with an individual or group email address.
146
+ def update_file_permission(file_id, email)
147
+ payload = { 'role' => 'writer', 'type' => 'group',
148
+ 'emailAddress' => email }.to_json
149
+
150
+ begin
151
+ update = @drive_manager[file_id + '/permissions'].post(
152
+ payload
153
+ )
154
+ rescue StandardError => error
155
+ warn "#{error}; METHOD #{__callee__}; RESOURCE #{file_path}"
156
+ return
157
+ end
158
+
159
+ update
160
+ end
161
+
162
+ private
163
+
164
+ def create_manager_resource
165
+ @drive_manager = RestClient::Resource.new(
166
+ 'https://www.googleapis.com/drive/v3/files/',
167
+ headers: { 'Authorization' => "Bearer #{@access_token}",
168
+ 'Content-Type' => 'application/json' }
169
+ )
170
+ end
171
+
172
+ def create_uploader_resource
173
+ @drive_uploader = RestClient::Resource.new(
174
+ 'https://www.googleapis.com/upload/drive/v3/files',
175
+ headers: { 'Authorization' => "Bearer #{@access_token}" }
176
+ )
177
+ end
178
+
179
+ def update_refresh_token
180
+ @authorizer.refresh_access_token
181
+ @access_token = @authorizer.access_token
182
+ @token_lifetime = @authorizer.token_lifetime
183
+ @token_tob = @authorizer.token_tob
184
+ create_manager_resource
185
+ create_uploader_resource
186
+ end
187
+
188
+ def refresh_due?
189
+ token_timer = Time.now - @token_tob
190
+ return false unless token_timer > @token_lifetime * 0.9
191
+ true
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,13 @@
1
+ require 'json'
2
+ require 'oauth2'
3
+ require 'rest-client'
4
+ require File.dirname(__FILE__) + '/googledriver/uploader.rb'
5
+ require File.dirname(__FILE__) + '/googledriver/authorizer.rb'
6
+
7
+ # This module is used to authorize a Google account and upload files to its
8
+ # Drive. It also supports the management of uploaded files through changing the
9
+ # permissions and metadata of a file. Before use, a secrets file must be
10
+ # downloaded by following the wizard here
11
+ # https://console.developers.google.com/start/api?id=drive. See README.md for
12
+ # usage guide.
13
+ module Googledriver; end
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: googledriver
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Peter Giordano
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-08-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.1'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 2.1.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '2.1'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 2.1.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: oauth2
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.4'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 1.4.0
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '1.4'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 1.4.0
53
+ - !ruby/object:Gem::Dependency
54
+ name: rest-client
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '2.0'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 2.0.2
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '2.0'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 2.0.2
73
+ - !ruby/object:Gem::Dependency
74
+ name: rdoc
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: '5.1'
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 5.1.0
83
+ type: :development
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '5.1'
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 5.1.0
93
+ - !ruby/object:Gem::Dependency
94
+ name: rubocop
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: 0.49.1
100
+ type: :development
101
+ prerelease: false
102
+ version_requirements: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - "~>"
105
+ - !ruby/object:Gem::Version
106
+ version: 0.49.1
107
+ description:
108
+ email: pgg505@york.ac.uk
109
+ executables: []
110
+ extensions: []
111
+ extra_rdoc_files: []
112
+ files:
113
+ - lib/googledriver.rb
114
+ - lib/googledriver/authorizer.rb
115
+ - lib/googledriver/uploader.rb
116
+ homepage: http://rubygems.org/gems/googledriver
117
+ licenses:
118
+ - MIT
119
+ metadata: {}
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: 2.0.0
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ requirements: []
135
+ rubyforge_project:
136
+ rubygems_version: 2.5.1
137
+ signing_key:
138
+ specification_version: 4
139
+ summary: Authorize a Google account and upload files to its Drive.
140
+ test_files: []