aspera-cli 4.17.0 → 4.18.0

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 (81) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +2 -4
  3. data/CHANGELOG.md +23 -0
  4. data/CONTRIBUTING.md +15 -1
  5. data/README.md +620 -378
  6. data/bin/ascli +5 -0
  7. data/bin/asession +2 -2
  8. data/lib/aspera/agent/alpha.rb +6 -4
  9. data/lib/aspera/agent/base.rb +9 -6
  10. data/lib/aspera/agent/connect.rb +4 -4
  11. data/lib/aspera/agent/direct.rb +56 -37
  12. data/lib/aspera/agent/httpgw.rb +23 -324
  13. data/lib/aspera/agent/node.rb +19 -20
  14. data/lib/aspera/agent/trsdk.rb +19 -20
  15. data/lib/aspera/api/aoc.rb +17 -14
  16. data/lib/aspera/api/cos_node.rb +4 -4
  17. data/lib/aspera/api/httpgw.rb +339 -0
  18. data/lib/aspera/api/node.rb +34 -21
  19. data/lib/aspera/ascmd.rb +4 -3
  20. data/lib/aspera/ascp/installation.rb +15 -7
  21. data/lib/aspera/ascp/management.rb +2 -2
  22. data/lib/aspera/cli/basic_auth_plugin.rb +5 -9
  23. data/lib/aspera/cli/extended_value.rb +12 -6
  24. data/lib/aspera/cli/formatter.rb +155 -65
  25. data/lib/aspera/cli/hints.rb +18 -0
  26. data/lib/aspera/cli/main.rb +22 -29
  27. data/lib/aspera/cli/manager.rb +53 -36
  28. data/lib/aspera/cli/plugin.rb +26 -17
  29. data/lib/aspera/cli/plugin_factory.rb +31 -20
  30. data/lib/aspera/cli/plugins/alee.rb +14 -2
  31. data/lib/aspera/cli/plugins/aoc.rb +141 -131
  32. data/lib/aspera/cli/plugins/ats.rb +1 -1
  33. data/lib/aspera/cli/plugins/config.rb +52 -46
  34. data/lib/aspera/cli/plugins/console.rb +8 -5
  35. data/lib/aspera/cli/plugins/faspex.rb +27 -19
  36. data/lib/aspera/cli/plugins/faspex5.rb +222 -149
  37. data/lib/aspera/cli/plugins/faspio.rb +85 -0
  38. data/lib/aspera/cli/plugins/httpgw.rb +55 -0
  39. data/lib/aspera/cli/plugins/node.rb +86 -29
  40. data/lib/aspera/cli/plugins/orchestrator.rb +31 -29
  41. data/lib/aspera/cli/plugins/preview.rb +6 -2
  42. data/lib/aspera/cli/plugins/server.rb +5 -5
  43. data/lib/aspera/cli/plugins/shares.rb +16 -14
  44. data/lib/aspera/cli/sync_actions.rb +6 -6
  45. data/lib/aspera/cli/transfer_agent.rb +5 -4
  46. data/lib/aspera/cli/version.rb +1 -1
  47. data/lib/aspera/environment.rb +7 -6
  48. data/lib/aspera/faspex_gw.rb +5 -4
  49. data/lib/aspera/faspex_postproc.rb +2 -2
  50. data/lib/aspera/log.rb +6 -3
  51. data/lib/aspera/node_simulator.rb +2 -2
  52. data/lib/aspera/oauth/base.rb +31 -19
  53. data/lib/aspera/oauth/factory.rb +12 -13
  54. data/lib/aspera/oauth/generic.rb +1 -0
  55. data/lib/aspera/oauth/jwt.rb +18 -15
  56. data/lib/aspera/oauth/url_json.rb +8 -6
  57. data/lib/aspera/open_application.rb +5 -7
  58. data/lib/aspera/persistency_folder.rb +2 -2
  59. data/lib/aspera/preview/generator.rb +3 -3
  60. data/lib/aspera/preview/options.rb +3 -3
  61. data/lib/aspera/preview/terminal.rb +4 -4
  62. data/lib/aspera/preview/utils.rb +3 -3
  63. data/lib/aspera/proxy_auto_config.rb +5 -1
  64. data/lib/aspera/rest.rb +60 -74
  65. data/lib/aspera/rest_call_error.rb +1 -1
  66. data/lib/aspera/rest_error_analyzer.rb +2 -2
  67. data/lib/aspera/rest_errors_aspera.rb +1 -1
  68. data/lib/aspera/resumer.rb +1 -1
  69. data/lib/aspera/secret_hider.rb +2 -4
  70. data/lib/aspera/ssh.rb +1 -1
  71. data/lib/aspera/transfer/parameters.rb +39 -36
  72. data/lib/aspera/transfer/spec.rb +2 -0
  73. data/lib/aspera/transfer/sync.rb +2 -1
  74. data/lib/aspera/transfer/uri.rb +1 -1
  75. data/lib/aspera/uri_reader.rb +5 -4
  76. data/lib/aspera/web_auth.rb +1 -1
  77. data/lib/aspera/web_server_simple.rb +4 -3
  78. data.tar.gz.sig +0 -0
  79. metadata +5 -3
  80. metadata.gz.sig +0 -0
  81. data/lib/aspera/cli/plugins/bss.rb +0 -71
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # cspell:ignore jsonpp
4
+ require 'aspera/preview/terminal'
4
5
  require 'aspera/secret_hider'
