aspera-cli 4.21.1 → 4.22.0

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 (105) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/BUGS.md +1 -1
  4. data/CHANGELOG.md +52 -22
  5. data/CONTRIBUTING.md +69 -148
  6. data/README.md +929 -668
  7. data/bin/ascli +5 -14
  8. data/bin/asession +1 -3
  9. data/examples/get_proto_file.rb +4 -3
  10. data/examples/proxy.pac +20 -20
  11. data/lib/aspera/agent/base.rb +11 -5
  12. data/lib/aspera/agent/connect.rb +30 -28
  13. data/lib/aspera/agent/{alpha.rb → desktop.rb} +35 -31
  14. data/lib/aspera/agent/direct.rb +141 -121
  15. data/lib/aspera/agent/httpgw.rb +22 -26
  16. data/lib/aspera/agent/node.rb +14 -11
  17. data/lib/aspera/agent/transferd.rb +30 -19
  18. data/lib/aspera/api/alee.rb +1 -1
  19. data/lib/aspera/api/aoc.rb +6 -6
  20. data/lib/aspera/api/cos_node.rb +2 -2
  21. data/lib/aspera/api/httpgw.rb +7 -3
  22. data/lib/aspera/api/node.rb +10 -8
  23. data/lib/aspera/ascmd.rb +3 -3
  24. data/lib/aspera/ascp/installation.rb +53 -72
  25. data/lib/aspera/ascp/management.rb +1 -1
  26. data/lib/aspera/assert.rb +11 -2
  27. data/lib/aspera/cli/error.rb +2 -2
  28. data/lib/aspera/cli/extended_value.rb +46 -21
  29. data/lib/aspera/cli/formatter.rb +55 -48
  30. data/lib/aspera/cli/hints.rb +1 -1
  31. data/lib/aspera/cli/info.rb +1 -0
  32. data/lib/aspera/cli/main.rb +192 -170
  33. data/lib/aspera/cli/manager.rb +18 -18
  34. data/lib/aspera/cli/plugin.rb +23 -20
  35. data/lib/aspera/cli/plugin_factory.rb +1 -1
  36. data/lib/aspera/cli/plugins/alee.rb +1 -1
  37. data/lib/aspera/cli/plugins/aoc.rb +247 -159
  38. data/lib/aspera/cli/plugins/ats.rb +19 -17
  39. data/lib/aspera/cli/plugins/config.rb +76 -113
  40. data/lib/aspera/cli/plugins/console.rb +5 -3
  41. data/lib/aspera/cli/plugins/faspex.rb +39 -35
  42. data/lib/aspera/cli/plugins/faspex5.rb +111 -84
  43. data/lib/aspera/cli/plugins/faspio.rb +13 -1
  44. data/lib/aspera/cli/plugins/httpgw.rb +13 -1
  45. data/lib/aspera/cli/plugins/node.rb +312 -182
  46. data/lib/aspera/cli/plugins/orchestrator.rb +34 -40
  47. data/lib/aspera/cli/plugins/preview.rb +3 -3
  48. data/lib/aspera/cli/plugins/server.rb +6 -6
  49. data/lib/aspera/cli/plugins/shares.rb +5 -5
  50. data/lib/aspera/cli/sync_actions.rb +19 -18
  51. data/lib/aspera/cli/transfer_agent.rb +5 -5
  52. data/lib/aspera/cli/transfer_progress.rb +2 -2
  53. data/lib/aspera/cli/version.rb +1 -1
  54. data/lib/aspera/command_line_builder.rb +116 -95
  55. data/lib/aspera/coverage.rb +8 -5
  56. data/lib/aspera/environment.rb +26 -17
  57. data/lib/aspera/faspex_gw.rb +14 -14
  58. data/lib/aspera/faspex_postproc.rb +10 -11
  59. data/lib/aspera/hash_ext.rb +4 -14
  60. data/lib/aspera/json_rpc.rb +1 -1
  61. data/lib/aspera/keychain/encrypted_hash.rb +47 -34
  62. data/lib/aspera/keychain/factory.rb +41 -0
  63. data/lib/aspera/keychain/hashicorp_vault.rb +71 -0
  64. data/lib/aspera/keychain/macos_security.rb +19 -11
  65. data/lib/aspera/log.rb +28 -34
  66. data/lib/aspera/nagios.rb +6 -6
  67. data/lib/aspera/node_simulator.rb +8 -8
  68. data/lib/aspera/oauth/base.rb +14 -7
  69. data/lib/aspera/oauth/factory.rb +5 -6
  70. data/lib/aspera/oauth/url_json.rb +6 -6
  71. data/lib/aspera/persistency_action_once.rb +6 -4
  72. data/lib/aspera/persistency_folder.rb +2 -2
  73. data/lib/aspera/preview/generator.rb +13 -10
  74. data/lib/aspera/preview/options.rb +16 -16
  75. data/lib/aspera/preview/terminal.rb +4 -4
  76. data/lib/aspera/preview/utils.rb +15 -17
  77. data/lib/aspera/products/connect.rb +35 -1
  78. data/lib/aspera/products/{alpha.rb → desktop.rb} +3 -3
  79. data/lib/aspera/products/transferd.rb +9 -2
  80. data/lib/aspera/proxy_auto_config.rb +2 -2
  81. data/lib/aspera/rest.rb +56 -47
  82. data/lib/aspera/rest_errors_aspera.rb +1 -1
  83. data/lib/aspera/secret_hider.rb +12 -5
  84. data/lib/aspera/ssh.rb +4 -4
  85. data/lib/aspera/temp_file_manager.rb +5 -4
  86. data/lib/aspera/transfer/convert.rb +29 -0
  87. data/lib/aspera/transfer/error_info.rb +66 -66
  88. data/lib/aspera/transfer/parameters.rb +13 -68
  89. data/lib/aspera/transfer/spec.rb +5 -6
  90. data/lib/aspera/transfer/spec.schema.yaml +753 -0
  91. data/lib/aspera/transfer/spec_doc.rb +62 -0
  92. data/lib/aspera/transfer/sync.rb +23 -72
  93. data/lib/aspera/transfer/sync_instance.schema.yaml +13 -0
  94. data/lib/aspera/transfer/sync_session.schema.yaml +79 -0
  95. data/lib/aspera/transfer/uri.rb +6 -6
  96. data/lib/aspera/uri_reader.rb +18 -1
  97. data/lib/aspera/web_auth.rb +1 -1
  98. data/lib/aspera/web_server_simple.rb +53 -44
  99. data.tar.gz.sig +0 -0
  100. metadata +28 -165
  101. metadata.gz.sig +0 -0
  102. data/examples/build_exec +0 -74
  103. data/examples/build_exec_rubyc +0 -40
  104. data/examples/build_package.sh +0 -28
  105. data/lib/aspera/transfer/spec.yaml +0 -718
