aspera-cli 4.24.0 → 4.24.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.
@@ -378,7 +378,7 @@ module Aspera
378
378
  :invalid_characters, 'Replacement character and invalid filename characters',
379
379
  handler: {o: Environment.instance, m: :file_illegal_characters}
380
380
  )
381
- @context.options.declare(:log_level, 'Log level', values: Log.levels, handler: {o: Log.instance, m: :level})
381
+ @context.options.declare(:log_level, 'Log level', values: Log::LEVELS, handler: {o: Log.instance, m: :level})
382
382
  @context.options.declare(:log_format, 'Log formatter', types: [Proc, Logger::Formatter, String], handler: {o: Log.instance, m: :formatter})
383
383
  @context.options.declare(:logger, 'Logging method', values: Log::LOG_TYPES, handler: {o: Log.instance, m: :logger_type})
384
384
  @context.options.declare(:lock_port, 'Prevent dual execution of a command, e.g. in cron', coerce: Integer, types: Integer)
@@ -473,6 +473,19 @@ module Aspera
473
473
  return result
474
474
  end
475
475
 
476
+ def defaults_set(plugin_name, preset_name, preset_values, option_default, option_override)
477
+ @config_presets[CONF_PRESET_DEFAULTS] ||= {}
478
+ raise Cli::Error, "A default configuration already exists for plugin '#{plugin_name}' (use --override=yes or --default=no)" \
479
+ if !option_override && option_default && @config_presets[CONF_PRESET_DEFAULTS].key?(plugin_name)
480
+ raise Cli::Error, "Preset already exists: #{preset_name} (use --override=yes or provide alternate name on command line)" \
481
+ if !option_override && @config_presets.key?(preset_name)
482
+ if option_default
483
+ formatter.display_status("Setting config preset as default for #{plugin_name}")
484
+ @config_presets[CONF_PRESET_DEFAULTS][plugin_name.to_s] = preset_name
485
+ end
486
+ @config_presets[preset_name] = preset_values
487
+ end
488
+
476
489
  def set_preset_key(preset, param_name, param_value)
477
490
  Aspera.assert_values(param_name.class, [String, Symbol]){'parameter'}
478
491
  param_name = param_name.to_s
@@ -505,9 +518,9 @@ module Aspera
505
518
  attr_reader :gem_url
506
519
  attr_accessor :option_config_file
507
520
 
508
- # @return the hash from name (also expands possible includes)
509
521
  # @param config_name name of the preset in config file
510
522
  # @param include_path used to detect and avoid include loops
523
+ # @return copy of the hash from name (also expands possible includes)
511
524
  def preset_by_name(config_name, include_path = [])
512
525
  raise Cli::Error, 'loop in include' if include_path.include?(config_name)
513
526
  include_path = include_path.clone # avoid messing up if there are multiple branches
@@ -113,13 +113,13 @@ module Aspera
113
113
  raise "Username shall be an email in Faspex: #{wiz_username}" if !(wiz_username =~ /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i)
114
114
  if options.get_option(:client_id).nil? || options.get_option(:client_secret).nil?
115
115
  formatter.display_status('Ask the ascli client id and secret to your Administrator.'.red)
116
- formatter.display_status("Admin should login to: #{instance_url}")
116
+ formatter.display_status("Log in as an admin user at: #{instance_url}")
117
117
  Environment.instance.open_uri(instance_url)
118
118
  formatter.display_status('Navigate to: 𓃑 → Admin → Configurations → API clients')
119
119
  formatter.display_status('Create an API client with:')
120
120
  formatter.display_status('- name: ascli')
121
121
  formatter.display_status('- JWT: enabled')
122
- formatter.display_status("Then, logged in as #{wiz_username.red} go to your profile:")
122
+ formatter.display_status("Log in as user #{wiz_username.red}. Navigate to your profile:")
123
123
  formatter.display_status('👤 → Account Settings → Preferences → Public Key in PEM:')
124
124
  formatter.display_status(pub_key_pem)
125
125
  formatter.display_status('Once set, fill in the parameters:')
@@ -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.24.0'
7
+ VERSION = '4.24.1'
8
8
  end