5
6
  require 'aspera/environment'
6
7
  require 'aspera/log'
7
8
  require 'aspera/assert'
8
9
  require 'terminal-table'
10
+ require 'tty-spinner'
9
11
  require 'yaml'
10
12
  require 'pp'
11
13
 
@@ -14,8 +16,9 @@ module Aspera
14
16
  CONF_OVERVIEW_KEYS = %w[preset parameter value].freeze
15
17
  # This class is used to transform a complex structure into a simple hash
16
18
  class Flattener
17
- def initialize
19
+ def initialize(formatter)
18
20
  @result = nil
21
+ @formatter = formatter
19
22
  end
20
23
 
21
24
  # General method
@@ -48,9 +51,9 @@ module Aspera
48
51
  elsif something.is_a?(Array)
49
52
  flatten_array(something, name)
50
53
  elsif something.is_a?(String) && something.empty?
51
- @result[name] = Formatter.special('empty string')
54
+ @result[name] = @formatter.special_format('empty string')
52
55
  elsif something.nil?
53
- @result[name] = Formatter.special('null')
56
+ @result[name] = @formatter.special_format('null')
54
57
  # elsif something.eql?(true) || something.eql?(false)
55
58
  # @result[name] = something
56
59
  else
@@ -63,7 +66,7 @@ module Aspera
63
66
  # @param name [String] name of englobing key
64
67
  def flatten_array(array, name)
65
68
  if array.empty?
66
- @result[name] = Formatter.special('empty list')
69
+ @result[name] = @formatter.special_format('empty list')
67
70
  elsif array.all?(String)
68
71
  @result[name] = array.join("\n")
69
72
  elsif array.all?{|i| i.is_a?(Hash) && i.keys.eql?(%w[name])}
@@ -85,18 +88,18 @@ module Aspera
85
88
  flatten_any(v, "#{prefix}#{k}")
86
89
  end
87
90
  end
88
- end # class
91
+ end
89
92
 
90
93
  # Take care of output
91
94
  class Formatter
95
+ # remove a fields from the list
92
96
  FIELDS_LESS = '-'
93
97
  CSV_RECORD_SEPARATOR = "\n"
94
98
  CSV_FIELD_SEPARATOR = ','
95
99
  # supported output formats
96
- DISPLAY_FORMATS = %i[text nagios ruby json jsonpp yaml table csv].freeze
100
+ DISPLAY_FORMATS = %i[text nagios ruby json jsonpp yaml table csv image].freeze
97
101
  # user output levels
98
102
  DISPLAY_LEVELS = %i[info data error].freeze
99
- RESULT_PARAMS = %i[type data total fields name].freeze
100
103
 
101
104
  private_constant :DISPLAY_FORMATS, :DISPLAY_LEVELS, :CSV_RECORD_SEPARATOR, :CSV_FIELD_SEPARATOR
102
105
  # prefix to display error messages in user messages (terminal)
@@ -105,19 +108,6 @@ module Aspera
105
108
  HINT_FLASH = 'HINT:'.bg_green.gray.blink.freeze
106
109
 
107
110
  class << self
108
- # Highlight special values
109
- def special(what, use_colors: $stdout.isatty)
110
- result = $stdout.isatty ? "<#{what}>" : "&lt;#{what}&gt;"
111
- if use_colors
112
- result = if %w[null empty].any?{|s|what.include?(s)}
113
- result.dim
114
- else
115
- result.reverse_color
116
- end
117
- end
118
- return result
119
- end
120
-
121
111
  def all_but(list)
122
112
  list = [list] unless list.is_a?(Array)
123
113
  return list.map{|i|"#{FIELDS_LESS}#{i}"}.unshift(ExtendedValue::ALL)
@@ -125,7 +115,7 @@ module Aspera
125
115
 
126
116
  def tick(yes)
127
117
  result =
128
- if Environment.use_unicode?
118
+ if Environment.terminal_supports_unicode?
129
119
  if yes
130
120
  "\u2713"
131
121
  else
@@ -150,24 +140,55 @@ module Aspera
150
140
  end
151
141
  return result
152
142
  end
153
- end # self
143
+ end
154
144
 
155
145
  # initialize the formatter
156
146
  def initialize
157
147
  @options = {}
148
+ @spinner = nil
149
+ end
150
+
151
+ # Highlight special values
152
+ def special_format(what, use_colors: $stdout.isatty)
153
+ result = $stdout.isatty ? "<#{what}>" : "&lt;#{what}&gt;"
154
+ if use_colors
155
+ result = if %w[null empty].any?{|s|what.include?(s)}
156
+ result.dim
157
+ else
158
+ result.reverse_color
159
+ end
160
+ end
161
+ return result
158
162
  end
159
163
 
164
+ # call this after REST calls if several api calls are expected
165
+ def long_operation_running(title = '')
166
+ return unless Environment.terminal?
167
+ if @spinner.nil?
168
+ @spinner = TTY::Spinner.new('[:spinner] :title', format: :classic)
169
+ @spinner.start
170
+ end
171
+ @spinner.update(title: title)
172
+ @spinner.spin
173
+ end
174
+
175
+ # options are: format, output, display, fields, select, table_style, flat_hash, transpose_single
160
176
  def option_handler(option_symbol, operation, value=nil)
161
177
  Aspera.assert_values(operation, %i[set get])
162
178
  case operation
163
179
  when :set
164
180
  @options[option_symbol] = value
165
- if option_symbol.eql?(:output)
181
+ case option_symbol
182
+ when :output
166
183
  $stdout = if value.eql?('-')
167
184
  STDOUT # rubocop:disable Style/GlobalStdStream
168
185
  else
169
186
  File.open(value, 'w')
170
187
  end
188
+ when :image
189
+ allowed_options = Preview::Terminal.method(:build).parameters.select{|i|i[0].eql?(:key)}.map{|i|i[1]}
190
+ unknown_options = value.keys.map(&:to_sym) - allowed_options
191
+ raise "Invalid parameter(s) for option image: #{unknown_options.join(', ')}, use #{allowed_options.join(', ')}" unless unknown_options.empty?
171
192
  end
172
193
  when :get then return @options[option_symbol]
173
194
  else Aspera.error_unreachable_line
@@ -176,6 +197,12 @@ module Aspera
176
197
  end
177
198
 
178
199
  def declare_options(options)
200
+ default_table_style = if Environment.terminal_supports_unicode?
201
+ {border: :unicode_round}
202
+ else
203
+ {}
204
+ end
205
+
179
206
  options.declare(:format, 'Output format', values: DISPLAY_FORMATS, handler: {o: self, m: :option_handler}, default: :table)
180
207
  options.declare(:output, 'Destination for results', types: String, handler: {o: self, m: :option_handler})
181
208
  options.declare(:display, 'Output only some information', values: DISPLAY_LEVELS, handler: {o: self, m: :option_handler}, default: :info)
@@ -184,10 +211,11 @@ module Aspera
184
211
  types: [String, Array, Regexp, Proc],
185
212
  default: ExtendedValue::DEF)
186
213
  options.declare(:select, 'Select only some items in lists: column, value', types: [Hash, Proc], handler: {o: self, m: :option_handler})
187
- options.declare(:table_style, 'Table display style', handler: {o: self, m: :option_handler}, default: ':.:')
214
+ options.declare(:table_style, 'Table display style', types: [Hash], handler: {o: self, m: :option_handler}, default: default_table_style)
188
215
  options.declare(:flat_hash, 'Display deep values as additional keys', values: :bool, handler: {o: self, m: :option_handler}, default: true)
189
216
  options.declare(:transpose_single, 'Single object fields output vertically', values: :bool, handler: {o: self, m: :option_handler}, default: true)
190
217
  options.declare(:show_secrets, 'Show secrets on command output', values: :bool, handler: {o: self, m: :option_handler}, default: false)
218
+ options.declare(:image, 'Options for image display', types: Hash, handler: {o: self, m: :option_handler}, default: {})
191
219
  end
192
220
 
193
221
  # main output method
@@ -220,14 +248,15 @@ module Aspera
220
248
  data.each_with_object({}){|v, m|v.each_key{|c|m[c] = true}}.keys
221
249
  end
222
250
 
223
- # this method computes the list of fields to display
224
- # data: array of hash
225
- # default: list of fields to display by default (may contain special values)
251
+ # @return the list of fields to display
252
+ # @param data [Array<Hash>] data to display
253
+ # @param default [Array<String>, Proc] list of fields to display by default (may contain special values)
226
254
  def compute_fields(data, default)
227
255
  Log.log.debug{"compute_fields: data:#{data.class} default:#{default.class} #{default}"}
256
+ # the requested list of fields, but if can contain special values
228
257
  request =
229
258
  case @options[:fields]
230
- when NilClass then [ExtendedValue::DEF]
259
+ # when NilClass then [ExtendedValue::DEF]
231
260
  when String then @options[:fields].split(',')
232
261
  when Array then @options[:fields]
233
262
  when Regexp then return all_fields(data).select{|i|i.match(@options[:fields])}
@@ -261,20 +290,39 @@ module Aspera
261
290
  return result
262
291
  end
263
292
 
293
+ # filter the list of items on the fields option
294
+ def filter_list_on_fields(data)
295
+ # by default, keep all data intact
296
+ return data if @options[:fields].eql?(ExtendedValue::DEF) && @options[:select].nil?
297
+ Aspera.assert_type(data, Array){'Filtering fields or select requires result is an Array of Hash'}
298
+ Aspera.assert(data.all?(Hash)){'Filtering fields or select requires result is an Array of Hash'}
299
+ filter_columns_on_select(data)
300
+ return data if @options[:fields].eql?(ExtendedValue::DEF)
301
+ selected_fields = compute_fields(data, @options[:fields])
302
+ return data.map{|i|i[selected_fields.first]} if selected_fields.length == 1
303
+ return data.map{|i|i.select{|k, _|selected_fields.include?(k)}}
304
+ end
305
+
306
+ # filter the list of items on the select option
307
+ # @param data [Array<Hash>] list of items
308
+ def filter_columns_on_select(data)
309
+ case @options[:select]
310
+ when Proc
311
+ data.select!{|i|@options[:select].call(i)}
312
+ when Hash
313
+ @options[:select].each{|k, v|data.select!{|i|i[k].eql?(v)}}
314
+ end
315
+ end
316
+
264
317
  # this method displays a table
265
318
  # object_array: array of hash
266
319
  # fields: list of column names
267
320
  def display_table(object_array, fields)
268
321
  Aspera.assert(!fields.nil?){'missing fields parameter'}
269
- case @options[:select]
270
- when Proc
271
- object_array.select!{|i|@options[:select].call(i)}
272
- when Hash
273
- @options[:select].each{|k, v|object_array.select!{|i|i[k].eql?(v)}}
274
- end
322
+ filter_columns_on_select(object_array)
275
323
  if object_array.empty?
276
324
  # no display for csv
277
- display_message(:info, Formatter.special('empty')) if @options[:format].eql?(:table)
325
+ display_message(:info, special_format('empty')) if @options[:format].eql?(:table)
278
326
  return
279
327
  end
280
328
  if object_array.length == 1 && fields.length == 1
@@ -294,72 +342,114 @@ module Aspera
294
342
  # here : fields : list of column names
295
343
  case @options[:format]
296
344
  when :table
297
- style = @options[:table_style].chars
298
345
  # display the table !
299
346
  display_message(:data, Terminal::Table.new(
300
347
  headings: fields,
301
348
  rows: final_table_rows,
302
- border_x: style[0],
303
- border_y: style[1],
304
- border_i: style[2]))
349
+ style: @options[:table_style]&.symbolize_keys))
305
350
  when :csv
306
351
  display_message(:data, final_table_rows.map{|t| t.join(CSV_FIELD_SEPARATOR)}.join(CSV_RECORD_SEPARATOR))
307
352
  end
308
353
  end
309
354
 
355
+ # @return text suitable to display an image from url
356
+ def status_image(blob)
357
+ begin
358
+ raise URI::InvalidURIError, 'not uri' if !(blob =~ /\A#{URI::DEFAULT_PARSER.make_regexp}\z/)
359
+ # it's a url
360
+ url = blob
361
+ unless OpenApplication.instance.url_method.eql?(:text)
362
+ OpenApplication.instance.uri(url)
363
+ return ''
364
+ end
365
+ # remote_image = Rest.new(base_url: url).read('')
366
+ # mime = remote_image[:http]['content-type']
367
+ # blob = remote_image[:http].body
368
+ # Log.log.warn("Image ? #{remote_image[:http]['content-type']}") unless mime.include?('image/')
369
+ blob = UriReader.read(url)
370
+ rescue URI::InvalidURIError
371
+ nil
372
+ end
373
+ # try base64
374
+ begin
375
+ blob = Base64.strict_decode64(blob)
376
+ rescue
377
+ nil
378
+ end
379
+ return Preview::Terminal.build(blob, **@options[:image].symbolize_keys)
380
+ end
381
+
310
382
  # this method displays the results, especially the table format
311
- def display_results(results)
312
- Aspera.assert_type(results, Hash)
313
- Aspera.assert((results.keys - RESULT_PARAMS).empty?){"result unsupported key: #{results.keys - RESULT_PARAMS}"}
314
- Aspera.assert(results.key?(:type)){"result must have type (#{results})"}
315
- Aspera.assert(results.key?(:data) || %i[empty nothing].include?(results[:type])){'result must have data'}
316
- Log.log.debug{"display_results: #{results[:data].class} #{results[:type]}"}
317
- display_item_count(results[:data].length, results[:total]) if results.key?(:total)
318
- SecretHider.deep_remove_secret(results[:data]) unless @options[:show_secrets] || @options[:display].eql?(:data)
383
+ # @param type [Symbol] type of data
384
+ # @param data [Object] data to display
385
+ # @param total [Integer] total number of items
386
+ # @param fields [Array<String>] list of fields to display
387
+ # @param name [String] name of the column to display
388
+ def display_results(type:, data: nil, total: nil, fields: nil, name: nil)
389
+ Log.log.debug{"display_results: #{type} class=#{data.class} data=#{data}"}
390
+ Aspera.assert_type(type, Symbol){'result must have type'}
391
+ Aspera.assert(!data.nil? || %i[empty nothing].include?(type)){'result must have data'}
392
+ display_item_count(data.length, total) unless total.nil?
393
+ SecretHider.deep_remove_secret(data) unless @options[:show_secrets] || @options[:display].eql?(:data)
319
394
  case @options[:format]
320
395
  when :text
321
- display_message(:data, results[:data].to_s)
396
+ display_message(:data, data.to_s)
322
397
  when :nagios
323
- Nagios.process(results[:data])
398
+ Nagios.process(data)
324
399
  when :ruby
325
- display_message(:data, PP.pp(results[:data], +''))
400
+ display_message(:data, PP.pp(filter_list_on_fields(data), +''))
326
401
  when :json
327
- display_message(:data, JSON.generate(results[:data]))
402
+ display_message(:data, JSON.generate(filter_list_on_fields(data)))
328
403
  when :jsonpp
329
- display_message(:data, JSON.pretty_generate(results[:data]))
404
+ display_message(:data, JSON.pretty_generate(filter_list_on_fields(data)))
330
405
  when :yaml
331
- display_message(:data, results[:data].to_yaml)
406
+ display_message(:data, YAML.dump(filter_list_on_fields(data)))
407
+ when :image
408
+ # assume it is an url
409
+ url = data
410
+ case type
411
+ when :single_object, :object_list
412
+ url = [url] if type.eql?(:single_object)
413
+ raise 'image display requires a single result' unless url.length == 1
414
+ fields = compute_fields(url, fields)
415
+ raise 'select a field to display' unless fields.length == 1
416
+ url = url.first
417
+ raise 'no such field' unless url.key?(fields.first)
418
+ url = url[fields.first]
419
+ end
420
+ raise "not url: #{url.class} #{url}" unless url.is_a?(String)
421
+ display_message(:data, status_image(url))
332
422
  when :table, :csv
333
- case results[:type]
423
+ case type
334
424
  when :config_over
335
- display_table(Flattener.new.config_over(results[:data]), CONF_OVERVIEW_KEYS)
425
+ display_table(Flattener.new(self).config_over(data), CONF_OVERVIEW_KEYS)
336
426
  when :object_list, :single_object
337
- obj_list = results[:data]
338
- obj_list = [obj_list] if results[:type].eql?(:single_object)
427
+ obj_list = data
428
+ obj_list = [obj_list] if type.eql?(:single_object)
339
429
  Aspera.assert_type(obj_list, Array)
340
430
  Aspera.assert(obj_list.all?(Hash)){"expecting Array of Hash: #{obj_list.inspect}"}
341
431
  # :object_list is an array of hash tables, where key=colum name
342
- obj_list = obj_list.map{|obj|Flattener.new.flatten(obj)} if @options[:flat_hash]
343
- display_table(obj_list, compute_fields(obj_list, results[:fields]))
432
+ obj_list = obj_list.map{|obj|Flattener.new(self).flatten(obj)} if @options[:flat_hash]
433
+ display_table(obj_list, compute_fields(obj_list, fields))
344
434
  when :value_list
345
435
  # :value_list is a simple array of values, name of column provided in the :name
346
- display_table(results[:data].map { |i| { results[:name] => i } }, [results[:name]])
436
+ display_table(data.map { |i| { name => i } }, [name])
347
437
  when :empty # no table
348
- display_message(:info, Formatter.special('empty'))
438
+ display_message(:info, special_format('empty'))
349
439
  return
350
440
  when :nothing # no result expected
351
441
  Log.log.debug('no result expected')
352
442
  when :status # no table
353
443
  # :status displays a simple message
354
- display_message(:info, results[:data])
444
+ display_message(:info, data)
355
445
  when :text # no table
356
446
  # :status displays a simple message
357
- display_message(:data, results[:data])
447
+ display_message(:data, data)
358
448
  when :other_struct # no table
359
449
  # :other_struct is any other type of structure
360
- display_message(:data, PP.pp(results[:data], +''))
450
+ display_message(:data, PP.pp(data, +''))
361
451
  else
362
- raise "unknown data type: #{results[:type]}"
452
+ raise "unknown data type: #{type}"
363
453
  end
364
454
  end
365
455
  end
@@ -47,6 +47,24 @@ module Aspera
47
47
  'if you provide a path: prefix with @file:',
48
48
  'e.g. --private-key=@file:/path/to/key.pem'
49
49
  ]
