aspera-cli 4.25.4 → 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 +22 -6
- data/CONTRIBUTING.md +31 -22
- 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 +10 -6
- data/lib/aspera/api/ats.rb +1 -1
- data/lib/aspera/api/cos_node.rb +1 -0
- data/lib/aspera/api/faspex.rb +17 -19
- data/lib/aspera/api/httpgw.rb +19 -3
- data/lib/aspera/api/node.rb +43 -2
- data/lib/aspera/ascp/installation.rb +9 -10
- data/lib/aspera/cli/error.rb +8 -0
- data/lib/aspera/cli/info.rb +2 -1
- data/lib/aspera/cli/main.rb +30 -12
- data/lib/aspera/cli/manager.rb +31 -28
- data/lib/aspera/cli/plugins/aoc.rb +2 -2
- data/lib/aspera/cli/plugins/base.rb +1 -88
- data/lib/aspera/cli/plugins/faspex.rb +6 -6
- data/lib/aspera/cli/plugins/faspex5.rb +41 -53
- data/lib/aspera/cli/plugins/node.rb +26 -68
- data/lib/aspera/cli/plugins/shares.rb +4 -2
- data/lib/aspera/cli/transfer_agent.rb +3 -0
- data/lib/aspera/cli/version.rb +1 -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 +8 -36
- 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/yaml.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +4 -3
- metadata.gz.sig +0 -0
data/lib/aspera/api/node.rb
CHANGED
|
@@ -14,7 +14,8 @@ require 'net/ssh/buffer'
|
|
|
14
14
|
|
|
15
15
|
module Aspera
|
|
16
16
|
module Api
|
|
17
|
-
#
|
|
17
|
+
# Aspera Node API client
|
|
18
|
+
# with gen4 extensions (access keys)
|
|
18
19
|
class Node < Rest
|
|
19
20
|
# Format of node scope : node.<access key>:<scope>
|
|
20
21
|
module Scope
|
|
@@ -508,7 +509,7 @@ module Aspera
|
|
|
508
509
|
# Get the transfer user from info on access key
|
|
509
510
|
transfer_spec['remote_user'] = info['transfer_user'] if info['transfer_user']
|
|
510
511
|
# Get settings from name.value array to hash key.value
|
|
511
|
-
settings = info['settings']&.
|
|
512
|
+
settings = info['settings']&.to_h{ |i| [i['name'], i['value']]}
|
|
512
513
|
# Check WSS ports
|
|
513
514
|
Transfer::Spec::WSS_FIELDS.each do |i|
|
|
514
515
|
transfer_spec[i] = settings[i] if settings.key?(i)
|
|
@@ -520,6 +521,46 @@ module Aspera
|
|
|
520
521
|
return transfer_spec
|
|
521
522
|
end
|
|
522
523
|
|
|
524
|
+
# Executes `GET` call in loop using `iteration_token` (`/ops/transfers`)
|
|
525
|
+
# @param iteration [Array] a single element array with the iteration token or nil
|
|
526
|
+
# @param call_args [Hash] additional arguments to pass to `Rest.call`
|
|
527
|
+
# @return [Array] list of items returned by the API call
|
|
528
|
+
def read_with_paging(subpath, query = nil, iteration: nil, **call_args)
|
|
529
|
+
Aspera.assert_type(iteration, Array, NilClass){'iteration'}
|
|
530
|
+
Aspera.assert_type(query, Hash, NilClass){'query'}
|
|
531
|
+
Aspera.assert(!call_args.key?(:query))
|
|
532
|
+
query = {} if query.nil?
|
|
533
|
+
query[:iteration_token] = iteration[0] unless iteration.nil?
|
|
534
|
+
max = query.delete(RestList::MAX_ITEMS)
|
|
535
|
+
item_list = []
|
|
536
|
+
loop do
|
|
537
|
+
data, http = read(subpath, query, **call_args, ret: :both)
|
|
538
|
+
Aspera.assert_type(data, Array){"Expected data to be an Array, got: #{data.class}"}
|
|
539
|
+
# no data
|
|
540
|
+
break if data.empty?
|
|
541
|
+
# get next iteration token from link
|
|
542
|
+
next_iteration_token = nil
|
|
543
|
+
link_info = http['Link']
|
|
544
|
+
unless link_info.nil?
|
|
545
|
+
m = link_info.match(/<([^>]+)>/)
|
|
546
|
+
Aspera.assert(m){"Cannot parse iteration in Link: #{link_info}"}
|
|
547
|
+
next_iteration_token = Rest.query_to_h(URI.parse(m[1]).query)['iteration_token']
|
|
548
|
+
end
|
|
549
|
+
# same as last iteration: stop
|
|
550
|
+
break if next_iteration_token&.eql?(query[:iteration_token])
|
|
551
|
+
query[:iteration_token] = next_iteration_token
|
|
552
|
+
item_list.concat(data)
|
|
553
|
+
if max&.<=(item_list.length)
|
|
554
|
+
item_list = item_list.slice(0, max)
|
|
555
|
+
break
|
|
556
|
+
end
|
|
557
|
+
break if next_iteration_token.nil?
|
|
558
|
+
end
|
|
559
|
+
# save iteration token if needed
|
|
560
|
+
iteration[0] = query[:iteration_token] unless iteration.nil?
|
|
561
|
+
item_list
|
|
562
|
+
end
|
|
563
|
+
|
|
523
564
|
private
|
|
524
565
|
|
|
525
566
|
# Method called in loop for each entry for `resolve_api_fid`
|
|
@@ -83,16 +83,15 @@ module Aspera
|
|
|
83
83
|
|
|
84
84
|
# @return [Hash] with key = file name (String), and value = path to file
|
|
85
85
|
def file_paths
|
|
86
|
-
return SDK_FILES.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
end
|
|
86
|
+
return SDK_FILES.to_h do |v|
|
|
87
|
+
[v.to_s, begin
|
|
88
|
+
path(v)
|
|
89
|
+
rescue Errno::ENOENT => e
|
|
90
|
+
e.message.gsub(/.*assertion failed: /, '').gsub(/\): .*/, ')')
|
|
91
|
+
rescue => e
|
|
92
|
+
e.message
|
|
93
|
+
end]
|
|
94
|
+
end
|
|
96
95
|
end
|
|
97
96
|
|
|
98
97
|
# TODO: if using another product than SDK, should use files from there
|
data/lib/aspera/cli/error.rb
CHANGED
|
@@ -9,7 +9,15 @@ module Aspera
|
|
|
9
9
|
class MissingArgument < Error; end
|
|
10
10
|
class NoSuchElement < Error; end
|
|
11
11
|
|
|
12
|
+
# Raised when a lookup for a specific entity fails to return exactly one result.
|
|
13
|
+
#
|
|
14
|
+
# Provides a formatted message indicating whether the entity was missing
|
|
15
|
+
# or if multiple matches were found (ambiguity).
|
|
12
16
|
class BadIdentifier < Error
|
|
17
|
+
# @param res_type [String] The type of entity being looked up (e.g., 'user').
|
|
18
|
+
# @param res_id [String] The value of the identifier that failed.
|
|
19
|
+
# @param field [String] The name of the field used for lookup (defaults to 'identifier').
|
|
20
|
+
# @param count [Integer] The number of matches found (0 for not found, >1 for ambiguous).
|
|
13
21
|
def initialize(res_type, res_id, field: 'identifier', count: 0)
|
|
14
22
|
msg = count.eql?(0) ? 'not found' : "found #{count}"
|
|
15
23
|
super("#{res_type} with #{field}=#{res_id}: #{msg}")
|
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
|
|
@@ -466,21 +468,17 @@ module Aspera
|
|
|
466
468
|
# @return [Hash] options as taken from config file and command line just before command execution
|
|
467
469
|
def unprocessed_options_with_value
|
|
468
470
|
result = {}
|
|
469
|
-
@initial_cli_options.each do |
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
@unprocessed_cmd_line_options.delete(option_value)
|
|
481
|
-
else
|
|
482
|
-
raise Cli::BadArgument, "wrong option format: #{option_value}"
|
|
483
|
-
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)
|
|
484
482
|
end
|
|
485
483
|
return result
|
|
486
484
|
end
|
|
@@ -516,20 +514,25 @@ module Aspera
|
|
|
516
514
|
rescue OptionParser::InvalidOption => e
|
|
517
515
|
Log.log.trace1{"InvalidOption #{e}".red}
|
|
518
516
|
# An option like --a.b.c=d does: a={"b":{"c":ext_val(d)}}
|
|
519
|
-
if
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
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
|
|
525
528
|
end
|
|
526
529
|
end
|
|
527
|
-
#
|
|
530
|
+
# Save for later processing
|
|
528
531
|
unknown_options.push(e.args.first)
|
|
529
532
|
retry
|
|
530
533
|
end
|
|
531
534
|
Log.log.trace1{"remains: #{unknown_options}"}
|
|
532
|
-
#
|
|
535
|
+
# Set unprocessed options for next time
|
|
533
536
|
@unprocessed_cmd_line_options = unknown_options
|
|
534
537
|
end
|
|
535
538
|
|
|
@@ -596,9 +599,9 @@ module Aspera
|
|
|
596
599
|
ExtendedValue.assert_no_value(arg, :p)
|
|
597
600
|
result = nil
|
|
598
601
|
get_next_argument(:args, multiple: true).each do |arg|
|
|
599
|
-
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}"}
|
|
600
603
|
path, value = arg.split(OPTION_VALUE_SEPARATOR, 2)
|
|
601
|
-
result = DotContainer.dotted_to_container(path, smart_convert(value), result)
|
|
604
|
+
result = DotContainer.dotted_to_container(path.split(DotContainer::SEPARATOR), smart_convert(value), result)
|
|
602
605
|
end
|
|
603
606
|
result
|
|
604
607
|
end
|
|
@@ -744,7 +744,7 @@ module Aspera
|
|
|
744
744
|
# Short link entity: `short_links` have:
|
|
745
745
|
# - a numerical id, e.g. `764412`
|
|
746
746
|
# - a resource type, e.g. `UrlToken`
|
|
747
|
-
# - a
|
|
747
|
+
# - a resource id, e.g. `scQ7uXPbvQ`
|
|
748
748
|
# - a short URL path, e.g. `dxyRpT9`
|
|
749
749
|
# @param shared_data [Hash] Information for shared data: dropbox_id+name or file_id+node_id
|
|
750
750
|
# @param &perm_block [Proc] Optional: create/modify/delete permissions on node
|
|
@@ -887,7 +887,7 @@ module Aspera
|
|
|
887
887
|
|
|
888
888
|
def reject_packages_from_persistency(all_packages, skip_ids_persistency)
|
|
889
889
|
return if skip_ids_persistency.nil?
|
|
890
|
-
skip_package = skip_ids_persistency.data.
|
|
890
|
+
skip_package = skip_ids_persistency.data.to_h{ |i| [i, true]}
|
|
891
891
|
all_packages.reject!{ |pkg| skip_package[pkg['id']]}
|
|
892
892
|
end
|
|
893
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
|
-
RestParameters.instance.spinner_cb.call("#{result.length} / #{total_count || '?'}")
|
|
304
|
-
end
|
|
305
|
-
RestParameters.instance.spinner_cb.call(action: :success)
|
|
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
|
|
@@ -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
|