aspera-cli 4.25.6 → 4.26.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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +89 -48
  4. data/CONTRIBUTING.md +1 -1
  5. data/lib/aspera/api/aoc.rb +120 -79
  6. data/lib/aspera/api/node.rb +103 -51
  7. data/lib/aspera/ascp/installation.rb +99 -32
  8. data/lib/aspera/assert.rb +17 -13
  9. data/lib/aspera/cli/extended_value.rb +7 -2
  10. data/lib/aspera/cli/formatter.rb +107 -95
  11. data/lib/aspera/cli/main.rb +69 -10
  12. data/lib/aspera/cli/manager.rb +158 -78
  13. data/lib/aspera/cli/options.schema.yaml +82 -0
  14. data/lib/aspera/cli/plugins/aoc.rb +247 -144
  15. data/lib/aspera/cli/plugins/ats.rb +3 -3
  16. data/lib/aspera/cli/plugins/base.rb +60 -76
  17. data/lib/aspera/cli/plugins/config.rb +14 -12
  18. data/lib/aspera/cli/plugins/console.rb +3 -3
  19. data/lib/aspera/cli/plugins/faspex.rb +6 -6
  20. data/lib/aspera/cli/plugins/faspex5.rb +24 -23
  21. data/lib/aspera/cli/plugins/node.rb +67 -71
  22. data/lib/aspera/cli/plugins/oauth.rb +5 -12
  23. data/lib/aspera/cli/plugins/orchestrator.rb +13 -13
  24. data/lib/aspera/cli/plugins/preview.rb +116 -80
  25. data/lib/aspera/cli/plugins/server.rb +2 -10
  26. data/lib/aspera/cli/plugins/shares.rb +7 -7
  27. data/lib/aspera/cli/sync_actions.rb +1 -1
  28. data/lib/aspera/cli/transfer_agent.rb +17 -15
  29. data/lib/aspera/cli/version.rb +1 -1
  30. data/lib/aspera/command_line_builder.rb +22 -18
  31. data/lib/aspera/dot_container.rb +7 -3
  32. data/lib/aspera/environment.rb +6 -5
  33. data/lib/aspera/formatter_interface.rb +14 -0
  34. data/lib/aspera/hash_ext.rb +6 -0
  35. data/lib/aspera/log.rb +5 -4
  36. data/lib/aspera/markdown.rb +4 -1
  37. data/lib/aspera/oauth/factory.rb +1 -1
  38. data/lib/aspera/preview/file_types.rb +1 -1
  39. data/lib/aspera/preview/generator.rb +146 -91
  40. data/lib/aspera/preview/options.rb +4 -1
  41. data/lib/aspera/preview/terminal.rb +50 -20
  42. data/lib/aspera/preview/utils.rb +76 -34
  43. data/lib/aspera/products/transferd.rb +1 -1
  44. data/lib/aspera/proxy_auto_config.rb +3 -0
  45. data/lib/aspera/rest.rb +2 -1
  46. data/lib/aspera/rest_list.rb +23 -16
  47. data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +62801 -0
  48. data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +8898 -0
  49. data/lib/aspera/schema/documentation.rb +107 -0
  50. data/lib/aspera/schema/reader.rb +75 -0
  51. data/lib/aspera/schema/registry.rb +63 -0
  52. data/lib/aspera/secret_hider.rb +3 -1
  53. data/lib/aspera/sync/conf.schema.yaml +0 -26
  54. data/lib/aspera/sync/operations.rb +9 -5
  55. data/lib/aspera/transfer/faux_file.rb +1 -1
  56. data/lib/aspera/transfer/resumer.rb +1 -1
  57. data/lib/aspera/transfer/spec.rb +3 -3
  58. data/lib/aspera/transfer/spec.schema.yaml +1 -1
  59. data/lib/aspera/uri_reader.rb +17 -2
  60. data/lib/aspera/yaml.rb +4 -2
  61. data.tar.gz.sig +0 -0
  62. metadata +13 -7
  63. metadata.gz.sig +0 -0
  64. data/lib/aspera/transfer/spec_doc.rb +0 -76
@@ -56,7 +56,7 @@ module Aspera
56
56
  commands = %i[create list show modify delete node cluster entitlement]
57
57
  command = options.get_next_command(commands)
58
58
  # those do not require access key id
59
- access_key_id = instance_identifier unless %i[create list].include?(command)
59
+ access_key_id = options.instance_identifier unless %i[create list].include?(command)
60
60
  case command
61
61
  when :create
62
62
  params = value_create_modify(command: command, default: {})
@@ -151,7 +151,7 @@ module Aspera
151
151
  if options.get_option(:cloud) || options.get_option(:region)
152
152
  server_data = server_by_cloud_region
153
153
  else
154
- server_id = instance_identifier
154
+ server_id = options.instance_identifier
155
155
  server_data = @ats_api_open.all_servers.find{ |i| i['id'].eql?(server_id)}
156
156
  raise BadIdentifier.new('server', server_id) if server_data.nil?
157
157
  end
@@ -179,7 +179,7 @@ module Aspera
179
179
 
180
180
  def execute_action_api_key
181
181
  command = options.get_next_command(%i[instances create list show delete])
182
- concerned_id = instance_identifier if %i[show delete].include?(command)
182
+ concerned_id = options.instance_identifier if %i[show delete].include?(command)
183
183
  rest_add_header = {}
184
184
  if !command.eql?(:instances)
185
185
  instance = options.get_option(:instance)
@@ -8,28 +8,22 @@ module Aspera
8
8
  module Plugins
9
9
  # Base class for command plugins
10
10
  class Base
11
- # Operations without id (create list)
12
- GLOBAL_OPS = %i[create list].freeze
13
- # Operations with id (modify delete show)
14
- INSTANCE_OPS = %i[modify delete show].freeze
15
- # All standard operations (create list modify delete show)
16
- ALL_OPS = (GLOBAL_OPS + INSTANCE_OPS).freeze
17
-
11
+ module Operations
12
+ # Operations without id: `create` `list`
13
+ GLOBAL = %i[create list].freeze
14
+ # Operations on singleton: `modify` `show`
15
+ SINGLETON = %i[modify show].freeze
16
+ # Operations with id: `modify` `show` `delete`
17
+ INSTANCE = (SINGLETON + %i[delete]).freeze
18
+ # All standard operations: `create` `list` `modify` `show` `delete`
19
+ ALL = (GLOBAL + INSTANCE).freeze
20
+ end
18
21
  class << self
19
22
  def declare_options(options)
20
23
  options.declare(:query, 'Additional filter for for some commands (list/delete)', allowed: [Hash, Array, NilClass])
21
24
  options.declare(:bulk, 'Bulk operation (only some)', allowed: Allowed::TYPES_BOOLEAN, default: false)
22
25
  options.declare(:bfail, 'Bulk operation error handling', allowed: Allowed::TYPES_BOOLEAN, default: true)
23
26
  end
24
-
25
- # @return [Hash,NilClass] `{field:,value:}` if identifier is a percent selector, else `nil`
26
- def percent_selector(identifier)
27
- Aspera.assert_type(identifier, String)
28
- if (m = identifier.match(REGEX_LOOKUP_ID_BY_FIELD))
29
- return {field: m[1], value: ExtendedValue.instance.evaluate(m[2], context: "percent selector: #{m[1]}")}
30
- end
31
- return
32
- end
33
27
  end
34
28
 
35
29
  def initialize(context:)
@@ -43,15 +37,15 @@ module Aspera
43
37
  # Global objects
44
38
  attr_reader :context
45
39
 
46
- # @return [Manager]
40
+ # @return [Aspera::Cli::Manager]
47
41
  def options; @context.options; end
48
- # @return [TransferAgent]
42
+ # @return [Aspera::Cli::TransferAgent]
49
43
  def transfer; @context.transfer; end
50
- # @return [Config]
44
+ # @return [Aspera::Cli::Plugins::Config]
51
45
  def config; @context.config; end
52
- # @return [Formatter]
46
+ # @return [Aspera::Cli::Formatter]
53
47
  def formatter; @context.formatter; end
54
- # @return [PersistencyFolder]
48
+ # @return [Aspera::PersistencyFolder]
55
49
  def persistency; @context.persistency; end
56
50
 
57
51
  def add_manual_header(has_options = true)
@@ -62,36 +56,24 @@ module Aspera
62
56
  options.parser.separator('OPTIONS:') if has_options
63
57
  end
64
58
 
65
- # Resource identifier as positional parameter
66
- #
67
- # @param description [String] description of the identifier
68
- # @param &block [Proc] block to search for identifier based on attribute value
69
- # @return [String, Array] identifier or list of ids
70
- def instance_identifier(description: 'identifier')
71
- res_id = options.get_next_argument(description, multiple: options.get_option(:bulk)) if res_id.nil?
72
- # Can be an Array
73
- if res_id.is_a?(String) && (m = Base.percent_selector(res_id))
74
- Aspera.assert(block_given?, type: Cli::BadArgument){"Percent syntax for #{description} not supported in this context"}
75
- res_id = yield(m[:field], m[:value])
76
- end
77
- return res_id
78
- end
79
-
80
59
  # For create and delete operations: execute one action or multiple if bulk is yes
