aspera-cli 4.25.4 → 4.25.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,7 +14,8 @@ require 'net/ssh/buffer'
14
14
 
15
15
  module Aspera
16
16
  module Api
17
- # Provides additional functions using node API with gen4 extensions (access keys)
17
+ # Aspera Node API client
18
+ # with gen4 extensions (access keys)
18
19
  class Node < Rest
19
20
  # Format of node scope : node.<access key>:<scope>
20
21
  module Scope
@@ -508,7 +509,7 @@ module Aspera
508
509
  # Get the transfer user from info on access key
509
510
  transfer_spec['remote_user'] = info['transfer_user'] if info['transfer_user']
510
511
  # Get settings from name.value array to hash key.value
511
- settings = info['settings']&.each_with_object({}){ |i, h| h[i['name']] = i['value']}
512
+ settings = info['settings']&.to_h{ |i| [i['name'], i['value']]}
512
513
  # Check WSS ports
513
514
  Transfer::Spec::WSS_FIELDS.each do |i|
514
515
  transfer_spec[i] = settings[i] if settings.key?(i)
@@ -520,6 +521,46 @@ module Aspera
520
521
  return transfer_spec
521
522
  end
522
523
 
524
+ # Executes `GET` call in loop using `iteration_token` (`/ops/transfers`)
525
+ # @param iteration [Array] a single element array with the iteration token or nil
526
+ # @param call_args [Hash] additional arguments to pass to `Rest.call`
527
+ # @return [Array] list of items returned by the API call
528
+ def read_with_paging(subpath, query = nil, iteration: nil, **call_args)
529
+ Aspera.assert_type(iteration, Array, NilClass){'iteration'}
530
+ Aspera.assert_type(query, Hash, NilClass){'query'}
531
+ Aspera.assert(!call_args.key?(:query))
532
+ query = {} if query.nil?
533
+ query[:iteration_token] = iteration[0] unless iteration.nil?
534
+ max = query.delete(RestList::MAX_ITEMS)
535
+ item_list = []
536
+ loop do
537
+ data, http = read(subpath, query, **call_args, ret: :both)
538
+ Aspera.assert_type(data, Array){"Expected data to be an Array, got: #{data.class}"}
539
+ # no data
540
+ break if data.empty?
541
+ # get next iteration token from link
542
+ next_iteration_token = nil
543
+ link_info = http['Link']
544
+ unless link_info.nil?
545
+ m = link_info.match(/<([^>]+)>/)
546
+ Aspera.assert(m){"Cannot parse iteration in Link: #{link_info}"}
547
+ next_iteration_token = Rest.query_to_h(URI.parse(m[1]).query)['iteration_token']
548
+ end
549
+ # same as last iteration: stop
550
+ break if next_iteration_token&.eql?(query[:iteration_token])
551
+ query[:iteration_token] = next_iteration_token
552
+ item_list.concat(data)
553
+ if max&.<=(item_list.length)
554
+ item_list = item_list.slice(0, max)
555
+ break
556
+ end
557
+ break if next_iteration_token.nil?
558
+ end
559
+ # save iteration token if needed
560
+ iteration[0] = query[:iteration_token] unless iteration.nil?
561
+ item_list
562
+ end
563
+
523
564
  private
524
565
 
525
566
  # Method called in loop for each entry for `resolve_api_fid`
@@ -83,16 +83,15 @@ module Aspera
83
83
 
84
84
  # @return [Hash] with key = file name (String), and value = path to file
85
85
  def file_paths
86
- return SDK_FILES.each_with_object({}) do |v, m|
87
- m[v.to_s] =
88
- begin
89
- path(v)
90
- rescue Errno::ENOENT => e
91
- e.message.gsub(/.*assertion failed: /, '').gsub(/\): .*/, ')')
92
- rescue => e
93
- e.message
94
- end
95
- end
86
+ return SDK_FILES.to_h do |v|
87
+ [v.to_s, begin
88
+ path(v)
89
+ rescue Errno::ENOENT => e
90
+ e.message.gsub(/.*assertion failed: /, '').gsub(/\): .*/, ')')
91
+ rescue => e
92
+ e.message
93
+ end]
94
+ end
96
95
  end
97
96
 
98
97
  # TODO: if using another product than SDK, should use files from there
@@ -9,7 +9,15 @@ module Aspera
9
9
  class MissingArgument < Error; end
10
10
  class NoSuchElement < Error; end
11
11
 
12
+ # Raised when a lookup for a specific entity fails to return exactly one result.
13
+ #
14
+ # Provides a formatted message indicating whether the entity was missing
15
+ # or if multiple matches were found (ambiguity).
12
16
  class BadIdentifier < Error
17
+ # @param res_type [String] The type of entity being looked up (e.g., 'user').
18
+ # @param res_id [String] The value of the identifier that failed.
19
+ # @param field [String] The name of the field used for lookup (defaults to 'identifier').
20
+ # @param count [Integer] The number of matches found (0 for not found, >1 for ambiguous).
13
21
  def initialize(res_type, res_id, field: 'identifier', count: 0)
