aspera-cli 4.25.4 → 4.26.0.pre

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.
@@ -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
- @man_header = true
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].map(&:to_s).join(',')
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
- init_agents_options_plugins
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.map(&:to_s).join(', ')}
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
@@ -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
- # change option name with dash to name with underscore
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) unil END
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 |option_value|
470
- case option_value
471
- when /^#{OPTION_PREFIX}([^=]+)$/o
472
- # ignore
473
- when /^#{OPTION_PREFIX}([^=]+)=(.*)$/o
474
- name = Regexp.last_match(1)
475
- value = Regexp.last_match(2)
476
- name.gsub!(OPTION_SEP_LINE, OPTION_SEP_SYMBOL)
477
- value = ExtendedValue.instance.evaluate(value, context: "option: #{name}")
478
- Log.log.debug{"option #{name}=#{value}"}
479
- result[name] = value
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 (m = e.args.first.match(/^--([a-z\-]+)\.([^=]+)=(.+)$/))
520
- option, path, value = m.captures
521
- option_sym = self.class.option_line_to_name(option).to_sym
522
- if @declared_options.key?(option_sym)
523
- set_option(option_sym, DotContainer.dotted_to_container(path, smart_convert(value), get_option(option_sym)), where: 'dotted')
524
- retry
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)
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
- # save for later processing
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
- # set unprocessed options for next time
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 inlude #{OPTION_VALUE_SEPARATOR}"}
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 ressource id, e.g. `scQ7uXPbvQ`
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.each_with_object({}){ |i, m| m[i] = true}
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
 
@@ -190,7 +190,7 @@ module Aspera
190
190
  return Main.result_single_object(api.read(one_res_path), fields: display_fields)
191
191
  when :list
192
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))
193
+ data, total = api.list_entities_limit_offset_total_count(entity:, items_key: items_key, query: query_read_delete(default: list_query))
194
194
  return Main.result_object_list(data, total: total, fields: display_fields)
195
195
  end
196
196
  data, http = api.read(entity, query_read_delete, ret: :both)
@@ -259,86 +259,9 @@ module Aspera
259
259
  return value
260
260
  end
261
261
 
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
262
  # Percent selector: select by this field for this value
340
263
  REGEX_LOOKUP_ID_BY_FIELD = /^%([^:]+):(.*)$/
341
- private_constant :PER_PAGE_DEFAULT, :REGEX_LOOKUP_ID_BY_FIELD
264
+ private_constant :REGEX_LOOKUP_ID_BY_FIELD
342
265
  end
343
266
  end
344
267
  end
@@ -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'].each_with_object({}){ |i, m| m[i['name']] = i['content']}
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
@@ -108,16 +108,17 @@ module Aspera
108
108
  end
109
109
 
110
110
  # if recipient is just an email, then convert to expected API hash : name and type
111
- def normalize_recipients(parameters)
112
- return unless parameters.key?('recipients')
113
- Aspera.assert_type(parameters['recipients'], Array){'recipients'}
111
+ def normalize_recipients(parameters, type)
112
+ type = type.to_s
113
+ return unless parameters.key?(type)
114
+ Aspera.assert_type(parameters[type], Array){type}
114
115
  recipient_types = Api::Faspex::RECIPIENT_TYPES
115
116
  if parameters.key?('recipient_types')
116
117
  recipient_types = parameters['recipient_types']
117
118
  parameters.delete('recipient_types')
118
119
  recipient_types = [recipient_types] unless recipient_types.is_a?(Array)
119
120
  end
120
- parameters['recipients'].map! do |recipient_data|
121
+ parameters[type].map! do |recipient_data|
121
122
  # If just a string, make a general lookup and build expected name/type hash
122
123
  if recipient_data.is_a?(String)
123
124
  matched = @api_v5.lookup_by_name('contacts', recipient_data, query: Rest.php_style({context: 'packages', type: recipient_types}))
@@ -157,7 +158,7 @@ module Aspera
157
158
  end
158
159
  end
159
160
 
160
- # @param [Srting] job identifier
161
+ # @param [String] job identifier
161
162
  # @return [Hash] result of API call for job status
162
163
  def wait_for_job(job_id)
163
164
  result = nil
@@ -182,13 +183,9 @@ module Aspera
182
183
  when *Api::Faspex::API_LIST_MAILBOX_TYPES then "#{box}/packages"
183
184
  else
184
185
  group_type = options.get_option(:group_type)
185
- "#{group_type}/#{lookup_entity_by_field(api: @api_v5, entity: group_type, value: box)['id']}/packages"
186
+ "#{group_type}/#{@api_v5.lookup_entity_by_field(entity: group_type, value: box)['id']}/packages"
186
187
  end