9
9
  end
@@ -28,6 +28,10 @@ module Aspera
28
28
  @parent.formatter
29
29
  end
30
30
 
31
+ def config
32
+ @parent.config
33
+ end
34
+
31
35
  # Find a plugin, and issue the "require"
32
36
  # @return [Hash] plugin info: { product:, name:, url:, version: }
33
37
  def identify_plugins_for_url
@@ -68,6 +72,8 @@ module Aspera
68
72
  return found_apps
69
73
  end
70
74
 
75
+ # Wizard function, creates configuration
76
+ # @param apps [Array] list of detected apps
71
77
  def find(apps)
72
78
  identification = if apps.length.eql?(1)
73
79
  Log.log.debug{"Detected: #{identification}"}
@@ -101,7 +107,7 @@ module Aspera
101
107
  private_key_path = options.get_option(:key_path)
102
108
  # give a chance to provide
103
109
  if private_key_path.nil?
104
- formatter.display_status('Please provide the path to your private RSA key, or nothing to generate one:')
110
+ formatter.display_status('Path to private RSA key (leave empty to generate):')
105
111
  private_key_path = options.get_option(:key_path, mandatory: true).to_s
106
112
  end
107
113
  # else generate path
@@ -138,20 +144,11 @@ module Aspera
138
144
  # Write configuration file
139
145
  formatter.display_status("Preparing preset: #{wiz_preset_name}")
140
146
  # init defaults if necessary
141
- @config_presets[CONF_PRESET_DEFAULTS] ||= {}
142
147
  option_override = options.get_option(:override, mandatory: true)
143
- raise Cli::Error, "A default configuration already exists for plugin '#{identification[:product]}' (use --override=yes or --default=no)" \
144
- if !option_override && options.get_option(:default, mandatory: true) && @config_presets[CONF_PRESET_DEFAULTS].key?(identification[:product])
145
- raise Cli::Error, "Preset already exists: #{wiz_preset_name} (use --override=yes or --id=<name>)" \
146
- if !option_override && @config_presets.key?(wiz_preset_name)
147
- @config_presets[wiz_preset_name] = wizard_result[:preset_value].stringify_keys
148
+ option_default = options.get_option(:default, mandatory: true)
149
+ config.defaults_set(identification[:product], wiz_preset_name, wizard_result[:preset_value].stringify_keys, option_default, option_override)
148
150
  test_args = wizard_result[:test_args]
149
- if options.get_option(:default, mandatory: true)
150
- formatter.display_status("Setting config preset as default for #{identification[:product]}")
151
- @config_presets[CONF_PRESET_DEFAULTS][identification[:product].to_s] = wiz_preset_name
152
- else
153
- test_args = "-P#{wiz_preset_name} #{test_args}"
154
- end
151
+ test_args = "-P#{wiz_preset_name} #{test_args}" unless option_default
155
152
  # TODO: actually test the command
156
153
  return Main.result_status("You can test with:\n#{Info::CMD_NAME} #{identification[:product]} #{test_args}")
157
154
  end
@@ -311,11 +311,12 @@ module Aspera
311
311
  # @return [String] A file name safe to use on file system
312
312
  def sanitized_filename(filename)
313
313
  safe_char = safe_filename_character
314
- # Windows does not allow file name ending with space or dot
315
- # nor control characters anywhere.
314
+ # Windows does not allow file name:
315
+ # - with control characters anywhere
316
+ # - ending with space or dot
316
317
  filename = filename
317
- .gsub(/[\. ]+$/, safe_char)
318
318
  .gsub(/[\x00-\x1F\x7F]/, safe_char)
319
+ .sub(/[. ]+\z/, safe_char)
319
320
  if @file_illegal_characters&.size.to_i >= 2
320
321
  # replace all illegal characters with safe_char
321
322
  filename = filename.tr(@file_illegal_characters[1..-1], safe_char)
data/lib/aspera/log.rb CHANGED
@@ -17,12 +17,17 @@ $VERBOSE = nil
17
17
  class Logger
18
18
  # Two additionnal trace levels
19
19
  TRACE_MAX = 2
20
+
20
21
  # Add custom level to logger severity, below debug level