14
22
  msg = count.eql?(0) ? 'not found' : "found #{count}"
15
23
  super("#{res_type} with #{field}=#{res_id}: #{msg}")
@@ -7,7 +7,8 @@ module Aspera
7
7
  CMD_NAME = 'ascli'
8
8
  # Name of the containing gem, same as in <gem name>.gemspec
9
9
  GEM_NAME = 'aspera-cli'
10
- DOC_URL = "https://www.rubydoc.info/gems/#{GEM_NAME}"
10
+ DOC_URL = 'https://ibm.biz/ascli-doc'
11
+ RUBYDOC_URL = "https://www.rubydoc.info/gems/#{GEM_NAME}"
11
12
  GEM_URL = "https://rubygems.org/gems/#{GEM_NAME}"
12
13
  SRC_URL = 'https://github.com/IBM/aspera-cli'
13
14
  CONTAINER = 'docker.io/martinlaurent/ascli'
@@ -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).to_sym
522
+ if @declared_options.key?(option_sym)
523
+ # it's a known option, so let's process it
524
+ set_option(option_sym, DotContainer.dotted_to_container(path, smart_convert(value), get_option(option_sym)), where: 'dotted')
525
+ # resume to next
526
+ retry
527
+ end
525
528
  end
526
529
  end
527
- # 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
 
@@ -14,10 +14,6 @@ module Aspera
14
14
  INSTANCE_OPS = %i[modify delete show].freeze
15
15
  # All standard operations (create list modify delete show)
16
16
  ALL_OPS = (GLOBAL_OPS + INSTANCE_OPS).freeze
17
- # Special query parameter: `max`: max number of items for list command
18
- MAX_ITEMS = 'max'
19
- # Special query parameter: `pmax`: max number of pages for list command
20
- MAX_PAGES = 'pmax'
21
17
 
22
18
  class << self
23
19
  def declare_options(options)
@@ -138,7 +134,6 @@ module Aspera
138
134
  # @param delete_style [String] If set, the delete operation by array in payload
139
135
  # @param id_as_arg [String] If set, the id is provided as url argument ?<id_as_arg>=<id>
140
136
  # @param is_singleton [Boolean] If `true`, entity is the full path to the resource
141
- # @param tclo [Boolean] If `true`, :list use paging with total_count, limit, offset
142
137
  # @param block [Proc] Block to search for identifier based on attribute value
143
138
  # @return [Hash] Result suitable for CLI result
144
139
  def entity_execute(
@@ -151,7 +146,6 @@ module Aspera
151
146
  id_as_arg: false,
152
147
  is_singleton: false,
153
148
  list_query: nil,
154
- tclo: false,
155
149
  &block
156
150
  )
157
151
  command = options.get_next_command(ALL_OPS) if command.nil?
@@ -189,10 +183,6 @@ module Aspera
189
183
  when :show
190
184
  return Main.result_single_object(api.read(one_res_path), fields: display_fields)
191
185
  when :list
192
- if tclo
193
- data, total = list_entities_limit_offset_total_count(api: api, entity:, items_key: items_key, query: query_read_delete(default: list_query))
194
- return Main.result_object_list(data, total: total, fields: display_fields)
195
- end
196
186
  data, http = api.read(entity, query_read_delete, ret: :both)
197
187
  return Main.result_empty if http.code == '204'
198
188
  # TODO: not generic : which application is this for ?
@@ -259,86 +249,9 @@ module Aspera
259
249
  return value
260
250
  end
261
251
 