187
- list, total = list_entities_limit_offset_total_count(
188
- api: @api_v5,
189
- entity: entity,
190
- query: query_read_delete(default: query)
191
- )
188
+ list, total = @api_v5.list_entities_limit_offset_total_count(entity: entity, query: query_read_delete(default: query))
192
189
  return list.select(&filter), total
193
190
  end
194
191
 
@@ -251,7 +248,7 @@ module Aspera
251
248
  type: Api::Faspex.box_type(box),
252
249
  transfer_type: Api::Faspex::TRANSFER_CONNECT
253
250
  }
254
- # download_params[:recipient_workgroup_id] = lookup_entity_by_field(api: @api_v5, entity: options.get_option(:group_type), value: box)['id'] if !Api::Faspex::API_LIST_MAILBOX_TYPES.include?(box) && box != SpecialValues::ALL
251
+ # download_params[:recipient_workgroup_id] = @api_v5.lookup_entity_by_field(entity: options.get_option(:group_type), value: box)['id'] if !Api::Faspex::API_LIST_MAILBOX_TYPES.include?(box) && box != SpecialValues::ALL
255
252
  packages.each do |package|
256
253
  pkg_id = package['id']
257
254
  formatter.display_status("Receiving package #{pkg_id}")
@@ -286,7 +283,7 @@ module Aspera
286
283
  recipient_type: @api_v5.pub_link_context['recipient_type']
287
284
  }]
288
285
  end
289
- normalize_recipients(parameters)
286
+ PACKAGE_RECIPIENT_TYPES.each{ |type| normalize_recipients(parameters, type)}
290
287
  # User specified content prot in tspec, but faspex requires in package creation
291
288
  # `transfer_spec/upload` will set `content_protection`
292
289
  if transfer.user_transfer_spec['content_protection'] && !parameters.key?('ear_enabled')
@@ -308,8 +305,7 @@ module Aspera
308
305
  else
309
306
  # send from remote shared folder
310
307
  if (m = Base.percent_selector(shared_folder))
