aspera-cli 4.27.0 → 4.27.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 -0
- data/CONTRIBUTING.md +1 -7
- data/TODO.md +50 -0
- data/docs/README.md +708 -488
- data/lib/aspera/agent/base.rb +10 -2
- data/lib/aspera/agent/connect.rb +7 -7
- data/lib/aspera/agent/desktop.rb +3 -3
- data/lib/aspera/agent/direct.rb +48 -19
- data/lib/aspera/agent/factory.rb +3 -3
- data/lib/aspera/agent/httpgw.rb +55 -14
- data/lib/aspera/agent/node.rb +2 -2
- data/lib/aspera/agent/transferd.rb +12 -12
- data/lib/aspera/api/aoc.rb +28 -28
- data/lib/aspera/api/cos_node.rb +10 -9
- data/lib/aspera/api/faspex.rb +2 -2
- data/lib/aspera/api/httpgw.rb +20 -20
- data/lib/aspera/api/node.rb +31 -53
- data/lib/aspera/ascmd.rb +13 -13
- data/lib/aspera/ascp/installation.rb +43 -36
- data/lib/aspera/ascp/management.rb +6 -6
- data/lib/aspera/assert.rb +12 -13
- data/lib/aspera/cli/ascp_actions.rb +7 -7
- data/lib/aspera/cli/async_transfer_store.rb +49 -5
- data/lib/aspera/cli/bootstrapper.rb +9 -9
- data/lib/aspera/cli/command_registry.rb +1 -1
- data/lib/aspera/cli/command_spec.rb +6 -3
- data/lib/aspera/cli/context.rb +3 -3
- data/lib/aspera/cli/extended_value.rb +26 -26
- data/lib/aspera/cli/formatter.rb +76 -54
- data/lib/aspera/cli/gem_checker.rb +1 -1
- data/lib/aspera/cli/hints.rb +2 -2
- data/lib/aspera/cli/http.rb +22 -29
- data/lib/aspera/cli/mailer.rb +7 -7
- data/lib/aspera/cli/mcp_tool.rb +31 -4
- data/lib/aspera/cli/option_declarator.rb +77 -0
- data/lib/aspera/cli/options.schema.yaml +273 -3
- data/lib/aspera/cli/parser.rb +539 -280
- data/lib/aspera/cli/plugins/alee.rb +1 -1
- data/lib/aspera/cli/plugins/aoc.rb +297 -313
- data/lib/aspera/cli/plugins/ats.rb +25 -21
- data/lib/aspera/cli/plugins/base.rb +357 -186
- data/lib/aspera/cli/plugins/basic_auth.rb +0 -12
- data/lib/aspera/cli/plugins/config.rb +76 -73
- data/lib/aspera/cli/plugins/console.rb +8 -8
- data/lib/aspera/cli/plugins/cos.rb +4 -3
- data/lib/aspera/cli/plugins/factory.rb +2 -2
- data/lib/aspera/cli/plugins/faspex5.rb +259 -248
- data/lib/aspera/cli/plugins/faspio.rb +7 -2
- data/lib/aspera/cli/plugins/httpgw.rb +1 -1
- data/lib/aspera/cli/plugins/mcp.rb +11 -11
- data/lib/aspera/cli/plugins/node.rb +227 -283
- data/lib/aspera/cli/plugins/orchestrator.rb +23 -23
- data/lib/aspera/cli/plugins/preview.rb +61 -64
- data/lib/aspera/cli/plugins/server.rb +36 -31
- data/lib/aspera/cli/plugins/shares.rb +174 -135
- data/lib/aspera/cli/preset_actions.rb +39 -19
- data/lib/aspera/cli/preset_manager.rb +74 -32
- data/lib/aspera/cli/result.rb +18 -16
- data/lib/aspera/cli/runner.rb +75 -39
- data/lib/aspera/cli/secret_finder.rb +1 -1
- data/lib/aspera/cli/sync_actions.rb +36 -35
- data/lib/aspera/cli/terminal_formatter.rb +2 -2
- data/lib/aspera/cli/transfer_actions.rb +17 -10
- data/lib/aspera/cli/transfer_agent.rb +44 -44
- data/lib/aspera/cli/transfer_progress.rb +7 -7
- data/lib/aspera/cli/vault_manager.rb +34 -12
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +17 -20
- data/lib/aspera/colors.rb +3 -3
- data/lib/aspera/command_line_builder.rb +16 -16
- data/lib/aspera/command_line_converter.rb +1 -1
- data/lib/aspera/coverage.rb +1 -2
- data/lib/aspera/data_repository.rb +1 -1
- data/lib/aspera/dot_container.rb +8 -8
- data/lib/aspera/environment.rb +12 -12
- data/lib/aspera/faspex_gw.rb +4 -4
- data/lib/aspera/faspex_postproc.rb +14 -9
- data/lib/aspera/graphql.rb +4 -2
- data/lib/aspera/hash_ext.rb +2 -2
- data/lib/aspera/json_rpc/client.rb +1 -1
- data/lib/aspera/keychain/base.rb +10 -3
- data/lib/aspera/keychain/encrypted_hash.rb +92 -17
- data/lib/aspera/keychain/factory.rb +17 -5
- data/lib/aspera/keychain/hashicorp_vault.rb +1 -1
- data/lib/aspera/keychain/macos_security.rb +23 -25
- data/lib/aspera/keychain/one_password_api.rb +86 -0
- data/lib/aspera/keychain/one_password_base.rb +34 -0
- data/lib/aspera/keychain/one_password_cli.rb +98 -0
- data/lib/aspera/link_header.rb +2 -2
- data/lib/aspera/log.rb +13 -13
- data/lib/aspera/markdown.rb +5 -5
- data/lib/aspera/nagios.rb +8 -8
- data/lib/aspera/node_simulator.rb +33 -27
- data/lib/aspera/oauth/base.rb +4 -5
- data/lib/aspera/oauth/boot.rb +3 -3
- data/lib/aspera/oauth/factory.rb +5 -5
- data/lib/aspera/oauth/jwt.rb +5 -7
- data/lib/aspera/oauth/web.rb +4 -4
- data/lib/aspera/persistency_action_once.rb +4 -4
- data/lib/aspera/persistency_folder.rb +8 -8
- data/lib/aspera/preview/file_types.rb +4 -4
- data/lib/aspera/preview/generator.rb +49 -46
- data/lib/aspera/preview/options.rb +3 -2
- data/lib/aspera/preview/terminal.rb +5 -5
- data/lib/aspera/preview/utils.rb +65 -18
- data/lib/aspera/products/connect.rb +2 -2
- data/lib/aspera/products/desktop.rb +1 -1
- data/lib/aspera/products/other.rb +2 -2
- data/lib/aspera/products/transferd.rb +3 -3
- data/lib/aspera/proxy_auto_config.rb +10 -10
- data/lib/aspera/rest.rb +149 -115
- data/lib/aspera/rest_error_analyzer.rb +3 -3
- data/lib/aspera/rest_list.rb +10 -9
- data/lib/aspera/schema/IBM Aspera Node API-4.4.6.yaml +6232 -0
- data/lib/aspera/schema/IBM Aspera faspio Gateway API-1.0.0.yaml +249 -0
- data/lib/aspera/schema/IBM_Aspera_Shares.yaml +4653 -0
- data/lib/aspera/schema/documentation.rb +17 -11
- data/lib/aspera/schema/reader.rb +78 -4
- data/lib/aspera/schema/registry.rb +26 -7
- data/lib/aspera/secret_hider.rb +5 -5
- data/lib/aspera/ssh.rb +64 -31
- data/lib/aspera/ssl.rb +3 -3
- data/lib/aspera/sync/database.rb +2 -2
- data/lib/aspera/sync/operations.rb +13 -13
- data/lib/aspera/temp_file_manager.rb +24 -4
- data/lib/aspera/transfer/faux_file.rb +2 -2
- data/lib/aspera/transfer/parameters.rb +15 -15
- data/lib/aspera/transfer/resumer.rb +9 -9
- data/lib/aspera/transfer/spec.rb +1 -1
- data/lib/aspera/uri_reader.rb +52 -15
- data/lib/aspera/web_auth.rb +6 -5
- data/lib/aspera/web_server_simple.rb +9 -9
- data/lib/aspera/yaml.rb +4 -4
- data.tar.gz.sig +0 -0
- metadata +10 -4
- metadata.gz.sig +0 -0
- data/lib/aspera/cli/plugins/faspex.rb +0 -576
- data/lib/aspera/transfer/uri.rb +0 -56
|
@@ -5,6 +5,7 @@ require 'aspera/cli/parser'
|
|
|
5
5
|
require 'aspera/assert'
|
|
6
6
|
require 'aspera/cli/result'
|
|
7
7
|
require 'aspera/cli/command_registry'
|
|
8
|
+
require 'aspera/cli/option_declarator'
|
|
8
9
|
require 'aspera/schema/registry'
|
|
9
10
|
|
|
10
11
|
module Aspera
|
|
@@ -25,6 +26,16 @@ module Aspera
|
|
|
25
26
|
class << self
|
|
26
27
|
# Per-class DSL registry (not inherited: each subclass gets its own instance).
|
|
27
28
|
# @return [CommandRegistry]
|
|
29
|
+
def used_option_sources
|
|
30
|
+
@used_option_sources ||= []
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Include options from another plugin or OptionDeclarator module.
|
|
34
|
+
# @param source [Class, Module]
|
|
35
|
+
def use_options(source)
|
|
36
|
+
used_option_sources << source unless used_option_sources.include?(source)
|
|
37
|
+
end
|
|
38
|
+
|
|
28
39
|
def command_registry
|
|
29
40
|
@command_registry ||= CommandRegistry.new
|
|
30
41
|
end
|
|
@@ -38,18 +49,70 @@ module Aspera
|
|
|
38
49
|
command_registry.register(CommandSpec.new(id: id, **kwargs))
|
|
39
50
|
end
|
|
40
51
|
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
# Derive a display name from an entity path:
|
|
53
|
+
# last segment after '/', underscores replaced by spaces, first letter capitalized.
|
|
54
|
+
# e.g. 'data/smtp_server' -> 'Smtp server', 'data/transfer_settings' -> 'Transfer settings'
|
|
55
|
+
def entity_display_name(entity)
|
|
56
|
+
entity.to_s.split('/').last.tr('_', ' ').capitalize
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# DSL class method: declare CRUD commands for a REST entity.
|
|
60
|
+
#
|
|
61
|
+
# For each verb in operations:, registers one CommandSpec with:
|
|
62
|
+
# - description: "#{verb.capitalize} #{name}"
|
|
63
|
+
# - arguments: [{name: :id, type: :identifier, lookup: lookup}] for instance verbs
|
|
64
|
+
# (:show, :modify, :delete) when not a singleton; none for global verbs
|
|
65
|
+
# - action: calls entity_<verb>(api:, entity:, **shared_kwargs, **ctx)
|
|
66
|
+
#
|
|
67
|
+
# api: is resolved at runtime: :@ivar -> instance_variable_get, else -> send.
|
|
68
|
+
# entity: may also be a Symbol — resolved at runtime as a ctx key (e.g. :sf_entity).
|
|
69
|
+
# This covers cases where the entity path is injected by a parent setup: method.
|
|
70
|
+
#
|
|
71
|
+
# @param api [Symbol, String] Runtime API ref (:@ivar or method name) or literal string
|
|
72
|
+
# @param entity [String, Symbol] REST sub-path, or ctx key Symbol resolved at runtime
|
|
73
|
+
# @param operations [Array<Symbol>] Verbs to expose; defaults to Operations::ALL
|
|
74
|
+
# @param name [String, nil] Display name; defaults to last segment of entity (static only)
|
|
75
|
+
# @param lookup [Symbol, nil] Instance method for percent-selector resolution
|
|
76
|
+
# @param kwargs [Hash] Shared params forwarded to every per-verb method
|
|
77
|
+
def crud_commands(api:, entity:, operations: nil, name: nil, lookup: nil, **kwargs)
|
|
78
|
+
name ||= entity_display_name(entity) unless entity.is_a?(Symbol)
|
|
79
|
+
operations ||= Operations::ALL
|
|
80
|
+
operations.each do |verb|
|
|
81
|
+
id_arg = ({name: :id, type: :identifier, lookup: lookup} if Operations::INSTANCE.include?(verb) && !kwargs[:is_singleton])
|
|
82
|
+
schema_val =
|
|
83
|
+
if kwargs[:body_component] && entity.is_a?(String)
|
|
84
|
+
case verb
|
|
85
|
+
when :create then Schema::Registry.req_body(kwargs[:body_component], "#{entity}.post")
|
|
86
|
+
when :modify then Schema::Registry.req_body(kwargs[:body_component], "#{entity}/{id}.put")
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
args =
|
|
90
|
+
case verb
|
|
91
|
+
when :create
|
|
92
|
+
[{name: :data, type: Hash, bulk: true, schema: schema_val}]
|
|
93
|
+
when :modify
|
|
94
|
+
[id_arg, {name: :data, type: Hash, schema: schema_val}].compact
|
|
95
|
+
when :delete
|
|
96
|
+
id_arg ? [id_arg.merge(bulk: true)] : nil
|
|
97
|
+
else
|
|
98
|
+
id_arg ? [id_arg] : nil
|
|
99
|
+
end
|
|
100
|
+
action_proc = lambda do |**ctx|
|
|
101
|
+
resolved_api =
|
|
102
|
+
if api.is_a?(Symbol)
|
|
103
|
+
api.to_s.start_with?('@') ? instance_variable_get(api) : send(api)
|
|
104
|
+
elsif api.is_a?(Proc)
|
|
105
|
+
instance_exec(&api)
|
|
106
|
+
else
|
|
107
|
+
api
|
|
108
|
+
end
|
|
109
|
+
resolved_entity = entity.is_a?(Symbol) ? ctx.fetch(entity) : entity
|
|
110
|
+
send(:"entity_#{verb}", api: resolved_api, entity: resolved_entity, **kwargs, **ctx)
|
|
111
|
+
end
|
|
112
|
+
cmd_attrs = {description: "#{verb.capitalize} #{name || entity.inspect}", action: action_proc}
|
|
113
|
+
cmd_attrs[:arguments] = args if args
|
|
114
|
+
command(verb, **cmd_attrs)
|
|
115
|
+
end
|
|
53
116
|
end
|
|
54
117
|
|
|
55
118
|
# DSL class method: define an instance method whose name is derived from a path array.
|
|
@@ -62,11 +125,29 @@ module Aspera
|
|
|
62
125
|
|
|
63
126
|
# DSL class method: scope block that sets a default parent for nested command() calls.
|
|
64
127
|
# Fully re-entrant: blocks may be nested for multi-level parent paths.
|
|
65
|
-
#
|
|
128
|
+
# If the terminal node of `parent` has not been declared yet, it is auto-declared
|
|
129
|
+
# as an intermediate command with description: "Manage <name>" (or the given description:).
|
|
130
|
+
#
|
|
131
|
+
# `parent` is always resolved relative to the current scope:
|
|
132
|
+
# Array(@current_parent) + Array(parent)
|
|
133
|
+
#
|
|
134
|
+
# @param parent [Symbol, Array<Symbol>] one or more path segments, relative to current scope
|
|
135
|
+
# @param description [String, nil] Description of entity for the auto-declared node
|
|
66
136
|
# @yieldreturn [void]
|
|
67
|
-
def commands_under(parent)
|
|
137
|
+
def commands_under(parent, description: nil)
|
|
138
|
+
# Always relative: append the given segments to the current scope.
|
|
139
|
+
path = Array(@current_parent) + Array(parent)
|
|
140
|
+
unless command_registry[path]
|
|
141
|
+
id = path.last
|
|
142
|
+
desc = description || "Manage #{entity_display_name(id)}"
|
|
143
|
+
parent_path = path[0..-2]
|
|
144
|
+
saved = @current_parent
|
|
145
|
+
@current_parent = parent_path.empty? ? nil : parent_path
|
|
146
|
+
command(id, description: desc)
|
|
147
|
+
@current_parent = saved
|
|
148
|
+
end
|
|
68
149
|
previous = @current_parent
|
|
69
|
-
@current_parent =
|
|
150
|
+
@current_parent = path
|
|
70
151
|
yield
|
|
71
152
|
ensure
|
|
72
153
|
@current_parent = previous
|
|
@@ -114,6 +195,47 @@ module Aspera
|
|
|
114
195
|
)
|
|
115
196
|
end
|
|
116
197
|
|
|
198
|
+
# Declare all options registered on this plugin class onto a Parser instance.
|
|
199
|
+
# Walks inherited options and any sources added via `use_options`.
|
|
200
|
+
# @param options [Aspera::Cli::Parser]
|
|
201
|
+
# @param parse [Boolean] whether to call parse_options! after declaring
|
|
202
|
+
def declare_options(options, parse: false)
|
|
203
|
+
sources = []
|
|
204
|
+
ancestors.each do |klass|
|
|
205
|
+
next unless klass.is_a?(Class) && klass <= Base
|
|
206
|
+
sources << klass if klass.instance_variable_defined?(:@command_registry)
|
|
207
|
+
sources.concat(klass.used_option_sources) if klass.respond_to?(:used_option_sources)
|
|
208
|
+
end
|
|
209
|
+
sources.uniq.each do |src|
|
|
210
|
+
specs =
|
|
211
|
+
if src.respond_to?(:command_registry)
|
|
212
|
+
src.command_registry.option_specs
|
|
213
|
+
elsif src.respond_to?(:option_specs)
|
|
214
|
+
src.option_specs
|
|
215
|
+
else
|
|
216
|
+
{}
|
|
217
|
+
end
|
|
218
|
+
specs.each_value do |spec|
|
|
219
|
+
next if options.option_declared?(spec.name)
|
|
220
|
+
resolved_handler =
|
|
221
|
+
case spec.handler
|
|
222
|
+
when Hash then spec.handler
|
|
223
|
+
end
|
|
224
|
+
options.declare(
|
|
225
|
+
spec.name,
|
|
226
|
+
description: spec.description,
|
|
227
|
+
short: spec.short,
|
|
228
|
+
allowed: spec.allowed,
|
|
229
|
+
default: spec.default,
|
|
230
|
+
handler: resolved_handler,
|
|
231
|
+
deprecation: spec.deprecation,
|
|
232
|
+
schema: spec.schema
|
|
233
|
+
)
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
options.parse_options! if parse
|
|
237
|
+
end
|
|
238
|
+
|
|
117
239
|
# DSL class method: declare a setup method to run once before root dispatch.
|
|
118
240
|
# The method is called before any command is consumed, and its return value
|
|
119
241
|
# (a Hash) is merged into the initial ctx. This is useful when conditions
|
|
@@ -135,16 +257,38 @@ module Aspera
|
|
|
135
257
|
@application_name = name unless name.nil?
|
|
136
258
|
@application_name || self.name.split('::').last
|
|
137
259
|
end
|
|
260
|
+
|
|
261
|
+
# Build a filter lambda from a match expression (String glob, Regexp, Proc, or nil).
|
|
262
|
+
# @param match_expression [String, Regexp, Proc, NilClass] as in FILTER_ARGS
|
|
263
|
+
# @return [Proc] lambda(entry) -> Boolean
|
|
264
|
+
def file_matcher(match_expression)
|
|
265
|
+
case match_expression
|
|
266
|
+
when Proc then match_expression
|
|
267
|
+
when Regexp then ->(f) { f['name'].match?(match_expression) }
|
|
268
|
+
when String then ->(f) { File.fnmatch(match_expression, f['name'], File::FNM_DOTMATCH) }
|
|
269
|
+
when NilClass then ->(_) { true }
|
|
270
|
+
else Aspera.error_unexpected_value(match_expression.class.name, type: ParameterError)
|
|
271
|
+
end
|
|
272
|
+
end
|
|
138
273
|
end
|
|
139
274
|
|
|
275
|
+
# Shared positional argument for commands that accept an optional file name filter.
|
|
276
|
+
# Accepted types: String (shell glob matched against entry name), Regexp, or Proc.
|
|
277
|
+
# Used by node files find, and preview scan/events/trevents.
|
|
278
|
+
FILTER_ARGS = [{name: :filter, type: [String, Regexp, Proc], description: 'File name filter: String (glob), Regexp, or Proc', mandatory: false, default: nil}].freeze
|
|
279
|
+
|
|
140
280
|
option :query, description: 'Additional filter for for some commands (list/delete)', allowed: [Hash, Array, NilClass]
|
|
141
|
-
option :bulk, description: 'Bulk operation (only some)', allowed:
|
|
142
|
-
option :bfail, description: 'Bulk operation error handling', allowed:
|
|
281
|
+
option :bulk, description: 'Bulk operation (only some)', allowed: Type::BOOLEAN, default: false
|
|
282
|
+
option :bfail, description: 'Bulk operation error handling', allowed: Type::BOOLEAN, default: true
|
|
143
283
|
|
|
144
284
|
def initialize(context:)
|
|
145
|
-
Aspera.assert_type(context, Context){'context'}
|
|
146
|
-
Aspera.assert_type(context.man_header, TrueClass, FalseClass){'context.man_header'}
|
|
285
|
+
Aspera.assert_type(context, Context) { 'context' }
|
|
286
|
+
Aspera.assert_type(context.man_header, TrueClass, FalseClass) { 'context.man_header' }
|
|
147
287
|
@context = context
|
|
288
|
+
# Switch to the plugin-specific options group so that all options declared
|
|
289
|
+
# below (DSL-registered and imperative) appear under the plugin section in
|
|
290
|
+
# --help output, separate from the global options.
|
|
291
|
+
options.group(self.class.name.split('::').last.downcase) if @context.man_header
|
|
148
292
|
# Auto-declare all options registered via the DSL `option` class method.
|
|
149
293
|
# Walk the ancestor chain so that options declared on parent plugin classes
|
|
150
294
|
# (e.g. Oauth, BasicAuth) are also registered for sub-classes (e.g. Aoc).
|
|
@@ -154,9 +298,22 @@ module Aspera
|
|
|
154
298
|
# handler: shorthand:
|
|
155
299
|
# Symbol handler: {o: self, m: <symbol>} (Category B - plugin instance methods)
|
|
156
300
|
# Hash handler: used as-is (Category A - singletons / class constants)
|
|
301
|
+
sources = []
|
|
157
302
|
self.class.ancestors.each do |klass|
|
|
158
|
-
next unless klass.is_a?(Class) && klass <= Base
|
|
159
|
-
klass.command_registry
|
|
303
|
+
next unless klass.is_a?(Class) && klass <= Base
|
|
304
|
+
sources << klass if klass.instance_variable_defined?(:@command_registry)
|
|
305
|
+
sources.concat(klass.used_option_sources) if klass.respond_to?(:used_option_sources)
|
|
306
|
+
end
|
|
307
|
+
sources.uniq.each do |src|
|
|
308
|
+
specs =
|
|
309
|
+
if src.respond_to?(:command_registry)
|
|
310
|
+
src.command_registry.option_specs
|
|
311
|
+
elsif src.respond_to?(:option_specs)
|
|
312
|
+
src.option_specs
|
|
313
|
+
else
|
|
314
|
+
{}
|
|
315
|
+
end
|
|
316
|
+
specs.each_value do |spec|
|
|
160
317
|
next if options.option_declared?(spec.name)
|
|
161
318
|
resolved_handler =
|
|
162
319
|
case spec.handler
|
|
@@ -175,7 +332,6 @@ module Aspera
|
|
|
175
332
|
)
|
|
176
333
|
end
|
|
177
334
|
end
|
|
178
|
-
add_manual_header if @context.man_header
|
|
179
335
|
end
|
|
180
336
|
|
|
181
337
|
# Global objects
|
|
@@ -202,7 +358,8 @@ module Aspera
|
|
|
202
358
|
def progress_bar; @context.progress_bar; end
|
|
203
359
|
|
|
204
360
|
def add_manual_header(_has_options = true)
|
|
205
|
-
|
|
361
|
+
# No-op: the group is set at the start of initialize.
|
|
362
|
+
# Kept for compatibility with Config, which calls add_manual_header(false) from Runner.
|
|
206
363
|
end
|
|
207
364
|
|
|
208
365
|
# Entry point for all DSL-based plugins.
|
|
@@ -251,11 +408,13 @@ module Aspera
|
|
|
251
408
|
(spec&.arguments || []).each do |arg_spec|
|
|
252
409
|
next if ctx.key?(arg_spec.name)
|
|
253
410
|
if arg_spec.type.eql?(:identifier)
|
|
254
|
-
|
|
255
|
-
res_id = if
|
|
256
|
-
options.instance_identifier(description: arg_spec.name.to_s){ |f, v| send(lookup_method, f, v, **ctx)}
|
|
257
|
-
else
|
|
411
|
+
lookup_cb = arg_spec.lookup
|
|
412
|
+
res_id = if lookup_cb.nil?
|
|
258
413
|
options.instance_identifier(description: arg_spec.name.to_s)
|
|
414
|
+
elsif lookup_cb.is_a?(Symbol)
|
|
415
|
+
options.instance_identifier(description: arg_spec.name.to_s) { |f, v| send(lookup_cb, f, v, **ctx) }
|
|
416
|
+
else
|
|
417
|
+
options.instance_identifier(description: arg_spec.name.to_s) { |f, v| instance_exec(f, v, **ctx, &lookup_cb) }
|
|
259
418
|
end
|
|
260
419
|
ctx = ctx.merge(arg_spec.name => res_id)
|
|
261
420
|
else
|
|
@@ -289,22 +448,36 @@ module Aspera
|
|
|
289
448
|
end
|
|
290
449
|
|
|
291
450
|
# Phase B, child branch: consume the next command argument, resolve the matching
|
|
292
|
-
# child spec, handle delegation
|
|
451
|
+
# child spec, handle delegation, and recurse or execute.
|
|
452
|
+
# --help is intercepted at two points:
|
|
453
|
+
# 1. Before get_next_command when no positional arg is pending: raises HelpRequest
|
|
454
|
+
# immediately so the subcommand list with descriptions is shown rather than a
|
|
455
|
+
# MissingArgument error.
|
|
456
|
+
# 2. After get_next_command when no further args remain: raises HelpRequest scoped
|
|
457
|
+
# to the consumed command (e.g. `aoc files find -h`).
|
|
293
458
|
# @param current_path [Array<Symbol>]
|
|
294
|
-
# @param registry
|
|
295
|
-
# @param ctx
|
|
459
|
+
# @param registry [CommandRegistry]
|
|
460
|
+
# @param ctx [Hash]
|
|
296
461
|
# @return [Object]
|
|
297
462
|
def dispatch_child(current_path, registry, ctx)
|
|
298
463
|
children = registry.children_of(current_path)
|
|
299
|
-
available = children.reject{ |_, c| c.condition && !send(c.condition)}
|
|
464
|
+
available = children.reject { |_, c| c.condition && !send(c.condition) }
|
|
300
465
|
aliases = children.values.each_with_object({}) do |c, h|
|
|
301
|
-
Array(c.aliases).each{ |a| h[a] = c.id} if c.aliases
|
|
466
|
+
Array(c.aliases).each { |a| h[a] = c.id } if c.aliases
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
# Intercept --help before consuming the command token when no arg is pending.
|
|
470
|
+
# This avoids MissingArgument being raised by get_next_command before HelpRequest.
|
|
471
|
+
if options.help_requested && options.command_or_arg_empty?
|
|
472
|
+
@help_path = current_path
|
|
473
|
+
raise Cli::HelpRequest, self
|
|
302
474
|
end
|
|
475
|
+
|
|
303
476
|
command = options.get_next_command(available.keys, aliases: aliases.empty? ? nil : aliases)
|
|
304
477
|
child = available[command]
|
|
305
478
|
|
|
306
|
-
# Intercept --help
|
|
307
|
-
# (e.g. `aoc files
|
|
479
|
+
# Intercept --help after a command was consumed but no further args remain.
|
|
480
|
+
# (e.g. `aoc files find -h`). When further args remain, keep recursing.
|
|
308
481
|
if options.help_requested && options.command_or_arg_empty?
|
|
309
482
|
@help_path = current_path + [command]
|
|
310
483
|
raise Cli::HelpRequest, self
|
|
@@ -317,9 +490,6 @@ module Aspera
|
|
|
317
490
|
end
|
|
318
491
|
return dispatch_from_registry(Array(child.delegates_to), ctx) if child.delegates_to
|
|
319
492
|
|
|
320
|
-
# entity_execute shorthand
|
|
321
|
-
return run_entity_execute(child, ctx) if child.entity_execute
|
|
322
|
-
|
|
323
493
|
# Both intermediate and leaf: instance_arg + setup are handled by Phase A of the next call
|
|
324
494
|
dispatch_from_registry(current_path + [command], ctx)
|
|
325
495
|
end
|
|
@@ -349,23 +519,28 @@ module Aspera
|
|
|
349
519
|
end
|
|
350
520
|
end
|
|
351
521
|
|
|
352
|
-
# Execute a leaf CommandSpec: resolve arguments
|
|
522
|
+
# Execute a leaf CommandSpec: resolve arguments and call action.
|
|
523
|
+
# Arguments already present in +ctx+ (pre-resolved by a parent plugin, e.g. aoc.rb forwarding
|
|
524
|
+
# path: into execute_nodegen4_command) are skipped — the token has already been consumed.
|
|
353
525
|
# instance_arg (if any) is resolved here as an ArgumentSpec(type: :identifier) and merged
|
|
354
526
|
# into ctx, exactly like any other keyword argument received by the action.
|
|
355
527
|
# @param spec [CommandSpec] a leaf node (no children)
|
|
356
|
-
# @param ctx [Hash] accumulated context
|
|
528
|
+
# @param ctx [Hash] accumulated context (pre-resolved keys are not re-consumed)
|
|
357
529
|
# @return [Object]
|
|
358
530
|
def execute_leaf(spec, ctx)
|
|
359
531
|
a = action_for(spec)
|
|
360
532
|
# Always resolve declared arguments (even when transfer_paths is set — those arguments
|
|
361
533
|
# are consumed first; ts_source_paths then reads whatever remains in the queue).
|
|
362
534
|
(spec.arguments || []).each do |arg_spec|
|
|
535
|
+
next if ctx.key?(arg_spec.name)
|
|
363
536
|
if arg_spec.type.eql?(:identifier)
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
537
|
+
lookup_cb = arg_spec.lookup
|
|
538
|
+
block =
|
|
539
|
+
if lookup_cb.nil? then nil
|
|
540
|
+
elsif lookup_cb.is_a?(Symbol) then ->(f, v) { send(lookup_cb, f, v, **ctx) }
|
|
541
|
+
else ->(f, v) { instance_exec(f, v, **ctx, &lookup_cb) }
|
|
542
|
+
end
|
|
543
|
+
ctx = ctx.merge(arg_spec.name => resolve_argument(arg_spec, &block))
|
|
369
544
|
else
|
|
370
545
|
ctx = ctx.merge(arg_spec.name => resolve_argument(arg_spec))
|
|
371
546
|
end
|
|
@@ -373,33 +548,6 @@ module Aspera
|
|
|
373
548
|
invoke_action(a, [], ctx)
|
|
374
549
|
end
|
|
375
550
|
|
|
376
|
-
# Expand an entity_execute shorthand from a CommandSpec.
|
|
377
|
-
# Calls Base#entity_execute with the parameters from spec.entity_execute merged
|
|
378
|
-
# with the context hash (context entries are low-priority: spec params win).
|
|
379
|
-
# @param spec [CommandSpec] the command spec carrying entity_execute: Hash
|
|
380
|
-
# @param ctx [Hash] accumulated context (e.g. api:, lookup block)
|
|
381
|
-
# @return [Object]
|
|
382
|
-
def run_entity_execute(spec, ctx)
|
|
383
|
-
ee_params = spec.entity_execute.dup
|
|
384
|
-
# Resolve api: Symbol at runtime: :@ivar -> instance_variable_get, :method -> send
|
|
385
|
-
if (api_ref = ee_params[:api]).is_a?(Symbol)
|
|
386
|
-
ee_params[:api] = if api_ref.to_s.start_with?('@')
|
|
387
|
-
instance_variable_get(api_ref)
|
|
388
|
-
else
|
|
389
|
-
send(api_ref)
|
|
390
|
-
end
|
|
391
|
-
end
|
|
392
|
-
# Merge context into params (spec wins on key collision)
|
|
393
|
-
merged = ctx.merge(ee_params)
|
|
394
|
-
# Extract lookup_block before passing to entity_execute (it is not a kwarg of entity_execute)
|
|
395
|
-
block = merged.delete(:lookup_block)
|
|
396
|
-
if block
|
|
397
|
-
entity_execute(**merged, &block)
|
|
398
|
-
else
|
|
399
|
-
entity_execute(**merged)
|
|
400
|
-
end
|
|
401
|
-
end
|
|
402
|
-
|
|
403
551
|
# Resolve a single positional argument from the CLI argument stream.
|
|
404
552
|
# When arg_spec.bulk is true, always returns an Array (normalized to [value] when non-bulk).
|
|
405
553
|
# For type: :identifier, an optional block provides the percent-selector lookup.
|
|
@@ -422,7 +570,7 @@ module Aspera
|
|
|
422
570
|
schema: arg_spec.schema
|
|
423
571
|
)
|
|
424
572
|
if is_bulk
|
|
425
|
-
Aspera.assert_array_all(val, arg_spec.type, type: Cli::BadArgument){'type'} unless arg_spec.type.nil?
|
|
573
|
+
Aspera.assert_array_all(val, arg_spec.type, type: Cli::BadArgument) { 'type' } unless arg_spec.type.nil?
|
|
426
574
|
end
|
|
427
575
|
end
|
|
428
576
|
# Always return an Array when bulk: true
|
|
@@ -465,124 +613,147 @@ module Aspera
|
|
|
465
613
|
end
|
|
466
614
|
end
|
|
467
615
|
|
|
468
|
-
#
|
|
469
|
-
#
|
|
470
|
-
#
|
|
471
|
-
#
|
|
472
|
-
#
|
|
473
|
-
#
|
|
474
|
-
# @param
|
|
475
|
-
# @param
|
|
476
|
-
# @param
|
|
477
|
-
# @param
|
|
478
|
-
# @
|
|
479
|
-
# @
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
bfail: true,
|
|
508
|
-
&block
|
|
509
|
-
)
|
|
510
|
-
# Fallback: read command from CLI when not provided (entity_command shorthand without command:)
|
|
511
|
-
command = options.get_next_command(Operations::ALL) if command.nil?
|
|
512
|
-
# Derive query schema path from component + entity when component is given
|
|
616
|
+
# Convenience wrapper: reads :bulk and :bfail from options, normalises +items+
|
|
617
|
+
# to an Array, then delegates to Result.bulk.
|
|
618
|
+
# Use this in action methods instead of the three-line boilerplate:
|
|
619
|
+
# is_bulk = options.get_option(:bulk)
|
|
620
|
+
# items = x.is_a?(Array) ? x : [x]
|
|
621
|
+
# Result.bulk(items, is_bulk: is_bulk, ...)
|
|
622
|
+
# @param items [Object, Array] Single item or Array; wrapped in Array when needed
|
|
623
|
+
# @param command [Symbol] Operation name (:create, :delete, ...)
|
|
624
|
+
# @param id_result [String] Key used as item identifier in the result row
|
|
625
|
+
# @param fields [Object] Fields hint passed to Result constructor (non-bulk only)
|
|
626
|
+
# @yieldparam item [Object] Each item in +items+
|
|
627
|
+
# @return [Result::ObjectList, Result::SingleObject]
|
|
628
|
+
def bulk_result(items, command:, id_result: 'id', fields: :default, &block)
|
|
629
|
+
items = items.is_a?(Array) ? items : [items]
|
|
630
|
+
Result.bulk(
|
|
631
|
+
items,
|
|
632
|
+
is_bulk: options.get_option(:bulk),
|
|
633
|
+
command: command,
|
|
634
|
+
id_result: id_result,
|
|
635
|
+
fields: fields,
|
|
636
|
+
bfail: options.get_option(:bfail),
|
|
637
|
+
&block
|
|
638
|
+
)
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
# --- Per-verb entity action methods ---
|
|
642
|
+
# Each method handles exactly one CRUD verb.
|
|
643
|
+
# The resource id (when needed) is received as `id:` from ctx — it must be
|
|
644
|
+
# resolved upstream via an ArgumentSpec(type: :identifier) on the command,
|
|
645
|
+
# NOT read from the CLI queue inside the method.
|
|
646
|
+
|
|
647
|
+
# List all instances of an entity.
|
|
648
|
+
# @param api [Aspera::Rest] REST API object
|
|
649
|
+
# @param entity [String] API sub-path
|
|
650
|
+
# @param display_fields [Array, nil] Fields to display
|
|
651
|
+
# @param items_key [String, nil] Sub-key in response containing the array
|
|
652
|
+
# @param list_query [Hash, nil] Default query parameters
|
|
653
|
+
# @param query_component [String, nil] Registry key for --query=help schema
|
|
654
|
+
def entity_list(api:, entity:, display_fields: nil, items_key: nil, list_query: nil, query_component: nil, **)
|
|
513
655
|
qs_path = query_component ? Schema::Registry.query_params(query_component, entity) : nil
|
|
656
|
+
data, http = api.read(entity, query_read_delete(default: list_query, schema: qs_path), ret: :both)
|
|
657
|
+
return Result::Empty.new if http.code == '204'
|
|
658
|
+
# TODO: not generic : which application is this for ?
|
|
659
|
+
if http['Content-Type'].start_with?('application/vnd.api+json')
|
|
660
|
+
Log.log.debug('is vnd.api')
|
|
661
|
+
data = data[entity]
|
|
662
|
+
end
|
|
663
|
+
data = data[items_key] if items_key
|
|
664
|
+
case data
|
|
665
|
+
when Hash then Result::SingleObject.new(data, fields: display_fields)
|
|
666
|
+
when Array
|
|
667
|
+
return Result::ObjectList.new(data, fields: display_fields) if data.empty? || data.first.is_a?(Hash)
|
|
668
|
+
Result::ValueList.new(data)
|
|
669
|
+
else Aspera.error_unexpected_value(data.class.name) { 'list type' }
|
|
670
|
+
end
|
|
671
|
+
end
|
|
514
672
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
entity,
|
|
545
|
-
nil,
|
|
546
|
-
content_type: Mime::JSON,
|
|
547
|
-
body: {delete_style => one_res_id}
|
|
548
|
-
)
|
|
549
|
-
return Result::Status.new('deleted')
|
|
550
|
-
end
|
|
551
|
-
items = one_res_id.is_a?(Array) ? one_res_id : [one_res_id]
|
|
552
|
-
return Result.bulk(items, is_bulk: is_bulk, command: command, bfail: bfail) do |one_id|
|
|
553
|
-
api.delete("#{entity}/#{one_id}", query_read_delete(schema: qs_path))
|
|
554
|
-
{'id' => one_id}
|
|
555
|
-
end
|
|
556
|
-
when :show
|
|
557
|
-
return Result::SingleObject.new(api.read(one_res_path), fields: display_fields)
|
|
558
|
-
when :list
|
|
559
|
-
data, http = api.read(entity, query_read_delete(default: list_query, schema: qs_path), ret: :both)
|
|
560
|
-
return Result::Empty.new if http.code == '204'
|
|
561
|
-
# TODO: not generic : which application is this for ?
|
|
562
|
-
if http['Content-Type'].start_with?('application/vnd.api+json')
|
|
563
|
-
Log.log.debug('is vnd.api')
|
|
564
|
-
data = data[entity]
|
|
565
|
-
end
|
|
566
|
-
data = data[items_key] if items_key
|
|
567
|
-
case data
|
|
568
|
-
when Hash
|
|
569
|
-
return Result::SingleObject.new(data, fields: display_fields)
|
|
570
|
-
when Array
|
|
571
|
-
return Result::ObjectList.new(data, fields: display_fields) if data.empty? || data.first.is_a?(Hash)
|
|
572
|
-
return Result::ValueList.new(data)
|
|
573
|
-
else
|
|
574
|
-
Aspera.error_unexpected_value(data.class.name){'list type'}
|
|
575
|
-
end
|
|
576
|
-
when :modify
|
|
577
|
-
# Use pre-resolved data if provided; otherwise read from CLI
|
|
578
|
-
parameters = input_data || options.get_next_argument('data', validation: Hash, schema: schema)
|
|
579
|
-
api.update(one_res_path, parameters)
|
|
580
|
-
return Result::Status.new('modified')
|
|
581
|
-
else
|
|
582
|
-
Aspera.error_unexpected_value(command){'command'}
|
|
673
|
+
# Show one instance of an entity.
|
|
674
|
+
# @param api [Aspera::Rest] REST API object
|
|
675
|
+
# @param entity [String] API sub-path
|
|
676
|
+
# @param id [String, nil] Resource identifier; nil when is_singleton: true
|
|
677
|
+
# @param display_fields [Array, nil] Fields to display
|
|
678
|
+
# @param is_singleton [Boolean] When true, entity is the full path (no id appended)
|
|
679
|
+
# @param id_as_arg [Boolean, String] When set, id is appended as ?<id_as_arg>=<id>
|
|
680
|
+
def entity_show(api:, entity:, id: nil, display_fields: nil, is_singleton: false, id_as_arg: false, **)
|
|
681
|
+
path = entity_res_path(entity, id, is_singleton: is_singleton, id_as_arg: id_as_arg)
|
|
682
|
+
Result::SingleObject.new(api.read(path), fields: display_fields)
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
# Create one or more instances of an entity (supports bulk).
|
|
686
|
+
# @param api [Aspera::Rest] REST API object
|
|
687
|
+
# @param entity [String] API sub-path
|
|
688
|
+
# @param display_fields [Array, nil] Fields to display
|
|
689
|
+
# @param body_component [String, nil] Registry key for request body schema
|
|
690
|
+
# @param input_data [Array, nil] Pre-resolved data; when nil, read from CLI
|
|
691
|
+
def entity_create(api:, entity:, display_fields: nil, body_component: nil, input_data: nil, data: nil, **)
|
|
692
|
+
schema = body_component ? Schema::Registry.req_body(body_component, "#{entity}.post") : nil
|
|
693
|
+
input_data ||= data
|
|
694
|
+
unless input_data
|
|
695
|
+
is_bulk = options.get_option(:bulk)
|
|
696
|
+
raw = options.get_next_argument('data', validation: is_bulk ? Array : Hash, schema: schema)
|
|
697
|
+
input_data = is_bulk ? raw : [raw]
|
|
698
|
+
end
|
|
699
|
+
input_data = [input_data] unless input_data.is_a?(Array)
|
|
700
|
+
bulk_result(input_data, command: :create, fields: display_fields) do |params|
|
|
701
|
+
api.create(entity, params)
|
|
583
702
|
end
|
|
584
703
|
end
|
|
585
704
|
|
|
705
|
+
# Modify an existing instance of an entity.
|
|
706
|
+
# @param api [Aspera::Rest] REST API object
|
|
707
|
+
# @param entity [String] API sub-path
|
|
708
|
+
# @param id [String, nil] Resource identifier; nil when is_singleton: true
|
|
709
|
+
# @param is_singleton [Boolean] When true, entity is the full path (no id appended)
|
|
710
|
+
# @param id_as_arg [Boolean, String] When set, id is appended as ?<id_as_arg>=<id>
|
|
711
|
+
# @param body_component [String, nil] Registry key for request body schema
|
|
712
|
+
# @param input_data [Hash, nil] Pre-resolved data; when nil, read from CLI
|
|
713
|
+
def entity_modify(api:, entity:, id: nil, is_singleton: false, id_as_arg: false, body_component: nil, input_data: nil, data: nil, **)
|
|
714
|
+
schema = body_component ? Schema::Registry.req_body(body_component, "#{entity}/{id}.put") : nil
|
|
715
|
+
path = entity_res_path(entity, id, is_singleton: is_singleton, id_as_arg: id_as_arg)
|
|
716
|
+
parameters = input_data || data || options.get_next_argument('data', validation: Hash, schema: schema)
|
|
717
|
+
api.update(path, parameters)
|
|
718
|
+
Result::Status.new('modified')
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
# Delete one or more instances of an entity (supports bulk).
|
|
722
|
+
# @param api [Aspera::Rest] REST API object
|
|
723
|
+
# @param entity [String] API sub-path
|
|
724
|
+
# @param id [String, Array, nil] Resource identifier(s)
|
|
725
|
+
# @param id_as_arg [Boolean, String] When set, id is appended as ?<id_as_arg>=<id>
|
|
726
|
+
# @param delete_style [String, nil] When set, deletes by sending id array in payload
|
|
727
|
+
# @param query_component [String, nil] Registry key for --query=help schema
|
|
728
|
+
def entity_delete(api:, entity:, id: nil, id_as_arg: false, delete_style: nil, query_component: nil, **)
|
|
729
|
+
qs_path = query_component ? Schema::Registry.query_params(query_component, entity) : nil
|
|
730
|
+
if !delete_style.nil?
|
|
731
|
+
ids = id.is_a?(Array) ? id : [id]
|
|
732
|
+
Aspera.assert_type(ids, Array, type: Cli::BadArgument)
|
|
733
|
+
api.delete(entity, nil, content_type: Mime::JSON, body: {delete_style => ids})
|
|
734
|
+
return Result::Status.new('deleted')
|
|
735
|
+
end
|
|
736
|
+
bulk_result(id, command: :delete) do |one_id|
|
|
737
|
+
api.delete(
|
|
738
|
+
id_as_arg ? "#{entity}?#{id_as_arg}=#{one_id}" : "#{entity}/#{one_id}",
|
|
739
|
+
query_read_delete(schema: qs_path)
|
|
740
|
+
)
|
|
741
|
+
{'id' => one_id}
|
|
742
|
+
end
|
|
743
|
+
end
|
|
744
|
+
|
|
745
|
+
# Build the resource path for an instance operation.
|
|
746
|
+
# @param entity [String] API sub-path
|
|
747
|
+
# @param id [String, nil] Resource identifier
|
|
748
|
+
# @param is_singleton [Boolean] When true, entity IS the full path
|
|
749
|
+
# @param id_as_arg [Boolean, String] When set, id appended as ?<id_as_arg>=<id>
|
|
750
|
+
# @return [String]
|
|
751
|
+
def entity_res_path(entity, id, is_singleton: false, id_as_arg: false)
|
|
752
|
+
return entity if is_singleton
|
|
753
|
+
return "#{entity}?#{id_as_arg}=#{id}" if id_as_arg
|
|
754
|
+
"#{entity}/#{id}"
|
|
755
|
+
end
|
|
756
|
+
|
|
586
757
|
# Query parameters in URL suitable for REST: list/`GET` and delete/`DELETE`
|
|
587
758
|
# @param default [Hash, nil] Default query parameters
|
|
588
759
|
# @param schema [String, nil] Contextual schema path for --query help display
|
|
@@ -590,7 +761,7 @@ module Aspera
|
|
|
590
761
|
def query_read_delete(default: nil, schema: nil)
|
|
591
762
|
# Dup default, as it could be frozen
|
|
592
763
|
query = options.get_option(:query, schema: schema) || default&.dup
|
|
593
|
-
Log.
|
|
764
|
+
Log.dump(:query_read_delete, query)
|
|
594
765
|
begin
|
|
595
766
|
# Check it is suitable
|
|
596
767
|
URI.encode_www_form(query) unless query.nil?
|