@@ -11,8 +11,10 @@ module Aspera
11
11
  module Cli
12
12
  module Plugins
13
13
  class Orchestrator < Cli::BasicAuthPlugin
14
+ STANDARD_PATH = '/aspera/orchestrator'
15
+ TEST_ENDPOINT = 'api/remote_node_ping'
16
+ private_constant :STANDARD_PATH, :TEST_ENDPOINT
14
17
  class << self
15
- STANDARD_PATH = '/aspera/orchestrator'
16
18
  def detect(address_or_url)
17
19
  address_or_url = "https://#{address_or_url}" unless address_or_url.match?(%r{^[a-z]{1,6}://})
18
20
  urls = [address_or_url]
@@ -21,13 +23,12 @@ module Aspera
21
23
  urls.each do |base_url|
22
24
  next unless base_url.match?('https?://')
23
25
  api = Rest.new(base_url: base_url)
24
- test_endpoint = 'api/remote_node_ping'
25
- result = api.call(operation: 'GET', subpath: test_endpoint, headers: {'Accept' => 'application/json'}, query: {format: :json})
26
+ result = api.call(operation: 'GET', subpath: TEST_ENDPOINT, headers: {'Accept' => Rest::MIME_JSON}, query: {format: :json})
26
27
  next unless result[:data]['remote_orchestrator_info']
27
28
  url = result[:http].uri.to_s
28
29
  return {
29
30
  version: result[:data]['remote_orchestrator_info']['orchestrator-version'],
30
- url: url[0..url.index(test_endpoint) - 2]
31
+ url: url[0..url.index(TEST_ENDPOINT) - 2]
31
32
  }
32
33
  rescue StandardError => e
33
34
  error = e
@@ -61,27 +62,22 @@ module Aspera
61
62
 
62
63
  ACTIONS = %i[health info workflow plugins processes].freeze
63
64
 
64
- # call orchestrator api
65
- # @param endpoint [String] the endpoint to call
66
- # @param prefix [String] the prefix to add to the endpoint
67
- # @param id [String] the id to add to the endpoint
68
- # @param ret_style [Symbol] the return style, :header, :arg, :ext(extension)
69
- # @param format [String] the format to request, 'json', 'xml', nil
70
- # @param args [Hash] the arguments to pass
65
+ # Call orchestrator API, it's a bit special
66
+ # @param endpoint [String] the endpoint to call
67
+ # @param ret_style [Symbol] the return style, :header, :arg, :ext(extension)
68
+ # @param format [String] the format to request, 'json', 'xml', nil
69
+ # @param args [Hash] the arguments to pass
71
70
  # @param xml_arrays [Boolean] if true, force arrays in xml parsing
72
- def call_ao(endpoint, prefix: 'api', id: nil, ret_style: nil, format: 'json', args: nil, xml_arrays: true, http: false)
73
- # calls are GET
74
- call_args = {operation: 'GET', subpath: endpoint}
75
- # specify prefix if necessary
76
- call_args[:subpath] = "#{prefix}/#{call_args[:subpath]}" unless prefix.nil?
77
- # specify id if necessary
78
- call_args[:subpath] = "#{call_args[:subpath]}/#{id}" unless id.nil?
71
+ # @param http [Boolean] if true, returns the HttpResponse, else
72
+ def call_ao(endpoint, ret_style: nil, format: 'json', args: nil, xml_arrays: true, http: false)
73
+ # calls are all GET
74
+ call_args = {operation: 'GET', subpath: "api/#{endpoint}"}
79
75
  ret_style = options.get_option(:ret_style, mandatory: true) if ret_style.nil?
80
76
  call_args[:query] = args unless args.nil?
81
77
  unless format.nil?
82
78
  case ret_style
83
79
  when :header
84
- call_args[:headers] = {'Accept' => "application/#{format}" }
80
+ call_args[:headers] = {'Accept' => "application/#{format}"}
85
81
  when :arg
86
82
  call_args[:query] ||= {}
87
83
  call_args[:query][:format] = format
@@ -92,9 +88,9 @@ module Aspera
92
88
  end
93
89
  result = @api_orch.call(**call_args)
94
90
  return result[:http] if http
95
- result[:data] = XmlSimple.xml_in(result[:http].body, {'ForceArray' => xml_arrays}) if format.eql?('xml')
96
- Log.log.debug{Log.dump(:data, result[:data])}
97
- return result[:data]
91
+ result = format.eql?('xml') ? XmlSimple.xml_in(result[:http].body, {'ForceArray' => xml_arrays}) : result[:data]
92
+ Log.log.debug{Log.dump(:data, result)}
93
+ return result
98
94
  end
99
95
 
100
96
  def execute_action
@@ -105,7 +101,7 @@ module Aspera
105
101
  type: :url,
106
102
  url_query: {
107
103
  'login' => options.get_option(:username, mandatory: true),
108
- 'password' => options.get_option(:password, mandatory: true) }
104
+ 'password' => options.get_option(:password, mandatory: true)}
109
105
  }
