aspera-cli 4.20.0 → 4.21.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +41 -3
  4. data/CONTRIBUTING.md +69 -142
  5. data/README.md +687 -461
  6. data/bin/ascli +5 -14
  7. data/bin/asession +3 -5
  8. data/examples/get_proto_file.rb +4 -3
  9. data/examples/proxy.pac +20 -20
  10. data/lib/aspera/agent/base.rb +2 -0
  11. data/lib/aspera/agent/connect.rb +20 -2
  12. data/lib/aspera/agent/{alpha.rb → desktop.rb} +12 -18
  13. data/lib/aspera/agent/direct.rb +30 -31
  14. data/lib/aspera/agent/node.rb +1 -11
  15. data/lib/aspera/agent/{trsdk.rb → transferd.rb} +37 -51
  16. data/lib/aspera/api/alee.rb +1 -1
  17. data/lib/aspera/api/aoc.rb +13 -8
  18. data/lib/aspera/api/cos_node.rb +1 -1
  19. data/lib/aspera/api/node.rb +49 -32
  20. data/lib/aspera/ascp/installation.rb +98 -77
  21. data/lib/aspera/ascp/management.rb +27 -6
  22. data/lib/aspera/cli/extended_value.rb +9 -3
  23. data/lib/aspera/cli/formatter.rb +155 -154
  24. data/lib/aspera/cli/info.rb +2 -1
  25. data/lib/aspera/cli/main.rb +12 -0
  26. data/lib/aspera/cli/manager.rb +4 -4
  27. data/lib/aspera/cli/plugin.rb +2 -2
  28. data/lib/aspera/cli/plugins/aoc.rb +134 -73
  29. data/lib/aspera/cli/plugins/config.rb +114 -83
  30. data/lib/aspera/cli/plugins/cos.rb +1 -0
  31. data/lib/aspera/cli/plugins/faspex.rb +4 -2
  32. data/lib/aspera/cli/plugins/faspex5.rb +29 -14
  33. data/lib/aspera/cli/plugins/node.rb +51 -41
  34. data/lib/aspera/cli/transfer_progress.rb +2 -0
  35. data/lib/aspera/cli/version.rb +1 -1
  36. data/lib/aspera/command_line_builder.rb +1 -1
  37. data/lib/aspera/coverage.rb +5 -3
  38. data/lib/aspera/environment.rb +59 -16
  39. data/lib/aspera/faspex_postproc.rb +3 -5
  40. data/lib/aspera/hash_ext.rb +2 -12
  41. data/lib/aspera/node_simulator.rb +230 -112
  42. data/lib/aspera/oauth/base.rb +40 -48
  43. data/lib/aspera/oauth/factory.rb +41 -2
  44. data/lib/aspera/oauth/jwt.rb +4 -1
  45. data/lib/aspera/persistency_action_once.rb +1 -1
  46. data/lib/aspera/persistency_folder.rb +20 -2
  47. data/lib/aspera/preview/generator.rb +13 -10
  48. data/lib/aspera/preview/options.rb +2 -2
  49. data/lib/aspera/preview/terminal.rb +1 -1
  50. data/lib/aspera/preview/utils.rb +11 -6
  51. data/lib/aspera/products/connect.rb +82 -0
  52. data/lib/aspera/products/desktop.rb +30 -0
  53. data/lib/aspera/products/other.rb +82 -0
  54. data/lib/aspera/products/transferd.rb +61 -0
  55. data/lib/aspera/rest.rb +22 -17
  56. data/lib/aspera/secret_hider.rb +9 -2
  57. data/lib/aspera/ssh.rb +31 -24
  58. data/lib/aspera/temp_file_manager.rb +5 -4
  59. data/lib/aspera/transfer/parameters.rb +2 -1
  60. data/lib/aspera/transfer/spec.yaml +22 -20
  61. data/lib/aspera/transfer/sync.rb +1 -5
  62. data/lib/aspera/transfer/uri.rb +2 -2
  63. data/lib/aspera/uri_reader.rb +18 -1
  64. data/lib/transferd_pb.rb +86 -0
  65. data/lib/transferd_services_pb.rb +84 -0
  66. data.tar.gz.sig +0 -0
  67. metadata +13 -166
  68. metadata.gz.sig +0 -0
  69. data/examples/build_exec +0 -74
  70. data/examples/build_exec_rubyc +0 -40
  71. data/lib/aspera/ascp/products.rb +0 -168
  72. data/lib/transfer_pb.rb +0 -84
  73. data/lib/transfer_services_pb.rb +0 -82
