aspera-cli 4.18.0 → 4.19.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 (60) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +23 -0
  4. data/CONTRIBUTING.md +5 -12
  5. data/README.md +152 -84
  6. data/examples/build_exec +85 -0
  7. data/examples/build_package.sh +28 -0
  8. data/lib/aspera/agent/alpha.rb +4 -4
  9. data/lib/aspera/agent/base.rb +2 -0
  10. data/lib/aspera/agent/connect.rb +3 -4
  11. data/lib/aspera/agent/direct.rb +108 -104
  12. data/lib/aspera/agent/httpgw.rb +1 -1
  13. data/lib/aspera/api/aoc.rb +2 -2
  14. data/lib/aspera/api/httpgw.rb +95 -57
  15. data/lib/aspera/api/node.rb +110 -77
  16. data/lib/aspera/ascp/installation.rb +47 -32
  17. data/lib/aspera/ascp/management.rb +4 -1
  18. data/lib/aspera/ascp/products.rb +2 -8
  19. data/lib/aspera/cli/extended_value.rb +27 -14
  20. data/lib/aspera/cli/formatter.rb +35 -28
  21. data/lib/aspera/cli/main.rb +11 -11
  22. data/lib/aspera/cli/manager.rb +109 -94
  23. data/lib/aspera/cli/plugin.rb +4 -7
  24. data/lib/aspera/cli/plugin_factory.rb +10 -1
  25. data/lib/aspera/cli/plugins/aoc.rb +15 -14
  26. data/lib/aspera/cli/plugins/config.rb +35 -29
  27. data/lib/aspera/cli/plugins/faspex.rb +5 -4
  28. data/lib/aspera/cli/plugins/faspex5.rb +16 -13
  29. data/lib/aspera/cli/plugins/node.rb +50 -41
  30. data/lib/aspera/cli/plugins/orchestrator.rb +3 -2
  31. data/lib/aspera/cli/plugins/preview.rb +1 -1
  32. data/lib/aspera/cli/plugins/server.rb +2 -2
  33. data/lib/aspera/cli/plugins/shares.rb +11 -7
  34. data/lib/aspera/cli/special_values.rb +13 -0
  35. data/lib/aspera/cli/sync_actions.rb +73 -32
  36. data/lib/aspera/cli/transfer_agent.rb +3 -2
  37. data/lib/aspera/cli/transfer_progress.rb +1 -1
  38. data/lib/aspera/cli/version.rb +1 -1
  39. data/lib/aspera/environment.rb +100 -7
  40. data/lib/aspera/faspex_gw.rb +1 -1
  41. data/lib/aspera/keychain/encrypted_hash.rb +2 -0
  42. data/lib/aspera/log.rb +1 -0
  43. data/lib/aspera/node_simulator.rb +1 -1
  44. data/lib/aspera/oauth/jwt.rb +1 -1
  45. data/lib/aspera/oauth/url_json.rb +2 -0
  46. data/lib/aspera/oauth/web.rb +7 -6
  47. data/lib/aspera/rest.rb +46 -15
  48. data/lib/aspera/secret_hider.rb +3 -2
  49. data/lib/aspera/ssh.rb +1 -1
  50. data/lib/aspera/transfer/faux_file.rb +7 -5
  51. data/lib/aspera/transfer/parameters.rb +27 -19
  52. data/lib/aspera/transfer/spec.rb +8 -10
  53. data/lib/aspera/transfer/sync.rb +52 -47
  54. data/lib/aspera/web_auth.rb +0 -1
  55. data/lib/aspera/web_server_simple.rb +24 -13
  56. data.tar.gz.sig +0 -0
  57. metadata +5 -4
  58. metadata.gz.sig +0 -0
  59. data/examples/rubyc +0 -24
  60. data/lib/aspera/open_application.rb +0 -69
@@ -3,6 +3,7 @@
3
3
  # cspell:ignore initdemo genkey pubkey asperasoft filelists
4
4
  require 'aspera/cli/basic_auth_plugin'
5
5
  require 'aspera/cli/extended_value'
6
+ require 'aspera/cli/special_values'
6
7
  require 'aspera/cli/version'
7
8
  require 'aspera/cli/formatter'
8
9
  require 'aspera/cli/info'
@@ -15,7 +16,7 @@ require 'aspera/transfer/spec'
15
16
  require 'aspera/keychain/encrypted_hash'
16
17
  require 'aspera/keychain/macos_security'
17
18
  require 'aspera/proxy_auto_config'
18
- require 'aspera/open_application'
19
+ require 'aspera/environment'
19
20
  require 'aspera/persistency_action_once'
20
21
  require 'aspera/id_generator'
21
22
  require 'aspera/persistency_folder'
@@ -280,6 +281,8 @@ module Aspera
280
281
  attr_reader :option_ignore_cert_host_port, :progress_bar
281
282
 
282
283
  # add files, folders or default locations to the certificate store
284
+ # @param path_list [Array<String>] list of paths to add
285
+ # @return the list of paths
283
286
  def trusted_cert_locations=(path_list)
284
287
  path_list = [path_list] unless path_list.is_a?(Array)
285
288
  Aspera.assert_type(path_list, Array){'cert locations'}
@@ -293,12 +296,12 @@ module Aspera
293
296
  Aspera.assert_type(path, String){'Expecting a String for certificate location'}
