turbot 0.1.36 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +15 -0
  5. data/.yardopts +3 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +22 -0
  8. data/README.md +44 -25
  9. data/Rakefile +16 -0
  10. data/appveyor.yml +35 -0
  11. data/bin/turbot +2 -16
  12. data/data/schema.json +134 -0
  13. data/{templates → data/templates}/LICENSE.txt +0 -0
  14. data/{templates → data/templates}/manifest.json +0 -0
  15. data/{templates → data/templates}/python/scraper.py +0 -0
  16. data/{templates → data/templates}/ruby/scraper.rb +0 -0
  17. data/dist/deb.rake +32 -0
  18. data/dist/gem.rake +16 -0
  19. data/dist/manifest.rake +9 -0
  20. data/dist/pkg.rake +60 -0
  21. data/dist/resources/deb/control +10 -0
  22. data/dist/resources/deb/postinst +45 -0
  23. data/dist/resources/deb/turbot +25 -0
  24. data/dist/resources/deb/turbot-release-key.txt +30 -0
  25. data/dist/resources/pkg/Distribution.erb +15 -0
  26. data/dist/resources/pkg/PackageInfo.erb +6 -0
  27. data/dist/resources/pkg/postinstall +45 -0
  28. data/dist/resources/pkg/turbot +24 -0
  29. data/dist/resources/tgz/turbot +24 -0
  30. data/dist/rpm.rake +35 -0
  31. data/dist/tgz.rake +26 -0
  32. data/dist/zip.rake +40 -0
  33. data/lib/turbot.rb +18 -15
  34. data/lib/turbot/cli.rb +10 -27
  35. data/lib/turbot/command.rb +59 -212
  36. data/lib/turbot/command/auth.rb +72 -34
  37. data/lib/turbot/command/base.rb +22 -61
  38. data/lib/turbot/command/bots.rb +251 -300
  39. data/lib/turbot/command/help.rb +57 -110
  40. data/lib/turbot/command/version.rb +6 -10
  41. data/lib/turbot/handlers/base_handler.rb +21 -0
  42. data/lib/turbot/handlers/dump_handler.rb +10 -0
  43. data/lib/turbot/handlers/preview_handler.rb +30 -0
  44. data/lib/turbot/handlers/validation_handler.rb +17 -0
  45. data/lib/turbot/helpers.rb +14 -482
  46. data/lib/turbot/helpers/api_helper.rb +41 -0
  47. data/lib/turbot/helpers/netrc_helper.rb +66 -0
  48. data/lib/turbot/helpers/shell_helper.rb +36 -0
  49. data/lib/turbot/version.rb +1 -1
  50. data/spec/fixtures/bad_permissions +0 -0
  51. data/spec/fixtures/empty +0 -0
  52. data/spec/fixtures/netrc +6 -0
  53. data/spec/spec_helper.rb +17 -219
  54. data/spec/support/bot_helper.rb +102 -0
  55. data/spec/support/command_helper.rb +20 -0
  56. data/spec/support/custom_matchers.rb +5 -0
  57. data/spec/support/fixture_helper.rb +9 -0
  58. data/spec/support/netrc_helper.rb +21 -0
  59. data/spec/turbot/command/auth_spec.rb +202 -20
  60. data/spec/turbot/command/base_spec.rb +22 -58
  61. data/spec/turbot/command/bots_spec.rb +580 -89
  62. data/spec/turbot/command/help_spec.rb +32 -75
  63. data/spec/turbot/command/version_spec.rb +11 -10
  64. data/spec/turbot/command_spec.rb +55 -87
  65. data/spec/turbot/helpers_spec.rb +28 -44
  66. data/turbot.gemspec +31 -0
  67. metadata +88 -178
  68. data/data/cacert.pem +0 -3988
  69. data/lib/turbot/auth.rb +0 -315
  70. data/lib/turbot/client.rb +0 -757
  71. data/lib/turbot/client/cisaurus.rb +0 -25
  72. data/lib/turbot/client/pgbackups.rb +0 -113
  73. data/lib/turbot/client/rendezvous.rb +0 -111
  74. data/lib/turbot/client/ssl_endpoint.rb +0 -25
  75. data/lib/turbot/client/turbot_postgresql.rb +0 -148
  76. data/lib/turbot/command/ssl.rb +0 -43
  77. data/lib/turbot/deprecated.rb +0 -5
  78. data/lib/turbot/deprecated/help.rb +0 -38
  79. data/lib/turbot/distribution.rb +0 -9
  80. data/lib/turbot/errors.rb +0 -28
  81. data/lib/turbot/excon.rb +0 -11
  82. data/lib/turbot/helpers/log_displayer.rb +0 -70
  83. data/lib/turbot/helpers/pg_dump_restore.rb +0 -115
  84. data/lib/turbot/helpers/turbot_postgresql.rb +0 -213
  85. data/lib/turbot/plugin.rb +0 -165
  86. data/lib/turbot/updater.rb +0 -171
  87. data/lib/vendor/turbot/okjson.rb +0 -598
  88. data/spec/helper/legacy_help.rb +0 -16
  89. data/spec/helper/pg_dump_restore_spec.rb +0 -67
  90. data/spec/spec.opts +0 -1
  91. data/spec/support/display_message_matcher.rb +0 -49
  92. data/spec/support/dummy_api.rb +0 -120
  93. data/spec/support/openssl_mock_helper.rb +0 -8
  94. data/spec/support/organizations_mock_helper.rb +0 -11
  95. data/spec/turbot/auth_spec.rb +0 -214
  96. data/spec/turbot/client/pgbackups_spec.rb +0 -43
  97. data/spec/turbot/client/rendezvous_spec.rb +0 -62
  98. data/spec/turbot/client/ssl_endpoint_spec.rb +0 -48
  99. data/spec/turbot/client/turbot_postgresql_spec.rb +0 -71
  100. data/spec/turbot/client_spec.rb +0 -548
  101. data/spec/turbot/helpers/turbot_postgresql_spec.rb +0 -181
  102. data/spec/turbot/plugin_spec.rb +0 -172
  103. data/spec/turbot/updater_spec.rb +0 -44
