aspera-cli 4.14.0 → 4.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +54 -3
  4. data/CONTRIBUTING.md +7 -7
  5. data/README.md +1457 -880
  6. data/bin/ascli +18 -9
  7. data/bin/asession +12 -14
  8. data/examples/proxy.pac +1 -1
  9. data/lib/aspera/aoc.rb +198 -127
  10. data/lib/aspera/ascmd.rb +24 -14
  11. data/lib/aspera/cli/basic_auth_plugin.rb +9 -6
  12. data/lib/aspera/cli/error.rb +17 -0
  13. data/lib/aspera/cli/extended_value.rb +47 -12
  14. data/lib/aspera/cli/formatter.rb +260 -171
  15. data/lib/aspera/cli/hints.rb +80 -0
  16. data/lib/aspera/cli/main.rb +101 -147
  17. data/lib/aspera/cli/manager.rb +160 -124
  18. data/lib/aspera/cli/plugin.rb +70 -59
  19. data/lib/aspera/cli/plugins/alee.rb +0 -1
  20. data/lib/aspera/cli/plugins/aoc.rb +239 -273
  21. data/lib/aspera/cli/plugins/ats.rb +8 -5
  22. data/lib/aspera/cli/plugins/bss.rb +2 -2
  23. data/lib/aspera/cli/plugins/config.rb +516 -375
  24. data/lib/aspera/cli/plugins/console.rb +40 -0
  25. data/lib/aspera/cli/plugins/cos.rb +4 -5
  26. data/lib/aspera/cli/plugins/faspex.rb +99 -84
  27. data/lib/aspera/cli/plugins/faspex5.rb +179 -148
  28. data/lib/aspera/cli/plugins/node.rb +219 -153
  29. data/lib/aspera/cli/plugins/orchestrator.rb +52 -17
  30. data/lib/aspera/cli/plugins/preview.rb +46 -32
  31. data/lib/aspera/cli/plugins/server.rb +57 -17
  32. data/lib/aspera/cli/plugins/shares.rb +34 -12
  33. data/lib/aspera/cli/sync_actions.rb +68 -0
  34. data/lib/aspera/cli/transfer_agent.rb +45 -55
  35. data/lib/aspera/cli/transfer_progress.rb +74 -0
  36. data/lib/aspera/cli/version.rb +1 -1
  37. data/lib/aspera/colors.rb +3 -1
  38. data/lib/aspera/command_line_builder.rb +14 -11
  39. data/lib/aspera/cos_node.rb +3 -2
  40. data/lib/aspera/environment.rb +17 -6
  41. data/lib/aspera/fasp/agent_aspera.rb +126 -0
  42. data/lib/aspera/fasp/agent_base.rb +31 -77
  43. data/lib/aspera/fasp/agent_connect.rb +21 -22
  44. data/lib/aspera/fasp/agent_direct.rb +88 -102
  45. data/lib/aspera/fasp/agent_httpgw.rb +196 -192
  46. data/lib/aspera/fasp/agent_node.rb +41 -34
  47. data/lib/aspera/fasp/agent_trsdk.rb +75 -34
  48. data/lib/aspera/fasp/error_info.rb +2 -2
  49. data/lib/aspera/fasp/faux_file.rb +52 -0
  50. data/lib/aspera/fasp/installation.rb +43 -184
  51. data/lib/aspera/fasp/management.rb +244 -0
  52. data/lib/aspera/fasp/parameters.rb +59 -26
  53. data/lib/aspera/fasp/parameters.yaml +75 -8
  54. data/lib/aspera/fasp/products.rb +162 -0
  55. data/lib/aspera/fasp/transfer_spec.rb +1 -1
  56. data/lib/aspera/fasp/uri.rb +4 -4
  57. data/lib/aspera/faspex_gw.rb +2 -2
  58. data/lib/aspera/faspex_postproc.rb +2 -2
  59. data/lib/aspera/hash_ext.rb +2 -2
  60. data/lib/aspera/json_rpc.rb +49 -0
  61. data/lib/aspera/line_logger.rb +23 -0
  62. data/lib/aspera/log.rb +57 -16
  63. data/lib/aspera/node.rb +97 -14
  64. data/lib/aspera/oauth.rb +36 -18
  65. data/lib/aspera/open_application.rb +4 -4
  66. data/lib/aspera/persistency_folder.rb +2 -2
  67. data/lib/aspera/preview/file_types.rb +4 -2
  68. data/lib/aspera/preview/generator.rb +22 -35
  69. data/lib/aspera/preview/options.rb +2 -0
  70. data/lib/aspera/preview/terminal.rb +24 -13
  71. data/lib/aspera/preview/utils.rb +19 -26
  72. data/lib/aspera/rest.rb +103 -72
  73. data/lib/aspera/rest_call_error.rb +1 -1
  74. data/lib/aspera/rest_error_analyzer.rb +15 -14
  75. data/lib/aspera/rest_errors_aspera.rb +37 -34
  76. data/lib/aspera/secret_hider.rb +14 -16
  77. data/lib/aspera/ssh.rb +4 -1
  78. data/lib/aspera/sync.rb +128 -122
  79. data/lib/aspera/temp_file_manager.rb +10 -3
  80. data/lib/aspera/web_auth.rb +10 -7
  81. data/lib/aspera/web_server_simple.rb +9 -4
  82. data.tar.gz.sig +0 -0
  83. metadata +33 -15
  84. metadata.gz.sig +0 -0
  85. data/lib/aspera/cli/listener/line_dump.rb +0 -19
  86. data/lib/aspera/cli/listener/logger.rb +0 -22
  87. data/lib/aspera/cli/listener/progress.rb +0 -50
  88. data/lib/aspera/cli/listener/progress_multi.rb +0 -84
  89. data/lib/aspera/cli/plugins/sync.rb +0 -44
  90. data/lib/aspera/fasp/listener.rb +0 -13
