turbot 0.1.36 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) 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/Gemfile +4 -0
  6. data/LICENSE +22 -0
  7. data/README.md +44 -25
  8. data/Rakefile +16 -0
  9. data/appveyor.yml +35 -0
  10. data/bin/turbot +2 -16
  11. data/data/schema.json +134 -0
  12. data/{templates → data/templates}/LICENSE.txt +0 -0
  13. data/{templates → data/templates}/manifest.json +0 -0
  14. data/{templates → data/templates}/python/scraper.py +0 -0
  15. data/{templates → data/templates}/ruby/scraper.rb +0 -0
  16. data/dist/deb.rake +32 -0
  17. data/dist/gem.rake +16 -0
  18. data/dist/manifest.rake +9 -0
  19. data/dist/pkg.rake +60 -0
  20. data/dist/resources/deb/control +10 -0
  21. data/dist/resources/deb/postinst +45 -0
  22. data/dist/resources/deb/turbot +25 -0
  23. data/dist/resources/deb/turbot-release-key.txt +30 -0
  24. data/dist/resources/pkg/Distribution.erb +15 -0
  25. data/dist/resources/pkg/PackageInfo.erb +6 -0
  26. data/dist/resources/pkg/postinstall +45 -0
  27. data/dist/resources/pkg/turbot +24 -0
  28. data/dist/resources/tgz/turbot +24 -0
  29. data/dist/rpm.rake +35 -0
  30. data/dist/tgz.rake +26 -0
  31. data/dist/zip.rake +40 -0
  32. data/lib/turbot/cli.rb +10 -27
  33. data/lib/turbot/command/auth.rb +72 -34
  34. data/lib/turbot/command/base.rb +21 -60
  35. data/lib/turbot/command/bots.rb +245 -300
  36. data/lib/turbot/command/help.rb +57 -110
  37. data/lib/turbot/command/version.rb +6 -10
  38. data/lib/turbot/command.rb +53 -196
  39. data/lib/turbot/handlers/base_handler.rb +21 -0
  40. data/lib/turbot/handlers/dump_handler.rb +10 -0
  41. data/lib/turbot/handlers/preview_handler.rb +30 -0
  42. data/lib/turbot/handlers/validation_handler.rb +17 -0
  43. data/lib/turbot/helpers/api_helper.rb +41 -0
  44. data/lib/turbot/helpers/netrc_helper.rb +66 -0
  45. data/lib/turbot/helpers/shell_helper.rb +36 -0
  46. data/lib/turbot/helpers.rb +14 -482
  47. data/lib/turbot/version.rb +1 -1
  48. data/lib/turbot.rb +17 -15
  49. data/spec/fixtures/bad_permissions +0 -0
  50. data/spec/fixtures/empty +0 -0
  51. data/spec/fixtures/netrc +6 -0
  52. data/spec/spec_helper.rb +17 -219
  53. data/spec/support/bot_helper.rb +100 -0
  54. data/spec/support/command_helper.rb +20 -0
  55. data/spec/support/custom_matchers.rb +5 -0
  56. data/spec/support/fixture_helper.rb +9 -0
  57. data/spec/support/netrc_helper.rb +21 -0
  58. data/spec/turbot/command/auth_spec.rb +202 -20
  59. data/spec/turbot/command/base_spec.rb +22 -58
  60. data/spec/turbot/command/bots_spec.rb +558 -89
  61. data/spec/turbot/command/help_spec.rb +32 -75
  62. data/spec/turbot/command/version_spec.rb +11 -10
  63. data/spec/turbot/command_spec.rb +60 -85
  64. data/turbot.gemspec +31 -0
  65. metadata +86 -178
  66. data/data/cacert.pem +0 -3988
  67. data/lib/turbot/auth.rb +0 -315
  68. data/lib/turbot/client/cisaurus.rb +0 -25
  69. data/lib/turbot/client/pgbackups.rb +0 -113
  70. data/lib/turbot/client/rendezvous.rb +0 -111
  71. data/lib/turbot/client/ssl_endpoint.rb +0 -25
  72. data/lib/turbot/client/turbot_postgresql.rb +0 -148
  73. data/lib/turbot/client.rb +0 -757
  74. data/lib/turbot/command/ssl.rb +0 -43
  75. data/lib/turbot/deprecated/help.rb +0 -38
  76. data/lib/turbot/deprecated.rb +0 -5
  77. data/lib/turbot/distribution.rb +0 -9
  78. data/lib/turbot/errors.rb +0 -28
  79. data/lib/turbot/excon.rb +0 -11
  80. data/lib/turbot/helpers/log_displayer.rb +0 -70
  81. data/lib/turbot/helpers/pg_dump_restore.rb +0 -115
  82. data/lib/turbot/helpers/turbot_postgresql.rb +0 -213
  83. data/lib/turbot/plugin.rb +0 -165
  84. data/lib/turbot/updater.rb +0 -171
  85. data/lib/vendor/turbot/okjson.rb +0 -598
  86. data/spec/helper/legacy_help.rb +0 -16
  87. data/spec/helper/pg_dump_restore_spec.rb +0 -67
  88. data/spec/spec.opts +0 -1
  89. data/spec/support/display_message_matcher.rb +0 -49
  90. data/spec/support/dummy_api.rb +0 -120
  91. data/spec/support/openssl_mock_helper.rb +0 -8
  92. data/spec/support/organizations_mock_helper.rb +0 -11
  93. data/spec/turbot/auth_spec.rb +0 -214
  94. data/spec/turbot/client/pgbackups_spec.rb +0 -43
  95. data/spec/turbot/client/rendezvous_spec.rb +0 -62
  96. data/spec/turbot/client/ssl_endpoint_spec.rb +0 -48
  97. data/spec/turbot/client/turbot_postgresql_spec.rb +0 -71
  98. data/spec/turbot/client_spec.rb +0 -548
  99. data/spec/turbot/helpers/turbot_postgresql_spec.rb +0 -181
  100. data/spec/turbot/helpers_spec.rb +0 -48
  101. data/spec/turbot/plugin_spec.rb +0 -172
  102. data/spec/turbot/updater_spec.rb +0 -44