262
- # Get a (full or partial) list of all entities of a given type with query: offset/limit
263
- # @param api [Rest] API object
264
- # @param entity [String,Symbol] API endpoint of entity to list
265
- # @param items_key [String] Key in the result to get the list of items (Default: same as `entity`)
266
- # @param query [Hash,nil] Additional query parameters
267
- # @return [Array<(Array<Hash>, Integer)>] items, total_count
268
- def list_entities_limit_offset_total_count(
269
- api:,
270
- entity:,
271
- items_key: nil,
272
- query: nil
273
- )
274
- entity = entity.to_s if entity.is_a?(Symbol)
275
- items_key = entity.split('/').last if items_key.nil?
276
- query = {} if query.nil?
277
- Aspera.assert_type(entity, String)
278
- Aspera.assert_type(items_key, String)
279
- Aspera.assert_type(query, Hash)
280
- Log.log.debug{"list_entities t=#{entity} k=#{items_key} q=#{query}"}
281
- result = []
282
- offset = 0
283
- max_items = query.delete(MAX_ITEMS)
284
- remain_pages = query.delete(MAX_PAGES)
285
- # Merge default parameters, by default 100 per page
286
- query = {'limit'=> PER_PAGE_DEFAULT}.merge(query)
287
- total_count = nil
288
- loop do
289
- query['offset'] = offset
290
- page_result = api.read(entity, query)
291
- Aspera.assert_type(page_result[items_key], Array)
292
- result.concat(page_result[items_key])
293
- # Reach the limit set by user ?
294
- if !max_items.nil? && (result.length >= max_items)
295
- result = result.slice(0, max_items)
296
- break
297
- end
298
- total_count ||= page_result['total_count']
299
- break if result.length >= total_count
300
- remain_pages -= 1 unless remain_pages.nil?
301
- break if remain_pages == 0
302
- offset += page_result[items_key].length
303
- RestParameters.instance.spinner_cb.call("#{result.length} / #{total_count || '?'}")
304
- end
305
- RestParameters.instance.spinner_cb.call(action: :success)
306
- return result, total_count
307
- end
308
-
309
- # Lookup an entity id from its name.
310
- # Uses query `q` if `query` is `:default` and `field` is `name`.
311
- # @param entity [String] Type of entity to lookup, by default it is the path, and it is also the field name in result
312
- # @param value [String] Value to lookup
313
- # @param field [String] Field to match, by default it is `'name'`
314
- # @param items_key [String] Key in the result to get the list of items (override entity)
315
- # @param query [Hash] Additional query parameters (Default: `:default`)
316
- def lookup_entity_by_field(api:, entity:, value:, field: 'name', items_key: nil, query: :default)
317
- if query.eql?(:default)
318
- Aspera.assert(field.eql?('name')){'Default query is on name only'}
319
- query = {'q'=> value}
320
- end
321
- lookup_entity_generic(entity: entity, field: field, value: value){list_entities_limit_offset_total_count(api: api, entity: entity, items_key: items_key, query: query).first}
322
- end
323
-
324
- # Lookup entity by field and value. Extract single result from list of result returned by block.
325
- # @param entity [String] Type of entity to lookup, by default it is the path, and it is also the field name in result
326
- # @param value [String] Value to lookup
327
- # @param field [String] Field to match, by default it is `'name'`
328
- # @param block [Proc] Get list of entity matching query.
329
- def lookup_entity_generic(entity:, value:, field: 'name', &block)
330
- Aspera.assert(block_given?)
331
- found = yield
332
- Aspera.assert_array_all(found, Hash)
333
- found = found.select{ |i| i[field].eql?(value)}
334
- return found.first if found.length.eql?(1)
335
- raise Cli::BadIdentifier.new(entity, value, field: field, count: found.length)
336
- end
337
-
338
- PER_PAGE_DEFAULT = 1000
339
252
  # Percent selector: select by this field for this value
340
253
  REGEX_LOOKUP_ID_BY_FIELD = /^%([^:]+):(.*)$/
341
- private_constant :PER_PAGE_DEFAULT, :REGEX_LOOKUP_ID_BY_FIELD
254
+ private_constant :REGEX_LOOKUP_ID_BY_FIELD
342
255
  end
343
256
  end
344
257
  end
@@ -9,6 +9,7 @@ require 'aspera/cli/transfer_agent'
9
9
  require 'aspera/transfer/uri'
10
10
  require 'aspera/transfer/spec'
11
11
  require 'aspera/persistency_action_once'
12
+ require 'aspera/rest_list'
12
13
  require 'aspera/environment'
13
14
  require 'aspera/nagios'
14
15
  require 'aspera/id_generator'
@@ -31,7 +32,7 @@ module Aspera
31
32
  # allowed parameters for inbox.atom
32
33
  ATOM_PARAMS = %w[page count startIndex].freeze
33
34
  # with special parameters (from Plugin class) : max and pmax (from Plugin)
34
- ATOM_EXT_PARAMS = [MAX_ITEMS, MAX_PAGES].concat(ATOM_PARAMS).freeze
35
+ ATOM_EXT_PARAMS = [RestList::MAX_ITEMS, RestList::MAX_PAGES].concat(ATOM_PARAMS).freeze
35
36
  # sub path in url for public link delivery
36
37
  PUB_LINK_EXTERNAL_MATCH = 'external_deliveries/'
37
38
  STANDARD_PATH = '/aspera/faspex'
@@ -176,10 +177,9 @@ module Aspera
176
177
  Aspera.assert_type(mailbox_query, Hash){'query'}
177
178
  Aspera.assert((mailbox_query.keys - ATOM_EXT_PARAMS).empty?){"query: supported params: #{ATOM_EXT_PARAMS}"}
178
179
  Aspera.assert(!(mailbox_query.key?('startIndex') && mailbox_query.key?('page'))){'query: startIndex and page are exclusive'}
179
- max_items = mailbox_query[MAX_ITEMS]
180
- mailbox_query.delete(MAX_ITEMS)
181
- max_pages = mailbox_query[MAX_PAGES]
182
- mailbox_query.delete(MAX_PAGES)
180
+ # Extract pagination control parameters (not part of API query)
181
+ max_items = mailbox_query.delete(RestList::MAX_ITEMS)
182
+ max_pages = mailbox_query.delete(RestList::MAX_PAGES)
183
183
  end
184
184
  loop do
185
185
  # get a batch of package information
@@ -211,7 +211,7 @@ module Aspera
211
211
  end
212
212
  # add special key
213
213
  package['items'] = package['link'].is_a?(Array) ? package['link'].length : 0
214
- package['metadata'] = package['metadata']['field'].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