21
22
  module Severity
22
23
  1.upto(TRACE_MAX).each{ |level| const_set(:"TRACE#{level}", - level)}
23
24
  end
24
- # Quick access to label
25
+
26
+ # Hash : key: log level int, value: uppercase log level label
25
27
  SEVERITY_LABEL = Severity.constants.each_with_object({}){ |name, hash| hash[Severity.const_get(name)] = name}
28
+
29
+ # @param severity [Integer] Log severity as int
30
+ # @return [String] Log severity upper case label
26
31
  def format_severity(severity)
27
32
  SEVERITY_LABEL[severity] || 'ANY'
28
33
  end
@@ -51,34 +56,23 @@ module Aspera
51
56
 
52
57
  # Where logs are sent to
53
58
  LOG_TYPES = %i[stderr stdout syslog].freeze
54
- DEFAULT_FORMATTER = ->(s, _d, _p, m){"#{Log.color_level(s)} #{m}\n"}
55
- FORMATTERS = {
56
- standard: Logger::Formatter.new,
57
- default: DEFAULT_FORMATTER
58
- }.freeze
59
+
60
+ # Levels are :trace2,:trace1,:debug,:info,:warn,:error,fatal,:unknown
61
+ LEVELS = Logger::Severity.constants.sort{ |a, b| Logger::Severity.const_get(a) <=> Logger::Severity.const_get(b)}.map{ |c| c.downcase.to_sym}.freeze
62
+
59
63
  # Class methods
60
64
  class << self
61
- def color_level(level)
62
- case level
63
- when :TRACE2 then 'TR2'.dim
64
- when :TRACE1 then 'TR1'.blue
65
- when :DEBUG then 'DBG'.cyan
66
- when :INFO then 'INF'.green
67
- when :WARN then 'WRN'.bg_brown.black
68
- when :ERROR then 'ERR'.bg_red.blink
69
- when :FATAL then 'FTL'.magenta
70
- when :UNKNOWN then 'UKN'.blink
71
- else Aspera.error_unexpected_value(level){'log level'}
72
- end
65
+ # Applies the provided list of string decoration (colors) to the value.
66
+ # @param value [String] Value to enhance.
67
+ # @param colors [Array(Symbol)] List of decorations
68
+ def apply_colors(value, colors)
69
+ colors.inject(value){ |s, c| s.send(c)}
73
70
  end
74
71
 
75
- # levels are :debug,:info,:warn,:error,fatal,:unknown
76
- def levels; Logger::Severity.constants.sort{ |a, b| Logger::Severity.const_get(a) <=> Logger::Severity.const_get(b)}.map{ |c| c.downcase.to_sym}; end
77
-
78
- # get the logger object of singleton
72
+ # Get the logger object of singleton
79
73
  def log; instance.logger; end
80
74
 
81
- # Dump object (`Hash`) using specified level
75
+ # Dump object (`Hash`) to log using specified level
82
76
  #
83
77
  # @param name [String, Symbol] Name of object dumped
84
78
  # @param object [Hash, nil] Data to dump
@@ -90,14 +84,16 @@ module Aspera
90
84
  instance.logger.send(level, obj_dump(name, object))
91
85
  end
92
86
 
87
+ # @return [String] Dump of object
93
88
  def obj_dump(name, object)
94
- dump_text = case instance.dump_format
95
- when :json
96
- JSON.pretty_generate(object) rescue PP.pp(object, +'')
97
- when :ruby
98
- PP.pp(object, +'')
99
- else error_unexpected_value(instance.dump_format){'dump format'}
100
- end
89
+ dump_text =
90
+ case instance.dump_format
91
+ when :json
92
+ JSON.pretty_generate(object) rescue PP.pp(object, +'')
93
+ when :ruby
94
+ PP.pp(object, +'')
95
+ else error_unexpected_value(instance.dump_format){'dump format'}
96
+ end
101
97
  "#{name.to_s.green} (#{instance.dump_format})=\n#{dump_text}"
102
98
  end
103
99
 
@@ -121,6 +117,7 @@ module Aspera
121
117
  end
122
118
 
123
119
  # Set log level of underlying logger given symbol level