data/lib/turbot/cli.rb CHANGED
@@ -1,42 +1,25 @@
1
- load('turbot/helpers.rb') # reload helpers after possible inject_loadpath
2
- load('turbot/updater.rb') # reload updater after possible inject_loadpath
3
-
4
- require "turbot"
5
- require "turbot/command"
6
- require "turbot/helpers"
7
-
8
- # workaround for rescue/reraise to define errors in command.rb failing in 1.8.6
9
- if RUBY_VERSION =~ /^1.8.6/
10
- require('turbot-api')
11
- require('rest_client')
12
- end
13
-
14
1
  class Turbot::CLI
15
-
16
2
  extend Turbot::Helpers
17
3
 
18
4
  def self.start(*args)
19
5
  begin
20
- if $stdin.isatty
21
- $stdin.sync = true
22
- end
23
- if $stdout.isatty
24
- $stdout.sync = true
25
- end
26
- command = args.shift.strip rescue "help"
27
- Turbot::Command.load
28
- trap "SIGINT" do
6
+ $stdout.sync = true
7
+ $stderr.sync = true
8
+
9
+ trap 'SIGINT' do
29
10
  # Script terminated by Control-C.
30
11
  exit 130
31
12
  end
13
+
14
+ Turbot::Command.load
15
+ command = args.shift.strip rescue 'help'
32
16
  Turbot::Command.run(command, args)
33
17
  rescue Interrupt
34
18
  `stty icanon echo`
35
- error("Command cancelled.")
36
- rescue => error
37
- styled_error(error)
19
+ error 'Command cancelled.'
20
+ rescue => e
21
+ styled_error(e)
38
22
  exit(1)
39
23
  end
40
24
  end
41
-
42
25
  end
@@ -1,23 +1,20 @@
1
- require 'turbot/helpers'
2
- require 'turbot/plugin'
3
- require 'turbot/version'
4
- require "optparse"
5
- require 'excon'
6
-
7
1
  module Turbot
8
2
  module Command
9
- class CommandFailed < RuntimeError; end
10
-
11
3
  extend Turbot::Helpers
12
4
 
13
5
  def self.load
14
- Dir[File.join(File.dirname(__FILE__), "command", "*.rb")].each do |file|
6
+ Dir[File.join(File.dirname(__FILE__), 'command', '*.rb')].each do |file|
15
7
  require file
16
8
  end
17
- Turbot::Plugin.load!
18
9
  unregister_commands_made_private_after_the_fact
19
10
  end
20
11
 
12
+ def self.unregister_commands_made_private_after_the_fact
13
+ commands.values.
14
+ select { |c| c[:klass].private_method_defined? c[:method] }.
15
+ each { |c| commands.delete(c[:command]) }
16
+ end
17
+
21
18
  def self.commands