294
297
  paths_to_add = [path]
295
298
  Log.log.debug{"Adding cert location: #{path}"}
296
- if path.eql?(ExtendedValue::DEF)
299
+ if path.eql?(SpecialValues::DEF)
297
300
  @certificate_store.set_default_paths
298
- paths_to_add = [
299
- OpenSSL::X509::DEFAULT_CERT_DIR,
300
- OpenSSL::X509::DEFAULT_CERT_FILE
301
- ].select{|f|File.exist?(f)}
301
+ paths_to_add = [OpenSSL::X509::DEFAULT_CERT_DIR]
302
+ # JRuby cert file seems not to be PEM
303
+ paths_to_add.push(OpenSSL::X509::DEFAULT_CERT_FILE) unless defined?(JRUBY_VERSION)
304
+ paths_to_add.select!{|f|File.exist?(f)}
302
305
  elsif File.file?(path)
303
306
  @certificate_store.add_file(path)
304
307
  elsif File.directory?(path)
@@ -324,7 +327,7 @@ module Aspera
324
327
  locations = @certificate_paths
325
328
  if locations.nil?
326
329
  # compute default locations
327
- self.trusted_cert_locations = ExtendedValue::DEF
330
+ self.trusted_cert_locations = SpecialValues::DEF
328
331
  locations = @certificate_paths
329
332
  # restore defaults
330
333
  @certificate_paths = @certificate_store = nil
@@ -577,7 +580,7 @@ module Aspera
577
580
  @config_checksum_on_disk = config_checksum
578
581
  end
579
582
  files_to_copy = []
580
- Log.log.debug{Log.dump('Available_presets', @config_presets)}
583
+ Log.log.trace1{Log.dump('Available_presets', @config_presets)}
581
584
  Aspera.assert_type(@config_presets, Hash){'config file YAML'}
582
585
  # check there is at least the config section
583
586
  Aspera.assert(@config_presets.key?(CONF_PRESET_CONFIG)){"Cannot find key: #{CONF_PRESET_CONFIG}"}
@@ -685,7 +688,7 @@ module Aspera
685
688
  api_connect_cdn.call(operation: 'GET', subpath: file_url, save_to_file: File.join(folder_dest, filename))
686
689
  return Main.result_status("Downloaded: #{filename}")
687
690
  when :open
688
- OpenApplication.instance.uri(one_link['href'])
691
+ Environment.instance.open_uri(one_link['href'])
689
692
  return Main.result_status("Opened: #{one_link['href']}")
690
693
  end
691
694
  end
@@ -698,8 +701,7 @@ module Aspera
698
701
  return execute_connect_action
699
702
  when :use
700
703
  ascp_path = options.get_next_argument('path to ascp')
701
- ascp_version = Ascp::Installation.instance.get_ascp_version(ascp_path)
702
- formatter.display_status("ascp version: #{ascp_version}")
704
+ formatter.display_status("ascp version: #{Ascp::Installation.instance.get_ascp_version(ascp_path)}")
703
705
  set_global_default(:ascp_path, ascp_path)
704
706
  return Main.result_nothing
705
707
  when :show
@@ -728,8 +730,8 @@ module Aspera
728
730
  when :install
729
731
  # reset to default location, if older default was used
730
732
  Ascp::Installation.instance.sdk_folder = self.class.default_app_main_folder(app_name: APP_NAME_SDK) if @sdk_default_location
731
- v = Ascp::Installation.instance.install_sdk(options.get_option(:sdk_url, mandatory: true))
732
- return Main.result_status("Installed version #{v}")
733
+ n, v = Ascp::Installation.instance.install_sdk(options.get_option(:sdk_url, mandatory: true))
734
+ return Main.result_status("Installed #{n} version #{v}")
733
735
  when :spec
734
736
  return {
735
737
  type: :object_list,
@@ -786,11 +788,11 @@ module Aspera
786
788
  when :set
787
789
  param_name = options.get_next_argument('parameter name')
788
790
  param_name = Manager.option_line_to_name(param_name)
789
- param_value = options.get_next_argument('parameter value')
791
+ param_value = options.get_next_argument('parameter value', validation: nil)
790
792
  set_preset_key(name, param_name, param_value)
791
793
  return Main.result_nothing
792
794
  when :initialize
793
- config_value = options.get_next_argument('extended value', type: Hash)
795
+ config_value = options.get_next_argument('extended value', validation: Hash)
794
796
  if @config_presets.key?(name)
795
797
  Log.log.warn{"configuration already exists: #{name}, overwriting"}
796
798
  end
@@ -804,10 +806,10 @@ module Aspera
804
806
  @config_presets[name].merge!(unprocessed_options)
805
807
  return Main.result_status("Updated: #{name}")
806
808
  when :ask
807
- options.ask_missing_mandatory = :yes
809
+ options.ask_missing_mandatory = true
808
810
  @config_presets[name] ||= {}
809
- options.get_next_argument('option names', expected: :multiple).each do |option_name|
810
- option_value = options.get_interactive(:option, option_name)
811
+ options.get_next_argument('option names', multiple: true).each do |option_name|
812
+ option_value = options.get_interactive(option_name, option: true)
811
813
  @config_presets[name][option_name] = option_value
812
814
  end
813
815
  return Main.result_status("Updated: #{name}")
@@ -870,7 +872,9 @@ module Aspera
870
872
  check_update
871
873
  initdemo
872
874
  vault
873
- throw].freeze
875
+ throw
876
+ platform
877
+ ].freeze
874
878
 