50
+ },
51
+ {
52
+ exception: RuntimeError,
53
+ match: /unexpected last event type: INIT/,
54
+ remediation: [
55
+ 'ascp exited unexpectedly',
56
+ 'it might be due to SSH handshake failure',
57
+ 'one can check SSH connection using ssh command'
58
+ ]
59
+ },
60
+ {
61
+ exception: RuntimeError,
62
+ match: /Wrong remote host:/,
63
+ remediation: [
64
+ 'If remote node is Cloud Pak For Integration',
65
+ 'Make sure that a LoadBalancer is active on cluster',
66
+ 'Check the external address of Aspera tcp-proxy pod'
67
+ ]
50
68
  }
51
69
  ]
52
70
 
@@ -9,7 +9,6 @@ require 'aspera/cli/transfer_agent'
9
9
  require 'aspera/cli/version'
10
10
  require 'aspera/cli/info'
11
11
  require 'aspera/cli/hints'
12
- require 'aspera/preview/terminal'
13
12
  require 'aspera/secret_hider'
14
13
  require 'aspera/log'
15
14
  require 'aspera/assert'
@@ -20,9 +19,10 @@ module Aspera
20
19
  class Main
21
20
  # Plugins store transfer result using this key and use result_transfer_multiple()
22
21
  STATUS_FIELD = 'status'
