aspera-cli 4.25.6 → 4.26.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +74 -47
- data/CONTRIBUTING.md +1 -1
- data/lib/aspera/api/aoc.rb +118 -78
- data/lib/aspera/api/node.rb +101 -49
- data/lib/aspera/ascp/installation.rb +94 -30
- data/lib/aspera/cli/extended_value.rb +1 -0
- data/lib/aspera/cli/formatter.rb +47 -40
- data/lib/aspera/cli/manager.rb +30 -4
- data/lib/aspera/cli/plugins/aoc.rb +214 -136
- data/lib/aspera/cli/plugins/ats.rb +3 -3
- data/lib/aspera/cli/plugins/base.rb +17 -42
- data/lib/aspera/cli/plugins/config.rb +5 -3
- data/lib/aspera/cli/plugins/console.rb +3 -3
- data/lib/aspera/cli/plugins/faspex.rb +5 -5
- data/lib/aspera/cli/plugins/faspex5.rb +20 -18
- data/lib/aspera/cli/plugins/node.rb +66 -70
- data/lib/aspera/cli/plugins/oauth.rb +5 -12
- data/lib/aspera/cli/plugins/orchestrator.rb +13 -13
- data/lib/aspera/cli/plugins/preview.rb +116 -80
- data/lib/aspera/cli/plugins/server.rb +2 -10
- data/lib/aspera/cli/plugins/shares.rb +7 -7
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/dot_container.rb +7 -3
- data/lib/aspera/environment.rb +3 -2
- data/lib/aspera/log.rb +1 -1
- data/lib/aspera/preview/file_types.rb +1 -1
- data/lib/aspera/preview/generator.rb +146 -91
- data/lib/aspera/preview/options.rb +4 -1
- data/lib/aspera/preview/terminal.rb +50 -20
- data/lib/aspera/preview/utils.rb +76 -34
- data/lib/aspera/products/transferd.rb +1 -1
- data/lib/aspera/rest.rb +1 -0
- data/lib/aspera/rest_list.rb +23 -16
- data/lib/aspera/secret_hider.rb +3 -1
- data/lib/aspera/uri_reader.rb +17 -2
- data.tar.gz.sig +0 -0
- metadata +5 -5
- metadata.gz.sig +0 -0
data/lib/aspera/cli/formatter.rb
CHANGED
|
@@ -28,12 +28,14 @@ module Aspera
|
|
|
28
28
|
DISPLAY_LEVELS = %i[info data error].freeze
|
|
29
29
|
# column names for single object display in table
|
|
30
30
|
SINGLE_OBJECT_COLUMN_NAMES = %i[field value].freeze
|
|
31
|
+
# Terminal: vertical separator for list of strings.
|
|
32
|
+
STR_LST_SEP_VERT = "\n"
|
|
31
33
|
|
|
32
|
-
private_constant :FIELDS_LESS, :DISPLAY_FORMATS, :DISPLAY_LEVELS, :SINGLE_OBJECT_COLUMN_NAMES
|
|
34
|
+
private_constant :FIELDS_LESS, :DISPLAY_FORMATS, :DISPLAY_LEVELS, :SINGLE_OBJECT_COLUMN_NAMES, :STR_LST_SEP_VERT
|
|
33
35
|
|
|
34
36
|
class << self
|
|
35
37
|
# nicer display for boolean
|
|
36
|
-
# used by spec_doc
|
|
38
|
+
# used by `spec_doc`
|
|
37
39
|
def tick(yes)
|
|
38
40
|
result =
|
|
39
41
|
if Environment.terminal_supports_unicode?
|
|
@@ -47,14 +49,14 @@ module Aspera
|
|
|
47
49
|
|
|
48
50
|
# Highlight special values on terminal
|
|
49
51
|
# empty values are dim
|
|
50
|
-
# used by spec_doc
|
|
52
|
+
# used by `spec_doc`
|
|
51
53
|
def special_format(what)
|
|
52
54
|
result = "<#{what}>"
|
|
53
55
|
return %w[null empty].any?{ |s| what.include?(s)} ? result.dim : result.reverse_color
|
|
54
56
|
end
|
|
55
57
|
|
|
56
|
-
#
|
|
57
|
-
# used by spec_doc
|
|
58
|
+
# For transfer spec table, build line for display in terminal
|
|
59
|
+
# used by `spec_doc`
|
|
58
60
|
def check_row(row)
|
|
59
61
|
row.each_key do |k|
|
|
60
62
|
row[k] = row[k].map{ |i| WordWrap.ww(i.to_s, 120).chomp}.join("\n") if row[k].is_a?(Array)
|
|
@@ -62,7 +64,7 @@ module Aspera
|
|
|
62
64
|
end
|
|
63
65
|
|
|
64
66
|
# Give Markdown String, or matched data, return formatted string for terminal
|
|
65
|
-
# used by spec_doc
|
|
67
|
+
# used by `spec_doc`
|
|
66
68
|
# @param match [MatchData,String]
|
|
67
69
|
def markdown_text(match)
|
|
68
70
|
if match.is_a?(String)
|
|
@@ -83,7 +85,7 @@ module Aspera
|
|
|
83
85
|
end
|
|
84
86
|
|
|
85
87
|
# Replace special values with a readable version on terminal
|
|
86
|
-
def replace_specific_for_terminal(input_hash)
|
|
88
|
+
def replace_specific_for_terminal(input_hash, string_list_separator)
|
|
87
89
|
hash_to_process = [input_hash]
|
|
88
90
|
until hash_to_process.empty?
|
|
89
91
|
current = hash_to_process.pop
|
|
@@ -99,7 +101,7 @@ module Aspera
|
|
|
99
101
|
if value.empty?
|
|
100
102
|
current[key] = special_format('empty list')
|
|
101
103
|
elsif value.all?(String)
|
|
102
|
-
current[key] = value.join(
|
|
104
|
+
current[key] = value.join(string_list_separator)
|
|
103
105
|
else
|
|
104
106
|
value.each do |item|
|
|
105
107
|
hash_to_process.push(item) if item.is_a?(Hash)
|
|
@@ -117,8 +119,7 @@ module Aspera
|
|
|
117
119
|
end
|
|
118
120
|
|
|
119
121
|
def all_but(list)
|
|
120
|
-
list
|
|
121
|
-
return list.map{ |i| "#{FIELDS_LESS}#{i}"}.unshift(SpecialValues::ALL)
|
|
122
|
+
Array(list).map{ |i| "#{FIELDS_LESS}#{i}"}.unshift(SpecialValues::ALL)
|
|
122
123
|
end
|
|
123
124
|
end
|
|
124
125
|
|
|
@@ -159,11 +160,6 @@ module Aspera
|
|
|
159
160
|
end
|
|
160
161
|
|
|
161
162
|
def declare_options(options)
|
|
162
|
-
default_table_style = if Environment.terminal_supports_unicode?
|
|
163
|
-
{border: :unicode_round}
|
|
164
|
-
else
|
|
165
|
-
{}
|
|
166
|
-
end
|
|
167
163
|
options.declare(:display, 'Output only some information', allowed: DISPLAY_LEVELS, handler: {o: self, m: :option_handler}, default: :data)
|
|
168
164
|
options.declare(:format, 'Output format', allowed: DISPLAY_FORMATS, handler: {o: self, m: :option_handler}, default: :table)
|
|
169
165
|
options.declare(:output, 'Destination for results', handler: {o: self, m: :option_handler})
|
|
@@ -173,7 +169,7 @@ module Aspera
|
|
|
173
169
|
default: SpecialValues::DEF
|
|
174
170
|
)
|
|
175
171
|
options.declare(:select, 'Select only some items in lists: column, value', allowed: [Hash, Proc], handler: {o: self, m: :option_handler})
|
|
176
|
-
options.declare(:table_style, '(Table) Display style', allowed: [Hash], handler: {o: self, m: :option_handler}, default:
|
|
172
|
+
options.declare(:table_style, '(Table) Display style', allowed: [Hash], handler: {o: self, m: :option_handler}, default: {})
|
|
177
173
|
options.declare(:flat_hash, '(Table) Display deep values as additional keys', allowed: Allowed::TYPES_BOOLEAN, handler: {o: self, m: :option_handler}, default: true)
|
|
178
174
|
options.declare(
|
|
179
175
|
:multi_single, '(Table) Control how object list is displayed as single table, or multiple objects', allowed: %i[no yes single],
|
|
@@ -249,15 +245,16 @@ module Aspera
|
|
|
249
245
|
SecretHider.instance.deep_remove_secret(data) if hide_secrets?
|
|
250
246
|
end
|
|
251
247
|
|
|
252
|
-
#
|
|
253
|
-
# @param type [Symbol]
|
|
254
|
-
# @param data [Object]
|
|
255
|
-
# @param
|
|
256
|
-
# @param
|
|
257
|
-
# @param name [String]
|
|
258
|
-
def display_results(type:, data: nil,
|
|
259
|
-
Log.log.debug{"display_results: type=#{type}
|
|
260
|
-
Log.
|
|
248
|
+
# Display results, especially the table format
|
|
249
|
+
# @param type [Symbol] Type of data
|
|
250
|
+
# @param data [Object] Data to display
|
|
251
|
+
# @param fields [Array<String>] List of fields to display
|
|
252
|
+
# @param total [Integer] Total number of items
|
|
253
|
+
# @param name [String] Name of the column to display
|
|
254
|
+
def display_results(type:, data: nil, fields: nil, total: nil, name: nil)
|
|
255
|
+
Log.log.debug{"display_results: type=#{type}"}
|
|
256
|
+
Log.dump(:data, data, level: :trace1)
|
|
257
|
+
Log.dump(:fields, fields, level: :trace1)
|
|
261
258
|
Aspera.assert_type(type, Symbol){'result must have type'}
|
|
262
259
|
Aspera.assert(!data.nil? || %i[empty nothing].include?(type)){'result must have data'}
|
|
263
260
|
display_item_count(data.length, total) unless total.nil?
|
|
@@ -317,7 +314,7 @@ module Aspera
|
|
|
317
314
|
case type
|
|
318
315
|
when :single_object
|
|
319
316
|
# :single_object is a Hash, where key=column name
|
|
320
|
-
Aspera.assert_type(data, Hash)
|
|
317
|
+
Aspera.assert_type(data, Hash){'result'}
|
|
321
318
|
if data.empty?
|
|
322
319
|
display_message(:data, self.class.special_format('empty dict'))
|
|
323
320
|
else
|
|
@@ -364,6 +361,7 @@ module Aspera
|
|
|
364
361
|
# @param default [Array<String>, Proc] list of fields to display by default (may contain special values)
|
|
365
362
|
def compute_fields(data, default)
|
|
366
363
|
Log.log.debug{"compute_fields: data:#{data.class} default:#{default.class} #{default}"}
|
|
364
|
+
Log.dump(:compute_fields_default, default, level: :trace1)
|
|
367
365
|
# the requested list of fields, but if can contain special values
|
|
368
366
|
request =
|
|
369
367
|
case @options[:fields]
|
|
@@ -374,6 +372,7 @@ module Aspera
|
|
|
374
372
|
when Proc then return all_fields(data).select{ |i| @options[:fields].call(i)}
|
|
375
373
|
else Aspera.error_unexpected_value(@options[:fields])
|
|
376
374
|
end
|
|
375
|
+
Aspera.assert_array_all(request, String)
|
|
377
376
|
result = []
|
|
378
377
|
until request.empty?
|
|
379
378
|
item = request.shift
|
|
@@ -398,11 +397,14 @@ module Aspera
|
|
|
398
397
|
end
|
|
399
398
|
end
|
|
400
399
|
end
|
|
400
|
+
Log.dump(:compute_fields, result, level: :trace1)
|
|
401
401
|
return result
|
|
402
402
|
end
|
|
403
403
|
|
|
404
404
|
# filter the list of items on the fields option
|
|
405
405
|
def filter_list_on_fields(data)
|
|
406
|
+
# no filter for single element
|
|
407
|
+
return data unless data.is_a?(Array)
|
|
406
408
|
# by default, keep all data intact
|
|
407
409
|
return data if @options[:fields].eql?(SpecialValues::DEF) && @options[:select].nil?
|
|
408
410
|
Aspera.assert_array_all(data, Hash){'filter or select'}
|
|
@@ -428,21 +430,26 @@ module Aspera
|
|
|
428
430
|
end
|
|
429
431
|
end
|
|
430
432
|
|
|
431
|
-
#
|
|
432
|
-
# @param object_array
|
|
433
|
-
# @param fields
|
|
433
|
+
# Displays a list of objects
|
|
434
|
+
# @param object_array [Array<Hash>] Array of hash
|
|
435
|
+
# @param fields [Array<String>] List of column names
|
|
436
|
+
# @param single [Boolean] Contains a single object to display
|
|
434
437
|
def display_table(object_array, fields, single: false)
|
|
435
|
-
Aspera.
|
|
438
|
+
Aspera.assert_array_all(object_array, Hash)
|
|
439
|
+
Aspera.assert_array_all(fields, String)
|
|
436
440
|
if object_array.empty?
|
|
437
441
|
# no display for csv
|
|
438
442
|
display_message(:info, self.class.special_format('empty')) if @options[:format].eql?(:table)
|
|
439
443
|
return
|
|
440
444
|
end
|
|
441
445
|
filter_columns_on_select(object_array)
|
|
442
|
-
|
|
446
|
+
format_style = @options[:table_style].symbolize_keys
|
|
447
|
+
string_list_separator = format_style.delete(:str_lst_sep) || STR_LST_SEP_VERT
|
|
448
|
+
# convert data to string, and keep only display fields
|
|
449
|
+
object_array.each{ |i| self.class.replace_specific_for_terminal(i, string_list_separator)}
|
|
443
450
|
# if table has only one element, and only one field, display the value
|
|
444
451
|
if object_array.length == 1 && fields.length == 1
|
|
445
|
-
Log.log.debug("
|
|
452
|
+
Log.log.debug("single element, field: #{fields.first}")
|
|
446
453
|
data = object_array.first[fields.first]
|
|
447
454
|
unless data.is_a?(Array) && data.all?(Hash)
|
|
448
455
|
display_message(:data, data)
|
|
@@ -453,6 +460,7 @@ module Aspera
|
|
|
453
460
|
single = false
|
|
454
461
|
end
|
|
455
462
|
Log.dump(:object_array, object_array)
|
|
463
|
+
Log.dump(:fields, fields)
|
|
456
464
|
# convert data to string, and keep only display fields
|
|
457
465
|
final_table_rows = object_array.map{ |r| fields.map{ |c| r[c].to_s}}
|
|
458
466
|
# remove empty rows
|
|
@@ -460,6 +468,7 @@ module Aspera
|
|
|
460
468
|
# here : fields : list of column names
|
|
461
469
|
case @options[:format]
|
|
462
470
|
when :table
|
|
471
|
+
format_style[:border] = :unicode_round if Environment.terminal_supports_unicode?
|
|
463
472
|
if single || @options[:multi_single].eql?(:yes) ||
|
|
464
473
|
(@options[:multi_single].eql?(:single) && final_table_rows.length.eql?(1))
|
|
465
474
|
# display multiple objects as multiple transposed tables
|
|
@@ -467,23 +476,20 @@ module Aspera
|
|
|
467
476
|
display_message(:data, Terminal::Table.new(
|
|
468
477
|
headings: SINGLE_OBJECT_COLUMN_NAMES,
|
|
469
478
|
rows: fields.zip(row),
|
|
470
|
-
style:
|
|
479
|
+
style: format_style
|
|
471
480
|
))
|
|
472
481
|
end
|
|
473
482
|
else
|
|
474
|
-
# display the table
|
|
483
|
+
# display the table as single table
|
|
475
484
|
display_message(:data, Terminal::Table.new(
|
|
476
485
|
headings: fields,
|
|
477
486
|
rows: final_table_rows,
|
|
478
|
-
style:
|
|
487
|
+
style: format_style
|
|
479
488
|
))
|
|
480
489
|
end
|
|
481
490
|
when :csv
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
params.delete(:border)
|
|
485
|
-
add_headers = params.delete(:headers)
|
|
486
|
-
output = CSV.generate(**params) do |csv|
|
|
491
|
+
add_headers = format_style.delete(:headers)
|
|
492
|
+
output = CSV.generate(**format_style) do |csv|
|
|
487
493
|
csv << fields if add_headers
|
|
488
494
|
final_table_rows.each do |row|
|
|
489
495
|
csv << row
|
|
@@ -493,6 +499,7 @@ module Aspera
|
|
|
493
499
|
else
|
|
494
500
|
raise "not expected: #{@options[:format]}"
|
|
495
501
|
end
|
|
502
|
+
nil
|
|
496
503
|
end
|
|
497
504
|
end
|
|
498
505
|
end
|
data/lib/aspera/cli/manager.rb
CHANGED
|
@@ -146,7 +146,7 @@ module Aspera
|
|
|
146
146
|
new_value = [new_value] if @types.eql?(Allowed::TYPES_STRING_ARRAY) && new_value.is_a?(String)
|
|
147
147
|
# Setting a Hash to null set an empty hash
|
|
148
148
|
new_value = {} if new_value.eql?(nil) && @types&.first.eql?(Hash)
|
|
149
|
-
# Setting a Array to null set an empty
|
|
149
|
+
# Setting a Array to null set an empty array
|
|
150
150
|
new_value = [] if new_value.eql?(nil) && @types&.first.eql?(Array)
|
|
151
151
|
if @types.eql?(Aspera::Cli::Allowed::TYPES_SYMBOL_ARRAY)
|
|
152
152
|
new_value = [new_value] if new_value.is_a?(String)
|
|
@@ -204,6 +204,15 @@ module Aspera
|
|
|
204
204
|
def option_name_to_line(name)
|
|
205
205
|
return "#{OPTION_PREFIX}#{name.to_s.gsub(OPTION_SEP_SYMBOL, OPTION_SEP_LINE)}"
|
|
206
206
|
end
|
|
207
|
+
|
|
208
|
+
# @return [Hash{Symbol => String}, nil] `{field:,value:}` if identifier is a percent selector, else `nil`
|
|
209
|
+
def percent_selector(identifier)
|
|
210
|
+
Aspera.assert_type(identifier, String)
|
|
211
|
+
if (m = identifier.match(REGEX_LOOKUP_ID_BY_FIELD))
|
|
212
|
+
return {field: m[1], value: ExtendedValue.instance.evaluate(m[2], context: "percent selector: #{m[1]}")}
|
|
213
|
+
end
|
|
214
|
+
nil
|
|
215
|
+
end
|
|
207
216
|
end
|
|
208
217
|
|
|
209
218
|
attr_reader :parser
|
|
@@ -281,7 +290,7 @@ module Aspera
|
|
|
281
290
|
Aspera.assert(!@declared_options.key?(option_symbol)){"#{option_symbol} already declared"}
|
|
282
291
|
Aspera.assert(description[-1] != '.'){"#{option_symbol} ends with dot"}
|
|
283
292
|
Aspera.assert(description[0] == description[0].upcase){"#{option_symbol} description does not start with an uppercase"}
|
|
284
|
-
Aspera.assert(!['hash', 'extended value'].any?{ |s| description.downcase.include?(s)}){"#{option_symbol} shall use :allowed"}
|
|
293
|
+
Aspera.assert(!['hash', 'extended value'].any?{ |s| description.downcase.include?(s)}){"#{option_symbol} shall use :allowed instead of hash/extended value in option description"}
|
|
285
294
|
Aspera.assert_type(handler, Hash) if handler
|
|
286
295
|
Aspera.assert(handler.keys.sort.eql?(%i[m o])) if handler
|
|
287
296
|
option_attrs = @declared_options[option_symbol] = OptionValue.new(
|
|
@@ -390,6 +399,21 @@ module Aspera
|
|
|
390
399
|
return result
|
|
391
400
|
end
|
|
392
401
|
|
|
402
|
+
# Resource identifier as positional parameter
|
|
403
|
+
#
|
|
404
|
+
# @param description [String] description of the identifier
|
|
405
|
+
# @param block [Proc] block to search for identifier based on attribute value
|
|
406
|
+
# @return [String, Array] identifier or list of ids
|
|
407
|
+
def instance_identifier(description: 'identifier', &block)
|
|
408
|
+
res_id = get_next_argument(description, multiple: get_option(:bulk)) if res_id.nil?
|
|
409
|
+
# Can be an Array
|
|
410
|
+
if res_id.is_a?(String) && (m = Manager.percent_selector(res_id))
|
|
411
|
+
Aspera.assert(block_given?, type: Cli::BadArgument){"Percent syntax for #{description} not supported in this context"}
|
|
412
|
+
res_id = yield(m[:field], m[:value])
|
|
413
|
+
end
|
|
414
|
+
return res_id
|
|
415
|
+
end
|
|
416
|
+
|
|
393
417
|
def get_next_command(command_list, aliases: nil); return get_next_argument('command', accept_list: command_list, aliases: aliases); end
|
|
394
418
|
|
|
395
419
|
# Get an option value by name
|
|
@@ -508,7 +532,7 @@ module Aspera
|
|
|
508
532
|
begin
|
|
509
533
|
# remove known options one by one, exception if unknown
|
|
510
534
|
Log.log.trace1('Before parse')
|
|
511
|
-
Log.dump(:unprocessed_cmd_line_options, @unprocessed_cmd_line_options)
|
|
535
|
+
Log.dump(:unprocessed_cmd_line_options, @unprocessed_cmd_line_options, level: :trace1)
|
|
512
536
|
@parser.parse!(@unprocessed_cmd_line_options)
|
|
513
537
|
Log.log.trace1('After parse')
|
|
514
538
|
rescue OptionParser::InvalidOption => e
|
|
@@ -678,8 +702,10 @@ module Aspera
|
|
|
678
702
|
# when this is alone, this stops option processing
|
|
679
703
|
OPTIONS_STOP = '--'
|
|
680
704
|
SOURCE_USER = 'cmdline' # cspell:disable-line
|
|
705
|
+
# Percent selector: select by this field for this value
|
|
706
|
+
REGEX_LOOKUP_ID_BY_FIELD = /^%([^:]+):(.*)$/
|
|
681
707
|
|
|
682
|
-
private_constant :OPTION_SEP_LINE, :OPTION_SEP_SYMBOL, :OPTION_VALUE_SEPARATOR, :OPTION_PREFIX, :OPTIONS_STOP, :SOURCE_USER
|
|
708
|
+
private_constant :OPTION_SEP_LINE, :OPTION_SEP_SYMBOL, :OPTION_VALUE_SEPARATOR, :OPTION_PREFIX, :OPTIONS_STOP, :SOURCE_USER, :REGEX_LOOKUP_ID_BY_FIELD
|
|
683
709
|
end
|
|
684
710
|
end
|
|
685
711
|
end
|