875
879
  # Main action procedure for plugin
876
880
  def execute_action
@@ -879,16 +883,16 @@ module Aspera
879
883
  when :preset # newer syntax
880
884
  return execute_preset
881
885
  when :open
882
- OpenApplication.editor(@option_config_file.to_s)
886
+ Environment.open_editor(@option_config_file.to_s)
883
887
  return Main.result_nothing
884
888
  when :documentation
885
889
  section = options.get_next_argument('private key file path', mandatory: false)
886
890
  section = "##{section}" unless section.nil?
887
- OpenApplication.instance.uri("#{@help}#{section}")
891
+ Environment.instance.open_uri("#{@help}#{section}")
888
892
  return Main.result_nothing
889
893
  when :genkey # generate new rsa key
890
894
  private_key_path = options.get_next_argument('private key file path')
891
- private_key_length = options.get_next_argument('size in bits', mandatory: false, type: Integer, default: DEFAULT_PRIV_KEY_LENGTH)
895
+ private_key_length = options.get_next_argument('size in bits', mandatory: false, validation: Integer, default: DEFAULT_PRIV_KEY_LENGTH)
892
896
  self.class.generate_rsa_private_key(path: private_key_path, length: private_key_length)
893
897
  return Main.result_status("Generated #{private_key_length} bit RSA key: #{private_key_path}")
894
898
  when :pubkey # get pub key
@@ -908,7 +912,7 @@ module Aspera
908
912
  return Main.result_status(remote_chain.first.subject.to_a.find { |name, _, _| name == 'CN' }[1])
909
913
  end
910
914
  when :echo # display the content of a value given on command line
911
- return Formatter.auto_type(options.get_next_argument('value'))
915
+ return Formatter.auto_type(options.get_next_argument('value', validation: nil))
912
916
  when :flush_tokens
913
917
  deleted_files = OAuth::Factory.instance.flush_tokens
914
918
  return {type: :value_list, data: deleted_files, name: 'file'}
@@ -927,8 +931,8 @@ module Aspera
927
931
  end
928
932
  return {type: :object_list, data: result, fields: %w[plugin detect wizard path]}
929
933
  when :create
930
- plugin_name = options.get_next_argument('name', expected: :single).downcase
931
- destination_folder = options.get_next_argument('folder', expected: :single, mandatory: false) || File.join(@main_folder, ASPERA_PLUGINS_FOLDERNAME)
934
+ plugin_name = options.get_next_argument('name').downcase
935
+ destination_folder = options.get_next_argument('folder', mandatory: false) || File.join(@main_folder, ASPERA_PLUGINS_FOLDERNAME)
932
936
  plugin_file = File.join(destination_folder, "#{plugin_name}.rb")
933
937
  content = <<~END_OF_PLUGIN_CODE
934
938
  require 'aspera/cli/plugin'
@@ -1014,6 +1018,8 @@ module Aspera
1014
1018
  exception_class = Object.const_get(exception_class_name)
1015
1019
  Aspera.assert(exception_class <= Exception){"#{exception_class} is not an exception: #{exception_class.class}"}
1016
1020
  raise exception_class, exception_text
1021
+ when :platform
1022
+ return Main.result_status(Environment.architecture)
1017
1023
  else Aspera.error_unreachable_line
1018
1024
  end
1019
1025
  end
@@ -1077,7 +1083,7 @@ module Aspera
1077
1083
  Log.log.debug{"wizard result: #{wizard_result}"}
1078
1084
  Aspera.assert(WIZARD_RESULT_KEYS.eql?(wizard_result.keys.sort)){"missing or extra keys in wizard result: #{wizard_result.keys}"}
1079
1085
  # get preset name from user or default
1080
- wiz_preset_name = options.get_option(:id)
1086
+ wiz_preset_name = nil
1081
1087
  if wiz_preset_name.nil?
1082
1088
  elements = [
1083
1089
  identification[:product],
@@ -1217,8 +1223,8 @@ module Aspera
1217
1223
  when :show
1218
1224
  return {type: :single_object, data: vault.get(label: options.get_next_argument('label'))}
1219
1225
  when :create
1220
- label = options.get_next_argument('label', type: String)
1221
- info = options.get_next_argument('info', type: Hash)
1226
+ label = options.get_next_argument('label', validation: String)
1227
+ info = options.get_next_argument('info', validation: Hash)
1222
1228
  info = info.symbolize_keys
1223
1229
  info[:label] = label
1224
1230
  vault.set(info)
@@ -1270,7 +1276,7 @@ module Aspera
1270
1276
  info[:password])
1271
1277
  when 'system'
1272
1278
  case Environment.os
1273
- when Environment::OS_X
1279
+ when Environment::OS_MACOS
1274
1280
  @vault = Keychain::MacosSystem.new(info[:name], info[:password])
1275
1281
  else
1276
1282
  raise 'not implemented for this OS'
@@ -5,11 +5,12 @@ require 'aspera/cli/basic_auth_plugin'
5
5
  require 'aspera/cli/plugins/node'
