googledriver 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []