aspera-cli 4.20.0 → 4.21.1
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 +29 -3
- data/CONTRIBUTING.md +2 -0
- data/README.md +571 -375
- data/bin/asession +2 -2
- data/examples/get_proto_file.rb +1 -1
- data/lib/aspera/agent/alpha.rb +10 -16
- data/lib/aspera/agent/connect.rb +20 -2
- data/lib/aspera/agent/direct.rb +21 -30
- data/lib/aspera/agent/node.rb +1 -11
- data/lib/aspera/agent/{trsdk.rb → transferd.rb} +13 -34
- data/lib/aspera/api/aoc.rb +13 -8
- data/lib/aspera/api/node.rb +45 -28
- data/lib/aspera/ascp/installation.rb +87 -48
- data/lib/aspera/ascp/management.rb +27 -6
- data/lib/aspera/cli/formatter.rb +148 -154
- data/lib/aspera/cli/info.rb +1 -1
- data/lib/aspera/cli/main.rb +12 -0
- data/lib/aspera/cli/manager.rb +2 -2
- data/lib/aspera/cli/plugin.rb +2 -2
- data/lib/aspera/cli/plugins/aoc.rb +28 -18
- data/lib/aspera/cli/plugins/config.rb +106 -54
- data/lib/aspera/cli/plugins/cos.rb +1 -0
- data/lib/aspera/cli/plugins/faspex.rb +4 -2
- data/lib/aspera/cli/plugins/faspex5.rb +21 -9
- data/lib/aspera/cli/plugins/node.rb +45 -38
- data/lib/aspera/cli/transfer_progress.rb +2 -0
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/command_line_builder.rb +1 -1
- data/lib/aspera/environment.rb +48 -14
- data/lib/aspera/node_simulator.rb +230 -112
- data/lib/aspera/oauth/base.rb +34 -47
- data/lib/aspera/oauth/factory.rb +41 -2
- data/lib/aspera/oauth/jwt.rb +4 -1
- data/lib/aspera/persistency_action_once.rb +1 -1
- data/lib/aspera/persistency_folder.rb +20 -2
- data/lib/aspera/preview/generator.rb +1 -1
- data/lib/aspera/preview/utils.rb +8 -3
- data/lib/aspera/products/alpha.rb +30 -0
- data/lib/aspera/products/connect.rb +48 -0
- data/lib/aspera/products/other.rb +82 -0
- data/lib/aspera/products/transferd.rb +54 -0
- data/lib/aspera/rest.rb +18 -13
- data/lib/aspera/secret_hider.rb +2 -2
- data/lib/aspera/ssh.rb +31 -24
- data/lib/aspera/transfer/parameters.rb +2 -1
- data/lib/aspera/transfer/spec.yaml +22 -20
- data/lib/aspera/transfer/sync.rb +1 -5
- data/lib/aspera/transfer/uri.rb +2 -2
- data/lib/transferd_pb.rb +86 -0
- data/lib/transferd_services_pb.rb +84 -0
- data.tar.gz.sig +0 -0
- metadata +38 -21
- metadata.gz.sig +0 -0
- data/lib/aspera/ascp/products.rb +0 -168
- data/lib/transfer_pb.rb +0 -84
- data/lib/transfer_services_pb.rb +0 -82
data/lib/aspera/cli/formatter.rb
CHANGED
@@ -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
|
-
|
92
|
+
# column names for single object display in table
|
93
|
+
SINGLE_OBJECT_COLUMN_NAMES = %i[field value].freeze
|
105
94
|
|
106
|
-
private_constant :
|
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
|
-
|
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,28 +212,6 @@ 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
|
@@ -244,6 +238,116 @@ module Aspera
|
|
244
238
|
display_status(count_msg)
|
245
239
|
end
|
246
240
|
|
241
|
+
def hide_secrets(data)
|
242
|
+
SecretHider.deep_remove_secret(data) unless @options[:show_secrets] || @options[:display].eql?(:data)
|
243
|
+
end
|
244
|
+
|
245
|
+
# this method displays the results, especially the table format
|
246
|
+
# @param type [Symbol] type of data
|
247
|
+
# @param data [Object] data to display
|
248
|
+
# @param total [Integer] total number of items
|
249
|
+
# @param fields [Array<String>] list of fields to display
|
250
|
+
# @param name [String] name of the column to display
|
251
|
+
def display_results(type:, data: nil, total: nil, fields: nil, name: nil)
|
252
|
+
Log.log.debug{"display_results: #{type} class=#{data.class}"}
|
253
|
+
Log.log.trace1{"display_results:data=#{data}"}
|
254
|
+
Aspera.assert_type(type, Symbol){'result must have type'}
|
255
|
+
Aspera.assert(!data.nil? || %i[empty nothing].include?(type)){'result must have data'}
|
256
|
+
display_item_count(data.length, total) unless total.nil?
|
257
|
+
hide_secrets(data)
|
258
|
+
case @options[:format]
|
259
|
+
when :text
|
260
|
+
display_message(:data, data.to_s)
|
261
|
+
when :nagios
|
262
|
+
Nagios.process(data)
|
263
|
+
when :ruby
|
264
|
+
display_message(:data, PP.pp(filter_list_on_fields(data), +''))
|
265
|
+
when :json
|
266
|
+
display_message(:data, JSON.generate(filter_list_on_fields(data)))
|
267
|
+
when :jsonpp
|
268
|
+
display_message(:data, JSON.pretty_generate(filter_list_on_fields(data)))
|
269
|
+
when :yaml
|
270
|
+
display_message(:data, YAML.dump(filter_list_on_fields(data)))
|
271
|
+
when :image
|
272
|
+
# assume it is an url
|
273
|
+
url = data
|
274
|
+
case type
|
275
|
+
when :single_object, :object_list
|
276
|
+
url = [url] if type.eql?(:single_object)
|
277
|
+
raise 'image display requires a single result' unless url.length == 1
|
278
|
+
fields = compute_fields(url, fields)
|
279
|
+
raise 'select a field to display' unless fields.length == 1
|
280
|
+
url = url.first
|
281
|
+
raise 'no such field' unless url.key?(fields.first)
|
282
|
+
url = url[fields.first]
|
283
|
+
end
|
284
|
+
raise "not url: #{url.class} #{url}" unless url.is_a?(String)
|
285
|
+
display_message(:data, status_image(url))
|
286
|
+
when :table, :csv
|
287
|
+
case type
|
288
|
+
when :object_list, :single_object
|
289
|
+
obj_list = data
|
290
|
+
if type.eql?(:single_object)
|
291
|
+
obj_list = [obj_list]
|
292
|
+
@options[:multi_single] = :yes
|
293
|
+
end
|
294
|
+
Aspera.assert_type(obj_list, Array)
|
295
|
+
Aspera.assert(obj_list.all?(Hash)){"expecting Array of Hash: #{obj_list.inspect}"}
|
296
|
+
# :object_list is an array of hash tables, where key=colum name
|
297
|
+
obj_list = obj_list.map{|obj|Flattener.new(self).flatten(obj)} if @options[:flat_hash]
|
298
|
+
display_table(obj_list, compute_fields(obj_list, fields))
|
299
|
+
when :value_list
|
300
|
+
# :value_list is a simple array of values, name of column provided in the :name
|
301
|
+
display_table(data.map { |i| { name => i } }, [name])
|
302
|
+
when :empty # no table
|
303
|
+
display_message(:info, special_format('empty'))
|
304
|
+
return
|
305
|
+
when :nothing # no result expected
|
306
|
+
Log.log.debug('no result expected')
|
307
|
+
when :status # no table
|
308
|
+
# :status displays a simple message
|
309
|
+
display_message(:info, data)
|
310
|
+
when :text # no table
|
311
|
+
# :status displays a simple message
|
312
|
+
display_message(:data, data)
|
313
|
+
when :other_struct # no table
|
314
|
+
# :other_struct is any other type of structure
|
315
|
+
display_message(:data, PP.pp(data, +''))
|
316
|
+
else
|
317
|
+
raise "unknown data type: #{type}"
|
318
|
+
end
|
319
|
+
else
|
320
|
+
raise "not expected: #{@options[:format]}"
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
# @return text suitable to display an image from url
|
325
|
+
# @param blob [String] either a URL or image data
|
326
|
+
def status_image(blob)
|
327
|
+
# check if URL
|
328
|
+
begin
|
329
|
+
URI.parse(blob)
|
330
|
+
url = blob
|
331
|
+
unless Environment.instance.url_method.eql?(:text)
|
332
|
+
Environment.instance.open_uri(url)
|
333
|
+
return ''
|
334
|
+
end
|
335
|
+
blob = UriReader.read(url)
|
336
|
+
rescue URI::InvalidURIError
|
337
|
+
nil
|
338
|
+
end
|
339
|
+
# try base64
|
340
|
+
begin
|
341
|
+
blob = Base64.strict_decode64(blob)
|
342
|
+
rescue
|
343
|
+
nil
|
344
|
+
end
|
345
|
+
return Preview::Terminal.build(blob, **@options[:image].symbolize_keys)
|
346
|
+
end
|
347
|
+
#==========================================================================================
|
348
|
+
|
349
|
+
private
|
350
|
+
|
247
351
|
def all_fields(data)
|
248
352
|
data.each_with_object({}){|v, m|v.each_key{|c|m[c] = true}}.keys
|
249
353
|
end
|
@@ -300,7 +404,7 @@ module Aspera
|
|
300
404
|
return data if @options[:fields].eql?(SpecialValues::DEF)
|
301
405
|
selected_fields = compute_fields(data, @options[:fields])
|
302
406
|
return data.map{|i|i[selected_fields.first]} if selected_fields.length == 1
|
303
|
-
return data.map{|i|i.
|
407
|
+
return data.map{|i|i.slice(*selected_fields)}
|
304
408
|
end
|
305
409
|
|
306
410
|
# filter the list of items on the select option
|
@@ -314,9 +418,9 @@ module Aspera
|
|
314
418
|
end
|
315
419
|
end
|
316
420
|
|
317
|
-
#
|
318
|
-
# object_array
|
319
|
-
# fields
|
421
|
+
# displays a list of objects
|
422
|
+
# @param object_array [Array] array of hash
|
423
|
+
# @param fields [Array] list of column names
|
320
424
|
def display_table(object_array, fields)
|
321
425
|
Aspera.assert(!fields.nil?){'missing fields parameter'}
|
322
426
|
filter_columns_on_select(object_array)
|
@@ -330,13 +434,6 @@ module Aspera
|
|
330
434
|
display_message(:data, object_array.first[fields.first])
|
331
435
|
return
|
332
436
|
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
437
|
Log.log.debug{Log.dump(:object_array, object_array)}
|
341
438
|
# convert data to string, and keep only display fields
|
342
439
|
final_table_rows = object_array.map { |r| fields.map { |c| r[c].to_s } }
|
@@ -345,11 +442,12 @@ module Aspera
|
|
345
442
|
# here : fields : list of column names
|
346
443
|
case @options[:format]
|
347
444
|
when :table
|
348
|
-
if @options[:
|
445
|
+
if @options[:multi_single].eql?(:yes) ||
|
446
|
+
(@options[:multi_single].eql?(:single) && final_table_rows.length.eql?(1))
|
349
447
|
final_table_rows.each do |row|
|
350
448
|
Log.log.debug{Log.dump(:row, row)}
|
351
449
|
display_message(:data, Terminal::Table.new(
|
352
|
-
headings:
|
450
|
+
headings: SINGLE_OBJECT_COLUMN_NAMES,
|
353
451
|
rows: fields.zip(row),
|
354
452
|
style: @options[:table_style]&.symbolize_keys))
|
355
453
|
end
|
@@ -366,110 +464,6 @@ module Aspera
|
|
366
464
|
raise "not expected: #{@options[:format]}"
|
367
465
|
end
|
368
466
|
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
467
|
end
|
474
468
|
end
|
475
469
|
end
|
data/lib/aspera/cli/info.rb
CHANGED
@@ -12,7 +12,7 @@ module Aspera
|
|
12
12
|
SRC_URL = 'https://github.com/IBM/aspera-cli'
|
13
13
|
# set this to warn in advance when minimum required ruby version will increase
|
14
14
|
# see also required_ruby_version in gemspec file
|
15
|
-
RUBY_FUTURE_MINIMUM_VERSION = '3.
|
15
|
+
RUBY_FUTURE_MINIMUM_VERSION = '3.2'
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
data/lib/aspera/cli/main.rb
CHANGED
@@ -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)
|
68
|
+
return {type: :single_object, data: data}
|
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
|
data/lib/aspera/cli/manager.rb
CHANGED
@@ -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
|
@@ -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
|
data/lib/aspera/cli/plugin.rb
CHANGED
@@ -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{"
|
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
|
@@ -66,14 +66,14 @@ module Aspera
|
|
66
66
|
base_url = "#{base_url}.#{Api::AoC::SAAS_DOMAIN_PROD}" unless base_url.include?('.')
|
67
67
|
# AoC is only https
|
68
68
|
return nil unless base_url.start_with?('https://')
|
69
|
-
res_http = Rest.new(base_url: base_url, redirect_max:
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
69
|
+
res_http = Rest.new(base_url: base_url, redirect_max: 0).call(operation: 'GET', subpath: 'auth/ping', return_error: true)[:http]
|
70
|
+
return nil if res_http['Location'].nil?
|
71
|
+
redirect_uri = URI.parse(res_http['Location'])
|
72
|
+
od = Api::AoC.split_org_domain(URI.parse(base_url))
|
73
|
+
return nil unless redirect_uri.path.end_with?("oauth2/#{od[:organization]}/login")
|
74
74
|
# either in standard domain, or product name in page
|
75
75
|
return {
|
76
|
-
version: 'SaaS',
|
76
|
+
version: Api::AoC.saas_url?(base_url) ? 'SaaS' : 'Self-managed',
|
77
77
|
url: base_url
|
78
78
|
}
|
79
79
|
end
|
@@ -92,8 +92,6 @@ module Aspera
|
|
92
92
|
# set vars to look like object
|
93
93
|
options = object.options
|
94
94
|
formatter = object.formatter
|
95
|
-
options.declare(:use_generic_client, 'Wizard: AoC: use global or org specific jwt client id', values: :bool, default: true)
|
96
|
-
options.parse_options!
|
97
95
|
instance_url = options.get_option(:url, mandatory: true)
|
98
96
|
pub_link_info = Api::AoC.link_info(instance_url)
|
99
97
|
if !pub_link_info[:token].nil?
|
@@ -108,6 +106,8 @@ module Aspera
|
|
108
106
|
test_args: 'organization'
|
109
107
|
}
|
110
108
|
end
|
109
|
+
options.declare(:use_generic_client, 'Wizard: AoC: use global or org specific jwt client id', values: :bool, default: Api::AoC.saas_url?(instance_url))
|
110
|
+
options.parse_options!
|
111
111
|
# make username mandatory for jwt, this triggers interactive input
|
112
112
|
wiz_username = options.get_option(:username, mandatory: true)
|
113
113
|
raise "Username shall be an email in AoC: #{wiz_username}" if !(wiz_username =~ /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i)
|
@@ -133,15 +133,15 @@ module Aspera
|
|
133
133
|
formatter.display_status('Please login to your Aspera on Cloud instance.'.red)
|
134
134
|
formatter.display_status('Navigate to: 𓃑 → Admin → Integrations → API Clients')
|
135
135
|
formatter.display_status('Check or create in integration:')
|
136
|
-
formatter.display_status(
|
136
|
+
formatter.display_status('- name: cli')
|
137
137
|
formatter.display_status("- redirect uri: #{REDIRECT_LOCALHOST}")
|
138
138
|
formatter.display_status('- origin: localhost')
|
139
139
|
formatter.display_status('Use the generated client id and secret in the following prompts.'.red)
|
140
140
|
end
|
141
|
-
Environment.instance.open_uri("#{instance_url}/admin/api-clients")
|
141
|
+
Environment.instance.open_uri("#{instance_url}/admin/integrations/api-clients")
|
142
142
|
options.get_option(:client_id, mandatory: true)
|
143
143
|
options.get_option(:client_secret, mandatory: true)
|
144
|
-
use_browser_authentication = true
|
144
|
+
# use_browser_authentication = true
|
145
145
|
end
|
146
146
|
if use_browser_authentication
|
147
147
|
formatter.display_status('We will use web authentication to bootstrap.')
|
@@ -282,13 +282,17 @@ module Aspera
|
|
282
282
|
end
|
283
283
|
|
284
284
|
# list all entities, given additional, default and user's queries
|
285
|
+
# @param resource_class_path path to query on API
|
286
|
+
# @param fields fields to display
|
287
|
+
# @param base_query a query applied always
|
288
|
+
# @param default_query default query unless overriden by user
|
285
289
|
def result_list(resource_class_path, fields: nil, base_query: {}, default_query: {})
|
286
290
|
Aspera.assert_type(base_query, Hash)
|
287
291
|
Aspera.assert_type(default_query, Hash)
|
288
292
|
user_query = query_read_delete(default: default_query)
|
289
293
|
# caller may add specific modifications or checks
|
290
294
|
yield(user_query) if block_given?
|
291
|
-
return {type: :object_list, fields: fields}.merge(api_read_all(resource_class_path, base_query.merge(user_query)))
|
295
|
+
return {type: :object_list, fields: fields}.merge(api_read_all(resource_class_path, base_query.merge(user_query).compact))
|
292
296
|
end
|
293
297
|
|
294
298
|
def resolve_dropbox_name_default_ws_id(query)
|
@@ -411,8 +415,8 @@ module Aspera
|
|
411
415
|
when :operation then default_fields = nil
|
412
416
|
when :short_link then default_fields.push('short_url', 'data.url_token_data.purpose')
|
413
417
|
when :user then default_fields.push('name', 'email')
|
414
|
-
when :group_membership then default_fields.push(
|
415
|
-
when :workspace_membership then default_fields.push(
|
418
|
+
when :group_membership then default_fields.push('group_id', 'member_type', 'member_id')
|
419
|
+
when :workspace_membership then default_fields.push('workspace_id', 'member_type', 'member_id')
|
416
420
|
end
|
417
421
|
return result_list(resource_class_path, fields: default_fields, default_query: default_query)
|
418
422
|
when :show
|
@@ -801,6 +805,12 @@ module Aspera
|
|
801
805
|
else
|
802
806
|
ids_to_download = [ids_to_download] unless ids_to_download.is_a?(Array)
|
803
807
|
end
|
808
|
+
file_list =
|
809
|
+
begin
|
810
|
+
transfer.source_list.map{|i|{'source'=>i}}
|
811
|
+
rescue Cli::BadArgument
|
812
|
+
[{'source' => '.'}]
|
813
|
+
end
|
804
814
|
# list here
|
805
815
|
result_transfer = []
|
806
816
|
formatter.display_status("found #{ids_to_download.length} package(s).")
|
@@ -816,7 +826,7 @@ module Aspera
|
|
816
826
|
package_node_api.transfer_spec_gen4(
|
817
827
|
package_info['contents_file_id'],
|
818
828
|
Transfer::Spec::DIRECTION_RECEIVE,
|
819
|
-
{'paths'=>
|
829
|
+
{'paths'=> file_list}),
|
820
830
|
rest_token: package_node_api)
|
821
831
|
result_transfer.push({'package' => package_id, Main::STATUS_FIELD => statuses})
|
822
832
|
# update skip list only if all transfer sessions completed
|
@@ -837,14 +847,14 @@ module Aspera
|
|
837
847
|
resolve_dropbox_name_default_ws_id(query)
|
838
848
|
end
|
839
849
|
when :delete
|
840
|
-
return do_bulk_operation(command: package_command, descr: 'identifier', values:
|
850
|
+
return do_bulk_operation(command: package_command, descr: 'identifier', values: instance_identifier) do |id|
|
841
851
|
Aspera.assert_values(id.class, [String, Integer]){'identifier'}
|
842
852
|
aoc_api.delete("packages/#{id}")
|
843
853
|
end
|
844
854
|
when *Node::NODE4_READ_ACTIONS
|
845
855
|
package_id = instance_identifier
|
846
856
|
package_info = aoc_api.read("packages/#{package_id}")
|
847
|
-
return execute_nodegen4_command(package_command, package_info['node_id'], file_id: package_info['
|
857
|
+
return execute_nodegen4_command(package_command, package_info['node_id'], file_id: package_info['contents_file_id'], scope: Api::Node::SCOPE_USER)
|
848
858
|
end
|
849
859
|
when :files
|
850
860
|
command_repo = options.get_next_command([:short_link].concat(NODE4_EXT_COMMANDS))
|
@@ -891,7 +901,7 @@ module Aspera
|
|
891
901
|
when :automation
|
892
902
|
Log.log.warn('BETA: work under progress')
|
893
903
|
# automation api is not in the same place
|
894
|
-
automation_api = Rest.new(**aoc_api.params
|
904
|
+
automation_api = Rest.new(**aoc_api.params, base_url: aoc_api.base_url.gsub('/api/', '/automation/'))
|
895
905
|
command_automation = options.get_next_command(%i[workflows instances])
|
896
906
|
case command_automation
|
897
907
|
when :instances
|