23
- CONF_PLUGIN_SYM = :config
22
+ COMMAND_CONFIG = :config
23
+ COMMAND_HELP = :help
24
24
 
25
- private_constant :CONF_PLUGIN_SYM
25
+ private_constant :COMMAND_CONFIG, :COMMAND_HELP
26
26
 
27
27
  class << self
28
28
  # expect some list, but nothing to display
@@ -60,14 +60,10 @@ module Aspera
60
60
  return {type: :object_list, data: status_table}
61
61
  end
62
62
 
63
- def result_picture_in_terminal(options, blob)
64
- terminal_options = options.get_option(:query, default: {}).symbolize_keys
65
- allowed_options = Preview::Terminal.method(:build).parameters.select{|i|i[0].eql?(:key)}.map{|i|i[1]}
66
- unknown_options = terminal_options.keys - allowed_options
67
- raise "invalid options: #{unknown_options.join(', ')}, use #{allowed_options.join(', ')}" unless unknown_options.empty?
68
- return Main.result_status(Preview::Terminal.build(blob, **terminal_options))
63
+ def result_image(blob, formatter:)
64
+ return Main.result_status(formatter.status_image(blob))
69
65
  end
70
- end # self
66
+ end
71
67
 
72
68
  private
73
69
 
@@ -88,6 +84,8 @@ module Aspera
88
84
  @option_help = false
89
85
  @option_show_config = false
90
86
  @bash_completion = false
87
+ early_debug_setup
88
+ Log.log.trace2{Log.dump(:argv, @argv)}
91
89
  end
92
90
 
93
91
  # This can throw exception if there is a problem with the environment, needs to be caught by execute method
@@ -95,8 +93,6 @@ module Aspera
95
93
  @plug_init[:only_manual] = false
96
94
  # create formatter, in case there is an exception, it is used to display.
97
95
  @plug_init[:formatter] = Formatter.new
98
- # second : manage debug level (allows debugging of option parser)
99
- early_debug_setup
100
96
  @plug_init[:options] = Manager.new(PROGRAM_NAME)
101
97
  # give command line arguments to option manager
102
98
  options.parse_command_line(@argv)
@@ -164,10 +160,9 @@ module Aspera
164
160
  options.declare(:bash_comp, 'Generate bash completion for command', values: :none) { @bash_completion = true }
165
161
  options.declare(:show_config, 'Display parameters used for the provided action', values: :none) { @option_show_config = true }
166
162
  options.declare(:version, 'Display version', values: :none, short: 'v') { formatter.display_message(:data, Cli::VERSION); Process.exit(0) } # rubocop:disable Style/Semicolon, Layout/LineLength
167
- options.declare(:warnings, 'Check for language warnings', values: :none, short: 'w') { $VERBOSE = true }
168
163
  options.declare(
169
164
  :ui, 'Method to start browser',
170
- values: OpenApplication.user_interfaces,
165
+ values: OpenApplication::USER_INTERFACES,
171
166
  handler: {o: OpenApplication.instance, m: :url_method},
172
167
  default: OpenApplication.default_gui_mode)
173
168
  options.declare(:log_level, 'Log level', values: Log.levels, handler: {o: Log.instance, m: :level})