120
+ # @param new_level [Symbol] One of LEVELS
124
121
  def level=(new_level)
125
122
  @logger.level = Logger::Severity.const_get(new_level.to_sym.upcase)
126
123
  end
@@ -141,6 +138,7 @@ module Aspera
141
138
  end
142
139
 
143
140
  # Get symbol of debug level of underlying logger
141
+ # @return [Symbol] One of LEVELS
144
142
  def level
145
143
  Logger::Severity.constants.each do |name|
146
144
  return name.downcase.to_sym if @logger.level.eql?(Logger::Severity.const_get(name))
@@ -188,5 +186,30 @@ module Aspera
188
186
  # This sets @logger and @logger_type (self needed to call method instead of local var)
189
187
  self.logger_type = @logger_type
190
188
  end
189
+
190
+ # Define decoration of levels
191
+ LVL_DECO = {
192
+ TRACE2: %i{dim},
193
+ TRACE1: %i{blue},
194
+ DEBUG: %i{cyan},
195
+ INFO: %i{green},
196
+ WARN: %i{bg_brown black},
197
+ ERROR: %i{bg_red blink},
198
+ FATAL: %i{magenta},
199
+ UNKNOWN: %i{blink}
200
+ }.freeze
201
+
202
+ # Short levels with color
203
+ LVL_COLOR = LVL_DECO.map{ |k, v| [k, apply_colors("#{k[..2]}#{k[-1]}", v)]}.to_h.freeze
204
+
205
+ DEFAULT_FORMATTER = ->(s, _d, _p, m){"#{LVL_COLOR[s]} #{m}\n"}
206
+
207
+ # pre-defined formatters
208
+ FORMATTERS = {
209
+ standard: Logger::Formatter.new,
210
+ default: DEFAULT_FORMATTER
211
+ }.freeze
212
+
213
+ private_constant :LVL_DECO, :LVL_COLOR, :DEFAULT_FORMATTER, :FORMATTERS
191
214
  end
192
215
  end
@@ -25,19 +25,19 @@ module Aspera
25
25
 
26
26
  class << self
27
27
  # Set `remote_dir` in sync parameters based on transfer spec
28
- # @param params [Hash] sync parameters, old or new format
28
+ # @param params [Hash] Sync parameters, old or new format
29
29
  # @param remote_dir_key [String] key to update in above hash
30
30
  # @param transfer_spec [Hash] transfer spec
31
- def update_remote_dir(sync_params, remote_dir_key, transfer_spec)
31
+ def update_remote_dir(params, remote_dir_key, transfer_spec)
32
32
  if transfer_spec.dig(*%w[tags aspera node file_id])
33
33
  # in AoC, use gen4
34
- sync_params[remote_dir_key] = '/'
34
+ params[remote_dir_key] = '/'
35
35
  elsif transfer_spec['cookie']&.start_with?('aspera.shares2')
36
36
  # TODO : something more generic, independent of Shares
37
37
  # in Shares, the actual folder on remote end is not always the same as the name of the share
38
38
  remote_key = transfer_spec['direction'].eql?('send') ? 'destination' : 'source'
39
39
  actual_remote = transfer_spec['paths']&.first&.[](remote_key)
40
- sync_params[remote_dir_key] = actual_remote if actual_remote
40
+ params[remote_dir_key] = actual_remote if actual_remote
41
41
  end
42
42
  nil
43
43
  end
@@ -70,34 +70,36 @@ module Aspera
70
70
  end
71
71
 
72
72
  # Get symbol of sync direction, defaulting to :push
73
- # @param params [Hash] sync parameters, old or new format
73
+ # @param params [Hash] Sync parameters, old or new format
74
74
  # @return [Symbol] direction symbol, one of :push, :pull, :bidi
75
75
  def direction_sym(params)
76
76
  (params['direction'] || DEFAULT_DIRECTION).to_sym
77
77
  end
78
78
 
79
79
  # Start the sync process
80
- # @param sync_params [Hash] sync parameters, old or new format
80
+ # @param params [Hash] Sync parameters, old or new format
81
+ # @param opt_ts [Hash] Optional transfer spec
81
82
  # @param &block [nil, Proc] block to generate transfer spec, takes: direction (one of DIRECTIONS), local_dir, remote_dir