6
6
  require 'aspera/cli/plugins/config'
7
7
  require 'aspera/cli/extended_value'
8
+ require 'aspera/cli/special_values'
8
9
  require 'aspera/cli/transfer_agent'
9
10
  require 'aspera/transfer/uri'
10
11
  require 'aspera/transfer/spec'
11
12
  require 'aspera/persistency_action_once'
12
- require 'aspera/open_application'
13
+ require 'aspera/environment'
13
14
  require 'aspera/nagios'
14
15
  require 'aspera/id_generator'
15
16
  require 'aspera/log'
@@ -353,9 +354,9 @@ module Aspera
353
354
  delivery_id = instance_identifier
354
355
  raise 'empty id' if delivery_id.empty?
355
356
  recipient = options.get_option(:recipient)
356
- if delivery_id.eql?(ExtendedValue::ALL)
357
+ if delivery_id.eql?(SpecialValues::ALL)
357
358
  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)}}
358
- elsif delivery_id.eql?(ExtendedValue::INIT)
359
+ elsif delivery_id.eql?(SpecialValues::INIT)
359
360
  Aspera.assert(skip_ids_persistency){'Only with option once_only'}
360
361
  skip_ids_persistency.data.clear.concat(mailbox_filtered_entries.map{|i|{id: i[PACKAGE_MATCH_FIELD]}})
361
362
  skip_ids_persistency.save
@@ -390,7 +391,7 @@ module Aspera
390
391
  headers: {'Accept' => 'application/xml'},
391
392
  query: {passcode: link_data[:query]['passcode']})
392
393
  if !package_creation_data[:http].body.start_with?('<?xml ')
393
- OpenApplication.instance.uri(link_url)
394
+ Environment.instance.open_uri(link_url)
394
395
  raise Cli::Error, 'Unexpected response: package not found ?'
395
396
  end
396
397
  package_entry = XmlSimple.xml_in(package_creation_data[:http].body, {'ForceArray' => false})
@@ -4,6 +4,7 @@
4
4
 
5
5
  require 'aspera/cli/basic_auth_plugin'
6
6
  require 'aspera/cli/extended_value'
7
+ require 'aspera/cli/special_values'
7
8
  require 'aspera/persistency_action_once'
8
9
  require 'aspera/id_generator'
9
10
  require 'aspera/nagios'
@@ -81,7 +82,7 @@ module Aspera
81
82
  if options.get_option(:client_id).nil? || options.get_option(:client_secret).nil?
82
83
  formatter.display_status('Ask the ascli client id and secret to your Administrator.'.red)
83
84
  formatter.display_status("Admin should login to: #{instance_url}")
84
- OpenApplication.instance.uri(instance_url)
85
+ Environment.instance.open_uri(instance_url)
85
86
  formatter.display_status('Navigate to: 𓃑 → Admin → Configurations → API clients')
86
87
  formatter.display_status('Create an API client with:')
87
88
  formatter.display_status('- name: ascli')
@@ -118,7 +119,7 @@ module Aspera
118
119
  options.declare(:auth, 'OAuth type of authentication', values: STD_AUTH_TYPES, default: :jwt)
119
120
  options.declare(:private_key, 'OAuth JWT RSA private key PEM value (prefix file path with @file:)')
120
121
  options.declare(:passphrase, 'OAuth JWT RSA private key passphrase')
121
- options.declare(:box, "Package inbox, either shared inbox name or one of: #{API_LIST_MAILBOX_TYPES.join(', ')} or #{ExtendedValue::ALL}", default: 'inbox')
122
+ options.declare(:box, "Package inbox, either shared inbox name or one of: #{API_LIST_MAILBOX_TYPES.join(', ')} or #{SpecialValues::ALL}", default: 'inbox')
122
123
  options.declare(:shared_folder, 'Send package with files from shared folder')
123
124
  options.declare(:group_type, 'Type of shared box', values: %i[shared_inboxes workgroups], default: :shared_inboxes)
124
125
  options.parse_options!
@@ -304,12 +305,12 @@ module Aspera
304
305
 
305
306
  # list all packages with optional filter
306
307
  def list_packages_with_filter(query: {})
307
- filter = options.get_next_argument('filter', mandatory: false, type: Proc, default: ->(_x){true})
308
+ filter = options.get_next_argument('filter', mandatory: false, validation: Proc, default: ->(_x){true})
308
309
  # translate box name to API prefix (with ending slash)
309
310
  box = options.get_option(:box)
310
311
  real_path =
311
312
  case box
312
- when ExtendedValue::ALL then 'packages' # only admin can list all packages globally
313
+ when SpecialValues::ALL then 'packages' # only admin can list all packages globally
313
314
  when *API_LIST_MAILBOX_TYPES then "#{box}/packages"
314
315
  else
315
316
  group_type = options.get_option(:group_type)
@@ -338,12 +339,12 @@ module Aspera
338
339
  end
339
340
  packages = []
340
341
  case package_ids
341
- when ExtendedValue::INIT
342
+ when SpecialValues::INIT
342
343
  Aspera.assert(skip_ids_persistency){'Only with option once_only'}
343
344
  skip_ids_persistency.data.clear.concat(list_packages_with_filter.map{|p|p['id']})
344
345
  skip_ids_persistency.save