@@ -21,6 +21,13 @@ module Aspera
21
21
  MARKER_END = ':'
22
22
  MARKER_IN_END = '@'
23
23
 
24
+ # special handlers stop processing of handlers on right
25
+ # extend includes processing of other handlers in itself
26
+ # val keeps the value intact
27
+ SPECIAL_HANDLERS = %i[extend val].freeze
28
+
29
+ private_constant :MARKER_START, :MARKER_END, :MARKER_IN_END, :SPECIAL_HANDLERS
30
+
24
31
  class << self
25
32
  # decode comma separated table text
26
33
  def decode_csvt(value)
@@ -64,7 +71,7 @@ module Aspera
64
71
  ruby: lambda{|v|Environment.secure_eval(v, __FILE__, __LINE__)},
65
72
  secret: lambda{|v|prompt = v.empty? ? 'secret' : v; $stdin.getpass("#{prompt}> ")}, # rubocop:disable Style/Semicolon
66
73
  stdin: lambda{|v|ExtendedValue.assert_no_value(v, :stdin); $stdin.read}, # rubocop:disable Style/Semicolon
67
- stdbin: lambda{|v|ExtendedValue.assert_no_value(v, :stdin); $stdin.binmode.read}, # rubocop:disable Style/Semicolon
74
+ stdbin: lambda{|v|ExtendedValue.assert_no_value(v, :stdbin); $stdin.binmode.read}, # rubocop:disable Style/Semicolon
68
75
  yaml: lambda{|v|YAML.load(v)},
69
76
  zlib: lambda{|v|Zlib::Inflate.inflate(v)},
70
77
  extend: lambda{|v|ExtendedValue.instance.evaluate_all(v)}
@@ -107,8 +114,7 @@ module Aspera
107
114
  handler = m[1].to_sym
108
115
  handlers_reversed.unshift(handler)
109
116
  value = m[2]
110
- # stop processing if handler is extend (it will be processed later)
111
- break if handler.eql?(:extend)
117
+ break if SPECIAL_HANDLERS.include?(handler)
112
118
  end
113
119
  Log.log.trace1{"evaluating: #{handlers_reversed}, value: #{value}"}
114
120
  handlers_reversed.each do |handler|
@@ -14,7 +14,6 @@ require 'pp'
14
14
 
15
15
  module Aspera
16
16
  module Cli
17
- CONF_OVERVIEW_KEYS = %w[preset parameter value].freeze
18
17
  # This class is used to transform a complex structure into a simple hash
19
18
  class Flattener
20
19
  def initialize(formatter)
@@ -30,17 +29,6 @@ module Aspera
30
29
  return @result
31
30
  end
32
31
 
33
- # Special method for configuration overview
34
- def config_over(something)
35
- @result = []
36
- something.each do |config, preset|
37
- preset.each do |parameter, value|
38
- @result.push(CONF_OVERVIEW_KEYS.zip([config, parameter, value]).to_h)
39
- end
40
- end
41
- return @result
42
- end
43
-
44
32
  private
45
33
 
46
34
  # Recursive function to flatten any type
@@ -101,9 +89,10 @@ module Aspera
101
89
  DISPLAY_FORMATS = %i[text nagios ruby json jsonpp yaml table csv image].freeze
102
90
  # user output levels
103
91
  DISPLAY_LEVELS = %i[info data error].freeze