82
- def start(sync_params, opt_ts = nil)
83
- Log.dump(:sync_params_initial, sync_params)
84
- Aspera.assert_type(sync_params, Hash)
83
+ def start(params, opt_ts = nil)
84
+ Log.dump(:sync_params_initial, params)
85
+ Aspera.assert_type(params, Hash)
86
+ Aspera.assert(PARAM_KEYS.any?{ |k| params.key?(k)}, type: Error){'At least one of `local` or `sessions` must be present in async parameters'}
85
87
  env_args = {
86
88
  args: [],
87
89
  env: {}
88
90
  }
89
- if sync_params.key?('local')
91
+ if params.key?('local')
90
92
  # "conf" format
91
- Aspera.assert_type(sync_params['local'], Hash){'local'}
92
- remote = sync_params['remote']
93
+ Aspera.assert_type(params['local'], Hash){'local'}
94
+ remote = params['remote']
93
95
  Aspera.assert_type(remote, Hash){'remote'}
94
96
  Aspera.assert_type(remote['path'], String){'remote path'}
95
97
  # get transfer spec if possible, and feed back to new structure
96
98
  if block_given?
97
- transfer_spec = yield(direction_sym(sync_params), sync_params['local']['path'], remote['path'])
99
+ transfer_spec = yield(direction_sym(params), params['local']['path'], remote['path'])
98
100
  Log.dump(:auth_ts, transfer_spec)
99
101
  transfer_spec.deep_merge!(opt_ts) unless opt_ts.nil?
100
- tspec_to_sync_info(transfer_spec, sync_params, CONF_SCHEMA)
102
+ tspec_to_sync_info(transfer_spec, params, CONF_SCHEMA)
101
103
  update_remote_dir(remote, 'path', transfer_spec)
102
104
  end
103
105
  remote['connect_mode'] ||= transfer_spec['wss_enabled'] ? 'ws' : 'ssh'
@@ -107,18 +109,18 @@ module Aspera
107
109
  remote['private_key_paths'].concat(add_certificates)
108
110
  end
109
111
  # '--exclusive-mgmt-port=12345', '--arg-err-path=-',
110
- env_args[:args] = ["--conf64=#{Base64.strict_encode64(JSON.generate(sync_params))}"]
111
- Log.dump(:sync_conf, sync_params)
112
+ env_args[:args] = ["--conf64=#{Base64.strict_encode64(JSON.generate(params))}"]
113
+ Log.dump(:sync_conf, params)
112
114
  agent = Agent::Direct.new
113
115
  agent.start_and_monitor_process(session: {}, name: :async, **env_args)
114
- elsif sync_params.key?('sessions')
116
+ else
115
117
  # "args" format
116
118
  raise StandardError, "Only 'sessions', and optionally 'instance' keys are allowed" unless
117
- sync_params.keys.push('instance').uniq.sort.eql?(CMDLINE_PARAMS_KEYS)
118
- Aspera.assert_type(sync_params['sessions'], Array)
119
- Aspera.assert_type(sync_params['sessions'].first, Hash)
119
+ params.keys.push('instance').uniq.sort.eql?(CMDLINE_PARAMS_KEYS)
120
+ Aspera.assert_type(params['sessions'], Array)
121
+ Aspera.assert_type(params['sessions'].first, Hash)
120
122
  if block_given?
121
- sync_params['sessions'].each do |session|
123
+ params['sessions'].each do |session|
122
124
  Aspera.assert_type(session['local_dir'], String){'local_dir'}
123
125
  Aspera.assert_type(session['remote_dir'], String){'remote_dir'}
124
126
  transfer_spec = yield(direction_sym(session), session['local_dir'], session['remote_dir'])
@@ -129,13 +131,13 @@ module Aspera
129
131
  update_remote_dir(session, 'remote_dir', transfer_spec)
130
132
  end
131
133
  end