345
346
  return Main.result_status("Initialized skip for #{skip_ids_persistency.data.count} package(s)")
346
- when ExtendedValue::ALL
347
+ when SpecialValues::ALL
347
348
  # TODO: if packages have same name, they will overwrite ?
348
349
  packages = list_packages_with_filter(query: {'status' => 'completed'})
349
350
  Log.log.trace1{Log.dump(:package_ids, packages.map{|p|p['id']})}
@@ -562,10 +563,12 @@ module Aspera
562
563
  res_command = options.get_next_command(available_commands)
563
564
  case res_command
564
565
  when *Plugin::ALL_OPS
565
- return entity_command(res_command, adm_api, res_path, item_list_key: list_key, display_fields: display_fields, id_as_arg: id_as_arg, delete_style: delete_style) do |field, value|
566
- lookup_entity_by_field(
567
- type: res_type, value: value, field: field, real_path: res_path, item_list_key: list_key, query: res_id_query)['id']
568
- end
566
+ return entity_command(
567
+ res_command, adm_api, res_path, item_list_key: list_key, display_fields: display_fields, id_as_arg: id_as_arg,
568
+ delete_style: delete_style) do |field, value|
569
+ lookup_entity_by_field(
570
+ type: res_type, value: value, field: field, real_path: res_path, item_list_key: list_key, query: res_id_query)['id']
571
+ end
569
572
  when :shared_folders
570
573
  node_id = instance_identifier do |field, value|
571
574
  lookup_entity_by_field(type: res_type, field: field, value: value)['id']
@@ -626,7 +629,7 @@ module Aspera
626
629
  user
627
630
  end
628
631
  end
629
- access = options.get_next_argument('level', mandatory: false, expected: %i[submit_only standard shared_inbox_admin], default: :standard)
632
+ access = options.get_next_argument('level', mandatory: false, accept_list: %i[submit_only standard shared_inbox_admin], default: :standard)
630
633
  # TODO: unshift to command line parameters instead of using deprecated option "value"
631
634
  options.set_option(:value, {user: users.map{|u|{id: u, access: access}}})
632
635
  end
@@ -644,7 +647,7 @@ module Aspera
644
647
  command = options.get_next_command(%i[configuration smtp resource events clean_deleted].concat(ADMIN_RESOURCES).freeze)
645
648
  case command
646
649
  when :resource
647
- # resource is will be deprecated
650
+ # resource will be deprecated
648
651
  Log.log.warn('resource command is deprecated (4.18), directly use the specific command instead')
649
652
  return execute_resource(options.get_next_command(ADMIN_RESOURCES))
650
653
  when *ADMIN_RESOURCES
@@ -722,7 +725,7 @@ module Aspera
722
725
  when :show
723
726
  return { type: :single_object, data: @api_v5.read('account/preferences')[:data] }
724
727
  when :modify
725
- @api_v5.update('account/preferences', options.get_next_argument('modified parameters', type: Hash))
728
+ @api_v5.update('account/preferences', options.get_next_argument('modified parameters', validation: Hash))
726
729
  return Main.result_status('modified')
727
730
  end
728
731
  end
@@ -3,6 +3,7 @@
3
3
  # cspell:ignore snid fnid bidi ssync asyncs rund asnodeadmin mkfile mklink asperabrowser asperabrowserurl watchfolders watchfolderd entsrv
4
4
  require 'aspera/cli/basic_auth_plugin'
5
5
  require 'aspera/cli/sync_actions'
6
+ require 'aspera/cli/special_values'
6
7
  require 'aspera/transfer/spec'
7
8
  require 'aspera/nagios'
8
9
  require 'aspera/hash_ext'
@@ -162,7 +163,7 @@ module Aspera
162
163
  # translates paths results into CLI result, and removes prefix
163
164
  def c_result_translate_rem_prefix(response, type, success_msg, path_prefix)
164
165
  errors = []
165
- final_result = { data: [], type: :object_list, fields: [type, 'result']}
166
+ final_result = {type: :object_list, data: [], fields: [type, 'result']}
166
167
  JSON.parse(response[:http].body)['paths'].each do |p|
167
168
  result = success_msg
168
169
  if p.key?('error')
@@ -179,20 +180,11 @@ module Aspera
179
180
  return c_result_remove_prefix_path(final_result, type, path_prefix)
180
181
  end
181
182
 
182
- # get path arguments from command line, and add prefix
183
- def get_next_arg_add_prefix(path_prefix, name, number=:single)
184
- path_or_list = options.get_next_argument(name, expected: number)
185
- return path_or_list if path_prefix.nil?
186
- return File.join(path_prefix, path_or_list) if path_or_list.is_a?(String)
187
- return path_or_list.map {|p| File.join(path_prefix, p)} if path_or_list.is_a?(Array)
188
- raise StandardError, 'expect: nil, String or Array'
189
- end
190
-
191
183
  # directory: node, container: shares
192
184
  FOLDER_TYPE = %w[directory container].freeze
193
185
 
194
186
  def browse_gen3(prefix_path)
195
- folders_to_process = [get_next_arg_add_prefix(prefix_path, 'path')]
187
+ folders_to_process = [get_one_argument_with_prefix(prefix_path, 'path')]
196
188
  query = options.get_option(:query, default: {})
197
189
  # special parameter: max number of entries in result