110
106
  when :head_basic
111
107
  {
@@ -136,47 +132,45 @@ module Aspera
136
132
  return nagios.result
137
133
  when :info
138
134
  result = call_ao('remote_node_ping', format: 'xml', xml_arrays: false)
139
- return {type: :single_object, data: result}
135
+ return Main.result_single_object(result)
140
136
  when :processes
141
137
  # TODO: Bug ? API has only XML format
142
138
  result = call_ao('processes_status', format: 'xml')
143
- return {type: :object_list, data: result['process']}
139
+ return Main.result_object_list(result['process'])
144
140
  when :plugins
145
141
  # TODO: Bug ? only json format on url
146
142
  result = call_ao('plugin_version')
147
- return {type: :object_list, data: result['Plugin']}
143
+ return Main.result_object_list(result['Plugin'])
148
144
  when :workflow
149
145
  command = options.get_next_command(%i[list status inputs details start export])
150
- unless [:list].include?(command)
151
- wf_id = instance_identifier
152
- end
153
146
  case command
154
147
  when :status
155
- wf_id = nil if wf_id.eql?(SpecialValues::ALL)
156
- result = call_ao('workflows_status', id: wf_id)
157
- return {type: :object_list, data: result['workflows']['workflow']}
148
+ wf_id = instance_identifier
149
+ result = call_ao(wf_id.eql?(SpecialValues::ALL) ? 'workflows_status' : "workflows_status/#{wf_id}")
150
+ return Main.result_object_list(result['workflows']['workflow'])
158
151
  when :list
159
- result = call_ao('workflows_list', id: 0)
152
+ result = call_ao('workflows_list/0')
160
153
  return {
161
154
  type: :object_list,
162
155
  data: result['workflows']['workflow'],
163
156
  fields: %w[id portable_id name published_status published_revision_id latest_revision_id last_modification]
164
157
  }
165
158
  when :details
166
- result = call_ao('workflow_details', id: wf_id)
167
- return {type: :object_list, data: result['workflows']['workflow']['statuses']}
159
+ result = call_ao("workflow_details/#{instance_identifier}")
160
+ return Main.result_object_list(result['workflows']['workflow']['statuses'])
168
161
  when :inputs
169
- result = call_ao('workflow_inputs_spec', id: wf_id)
170
- return {type: :single_object, data: result['workflow_inputs_spec']}
162
+ result = call_ao("workflow_inputs_spec/#{instance_identifier}")
163
+ return Main.result_single_object(result['workflow_inputs_spec'])
171
164
  when :export
172
- result = call_ao('export_workflow', id: wf_id, format: nil, http: true)
173
- return {type: :text, data: result.body}
165
+ result = call_ao("export_workflow/#{instance_identifier}", format: nil, http: true)
166
+ return Main.result_text(result.body)
174
167
  when :start
175
168
  result = {
176
169
  type: :single_object,
177
170
  data: nil
178
171
  }
179
172
  call_params = {format: :json}
173
+ wf_id = instance_identifier
180
174
  # get external parameters if any
181
175
  options.get_next_argument('external_parameters', mandatory: false, validation: Hash, default: {}).each do |name, value|
182
176
  call_params["external_parameters[#{name}]"] = value
@@ -197,7 +191,7 @@ module Aspera
197
191
  if call_params['synchronous']
198
192
  result[:type] = :text
199
193
  end
200
- result[:data] = call_ao('initiate', id: wf_id, args: call_params)
194
+ result[:data] = call_ao("initiate/#{wf_id}", args: call_params)
201
195
  return result
202
196
  end
203
197
  else Aspera.error_unexpected_value(command)
@@ -126,7 +126,7 @@ module Aspera
126
126
  # /files/id/files is normally cached in redis, but we can discard the cache
127
127
  # but /files/id is not cached
128
128
  def get_folder_entries(file_id, request_args=nil)
129
- headers = {'Accept' => 'application/json'}
129
+ headers = {'Accept' => Rest::MIME_JSON}
130
130
  headers['X-Aspera-Cache-Control'] = 'no-cache' if @option_folder_reset_cache.eql?(:header)
131
131
  return @api_node.call(
132
132
  operation: 'GET',
@@ -194,7 +194,7 @@ module Aspera
194
194
  if event.dig('data', 'type').eql?('file')
195
195
  file_entry = @api_node.read("files/#{event['data']['id']}") rescue nil
196
196
  if !file_entry.nil? &&
197
- @option_skip_folders.none?{|d|file_entry['path'].start_with?(d)}
197
+ @option_skip_folders.none?{ |d| file_entry['path'].start_with?(d)}
198
198
  file_entry['parent_file_id'] = event['data']['parent_file_id']
199
199
  if event['types'].include?('file.deleted')
200
200
  Log.log.error('TODO'.red)
@@ -460,7 +460,7 @@ module Aspera
460
460
  'id' => @access_key_self['root_file_id'],
461
461
  'name' => '/',
462
462
  'type' => 'folder',
463
- 'path' => '/' }
463
+ 'path' => '/'}
464
464
  else