@@ -1,176 +1,123 @@
1
- require "turbot/command/base"
2
- require "turbot/deprecated/help"
3
-
4
- # list commands and display help
1
+ #List commands and display help
5
2
  #
6
3
  class Turbot::Command::Help < Turbot::Command::Base
7
-
8
- PRIMARY_NAMESPACES = %w( auth bots ps run addons config releases domains logs sharing )
9
-
10
- include Turbot::Deprecated::Help
4
+ PRIMARY_NAMESPACES = Set.new(%w(auth bots))
11
5
 
12
6
  # help [COMMAND]
13
7
  #
14
- # list available commands or display help for a specific command
8
+ #List available commands or display help for a specific command.
15
9
  #
16
10
  #Examples:
17
11
  #
18
- # $ turbot help
19
- # Usage: turbot COMMAND [--bot APP] [command-specific-options]
12
+ # $ turbot help
13
+ # Usage: turbot COMMAND [--bot APP] [command-specific-options]
20
14
  #
21
- # Primary help topics, type "turbot help TOPIC" for more details:
15
+ # Primary help topics, type "turbot help TOPIC" for more details:
22
16
  #
23
- # addons # manage addon resources
24
- # bots # manage bots (create, destroy)
25
- # ...
17
+ # auth # Login or logout from Turbot
18
+ # bots # Manage bots (generate skeleton, validate data, submit code)
26
19
  #
27
- # Additional topics:
20
+ # Additional topics:
28
21
  #
29
- # account # manage turbot account options
30
- # accounts # manage multiple turbot accounts
31
- # ...
22
+ # help # List commands and display help
23
+ # version # Display version
32
24
  #
33
- # $ turbot help bots:create
34
- # Usage: turbot bots:create [NAME]
25
+ # $ turbot help auth:whoam
26
+ # Usage: turbot auth:whoami
35
27
  #
36
- # create a new bot
28
+ # Display your Turbot email address.
37
29
  #
38
- # --addons ADDONS # a comma-delimited list of addons to install
39
- # -b, --buildpack BUILDPACK # a buildpack url to use for this bot
40
- # -r, --remote REMOTE # the git remote to create, default "turbot"
41
- # -s, --stack STACK # the stack on which to create the bot
30
+ # Example:
31
+ #
32
+ # $ turbot auth:whoami
33
+ # email@example.com
42
34
  #
43
35
  def index
44
- if command = args.shift
36
+ command = args.shift
37
+ if command
45
38
  help_for_command(command)
46
39
  else
47
40
  help_for_root
48
41
  end
49
42
  end
50
-
51
- alias_command "-h", "help"
52
- alias_command "--help", "help"
43
+ alias_command '-h', 'help'
44
+ alias_command '--help', 'help'
53
45
 