81
- # @param command [Symbol] Operation: :create, :delete, ...
82
- # @param descr [String] Description of the value
83
- # @param values [Object] Value(s), or type of value to get from user
84
- # @param id_result [String] Key in result hash to use as identifier
85
- # @param fields [Array] Fields to display
86
- # @param &block [Proc] Block to execute for each value
87
- def do_bulk_operation(command:, descr: nil, values: Hash, id_result: 'id', fields: :default)
60
+ # @param command [Symbol] Operation: :create, :delete, ...
61
+ # @param descr [String, nil] Description of the value
62
+ # @param values [Class, Array, Symbol] Type, or list of values, or :identifier, result is given to the block in loop
63
+ # @param id_result [String] Key in result Hash to use as identifier
64
+ # @param fields [Symbol, Array] Fields to display
65
+ # @param schema [Hash, nil] JSON schema for validation
66
+ # @yieldparam param [Object] The parameter value to process
67
+ # @yieldreturn [Hash, nil] Result hash for the operation (optional)
68
+ # @return [Hash] Result suitable for CLI output
69
+ def do_bulk_operation(command:, descr: nil, values: Hash, id_result: 'id', fields: :default, schema: nil, &block)
88
70
  Aspera.assert(block_given?){'missing block'}
89
71
  is_bulk = options.get_option(:bulk)
90
72
  case values
91
73
  when :identifier
92
- values = instance_identifier(description: descr)
74
+ values = options.instance_identifier(description: descr)
93
75
  when Class
94
- values = value_create_modify(command: command, description: descr, type: values, bulk: is_bulk)
76
+ values = value_create_modify(command: command, description: descr, type: values, bulk: is_bulk, schema: schema)
95
77
  end
96
78
  # If not bulk, there is a single value
97
79
  params = is_bulk ? values : [values]
@@ -126,15 +108,17 @@ module Aspera
126
108
  end
127
109
 
128
110
  # Operations: Create, Delete, Show, List, Modify
129
- # @param api [Rest] api to use
130
- # @param entity [String] sub path in URL to resource relative to base url
131
- # @param command [Symbol] command to execute: create show list modify delete
132
- # @param display_fields [Array] fields to display by default
133
- # @param items_key [String] result is in a sub key of the json
134
- # @param delete_style [String] If set, the delete operation by array in payload
135
- # @param id_as_arg [String] If set, the id is provided as url argument ?<id_as_arg>=<id>
136
- # @param is_singleton [Boolean] If `true`, entity is the full path to the resource
137
- # @param block [Proc] Block to search for identifier based on attribute value
111
+ # @param api [Aspera::Rest] API to use
112
+ # @param entity [String] Sub path in URL to resource relative to base url
113
+ # @param command [Symbol, nil] Command to execute: :create, :show, :list, :modify, :delete
114
+ # @param display_fields [Array, nil] Fields to display by default
115
+ # @param items_key [String, nil] Result is in a sub key of the JSON
116
+ # @param delete_style [String, nil] If set, the delete operation by array in payload
117
+ # @param id_as_arg [Boolean, String] If set, the id is provided as url argument ?<id_as_arg>=<id>
118
+ # @param is_singleton [Boolean] If `true`, entity is the full path to the resource
119
+ # @param list_query [Hash, nil] Query parameters for list operation
120
+ # @yieldparam value [String] Value to search for identifier
121
+ # @yieldreturn [String] The identifier
138
122
  # @return [Hash] Result suitable for CLI result
139
123
  def entity_execute(
140
124
  api:,
@@ -148,11 +132,11 @@ module Aspera
148
132
  list_query: nil,
149
133
  &block
150
134
  )
151
- command = options.get_next_command(ALL_OPS) if command.nil?
135
+ command = options.get_next_command(Operations::ALL) if command.nil?
152
136
  if is_singleton
153
137
  one_res_path = entity
154
- elsif INSTANCE_OPS.include?(command)
155
- one_res_id = instance_identifier(&block)
138
+ elsif Operations::INSTANCE.include?(command)
139
+ one_res_id = options.instance_identifier(&block)
156
140
  one_res_path = "#{entity}/#{one_res_id}"
157
141
  one_res_path = "#{entity}?#{id_as_arg}=#{one_res_id}" if id_as_arg
158
142
  end
@@ -210,6 +194,8 @@ module Aspera
210
194
  end
211
195
 
212
196
  # Query parameters in URL suitable for REST: list/`GET` and delete/`DELETE`
197
+ # @param default [Hash, nil] Default query parameters
198
+ # @return [Hash, nil] Query parameters
213
199
  def query_read_delete(default: nil)
214
200
  # Dup default, as it could be frozen
215
201
  query = options.get_option(:query) || default.dup
@@ -223,35 +209,33 @@ module Aspera
223
209
  return query
