dbtools 0.5.2

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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +333 -0
  3. data/Thorfile +1 -0
  4. data/bin/dbtools +5 -0
  5. data/config/client_secret_dbtools.json +1 -0
  6. data/config/config.yml +1 -0
  7. data/config/database_config.yml +12 -0
  8. data/config/databases.txt +5 -0
  9. data/config/schedule.rb +8 -0
  10. data/dbtools.gemspec +37 -0
  11. data/lib/dbtools.rb +47 -0
  12. data/lib/dbtools/constants.rb +847 -0
  13. data/lib/dbtools/converter/csv2rdf_converter.rb +68 -0
  14. data/lib/dbtools/converter/csv_importer.rb +107 -0
  15. data/lib/dbtools/converter/excel2csv_converter.rb +40 -0
  16. data/lib/dbtools/converter/google_drive2_rdf_converter.rb +97 -0
  17. data/lib/dbtools/database/database_data.rb +146 -0
  18. data/lib/dbtools/database/db_connection.rb +236 -0
  19. data/lib/dbtools/database/mysql_connection.rb +78 -0
  20. data/lib/dbtools/database/postgresql_connection.rb +132 -0
  21. data/lib/dbtools/database/violation.rb +45 -0
  22. data/lib/dbtools/google_drive/google_drive_api.rb +211 -0
  23. data/lib/dbtools/google_drive/google_drive_entity.rb +22 -0
  24. data/lib/dbtools/google_drive/google_drive_file.rb +10 -0
  25. data/lib/dbtools/google_drive/google_drive_folder.rb +9 -0
  26. data/lib/dbtools/plsql_functions/connect_server.sql +30 -0
  27. data/lib/dbtools/plsql_functions/link.sql +17 -0
  28. data/lib/dbtools/plsql_functions/unlink.sql +15 -0
  29. data/lib/dbtools/rdf/rdf_reader.rb +136 -0
  30. data/lib/dbtools/version.rb +3 -0
  31. data/lib/rdf/geophy.rb +27 -0
  32. data/lib/tasks/aws.rb +43 -0
  33. data/lib/tasks/backup.rb +107 -0
  34. data/lib/tasks/check.rb +220 -0
  35. data/lib/tasks/ckan.rb +151 -0
  36. data/lib/tasks/convert.rb +139 -0
  37. data/lib/tasks/dump.rb +110 -0
  38. data/lib/tasks/googledrivetool.rb +252 -0
  39. data/lib/tasks/import.rb +142 -0
  40. data/lib/tasks/postgres.rb +29 -0
  41. metadata +307 -0