@@ -187,17 +182,15 @@ module Aspera
187
182
  def get_plugin_instance_with_options(plugin_name_sym, env=nil)
188
183
  env ||= @plug_init
189
184
  Log.log.debug{"get_plugin_instance_with_options(#{plugin_name_sym})"}
190
- require PluginFactory.instance.plugins[plugin_name_sym][:require_stanza]
191
185
  # load default params only if no param already loaded before plugin instantiation
192
186
  env[:config].add_plugin_default_preset(plugin_name_sym)
193
187
  command_plugin = PluginFactory.instance.create(plugin_name_sym, **env)
194
- Log.log.debug{"got #{command_plugin.class}"}
195
188
  return command_plugin
196
189
  end
197
190
 
198
191
  def generate_bash_completion
199
192
  if options.get_next_argument('', expected: :multiple, mandatory: false).nil?
200
- PluginFactory.instance.plugins.each_key{|p|puts p}
193
+ PluginFactory.instance.plugin_list.each{|p|puts p}
201
194
  else
202
195
  Log.log.warn('only first level completion so far')
203
196
  end
@@ -210,8 +203,8 @@ module Aspera
210
203
  formatter.display_message(:error, options.parser)
211
204
  if all_plugins
212
205
  # list plugins that have a "require" field, i.e. all but main plugin
213
- PluginFactory.instance.plugins.each_key do |plugin_name_sym|
214
- next if plugin_name_sym.eql?(CONF_PLUGIN_SYM)
206
+ PluginFactory.instance.plugin_list.each do |plugin_name_sym|
207
+ next if plugin_name_sym.eql?(COMMAND_CONFIG)
215
208
  # override main option parser with a brand new, to avoid having global options
216
209
  plugin_env = @plug_init.clone
217
210
  plugin_env[:only_manual] = true # force declaration of all options
@@ -261,17 +254,17 @@ module Aspera
261
254
  config.periodic_check_newer_gem_version
262
255
  command_sym =
263
256
  if @option_show_config && options.command_or_arg_empty?
264
- CONF_PLUGIN_SYM
257
+ COMMAND_CONFIG
265
258
  else
266
- options.get_next_command(PluginFactory.instance.plugins.keys.dup.unshift(:help))
259
+ options.get_next_command(PluginFactory.instance.plugin_list.unshift(COMMAND_HELP))
267
260
  end
268
261
  # command will not be executed, but we need manual
269
262
  options.fail_on_missing_mandatory = false if @option_help || @option_show_config
270
263
  # main plugin is not dynamically instantiated
271
264
  case command_sym
272
- when :help
265
+ when COMMAND_HELP
273
266
  exit_with_usage(true)
274
- when CONF_PLUGIN_SYM
267
+ when COMMAND_CONFIG
275
268
  command_plugin = config
276
269
  else
277
270
  # get plugin, set options, etc
@@ -282,7 +275,7 @@ module Aspera
282
275
  # help requested for current plugin
283
276
  exit_with_usage(false) if @option_help
284
277
  if @option_show_config
285
- formatter.display_results({type: :single_object, data: options.known_options(only_defined: true).stringify_keys})
278
+ formatter.display_results(type: :single_object, data: options.known_options(only_defined: true).stringify_keys)
286
279
  execute_command = false
287
280
  end
288
281
  # locking for single execution (only after "per plugin" option, in case lock port is there)
@@ -304,7 +297,7 @@ module Aspera
304
297
  at_exit{File.delete(pid_file)}
305
298
  end
306
299
  # execute and display (if not exclusive execution)
307
- formatter.display_results(command_plugin.execute_action) if execute_command
300
+ formatter.display_results(**command_plugin.execute_action) if execute_command
308
301
  # save config file if command modified it
309
302
  config.save_config_file_if_needed
310
303
  # finish
@@ -347,7 +340,7 @@ module Aspera
347
340
  Process.exit(1)
348
341
  end
349
342
  return nil
350
- end # process_command_line
351
- end # Main
352
- end # Cli
353
- end # Aspera
343
+ end
344
+ end
345
+ end
346
+ end