224
210
  end
225
211
 
226
- # Retrieves an extended value from command line, used for creation or modification of entities
227
- # @param command [Symbol] command name for error message
228
- # @param type [Class] expected type of value, either a Class, an Array of Class
229
- # @param bulk [Boolean] if true, value must be an Array of <type>
230
- # @param default [Object] default value if not provided
231
- def value_create_modify(command:, description: nil, type: Hash, bulk: false, default: nil)
212
+ # Retrieves an extended value from command line.
213
+ # Used for creation or modification of entities.
214
+ # @param command [Symbol] Command name for error message
215
+ # @param description [String, nil] Description of the value
216
+ # @param type [Class, nil] Expected type of value
217
+ # @param bulk [Boolean] If `true`, value must be an Array of `type`
218
+ # @param default [Object, nil] Default value if not provided
219
+ # @param schema [Hash, nil] JSON schema for validation
220
+ # @return [Hash, Array<Hash>] The value(s) to create object(s)
221
+ def value_create_modify(command:, description: nil, type: Hash, bulk: false, default: nil, schema: nil)
232
222
  value = options.get_next_argument(
233
- "parameters for #{command}#{" (#{description})" unless description.nil?}", mandatory: default.nil?,
234
- validation: bulk ? Array : type
223
+ "parameters for #{command}#{" (#{description})" unless description.nil?}",
224
+ mandatory: default.nil?,
225
+ validation: bulk ? Array : type,
226
+ schema: schema
235
227
  )
236
228
  value = default if value.nil?
237
229
  unless type.nil?
238
- type = [type] unless type.is_a?(Array)
239
- Aspera.assert_array_all(type, Class){'check types'}
230
+ Aspera.assert_type(type, Class){'type'}
240
231
  if bulk
241
- Aspera.assert_type(value, Array, type: Cli::BadArgument)
242
- value.each do |v|
243
- Aspera.assert_values(v.class, type, type: Cli::BadArgument)
244
- end
232
+ Aspera.assert_array_all(value, type, type: Cli::BadArgument){'type'}
245
233
  else
246
- Aspera.assert_values(value.class, type, type: Cli::BadArgument)
234
+ Aspera.assert_type(value, type, type: Cli::BadArgument){'type'}
247
235
  end
248
236
  end
249
237
  return value
250
238
  end
251
-
252
- # Percent selector: select by this field for this value
253
- REGEX_LOOKUP_ID_BY_FIELD = /^%([^:]+):(.*)$/
254
- private_constant :REGEX_LOOKUP_ID_BY_FIELD
255
239
  end
256
240
  end
257
241
  end
@@ -16,7 +16,7 @@ require 'aspera/sync/operations'
16
16
  require 'aspera/products/transferd'
17
17
  require 'aspera/transfer/parameters'
18
18
  require 'aspera/transfer/spec'
19
- require 'aspera/transfer/spec_doc'
19
+ require 'aspera/schema/documentation'
20
20
  require 'aspera/keychain/macos_security'
21
21
  require 'aspera/proxy_auto_config'
22
22
  require 'aspera/environment'
@@ -436,7 +436,7 @@ module Aspera
436
436
  end
437
437
 
438
438
  def set_preset_key(preset, param_name, param_value)
439
- Aspera.assert_values(param_name.class, [String, Symbol]){'parameter'}
439
+ Aspera.assert_type(param_name, String, Symbol){'parameter'}
440
440
  param_name = param_name.to_s
441
441
  selected_preset = @config_presets[preset]
442
442
  if selected_preset.nil?
@@ -568,7 +568,9 @@ module Aspera
568
568
 
569
569
  def install_transfer_sdk
570
570
  asked_version = options.get_next_argument('transferd version', mandatory: false)
571
- name, version, folder = Ascp::Installation.instance.install_sdk(url: options.get_option(:sdk_url, mandatory: true), version: asked_version)
571
+ sdk_url = options.get_option(:sdk_url, mandatory: true)
572
+ sdk_url = nil if sdk_url.eql?(SpecialValues::DEF)
573
+ name, version, folder = Ascp::Installation.instance.retrieve_sdk(url: sdk_url, version: asked_version)
572
574
  return Main.result_status("Installed #{name} version #{version} in #{folder}")
573
575
  end
574
576
 
@@ -596,10 +598,10 @@ module Aspera
596
598
  when :install
597
599
  return install_transfer_sdk
598
600
  when :spec
599
- fields, data = Transfer::SpecDoc.man_table(Formatter, include_option: true)
600
- return Main.result_object_list(data, fields: fields.map(&:to_s))
601
+ builder = Schema::Documentation.new(TerminalFormatter, Transfer::Spec::SCHEMA, include_option: true, agent_columns: true).build
602
+ return Main.result_object_list(builder.rows, fields: builder.columns)
601
603
  when :schema
602
- schema = Transfer::Spec::SCHEMA.merge({'$comment'=>'DO NOT EDIT, this file was generated from the YAML.'})
604
+ schema = Transfer::Spec::SCHEMA.current.merge({'$comment'=>'DO NOT EDIT, this file was generated from the YAML.'})
603
605
  agent = options.get_next_argument('transfer agent name', mandatory: false)
604
606
  schema['properties'] = schema['properties'].select{ |_k, v| CommandLineBuilder.supported_by_agent(agent, v)} unless agent.nil?
605
607
  schema['properties'] = schema['properties'].sort.to_h
@@ -641,7 +643,7 @@ module Aspera
641
643
 
642
644
  def execute_preset(action: nil, name: nil)
643
645
  action = options.get_next_command(PRESET_ALL_ACTIONS) if action.nil?
644
- name = instance_identifier if name.nil? && PRESET_INSTANCE_ACTIONS.include?(action)
646
+ name = options.instance_identifier if name.nil? && PRESET_INSTANCE_ACTIONS.include?(action)
645
647
  name = global_default_preset if name.eql?(GLOBAL_DEFAULT_KEYWORD)
646
648
  # Those operations require existing option
647
649
  raise "no such preset: #{name}" if PRESET_EXIST_ACTIONS.include?(action) && !@config_presets.key?(name)
@@ -820,7 +822,7 @@ module Aspera
820
822
  when :list
821
823
  return Main.result_object_list(OAuth::Factory.instance.persisted_tokens)
822
824
  when :show
823
- data = OAuth::Factory.instance.get_token_info(instance_identifier)
825
+ data = OAuth::Factory.instance.get_token_info(options.instance_identifier)
824
826
  raise Cli::Error, 'Unknown identifier' if data.nil?
825
827
  return Main.result_single_object(data)
826
828
  end
@@ -832,8 +834,8 @@ module Aspera
832
834
  plugin_class = Plugins::Factory.instance.plugin_class(name)
833
835
  result.push({
834
836
  plugin: name,
835
- detect: Formatter.tick(plugin_class.respond_to?(:detect)),
836
- wizard: Formatter.tick(plugin_class.method_defined?(:wizard)),
837
+ detect: TerminalFormatter.tick(plugin_class.respond_to?(:detect)),
838
+ wizard: TerminalFormatter.tick(plugin_class.method_defined?(:wizard)),
837
839
  path: Plugins::Factory.instance.plugin_source(name)
838
840
  })
839
841
  end
@@ -876,8 +878,8 @@ module Aspera
876
878
  when :sync
877
879
  case options.get_next_command(%i[spec admin translate])
878
880
  when :spec
879
- fields, data = Transfer::SpecDoc.man_table(Formatter, include_option: true, agent_columns: false, schema: Sync::Operations::CONF_SCHEMA)
880
- return Main.result_object_list(data, fields: fields.map(&:to_s))
881
+ builder = Schema::Documentation.new(TerminalFormatter, Sync::Operations::CONF_SCHEMA, include_option: true).build
882
+ return Main.result_object_list(builder.rows, fields: builder.columns)
881
883
  when :admin
882
884
  return execute_sync_admin
883
885
  when :translate
@@ -130,15 +130,15 @@ module Aspera
130
130
  fields: %w[id contact name status]
131
131
  )
132
132
  when :show
133
- transfer_id = instance_identifier(description: 'transfer ID')
133
+ transfer_id = options.instance_identifier(description: 'transfer ID')
134
134
  return Main.result_single_object(api_console.read("transfers/#{transfer_id}"))
135
135
  when :files
136
- transfer_id = instance_identifier(description: 'transfer ID')
136
+ transfer_id = options.instance_identifier(description: 'transfer ID')
137
137
  query = query_read_delete(default: {})
138
138
  query['limit'] ||= 100
139
139
  return Main.result_object_list(api_console.read("transfers/#{transfer_id}/files", query))
140
140
  when :start, :pause, :cancel, :resume, :rerun, :change_rate, :change_policy, :move_forwards, :move_back
141
- transfer_id = instance_identifier(description: 'transfer ID')
141
+ transfer_id = options.instance_identifier(description: 'transfer ID')
142
142
  return Main.result_single_object(api_console.update("transfers/#{transfer_id}/#{command}", query_read_delete))
143
143
  end
144
144
  end
@@ -165,7 +165,7 @@ module Aspera
165
165
  def mailbox_filtered_entries(stop_at_id: nil)
166
166
  recipient_names = [options.get_option(:recipient) || options.get_option(:username, mandatory: true)]
167
167
  # some workgroup messages have no star in recipient name
168
- recipient_names.push(recipient_names.first[1..-1]) if recipient_names.first.start_with?('*')
168
+ recipient_names.push(recipient_names.first.delete_prefix('*')) if recipient_names.first.start_with?('*')
169
169
  # mailbox is in ATOM_MAILBOXES
170
170
  mailbox = options.get_option(:box, mandatory: true)
171
171
  # parameters
@@ -291,7 +291,7 @@ module Aspera
291
291
  command_pkg = options.get_next_command(%i[send receive list show], aliases: {recv: :receive})
292
292
  case command_pkg
293
293
  when :show
294
- delivery_id = instance_identifier
294
+ delivery_id = options.instance_identifier
295
295
  return Main.result_single_object(mailbox_filtered_entries(stop_at_id: delivery_id).find{ |p| p[PACKAGE_MATCH_FIELD].eql?(delivery_id)})
296
296
  when :list
297
297
  return Main.result_object_list(mailbox_filtered_entries, fields: [PACKAGE_MATCH_FIELD, 'title', 'items'])
@@ -307,7 +307,7 @@ module Aspera
307
307
  first_source = delivery_info['sources'].first
308
308
  first_source['paths'].concat(transfer.source_list)
309
309
  source_id = options.get_option(:remote_source)
310
- if source_id && (m = Base.percent_selector(source_id))
310
+ if source_id && (m = Manager.percent_selector(source_id))
311
311
  Aspera.assert(m[:field].eql?('name'), type: Cli::BadArgument){'only name as selector, or give id'}
312
312
  source_list = api_v3.read('source_shares')['items']
313
313
  source_id = self.class.get_source_id_by_name(m[:value], source_list)
@@ -348,7 +348,7 @@ module Aspera
348
348
  )