465
465
  @api_node.read("files/#{scan_id}")
466
466
  end
@@ -148,7 +148,7 @@ module Aspera
148
148
  ssh_key_list = [ssh_key_list] if ssh_key_list.is_a?(String)
149
149
  Aspera.assert_type(ssh_key_list, Array){'ssh_keys'}
150
150
  Aspera.assert(ssh_key_list.all?(String))
151
- ssh_key_list.map!{|p|File.expand_path(p)}
151
+ ssh_key_list.map!{ |p| File.expand_path(p)}
152
152
  Log.log.debug{"SSH keys=#{ssh_key_list}"}
153
153
  if !ssh_key_list.empty?
154
154
  @ssh_opts[:keys] = ssh_key_list
@@ -176,7 +176,7 @@ module Aspera
176
176
  return Main.result_transfer(transfer.start(transfer_spec))
177
177
  when :sync
178
178
  # lets ignore the arguments provided by execute_sync_action, we just give the transfer spec
179
- return execute_sync_action {transfer_spec}
179
+ return execute_sync_action{transfer_spec}
180
180
  end
181
181
  end
182
182
 
@@ -220,7 +220,7 @@ module Aspera
220
220
  if TransferAgent.session_status(statuses).eql?(:success)
221
221
  nagios.add_ok('transfer', 'ok')