54
46
  private
55
47
 
56
- def commands_for_namespace(name)
57
- Turbot::Command.commands.values.select do |command|
58
- command[:namespace] == name && command[:command] != name
59
- end
60
- end
61
-
62
- def namespaces
63
- namespaces = Turbot::Command.namespaces
64
- namespaces.delete("bot")
65
- namespaces
66
- end
67
-
68
- def commands
69
- Turbot::Command.commands
70
- end
71
-
72
- def legacy_help_for_namespace(namespace)
73
- instance = Turbot::Command::Help.groups.map do |group|
74
- [ group.title, group.select { |c| c.first =~ /^#{namespace}/ }.length ]
75
- end.sort_by { |l| l.last }.last
76
- return nil unless instance
77
- return nil if instance.last.zero?
78
- instance.first
79
- end
80
-
81
- def legacy_help_for_command(command)
82
- Turbot::Command::Help.groups.each do |group|
83
- group.each do |cmd, description|
84
- return description if cmd.split(" ").first == command
85
- end
48
+ def help_for(items, name_key, description_key)
49
+ size = items.map { |namespace| namespace[name_key].size }.max
50
+ items.sort_by { |namespace| namespace[name_key] }.each do |namespace|
51
+ puts " %-#{size}s # %s" % [ namespace[name_key], namespace[description_key] ]
86
52
  end
87
- nil
88
53
  end
89
54
 
90
- def skip_namespace?(ns)
91
- return true if ns[:description] =~ /DEPRECATED:/
92
- return true if ns[:description] =~ /HIDDEN:/
93
- false
94
- end
55
+ ### Root
95
56
 
96
- def skip_command?(command)
97
- return true if command[:help] =~ /DEPRECATED:/
98
- return true if command[:help] =~ /^ HIDDEN:/
99
- false
57
+ def namespaces
58
+ Turbot::Command.namespaces
100
59
  end
101
60
 
102
61
  def primary_namespaces
103
- PRIMARY_NAMESPACES.map { |name| namespaces[name] }.compact
62
+ namespaces.select { |name,_| PRIMARY_NAMESPACES.include?(name) }.values
104
63
  end
105
64
 
106
65
  def additional_namespaces
107
- (namespaces.values - primary_namespaces)
66
+ namespaces.reject { |name,_| PRIMARY_NAMESPACES.include?(name) }.values
108
67
  end
109
68
 
110
69
  def summary_for_namespaces(namespaces)
111
- size = longest(namespaces.map { |n| n[:name] })
112
- namespaces.sort_by {|namespace| namespace[:name]}.each do |namespace|
113
- next if skip_namespace?(namespace)
114
- name = namespace[:name]
115
- namespace[:description] ||= legacy_help_for_namespace(name)
116
- puts " %-#{size}s # %s" % [ name, namespace[:description] ]
117
- end
70
+ help_for(namespaces, :name, :description)
118
71
  end
119
72
 
120
73
  def help_for_root
121
- puts "Usage: turbot COMMAND [--bot APP] [command-specific-options]"
74
+ puts 'Usage: turbot COMMAND [--bot APP] [command-specific-options]'
122
75
  puts
123
- puts "Primary help topics, type \"turbot help TOPIC\" for more details:"
76
+ puts 'Primary help topics, type "turbot help TOPIC" for more details:'
124
77
  puts
125
78
  summary_for_namespaces(primary_namespaces)
126
79
  puts
127
- puts "Additional topics:"
80
+ puts 'Additional topics:'
128
81
  puts
129
82
  summary_for_namespaces(additional_namespaces)
130
83
  puts
131
84
  end
132
85
 
133
- def help_for_namespace(name)
134
- namespace_commands = commands_for_namespace(name)
86
+ ### Command
87
+
88
+ def commands
89
+ Turbot::Command.commands
90
+ end
135
91
 
136
- unless namespace_commands.empty?
137
- size = longest(namespace_commands.map { |c| c[:banner] })
138
- namespace_commands.sort_by { |c| c[:banner].to_s }.each do |command|
139
- next if skip_command?(command)
140
- command[:summary] ||= legacy_help_for_command(command[:command])
141
- puts " %-#{size}s # %s" % [ command[:banner], command[:summary] ]
142
- end
92
+ def commands_for_namespace(name)
93
+ commands.values.select do |command|
94
+ command[:namespace] == name && command[:command] != name
143
95
  end
144
96
  end
145
97
 
98
+ def help_for_namespace(namespace_commands)
99
+ help_for(namespace_commands, :banner, :summary)
100
+ end
101
+
146
102
  def help_for_command(name)
147
- if command_alias = Turbot::Command.command_aliases[name]
148
- display("Alias: #{name} redirects to #{command_alias}")
103
+ command_alias = Turbot::Command.command_aliases[name]
104
+ if command_alias
105
+ puts "Alias: #{name} redirects to #{command_alias}"
149
106
  name = command_alias
150
107
  end
151
- if command = commands[name]
152
- puts "Usage: turbot #{command[:banner]}"
153
108
 
154
- if command[:help].strip.length > 0
155
- help = command[:help].split("\n").reject do |line|
156
- line =~ /HIDDEN/
157
- end
158
- puts help[1..-1].join("\n")
159
- else
160
- puts
161
- puts " " + legacy_help_for_command(name).to_s
162
- end
109
+ command = commands[name]
110
+ if command
111
+ puts "Usage: turbot #{command[:banner]}"
112
+ puts command[:help].split("\n").drop(1).join("\n")
163
113
  puts
164
114
  end
165
115
 
166
- namespace_commands = commands_for_namespace(name).reject do |command|
167
- command[:help] =~ /DEPRECATED/
168
- end
169
-
116
+ namespace_commands = commands_for_namespace(name)
170
117
  if !namespace_commands.empty?
171
- puts "Additional commands, type \"turbot help COMMAND\" for more details:"
118
+ puts 'Additional commands, type "turbot help COMMAND" for more details:'
172
119
  puts
173
- help_for_namespace(name)
120
+ help_for_namespace(namespace_commands)
174
121
  puts
175
122
  elsif command.nil?
176
123
  error "#{name} is not a turbot command. See `turbot help`."
@@ -1,23 +1,19 @@
1
- require "turbot/command/base"
2
- require "turbot/version"
3
-
4
- # display version
1
+ #Display version
5
2
  #
6
3
  class Turbot::Command::Version < Turbot::Command::Base
7
4
 
8
5
  # version
9
6
  #
10
- # show turbot client version
7
+ #Show the Turbot client's version.
11
8
  #
12
9
  #Example:
13
10
  #
14
- # $ turbot version
15
- # turbot-toolbelt/1.2.3 (x86_64-darwin11.2.0) ruby/1.9.3
11
+ # $ turbot version
12
+ # turbot-gem/0.1.36 (x86_64-darwin14) ruby/2.2.0
16
13
  #
17
14
  def index
18
15
  validate_arguments!
19
-
20
- display(Turbot.user_agent)
16
+ puts "#{Turbot::DEBUG_VERSION}"
21
17
  end
22
-
18
+ alias_command '--version', 'version'
23
19
  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,92 +43,23 @@ 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)
94
- end
95
- end
96
-
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"))
54
+ run('help', [current_command])
55
+ error message
104
56
  end
105
57
  end
106
58
 
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
60
  command = parse(cmd)
127
61
 
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
-
139
62
  @current_command = cmd
140
- @anonymized_args, @normalized_args = [], []
141
63
 
142
64
  opts = {}
143
65
  invalid_options = []
@@ -146,15 +68,10 @@ module Turbot
146
68
  # remove OptionParsers Officious['version'] to avoid conflicts
147
69
  # see: https://github.com/ruby/ruby/blob/trunk/lib/optparse.rb#L814
148
70
  parser.base.long.delete('version')
149
- (global_options + (command && command[:options] || [])).each do |option|
71
+ (command && command[:options] || []).each do |option|
150
72
  parser.on(*option[:args]) do |value|
151
- if option[:proc]
152
- option[:proc].call(value)
153
- end
154
73
  opts[option[:name].gsub('-', '_').to_sym] = value
155
74
  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
75
  end
159
76
  end
160
77
  end
@@ -162,144 +79,84 @@ module Turbot
162
79
  begin
163
80
  parser.order!(args) do |nonopt|
164
81
  invalid_options << nonopt
165
- @anonymized_args << '!'
166
- @normalized_args << '!'
167
82
  end
168
- rescue OptionParser::InvalidOption => ex
169
- invalid_options << ex.args.first
170
- @anonymized_args << '!'
171
- @normalized_args << '!'
83
+ rescue OptionParser::InvalidOption => e
84
+ invalid_options << e.args.first
172
85
  retry
173
86
  end
174
87
 
175
88
  args.concat(invalid_options)
176
89
 
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
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=[])
104
+ def self.run(command, arguments=[])
217
105
  begin