349
349
  end
350
350
  # get command line parameters
351
- delivery_id = instance_identifier
351
+ delivery_id = options.instance_identifier
352
352
  Aspera.assert(!delivery_id.empty?){'empty id'}
353
353
  recipient = options.get_option(:recipient)
354
354
  if delivery_id.eql?(SpecialValues::ALL)
@@ -441,7 +441,7 @@ module Aspera
441
441
  when :list
442
442
  return Main.result_object_list(source_list)
443
443
  else # :info :node
444
- source_id = instance_identifier do |field, value|
444
+ source_id = options.instance_identifier do |field, value|
445
445
  Aspera.assert(field.eql?('name'), type: Cli::BadArgument){'only name as selector, or give id'}
446
446
  self.class.get_source_id_by_name(value, source_list)
447
447
  end.to_i
@@ -506,7 +506,7 @@ module Aspera
506
506
  return entity_execute(api: api_v4, entity: 'metadata_profiles')
507
507
  when :package
508
508
  pkg_box_type = options.get_next_command([:users])
509
- pkg_box_id = instance_identifier
509
+ pkg_box_id = options.instance_identifier
510
510
  return entity_execute(api: api_v4, entity: "#{pkg_box_type}/#{pkg_box_id}/packages")
511
511
  end
512
512
  when :address_book