@@ -7,6 +7,46 @@ module Aspera
7
7
  module Cli
8
8
  module Plugins
9
9
  class Console < Aspera::Cli::BasicAuthPlugin
10
+ STANDARD_PATH = '/aspera/console'
11
+ class << self
12
+ def detect(address_or_url)
13
+ address_or_url = "https://#{address_or_url}" unless address_or_url.match?(%r{^[a-z]{1,6}://})
14
+ urls = [address_or_url]
15
+ urls.push("#{address_or_url}#{STANDARD_PATH}") unless address_or_url.end_with?(STANDARD_PATH)
16
+
17
+ urls.each do |base_url|
18
+ next unless base_url.start_with?('https://')
19
+ api = Rest.new(base_url: base_url, redirect_max: 2)
20
+ test_endpoint = 'login'
21
+ test_page = api.call({operation: 'GET', subpath: test_endpoint, url_params: {local: true}})
22
+ next unless test_page[:http].body.include?('Aspera Console')
23
+ version = 'unknown'
24
+ if (m = test_page[:http].body.match(/\(v([1-9]\..*)\)/))
25
+ version = m[1]
26
+ end
27
+ url = test_page[:http].uri.to_s
28
+ return {
29
+ version: version,
30
+ url: url[0..url.index(test_endpoint) - 2]
31
+ }
32
+ rescue StandardError => e
33
+ Log.log.debug{"detect error: #{e}"}
34
+ end
35
+ return nil
36
+ end
37
+
38
+ def wizard(object:, private_key_path: nil, pub_key_pem: nil)
39
+ options = object.options
40
+ return {
41
+ preset_value: {
42
+ url: options.get_option(:url, mandatory: true),
43
+ username: options.get_option(:username, mandatory: true),
44
+ password: options.get_option(:password, mandatory: true)
45
+ },
46
+ test_args: 'transfer list'
47
+ }
48
+ end
49
+ end
10
50
  DEFAULT_FILTER_AGE_SECONDS = 3 * 3600
11
51
  private_constant :DEFAULT_FILTER_AGE_SECONDS
12
52
  def initialize(env)
@@ -10,7 +10,6 @@ module Aspera
10
10
  class Cos < Aspera::Cli::Plugin
11
11
  def initialize(env)
12
12
  super(env)
13
- @service_creds = nil
14
13
  options.declare(:bucket, 'Bucket name')
15
14
  options.declare(:endpoint, 'Storage endpoint url')
16
15
  options.declare(:apikey, 'Storage API key')
@@ -31,19 +30,19 @@ module Aspera
31
30
  # get service credentials, Hash, e.g. @json:@file:...
32
31
  service_credentials = options.get_option(:service_credentials)
33
32
  storage_endpoint = options.get_option(:endpoint)
34
- raise CliBadArgument, 'one of: endpoint or service_credentials is required' if service_credentials.nil? && storage_endpoint.nil?
35
- raise CliBadArgument, 'endpoint and service_credentials are mutually exclusive' unless service_credentials.nil? || storage_endpoint.nil?
33
+ raise Cli::BadArgument, 'one of: endpoint or service_credentials is required' if service_credentials.nil? && storage_endpoint.nil?
34
+ raise Cli::BadArgument, 'endpoint and service_credentials are mutually exclusive' unless service_credentials.nil? || storage_endpoint.nil?
36
35
  if service_credentials.nil?
37
36
  service_api_key = options.get_option(:apikey, mandatory: true)
38
37
  instance_id = options.get_option(:crn, mandatory: true)
39
38
  else
40
- params = CosNode.parameters_from_svc_creds(service_credentials, options.get_option(:region, mandatory: true))
39
+ params = CosNode.parameters_from_svc_credentials(service_credentials, options.get_option(:region, mandatory: true))
41
40
  storage_endpoint = params[:storage_endpoint]
42
41
  service_api_key = params[:service_api_key]
43
42
  instance_id = params[:instance_id]
44
43
  end
45
44
  api_node = CosNode.new(bucket_name, storage_endpoint, instance_id, service_api_key, options.get_option(:identity, mandatory: true))
46
- node_plugin = Node.new(@agents.merge(skip_basic_auth_options: true, node_api: api_node))
45
+ node_plugin = Node.new(@agents, api: api_node)
47
46
  command = options.get_next_command(Node::COMMANDS_COS)
48
47
  return node_plugin.execute_action(command)
49
48
  end
@@ -1,14 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # cspell:ignore passcode xrds workgroups dmembership wmembership
3
4
  require 'aspera/cli/basic_auth_plugin'
4
5
  require 'aspera/cli/plugins/node'
5
6
  require 'aspera/cli/plugins/config'
6
7
  require 'aspera/cli/extended_value'
7
8
  require 'aspera/cli/transfer_agent'
8
- require 'aspera/persistency_action_once'
9
- require 'aspera/open_application'
10
9
  require 'aspera/fasp/uri'
11
10
  require 'aspera/fasp/transfer_spec'
11
+ require 'aspera/persistency_action_once'
12
+ require 'aspera/open_application'
12
13
  require 'aspera/nagios'
13
14
  require 'aspera/id_generator'
14
15
  require 'xmlsimple'
@@ -32,45 +33,68 @@ module Aspera
32
33
  ATOM_EXT_PARAMS = [MAX_ITEMS, MAX_PAGES].concat(ATOM_PARAMS).freeze
33
34
  # sub path in url for public link delivery
34
35
  PUB_LINK_EXTERNAL_MATCH = 'external_deliveries/'
36
+ STANDARD_PATH = '/aspera/faspex'
35
37
  private_constant(*%i[KEY_NODE KEY_PATH PACKAGE_MATCH_FIELD ATOM_MAILBOXES ATOM_PARAMS ATOM_EXT_PARAMS PUB_LINK_EXTERNAL_MATCH])
36
38
 
37
39
  class << self
38
- def detect(base_url)
39
- api = Rest.new(base_url: base_url)
40
- result = api.call(
41
- operation: 'POST',
42
- subpath: 'aspera/faspex',
43
- headers: {'Accept' => 'application/xrds+xml', 'Content-type' => 'text/plain'},
44
- text_body_params: '')
45
- # 4.x
46
- if result[:http].body.start_with?('<?xml')
40
+ def detect(address_or_url)
41
+ address_or_url = "https://#{address_or_url}" unless address_or_url.match?(%r{^[a-z]{1,6}://})
42
+ urls = [address_or_url]
43
+ urls.push("#{address_or_url}#{STANDARD_PATH}") unless address_or_url.end_with?(STANDARD_PATH)
44
+
45
+ urls.each do |base_url|
46
+ next unless base_url.start_with?('https://')
47
+ api = Rest.new(base_url: base_url, redirect_max: 1)
48
+ result = api.call(
49
+ operation: 'POST',
50
+ subpath: '',
51
+ headers: {'Accept' => 'application/xrds+xml', 'Content-type' => 'text/plain'},
52
+ text_body_params: '')
53
+ # 4.x
54
+ next unless result[:http].body.start_with?('<?xml')
47
55
  res_s = XmlSimple.xml_in(result[:http].body, {'ForceArray' => false})
56
+ Log.log.debug{"version: #{result[:http]['X-IBM-Aspera']}"}
48
57
  version = res_s['XRD']['application']['version']
49
- return {version: version, url: result[:http].uri}
58
+ # take redirect if any
59
+ return {version: version, url: result[:http].uri.to_s}
60
+ rescue StandardError => e
61
+ Log.log.debug{"detect error: #{e}"}
50
62
  end
51
63
  return nil
52
64
  end
53
65
 
66
+ def wizard(object:, private_key_path: nil, pub_key_pem: nil)
67
+ options = object.options
68
+ return {
69
+ preset_value: {
70
+ url: options.get_option(:url, mandatory: true),
71
+ username: options.get_option(:username, mandatory: true),
72
+ password: options.get_option(:password, mandatory: true)
73
+ },
74
+ test_args: 'me'
75
+ }
76
+ end
77
+
54
78
  # extract elements from anonymous faspex link
55
79
  def get_link_data(public_url)
56
80
  public_uri = URI.parse(public_url)
57
- raise CliBadArgument, 'Public link does not match Faspex format' unless (m = public_uri.path.match(%r{^(.*)/(external.*)$}))
81
+ raise Cli::BadArgument, 'Public link does not match Faspex format' unless (m = public_uri.path.match(%r{^(.*)/(external.*)$}))
58
82
  base = m[1]
59
83
  subpath = m[2]
60
84
  port_add = public_uri.port.eql?(public_uri.default_port) ? '' : ":#{public_uri.port}"
61
85
  result = {
62
86
  base_url: "#{public_uri.scheme}://#{public_uri.host}#{port_add}#{base}",
63
87
  subpath: subpath,
64
- query: URI.decode_www_form(public_uri.query).each_with_object({}){|v, h|h[v.first] = v.last; }
88
+ query: Rest.decode_query(public_uri.query)
65
89
  }
66
- Log.dump('link data', result)
90
+ Log.log.debug{Log.dump('link data', result)}
67
91
  return result
68
92
  end
69
93
 
70
- # get faspe: URI from entry in xml, and fix problems..
94
+ # get Fasp::Uri::SCHEME URI from entry in xml, and fix problems..
71
95
  def get_fasp_uri_from_entry(entry, raise_no_link: true)
72
96
  unless entry.key?('link')
73
- raise CliBadArgument, 'package has no link (deleted?)' if raise_no_link
97
+ raise Cli::BadArgument, 'package has no link (deleted?)' if raise_no_link
74
98
  return nil
75
99
  end
76
100
  result = entry['link'].find{|e| e['rel'].eql?('package')}['href']
@@ -80,21 +104,11 @@ module Aspera
80
104
  return result
81
105
  end
82
106
 
83
- def textify_package_list(table_data)
84
- return table_data.map do |e|
85
- e.each_key {|k| e[k] = e[k].first if e[k].is_a?(Array) && (e[k].length == 1)}
86
- e['items'] = e.key?('link') ? e['link'].length : 0
87
- e
88
- end
89
- end
90
-
91
- # field_sym : :id or :name
92
- def get_source_id(source_list, source_name)
93
- source_ids = source_list.select { |i| i['name'].eql?(source_name) }
94
- if source_ids.empty?
95
- raise CliError, %Q(No such Faspex source "#{source_name}" in [#{source_list.map{|i| %Q("#{i['name']}")}.join(', ')}])
96
- end
97
- return source_ids.first['id']
107
+ # @return [Integer] identifier of source
108
+ def get_source_id_by_name(source_name, source_list)
109
+ match_source = source_list.find { |i| i['name'].eql?(source_name) }
110
+ return match_source['id'] unless match_source.nil?
111
+ raise Cli::Error, %Q(No such Faspex source: "#{source_name}" in [#{source_list.map{|i| %Q("#{i['name']}")}.join(', ')}])
98
112
  end
99
113
  end
100
114
 
@@ -104,8 +118,8 @@ module Aspera
104
118
  super(env)
105
119
  options.declare(:link, 'Public link for specific operation')
106
120
  options.declare(:delivery_info, 'Package delivery information', types: Hash)
107
- options.declare(:source_name, 'Create package from remote source (by name)')
108
- options.declare(:storage, 'Faspex local storage definition')
121
+ options.declare(:remote_source, 'Remote source for package send (id or %name:)')
122
+ options.declare(:storage, 'Faspex local storage definition (for browsing source)')
109
123
  options.declare(:recipient, 'Use if recipient is a dropbox (with *)')
110
124
  options.declare(:box, 'Package box', values: ATOM_MAILBOXES, default: :inbox)
111
125
  options.parse_options!
@@ -160,8 +174,8 @@ module Aspera
160
174
  # get a batch of package information
161
175
  # order: first batch is latest packages, and then in a batch ids are increasing
162
176
  atom_xml = api_v3.call({operation: 'GET', subpath: "#{mailbox}.atom", headers: {'Accept' => 'application/xml'}, url_params: mailbox_query})[:http].body
163
- box_data = XmlSimple.xml_in(atom_xml, {'ForceArray' => true})
164
- Log.dump(:box_data, box_data)
177
+ box_data = XmlSimple.xml_in(atom_xml, {'ForceArray' => %w[entry field link to]})
178
+ Log.log.debug{Log.dump(:box_data, box_data)}
165
179
  items = box_data.key?('entry') ? box_data['entry'] : []
166
180
  Log.log.debug{"new items: #{items.count}"}
167
181
  # it is the end if page is empty
@@ -173,11 +187,14 @@ module Aspera
173
187
  package[PACKAGE_MATCH_FIELD] =
174
188
  case mailbox
175
189
  when :inbox, :archive
176
- recipient = package['to'].find{|i|recipient_names.include?(i['name'].first)}
177
- recipient.nil? ? nil : recipient['recipient_delivery_id'].first
190
+ recipient = package['to'].find{|i|recipient_names.include?(i['name'])}
191
+ recipient.nil? ? nil : recipient['recipient_delivery_id']
178
192
  else # :sent
179
- package['delivery_id'].first
193
+ package['delivery_id']
180
194
  end
195
+ # add special key
196
+ package['items'] = package['link'].is_a?(Array) ? package['link'].length : 0
197
+ package['metadata'] = package['metadata']['field'].each_with_object({}){|i, m| m[i['name']] = i['content'] }
181
198
  # if we look for a specific package
182
199
  stop_condition = true if !stop_at_id.nil? && stop_at_id.eql?(package[PACKAGE_MATCH_FIELD])
183
200
  # keep only those for the specified recipient
@@ -197,7 +214,7 @@ module Aspera
197
214
  break if link.nil?
198
215
  # replace parameters with the ones from next link
199
216
  params = CGI.parse(URI.parse(link['href']).query)
200
- mailbox_query = params.keys.each_with_object({}){|i, m|; m[i] = params[i].first; }
217
+ mailbox_query = params.keys.each_with_object({}){|i, m| m[i] = params[i].first }
201
218
  Log.log.debug{"query: #{mailbox_query}"}
202
219
  break if !max_pages.nil? && (mailbox_query['page'].to_i > max_pages)
203
220
  end
@@ -210,7 +227,7 @@ module Aspera
210
227
  # pub link user
211
228
  link_data = self.class.get_link_data(public_link_url)
212
229
  if !['external/submissions/new', 'external/dropbox_submissions/new'].include?(link_data[:subpath])
213
- raise CliBadArgument, "pub link is #{link_data[:subpath]}, expecting external/submissions/new"
230
+ raise Cli::BadArgument, "pub link is #{link_data[:subpath]}, expecting external/submissions/new"
214
231
  end
215
232
  create_path = link_data[:subpath].split('/')[0..-2].join('/')
216
233
  package_create_params[:passcode] = link_data[:query]['passcode']
@@ -225,11 +242,11 @@ module Aspera
225
242
  subpath: create_path,
226
243
  json_params: package_create_params,
227
244
  headers: {'Accept' => 'text/javascript'}})[:http].body
228
- # get args of function call
245
+ # get arguments of function call
229
246
  package_creation_data.delete!("\n") # one line
230
247
  package_creation_data.gsub!(/^[^"]+\("\{/, '{') # delete header
231
248
  package_creation_data.gsub!(/"\);[^"]+$/, '"') # delete trailer
232
- package_creation_data.gsub!(/\}", *"/, '},"') # between two args
249
+ package_creation_data.gsub!(/\}", *"/, '},"') # between two arguments
233
250
  package_creation_data.gsub!('\\"', '"') # remove protecting quote
234
251
  begin
235
252
  package_creation_data = JSON.parse("[#{package_creation_data}]")
@@ -254,18 +271,20 @@ module Aspera
254
271
  end
255
272
  return nagios.result
256
273
  when :package
257
- command_pkg = options.get_next_command(%i[send recv list])
274
+ command_pkg = options.get_next_command(%i[send recv list show])
258
275
  case command_pkg
276
+ when :show
277
+ delivery_id = instance_identifier
278
+ return {type: :single_object, data: mailbox_filtered_entries(stop_at_id: delivery_id).find{|p|p[PACKAGE_MATCH_FIELD].eql?(delivery_id)} }
259
279
  when :list
260
280
  return {
261
- type: :object_list,
262
- data: mailbox_filtered_entries,
263
- fields: [PACKAGE_MATCH_FIELD, 'title', 'items'],
264
- textify: lambda {|table_data|Faspex.textify_package_list(table_data)}
281
+ type: :object_list,
282
+ data: mailbox_filtered_entries,
283
+ fields: [PACKAGE_MATCH_FIELD, 'title', 'items']
265
284
  }
266
285
  when :send
267
286
  delivery_info = options.get_option(:delivery_info, mandatory: true)
268
- raise CliBadArgument, 'delivery_info must be hash, refer to doc' unless delivery_info.is_a?(Hash)
287
+ raise Cli::BadArgument, 'delivery_info must be hash, refer to doc' unless delivery_info.is_a?(Hash)
269
288
  # actual parameter to faspex API
270
289
  package_create_params = {'delivery' => delivery_info}
271
290
  public_link_url = options.get_option(:link)
@@ -274,30 +293,30 @@ module Aspera
274
293
  delivery_info['sources'] ||= [{'paths' => []}]
275
294
  first_source = delivery_info['sources'].first
276
295
  first_source['paths'].push(*transfer.source_list)
277
- source_name = options.get_option(:source_name)
278
- if !source_name.nil?
296
+ source_id = instance_identifier(as_option: :remote_source) do |field, value|
297
+ raise Cli::BadArgument, 'only name as selector, or give id' unless field.eql?('name')
279
298
  source_list = api_v3.call({operation: 'GET', subpath: 'source_shares', headers: {'Accept' => 'application/json'}})[:data]['items']
280
- source_id = self.class.get_source_id(source_list, source_name)
281
- first_source['id'] = source_id
299
+ self.class.get_source_id_by_name(value, source_list)
282
300
  end
301
+ first_source['id'] = source_id.to_i unless source_id.nil?
283
302
  pkg_created = api_v3.call({
284
303
  operation: 'POST',
285
304
  subpath: 'send',
286
305
  json_params: package_create_params,
287
306
  headers: {'Accept' => 'application/json'}
288
307
  })[:data]
289
- if !source_name.nil?
290
- # no transfer spec if remote source
308
+ if first_source.key?('id')
309
+ # no transfer spec if remote source: handled by faspex
291
310
  return {data: [pkg_created['links']['status']], type: :value_list, name: 'link'}
292
311
  end
293
- raise CliBadArgument, 'expecting one session exactly' if pkg_created['xfer_sessions'].length != 1
312
+ raise Cli::BadArgument, 'expecting one session exactly' if pkg_created['xfer_sessions'].length != 1
294
313
  transfer_spec = pkg_created['xfer_sessions'].first
295
314
  # use source from cmd line, this one only contains destination (already in dest root)
296
315
  transfer_spec.delete('paths')
297
316
  else # public link
298
317
  transfer_spec = send_public_link_to_ts(public_link_url, package_create_params)
299
318
  end
300
- # Log.dump('transfer_spec',transfer_spec)
319
+ # Log.log.debug{Log.dump('transfer_spec',transfer_spec)}
301
320
  return Main.result_transfer(transfer.start(transfer_spec))
302
321
  when :recv
303
322
  link_url = options.get_option(:link)
@@ -322,11 +341,11 @@ module Aspera
322
341
  delivery_id = instance_identifier
323
342
  raise 'empty id' if delivery_id.empty?
324
343
  recipient = options.get_option(:recipient)
325
- if VAL_ALL.eql?(delivery_id)
344
+ if ExtendedValue::ALL.eql?(delivery_id)
326
345
  pkg_id_uri = mailbox_filtered_entries.map{|i|{id: i[PACKAGE_MATCH_FIELD], uri: self.class.get_fasp_uri_from_entry(i, raise_no_link: false)}}
327
346
  elsif !recipient.nil? && recipient.start_with?('*')
328
347
  found_package_link = mailbox_filtered_entries(stop_at_id: delivery_id).find{|p|p[PACKAGE_MATCH_FIELD].eql?(delivery_id)}['link'].first['href']
329
- raise 'Not Found. Dropbox and Workgroup packages can use the link option with faspe:' if found_package_link.nil?
348
+ raise "Not Found. Dropbox and Workgroup packages can use the link option with #{Fasp::Uri::SCHEME}" if found_package_link.nil?
330
349
  pkg_id_uri = [{id: delivery_id, uri: found_package_link}]
331
350
  else
332
351
  # TODO: delivery id is the right one if package was receive by workgroup
@@ -339,12 +358,12 @@ module Aspera
339
358
  package_entry = XmlSimple.xml_in(entry_xml, {'ForceArray' => true})
340
359
  pkg_id_uri = [{id: delivery_id, uri: self.class.get_fasp_uri_from_entry(package_entry)}]
341
360
  end
342
- when /^faspe:/
361
+ when /^#{Fasp::Uri::SCHEME}:/o
343
362
  pkg_id_uri = [{id: 'package', uri: link_url}]
344
363
  else
345
364
  link_data = self.class.get_link_data(link_url)
346
365
  if !link_data[:subpath].start_with?(PUB_LINK_EXTERNAL_MATCH)
347
- raise CliBadArgument, "Pub link is #{link_data[:subpath]}. Expecting #{PUB_LINK_EXTERNAL_MATCH}"
366
+ raise Cli::BadArgument, "Pub link is #{link_data[:subpath]}. Expecting #{PUB_LINK_EXTERNAL_MATCH}"
348
367
  end
349
368
  # NOTE: unauthenticated API (authorization is in url params)
350
369
  api_public_link = Rest.new({base_url: link_data[:base_url]})
@@ -355,10 +374,10 @@ module Aspera
355
374
  headers: {'Accept' => 'application/xml'})
356
375
  if !package_creation_data[:http].body.start_with?('<?xml ')
357
376
  OpenApplication.instance.uri(link_url)
358
- raise CliError, 'Unexpected response: package not found ?'
377
+ raise Cli::Error, 'Unexpected response: package not found ?'
359
378
  end
360
379
  package_entry = XmlSimple.xml_in(package_creation_data[:http].body, {'ForceArray' => false})
361
- Log.dump(:package_entry, package_entry)
380
+ Log.log.debug{Log.dump(:package_entry, package_entry)}
362
381
  transfer_uri = self.class.get_fasp_uri_from_entry(package_entry)
363
382
  pkg_id_uri = [{id: package_entry['id'], uri: transfer_uri}]
364
383
  end # public link
@@ -366,7 +385,7 @@ module Aspera
366
385
  # TODO : remove ids from skip not present in inbox to avoid growing too big
367
386
  # skip_ids_data.select!{|id|pkg_id_uri.select{|p|p[:id].eql?(id)}}
368
387
  pkg_id_uri.reject!{|i|skip_ids_data.include?(i[:id])}
369
- Log.dump(:pkg_id_uri, pkg_id_uri)
388
+ Log.log.debug{Log.dump(:pkg_id_uri, pkg_id_uri)}
370
389
  return Main.result_status('no new package') if pkg_id_uri.empty?
371
390
  result_transfer = []
372
391
  pkg_id_uri.each do |id_uri|
@@ -375,7 +394,7 @@ module Aspera
375
394
  statuses = [:success]
376
395
  else
377
396
  transfer_spec = Fasp::Uri.new(id_uri[:uri]).transfer_spec
378
- # NOTE: only external users have token in faspe: link !
397
+ # NOTE: only external users have token in Fasp::Uri::SCHEME link !
379
398
  if !transfer_spec.key?('token')
380
399
  sanitized = id_uri[:uri].gsub('&', '&amp;')
381
400
  xml_payload =
@@ -397,41 +416,37 @@ module Aspera
397
416
  return Main.result_transfer_multiple(result_transfer)
398
417
  end
399
418
  when :source
400
- command_source = options.get_next_command(%i[list id name])
419
+ command_source = options.get_next_command(%i[list info node])
401
420
  source_list = api_v3.call({operation: 'GET', subpath: 'source_shares', headers: {'Accept' => 'application/json'}})[:data]['items']
402
421
  case command_source
403
422
  when :list
404
423
  return {type: :object_list, data: source_list}
405
- else # :id or :name
406
- source_match_val = options.get_next_argument('source id or name')
407
- source_ids = source_list.select { |i| i[command_source.to_s].to_s.eql?(source_match_val) }
408
- if source_ids.empty?
409
- raise CliError, "No such Faspex source #{command_source}: #{source_match_val} in [#{source_list.map{|i| i[command_source.to_s]}.join(', ')}]"
410
- end
411
- # get id and name
412
- source_name = source_ids.first['name']
413
- # source_id=source_ids.first['id']
424
+ else # :info :node
425
+ source_id = instance_identifier do |field, value|
426
+ raise Cli::BadArgument, 'only name as selector, or give id' unless field.eql?('name')
427
+ self.class.get_source_id_by_name(value, source_list)
428
+ end.to_i
429
+ source_name = source_list.find{|i|i['id'].eql?(source_id)}['name']
414
430
  source_hash = options.get_option(:storage, mandatory: true)
415
431
  # check value of option
416
- raise CliError, 'storage option must be a Hash' unless source_hash.is_a?(Hash)
432
+ raise Cli::Error, 'storage option must be a Hash' unless source_hash.is_a?(Hash)
417
433
  source_hash.each do |name, storage|
418
- raise CliError, "storage '#{name}' must be a Hash" unless storage.is_a?(Hash)
434
+ raise Cli::Error, "storage '#{name}' must be a Hash" unless storage.is_a?(Hash)
419
435
  [KEY_NODE, KEY_PATH].each do |key|
420
- raise CliError, "storage '#{name}' must have a '#{key}'" unless storage.key?(key)
436
+ raise Cli::Error, "storage '#{name}' must have a '#{key}'" unless storage.key?(key)
421
437
  end
422
438
  end
423
439
  if !source_hash.key?(source_name)
424
- raise CliError, "No such storage in config file: \"#{source_name}\" in [#{source_hash.keys.join(', ')}]"
440
+ raise Cli::Error, "No such storage in config file: \"#{source_name}\" in [#{source_hash.keys.join(', ')}]"
425
441
  end
426
442
  source_info = source_hash[source_name]
427
- Log.log.debug{"source_info: #{source_info}"}
428
- command_node = options.get_next_command(%i[info node])
429
- case command_node
443
+ Log.log.debug{Log.dump(:source_info, source_info)}
444
+ case command_source
430
445
  when :info
431
446
  return {data: source_info, type: :single_object}
432
447
  when :node
433
448
  node_config = ExtendedValue.instance.evaluate(source_info[KEY_NODE])
434
- raise CliError, "bad type for: \"#{source_info[KEY_NODE]}\"" unless node_config.is_a?(Hash)
449
+ raise Cli::Error, "bad type for: \"#{source_info[KEY_NODE]}\"" unless node_config.is_a?(Hash)
435
450
  Log.log.debug{"node=#{node_config}"}
436
451
  api_node = Rest.new({
437
452
  base_url: node_config['url'],
@@ -440,7 +455,7 @@ module Aspera
440
455
  username: node_config['username'],
441
456
  password: node_config['password']}})
442
457
  command = options.get_next_command(Node::COMMANDS_FASPEX)
443
- return Node.new(@agents.merge(skip_basic_auth_options: true, node_api: api_node)).execute_action(command, source_info[KEY_PATH])
458
+ return Node.new(@agents, api: api_node).execute_action(command, source_info[KEY_PATH])
444
459
  end
445
460
  end
446
461
  when :me