218
- object, method = prepare_run(cmd, arguments.dup)
106
+ object, method = prepare_run(command, arguments.dup)
219
107
  object.send(method)
220
108
  rescue Interrupt, StandardError, SystemExit => error
221
109
  # load likely error classes, as they may not be loaded yet due to defered loads
222
110
  require 'rest_client'
223
111
  raise(error)
224
112
  end
225
- rescue Turbot::API::Errors::Unauthorized, RestClient::Unauthorized
226
- puts "Authentication failure"
227
- if ENV['TURBOT_API_KEY']
228
- exit 1
113
+ rescue SocketError => e
114
+ error 'Unable to connect to Turbot API, please check internet connectivity and try again.'
115
+ rescue OptionParser::ParseError
116
+ if commands[command]
117
+ run('help', [command])
229
118
  else
230
- run "login"
231
- retry
119
+ run('help')
232
120
  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
252
- 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
121
  end
271
122
 
272
- def self.parse(cmd)
273
- commands[cmd] || commands[command_aliases[cmd]]
123
+ def self.parse(command)
124
+ commands[command] || commands[command_aliases[command]]
274
125
  end
275
126
 
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
127
+ def self.extract_error(body, options = {})
128
+ if block_given?
129
+ default_error = yield
130
+ else
131
+ default_error = 'Internal server error'
132
+ end
133
+ parse_error_json(body) || default_error
286
134
  end