@@ -98,6 +98,8 @@ module Aspera
98
98
  options.declare(:shared_folder, 'Send package with files from shared folder')
99
99
  options.declare(:group_type, 'Type of shared box', allowed: %i[shared_inboxes workgroups], default: :shared_inboxes)
100
100
  options.parse_options!
101
+ # [Aspera::Api::Faspex]
102
+ @api_v5 = nil
101
103
  end
102
104
 
103
105
  # if recipient is just an email, then convert to expected API hash : name and type
@@ -114,7 +116,7 @@ module Aspera
114
116
  parameters[type].map! do |recipient_data|
115
117
  # If just a string, make a general lookup and build expected name/type hash
116
118
  if recipient_data.is_a?(String)
117
- matched = @api_v5.lookup_by_name('contacts', recipient_data, query: Rest.php_style({context: 'packages', type: recipient_types}))
119
+ matched = @api_v5.lookup_with_q('contacts', value: recipient_data, query: Rest.php_style({context: 'packages', type: recipient_types}))
118
120
  recipient_data = {
119
121
  name: matched['name'],
120
122
  recipient_type: matched['type']
@@ -232,8 +234,8 @@ module Aspera
232
234
  result_transfer = []
233
235
  param_file_list = {}
234
236
  begin
235
- param_file_list['paths'] = transfer.source_list.map{ |source| {'path'=>source}}
236
- rescue Cli::BadArgument
237
+ param_file_list['paths'] = transfer.ts_source_paths
238
+ rescue Cli::MissingArgument
237
239
  # paths is optional
238
240
  end
239
241
  box = options.get_option(:box)
@@ -268,7 +270,7 @@ module Aspera
268
270
  end
269
271
 
270
272
  def package_send
271
- parameters = value_create_modify(command: :send)
273
+ parameters = value_create_modify(command: :send, schema: Schema::Registry.req_body(Schema::Registry::FASPEX, 'packages.post'))
272
274
  # autofill recipient for public url
273
275
  if @api_v5.pub_link_context&.key?('recipient_type') && !parameters.key?('recipients')
274
276
  parameters['recipients'] = [{
@@ -297,7 +299,7 @@ module Aspera
297
299
  return Main.result_transfer(transfer.start(transfer_spec))
298
300
  else
299
301
  # send from remote shared folder
300
- if (m = Base.percent_selector(shared_folder))
302
+ if (m = Manager.percent_selector(shared_folder))
301
303
  shared_folder = @api_v5.lookup_entity_by_field(
302
304
  entity: 'shared_folders',
303
305
  field: m[:field],
@@ -376,7 +378,7 @@ module Aspera
376
378
  command = options.get_next_command(%i[show browse status delete receive send list])
377
379
  package_id =
378
380
  if %i[receive show browse status delete].include?(command)
379
- @api_v5.pub_link_context&.key?('package_id') ? @api_v5.pub_link_context['package_id'] : instance_identifier
381
+ @api_v5.pub_link_context&.key?('package_id') ? @api_v5.pub_link_context['package_id'] : options.instance_identifier
380
382
  end
381
383
  case command
382
384
  when :show
@@ -419,7 +421,7 @@ module Aspera
419
421
  entity: res_sym.to_s
420
422
  }
421
423
  res_id_query = :default
422
- available_commands = ALL_OPS
424
+ available_commands = Operations::ALL
423
425
  case res_sym
424
426
  when :metadata_profiles
425
427
  exec_args[:entity] = 'configuration/metadata_profiles'
@@ -437,7 +439,7 @@ module Aspera
437
439
  available_commands += [:reset_password]
438
440
  when :oauth_clients
439
441
  exec_args[:display_fields] = Formatter.all_but('public_key')
440
- exec_args[:api] = Api::Faspex.new(root: Api::Faspex::PATH_AUTH, **Oauth.args_from_options(options))
442
+ exec_args[:api] = Api::Faspex.new(root: Api::Faspex::PATH_AUTH, **Oauth.kwargs_from_options(options))
441
443
  exec_args[:list_query] = {'expand': true, 'no_api_path': true, 'client_types[]': 'public'}
442
444
  when :shared_inboxes, :workgroups
443
445
  available_commands += %i[members saml_groups invite_external_collaborator]
@@ -459,13 +461,13 @@ module Aspera
459
461
  return Main.result_object_list(data, total: total, fields: exec_args[:display_fields])
460
462
  when :shared_folders
461
463
  # nodes
462
- node_id = instance_identifier do |field, value|
464
+ node_id = options.instance_identifier do |field, value|
463
465
  @api_v5.lookup_entity_by_field(entity: 'nodes', field: field, value: value)['id']
464
466
  end
465
467
  shfld_entity = "nodes/#{node_id}/shared_folders"
466
- sh_command = options.get_next_command(ALL_OPS + [:user])
468
+ sh_command = options.get_next_command(Operations::ALL + [:user])
467
469
  case sh_command
468
- when *ALL_OPS
470
+ when *Operations::ALL
469
471
  return entity_execute(
470
472
  api: @api_v5,
471
473
  entity: shfld_entity,
@@ -474,7 +476,7 @@ module Aspera
474
476
  @api_v5.lookup_entity_by_field(entity: shfld_entity, field: field, value: value)['id']
475
477
  end
476
478
  when :user
477
- sh_id = instance_identifier do |field, value|
479
+ sh_id = options.instance_identifier do |field, value|
478
480
  @api_v5.lookup_entity_by_field(entity: shfld_entity, field: field, value: value)['id']
479
481
  end
480
482
  user_path = "#{shfld_entity}/#{sh_id}/custom_access_users"
@@ -485,15 +487,14 @@ module Aspera
485
487
  end
486
488
  when :browse
487
489
  # nodes
488
- node_id = instance_identifier do |field, value|
490
+ node_id = options.instance_identifier do |field, value|
489
491
  @api_v5.lookup_entity_by_field(entity: 'nodes', value: value, field: field)['id']
490
492
  end
491
493
  return browse_folder("nodes/#{node_id}/browse")
492
494
  when :invite_external_collaborator
493
495
  # :shared_inboxes, :workgroups
494
- shared_inbox_id = instance_identifier{ |field, value| @api_v5.lookup_entity_by_field(entity: res_sym.to_s, field: field, value: value, query: res_id_query)['id']}
495
- creation_payload = value_create_modify(command: res_command, type: [Hash, String])
496
- creation_payload = {'email_address' => creation_payload} if creation_payload.is_a?(String)
496
+ shared_inbox_id = options.instance_identifier{ |field, value| @api_v5.lookup_entity_by_field(entity: res_sym.to_s, field: field, value: value, query: res_id_query)['id']}
497
+ creation_payload = value_create_modify(command: res_command)
497
498
  result = @api_v5.create("#{res_sym}/#{shared_inbox_id}/external_collaborator", creation_payload)
498
499
  formatter.display_status(result['message'])
499
500
  result = @api_v5.lookup_entity_by_field(
@@ -505,7 +506,7 @@ module Aspera
505
506
  return Main.result_single_object(result)
506
507
  when :members, :saml_groups
507
508
  # res_command := :shared_inboxes, :workgroups
508
- res_id = instance_identifier{ |field, value| @api_v5.lookup_entity_by_field(entity: res_sym.to_s, field: field, value: value, query: res_id_query)['id']}
509
+ res_id = options.instance_identifier{ |field, value| @api_v5.lookup_entity_by_field(entity: res_sym.to_s, field: field, value: value, query: res_id_query)['id']}
509
510
  res_path = "#{res_sym}/#{res_id}/#{res_command}"
510
511
  list_key = res_command.to_s
511
512
  list_key = 'groups' if res_command.eql?(:saml_groups)
@@ -515,7 +516,7 @@ module Aspera
515
516
  users = options.get_next_argument('user id, %name:, or Array')
516
517
  users = [users] unless users.is_a?(Array)
517
518
  users = users.map do |user|
518
- if (m = Base.percent_selector(user))
519
+ if (m = Manager.percent_selector(user))
519
520
  @api_v5.lookup_entity_by_field(
520
521
  entity: 'accounts',
521
522
  field: m[:field],
@@ -546,7 +547,7 @@ module Aspera
546
547
  end
547
548
  when :reset_password
548
549
  # :accounts
549
- contact_id = instance_identifier{ |field, value| @api_v5.lookup_entity_by_field(entity: 'accounts', field: field, value: value, query: res_id_query)['id']}
550
+ contact_id = options.instance_identifier{ |field, value| @api_v5.lookup_entity_by_field(entity: 'accounts', field: field, value: value, query: res_id_query)['id']}
550
551
  @api_v5.create("accounts/#{contact_id}/reset_password", {})
551
552
  return Main.result_status('password reset, user shall check email')
552
553
  end
@@ -617,7 +618,7 @@ module Aspera
617
618
  command = options.get_next_command(ACTIONS)
618
619
  unless %i{postprocessing health}.include?(command)
619
620
  # create an API object with the same options, but with a different subpath
620
- @api_v5 = Api::Faspex.new(**Oauth.args_from_options(options))
621
+ @api_v5 = Api::Faspex.new(**Oauth.kwargs_from_options(options))
621
622
  # in case user wants to use HTTPGW tell transfer agent how to get address
622
623
  transfer.httpgw_url_cb = lambda{@api_v5.read('account')['gateway_url']}
623
624
  end
@@ -660,7 +661,7 @@ module Aspera
660
661
  when :list
661
662
  return Main.result_object_list(all_shared_folders)
662
663
  when :browse
663
- shared_folder_id = instance_identifier do |field, value|
664
+ shared_folder_id = options.instance_identifier do |field, value|
664
665
  matches = all_shared_folders.select{ |i| i[field].eql?(value)}
665
666
  raise "no match for #{field} = #{value}" if matches.empty?
666
667
  raise "multiple matches for #{field} = #{value}" if matches.length > 1
@@ -674,7 +675,7 @@ module Aspera
674
675
  return execute_admin
675
676
  when :invitations
676
677
  invitation_endpoint = 'invitations'
677
- invitation_command = options.get_next_command(%i[resend].concat(ALL_OPS))
678
+ invitation_command = options.get_next_command(%i[resend].concat(Operations::ALL))
678
679
  case invitation_command
679
680
  when :create
680
681
  return do_bulk_operation(command: invitation_command, descr: 'data') do |params|
@@ -682,7 +683,7 @@ module Aspera
682
683
  @api_v5.create(invitation_endpoint, params)
683
684
  end
684
685
  when :resend
685
- @api_v5.create("#{invitation_endpoint}/#{instance_identifier}/resend", nil)
686
+ @api_v5.create("#{invitation_endpoint}/#{options.instance_identifier}/resend", nil)
686
687
  return Main.result_status('Invitation resent')
687
688
  else
688
689
  return entity_execute(