googledrive-easy 0.0.0 → 0.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab73da055618c822a57f404e489972a67c828310caaae944f6229cf8cf400874
4
- data.tar.gz: 6f341e29ce87d8aa06a614840ab53cc2939b3864cab9dfafe38bc0fc1b254830
3
+ metadata.gz: c9ca051449095281c00cdd84d3b13ca6f654d9e96737ae2efad24eb438a60ae0
4
+ data.tar.gz: 5210793c5263ef128f9824f3193226402cebbee179b822c12f6682904e5de1f9
5
5
  SHA512:
6
- metadata.gz: 77d25d52d97237a7edf2b23d9fd09a9e2497f02cc184f1133f812a8b8bf0df3b5a2234d9b3d48b22db563da0e76a793f0b6acc12ea23d019996f9621971ff9fb
7
- data.tar.gz: dc6ce34cf3e3a1b25ef24c121ce56c728eb02f23acb750a17aa03a4330dba8d20dc72037f67731062133884ffa7e6d4dba27747958267ef137b23511e7d93b9a
6
+ metadata.gz: 989211006d67eaaf058804aa38f5caa4e371d452c7b5031ccc8c0a3afcc9f72cc776a1007e136862e6c1032c5780d1599ce373b0d926bf551b3033b14403a3ce
7
+ data.tar.gz: 4ff67bff9c464f7efbdef28022ebc4e9a218524014149ea16b965a789e248842846f986ae15498c438e35b4970fe9c32a44f0f17896b0af9605214966960a7d1
@@ -1,19 +1,19 @@
1
- #https://stackoverflow.com/questions/6449373/wildcard-string-matching-in-ruby
1
+ # https://stackoverflow.com/questions/6449373/wildcard-string-matching-in-ruby
2
2
 
3
3
  # wildcard search. For file matching.
4
- class Wildcard
5
- def self.parse_to_regex(str)
6
- escaped = Regexp.escape(str).gsub('\*','.*?')
7
- Regexp.new "^#{escaped}$", Regexp::IGNORECASE
8
- end
4
+ class GoogleDriveWildcard
5
+ def self.parse_to_regex(str)
6
+ escaped = Regexp.escape(str).gsub('\*','.*?')
7
+ Regexp.new "^#{escaped}$", Regexp::IGNORECASE
8
+ end
9
9
 
10
- def initialize(str)
11
- @regex = self.class.parse_to_regex str
12
- end
10
+ def initialize(str)
11
+ @regex = self.class.parse_to_regex str
12
+ end
13
13
 
14
- def =~(str)
15
- !!(str =~ @regex)
16
- end
14
+ def =~(str)
15
+ !!(str =~ @regex)
17
16
  end
17
+ end
18
18
 
19
19
 
@@ -94,9 +94,9 @@ class GoogleDrive
94
94
  end
95
95
 
96
96
  # expected values in JSON
97
- @client_id = api_hash["DRIVEAPI_CLIENT_ID"]
98
- @client_secret = api_hash["DRIVEAPI_CLIENT_SECRET"]
99
- @refresh_token = api_hash["DRIVEAPI_REFRESH_TOKEN"]
97
+ @client_id = api_hash["CLIENT_ID"]
98
+ @client_secret = api_hash["CLIENT_SECRET"]
99
+ @refresh_token = api_hash["REFRESH_TOKEN"]
100
100
 
101
101
  if !@client_id || !@client_secret || !@refresh_token
102
102
  if @raise_error; raise "Not all tokens provided." end
@@ -106,12 +106,8 @@ class GoogleDrive
106
106
  return generate_access_token()
107
107
  end
108
108
 
109
- # def self.hi
110
- # puts "Hello world!"
111
- # end
112
109
 
113
110
  def generate_access_token
114
-
115
111
  # Refresh auth token from google_oauth2 and then requeue the job.
116
112
  # https://stackoverflow.com/questions/12792326/how-do-i-refresh-my-google-oauth2-access-token-using-my-refresh-token