287
135
 
288
136
  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
137
+ begin
138
+ JSON.load(body.to_s)['message']
139
+ rescue JSON::ParserError
296
140
  nil
297
141
  end
298
142
  end
299
143
 
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
144
+ private
145
+
146
+ def self.suggestion(actual, possibilities)
147
+ distances = Hash.new { |hash,key| hash[key] = [] }
148
+ possibilities.each do |possibility|
149
+ distances[Text::Levenshtein.distance(actual, possibility, 4)] << possibility
150
+ end
151
+ minimum_distance = distances.keys.min
152
+ if minimum_distance < 4
153
+ suggestions = distances[minimum_distance].sort
154
+ if suggestions.length == 1
155
+ "Perhaps you meant `#{suggestions.first}`."
156
+ else
157
+ "Perhaps you meant #{suggestions[0...-1].map { |suggestion| "`#{suggestion}`" }.join(', ')} or `#{suggestions.last}`."
158
+ end
159
+ end
303
160
  end
304
161
  end
305
162
  end
@@ -0,0 +1,21 @@
1
+ module Turbot
2
+ module Handlers
3
+ class BaseHandler < TurbotRunner::BaseHandler
4
+ # Implements `TurbotRunner::BaseHandler#handle_invalid_record`.
5
+ def handle_invalid_record(record, data_type, error_message)
6
+ puts
7
+ puts 'The following record is invalid:'
8
+ puts record.to_json
9
+ puts " * #{error_message}"
10
+ puts
11
+ end
12
+
13
+ # Implements `TurbotRunner::BaseHandler#handle_invalid_json`.
14
+ def handle_invalid_json(line)
15
+ puts
16
+ puts 'The following line was not valid JSON:'
17
+ puts line
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ module Turbot
2
+ module Handlers
3
+ class DumpHandler < BaseHandler
4
+ # Implements `TurbotRunner::BaseHandler#handle_valid_record`.
5
+ def handle_valid_record(record, data_type)
6
+ puts record.to_json
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,30 @@
1
+ module Turbot
2
+ module Handlers
3
+ class PreviewHandler < BaseHandler
4
+ attr_reader :count
5
+
6
+ def initialize(bot_name, api)
7
+ @bot_name = bot_name
8
+ @api = api
9
+ @count = 0
10
+ @batch = []
11
+ end
12
+
13
+ # Implements `TurbotRunner::BaseHandler#handle_valid_record`.
14
+ def handle_valid_record(record, data_type)
15
+ @count += 1
16
+ STDOUT.write('.')
17
+ @batch << record.merge(:data_type => data_type)
18
+ if @count % 20 == 0
19
+ submit_batch
20
+ end
21
+ end
22
+
23
+ def submit_batch
24
+ result = @api.create_draft_data(@bot_name, @batch.to_json)
25
+ @batch = []
26
+ result
27
+ end
28
+ end
29
+ end
30
+ end