22
19
  @@commands ||= {}
23
20
  end
@@ -38,12 +35,6 @@ module Turbot
38
35
  commands[command[:command]] = command
39
36
  end
40
37
 
41
- def self.unregister_commands_made_private_after_the_fact
42
- commands.values \
43
- .select { |c| c[:klass].private_method_defined? c[:method] } \
44
- .each { |c| commands.delete c[:command] }
45
- end
46
-
47
38
  def self.register_namespace(namespace)
48
39
  namespaces[namespace[:name]] = namespace
49
40
  end
@@ -52,109 +43,36 @@ module Turbot
52
43
  @current_command
53
44
  end
54
45
 
55
- def self.current_command=(new_current_command)
56
- @current_command = new_current_command
57
- end
58
-
59
- def self.current_args
60
- @current_args
61
- end
62
-
63
- def self.current_options
64
- @current_options ||= {}
65
- end
66
-
67
- def self.global_options
68
- @global_options ||= []
69
- end
70
-
71
- def self.invalid_arguments
72
- @invalid_arguments
73
- end
74
-
75
- def self.shift_argument
76
- # dup argument to get a non-frozen string
77
- @invalid_arguments.shift.dup rescue nil
78
- end
79
-
80
46
  def self.validate_arguments!
81
- unless invalid_arguments.empty?
82
- arguments = invalid_arguments.map {|arg| "\"#{arg}\""}
47
+ unless @invalid_arguments.empty?
48
+ arguments = @invalid_arguments.map(&:inspect)
83
49
  if arguments.length == 1
84
50
  message = "Invalid argument: #{arguments.first}"
85
- elsif arguments.length > 1
86
- message = "Invalid arguments: "
87
- message << arguments[0...-1].join(", ")
88
- message << " and "
89
- message << arguments[-1]
51
+ else
52
+ message = "Invalid arguments: #{arguments[0...-1].join(', ')} and #{arguments[-1]}"
90
53
  end
91
- $stderr.puts(format_with_bang(message))
92
- run(current_command, ["--help"])
93
- exit(1)
54
+ run('help', [current_command])
55
+ error message
94
56
  end
95
57
  end
96
58
 
97
- def self.warnings
98
- @warnings ||= []
99
- end
100
-
101
- def self.display_warnings
102
- unless warnings.empty?
103
- $stderr.puts(warnings.map {|warning| " ! #{warning}"}.join("\n"))
104
- end
105
- end
106
-
107
- def self.global_option(name, *args, &blk)
108
- # args.sort.reverse gives -l, --long order
109
- global_options << { :name => name.to_s, :args => args.sort.reverse, :proc => blk }
110
- end
111
-
112
- global_option :bot, "-b", "--bot APP" do |bot|
113
- raise OptionParser::InvalidOption.new(bot) if bot == "pp"
114
- end
115
-
116
- global_option :org, "-o", "--org ORG" do |org|
117
- raise OptionParser::InvalidOption.new(org) if org == "rg"
118
- end
119
- global_option :personal, "-p", "--personal"
120
-
121
- global_option :confirm, "--confirm APP"
122
- global_option :help, "-h", "--help"
123
- global_option :remote, "-r", "--remote REMOTE"
124
-
125
59
  def self.prepare_run(cmd, args=[])
126
- command = parse(cmd)
127
-
128
- if args.include?('-h') || args.include?('--help')
129
- args.unshift(cmd) unless cmd =~ /^-.*/
130
- cmd = 'help'
131
- command = parse(cmd)
132
- end
133
-
134
- if cmd == '--version'
135
- cmd = 'version'
136
- command = parse(cmd)
137
- end
138
-
60
+ command = commands[cmd] || commands[command_aliases[cmd]]
139
61
  @current_command = cmd
140
- @anonymized_args, @normalized_args = [], []
141
62
 
142
63
  opts = {}
143
64
  invalid_options = []
144
65
 
145
66
  parser = OptionParser.new do |parser|
146
67
  # remove OptionParsers Officious['version'] to avoid conflicts
147
- # see: https://github.com/ruby/ruby/blob/trunk/lib/optparse.rb#L814
68
+ # see: https://github.com/ruby/ruby/blob/6860034546808d4f67ba8f407f3d7aced0c54c5a/lib/optparse.rb#L989
148
69
  parser.base.long.delete('version')