117
113
  options = {
@@ -135,4 +131,299 @@ class GoogleDrive
135
131
  return true
136
132
  end
137
133
 
138
- end
134
+ # parentfolderid: "root" gets the root directory. Not all folders are under the root. Has to do with permissions
135
+ # and how Google Drive works.
136
+ def get_all_files(justfiles: false, justfolders: false, parentfolderid: nil, name: nil)
137
+
138
+ # Number of files/directories to be returned each call to /files.
139
+ # multiple page sizes are handled with the pageToken return value.
140
+ # 100 is default from google.
141
+ pageSize = 100
142
+
143
+ files = [ ]
144
+ nextPageToken = nil
145
+ loop do
146
+ headers = {
147
+ "GData-Version" => "3.0",
148
+ "Authorization" => "Bearer #{@access_token}"
149
+ }
150
+
151
+ url = "https://www.googleapis.com/drive/v3/files"
152
+ url = url + "?pageSize=#{pageSize}"
153
+
154
+ # If a query, it must be appended to all URL
155
+ # From SO: https://stackoverflow.com/questions/62069155/how-to-filter-google-drive-api-v3-mimetype
156
+ #var query = "('GoogleDriveFolderKey01' in parents or 'GoogleDriveFolderKey02' in parents) and trashed = false and mimeType = 'application/vnd.google-apps.folder'"
157
+
158
+ # default query is non-trashed folders
159
+ query = "(trashed = false)"
160
+
161
+ if justfiles && !justfolders
162
+ query = query + " and (mimeType != 'application/vnd.google-apps.folder')"
163
+ end
164
+
165
+ if justfolders && !justfiles
166
+ query = query + " and (mimeType = 'application/vnd.google-apps.folder')"
167
+ end
168
+
169
+ if parentfolderid # parent folder has to be surrounded by single quotes in query
170
+ query = query + " and ('#{parentfolderid}' in parents)"
171
+ end
172
+
173
+ if name # filename has to be surrounded by single quotes in query
174
+ if name =~ /\*/ # if name contains wildcard
175
+ query = query + " and (name contains '#{name}')"
176
+ else
177
+ query = query + " and (name = '#{name}')"
178
+ end
179
+ end
180
+
181
+ url = url + "&q=" + URI.escape(query)
182
+
183
+ if nextPageToken
184
+ url = url + "&pageSize=1&pageToken=#{nextPageToken}"
185
+ end
186
+
187
+ response = HTTParty.get(url, :headers => headers)
188
+
189
+ if response.code == 404 # file not found, not an error
190
+ break
191
+ elsif response.code != 200
192
+ if @raise_error; raise "Non-200 HTTP error code: #{response.code}: " + response.parsed_response.inspect end
193
+ return false
194
+ end
195
+
196
+ if response.parsed_response.has_key?("nextPageToken")
197
+ nextPageToken = response.parsed_response["nextPageToken"]
198
+ else
199
+ nextPageToken = nil
200
+ end
201
+
202
+ if !response.parsed_response.has_key?("files")
203
+ if @raise_error; raise "Required key not in response: 'files'." end
204
+ return false
205
+ end
206
+
207
+ # clean google drive response and only return relavent information.
208
+ response.parsed_response["files"].each do |gd_file_entry|
209
+ # each entry has the following fields: name, kind, mimeType, id
210
+ file_hash = { }
211
+ file_hash[:name] = gd_file_entry["name"]
212
+ file_hash[:id] = gd_file_entry["id"]
213
+ file_hash[:isfolder] = false
214
+ if gd_file_entry["mimeType"] == 'application/vnd.google-apps.folder'
215
+ file_hash[:isfolder] = true
216
+ end
217
+
218
+ files << file_hash
219
+ end
220
+
221
+ break if nextPageToken.nil?
222
+ end
223
+
224
+ # we have additional processing to do it a wildcard character was passed. Because Google Drive "contains" returns all portions of it.
225
+ # so we need to filter here
226
+ if name =~ /\*/ # if name contains wildcard
227
+ ret_files = [ ]
228
+ files.each do |file|
229
+ if GoogleDriveWildcard.new(name) =~ file[:name]
230
+ ret_files << file
231
+ end
232
+ end
233
+ return ret_files
234
+ else
235
+ return files
236
+ end
237
+ end
238
+
239
+ # returns all files by default in all folders
240
+ def find_files(name = "*", parentfolderid: nil)
241
+ return get_all_files(justfiles: true, parentfolderid: parentfolderid, name: name)
242
+ end
243
+
244
+
245
+ def get_file_info(fileid)
246
+
247
+ headers = {
248
+ "GData-Version" => "3.0",
249
+ "Authorization" => "Bearer #{@access_token}"
250
+ }
251
+
252
+ url = "https://www.googleapis.com/drive/v3/files/#{fileid}"
253
+ response = HTTParty.get(url, :headers => headers)
254
+
255
+ if response.code == 404 # not found. Could be normal
256
+ #puts "404 not found"
257
+ return false
258
+ elsif response.code != 200
259
+ if @raise_error; raise "Non-200 HTTP error code: #{response.code}: " + response.parsed_response.inspect end
260
+ return false
261
+ end
262
+
263
+ if !response.parsed_response.has_key?("kind")
264
+ return false
265
+ elsif response.parsed_response["kind"] != "drive#file"
266
+ return false
267
+ elsif !response.parsed_response.has_key?("name")
268
+ return false
269
+ end
270
+
271
+ # returns the following keys: kind, id, name, mimeType
272
+ file_hash = { }
273
+ file_hash[:name] = response.parsed_response["name"]
274
+ file_hash[:id] = response.parsed_response["id"]
275
+ file_hash[:isfolder] = false
276
+ if response.parsed_response["mimeType"] == 'application/vnd.google-apps.folder'
277
+ file_hash[:isfolder] = true
278
+ end
279
+
280
+ return file_hash
281
+ end
282
+
283
+ def find_directory_id(directory_name, parentfolderid: nil)
284
+
285
+ file_list = get_all_files(justfolders: true, name: directory_name, parentfolderid: parentfolderid)
286
+
287
+ if !file_list || (file_list.count == 0)
288
+ if @raise_error; raise "Directory not found." end
289
+ return false
290
+ end
291
+
292
+ return file_list.first[:id]
293
+ end
294
+
295
+ def upload_file(file, directory_id = nil)
296
+
297
+ file_basename = File.basename(file)
298
+
299
+ # see if file exists on Drive
300
+ file_list = driveapi_get_all_files(access_token, justfiles: true, parentfolderid: directory_id, name: file_basename)
301
+ if file_list.count > 0
302
+ if @raise_error; raise "ERROR: File '#{file_basename}' already exists." end
303
+ return false
304
+ end
305
+
306
+ metadata = "name : '#{file_basename}'"
307
+
308
+ if !directory_id.nil? # if directory if specified, add it to the parent
309
+ metadata = metadata + ", parents : [ '#{directory_id}' ]"
310
+ end
311
+
312
+ metadata = "{#{metadata}}" # wrap metadata in braces
313
+
314
+ # Upload with CURL. HTTPParty and RestClient seem to be incompatible.
315
+ cmd = "curl -X POST -L " +
316
+ "--silent " +
317
+ "-H \"Authorization: Bearer #{@access_token}\" " +
318
+ "-F \"metadata=#{metadata};type=application/json;charset=UTF-8\" " +
319
+ "-F \"file=@#{file};\" " +
320
+ "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart"
321
+
322
+ # puts "cmd:\n#{cmd}"
323
+
324
+ response = `#{cmd} 2>/dev/null`
325
+ exit_status = $?.exitstatus
326
+
327
+ if exit_status != 0
328
+ if @raise_error; raise "CURL existed with non-0 error code: #{exit_status}." end
329
+ return false
330
+ end
331
+
332
+ begin
333
+ response = JSON.load(response)
334
+ rescue => e
335
+ if @raise_error; raise "CURL response is not in valid JSON." end
336
+ return false
337
+ end
338
+
339
+ # example of response.
340
+ # {
341
+ # "kind": "drive#file",
342
+ # "id": "1Zw9YD3TXci3Ja_wU0g7f30DHpsEbk2zf",
343
+ # "name": "ruby-3.1.0.tar.gz",
344
+ # "mimeType": "application/gzip"
345
+ # }
346
+
347
+
348
+ # check that name = filename
349
+ # check that kind = drive#file
350
+ if !response.key?("name") # name key does not exist
351
+ if @raise_error; raise "no name key specified in response." end
352
+ return false
353
+ elsif !response.key?("kind") # kind key does not exist
354
+ if @raise_error; raise "no kind key specified in response." end
355
+ return false
356
+ elsif response["kind"] != "drive#file" # Not of file type
357
+ if @raise_error; raise "kind is of non-file type." end
358
+ return false
359
+ elsif response["name"] != file_basename # file name mismatch
360
+ if @raise_error; raise "file name mismatch." end
361
+ return false
362
+ end
363
+
364
+ return true
365
+ end
366
+
367
+ # returns full path of downloaded file
368
+ def download_file(file_name_or_id, parentfolderid: nil, file_path: nil)
369
+
370
+ # https://stackoverflow.com/questions/60608901/how-to-download-a-big-file-from-google-drive-via-curl-in-bash
371
+ # curl -H "Authorization: Bearer $token" "https://www.googleapis.com/drive/v3/files/$id?alt=media" -o "$file"
372
+
373
+ # if file path passed, check it is valid.
374
+ if file_path && !Dir.exist?(file_path)
375
+ if @raise_error; raise "File path '#{file_path}' does not exist." end
376
+ return false
377
+ elsif !file_path # no path passed, use current directory
378
+ file_path = Dir.getwd
379
+ end
380
+
381
+ # path passed and valid. Append forward slash if not already there.
382
+ file_path = file_path.gsub(/\/$/, '') + "/"
383
+
384
+ # 1) assume file_name_or_id is a filename
385
+ files = find_files(file_name_or_id, parentfolderid: parentfolderid)
386
+ if files && (files.count == 1)
387
+ file_info = files.first
388
+ elsif files && (files.count > 1)
389
+ if @raise_error; raise "Multiple files with name '#{file_name_or_id}' exist. dowload_file() can only handle a single filename." end
390
+ return false
391
+ else # either files is false or count is 0. assume file_name_or_id is an id.
392
+ file_info = get_file_info(file_name_or_id)
393
+ if !file_info
394
+ if @raise_error; raise "No file with ID '#{file_name_or_id}' exist." end
395
+ return false
396
+ end
397
+ end
398
+
399
+ file_name = file_info[:name]
400
+
401
+ # Delete local file if it exists
402
+ `rm #{file_path + file_name} > /dev/null 2>&1`
403
+
404
+ # temp file is automatically unlinked at end of function.
405
+ output_file = Tempfile.new('driveapi_').path
406
+
407
+ url = "https://www.googleapis.com/drive/v3/files/#{file_info[:id]}?alt=media"
408
+
409
+ # --write-out \"%{http_code}\" \"$@\" returns http code in stdout
410
+ cmd = "curl --silent --write-out \"%{http_code}\" \"$@\" -H \"Authorization: Bearer #{@access_token}\" \"#{url}\" -o \"#{output_file}\""
411
+ response = `#{cmd} 2>/dev/null` # this is the http code as string.
412
+ exit_status = $?.exitstatus
413
+
414
+ if exit_status != 0
415
+ if @raise_error; raise "non-0 exit status #{exit_status}." end
416
+ return false
417
+ elsif response.to_i != 200
418
+ if @raise_error; raise "Non HTTP 200 code for download: '#{response}'" end
419
+ return false
420
+ end
421
+
422
+ # file temp file to file
423
+ `cp #{output_file} #{file_path + file_name}`
424
+
425
+ return file_path + file_name
426
+ end
427
+ end # end of class GoogleDrive
428
+
429
+
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: googledrive-easy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Bullock
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-11 00:00:00.000000000 Z
11
+ date: 2022-02-15 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Easy File interface to Google Drive
14
14
  email: jmb@rgnets.com