104
- FIELD_VALUE_HEADINGS = %i[key value].freeze
92
+ # column names for single object display in table
93
+ SINGLE_OBJECT_COLUMN_NAMES = %i[field value].freeze
105
94
 
106
- private_constant :DISPLAY_FORMATS, :DISPLAY_LEVELS, :CSV_RECORD_SEPARATOR, :CSV_FIELD_SEPARATOR, :FIELD_VALUE_HEADINGS
95
+ private_constant :FIELDS_LESS, :CSV_RECORD_SEPARATOR, :CSV_FIELD_SEPARATOR, :DISPLAY_FORMATS, :DISPLAY_LEVELS, :SINGLE_OBJECT_COLUMN_NAMES
107
96
  # prefix to display error messages in user messages (terminal)
108
97
  ERROR_FLASH = 'ERROR:'.bg_red.gray.blink.freeze
109
98
  WARNING_FLASH = 'WARNING:'.bg_brown.black.blink.freeze
@@ -172,12 +161,37 @@ module Aspera
172
161
  @spinner = nil
173
162
  end
174
163
 
175
- # options are: format, output, display, fields, select, table_style, flat_hash, transpose_single
164
+ def declare_options(options)
165
+ default_table_style = if Environment.instance.terminal_supports_unicode?
166
+ {border: :unicode_round}
167
+ else
168
+ {}
169
+ end
170
+ options.declare(:format, 'Output format', values: DISPLAY_FORMATS, handler: {o: self, m: :option_handler}, default: :table)
171
+ options.declare(:output, 'Destination for results', types: String, handler: {o: self, m: :option_handler})
172
+ options.declare(:display, 'Output only some information', values: DISPLAY_LEVELS, handler: {o: self, m: :option_handler}, default: :info)
173
+ options.declare(
174
+ :fields, "Comma separated list of: fields, or #{SpecialValues::ALL}, or #{SpecialValues::DEF}", handler: {o: self, m: :option_handler},
175
+ types: [String, Array, Regexp, Proc],
176
+ default: SpecialValues::DEF)
177
+ options.declare(:select, 'Select only some items in lists: column, value', types: [Hash, Proc], handler: {o: self, m: :option_handler})
178
+ options.declare(:table_style, 'Table display style', types: [Hash], handler: {o: self, m: :option_handler}, default: default_table_style)
179
+ options.declare(:flat_hash, '(Table) Display deep values as additional keys', values: :bool, handler: {o: self, m: :option_handler}, default: true)
180
+ options.declare(
181
+ :multi_single, '(Table) Control how object list is displayed as single table, or multiple objects', values: %i[no yes single],
182
+ handler: {o: self, m: :option_handler}, default: :no)
183
+ options.declare(:show_secrets, 'Show secrets on command output', values: :bool, handler: {o: self, m: :option_handler}, default: false)
184
+ options.declare(:image, 'Options for image display', types: Hash, handler: {o: self, m: :option_handler}, default: {})
185
+ end
186
+
187
+ # method accessed by option manager
188
+ # options are: format, output, display, fields, select, table_style, flat_hash, multi_single
176
189
  def option_handler(option_symbol, operation, value=nil)
177
190
  Aspera.assert_values(operation, %i[set get])
178
191
  case operation
179
192
  when :set
180
193
  @options[option_symbol] = value
194
+ # special handling of some options
181
195
  case option_symbol
182
196
  when :output
183
197
  $stdout = if value.eql?('-')
@@ -186,7 +200,9 @@ module Aspera
186
200
  File.open(value, 'w')
187
201
  end
188
202
  when :image
203
+ # get list if key arguments of method
189
204
  allowed_options = Preview::Terminal.method(:build).parameters.select{|i|i[0].eql?(:key)}.map{|i|i[1]}
205
+ # check that only supported options are given
190
206
  unknown_options = value.keys.map(&:to_sym) - allowed_options