132
- if sync_params.key?('instance')
133
- Aspera.assert_type(sync_params['instance'], Hash)
134
- instance_builder = CommandLineBuilder.new(sync_params['instance'], INSTANCE_SCHEMA, CommandLineConverter)
134
+ if params.key?('instance')
135
+ Aspera.assert_type(params['instance'], Hash)
136
+ instance_builder = CommandLineBuilder.new(params['instance'], INSTANCE_SCHEMA, CommandLineConverter)
135
137
  instance_builder.process_params
136
138
  instance_builder.add_env_args(env_args)
137
139
  end
138
- sync_params['sessions'].each do |session_params|
140
+ params['sessions'].each do |session_params|
139
141
  Aspera.assert_type(session_params, Hash)
140
142
  Aspera.assert(session_params.key?('name')){'session must contain at least: name'}
141
143
  session_builder = CommandLineBuilder.new(session_params, SESSION_SCHEMA, CommandLineConverter)
@@ -143,8 +145,6 @@ module Aspera
143
145
  session_builder.add_env_args(env_args)
144
146
  end
145
147
  Environment.secure_execute(exec: Ascp::Installation.instance.path(:async), **env_args)
146
- else
147
- raise Error, 'At least one of `local` or `sessions` must be present in async parameters'
148
148
  end
149
149
  return
150
150
  end
@@ -168,23 +168,24 @@ module Aspera
168
168
  end
169
169
 
170
170
  # Run `asyncadmin` to get status of sync session
171
- # @param sync_params [Hash] sync parameters in conf or args format
171
+ # @param params [Hash] sync parameters in conf or args format
172
172
  # @return [Hash] parsed output of asyncadmin
173
- def admin_status(sync_params)
173
+ def admin_status(params)
174
+ Aspera.assert(PARAM_KEYS.any?{ |k| params.key?(k)}, type: Error){'At least one of `local` or `sessions` must be present in async parameters'}
174
175
  arguments = ['--quiet']
175
- if sync_params.key?('local')
176
+ if params.key?('local')
176
177
  # "conf" format
177
- arguments.push("--name=#{sync_params['name']}")
178
- if sync_params.key?('local_db_dir')
179
- arguments.push("--local-db-dir=#{sync_params['local_db_dir']}")
180
- elsif sync_params.dig('local', 'path')
181
- arguments.push("--local-dir=#{sync_params.dig('local', 'path')}")
178
+ arguments.push("--name=#{params['name']}")
179
+ if params.key?('local_db_dir')
180
+ arguments.push("--local-db-dir=#{params['local_db_dir']}")
181
+ elsif params.dig('local', 'path')
182
+ arguments.push("--local-dir=#{params.dig('local', 'path')}")
182
183
  else
183
184
  raise Error, 'Missing either local_db_dir or local.path'
184
185
  end
185
- elsif sync_params.key?('sessions')
186
+ else
186
187
  # "args" format
187
- session = sync_params['sessions'].first
188
+ session = params['sessions'].first
188
189
  arguments.push("--name=#{session['name']}")
189
190
  if session.key?('local_db_dir')
190
191
  arguments.push("--local-db-dir=#{session['local_db_dir']}")
@@ -193,30 +194,28 @@ module Aspera
193
194
  else
194
195
  raise Error, 'Missing either local_db_dir or local_dir'
195
196
  end
196
- else
197
- raise Error, 'At least one of `local` or `sessions` must be present in async parameters'
198
197
  end
199
198
  stdout = Environment.secure_capture(exec: ASYNC_ADMIN_EXECUTABLE, args: arguments)
200
199
  return parse_status(stdout)
201
200
  end
202
201
 
203
- # Find the local database folder based on sync_params
204
- # @param sync_params [Hash] sync parameters in conf or args format
205
- # @param exception [Bool] Raise exception in case of problem, else return nil
202
+ # Find the local database folder based on params
203
+ # @param params [Hash] sync parameters in conf or args format
206
204
  # @return [String, nil] path to "local DB dir", i.e. folder that contains folders that contain snap.db
207
- def local_db_folder(sync_params, exception: true)
208
- if sync_params.key?('local')
205
+ def local_db_folder(params)
206
+ Aspera.assert(PARAM_KEYS.any?{ |k| params.key?(k)}, type: Error){'At least one of `local` or `sessions` must be present in async parameters'}
207
+ if params.key?('local')
209
208
  # "conf" format