222
222
  else
223
- nagios.add_critical('transfer', statuses.reject{|i|i.eql?(:success)}.first.to_s)
223
+ nagios.add_critical('transfer', statuses.reject{ |i| i.eql?(:success)}.first.to_s)
224
224
  end
225
225
  else Aspera.error_unexpected_value(command_nagios)
226
226
  end
@@ -236,11 +236,11 @@ module Aspera
236
236
  when :mkdir, :mv, :cp, :rm
237
237
  return Main.result_success
238
238
  when :ls
239
- return {type: :object_list, data: result.map(&:stringify_keys), fields: %w[zmode zuid zgid size mtime name]}
239
+ return Main.result_object_list(result.map(&:stringify_keys), fields: %w[zmode zuid zgid size mtime name])
240
240
  when :df
241
- return {type: :object_list, data: result.map(&:stringify_keys)}
241
+ return Main.result_object_list(result.map(&:stringify_keys))
242
242
  when :du, :md5sum, :info
243
- return {type: :single_object, data: result.stringify_keys}
243
+ return Main.result_single_object(result.stringify_keys)
244
244
  end
245
245
  rescue AsCmd::Error => e
246
246
  raise Cli::BadArgument, e.extended_message
@@ -50,7 +50,7 @@ module Aspera
50
50
  end
51
51
  end
52
52
 
53
- def initialize(**env)
53
+ def initialize(**_)
54
54
  super
55
55
  end
56
56
 
@@ -72,14 +72,14 @@ module Aspera
72
72
  .call(
73
73
  operation: 'GET',
74
74
  subpath: 'ping',
75
- headers: {'content-type': 'application/json'})
75
+ headers: {'content-type': Rest::MIME_JSON})
76
76
  raise 'Shares not detected' unless res[:http].body.eql?(' ')
77
77
  nagios.add_ok('shares api', 'accessible')
78
78
  rescue StandardError => e
79
79
  nagios.add_critical('API', e.to_s)
80
80
  end
81
81
  return nagios.result
82
- when :repository, :files
82
+ when :files
83
83
  api_shares_node = basic_auth_api(NODE_API_PATH)
84
84
  repo_command = options.get_next_command(Node::COMMANDS_SHARES)
85
85
  return Node
@@ -138,9 +138,9 @@ module Aspera
138
138
  return entity_action(api_shares_admin, entities_path, is_singleton: !entity_verb.eql?(:share_permissions))
139
139
  when :import # saml
140
140
  return do_bulk_operation(command: entity_verb, descr: 'user information') do |entity_parameters|
141
- entity_parameters = entity_parameters.transform_keys{|k|k.gsub(/\s+/, '_').downcase}
141
+ entity_parameters = entity_parameters.transform_keys{ |k| k.gsub(/\s+/, '_').downcase}
142
142
  Aspera.assert_type(entity_parameters, Hash)