191
207
  raise "Invalid parameter(s) for option image: #{unknown_options.join(', ')}, use #{allowed_options.join(', ')}" unless unknown_options.empty?
192
208
  end
@@ -196,33 +212,12 @@ module Aspera
196
212
  nil
197
213
  end
198
214
 
199
- def declare_options(options)
200
- default_table_style = if Environment.instance.terminal_supports_unicode?
201
- {border: :unicode_round}
202
- else
203
- {}
204
- end
205
- options.declare(:format, 'Output format', values: DISPLAY_FORMATS, handler: {o: self, m: :option_handler}, default: :table)
206
- options.declare(:output, 'Destination for results', types: String, handler: {o: self, m: :option_handler})
207
- options.declare(:display, 'Output only some information', values: DISPLAY_LEVELS, handler: {o: self, m: :option_handler}, default: :info)
208
- options.declare(
209
- :fields, "Comma separated list of: fields, or #{SpecialValues::ALL}, or #{SpecialValues::DEF}", handler: {o: self, m: :option_handler},
210
- types: [String, Array, Regexp, Proc],
211
- default: SpecialValues::DEF)
212
- options.declare(:select, 'Select only some items in lists: column, value', types: [Hash, Proc], handler: {o: self, m: :option_handler})
213
- options.declare(:table_style, 'Table display style', types: [Hash], handler: {o: self, m: :option_handler}, default: default_table_style)
214
- options.declare(:flat_hash, '(Table) Display deep values as additional keys', values: :bool, handler: {o: self, m: :option_handler}, default: true)
215
- options.declare(:transpose_single, '(Table) Single object fields output vertically', values: :bool, handler: {o: self, m: :option_handler}, default: true)
216
- options.declare(:multi_table, '(Table) Each element of a table are displayed as a table', values: :bool, handler: {o: self, m: :option_handler}, default: false)
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: {})
219
- end
220
-
221
215
  # main output method
222
216
  # data: for requested data, not displayed if level==error
223
217
  # info: additional info, displayed if level==info
224
218
  # error: always displayed on stderr
225
219
  def display_message(message_level, message)
220
+ message = SecretHider.hide_secrets_in_string(message) if message.is_a?(String) && hide_secrets?
226
221
  case message_level
227
222
  when :data then $stdout.puts(message) unless @options[:display].eql?(:error)
228
223
  when :info then $stdout.puts(message) if @options[:display].eql?(:info)
@@ -244,6 +239,122 @@ module Aspera
244
239
  display_status(count_msg)
245
240
  end
246
241
 