198
190
  max_items = query.delete('max')
@@ -254,11 +246,12 @@ module Aspera
254
246
  def execute_command_gen3(command, prefix_path)
255
247
  case command
256
248
  when :delete
257
- paths_to_delete = get_next_arg_add_prefix(prefix_path, 'file list', :multiple)
249
+ # TODO: add query for recursive
250
+ paths_to_delete = get_all_arguments_with_prefix(prefix_path, 'file list')
258
251
  resp = @api_node.create('files/delete', { paths: paths_to_delete.map{|i| {'path' => i.start_with?('/') ? i : "/#{i}"} }})
259
252
  return c_result_translate_rem_prefix(resp, 'file', 'deleted', prefix_path)
260
253
  when :search
261
- search_root = get_next_arg_add_prefix(prefix_path, 'search root')
254
+ search_root = get_one_argument_with_prefix(prefix_path, 'search root')
262
255
  parameters = {'path' => search_root}
263
256
  other_options = query_option
264
257
  parameters.merge!(other_options) unless other_options.nil?
@@ -270,31 +263,30 @@ module Aspera
270
263
  formatter.display_status("params: #{resp[:data]['parameters'].keys.map{|k|"#{k}:#{resp[:data]['parameters'][k]}"}.join(',')}")
271
264
  return c_result_remove_prefix_path(result, 'path', prefix_path)
272
265
  when :space
273
- path_list = get_next_arg_add_prefix(prefix_path, 'folder path or ext.val. list')
274
- path_list = [path_list] unless path_list.is_a?(Array)
266
+ path_list = get_all_arguments_with_prefix(prefix_path, 'folder path or ext.val. list')
275
267
  resp = @api_node.create('space', { 'paths' => path_list.map {|i| { path: i} } })
276
- result = { data: resp[:data]['paths'], type: :object_list}
268
+ result = { type: :object_list, data: resp[:data]['paths']}
277
269
  # return c_result_translate_rem_prefix(resp,'folder','created',prefix_path)
278
270
  return c_result_remove_prefix_path(result, 'path', prefix_path)
279
271
  when :mkdir
280
- path_list = get_next_arg_add_prefix(prefix_path, 'folder path or ext.val. list')
281
- path_list = [path_list] unless path_list.is_a?(Array)
282
- resp = @api_node.create('files/create', { 'paths' => [{ type: :directory, path: path_list }] })
272
+ path_list = get_all_arguments_with_prefix(prefix_path, 'folder path or ext.val. list')
273
+ resp = @api_node.create('files/create', { 'paths' => path_list.map{|i|{ type: :directory, path: i }}})
283
274
  return c_result_translate_rem_prefix(resp, 'folder', 'created', prefix_path)
284
275
  when :mklink
285
- target = get_next_arg_add_prefix(prefix_path, 'target')
286
- path_list = get_next_arg_add_prefix(prefix_path, 'link path')
287
- resp = @api_node.create('files/create', { 'paths' => [{ type: :symbolic_link, path: path_list, target: { path: target} }] })
276
+ target = get_one_argument_with_prefix(prefix_path, 'target')
277
+ one_path = get_one_argument_with_prefix(prefix_path, 'link path')
278
+ resp = @api_node.create('files/create', { 'paths' => [{ type: :symbolic_link, path: one_path, target: { path: target} }] })
288
279
  return c_result_translate_rem_prefix(resp, 'folder', 'created', prefix_path)
289
280
  when :mkfile
290
- path_list = get_next_arg_add_prefix(prefix_path, 'file path')
281
+ one_path = get_one_argument_with_prefix(prefix_path, 'file path')
291
282
  contents64 = Base64.strict_encode64(options.get_next_argument('contents'))
292
- resp = @api_node.create('files/create', { 'paths' => [{ type: :file, path: path_list, contents: contents64 }] })
283
+ resp = @api_node.create('files/create', { 'paths' => [{ type: :file, path: one_path, contents: contents64 }] })
293
284
  return c_result_translate_rem_prefix(resp, 'folder', 'created', prefix_path)
294
285
  when :rename
295
- path_base = get_next_arg_add_prefix(prefix_path, 'path_base')
296
- path_src = get_next_arg_add_prefix(prefix_path, 'path_src')
297
- path_dst = get_next_arg_add_prefix(prefix_path, 'path_dst')
286
+ # TODO: multiple ?
287
+ path_base = get_one_argument_with_prefix(prefix_path, 'path_base')
288
+ path_src = get_one_argument_with_prefix(prefix_path, 'path_src')
289
+ path_dst = get_one_argument_with_prefix(prefix_path, 'path_dst')
298
290
  resp = @api_node.create('files/rename', { 'paths' => [{ 'path' => path_base, 'source' => path_src, 'destination' => path_dst }] })
299
291
  return c_result_translate_rem_prefix(resp, 'entry', 'moved', prefix_path)
300
292
  when :browse
@@ -346,7 +338,7 @@ module Aspera
346
338
  transfer_spec.delete('paths') if command.eql?(:upload)
347
339
  return Main.result_transfer(transfer.start(transfer_spec))
348
340
  when :http_node_download
349
- remote_path = get_next_arg_add_prefix(prefix_path, 'remote path')
341
+ remote_path = get_one_argument_with_prefix(prefix_path, 'remote path')
350
342
  file_name = File.basename(remote_path)
351
343
  @api_node.call(
352
344
  operation: 'GET',
@@ -390,7 +382,7 @@ module Aspera
390
382
  when :set_bearer_key
391
383
  access_key_id = options.get_next_argument('access key id')
392
384
  access_key_id = @api_node.read('access_keys/self')[:data]['id'] if access_key_id.eql?('self')
393
- bearer_key_pem = options.get_next_argument('public or private RSA key PEM value', type: String)
385
+ bearer_key_pem = options.get_next_argument('public or private RSA key PEM value', validation: String)
394
386
  key = OpenSSL::PKey.read(bearer_key_pem)
395
387
  key = key.public_key if key.private?
396
388
  bearer_key_pem = key.to_pem
@@ -539,16 +531,17 @@ module Aspera
539
531
  # if a single file: split into folder and path
540
532
  apifid = @api_node.resolve_api_fid(top_file_id, source_folder)
541
533
  if source_paths.empty?
534
+ # get precise info in this element
542
535
  file_info = apifid[:api].read("files/#{apifid[:file_id]}")[:data]
543
536
  case file_info['type']
544
537
  when 'file'
545
538
  # if the single source is a file, we need to split into folder path and filename
546
539
  src_dir_elements = source_folder.split(Api::Node::PATH_SEPARATOR)
547
- # filename is the last one
540
+ # filename is the last one, source folder is what remains
548
541
  source_paths = [{'source' => src_dir_elements.pop}]
549
- # source folder is what remains
542
+ # add trailing / so that link is resolved, if it's a shared folder
543
+ src_dir_elements.push('')
550
544
  source_folder = src_dir_elements.join(Api::Node::PATH_SEPARATOR)
551
- # TODO: instead of creating a new object, use the same, and change file id with parent folder id ? possible ?
552
545
  apifid = @api_node.resolve_api_fid(top_file_id, source_folder)
553
546
  when 'link', 'folder'
554
547
  # single source is 'folder' or 'link'
@@ -581,7 +574,7 @@ module Aspera
581
574
  return {type: :single_object, data: items}
582
575
  when :modify
583
576
  apifid = apifid_from_next_arg(top_file_id)
584
- update_param = options.get_next_argument('update data', type: Hash)
577
+ update_param = options.get_next_argument('update data', validation: Hash)
585
578
  apifid[:api].update("files/#{apifid[:file_id]}", update_param)[:data]
586
579
  return Main.result_status('Done')
587
580
  when :thumbnail
@@ -612,7 +605,7 @@ module Aspera
612
605
  {'id' => one_id}
613
606
  end
614
607
  when :create
615
- create_param = options.get_next_argument('creation data', type: Hash)
608
+ create_param = options.get_next_argument('creation data', validation: Hash)
616
609
  raise 'no file_id' if create_param.key?('file_id')
617
610
  create_param['file_id'] = apifid[:file_id]
618
611
  create_param['access_levels'] = Api::Node::ACCESS_LEVELS unless create_param.key?('access_levels')
@@ -638,7 +631,7 @@ module Aspera
638
631
  async_name = options.get_option(:sync_name)
639
632
  if async_name.nil?
640
633
  async_id = instance_identifier
641
- if async_id.eql?(ExtendedValue::ALL) && %i[show delete].include?(command)
634
+ if async_id.eql?(SpecialValues::ALL) && %i[show delete].include?(command)
642
635
  async_ids = @api_node.read('async/list')[:data]['sync_ids']
643
636
  else
644
637
  Integer(async_id) # must be integer
@@ -661,7 +654,7 @@ module Aspera
661
654
  when :show
662
655
  resp = @api_node.create('async/summary', post_data)[:data]['sync_summaries']
663
656
  return Main.result_empty if resp.empty?
664
- return { type: :object_list, data: resp, fields: %w[snid name local_dir remote_dir] } if async_id.eql?(ExtendedValue::ALL)
657
+ return { type: :object_list, data: resp, fields: %w[snid name local_dir remote_dir] } if async_id.eql?(SpecialValues::ALL)
665
658
  return { type: :single_object, data: resp.first }
666
659
  when :delete
667
660
  resp = @api_node.create('async/delete', post_data)[:data]
@@ -815,7 +808,7 @@ module Aspera
815
808
  resp = @api_node.read(one_res_path)
816
809
  return { type: :other_struct, data: resp[:data] }
817
810
  when :modify
818
- resp = @api_node.update(one_res_path, options.get_next_argument('update value', type: Hash))
811
+ resp = @api_node.update(one_res_path, options.get_next_argument('update value', validation: Hash))
819
812
  return { type: :other_struct, data: resp[:data] }
820
813
  when :bandwidth_average
821
814
  transfers_data = @api_node.read(res_class_path, query_read_delete)[:data]
@@ -876,7 +869,7 @@ module Aspera
876
869
  return { type: :object_list, data: resp[:data]['services'] }
877
870
  when :create
878
871
  # @json:'{"type":"WATCHFOLDERD","run_as":{"user":"user1"}}'
879
- params = options.get_next_argument('Run creation data (structure)')
872
+ params = options.get_next_argument('creation data', validation: Hash)
880
873
  resp = @api_node.create('rund/services', params)
881
874
  return Main.result_status("#{resp[:data]['id']} created")
882
875
  when :delete
@@ -952,13 +945,13 @@ module Aspera
952
945
  }
953
946
  # encode parameters so that it looks good in url
954
947
  encoded_params = Base64.strict_encode64(Zlib::Deflate.deflate(JSON.generate(browse_params))).gsub(/=+$/, '').tr('+/', '-_').reverse
955
- OpenApplication.instance.uri("#{options.get_option(:asperabrowserurl)}?goto=#{encoded_params}")
948
+ Environment.instance.open_uri("#{options.get_option(:asperabrowserurl)}?goto=#{encoded_params}")
956
949
  return Main.result_status('done')
957
950
  when :basic_token
958
951
  return Main.result_status(Rest.basic_token(options.get_option(:username, mandatory: true), options.get_option(:password, mandatory: true)))
959
952
  when :bearer_token
960
- private_key = OpenSSL::PKey::RSA.new(options.get_next_argument('private RSA key PEM value', type: String))
961
- token_info = options.get_next_argument('user and group identification', type: Hash)
953
+ private_key = OpenSSL::PKey::RSA.new(options.get_next_argument('private RSA key PEM value', validation: String))
954
+ token_info = options.get_next_argument('user and group identification', validation: Hash)
962
955
  access_key = options.get_option(:username, mandatory: true)
963
956
  return Main.result_status(Api::Node.bearer_token(payload: token_info, access_key: access_key, private_key: private_key))
964
957
  when :simulator
@@ -974,6 +967,22 @@ module Aspera
974
967
  end
975
968
  raise 'ERROR: shall not reach this line'
976
969
  end
970
+
971
+ private
972
+
973
+ # get remaining path arguments from command line, and add prefix
974
+ def get_all_arguments_with_prefix(path_prefix, name)
975
+ path_args = options.get_next_argument(name, multiple: true)
976
+ return path_args if path_prefix.nil?
977
+ return path_args.map {|p| File.join(path_prefix, p)}
978
+ end
979
+
980
+ # get next path argument from command line, and add prefix
981
+ def get_one_argument_with_prefix(path_prefix, name)
982
+ path_arg = options.get_next_argument(name, validation: String)
983
+ return path_arg if path_prefix.nil?
984
+ return File.join(path_prefix, path_arg)
985
+ end
977
986
  end
978
987
  end
979
988
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'aspera/cli/basic_auth_plugin'
4
+ require 'aspera/cli/special_values'
4
5
  require 'aspera/nagios'
5
6
  require 'aspera/log'
6
7
  require 'aspera/assert'
@@ -150,7 +151,7 @@ module Aspera
150
151
  end
151
152
  case command
152
153
  when :status
153
- wf_id = nil if wf_id.eql?(ExtendedValue::ALL)
154
+ wf_id = nil if wf_id.eql?(SpecialValues::ALL)
154
155
  result = call_ao('workflows_status', id: wf_id)[:data]
155
156
  return {type: :object_list, data: result['workflows']['workflow']}
156
157
  when :list
@@ -176,7 +177,7 @@ module Aspera
176
177
  }
