aspera-cli 4.25.3 → 4.25.5
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 +40 -6
- data/CONTRIBUTING.md +122 -111
- data/README.md +9 -7
- data/lib/aspera/agent/direct.rb +10 -8
- data/lib/aspera/agent/factory.rb +3 -3
- data/lib/aspera/agent/node.rb +1 -1
- data/lib/aspera/api/alee.rb +1 -0
- data/lib/aspera/api/aoc.rb +15 -14
- data/lib/aspera/api/ats.rb +1 -1
- data/lib/aspera/api/cos_node.rb +5 -0
- data/lib/aspera/api/faspex.rb +27 -20
- data/lib/aspera/api/httpgw.rb +19 -3
- data/lib/aspera/api/node.rb +122 -29
- data/lib/aspera/ascp/installation.rb +9 -10
- data/lib/aspera/cli/error.rb +8 -0
- data/lib/aspera/cli/formatter.rb +27 -11
- data/lib/aspera/cli/info.rb +2 -1
- data/lib/aspera/cli/main.rb +30 -12
- data/lib/aspera/cli/manager.rb +43 -31
- data/lib/aspera/cli/plugins/aoc.rb +7 -5
- data/lib/aspera/cli/plugins/base.rb +1 -88
- data/lib/aspera/cli/plugins/config.rb +2 -1
- data/lib/aspera/cli/plugins/faspex.rb +6 -6
- data/lib/aspera/cli/plugins/faspex5.rb +64 -64
- data/lib/aspera/cli/plugins/node.rb +33 -78
- data/lib/aspera/cli/plugins/shares.rb +4 -2
- data/lib/aspera/cli/special_values.rb +1 -0
- data/lib/aspera/cli/transfer_agent.rb +3 -0
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +2 -1
- data/lib/aspera/dot_container.rb +10 -10
- data/lib/aspera/log.rb +1 -1
- data/lib/aspera/markdown.rb +1 -1
- data/lib/aspera/persistency_folder.rb +1 -1
- data/lib/aspera/rest.rb +39 -54
- data/lib/aspera/rest_list.rb +121 -0
- data/lib/aspera/sync/operations.rb +1 -1
- data/lib/aspera/transfer/parameters.rb +8 -8
- data/lib/aspera/transfer/spec.rb +1 -0
- data/lib/aspera/yaml.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +4 -3
- metadata.gz.sig +0 -0
data/lib/aspera/cli/formatter.rb
CHANGED
|
@@ -128,20 +128,34 @@ module Aspera
|
|
|
128
128
|
@spinner = nil
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
-
|
|
132
|
-
def long_operation_running(title = '')
|
|
131
|
+
def long_operation(title = nil, action: :spin)
|
|
133
132
|
return unless Environment.terminal?
|
|
134
|
-
if
|
|
133
|
+
return if %i[error data].include?(@options[:display])
|
|
134
|
+
|
|
135
|
+
# Handle the "delayed start" state
|
|
136
|
+
return @spinner = :starting if action == :spin && @spinner.nil?
|
|
137
|
+
|
|
138
|
+
# Cleanup if we try to stop a spinner that never actually started
|
|
139
|
+
@spinner = nil if action != :spin && @spinner == :starting
|
|
140
|
+
return if @spinner.nil?
|
|
141
|
+
|
|
142
|
+
# Initialize the real TTY object if it's currently just the :starting symbol
|
|
143
|
+
if @spinner == :starting
|
|
135
144
|
@spinner = TTY::Spinner.new('[:spinner] :title', format: :classic)
|
|
145
|
+
@spinner.update(title: '')
|
|
136
146
|
@spinner.start
|
|
137
147
|
end
|
|
138
|
-
@spinner.update(title: title)
|
|
139
|
-
@spinner.spin
|
|
140
|
-
end
|
|
141
148
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
149
|
+
@spinner.update(title: title) if title
|
|
150
|
+
|
|
151
|
+
case action
|
|
152
|
+
when :spin
|
|
153
|
+
@spinner.spin
|
|
154
|
+
when :success, :fail
|
|
155
|
+
action == :success ? @spinner.success : @spinner.error
|
|
156
|
+
@spinner.stop
|
|
157
|
+
@spinner = nil
|
|
158
|
+
end
|
|
145
159
|
end
|
|
146
160
|
|
|
147
161
|
def declare_options(options)
|
|
@@ -150,9 +164,9 @@ module Aspera
|
|
|
150
164
|
else
|
|
151
165
|
{}
|
|
152
166
|
end
|
|
167
|
+
options.declare(:display, 'Output only some information', allowed: DISPLAY_LEVELS, handler: {o: self, m: :option_handler}, default: :data)
|
|
153
168
|
options.declare(:format, 'Output format', allowed: DISPLAY_FORMATS, handler: {o: self, m: :option_handler}, default: :table)
|
|
154
169
|
options.declare(:output, 'Destination for results', handler: {o: self, m: :option_handler})
|
|
155
|
-
options.declare(:display, 'Output only some information', allowed: DISPLAY_LEVELS, handler: {o: self, m: :option_handler}, default: :info)
|
|
156
170
|
options.declare(
|
|
157
171
|
:fields, "Comma separated list of: fields, or #{SpecialValues::ALL}, or #{SpecialValues::DEF}", handler: {o: self, m: :option_handler},
|
|
158
172
|
allowed: [String, Array, Regexp, Proc],
|
|
@@ -178,6 +192,8 @@ module Aspera
|
|
|
178
192
|
@options[option_symbol] = value
|
|
179
193
|
# special handling of some options
|
|
180
194
|
case option_symbol
|
|
195
|
+
when :format
|
|
196
|
+
@options[:display] = value.eql?(:table) ? :info : :data
|
|
181
197
|
when :output
|
|
182
198
|
$stdout = if value.eql?('-')
|
|
183
199
|
STDOUT # rubocop:disable Style/GlobalStdStream
|
|
@@ -197,7 +213,7 @@ module Aspera
|
|
|
197
213
|
nil
|
|
198
214
|
end
|
|
199
215
|
|
|
200
|
-
#
|
|
216
|
+
# Main output method
|
|
201
217
|
# data: for requested data, not displayed if level==error
|
|
202
218
|
# info: additional info, displayed if level==info
|
|
203
219
|
# error: always displayed on stderr
|
data/lib/aspera/cli/info.rb
CHANGED
|
@@ -7,7 +7,8 @@ module Aspera
|
|
|
7
7
|
CMD_NAME = 'ascli'
|
|
8
8
|
# Name of the containing gem, same as in <gem name>.gemspec
|
|
9
9
|
GEM_NAME = 'aspera-cli'
|
|
10
|
-
DOC_URL =
|
|
10
|
+
DOC_URL = 'https://ibm.biz/ascli-doc'
|
|
11
|
+
RUBYDOC_URL = "https://www.rubydoc.info/gems/#{GEM_NAME}"
|
|
11
12
|
GEM_URL = "https://rubygems.org/gems/#{GEM_NAME}"
|
|
12
13
|
SRC_URL = 'https://github.com/IBM/aspera-cli'
|
|
13
14
|
CONTAINER = 'docker.io/martinlaurent/ascli'
|
data/lib/aspera/cli/main.rb
CHANGED
|
@@ -12,18 +12,34 @@ require 'aspera/cli/hints'
|
|
|
12
12
|
require 'aspera/secret_hider'
|
|
13
13
|
require 'aspera/log'
|
|
14
14
|
require 'aspera/assert'
|
|
15
|
+
require 'net/ssh/errors'
|
|
16
|
+
require 'openssl'
|
|
15
17
|
|
|
16
18
|
module Aspera
|
|
17
19
|
module Cli
|
|
18
20
|
# Global objects shared with plugins
|
|
19
21
|
class Context
|
|
22
|
+
# @!attribute [rw] options
|
|
23
|
+
# @return [Manager] the command line options manager
|
|
24
|
+
# @!attribute [rw] transfer
|
|
25
|
+
# @return [TransferAgent] the transfer agent, used by transfer plugins
|
|
26
|
+
# @!attribute [rw] config
|
|
27
|
+
# @return [Plugins::Config] the configuration plugin, used by plugins to get configuration values and presets
|
|
28
|
+
# @!attribute [rw] formatter
|
|
29
|
+
# @return [Formatter] the formatter, used by plugins to display results and messages
|
|
30
|
+
# @!attribute [rw] persistency
|
|
31
|
+
# @return [Object] # whatever the type is
|
|
32
|
+
# @!attribute [rw] man_header
|
|
33
|
+
# @return [Boolean] whether to display the manual header in plugin help
|
|
20
34
|
MEMBERS = %i[options transfer config formatter persistency man_header].freeze
|
|
21
35
|
attr_accessor(*MEMBERS)
|
|
22
36
|
|
|
37
|
+
# Initialize all members to nil, so that they are defined and can be validated later
|
|
23
38
|
def initialize
|
|
24
|
-
|
|
39
|
+
MEMBERS.each{ |i| instance_variable_set(:"@#{i}", nil)}
|
|
25
40
|
end
|
|
26
41
|
|
|
42
|
+
# Validate that all members are set, raise exception if not
|
|
27
43
|
def validate
|
|
28
44
|
MEMBERS.each do |i|
|
|
29
45
|
Aspera.assert(instance_variable_defined?(:"@#{i}"))
|
|
@@ -35,7 +51,7 @@ module Aspera
|
|
|
35
51
|
transfer.eql?(:only_manual)
|
|
36
52
|
end
|
|
37
53
|
|
|
38
|
-
def only_manual
|
|
54
|
+
def only_manual!
|
|
39
55
|
@transfer = :only_manual
|
|
40
56
|
end
|
|
41
57
|
end
|
|
@@ -89,13 +105,15 @@ module Aspera
|
|
|
89
105
|
status_table.each do |item|
|
|
90
106
|
worst = TransferAgent.session_status(item[STATUS_FIELD])
|
|
91
107
|
global_status = worst unless worst.eql?(:success)
|
|
92
|
-
item[STATUS_FIELD] = item[STATUS_FIELD].
|
|
108
|
+
item[STATUS_FIELD] = item[STATUS_FIELD].join(',')
|
|
93
109
|
end
|
|
94
110
|
raise global_status unless global_status.eql?(:success)
|
|
95
111
|
return result_object_list(status_table)
|
|
96
112
|
end
|
|
97
113
|
|
|
98
114
|
# Display image for that URL or directly blob
|
|
115
|
+
#
|
|
116
|
+
# @param url_or_blob [String] URL or blob to display as image
|
|
99
117
|
def result_image(url_or_blob)
|
|
100
118
|
return {type: :image, data: url_or_blob}
|
|
101
119
|
end
|
|
@@ -111,6 +129,9 @@ module Aspera
|
|
|
111
129
|
end
|
|
112
130
|
|
|
113
131
|
# A list of values
|
|
132
|
+
#
|
|
133
|
+
# @param data [Array] The list of values
|
|
134
|
+
# @param name [String] The name of the list (used for display)
|
|
114
135
|
def result_value_list(data, name: 'id')
|
|
115
136
|
Aspera.assert_type(data, Array)
|
|
116
137
|
Aspera.assert_type(name, String)
|
|
@@ -155,7 +176,9 @@ module Aspera
|
|
|
155
176
|
execute_command = true
|
|
156
177
|
# Catch exceptions
|
|
157
178
|
begin
|
|
158
|
-
|
|
179
|
+
init_agents_and_options
|
|
180
|
+
# Find plugins, shall be after parse! ?
|
|
181
|
+
Plugins::Factory.instance.add_plugins_from_lookup_folders
|
|
159
182
|
# Help requested without command ? (plugins must be known here)
|
|
160
183
|
show_usage if @option_help && @context.options.command_or_arg_empty?
|
|
161
184
|
generate_bash_completion if @bash_completion
|
|
@@ -253,17 +276,11 @@ module Aspera
|
|
|
253
276
|
return
|
|
254
277
|
end
|
|
255
278
|
|
|
256
|
-
def init_agents_options_plugins
|
|
257
|
-
init_agents_and_options
|
|
258
|
-
# Find plugins, shall be after parse! ?
|
|
259
|
-
Plugins::Factory.instance.add_plugins_from_lookup_folders
|
|
260
|
-
end
|
|
261
|
-
|
|
262
279
|
def show_usage(all: true, exit: true)
|
|
263
280
|
# Display main plugin options (+config)
|
|
264
281
|
@context.formatter.display_message(:error, @context.options.parser)
|
|
265
282
|
if all
|
|
266
|
-
@context.only_manual
|
|
283
|
+
@context.only_manual!
|
|
267
284
|
# List plugins that have a "require" field, i.e. all but main plugin
|
|
268
285
|
Plugins::Factory.instance.plugin_list.each do |plugin_name_sym|
|
|
269
286
|
# Config was already included in the global options
|
|
@@ -283,6 +300,7 @@ module Aspera
|
|
|
283
300
|
|
|
284
301
|
# This can throw exception if there is a problem with the environment, needs to be caught by execute method
|
|
285
302
|
def init_agents_and_options
|
|
303
|
+
@context.man_header = true
|
|
286
304
|
# Create formatter, in case there is an exception, it is used to display.
|
|
287
305
|
@context.formatter = Formatter.new
|
|
288
306
|
# Create command line manager with arguments
|
|
@@ -336,7 +354,7 @@ module Aspera
|
|
|
336
354
|
OPTIONS
|
|
337
355
|
#{t}Options begin with a '-' (minus), and value is provided on command line.
|
|
338
356
|
#{t}Special values are supported beginning with special prefix @pfx:, where pfx is one of:
|
|
339
|
-
#{t}#{ExtendedValue.instance.modifiers.
|
|
357
|
+
#{t}#{ExtendedValue.instance.modifiers.join(', ')}
|
|
340
358
|
#{t}Dates format is 'DD-MM-YY HH:MM:SS', or 'now' or '-<num>h'
|
|
341
359
|
|
|
342
360
|
ARGS
|
data/lib/aspera/cli/manager.rb
CHANGED
|
@@ -194,7 +194,9 @@ module Aspera
|
|
|
194
194
|
[error_msg, 'Use:'].concat(accept_list.map{ |c| "- #{c}"}.sort).join("\n")
|
|
195
195
|
end
|
|
196
196
|
|
|
197
|
-
#
|
|
197
|
+
# Change option name with dash to name with underscore
|
|
198
|
+
# @param name [String] option name
|
|
199
|
+
# @return [String]
|
|
198
200
|
def option_line_to_name(name)
|
|
199
201
|
return name.gsub(OPTION_SEP_LINE, OPTION_SEP_SYMBOL)
|
|
200
202
|
end
|
|
@@ -211,7 +213,7 @@ module Aspera
|
|
|
211
213
|
def initialize(program_name, argv = nil)
|
|
212
214
|
# command line values *not* starting with '-'
|
|
213
215
|
@unprocessed_cmd_line_arguments = []
|
|
214
|
-
# command line values starting with '-'
|
|
216
|
+
# command line values starting with at least one '-'
|
|
215
217
|
@unprocessed_cmd_line_options = []
|
|
216
218
|
# a copy of all initial options
|
|
217
219
|
@initial_cli_options = []
|
|
@@ -330,7 +332,7 @@ module Aspera
|
|
|
330
332
|
|
|
331
333
|
# @param descr [String] description for help
|
|
332
334
|
# @param mandatory [Boolean] if true, raise error if option not set
|
|
333
|
-
# @param multiple [Boolean] if true, return remaining arguments (Array)
|
|
335
|
+
# @param multiple [Boolean] if true, return remaining arguments (Array) until END
|
|
334
336
|
# @param accept_list [Array, NilClass] list of allowed values (Symbol)
|
|
335
337
|
# @param validation [Class, Array, NilClass] Accepted value type(s) or list of Symbols
|
|
336
338
|
# @param aliases [Hash] map of aliases: key = alias, value = real value
|
|
@@ -345,10 +347,19 @@ module Aspera
|
|
|
345
347
|
descr = "#{descr} (#{validation.join(', ')})" unless validation.nil? || validation.eql?(Allowed::TYPES_STRING)
|
|
346
348
|
result =
|
|
347
349
|
if !@unprocessed_cmd_line_arguments.empty?
|
|
348
|
-
|
|
349
|
-
|
|
350
|
+
if multiple
|
|
351
|
+
index = @unprocessed_cmd_line_arguments.index(SpecialValues::EOA)
|
|
352
|
+
if index.nil?
|
|
353
|
+
values = @unprocessed_cmd_line_arguments.shift(@unprocessed_cmd_line_arguments.length)
|
|
354
|
+
else
|
|
355
|
+
values = @unprocessed_cmd_line_arguments.shift(index)
|
|
356
|
+
@unprocessed_cmd_line_arguments.shift # remove EOA
|
|
357
|
+
end
|
|
358
|
+
else
|
|
359
|
+
values = [@unprocessed_cmd_line_arguments.shift]
|
|
360
|
+
end
|
|
350
361
|
values = values.map{ |v| ExtendedValue.instance.evaluate(v, context: "argument: #{descr}", allowed: validation)}
|
|
351
|
-
#
|
|
362
|
+
# If expecting list and only one arg of type array : it is the list
|
|
352
363
|
values = values.first if multiple && values.length.eql?(1) && values.first.is_a?(Array)
|
|
353
364
|
if accept_list
|
|
354
365
|
allowed_values = [].concat(accept_list)
|
|
@@ -457,21 +468,17 @@ module Aspera
|
|
|
457
468
|
# @return [Hash] options as taken from config file and command line just before command execution
|
|
458
469
|
def unprocessed_options_with_value
|
|
459
470
|
result = {}
|
|
460
|
-
@initial_cli_options.each do |
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
@unprocessed_cmd_line_options.delete(option_value)
|
|
472
|
-
else
|
|
473
|
-
raise Cli::BadArgument, "wrong option format: #{option_value}"
|
|
474
|
-
end
|
|
471
|
+
@initial_cli_options.each do |option_argument|
|
|
472
|
+
# ignore short options
|
|
473
|
+
next unless option_argument.start_with?(OPTION_PREFIX)
|
|
474
|
+
name, value = option_argument[OPTION_PREFIX.length..-1].split(OPTION_VALUE_SEPARATOR, 2)
|
|
475
|
+
# ignore options without value
|
|
476
|
+
next if value.nil?
|
|
477
|
+
Log.log.debug{"option #{name}=#{value}"}
|
|
478
|
+
path = name.split(DotContainer::SEPARATOR)
|
|
479
|
+
path[0] = self.class.option_line_to_name(path[0])
|
|
480
|
+
DotContainer.dotted_to_container(path, smart_convert(value), result)
|
|
481
|
+
@unprocessed_cmd_line_options.delete(option_argument)
|
|
475
482
|
end
|
|
476
483
|
return result
|
|
477
484
|
end
|
|
@@ -507,20 +514,25 @@ module Aspera
|
|
|
507
514
|
rescue OptionParser::InvalidOption => e
|
|
508
515
|
Log.log.trace1{"InvalidOption #{e}".red}
|
|
509
516
|
# An option like --a.b.c=d does: a={"b":{"c":ext_val(d)}}
|
|
510
|
-
if
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
517
|
+
if e.args.first.start_with?(OPTION_PREFIX)
|
|
518
|
+
name, value = e.args.first[OPTION_PREFIX.length..-1].split(OPTION_VALUE_SEPARATOR, 2)
|
|
519
|
+
if !value.nil?
|
|
520
|
+
path = name.split(DotContainer::SEPARATOR)
|
|
521
|
+
option_sym = self.class.option_line_to_name(path.shift).to_sym
|
|
522
|
+
if @declared_options.key?(option_sym)
|
|
523
|
+
# it's a known option, so let's process it
|
|
524
|
+
set_option(option_sym, DotContainer.dotted_to_container(path, smart_convert(value), get_option(option_sym)), where: 'dotted')
|
|
525
|
+
# resume to next
|
|
526
|
+
retry
|
|
527
|
+
end
|
|
516
528
|
end
|
|
517
529
|
end
|
|
518
|
-
#
|
|
530
|
+
# Save for later processing
|
|
519
531
|
unknown_options.push(e.args.first)
|
|
520
532
|
retry
|
|
521
533
|
end
|
|
522
534
|
Log.log.trace1{"remains: #{unknown_options}"}
|
|
523
|
-
#
|
|
535
|
+
# Set unprocessed options for next time
|
|
524
536
|
@unprocessed_cmd_line_options = unknown_options
|
|
525
537
|
end
|
|
526
538
|
|
|
@@ -587,9 +599,9 @@ module Aspera
|
|
|
587
599
|
ExtendedValue.assert_no_value(arg, :p)
|
|
588
600
|
result = nil
|
|
589
601
|
get_next_argument(:args, multiple: true).each do |arg|
|
|
590
|
-
Aspera.assert(arg.include?(OPTION_VALUE_SEPARATOR)){"Positional argument: #{arg} does not
|
|
602
|
+
Aspera.assert(arg.include?(OPTION_VALUE_SEPARATOR)){"Positional argument: #{arg} does not include #{OPTION_VALUE_SEPARATOR}"}
|
|
591
603
|
path, value = arg.split(OPTION_VALUE_SEPARATOR, 2)
|
|
592
|
-
result = DotContainer.dotted_to_container(path, smart_convert(value), result)
|
|
604
|
+
result = DotContainer.dotted_to_container(path.split(DotContainer::SEPARATOR), smart_convert(value), result)
|
|
593
605
|
end
|
|
594
606
|
result
|
|
595
607
|
end
|
|
@@ -233,8 +233,7 @@ module Aspera
|
|
|
233
233
|
defaults: {workspace: nil},
|
|
234
234
|
scope: @scope,
|
|
235
235
|
subpath: aoc_base_path,
|
|
236
|
-
secret_finder: config
|
|
237
|
-
progress_disp: formatter
|
|
236
|
+
secret_finder: config
|
|
238
237
|
))
|
|
239
238
|
end
|
|
240
239
|
|
|
@@ -256,7 +255,10 @@ module Aspera
|
|
|
256
255
|
# @param hash [Hash,nil] Optional base `Hash` (modified)
|
|
257
256
|
# @param string [Boolean] `true` to set key as `String`, else as `Symbol`
|
|
258
257
|
# @param name [Boolean] Include name
|
|
259
|
-
# @return [Hash
|
|
258
|
+
# @return [Hash{Symbol, String => String}] the modified hash containing:
|
|
259
|
+
# * `workspace_id` [String] the unique identifier.
|
|
260
|
+
# * `workspace_name` [String] (optional) the name, included if +name+ is true.
|
|
261
|
+
# @note The key type (String or Symbol) depends on the +string+ parameter.
|
|
260
262
|
def workspace_id_hash(hash = nil, string: false, name: false)
|
|
261
263
|
info = aoc_api.workspace
|
|
262
264
|
hash = {} if hash.nil?
|
|
@@ -742,7 +744,7 @@ module Aspera
|
|
|
742
744
|
# Short link entity: `short_links` have:
|
|
743
745
|
# - a numerical id, e.g. `764412`
|
|
744
746
|
# - a resource type, e.g. `UrlToken`
|
|
745
|
-
# - a
|
|
747
|
+
# - a resource id, e.g. `scQ7uXPbvQ`
|
|
746
748
|
# - a short URL path, e.g. `dxyRpT9`
|
|
747
749
|
# @param shared_data [Hash] Information for shared data: dropbox_id+name or file_id+node_id
|
|
748
750
|
# @param &perm_block [Proc] Optional: create/modify/delete permissions on node
|
|
@@ -885,7 +887,7 @@ module Aspera
|
|
|
885
887
|
|
|
886
888
|
def reject_packages_from_persistency(all_packages, skip_ids_persistency)
|
|
887
889
|
return if skip_ids_persistency.nil?
|
|
888
|
-
skip_package = skip_ids_persistency.data.
|
|
890
|
+
skip_package = skip_ids_persistency.data.to_h{ |i| [i, true]}
|
|
889
891
|
all_packages.reject!{ |pkg| skip_package[pkg['id']]}
|
|
890
892
|
end
|
|
891
893
|
|
|
@@ -14,10 +14,6 @@ module Aspera
|
|
|
14
14
|
INSTANCE_OPS = %i[modify delete show].freeze
|
|
15
15
|
# All standard operations (create list modify delete show)
|
|
16
16
|
ALL_OPS = (GLOBAL_OPS + INSTANCE_OPS).freeze
|
|
17
|
-
# Special query parameter: `max`: max number of items for list command
|
|
18
|
-
MAX_ITEMS = 'max'
|
|
19
|
-
# Special query parameter: `pmax`: max number of pages for list command
|
|
20
|
-
MAX_PAGES = 'pmax'
|
|
21
17
|
|
|
22
18
|
class << self
|
|
23
19
|
def declare_options(options)
|
|
@@ -138,7 +134,6 @@ module Aspera
|
|
|
138
134
|
# @param delete_style [String] If set, the delete operation by array in payload
|
|
139
135
|
# @param id_as_arg [String] If set, the id is provided as url argument ?<id_as_arg>=<id>
|
|
140
136
|
# @param is_singleton [Boolean] If `true`, entity is the full path to the resource
|
|
141
|
-
# @param tclo [Boolean] If `true`, :list use paging with total_count, limit, offset
|
|
142
137
|
# @param block [Proc] Block to search for identifier based on attribute value
|
|
143
138
|
# @return [Hash] Result suitable for CLI result
|
|
144
139
|
def entity_execute(
|
|
@@ -151,7 +146,6 @@ module Aspera
|
|
|
151
146
|
id_as_arg: false,
|
|
152
147
|
is_singleton: false,
|
|
153
148
|
list_query: nil,
|
|
154
|
-
tclo: false,
|
|
155
149
|
&block
|
|
156
150
|
)
|
|
157
151
|
command = options.get_next_command(ALL_OPS) if command.nil?
|
|
@@ -189,10 +183,6 @@ module Aspera
|
|
|
189
183
|
when :show
|
|
190
184
|
return Main.result_single_object(api.read(one_res_path), fields: display_fields)
|
|
191
185
|
when :list
|
|
192
|
-
if tclo
|
|
193
|
-
data, total = list_entities_limit_offset_total_count(api: api, entity:, items_key: items_key, query: query_read_delete(default: list_query))
|
|
194
|
-
return Main.result_object_list(data, total: total, fields: display_fields)
|
|
195
|
-
end
|
|
196
186
|
data, http = api.read(entity, query_read_delete, ret: :both)
|
|
197
187
|
return Main.result_empty if http.code == '204'
|
|
198
188
|
# TODO: not generic : which application is this for ?
|
|
@@ -259,86 +249,9 @@ module Aspera
|
|
|
259
249
|
return value
|
|
260
250
|
end
|
|
261
251
|
|
|
262
|
-
# Get a (full or partial) list of all entities of a given type with query: offset/limit
|
|
263
|
-
# @param api [Rest] API object
|
|
264
|
-
# @param entity [String,Symbol] API endpoint of entity to list
|
|
265
|
-
# @param items_key [String] Key in the result to get the list of items (Default: same as `entity`)
|
|
266
|
-
# @param query [Hash,nil] Additional query parameters
|
|
267
|
-
# @return [Array<(Array<Hash>, Integer)>] items, total_count
|
|
268
|
-
def list_entities_limit_offset_total_count(
|
|
269
|
-
api:,
|
|
270
|
-
entity:,
|
|
271
|
-
items_key: nil,
|
|
272
|
-
query: nil
|
|
273
|
-
)
|
|
274
|
-
entity = entity.to_s if entity.is_a?(Symbol)
|
|
275
|
-
items_key = entity.split('/').last if items_key.nil?
|
|
276
|
-
query = {} if query.nil?
|
|
277
|
-
Aspera.assert_type(entity, String)
|
|
278
|
-
Aspera.assert_type(items_key, String)
|
|
279
|
-
Aspera.assert_type(query, Hash)
|
|
280
|
-
Log.log.debug{"list_entities t=#{entity} k=#{items_key} q=#{query}"}
|
|
281
|
-
result = []
|
|
282
|
-
offset = 0
|
|
283
|
-
max_items = query.delete(MAX_ITEMS)
|
|
284
|
-
remain_pages = query.delete(MAX_PAGES)
|
|
285
|
-
# Merge default parameters, by default 100 per page
|
|
286
|
-
query = {'limit'=> PER_PAGE_DEFAULT}.merge(query)
|
|
287
|
-
total_count = nil
|
|
288
|
-
loop do
|
|
289
|
-
query['offset'] = offset
|
|
290
|
-
page_result = api.read(entity, query)
|
|
291
|
-
Aspera.assert_type(page_result[items_key], Array)
|
|
292
|
-
result.concat(page_result[items_key])
|
|
293
|
-
# Reach the limit set by user ?
|
|
294
|
-
if !max_items.nil? && (result.length >= max_items)
|
|
295
|
-
result = result.slice(0, max_items)
|
|
296
|
-
break
|
|
297
|
-
end
|
|
298
|
-
total_count ||= page_result['total_count']
|
|
299
|
-
break if result.length >= total_count
|
|
300
|
-
remain_pages -= 1 unless remain_pages.nil?
|
|
301
|
-
break if remain_pages == 0
|
|
302
|
-
offset += page_result[items_key].length
|
|
303
|
-
formatter.long_operation_running
|
|
304
|
-
end
|
|
305
|
-
formatter.long_operation_terminated
|
|
306
|
-
return result, total_count
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
# Lookup an entity id from its name.
|
|
310
|
-
# Uses query `q` if `query` is `:default` and `field` is `name`.
|
|
311
|
-
# @param entity [String] Type of entity to lookup, by default it is the path, and it is also the field name in result
|
|
312
|
-
# @param value [String] Value to lookup
|
|
313
|
-
# @param field [String] Field to match, by default it is `'name'`
|
|
314
|
-
# @param items_key [String] Key in the result to get the list of items (override entity)
|
|
315
|
-
# @param query [Hash] Additional query parameters (Default: `:default`)
|
|
316
|
-
def lookup_entity_by_field(api:, entity:, value:, field: 'name', items_key: nil, query: :default)
|
|
317
|
-
if query.eql?(:default)
|
|
318
|
-
Aspera.assert(field.eql?('name')){'Default query is on name only'}
|
|
319
|
-
query = {'q'=> value}
|
|
320
|
-
end
|
|
321
|
-
lookup_entity_generic(entity: entity, field: field, value: value){list_entities_limit_offset_total_count(api: api, entity: entity, items_key: items_key, query: query).first}
|
|
322
|
-
end
|
|
323
|
-
|
|
324
|
-
# Lookup entity by field and value. Extract single result from list of result returned by block.
|
|
325
|
-
# @param entity [String] Type of entity to lookup, by default it is the path, and it is also the field name in result
|
|
326
|
-
# @param value [String] Value to lookup
|
|
327
|
-
# @param field [String] Field to match, by default it is `'name'`
|
|
328
|
-
# @param block [Proc] Get list of entity matching query.
|
|
329
|
-
def lookup_entity_generic(entity:, value:, field: 'name', &block)
|
|
330
|
-
Aspera.assert(block_given?)
|
|
331
|
-
found = yield
|
|
332
|
-
Aspera.assert_array_all(found, Hash)
|
|
333
|
-
found = found.select{ |i| i[field].eql?(value)}
|
|
334
|
-
return found.first if found.length.eql?(1)
|
|
335
|
-
raise Cli::BadIdentifier.new(entity, value, field: field, count: found.length)
|
|
336
|
-
end
|
|
337
|
-
|
|
338
|
-
PER_PAGE_DEFAULT = 1000
|
|
339
252
|
# Percent selector: select by this field for this value
|
|
340
253
|
REGEX_LOOKUP_ID_BY_FIELD = /^%([^:]+):(.*)$/
|
|
341
|
-
private_constant :
|
|
254
|
+
private_constant :REGEX_LOOKUP_ID_BY_FIELD
|
|
342
255
|
end
|
|
343
256
|
end
|
|
344
257
|
end
|
|
@@ -191,7 +191,8 @@ module Aspera
|
|
|
191
191
|
def setup_rest_and_transfer_runtime
|
|
192
192
|
RestParameters.instance.user_agent = Info::CMD_NAME
|
|
193
193
|
RestParameters.instance.progress_bar = @progress_bar
|
|
194
|
-
RestParameters.instance.session_cb =
|
|
194
|
+
RestParameters.instance.session_cb = ->(http_session){update_http_session(http_session)}
|
|
195
|
+
RestParameters.instance.spinner_cb = ->(title = nil, action: :spin){formatter.long_operation(title, action: action)}
|
|
195
196
|
# Check http options that are global
|
|
196
197
|
keys_to_delete = []
|
|
197
198
|
@option_http_options.each do |k, v|
|
|
@@ -9,6 +9,7 @@ require 'aspera/cli/transfer_agent'
|
|
|
9
9
|
require 'aspera/transfer/uri'
|
|
10
10
|
require 'aspera/transfer/spec'
|
|
11
11
|
require 'aspera/persistency_action_once'
|
|
12
|
+
require 'aspera/rest_list'
|
|
12
13
|
require 'aspera/environment'
|
|
13
14
|
require 'aspera/nagios'
|
|
14
15
|
require 'aspera/id_generator'
|
|
@@ -31,7 +32,7 @@ module Aspera
|
|
|
31
32
|
# allowed parameters for inbox.atom
|
|
32
33
|
ATOM_PARAMS = %w[page count startIndex].freeze
|
|
33
34
|
# with special parameters (from Plugin class) : max and pmax (from Plugin)
|
|
34
|
-
ATOM_EXT_PARAMS = [MAX_ITEMS, MAX_PAGES].concat(ATOM_PARAMS).freeze
|
|
35
|
+
ATOM_EXT_PARAMS = [RestList::MAX_ITEMS, RestList::MAX_PAGES].concat(ATOM_PARAMS).freeze
|
|
35
36
|
# sub path in url for public link delivery
|
|
36
37
|
PUB_LINK_EXTERNAL_MATCH = 'external_deliveries/'
|
|
37
38
|
STANDARD_PATH = '/aspera/faspex'
|
|
@@ -176,10 +177,9 @@ module Aspera
|
|
|
176
177
|
Aspera.assert_type(mailbox_query, Hash){'query'}
|
|
177
178
|
Aspera.assert((mailbox_query.keys - ATOM_EXT_PARAMS).empty?){"query: supported params: #{ATOM_EXT_PARAMS}"}
|
|
178
179
|
Aspera.assert(!(mailbox_query.key?('startIndex') && mailbox_query.key?('page'))){'query: startIndex and page are exclusive'}
|
|
179
|
-
|
|
180
|
-
mailbox_query.delete(MAX_ITEMS)
|
|
181
|
-
max_pages = mailbox_query
|
|
182
|
-
mailbox_query.delete(MAX_PAGES)
|
|
180
|
+
# Extract pagination control parameters (not part of API query)
|
|
181
|
+
max_items = mailbox_query.delete(RestList::MAX_ITEMS)
|
|
182
|
+
max_pages = mailbox_query.delete(RestList::MAX_PAGES)
|
|
183
183
|
end
|
|
184
184
|
loop do
|
|
185
185
|
# get a batch of package information
|
|
@@ -211,7 +211,7 @@ module Aspera
|
|
|
211
211
|
end
|
|
212
212
|
# add special key
|
|
213
213
|
package['items'] = package['link'].is_a?(Array) ? package['link'].length : 0
|
|
214
|
-
package['metadata'] = package['metadata']['field'].
|
|
214
|
+
package['metadata'] = package['metadata']['field'].to_h{ |i| [i['name'], i['content']]}
|
|
215
215
|
# if we look for a specific package
|
|
216
216
|
stop_condition = true if !stop_at_id.nil? && stop_at_id.eql?(package[PACKAGE_MATCH_FIELD])
|
|
217
217
|
# keep only those for the specified recipient
|