311
- shared_folder = lookup_entity_by_field(
312
- api: @api_v5,
308
+ shared_folder = @api_v5.lookup_entity_by_field(
313
309
  entity: 'shared_folders',
314
310
  field: m[:field],
315
311
  value: m[:value]
@@ -464,12 +460,12 @@ module Aspera
464
460
  case res_command
465
461
  when *ALL_OPS
466
462
  return entity_execute(command: res_command, **exec_args) do |field, value|
467
- lookup_entity_by_field(api: @api_v5, entity: exec_args[:entity], value: value, field: field, items_key: exec_args[:items_key], query: res_id_query)['id']
463
+ @api_v5.lookup_entity_by_field(entity: exec_args[:entity], value: value, field: field, items_key: exec_args[:items_key], query: res_id_query)['id']
468
464
  end
469
465
  when :shared_folders
470
466
  # nodes
471
467
  node_id = instance_identifier do |field, value|
472
- lookup_entity_by_field(api: @api_v5, entity: 'nodes', field: field, value: value)['id']
468
+ @api_v5.lookup_entity_by_field(entity: 'nodes', field: field, value: value)['id']
473
469
  end
474
470
  shfld_entity = "nodes/#{node_id}/shared_folders"
475
471
  sh_command = options.get_next_command(ALL_OPS + [:user])
@@ -480,33 +476,32 @@ module Aspera
480
476
  entity: shfld_entity,
481
477
  command: sh_command
482
478
  ) do |field, value|
483
- lookup_entity_by_field(api: @api_v5, entity: shfld_entity, field: field, value: value)['id']
479
+ @api_v5.lookup_entity_by_field(entity: shfld_entity, field: field, value: value)['id']
484
480
  end
485
481
  when :user
486
482
  sh_id = instance_identifier do |field, value|
487
- lookup_entity_by_field(api: @api_v5, entity: shfld_entity, field: field, value: value)['id']
483
+ @api_v5.lookup_entity_by_field(entity: shfld_entity, field: field, value: value)['id']
488
484
  end
489
485
  user_path = "#{shfld_entity}/#{sh_id}/custom_access_users"
490
486
  return entity_execute(api: @api_v5, entity: user_path, items_key: 'users') do |field, value|
491
- lookup_entity_by_field(api: @api_v5, entity: user_path, items_key: 'users', field: field, value: value)['id']
487
+ @api_v5.lookup_entity_by_field(entity: user_path, items_key: 'users', field: field, value: value)['id']
492
488
  end
493
489
 
494
490
  end
495
491
  when :browse
496
492
  # nodes
497
493
  node_id = instance_identifier do |field, value|
498
- lookup_entity_by_field(api: @api_v5, entity: 'nodes', value: value, field: field)['id']
494
+ @api_v5.lookup_entity_by_field(entity: 'nodes', value: value, field: field)['id']
499
495
  end
500
496
  return browse_folder("nodes/#{node_id}/browse")
501
497
  when :invite_external_collaborator
502
498
  # :shared_inboxes, :workgroups
503
- shared_inbox_id = instance_identifier{ |field, value| lookup_entity_by_field(api: @api_v5, entity: res_sym.to_s, field: field, value: value, query: res_id_query)['id']}
499
+ 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']}
504
500
  creation_payload = value_create_modify(command: res_command, type: [Hash, String])
505
501
  creation_payload = {'email_address' => creation_payload} if creation_payload.is_a?(String)
506
502
  result = @api_v5.create("#{res_sym}/#{shared_inbox_id}/external_collaborator", creation_payload)
507
503
  formatter.display_status(result['message'])
508
- result = lookup_entity_by_field(
509
- api: @api_v5,
504
+ result = @api_v5.lookup_entity_by_field(
510
505
  entity: "#{res_sym}/#{shared_inbox_id}/members",
511
506
  items_key: 'members',
512
507
  value: creation_payload['email_address'],
@@ -515,7 +510,7 @@ module Aspera
515
510
  return Main.result_single_object(result)
516
511
  when :members, :saml_groups
517
512
  # res_command := :shared_inboxes, :workgroups
518
- res_id = instance_identifier{ |field, value| lookup_entity_by_field(api: @api_v5, entity: res_sym.to_s, field: field, value: value, query: res_id_query)['id']}
513
+ 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']}
519
514
  res_path = "#{res_sym}/#{res_id}/#{res_command}"
520
515
  list_key = res_command.to_s
521
516
  list_key = 'groups' if res_command.eql?(:saml_groups)
@@ -526,8 +521,7 @@ module Aspera
526
521
  users = [users] unless users.is_a?(Array)
527
522
  users = users.map do |user|
528
523
  if (m = Base.percent_selector(user))
529
- lookup_entity_by_field(
530
- api: @api_v5,
524
+ @api_v5.lookup_entity_by_field(
531
525
  entity: 'accounts',
532
526
  field: m[:field],
533
527
  value: m[:value],
@@ -548,8 +542,7 @@ module Aspera
548
542
  command: sub_command,
549
543
  items_key: list_key
550
544
  ) do |field, value|
551
- lookup_entity_by_field(
552
- api: @api_v5,
545
+ @api_v5.lookup_entity_by_field(
553
546
  entity: res_path,
554
547
  field: field,
555
548
  value: value,
@@ -558,7 +551,7 @@ module Aspera
558
551
  end
559
552
  when :reset_password
560
553
  # :accounts
561
- contact_id = instance_identifier{ |field, value| lookup_entity_by_field(api: @api_v5, entity: 'accounts', field: field, value: value, query: res_id_query)['id']}
554
+ contact_id = instance_identifier{ |field, value| @api_v5.lookup_entity_by_field(entity: 'accounts', field: field, value: value, query: res_id_query)['id']}
562
555
  @api_v5.create("accounts/#{contact_id}/reset_password", {})
563
556
  return Main.result_status('password reset, user shall check email')
564
557
  end
@@ -579,16 +572,10 @@ module Aspera
579
572
  event_type = options.get_next_command(%i[application webhook])
580
573
  case event_type
581
574
  when :application
582
- list, total = list_entities_limit_offset_total_count(
583
- api: @api_v5,
584
- entity: 'application_events',
585
- query: query_read_delete
586
- )
587
-
575
+ list, total = @api_v5.list_entities_limit_offset_total_count(entity: 'application_events', query: query_read_delete)
588
576
  return Main.result_object_list(list, total: total, fields: %w[event_type created_at application user.name])
589
577
  when :webhook
590
- list, total = list_entities_limit_offset_total_count(
591
- api: @api_v5,
578
+ list, total = @api_v5.list_entities_limit_offset_total_count(
592
579
  entity: 'all_webhooks_events',
593
580
  query: query_read_delete,
594
581
  items_key: 'events'
@@ -705,7 +692,7 @@ module Aspera
705
692
  items_key: invitation_endpoint,
706
693
  display_fields: %w[id public recipient_type recipient_name email_address]
707
694
  ) do |field, value|
708
- lookup_entity_by_field(api: @api_v5, entity: invitation_endpoint, field: field, value: value, query: {})['id']
695
+ @api_v5.lookup_entity_by_field(entity: invitation_endpoint, field: field, value: value, query: {})['id']
709
696
  end
710
697
  end
711
698
  when :gateway
@@ -732,7 +719,8 @@ module Aspera
732
719
  ACCOUNT_TYPES = %w{local_user saml_user self_registered_user external_user}.freeze
733
720
  WORKGROUP_TYPES = %w{workgroup shared_inbox}.freeze
734
721
  CONTACT_TYPES = (WORKGROUP_TYPES + %w{distribution_list user external_user}).freeze
735
- private_constant :SHARED_INBOX_MEMBER_LEVELS, :ACCOUNT_TYPES, :CONTACT_TYPES
722
+ PACKAGE_RECIPIENT_TYPES = %i{recipients private_recipients notified_on_upload notified_on_download notified_on_receipt}
723
+ private_constant :SHARED_INBOX_MEMBER_LEVELS, :ACCOUNT_TYPES, :CONTACT_TYPES, :PACKAGE_RECIPIENT_TYPES
736
724
  end
737
725
  end
738
726
  end
@@ -928,10 +928,8 @@ module Aspera
928
928
  # do not process last one
929
929
  break if end_date.nil?
930
930
  # init data for this period
931
- period_bandwidth = Transfer::Spec::DIRECTION_ENUM_VALUES.map(&:to_sym).each_with_object({}) do |direction, h|
932
- h[direction] = dir_info.each_with_object({}) do |k2, h2|
933
- h2[k2] = 0
934
- end
931
+ period_bandwidth = Transfer::Spec::DIRECTION_ENUM_VALUES.map(&:to_sym).to_h do |direction|
932
+ [direction, dir_info.to_h{ |k2| [k2, 0]}]
935
933
  end
936
934
  # find all transfers that were active at this time
937
935
  transfers_data.each do |transfer|
@@ -3,6 +3,8 @@
3
3
  require 'aspera/cli/plugins/basic_auth'
4
4
  require 'aspera/cli/plugins/node'
5
5
  require 'aspera/assert'
6
+ require 'aspera/rest_list'
7
+
6
8
  module Aspera
7
9
  module Cli
8
10
  module Plugins
@@ -128,7 +130,7 @@ module Aspera
128
130
  when :admin
129
131
  api_shares_admin = basic_auth_api(ADMIN_API_PATH)
130
132
  admin_command = options.get_next_command(%i[node share transfer_settings user group].freeze)
131
- lookup_share = ->(field, value){lookup_entity_generic(entity: 'share', field: field, value: value){api_shares_admin.read('data/shares')}['id']}
133
+ lookup_share = ->(field, value){RestList.lookup_entity_generic(entity: 'share', field: field, value: value){api_shares_admin.read('data/shares')}['id']}
132
134
  case admin_command
133
135
  when :node
134
136
  return entity_execute(api: api_shares_admin, entity: 'data/nodes')
@@ -177,7 +179,7 @@ module Aspera
177
179
  entity_commands = %i[import].freeze
178
180
  end
179
181
  entity_verb = options.get_next_command(entity_commands)
180
- lookup_block = ->(field, value){lookup_entity_generic(entity: entity_type, field: field, value: value){api_shares_admin.read(entities_path)}['id']}
182
+ lookup_block = ->(field, value){RestList.lookup_entity_generic(entity: entity_type, field: field, value: value){api_shares_admin.read(entities_path)}['id']}
181
183
  case entity_verb
182
184
  when *ALL_OPS # list, show, delete, create, modify
183
185
  display_fields = entity_type.eql?(:user) ? %w[id user_id username first_name last_name email] : nil
@@ -33,6 +33,9 @@ module Aspera
33
33
  :DEFAULT_TRANSFER_NOTIFY_TEMPLATE
34
34
 
35
35
  class << self
36
+ # Analyze transfer session statuses and return a global status
37
+ #
38
+ # @param statuses [Array] list of session status, each status is :success or an error message string
36
39
  # @return [:success] if all sessions statuses returned by "start" are success
37
40
  # @return [Exception] if one sessions statuses returned by "start" is failed
38
41
  def session_status(statuses)
@@ -4,6 +4,6 @@ module Aspera
4
4
  module Cli
5
5
  # For beta add extension : .beta1
6
6
  # For dev version add extension : .pre
7
- VERSION = '4.25.4'
7
+ VERSION = '4.26.0.pre'
8
8
  end
9
9
  end