242
+ def hide_secrets?
243
+ !@options[:show_secrets] && !@options[:display].eql?(:data)
244
+ end
245
+
246
+ # hides secrets in Hash or Array
247
+ def hide_secrets(data)
248
+ SecretHider.deep_remove_secret(data) if hide_secrets?
249
+ end
250
+
251
+ # this method displays the results, especially the table format
252
+ # @param type [Symbol] type of data
253
+ # @param data [Object] data to display
254
+ # @param total [Integer] total number of items
255
+ # @param fields [Array<String>] list of fields to display
256
+ # @param name [String] name of the column to display
257
+ def display_results(type:, data: nil, total: nil, fields: nil, name: nil)
258
+ Log.log.debug{"display_results: #{type} class=#{data.class}"}
259
+ Log.log.trace1{"display_results:data=#{data}"}
260
+ Aspera.assert_type(type, Symbol){'result must have type'}
261
+ Aspera.assert(!data.nil? || %i[empty nothing].include?(type)){'result must have data'}
262
+ display_item_count(data.length, total) unless total.nil?
263
+ hide_secrets(data)
264
+ data = SecretHider.hide_secrets_in_string(data) if data.is_a?(String) && hide_secrets?
265
+ case @options[:format]
266
+ when :text
267
+ display_message(:data, data.to_s)
268
+ when :nagios
269
+ Nagios.process(data)
270
+ when :ruby
271
+ display_message(:data, PP.pp(filter_list_on_fields(data), +''))
272
+ when :json
273
+ display_message(:data, JSON.generate(filter_list_on_fields(data)))
274
+ when :jsonpp
275
+ display_message(:data, JSON.pretty_generate(filter_list_on_fields(data)))
276
+ when :yaml
277
+ display_message(:data, YAML.dump(filter_list_on_fields(data)))
278
+ when :image
279
+ # assume it is an url
280
+ url = data
281
+ case type
282
+ when :single_object, :object_list
283
+ url = [url] if type.eql?(:single_object)
284
+ raise 'image display requires a single result' unless url.length == 1
285
+ fields = compute_fields(url, fields)
286
+ raise 'select a field to display' unless fields.length == 1
287
+ url = url.first
288
+ raise 'no such field' unless url.key?(fields.first)
289
+ url = url[fields.first]
290
+ end
291
+ raise "not url: #{url.class} #{url}" unless url.is_a?(String)
292
+ display_message(:data, status_image(url))
293
+ when :table, :csv
294
+ case type
295
+ when :object_list, :single_object
296
+ obj_list = data
297
+ if type.eql?(:single_object)
298
+ obj_list = [obj_list]
299
+ @options[:multi_single] = :yes
300
+ end
301
+ Aspera.assert_type(obj_list, Array)
302
+ Aspera.assert(obj_list.all?(Hash)){"expecting Array of Hash: #{obj_list.inspect}"}
303
+ # :object_list is an array of hash tables, where key=colum name
304
+ obj_list = obj_list.map{|obj|Flattener.new(self).flatten(obj)} if @options[:flat_hash]
305
+ display_table(obj_list, compute_fields(obj_list, fields))
306
+ when :value_list
307
+ # :value_list is a simple array of values, name of column provided in the :name
308
+ display_table(data.map { |i| { name => i } }, [name])
309
+ when :empty # no table
310
+ display_message(:info, special_format('empty'))
311
+ return
312
+ when :nothing # no result expected
313
+ Log.log.debug('no result expected')
314
+ when :status # no table
315
+ # :status displays a simple message
316
+ display_message(:info, data)
317
+ when :text # no table
318
+ # :status displays a simple message
319
+ display_message(:data, data)
320
+ when :other_struct # no table
321
+ # :other_struct is any other type of structure
322
+ display_message(:data, PP.pp(data, +''))
323
+ else
324
+ raise "unknown data type: #{type}"
325
+ end
326
+ else
327
+ raise "not expected: #{@options[:format]}"
328
+ end
329
+ end
330
+
331
+ # @return text suitable to display an image from url
332
+ # @param blob [String] either a URL or image data
333
+ def status_image(blob)
334
+ # check if URL
335
+ begin
336
+ URI.parse(blob)
337
+ url = blob
338
+ unless Environment.instance.url_method.eql?(:text)
339
+ Environment.instance.open_uri(url)
340
+ return ''
341
+ end
342
+ blob = UriReader.read(url)
343
+ rescue URI::InvalidURIError
344
+ nil
345
+ end
346
+ # try base64
347
+ begin
348
+ blob = Base64.strict_decode64(blob)
349
+ rescue
350
+ nil
351
+ end
352
+ return Preview::Terminal.build(blob, **@options[:image].symbolize_keys)
353
+ end
354
+ #==========================================================================================
355
+
356
+ private
357
+
247
358
  def all_fields(data)
248
359
  data.each_with_object({}){|v, m|v.each_key{|c|m[c] = true}}.keys
249
360
  end
@@ -300,7 +411,7 @@ module Aspera
300
411
  return data if @options[:fields].eql?(SpecialValues::DEF)
301
412
  selected_fields = compute_fields(data, @options[:fields])
302
413
  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)}}
414
+ return data.map{|i|i.slice(*selected_fields)}
304
415
  end
305
416
 
306
417
  # filter the list of items on the select option