177
178
  call_params = {format: :json}
178
179
  # get external parameters if any
179
- options.get_next_argument('external_parameters', mandatory: false, type: Hash, default: {}).each do |name, value|
180
+ options.get_next_argument('external_parameters', mandatory: false, validation: Hash, default: {}).each do |name, value|
180
181
  call_params["external_parameters[#{name}]"] = value
181
182
  end
182
183
  # synchronous call ?
@@ -489,7 +489,7 @@ module Aspera
489
489
  return Main.result_status('Tools validated')
490
490
  when :test, :show
491
491
  source = options.get_next_argument('source file')
492
- format = options.get_next_argument('format', expected: Aspera::Preview::Generator::PREVIEW_FORMATS, default: :png)
492
+ format = options.get_next_argument('format', accept_list: Aspera::Preview::Generator::PREVIEW_FORMATS, default: :png)
493
493
  generated_file_path = preview_filename(format, options.get_option(:base))
494
494
  g = Aspera::Preview::Generator.new(source, generated_file_path, @gen_options, @tmp_folder, nil)
495
495
  g.generate
@@ -176,7 +176,7 @@ module Aspera
176
176
  def execute_transfer(command, transfer_spec)
177
177
  case command
178
178
  when :upload, :download
179
- Transfer::Spec.action_to_direction(transfer_spec, command)
179
+ transfer_spec['direction'] = Transfer::Spec.transfer_type_to_direction(command)
180
180
  return Main.result_transfer(transfer.start(transfer_spec))
181
181
  when :sync
182
182
  # lets ignore the arguments provided by execute_sync_action, we just give the transfer spec
@@ -232,7 +232,7 @@ module Aspera
232
232
  when *TRANSFER_COMMANDS
233
233
  return execute_transfer(command, server_transfer_spec)
234
234
  when *AsCmd::OPERATIONS
235
- command_arguments = options.get_next_argument('ascmd command arguments', expected: :multiple, mandatory: false)
235
+ command_arguments = options.get_next_argument('ascmd command arguments', multiple: true, mandatory: false)
236
236
  ascmd = AsCmd.new(ascmd_executor)
237
237
  begin
238
238
  result = ascmd.execute_single(command, command_arguments)