143
- SAML_IMPORT_MANDATORY.each{|p|raise "missing mandatory field: #{p}" if entity_parameters[p].nil?}
143
+ SAML_IMPORT_MANDATORY.each{ |p| raise "missing mandatory field: #{p}" if entity_parameters[p].nil?}
144
144
  entity_parameters.each_key do |p|
145
145
  raise "unsupported field: #{p}, use: #{SAML_IMPORT_ALLOWED.join(',')}" unless SAML_IMPORT_ALLOWED.include?(p)
146
146
  end
@@ -7,30 +7,30 @@ module Aspera
7
7
  module Cli
8
8
  # Module for sync actions
9
9
  module SyncActions
10
- # optional simple command line arguments for sync
11
- # in Array to keep option order
10
+ # Optional simple command line arguments for sync
11
+ # in Array to keep order as on command line
12
12
  # conf: key in option --conf
13
13
  # args: key for command line args
14
14
  # values: possible values for argument
15
15
  # type: type for validation
16
- SYNC_ARGUMENTS_INFO = [
16
+ ARGUMENTS_INFO = [
17
17
  {
18
18
  conf: 'direction',
19
19
  args: 'direction',
20
20
  values: Transfer::Sync::DIRECTIONS
21
- }, {
22
- conf: 'remote.path',
23
- args: 'remote_dir',
24
- type: String
25
21
  }, {
26
22
  conf: 'local.path',
27
23
  args: 'local_dir',
28
24
  type: String
25
+ }, {
26
+ conf: 'remote.path',
27
+ args: 'remote_dir',
28
+ type: String
29
29
  }
30
30
  ].freeze
31
31
  # name of minimal arguments required, also used to generate a session name
32
- SYNC_SIMPLE_ARGS = SYNC_ARGUMENTS_INFO.map{|i|i[:conf]}.freeze
33
- private_constant :SYNC_ARGUMENTS_INFO, :SYNC_SIMPLE_ARGS
32
+ ARGUMENTS_LIST = ARGUMENTS_INFO.map{ |i| i[:conf]}.freeze
33
+ private_constant :ARGUMENTS_INFO
34
34
 
35
35
  class << self
36
36
  def declare_options(options)
@@ -42,7 +42,7 @@ module Aspera
42
42
  def sync_args_to_params(async_params)
43
43
  # sync session parameters can be provided on command line instead of sync_info
44
44
  arguments = {}
45
- SYNC_ARGUMENTS_INFO.each do |info|
45
+ ARGUMENTS_INFO.each do |info|
46
46
  value = options.get_next_argument(
47
47
  info[:conf],
48
48
  mandatory: false,
@@ -52,9 +52,9 @@ module Aspera
52
52
  arguments[info[:conf]] = value.to_s
53
53
  end
54
54
  Log.log.debug{Log.dump('arguments', arguments)}
55
- raise Cli::BadArgument, "Provide 0 or 3 arguments, not #{arguments.keys.length} for: #{SYNC_SIMPLE_ARGS.join(', ')}" unless
56
- [0, 3].include?(arguments.keys.length)
57
- if !arguments.empty?
55
+ case arguments.keys.length
56
+ when 0 then nil
57
+ when 3
58
58
  session_info = async_params
59
59
  param_path = :conf
60
60
  if async_params.key?('sessions') || async_params.key?('instance')
@@ -63,7 +63,7 @@ module Aspera
63
63
  session_info = async_params['sessions'][0]
64
64
  param_path = :args
65
65
  end
66
- SYNC_ARGUMENTS_INFO.each do |info|
66
+ ARGUMENTS_INFO.each do |info|
67
67
  key_path = info[param_path].split('.')
68
68
  hash_for_key = session_info
69
69
  if key_path.length > 1
@@ -76,10 +76,11 @@ module Aspera
76
76
  end
77
77
  if !session_info.key?('name')
78
78
  # if no name is specified, generate one from simple arguments
79
- session_info['name'] = SYNC_SIMPLE_ARGS.filter_map do |arg_name|
80
- arguments[arg_name]&.gsub(/[^a-zA-Z0-9]/, '')
81
- end.reject(&:empty?).join('_')
79
+ session_info['name'] = ARGUMENTS_LIST.filter_map do |arg_name|
80
+ arguments[arg_name]&.gsub(/[^a-zA-Z0-9]+/, '_')
81
+ end.reject(&:empty?).join('_').gsub(/__+/, '_')
82
82
  end
83
+ else raise Cli::BadArgument, "Provide 0 or 3 arguments, not #{arguments.keys.length} for: #{ARGUMENTS_LIST.join(', ')}"
83
84
  end
84
85
  end
85
86
 
@@ -100,7 +101,7 @@ module Aspera
100
101
  when :status
101
102
  sync_session_name = options.get_next_argument('name of sync session', mandatory: false, validation: String)
102
103
  async_params = options.get_option(:sync_info, mandatory: true)
103
- return {type: :single_object, data: Transfer::Sync.admin_status(async_params, sync_session_name)}
104
+ return Main.result_single_object(Transfer::Sync.admin_status(async_params, sync_session_name))
104
105
  end
105
106
  end
106
107
  end
@@ -38,7 +38,7 @@ module Aspera
38
38
  # @return :success if all sessions statuses returned by "start" are success
39
39
  # else return the first error exception object
40
40
  def session_status(statuses)
41
- error_statuses = statuses.reject{|i|i.eql?(:success)}
41
+ error_statuses = statuses.reject{ |i| i.eql?(:success)}
42
42
  return :success if error_statuses.empty?
43
43
  return error_statuses.first
44
44
  end
@@ -92,7 +92,7 @@ module Aspera
92
92
  def updated_ts(transfer_spec={})
93
93
  transfer_spec.deep_merge!(@transfer_spec_command_line)
94
94
  # recursively remove values that are nil (user wants to delete)
95
- transfer_spec.deep_do { |hash, key, value, _unused| hash.delete(key) if value.nil?}
95
+ transfer_spec.deep_do{ |hash, key, value, _unused| hash.delete(key) if value.nil?}
96
96
  return transfer_spec
97
97
  end
98
98
 
@@ -199,7 +199,7 @@ module Aspera
199
199
  return @transfer_paths
200
200
  when Array
201
201
  Log.log.debug('getting file list as extended value')
202
- raise Cli::BadArgument, 'sources must be a Array of String' if !file_list.reject{|f|f.is_a?(String)}.empty?
202
+ raise Cli::BadArgument, 'sources must be a Array of String' if !file_list.reject{ |f| f.is_a?(String)}.empty?
203
203
  else
204
204
  raise Cli::BadArgument, "sources must be a Array, not #{file_list.class}"
205
205
  end
@@ -211,10 +211,10 @@ module Aspera
211
211
  case source_type
212
212
  when :list
213
213
  # when providing a list, just specify source
214
- @transfer_paths = file_list.map{|i|{'source' => i}}
214
+ @transfer_paths = file_list.map{ |i| {'source' => i}}
215
215
  when :pair
216
216
  Aspera.assert(file_list.length.even?, exception_class: Cli::BadArgument){"When using pair, provide an even number of paths: #{file_list.length}"}
217
- @transfer_paths = file_list.each_slice(2).to_a.map{|s, d|{'source' => s, 'destination' => d}}
217
+ @transfer_paths = file_list.each_slice(2).to_a.map{ |s, d| {'source' => s, 'destination' => d}}
218
218
  else Aspera.error_unexpected_value(source_type)
219
219
  end
220
220
  Log.log.debug{"paths=#{@transfer_paths}"}
@@ -32,7 +32,7 @@ module Aspera
32
32
  if @progress_bar.nil?
33
33
  @progress_bar = ProgressBar.create(
34
34
  format: '%t %a %B %p%% %r Mbps %E',
35
- rate_scale: lambda{|rate|rate / Environment::BYTES_PER_MEBIBIT},
35
+ rate_scale: lambda{ |rate| rate / Environment::BYTES_PER_MEBIBIT},
36
36
  title: '',
37
37
  total: nil)
38
38
  end
@@ -86,7 +86,7 @@ module Aspera
86
86
  private
87
87
 
88
88
  def total(key)
89
- @sessions.values.inject(0){|m, s|m + s[key]}
89
+ @sessions.values.inject(0){ |m, s| m + s[key]}
90
90
  end
91
91
  end
92
92
  end
@@ -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.21.1'
7
+ VERSION = '4.22.0'
8
8
  end
9
9
  end