149
- (global_options + (command && command[:options] || [])).each do |option|
150
- parser.on(*option[:args]) do |value|
151
- if option[:proc]
152
- option[:proc].call(value)
70
+ if command && command[:options]
71
+ command[:options].each do |option|
72
+ parser.on(*option[:args]) do |value|
73
+ opts[option[:name].gsub('-', '_').to_sym] = value
74
+ ARGV.join(' ') =~ /(#{option[:args].map {|arg| arg.split(' ', 2).first}.join('|')})/
153
75
  end
154
- opts[option[:name].gsub('-', '_').to_sym] = value
155
- ARGV.join(' ') =~ /(#{option[:args].map {|arg| arg.split(' ', 2).first}.join('|')})/
156
- @anonymized_args << "#{$1} _"
157
- @normalized_args << "#{option[:args].last.split(' ', 2).first} _"
158
76
  end
159
77
  end
160
78
  end
@@ -162,144 +80,73 @@ module Turbot
162
80
  begin
163
81
  parser.order!(args) do |nonopt|
164
82
  invalid_options << nonopt
165
- @anonymized_args << '!'
166
- @normalized_args << '!'
167
83
  end
168
- rescue OptionParser::InvalidOption => ex
169
- invalid_options << ex.args.first
170
- @anonymized_args << '!'
171
- @normalized_args << '!'
84
+ rescue OptionParser::InvalidOption => e
85
+ invalid_options << e.args.first
172
86
  retry
173
87
  end
174
88
 
175
89
  args.concat(invalid_options)
176
-
177
- @current_args = args
178
- @current_options = opts
179
90
  @invalid_arguments = invalid_options
180
91
 
181
- @anonymous_command = [ARGV.first, *@anonymized_args].join(' ')
182
- begin
183
- usage_directory = "#{home_directory}/.turbot/usage"
184
- FileUtils.mkdir_p(usage_directory)
185
- usage_file = usage_directory << "/#{Turbot::VERSION}"
186
- usage = if File.exists?(usage_file)
187
- json_decode(File.read(usage_file))
188
- else
189
- {}
190
- end
191
- usage[@anonymous_command] ||= 0
192
- usage[@anonymous_command] += 1
193
- File.write(usage_file, json_encode(usage) + "\n")
194
- rescue
195
- # usage writing is not important, allow failures
196
- end
197
-
198
92
  if command
199
93
  command_instance = command[:klass].new(args.dup, opts.dup)
200
-
201
- if !@normalized_args.include?('--bot _') && (implied_bot = command_instance.bot rescue nil)
202
- @normalized_args << '--bot _'
203
- end
204
- @normalized_command = [ARGV.first, @normalized_args.sort_by {|arg| arg.gsub('-', '')}].join(' ')
205
-
206
- [ command_instance, command[:method] ]
94
+ [command_instance, command[:method]]
207
95
  else
208
96
  error([
209
97
  "`#{cmd}` is not a turbot command.",
210
98
  suggestion(cmd, commands.keys + command_aliases.keys),
211
- "See `turbot help` for a list of available commands."
99
+ 'See `turbot help` for a list of available commands.'
212
100
  ].compact.join("\n"))
213
101
  end
214
102
  end
215
103
 
216
- def self.run(cmd, arguments=[])
217
- begin
218
- object, method = prepare_run(cmd, arguments.dup)
219
- object.send(method)
220
- rescue Interrupt, StandardError, SystemExit => error
221
- # load likely error classes, as they may not be loaded yet due to defered loads
222
- require 'rest_client'
223
- raise(error)
224
- end
225
- rescue Turbot::API::Errors::Unauthorized, RestClient::Unauthorized
226
- puts "Authentication failure"
227
- if ENV['TURBOT_API_KEY']
228
- exit 1
104
+ def self.run(command, arguments=[])
105
+ object, method = prepare_run(command, arguments.dup)
106
+ object.send(method)
107
+ rescue SocketError => e
108
+ error 'Unable to connect to Turbot API, please check internet connectivity and try again.'
109
+ rescue OptionParser::ParseError
110
+ if commands[command]
111
+ run('help', [command])
229
112
  else
230
- run "login"
231
- retry
232
- end
233
- rescue Turbot::API::Errors::NotFound => e
234
- error extract_error(e.response.body) {
235
- e.response.body =~ /^([\w\s]+ not found).?$/ ? $1 : "Resource not found"
236
- }
237
- rescue RestClient::ResourceNotFound => e
238
- error extract_error(e.http_body) {
239
- e.http_body =~ /^([\w\s]+ not found).?$/ ? $1 : "Resource not found"
240
- }
241
- rescue Turbot::API::Errors::Locked => e
242
- bot = e.response.headers[:x_confirmation_required]
243
- if confirm_command(bot, extract_error(e.response.body))
244
- arguments << '--confirm' << bot
245
- retry
246
- end
247
- rescue RestClient::Locked => e
248
- bot = e.response.headers[:x_confirmation_required]
249
- if confirm_command(bot, extract_error(e.http_body))
250
- arguments << '--confirm' << bot
251
- retry
113
+ run('help')
252
114
  end
253
- rescue RestClient::PaymentRequired => e
254
- # We've repurposed a 402 as a general error
255
- error extract_error(e.http_body)
256
- rescue Turbot::API::Errors::Timeout, RestClient::RequestTimeout
257
- error "API request timed out. Please try again, or contact support@turbot.com if this issue persists."
258
- rescue Turbot::API::Errors::ErrorWithResponse => e
259
- error extract_error(e.response.body)
260
- rescue RestClient::RequestFailed => e
261
- error extract_error(e.http_body)
262
- rescue CommandFailed => e
263
- error e.message
264
- rescue OptionParser::ParseError
265
- commands[cmd] ? run("help", [cmd]) : run("help")
266
- rescue Excon::Errors::SocketError, SocketError => e
267
- error("Unable to connect to Turbot API, please check internet connectivity and try again.")
268
- ensure
269
- display_warnings
270
115
  end
271
116
 
272
- def self.parse(cmd)
273
- commands[cmd] || commands[command_aliases[cmd]]
274
- end
275
-
276
- def self.extract_error(body, options={})
277
- default_error = block_given? ? yield : "Internal server error.\nRun `turbot status` to check for known platform issues."
278
- parse_error_xml(body) || parse_error_json(body) || parse_error_plain(body) || default_error
279
- end
280
-
281
- def self.parse_error_xml(body)
282
- xml_errors = REXML::Document.new(body).elements.to_a("//errors/error")
283
- msg = xml_errors.map { |a| a.text }.join(" / ")
284
- return msg unless msg.empty?
285
- rescue Exception
117
+ def self.extract_error(body, options = {})
118
+ if block_given?
119
+ default_error = yield
120
+ else
121
+ default_error = 'Internal server error'
122
+ end
123
+ parse_error_json(body) || default_error
286
124
  end
287
125
 
288
126
  def self.parse_error_json(body)
289
- json = json_decode(body.to_s) rescue false
290
- case json
291
- when Array
292
- json.first.join(' ') # message like [['base', 'message']]
293
- when Hash
294
- json['error'] || json['error_message'] || json['message'] # message like {'error' => 'message'}
295
- else
127
+ begin
128
+ JSON.load(body.to_s)['message']
129
+ rescue JSON::ParserError
296
130
  nil
297
131
  end
298
132
  end
299
133
 
300
- def self.parse_error_plain(body)
301
- return unless body.respond_to?(:headers) && body.headers[:content_type].to_s.include?("text/plain")
302
- body.to_s
134
+ private
135
+
136
+ def self.suggestion(actual, possibilities)
137
+ distances = Hash.new { |hash,key| hash[key] = [] }
138
+ possibilities.each do |possibility|
139
+ distances[Text::Levenshtein.distance(actual, possibility, 4)] << possibility
140
+ end
141
+ minimum_distance = distances.keys.min
142
+ if minimum_distance < 4
143
+ suggestions = distances[minimum_distance].sort
144
+ if suggestions.length == 1
145
+ "Perhaps you meant `#{suggestions.first}`."
146
+ else
147
+ "Perhaps you meant #{suggestions[0...-1].map { |suggestion| "`#{suggestion}`" }.join(', ')} or `#{suggestions.last}`."
148
+ end
149
+ end
303
150
  end
304
151
  end
305
152
  end
@@ -1,85 +1,123 @@
1
- require "turbot/command/base"
2
-
3
- # authentication (login, logout)
1
+ #Login or logout from Turbot
4
2
  #
5
3
  class Turbot::Command::Auth < Turbot::Command::Base
6
-
7
4
  # auth
8
5
  #
9
- # Authenticate, display token and current user
6
+ #Login or logout. Display your Turbot API token or email address.
10
7
  def index
11
8
  validate_arguments!
12
-
13
9
  Turbot::Command::Help.new.send(:help_for_command, current_command)
14
10
  end
15
11
 
16
12
  # auth:login
17
13
  #
18
- # log in with your turbot credentials
14
+ #Login to Turbot and save your Turbot credentials.
19
15
  #
20
16
  #Example:
21
17
  #
22
- # $ turbot auth:login
23
- # Enter your Turbot credentials:
24
- # Email: email@example.com
25
- # Password (typing will be hidden):
26
- # Authentication successful.
18
+ # $ turbot auth:login
19
+ # Enter your Turbot email and password:
20
+ # Email: email@example.com
21
+ # Password (typing will be hidden):
22
+ # Logged in. Saved Turbot API key.
27
23
  #
28
24
  def login
29
25
  validate_arguments!
30
-
31
- Turbot::Auth.login
32
- display "Authentication successful."
26
+ email_address, api_key = ask_for_email_address_and_password
27
+ if api_key.empty?
28
+ error 'Authentication failed.'
29
+ end
30
+ save_netrc_entry(email_address, api_key)
31
+ puts 'Authentication successful.'
33
32
  end
34
-
35
- alias_command "login", "auth:login"
33
+ alias_command 'login', 'auth:login'
36
34
 
37
35
  # auth:logout
38
36
  #
39
- # clear local authentication credentials
37
+ #Delete your Turbot credentials.
40
38
  #
41
39
  #Example:
42
40
  #
43
- # $ turbot auth:logout
44
- # Local credentials cleared.
41
+ # $ turbot auth:logout
42
+ # Deleted Turbot credentials.
45
43
  #
46
44
  def logout
47
45
  validate_arguments!
48
-
49
- Turbot::Auth.logout
50
- display "Local credentials cleared."
46
+ delete_netrc_entry
47
+ puts 'Deleted Turbot credentials.'
51
48
  end
52
-
53
- alias_command "logout", "auth:logout"
49
+ alias_command 'logout', 'auth:logout'
54
50
 
55
51
  # auth:token
56
52
  #
57
- # display your api token
53
+ #Display your Turbot API token.
58
54
  #
59
55
  #Example:
60
56
  #
61
- # $ turbot auth:token
62
- # ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCD
57
+ # $ turbot auth:token
58
+ # 93a5c45595ed37dc9d040116
63
59
  #
64
60
  def token
65
61
  validate_arguments!
66
-
67
- display Turbot::Auth.api_key
62
+ result = email_address_and_api_key[1]
63
+ if result
64
+ puts result
65
+ else
66
+ error 'not logged in'
67
+ end
68
68
  end
69
69
 
70
70
  # auth:whoami
71
71
  #
72
- # display your turbot email address
72
+ #Display your Turbot email address.
73
73
  #
74
74
  #Example:
75
75
  #
76
- # $ turbot auth:whoami
77
- # email@example.com
76
+ # $ turbot auth:whoami
77
+ # email@example.com
78
78
  #
79
79
  def whoami
80
80
  validate_arguments!
81
+ result = email_address_and_api_key[0]
82
+ if result
83
+ puts result
84
+ else
85
+ error 'not logged in'
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ ### Shell-related
81
92
 
82
- display Turbot::Auth.user
93
+ # Prompts the user for an email address and password, and returns the email
94
+ # address and the user's API key.
95
+ #
96
+ # @return [Array<String>] the user's email address and API key
97
+ def ask_for_email_address_and_password
98
+ puts 'Enter your Turbot email and password.'
99
+
100
+ print 'Email: '
101
+ email_address = ask
102
+
103
+ print 'Password (typing will be hidden): '
104
+ password = running_on_windows? ? ask_for_password_on_windows : ask_for_password
105
+
106
+ puts
107
+
108
+ [email_address, get_api_key(email_address, password)]
83
109
  end
84
110
 
111
+ def running_on_windows?
112
+ RUBY_PLATFORM =~ /mswin32|mingw32/
113
+ end
114
+
115
+ ### API-related
116
+
117
+ # Gets the user's API key.
118
+ #
119
+ # @return [String] the API key, or the empty string if authentication fails
120
+ def get_api_key(email_address, password)
121
+ api.get_api_key_for_credentials(email_address, password)['api_key']
122
+ end
85
123
  end