@@ -314,9 +425,9 @@ module Aspera
314
425
  end
315
426
  end
316
427
 
317
- # this method displays a table
318
- # object_array: array of hash
319
- # fields: list of column names
428
+ # displays a list of objects
429
+ # @param object_array [Array] array of hash
430
+ # @param fields [Array] list of column names
320
431
  def display_table(object_array, fields)
321
432
  Aspera.assert(!fields.nil?){'missing fields parameter'}
322
433
  filter_columns_on_select(object_array)
@@ -330,13 +441,6 @@ module Aspera
330
441
  display_message(:data, object_array.first[fields.first])
331
442
  return
332
443
  end
333
- single_transposed = @options[:transpose_single] && object_array.length == 1
334
- # Special case if only one row (it could be object_list or single_object)
335
- if single_transposed
336
- single = object_array.first
337
- object_array = fields.map { |i| FIELD_VALUE_HEADINGS.zip([i, single[i]]).to_h }
338
- fields = FIELD_VALUE_HEADINGS
339
- end
340
444
  Log.log.debug{Log.dump(:object_array, object_array)}
341
445
  # convert data to string, and keep only display fields
342
446
  final_table_rows = object_array.map { |r| fields.map { |c| r[c].to_s } }
@@ -345,11 +449,12 @@ module Aspera
345
449
  # here : fields : list of column names
346
450
  case @options[:format]
347
451
  when :table
348
- if @options[:multi_table] && !single_transposed
452
+ if @options[:multi_single].eql?(:yes) ||
453
+ (@options[:multi_single].eql?(:single) && final_table_rows.length.eql?(1))
349
454
  final_table_rows.each do |row|
350
455
  Log.log.debug{Log.dump(:row, row)}
351
456
  display_message(:data, Terminal::Table.new(
352
- headings: FIELD_VALUE_HEADINGS,
457
+ headings: SINGLE_OBJECT_COLUMN_NAMES,
353
458
  rows: fields.zip(row),
354
459
  style: @options[:table_style]&.symbolize_keys))
355
460
  end
@@ -366,110 +471,6 @@ module Aspera
366
471
  raise "not expected: #{@options[:format]}"
367
472
  end
368
473
  end