@@ -0,0 +1,139 @@
1
+ require 'rdf'
2
+ require 'linkeddata'
3
+ require 'dbtools/converter/csv2rdf_converter'
4
+
5
+ module Dbtools
6
+ class Convert < Thor
7
+ package_name "dbtools"
8
+
9
+ def initialize(*args)
10
+ super
11
+ end
12
+
13
+ # desc 'csv2rdf [csv_file, format, metadata]', 'Converts a csv file to a RDF file. Output file has the same name as input file, with a different extension.'
14
+ # def csv2rdf(csv_file, format = 'ttl', metadata_file = nil)
15
+ # if csv_file
16
+ # # Output as the same file, but with a different extension.
17
+ # rdf_file = change_filename_extension(csv_file, format)
18
+ # RDF::Writer.open(rdf_file) do |f|
19
+ # # Load the csv into rdf graph, and write output to file.
20
+ # options = {}
21
+ # options[:metadata] = metadata_file if metadata_file
22
+ # graph = RDF::Graph.load(csv_file, options)
23
+ # f << graph
24
+ # end
25
+ # end
26
+ # end
27
+
28
+ desc 'excel2csv [excel_file, sheet_index, output_file(optional)]', 'Converts an excel file to CSV file.'
29
+ long_desc <<-LONGDESC
30
+ `excel2csv` converts an excel file to csv format. The sheet index should be specified.
31
+ It will default to the first sheet by default.
32
+ LONGDESC
33
+ # option :sheet,
34
+ def excel2csv(excel_file, sheet_index = 0, output_file = nil)
35
+ converter = Dbtools::Converter::Excel2csv_converter.new(excel_file)
36
+ if output_file.nil?
37
+ STDOUT << converter.sheet2csv(sheet_index)
38
+ else
39
+ File.open(output_file, 'a') do |f|
40
+ f << converter.sheet2csv(sheet_index)
41
+ end
42
+ end
43
+ end
44
+
45
+ desc 'csv2rdf [csv_file, uri, compressed(optional)]', 'Converts a csv file to a RDF ntriples file.'
46
+ long_desc <<-LONGDESC
47
+ `csv2rdf csv_file uri` will convert a csv file to a RDF NTriples file.
48
+ The URI will be the subject in the resulting RDF file.
49
+
50
+ You can optionally specify a third parameter, which will write
51
+ the output to a file. You can also specify a fourth parameter to compress
52
+ the file to .gz format.
53
+
54
+ Example:
55
+ \x5$ dbtools convert csv2rdf data.csv http://geophy.io output.nt
56
+
57
+ Resulting triples will look like:
58
+ <http://geophy.io#ROWNUMBER> <http://geophy.io/COLUMNNAME> VALUE .
59
+ <http://geophy.io#1> <http://geophy.io/name> "Bob" .
60
+
61
+ LONGDESC
62
+ def csv2rdf(csv_file, uri, output_file=nil, compressed=false)
63
+ csv_rdf = Dbtools::Converter::Csv2rdf_converter.new(csv_file, uri)
64
+ if output_file.nil?
65
+ csv_rdf.each_triple do |triple|
66
+ puts triple
67
+ end
68
+ else
69
+ begin
70
+ file = if compressed
71
+ Zlib::GzipWriter.open(output_file + '.gz')
72
+ else
73
+ File.open(output_file, 'w')
74
+ end
75
+ csv_rdf.each_triple do |triple|
76
+ file.write(triple << "\n")
77
+ end
78
+ ensure
79
+ file.close unless file.nil?
80
+ end
81
+ end
82
+ end
83
+
84
+ desc 'googledrive2rdf [file_id]', 'Converts a csv file from Google Drive to a RDF ntriples file. '
85
+ long_desc <<-LONGDESC
86
+ `googledrive2rdf file_id` will download a file from Google Drive and
87
+ convert that file to a RDF NTriples file.
88
+ The Google Drive file id will be the subject in the resulting RDF file.
89
+
90
+ You can optionally specify a second parameter, which will write
91
+ the output to a file. To compress the file to .gz, you can specify a third parameter.
92
+
93
+ Example:
94
+ \x5$ dbtools convert googledrive2rdf 0Byv6wMVo_JE4WGR6QWc2S3NiQjg output.nt true
95
+ LONGDESC
96
+ def googledrive2rdf(file_id, output_file=nil, compressed=false)
97
+ output_dir = File.join('/tmp', 'dbtools_googledrive/')
98
+ file_path = invoke("dbtools:google_drive:download", [file_id, output_dir])
99
+ uri = "https://drive.google.com/open?id=" << file_id
100
+ invoke "dbtools:convert:csv2rdf", [file_path, uri, output_file, compressed]
101
+ ensure
102
+ FileUtils.remove_entry_secure(output_dir)
103
+ end
104
+
105
+ # desc 'excel2rdf [excel_file, format, metadata]', 'Converts an excel file to an RDF file. Output file has the same name as input file, with a different extension.'
106
+ # def excel2rdf(excel_file, format = 'ttl', metadata_file = nil)
107
+ # if excel_file
108
+ # # Use tmp dir to output csv files.
109
+ # output_dir = File.join('/tmp', 'excel2csv')
110
+ # files = Dbtools::Import.new.excel2csv(excel_file, output_dir)
111
+ # # Output as the same file, but with a different extension.
112
+ # rdf_file = change_filename_extension(excel_file, format)
113
+ # RDF::Writer.open(rdf_file) do |f|
114
+ # options = {}
115
+ # # Use metadata file for mapping if specified.
116
+ # options[:metadata] = metadata_file if metadata_file
117
+ # graph = RDF::Graph.new
118
+ # files.each do |sheetname, csv_file|
119
+ # # Load all sheets in the graph.
120
+ # graph.load(csv_file, options)
121
+ # end
122
+ # f << graph
123
+ # end
124
+ # # Remove tmp dir
125
+ # FileUtils.remove_entry_secure(output_dir)
126
+ # end
127
+ # end
128
+
129
+
130
+ private
131
+ # Change the extension of the given filename with the new extension
132
+ def change_filename_extension(file, extension)
133
+ # Strip current extension from file name
134
+ file_extless = File.join(File.dirname(file), File.basename(file, ".*"))
135
+ ext = extension.delete(".")
136
+ return file_extless << "." << ext
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,110 @@
1
+ require 'fileutils'
2
+ require 'thor'
3
+ require 'rdf'
4
+ require 'sparql/client'
5
+
6
+ module Dbtools
7
+ class Dump < Thor
8
+ package_name "dbtools"
9
+
10
+ # Backs up a rdf graph at a sparql endpoint
11
+ desc 'rdf [sparql_endpoint, filename]', 'Dumps a rdf database to a file.'
12
+ long_desc <<-LONGDESC
13
+ `rdf [sparql_endpoint, filename]` will write all ntriple statements
14
+ located in the RDF repository to the specified file.
15
+
16
+ Example:
17
+ \x5$ dbtools dump rdf http://localhost:9999/blazegraph/namespace/test/sparql /tmp/repository.nt --compress=false
18
+ \x5$ dbtools dump rdf http://localhost:9999/blazegraph/namespace/test/sparql /tmp/repository.nt.gz --compress
19
+ LONGDESC
20
+ option :compress, :default => false, :type => :boolean
21
+ def rdf(sparql_endpoint, filename)
22
+ repo = SPARQL::Client::Repository.new(uri: sparql_endpoint)
23
+
24
+ # Use temp name when compressing
25
+ if options[:compress]
26
+ target = filename + '_tmp.nt'
27
+ STDERR.puts %q[Warning: compress option is selected, but filename doesn't end with .gz.
28
+ You should change the name to end in .gz if you want to open it.] if File.extname(filename) != '.gz'
29
+ else
30
+ target = filename
31
+ end
32
+
33
+ RDF::Writer.open(target, format: :ntriples) do |w|
34
+ repo.each {|stmt| w << stmt}
35
+ end
36
+
37
+ # Zlib::GzipWriter can't wrap around RDF::Writer, because RDF::Writer is not a IO-like object...
38
+ if options[:compress]
39
+ # Compress the file using gz
40
+ Zlib::GzipWriter.open(filename) do |gz|
41
+ # Write in chunks.
42
+ File.open(target) do |fp|
43
+ while chunk = fp.read(16 * 1024) do
44
+ gz.write chunk
45
+ end
46
+ end
47
+ gz.close
48
+ end
49
+ # Delete the original file after generating the compressed version.
50
+ File.delete(target) if File.exists?(target)
51
+ end
52
+ end
53
+
54
+ desc 'blazegraph [url, file]', 'Dumps a blazegraph database to a jnl file.'
55
+ long_desc <<-LONGDESC
56
+ `blazegraph [url, file]` will create a backup of a blazegraph database,
57
+ using the built in backup function, and output it to the specified file.
58
+ It is recommended to use absolute filepaths.
59
+
60
+ Note: the resulting file will reside on the same server as where blazegraph
61
+ is running.
62
+
63
+ Example:
64
+ \x5$ dbtools backup blazegraph http://localhost:9999/blazegraph/backup /tmp/dump.jnl
65
+ LONGDESC
66
+ option :compress, :default => false, :type => :boolean
67
+ def blazegraph(url, file)
68
+ uri = URI(url)
69
+ params = { :compress => options[:compress],
70
+ :file => file }
71
+ uri.query = URI.encode_www_form(params)
72
+ res = Net::HTTP.get_response(uri)
73
+ end
74
+
75
+
76
+ # Creates a schema dump of the database. Specify the database with an url.
77
+ desc 'schema URL PATH', 'Creates a schema dump of the database. Specify the database with an url.'
78
+ def schema(url, path)
79
+ adapter, user, password, host, database = url.match("^([a-zA-Z0-9]+):\/\/(.+):(.+)@(.+)\/(.+)").captures
80
+ case adapter
81
+ when "mysql2"
82
+ dump_mysql_schema(user, password, database, host, path)
83
+ when "postgres"
84
+ dump_postgresql_schema(user, password, database, host, path)
85
+ else
86
+ puts "Not supported database"
87
+ end
88
+ end
89
+
90
+ private
91
+ # Dumps a mysql database schema to a file.
92
+ def dump_mysql_schema(user, password, database, host, path)
93
+ dump_path = File.join(path, 'mysql', host)
94
+ FileUtils::mkdir_p(dump_path)
95
+ dump_file_name = File.join(dump_path, "#{database}_schema.sql")
96
+ puts "Dumping schema to #{dump_file_name}"
97
+ system "mysqldump -u #{user} -p#{password} -h #{host} --no-data #{database} > #{dump_file_name}"
98
+ end
99
+
100
+ # Dumps a postgres database schema to a file.
101
+ def dump_postgresql_schema(user, password, database, host, path)
102
+ dump_path = File.join(path, "postgres", host)
103
+ FileUtils::mkdir_p(dump_path)
104
+ dump_file_name = File.join(dump_path, "#{database}_schema.sql")
105
+ puts "Dumping schema to #{dump_file_name}"
106
+ system "pg_dump -h #{host} --dbname=#{database} --username=#{user} --schema-only > #{dump_file_name}"
107
+ end
108
+
109
+ end
110
+ end
@@ -0,0 +1,252 @@
1
+ require 'yaml'
2
+ require 'thor'
3
+ require 'find'
4
+ require 'dbtools/rdf/rdf_reader'
5
+ require 'dbtools/constants'
6
+ require 'dbtools/converter/csv_importer'
7
+ require 'dbtools/converter/google_drive2_rdf_converter'
8
+ require 'dbtools/converter/excel2csv_converter'
9
+ require 'dbtools/database/mysql_connection'
10
+ require 'dbtools/database/postgresql_connection'
11
+ require 'dbtools/google_drive/google_drive_api'
12
+ require 'fileutils'
13
+
14
+ require 'googleauth'
15
+ require 'googleauth/stores/file_token_store'
16
+ require 'google/apis/drive_v3'
17
+
18
+ module Dbtools
19
+ class FormatNotSupportedError < StandardError; end
20
+
21
+ class Google_drive < Thor
22
+
23
+ def initialize(*args)
24
+ super
25
+ @gdrive = Google_Drive::Google_drive_api.new(auth=authorize)
26
+ @service = @gdrive.service
27
+ end
28
+
29
+
30
+ desc 'export [File_id, format, target_dest(optional)]', 'Exports a file stored on Google Drive. Like Google Spreadsheets etc. For regular files, use download. If no target directory is specified, it will print the contents. Supported export formats: https://developers.google.com/drive/v3/web/manage-downloads#downloading_a_file'
31
+ long_desc <<-LONGDESC
32
+ `export` will download a Google document stored on Google Drive, like Google Spreadsheets, Google slides etc.
33
+ You need to specify the export format. The supported conversion formats can be found here:
34
+ https://developers.google.com/drive/v3/web/manage-downloads#downloading_a_file
35
+
36
+ An optional target directory can be given. This will download the file into the directory,
37
+ using the same name as on Google Drive.
38
+ If no target directory is given, the file will be streamed to STDOUT.
39
+
40
+ Examples:
41
+ \x5$ dbtools google_drive export 0B67ew1eLtcXxeUVmTndialhTRTA 'text/plain' /tmp/target_dir/"
42
+ \x5$ dbtools google_drive export 0B67ew1eLtcXxeUVmTndialhTRTA 'application/pdf' > /tmp/test.pdf"
43
+
44
+ LONGDESC
45
+ def export(file_id, target_format, target_dest = nil)
46
+ if target_dest.nil?
47
+ @service.export_file(file_id, target_format, download_dest: STDOUT)
48
+ else
49
+ file = @service.get_file(file_id)
50
+ FileUtils.mkdir_p(target_dest)
51
+ extension_mapping = { 'text/html' => '.html',
52
+ 'text/plain' => '.txt',
53
+ 'application/rtf' => '.rtf',
54
+ 'application/vnd.oasis.opendocument.text' => '.odt',
55
+ 'application/pdf' => '.pdf',
56
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => '.docx',
57
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => '.xlsx',
58
+ 'application/x-vnd.oasis.opendocument.spreadsheet' => '.ods',
59
+ 'text/csv' => '.csv',
60
+ 'image/jpeg' => '.jpg',
61
+ 'image/png' => '.png',
62
+ 'image/svg+xml' => '.svg',
63
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => '.pptx',
64
+ 'application/vnd.google-apps.script+json' => '.json' }
65
+ extension_mapping.default = ''
66
+ extension = extension_mapping[target_format]
67
+ destination = File.join(target_dest, file.name + extension)
68
+ @service.export_file(file_id, target_format, download_dest: destination)
69
+ return destination
70
+ end
71
+ end
72
+
73
+ desc 'download [File_id, target_dest(optional)]', 'Downloads a file stored on google drive. Regular files, no Google Spreadsheets etc. For Google documents, use export. If no target directory is specified, it will print the contents. '
74
+ long_desc <<-LONGDESC
75
+ `download` will download a file stored on Google Drive.
76
+ An optional target directory can be given. This will download the file into the directory,
77
+ using the same name as on Google Drive. If a Google Doc file is given, it will automatically
78
+ export it to the most commonly used format.
79
+ If no target directory is given, the file will be streamed to STDOUT.
80
+
81
+ Examples:
82
+ \x5$ dbtools google_drive download 0B67ew1eLtcXxeUVmTndialhTRTA /tmp/target_destination/"
83
+ \x5$ dbtools google_drive download 0B67ew1eLtcXxeUVmTndialhTRTA > /tmp/image.jpg"
84
+
85
+ LONGDESC
86
+ def download(file_id, target_dest = nil)
87
+ metadata = @service.get_file(file_id)
88
+ if metadata.mime_type.index('application/vnd.google-apps')
89
+ # Default conversion formats
90
+ googledoc_format_conversion = { 'application/vnd.google-apps.document' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
91
+ 'application/vnd.google-apps.presentation' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
92
+ 'application/vnd.google-apps.spreadsheet' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
93
+ 'application/vnd.google-apps.drawing' => 'image/png' }
94
+ target_format = googledoc_format_conversion[metadata.mime_type]
95
+ raise FormatNotSupportedError.new("Mimetype #{metadata.mime_type} is not supported. If you know the target conversion format, try using the `export` task.") if target_format.nil?
96
+ return export(file_id, target_format, target_dest)
97
+ end
98
+
99
+ if target_dest.nil?
100
+ @service.get_file(file_id, download_dest: STDOUT)
101
+ else
102
+ destination = target_dest
103
+ if target_dest.is_a?(String)
104
+ FileUtils.mkdir_p(target_dest)
105
+ destination = File.join(target_dest, metadata.name)
106
+ end
107
+ @service.get_file(file_id, download_dest: destination)
108
+ return destination
109
+ end
110
+ end
111
+
112
+ desc 'serialize_rdf [target_file]', 'Serializes the entire Google Drive to a RDF representation. '
113
+ long_desc <<-LONGDESC
114
+ `serialize_rdf` will serialize the entire Google Drive to a RDF representation. When no target file
115
+ is specified, it will output the results to STDOUT.
116
+
117
+ An optional `query` parameter can be given, to only serialize matching results.
118
+ See the `Search for files` guide from the Google Drive API for supported syntax.
119
+
120
+ Example:
121
+ \x5$ dbtools google_drive serialize_rdf output.nt --query="name contains 'Data Lake'"
122
+ LONGDESC
123
+ option :query, :option => '', :type => :string
124
+ option :verbose, :default => false, :type => :boolean
125
+ def serialize_rdf(target_file = nil)
126
+ f = if target_file.nil? then STDOUT else File.open(target_file, 'w') end
127
+ begin
128
+ print_progress = options[:verbose]
129
+ files = @gdrive.get_tree(optional_query=options[:query], verbose: print_progress)
130
+ googledrive2rdf_converter = Dbtools::Converter::GoogleDrive2RDFConverter.new
131
+ googledrive2rdf_converter.serialize_as_rdf(files=files, verbose: print_progress) do |statement|
132
+ f << statement
133
+ end
134
+ ensure
135
+ if f != STDOUT
136
+ f.flush
137
+ f.close
138
+ end
139
+ end
140
+ end
141
+
142
+ desc 'upload [file]', 'Upload a file to Google Drive'
143
+ long_desc <<-LONGDESC
144
+ `upload` will load a file stored on Google Drive. You can specify the target directory by
145
+ providing the --folder argument with the folder id. If no argument is given, the file will
146
+ be placed in the root directory.
147
+
148
+ Note: Google Drive allows the same file to be placed in multiple directories, similar to
149
+ symlinking a file. To place the uploaded file in multiple directories, pass an
150
+ array of folder ids to the --folder argument.
151
+
152
+ Examples:
153
+ \x5$ dbtools google_drive upload file.txt --folder=0B1ptxcLvq-tCNHlQMzU0ZFcyZjQ
154
+
155
+ LONGDESC
156
+ method_option :folder, :type => :array
157
+ method_option :filename, :type => :string
158
+ def upload(file)
159
+ mime_type = `file --mime-type -b #{file}`.chomp
160
+ file_metadata = {
161
+ name: File.basename(file),
162
+ mime_type: mime_type,
163
+ }
164
+ file_metadata.merge!({ parents: options[:folder] }) if options[:folder]
165
+ file_metadata.merge!({ name: options[:filename] }) if options[:filename]
166
+
167
+ result = @service.create_file(file_metadata, upload_source: file,
168
+ content_type: mime_type, options: { retries: 3 } )
169
+ return result
170
+ end
171
+
172
+ desc 'changes_as_rdf [page_token, target_file]', 'Queries all changes to the Google Drive from a starting point defined by the page token.'
173
+ def changes_as_rdf(start_page_token, target_file=nil)
174
+ # Check if it's not empty.
175
+ if start_page_token.nil? || start_page_token.to_s.empty?
176
+ STDERR.puts "Start page token cannot be nil. "
177
+ return
178
+ end
179
+ f = if target_file.nil?
180
+ STDOUT
181
+ else
182
+ File.open(target_file, 'w')
183
+ end
184
+ begin
185
+ changed_files, removed_files, new_start_page_token = @gdrive.get_changes(page_token=start_page_token)
186
+ googledrive2rdf = Dbtools::Converter::GoogleDrive2RDFConverter.new
187
+ googledrive2rdf.serialize_as_rdf(files=changed_files) do |statement|
188
+ f << statement
189
+ end
190
+ ensure
191
+ if f != STDOUT
192
+ f.flush
193
+ f.close
194
+ end
195
+ end
196
+ STDERR.puts "New page token: #{new_start_page_token}"
197
+ return changed_files, removed_files, new_start_page_token
198
+ end
199
+
200
+
201
+ desc 'list [file_id]', 'Lists all files in the folder.'
202
+ def list(file_id)
203
+ folder = @service.get_file(file_id)
204
+ puts @gdrive.print_child_files(folder)
205
+ end
206
+
207
+ no_commands do
208
+ def get_file(file_id, download_dest: nil)
209
+ @service.get_file(file_id, download_dest: download_dest)
210
+ end
211
+ end
212
+
213
+ private
214
+
215
+ OOB_URI = 'urn:ietf:wg:oauth:2.0:oob'
216
+ CLIENT_SECRETS_PATH = Dbtools::Google_Drive::Google_drive_api::CLIENT_SECRETS_PATH
217
+ CREDENTIALS_PATH = Dbtools::Google_Drive::Google_drive_api::CREDENTIALS_PATH
218
+ SCOPE = Google::Apis::DriveV3::AUTH_DRIVE
219
+
220
+ # Ensure valid credentials, either by restoring from the saved credentials
221
+ # files or intitiating an OAuth2 authorization. If authorization is required,
222
+ # the user's default browser will be launched to approve the request.
223
+ #
224
+ # @return [Google::Auth::UserRefreshCredentials] OAuth2 credentials
225
+ def authorize
226
+ unless File.exist?(CLIENT_SECRETS_PATH)
227
+ puts "#{CLIENT_SECRETS_PATH} not found."
228
+ puts "Create Google Drive API OAuth 2.0 credentials to allow access. "
229
+ exit(1)
230
+ end
231
+
232
+ FileUtils.mkdir_p(File.dirname(CREDENTIALS_PATH))
233
+ client_id = Google::Auth::ClientId.from_file(CLIENT_SECRETS_PATH)
234
+ token_store = Google::Auth::Stores::FileTokenStore.new(file: CREDENTIALS_PATH)
235
+ authorizer = Google::Auth::UserAuthorizer.new(
236
+ client_id, SCOPE, token_store)
237
+ user_id = 'default'
238
+ credentials = authorizer.get_credentials(user_id)
239
+ if credentials.nil?
240
+ url = authorizer.get_authorization_url(
241
+ base_url: OOB_URI)
242
+ puts url
243
+ code = ask("Open the following URL in the browser and enter the " +
244
+ "resulting code after authorization: ")
245
+ credentials = authorizer.get_and_store_credentials_from_code(
246
+ user_id: user_id, code: code, base_url: OOB_URI)
247
+ end
248
+ credentials
249
+ end
250
+
251
+ end
252
+ end