dbtools 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
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