369
-
370
- # @return text suitable to display an image from url
371
- def status_image(blob)
372
- begin
373
- raise URI::InvalidURIError, 'not uri' if !(blob =~ /\A#{URI::RFC2396_PARSER.make_regexp}\z/)
374
- # it's a url
375
- url = blob
376
- unless Environment.instance.url_method.eql?(:text)
377
- Environment.instance.open_uri(url)
378
- return ''
379
- end
380
- # remote_image = Rest.new(base_url: url).read('')
381
- # mime = remote_image[:http]['content-type']
382
- # blob = remote_image[:http].body
383
- # Log.log.warn("Image ? #{remote_image[:http]['content-type']}") unless mime.include?('image/')
384
- blob = UriReader.read(url)
385
- rescue URI::InvalidURIError
386
- nil
387
- end
388
- # try base64
389
- begin
390
- blob = Base64.strict_decode64(blob)
391
- rescue
392
- nil
393
- end
394
- return Preview::Terminal.build(blob, **@options[:image].symbolize_keys)
395
- end
396
-
397
- # this method displays the results, especially the table format
398
- # @param type [Symbol] type of data
399
- # @param data [Object] data to display
400
- # @param total [Integer] total number of items
401
- # @param fields [Array<String>] list of fields to display
402
- # @param name [String] name of the column to display
403
- def display_results(type:, data: nil, total: nil, fields: nil, name: nil)
404
- Log.log.debug{"display_results: #{type} class=#{data.class} data=#{data}"}
405
- Aspera.assert_type(type, Symbol){'result must have type'}
406
- Aspera.assert(!data.nil? || %i[empty nothing].include?(type)){'result must have data'}
407
- display_item_count(data.length, total) unless total.nil?
408
- SecretHider.deep_remove_secret(data) unless @options[:show_secrets] || @options[:display].eql?(:data)
409
- case @options[:format]
410
- when :text
411
- display_message(:data, data.to_s)
412
- when :nagios
413
- Nagios.process(data)
414
- when :ruby
415
- display_message(:data, PP.pp(filter_list_on_fields(data), +''))
416
- when :json
417
- display_message(:data, JSON.generate(filter_list_on_fields(data)))
418
- when :jsonpp
419
- display_message(:data, JSON.pretty_generate(filter_list_on_fields(data)))
420
- when :yaml
421
- display_message(:data, YAML.dump(filter_list_on_fields(data)))
422
- when :image
423
- # assume it is an url
424
- url = data
425
- case type
426
- when :single_object, :object_list
427
- url = [url] if type.eql?(:single_object)
428
- raise 'image display requires a single result' unless url.length == 1
429
- fields = compute_fields(url, fields)
430
- raise 'select a field to display' unless fields.length == 1
431
- url = url.first
432
- raise 'no such field' unless url.key?(fields.first)
433
- url = url[fields.first]
434
- end
435
- raise "not url: #{url.class} #{url}" unless url.is_a?(String)
436
- display_message(:data, status_image(url))
437
- when :table, :csv
438
- case type
439
- when :config_over
440
- display_table(Flattener.new(self).config_over(data), CONF_OVERVIEW_KEYS)
441
- when :object_list, :single_object
442
- obj_list = data
443
- obj_list = [obj_list] if type.eql?(:single_object)
444
- Aspera.assert_type(obj_list, Array)
445
- Aspera.assert(obj_list.all?(Hash)){"expecting Array of Hash: #{obj_list.inspect}"}
446
- # :object_list is an array of hash tables, where key=colum name
447
- obj_list = obj_list.map{|obj|Flattener.new(self).flatten(obj)} if @options[:flat_hash]
448
- display_table(obj_list, compute_fields(obj_list, fields))
449
- when :value_list
450
- # :value_list is a simple array of values, name of column provided in the :name
451
- display_table(data.map { |i| { name => i } }, [name])
452
- when :empty # no table
453
- display_message(:info, special_format('empty'))
454
- return
455
- when :nothing # no result expected
456
- Log.log.debug('no result expected')
457
- when :status # no table
458
- # :status displays a simple message
459
- display_message(:info, data)
460
- when :text # no table
461
- # :status displays a simple message
462
- display_message(:data, data)
463
- when :other_struct # no table
464
- # :other_struct is any other type of structure
465
- display_message(:data, PP.pp(data, +''))
466
- else
467
- raise "unknown data type: #{type}"
468
- end
469
- else
470
- raise "not expected: #{@options[:format]}"
471
- end
472
- end
473
474
  end
474
475
  end
475
476
  end
@@ -10,9 +10,10 @@ module Aspera
10
10
  DOC_URL = "https://www.rubydoc.info/gems/#{GEM_NAME}"
11
11
  GEM_URL = "https://rubygems.org/gems/#{GEM_NAME}"
12
12
  SRC_URL = 'https://github.com/IBM/aspera-cli'
13
+ CONTAINER = 'docker.io/martinlaurent/ascli'
13
14
  # set this to warn in advance when minimum required ruby version will increase
14
15
  # see also required_ruby_version in gemspec file
15
- RUBY_FUTURE_MINIMUM_VERSION = '3.0'
16
+ RUBY_FUTURE_MINIMUM_VERSION = '3.2'
16
17
  end
17
18
  end
18
19
  end
@@ -63,6 +63,18 @@ module Aspera
63
63
  def result_image(blob, formatter:)
64
64
  return Main.result_status(formatter.status_image(blob))
65
65
  end
66
+
67
+ def result_single_object(data, fields: nil)
68
+ return {type: :single_object, data: data, fields: fields}
69
+ end
70
+
71
+ def result_object_list(data, fields: nil, total: nil)
72
+ return {type: :object_list, data: data, fields: fields, total: nil}
73
+ end
74
+
75
+ def result_value_list(data, name)
76
+ return {type: :value_list, data: data, name: name}
77
+ end
66
78
  end
67
79
 
68
80
  private
@@ -105,13 +105,14 @@ module Aspera
105
105
  # @param descr [String] description for help
106
106
  # @param to_check [Object] value to check
107
107
  # @param type_list [NilClass, Class, Array[Class]] accepted value type(s)
108
+ # @param check_array [bool] set to true if it is a list of values to check
108
109
  def validate_type(what, descr, to_check, type_list, check_array: false)
109
110
  return nil if type_list.nil?
110
111
  Aspera.assert(type_list.is_a?(Array) && type_list.all?(Class)){'types must be a Class Array'}
111
112
  value_list = check_array ? to_check : [to_check]
112
113
  value_list.each do |value|
113
114
  raise Cli::BadArgument,
114
- "#{what.to_s.capitalize} #{descr} is a #{value.class} but must be #{type_list.length > 1 ? 'one of ' : ''}#{type_list.map(&:name).join(',')}" unless \
115
+ "#{what.to_s.capitalize} #{descr} is a #{value.class} but must be #{type_list.length > 1 ? 'one of: ' : ''}#{type_list.map(&:name).join(', ')}" unless
115
116
  type_list.any?{|t|value.is_a?(t)}
116
117
  end
117
118
  end
@@ -181,7 +182,7 @@ module Aspera
181
182
 
182
183
  # @param descr [String] description for help
183
184
  # @param mandatory [Boolean] if true, raise error if option not set
184
- # @param multiple [Boolean] if true, return remaining arguments
185
+ # @param multiple [Boolean] if true, return remaining arguments (Array)
185
186
  # @param accept_list [Array] list of allowed values (Symbol)
186
187
  # @param validation [Class, Array] accepted value type(s) or list of Symbols
187
188
  # @param aliases [Hash] map of aliases: key = alias, value = real value
@@ -203,7 +204,7 @@ module Aspera
203
204
  values = @unprocessed_cmd_line_arguments.shift(how_many)
204
205
  values = values.map{|v|evaluate_extended_value(v, allowed_types)}
205
206
  # if expecting list and only one arg of type array : it is the list
206
- values = values.first if values.length.eql?(1) && values.first.is_a?(Array)
207
+ values = values.first if multiple && values.length.eql?(1) && values.first.is_a?(Array)
207
208
  if accept_list
208
209
  allowed_values = [].concat(accept_list)
209
210
  allowed_values.concat(aliases.keys) unless aliases.nil?
@@ -500,7 +501,6 @@ module Aspera
500
501
  self.class.multi_choice_assert(false, message, accept_list)
501
502
  end
502
503
  end
503
- result = nil
504
504
  sensitive = option && @declared_options[descr.to_sym].is_a?(Hash) && @declared_options[descr.to_sym][:sensitive]
505
505
  default_prompt = "#{what}: #{descr}"
506
506
  # ask interactively
@@ -220,12 +220,12 @@ module Aspera
220
220
  query = options.get_option(:query)
221
221
  # dup default, as it could be frozen
222
222
  query = default.dup if query.nil?
223
- Log.log.debug{"Query=#{query}".bg_red}
223
+ Log.log.debug{"query_read_delete=#{query}".bg_red}
224
224
  begin
225
225
  # check it is suitable
226
226
  URI.encode_www_form(query) unless query.nil?
227
227
  rescue StandardError => e
228
- raise Cli::BadArgument, "Query must be an extended value which can be encoded with URI.encode_www_form. Refer to manual. (#{e.message})"
228
+ raise Cli::BadArgument, "Query must be an extended value (Hash, Array) which can be encoded with URI.encode_www_form. Refer to manual. (#{e.message})"
229
229
  end
230
230
  return query
231
231
  end