210
- if sync_params.key?('local_db_dir')
211
- return sync_params['local_db_dir']
212
- elsif (local_path = sync_params.dig('local', 'path'))
209
+ if params.key?('local_db_dir')
210
+ return params['local_db_dir']
211
+ elsif (local_path = params.dig('local', 'path'))
213
212
  return local_path
214
213
  elsif exception
215
214
  raise Error, 'Missing either local_db_dir or local.path'
216
215
  end
217
- elsif sync_params.key?('sessions')
216
+ else
218
217
  # "args" format
219
- session = sync_params['sessions'].first
218
+ session = params['sessions'].first
220
219
  if session.key?('local_db_dir')
221
220
  return session['local_db_dir']
222
221
  elsif session.key?('local_dir')
@@ -224,26 +223,23 @@ module Aspera
224
223
  elsif exception
225
224
  raise Error, 'Missing either local_db_dir or local_dir'
226
225
  end
227
- elsif exception
228
- raise Error, 'At least one of `local` or `sessions` must be present in async parameters'
229
226
  end
230
227
  nil
231
228
  end
232
229
 
233
- def session_name(sync_params)
234
- if sync_params.key?('local')
230
+ def session_name(params)
231
+ Aspera.assert(PARAM_KEYS.any?{ |k| params.key?(k)}, type: Error){'At least one of `local` or `sessions` must be present in async parameters'}
232
+ if params.key?('local')
235
233
  # "conf" format
236
- return sync_params['name']
237
- elsif sync_params.key?('sessions')
238
- # "args" format
239
- return sync_params['sessions'].first['name']
234
+ return params['name']
240
235
  else
241
- raise Error, 'At least one of `local` or `sessions` must be present in async parameters'
236
+ # "args" format
237
+ return params['sessions'].first['name']
242
238
  end
243
239
  end
244
240
 
245
- def session_db_file(sync_params)
246
- db_file = File.join(local_db_folder(sync_params), PRIVATE_FOLDER, session_name(sync_params), ASYNC_DB)
241
+ def session_db_file(params)
242
+ db_file = File.join(local_db_folder(params), PRIVATE_FOLDER, session_name(params), ASYNC_DB)
247
243
  Aspera.assert(File.exist?(db_file)){"Database file #{db_file} does not exist"}
248
244
  db_file
249
245
  end
@@ -292,8 +288,9 @@ module Aspera
292
288
  ASYNC_ADMIN_EXECUTABLE = 'asyncadmin'
293
289
  PRIVATE_FOLDER = '.private-asp'
294
290
  ASYNC_DB = 'snap.db'
291
+ PARAM_KEYS = %w[local sessions].freeze
295
292
 
296
- private_constant :INSTANCE_SCHEMA, :SESSION_SCHEMA, :CONF_SCHEMA, :CMDLINE_PARAMS_KEYS, :ASYNC_ADMIN_EXECUTABLE, :PRIVATE_FOLDER, :ASYNC_DB
293
+ private_constant :INSTANCE_SCHEMA, :SESSION_SCHEMA, :CONF_SCHEMA, :CMDLINE_PARAMS_KEYS, :ASYNC_ADMIN_EXECUTABLE, :PRIVATE_FOLDER, :ASYNC_DB, :PARAM_KEYS
297
294
  end
298
295
  end
299
296
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aspera-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.24.0
4
+ version: 4.24.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Laurent Martin
@@ -37,7 +37,7 @@ cert_chain:
37
37
  eTf9kxhVM40wGQOECVNA8UsEEZHD48eF+csUYZtAJOF5oxTI8UyV9T/o6CgO0c9/
38
38
  Gzz+Qm5ULOUcPiJLjSpaiTrkiIVYiDGnqNSr6R1Hb1c=
39
39
  -----END CERTIFICATE-----
40
- date: 2025-09-30 00:00:00.000000000 Z
40
+ date: 2025-10-02 00:00:00.000000000 Z
41
41
  dependencies:
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: blankslate
metadata.gz.sig CHANGED
Binary file