aspera-cli 4.24.0 → 4.24.2

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 (87) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +19 -1
  4. data/README.md +1264 -941
  5. data/bin/ascli +20 -1
  6. data/bin/asession +23 -27
  7. data/lib/aspera/agent/base.rb +10 -21
  8. data/lib/aspera/agent/connect.rb +2 -3
  9. data/lib/aspera/agent/desktop.rb +2 -2
  10. data/lib/aspera/agent/direct.rb +49 -32
  11. data/lib/aspera/agent/factory.rb +31 -0
  12. data/lib/aspera/api/aoc.rb +79 -49
  13. data/lib/aspera/api/faspex.rb +212 -0
  14. data/lib/aspera/api/node.rb +99 -84
  15. data/lib/aspera/ascp/installation.rb +22 -21
  16. data/lib/aspera/ascp/management.rb +119 -23
  17. data/lib/aspera/assert.rb +14 -8
  18. data/lib/aspera/cli/extended_value.rb +15 -15
  19. data/lib/aspera/cli/formatter.rb +7 -5
  20. data/lib/aspera/cli/hints.rb +8 -0
  21. data/lib/aspera/cli/info.rb +4 -4
  22. data/lib/aspera/cli/main.rb +56 -71
  23. data/lib/aspera/cli/manager.rb +7 -4
  24. data/lib/aspera/cli/plugins/alee.rb +2 -1
  25. data/lib/aspera/cli/plugins/aoc.rb +110 -186
  26. data/lib/aspera/cli/plugins/ats.rb +4 -4
  27. data/lib/aspera/cli/plugins/base.rb +335 -0
  28. data/lib/aspera/cli/plugins/basic_auth.rb +45 -0
  29. data/lib/aspera/cli/plugins/config.rb +263 -221
  30. data/lib/aspera/cli/plugins/console.rb +15 -15
  31. data/lib/aspera/cli/plugins/cos.rb +2 -2
  32. data/lib/aspera/cli/plugins/factory.rb +78 -0
  33. data/lib/aspera/cli/plugins/faspex.rb +17 -20
  34. data/lib/aspera/cli/plugins/faspex5.rb +79 -193
  35. data/lib/aspera/cli/plugins/faspio.rb +14 -13
  36. data/lib/aspera/cli/plugins/httpgw.rb +13 -12
  37. data/lib/aspera/cli/plugins/node.rb +34 -32
  38. data/lib/aspera/cli/plugins/oauth.rb +48 -0
  39. data/lib/aspera/cli/plugins/orchestrator.rb +15 -13
  40. data/lib/aspera/cli/plugins/preview.rb +4 -4
  41. data/lib/aspera/cli/plugins/server.rb +15 -13
  42. data/lib/aspera/cli/plugins/shares.rb +18 -15
  43. data/lib/aspera/cli/sync_actions.rb +1 -1
  44. data/lib/aspera/cli/transfer_agent.rb +24 -20
  45. data/lib/aspera/cli/transfer_progress.rb +6 -6
  46. data/lib/aspera/cli/version.rb +3 -3
  47. data/lib/aspera/cli/wizard.rb +74 -65
  48. data/lib/aspera/colors.rb +6 -0
  49. data/lib/aspera/command_line_builder.rb +45 -50
  50. data/lib/aspera/command_line_converter.rb +2 -1
  51. data/lib/aspera/coverage.rb +1 -1
  52. data/lib/aspera/data_repository.rb +1 -1
  53. data/lib/aspera/environment.rb +13 -9
  54. data/lib/aspera/faspex_gw.rb +6 -4
  55. data/lib/aspera/faspex_postproc.rb +1 -1
  56. data/lib/aspera/keychain/macos_security.rb +1 -1
  57. data/lib/aspera/log.rb +88 -37
  58. data/lib/aspera/nagios.rb +1 -1
  59. data/lib/aspera/oauth/base.rb +17 -10
  60. data/lib/aspera/oauth/factory.rb +8 -8
  61. data/lib/aspera/oauth/web.rb +2 -2
  62. data/lib/aspera/products/connect.rb +4 -3
  63. data/lib/aspera/products/desktop.rb +1 -4
  64. data/lib/aspera/products/other.rb +9 -1
  65. data/lib/aspera/products/transferd.rb +0 -1
  66. data/lib/aspera/rest.rb +126 -83
  67. data/lib/aspera/ssh.rb +3 -3
  68. data/lib/aspera/sync/args.schema.yaml +46 -3
  69. data/lib/aspera/sync/conf.schema.yaml +130 -94
  70. data/lib/aspera/sync/operations.rb +71 -74
  71. data/lib/aspera/temp_file_manager.rb +17 -5
  72. data/lib/aspera/transfer/error.rb +16 -7
  73. data/lib/aspera/transfer/parameters.rb +34 -20
  74. data/lib/aspera/transfer/resumer.rb +74 -0
  75. data/lib/aspera/transfer/spec.rb +4 -3
  76. data/lib/aspera/transfer/spec.schema.yaml +132 -51
  77. data/lib/aspera/transfer/spec_doc.rb +41 -35
  78. data/lib/aspera/uri_reader.rb +1 -1
  79. data/lib/aspera/web_auth.rb +6 -6
  80. data.tar.gz.sig +0 -0
  81. metadata +9 -7
  82. metadata.gz.sig +2 -2
  83. data/lib/aspera/cli/basic_auth_plugin.rb +0 -43
  84. data/lib/aspera/cli/plugin.rb +0 -333
  85. data/lib/aspera/cli/plugin_factory.rb +0 -81
  86. data/lib/aspera/resumer.rb +0 -77
  87. data/lib/aspera/transfer/error_info.rb +0 -91
@@ -1,9 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'aspera/oauth/jwt'
4
+ require 'aspera/assert'
5
+ require 'aspera/cli/plugins/factory'
4
6
 
5
7
  module Aspera
6
8
  module Cli
9
+ # The wizard detects applications and generates a config
7
10
  class Wizard
8
11
  WIZARD_RESULT_KEYS = %i[preset_value test_args].freeze
9
12
  DEFAULT_PRIV_KEY_FILENAME = 'my_private_key.pem' # pragma: allowlist secret
@@ -13,13 +16,17 @@ module Aspera
13
16
  def initialize(parent, main_folder)
14
17
  @parent = parent
15
18
  @main_folder = main_folder
16
- # wizard options
19
+ # Wizard options
17
20
  options.declare(:override, 'Wizard: override existing value', values: :bool, default: :no)
18
21
  options.declare(:default, 'Wizard: set as default configuration for specified plugin (also: update)', values: :bool, default: true)
19
- options.declare(:test_mode, 'Wizard: skip private key check step', values: :bool, default: false)
20
22
  options.declare(:key_path, 'Wizard: path to private key for JWT')
21
23
  end
22
24
 
25
+ # @return false if in test mode to avoid interactive input
26
+ def required
27
+ !ENV['ASCLI_WIZ_TEST']
28
+ end
29
+
23
30
  def options
24
31
  @parent.options
25
32
  end
@@ -28,6 +35,14 @@ module Aspera
28
35
  @parent.formatter
29
36
  end
30
37
 
38
+ def config
39
+ @parent.config
40
+ end
41
+
42
+ def check_email(email)
43
+ Aspera.assert(email =~ /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i, type: ParameterError){"Username shall be an email: #{email}"}
44
+ end
45
+
31
46
  # Find a plugin, and issue the "require"
32
47
  # @return [Hash] plugin info: { product:, name:, url:, version: }
33
48
  def identify_plugins_for_url
@@ -36,19 +51,19 @@ module Aspera
36
51
  check_only = check_only.to_sym unless check_only.nil?
37
52
  found_apps = []
38
53
  my_self_plugin_sym = self.class.name.split('::').last.downcase.to_sym
39
- PluginFactory.instance.plugin_list.each do |plugin_name_sym|
40
- # no detection for internal plugin
54
+ Plugins::Factory.instance.plugin_list.each do |plugin_name_sym|
55
+ # No detection for internal plugin
41
56
  next if plugin_name_sym.eql?(my_self_plugin_sym)
42
57
  next if check_only && !check_only.eql?(plugin_name_sym)
43
- # load plugin class
44
- detect_plugin_class = PluginFactory.instance.plugin_class(plugin_name_sym)
45
- # requires detection method
46
- next unless detect_plugin_class.respond_to?(:detect)
58
+ # Load plugin class
59
+ plugin_klass = Plugins::Factory.instance.plugin_class(plugin_name_sym)
60
+ # Requires detection method
61
+ next unless plugin_klass.respond_to?(:detect)
47
62
  detection_info = nil
48
63
  begin
49
64
  Log.log.debug{"detecting #{plugin_name_sym} at #{app_url}"}
50
65
  formatter.long_operation_running("#{plugin_name_sym}\r")
51
- detection_info = detect_plugin_class.detect(app_url)
66
+ detection_info = plugin_klass.detect(app_url)
52
67
  rescue OpenSSL::SSL::SSLError => e
53
68
  Log.log.warn(e.message)
54
69
  Log.log.warn('Use option --insecure=yes to allow unchecked certificate') if e.message.include?('cert')
@@ -59,8 +74,8 @@ module Aspera
59
74
  next if detection_info.nil?
60
75
  Aspera.assert_type(detection_info, Hash)
61
76
  Aspera.assert_type(detection_info[:url], String) if detection_info.key?(:url)
62
- app_name = detect_plugin_class.respond_to?(:application_name) ? detect_plugin_class.application_name : detect_plugin_class.name.split('::').last
63
- # if there is a redirect, then the detector can override the url.
77
+ app_name = plugin_klass.respond_to?(:application_name) ? plugin_klass.application_name : plugin_klass.name.split('::').last
78
+ # If there is a redirect, then the detector can override the url.
64
79
  found_apps.push({product: plugin_name_sym, name: app_name, url: app_url, version: 'unknown'}.merge(detection_info))
65
80
  end
66
81
  raise "No known application found at #{app_url}" if found_apps.empty?
@@ -68,6 +83,41 @@ module Aspera
68
83
  return found_apps
69
84
  end
70
85
 
86
+ # To be called in public wizard method to get private key
87
+ # @return [Array] Private key path, pub key PEM
88
+ def ask_private_key(user:, url:, page:)
89
+ # Lets see if path to priv key is provided
90
+ private_key_path = options.get_option(:key_path)
91
+ # Give a chance to provide
92
+ if private_key_path.nil?
93
+ formatter.display_status('Path to private RSA key (leave empty to generate):')
94
+ private_key_path = options.get_option(:key_path, mandatory: true).to_s
95
+ end
96
+ # Else generate path
97
+ private_key_path = File.join(@main_folder, DEFAULT_PRIV_KEY_FILENAME) if private_key_path.empty?
98
+ if File.exist?(private_key_path)
99
+ formatter.display_status('Using existing key:')
100
+ else
101
+ formatter.display_status("Generating #{OAuth::Jwt::DEFAULT_PRIV_KEY_LENGTH} bit RSA key...")
102
+ OAuth::Jwt.generate_rsa_private_key(path: private_key_path)
103
+ formatter.display_status('Created key:')
104
+ end
105
+ formatter.display_status(private_key_path)
106
+ private_key_pem = File.read(private_key_path)
107
+ pub_key_pem = OpenSSL::PKey::RSA.new(private_key_pem).public_key.to_s
108
+ options.set_option(:private_key, private_key_pem)
109
+ formatter.display_status("Please Log in as user #{user.red} at: #{url.red}")
110
+ formatter.display_status("Navigate to: #{page}")
111
+ formatter.display_status("Check or update the value to (#{'including BEGIN/END lines'.red}):".blink)
112
+ formatter.display_status(pub_key_pem, hide_secrets: false)
113
+ formatter.display_status('Once updated or validated, press [Enter].')
114
+ Environment.instance.open_uri(url)
115
+ $stdin.gets if required
116
+ private_key_path
117
+ end
118
+
119
+ # Wizard function, creates configuration
120
+ # @param apps [Array] list of detected apps
71
121
  def find(apps)
72
122
  identification = if apps.length.eql?(1)
73
123
  Log.log.debug{"Detected: #{identification}"}
@@ -82,49 +132,18 @@ module Aspera
82
132
  Log.dump(:identification, identification)
83
133
  wiz_url = identification[:url]
84
134
  formatter.display_status("Using: #{identification[:name]} at #{wiz_url}".bold)
85
- # set url for instantiation of plugin
135
+ # Set url for instantiation of plugin
86
136
  options.add_option_preset({url: wiz_url}, 'wizard')
87
- # instantiate plugin: command line options will be known and wizard can be called
88
- wiz_plugin_class = PluginFactory.instance.plugin_class(identification[:product])
89
- Aspera.assert(wiz_plugin_class.respond_to?(:wizard), type: Cli::BadArgument) do
137
+ # Instantiate plugin: command line options will be known, e.g. private_key, and wizard can be called
138
+ plugin_instance = Plugins::Factory.instance.plugin_class(identification[:product]).new(context: @parent.context)
139
+ Aspera.assert(plugin_instance.respond_to?(:wizard), type: Cli::BadArgument) do
90
140
  "Detected: #{identification[:product]}, but this application has no wizard"
91
141
  end
92
- # instantiate plugin: command line options will be known, e.g. private_key
93
- plugin_instance = wiz_plugin_class.new(context: @parent.context)
94
- wiz_params = {
95
- object: plugin_instance
96
- }
97
- # is private key needed ?
98
- if options.known_options.key?(:private_key) &&
99
- (!wiz_plugin_class.respond_to?(:private_key_required?) || wiz_plugin_class.private_key_required?(wiz_url))
100
- # lets see if path to priv key is provided
101
- private_key_path = options.get_option(:key_path)
102
- # give a chance to provide
103
- if private_key_path.nil?
104
- formatter.display_status('Please provide the path to your private RSA key, or nothing to generate one:')
105
- private_key_path = options.get_option(:key_path, mandatory: true).to_s
106
- end
107
- # else generate path
108
- private_key_path = File.join(@main_folder, DEFAULT_PRIV_KEY_FILENAME) if private_key_path.empty?
109
- if File.exist?(private_key_path)
110
- formatter.display_status('Using existing key:')
111
- else
112
- formatter.display_status("Generating #{OAuth::Jwt::DEFAULT_PRIV_KEY_LENGTH} bit RSA key...")
113
- OAuth::Jwt.generate_rsa_private_key(path: private_key_path)
114
- formatter.display_status('Created key:')
115
- end
116
- formatter.display_status(private_key_path)
117
- private_key_pem = File.read(private_key_path)
118
- options.set_option(:private_key, private_key_pem)
119
- wiz_params[:private_key_path] = private_key_path
120
- wiz_params[:pub_key_pem] = OpenSSL::PKey::RSA.new(private_key_pem).public_key.to_s
121
- end
122
- Log.dump(:wiz_params, wiz_params)
123
- # finally, call the wizard
124
- wizard_result = wiz_plugin_class.wizard(**wiz_params)
142
+ # Call the wizard
143
+ wizard_result = plugin_instance.wizard(self, wiz_url)
125
144
  Log.log.debug{"wizard result: #{wizard_result}"}
126
145
  Aspera.assert(WIZARD_RESULT_KEYS.eql?(wizard_result.keys.sort)){"missing or extra keys in wizard result: #{wizard_result.keys}"}
127
- # get preset name from user or default
146
+ # Get preset name from user or default
128
147
  if wiz_preset_name.empty?
129
148
  elements = [
130
149
  identification[:product],
@@ -133,27 +152,17 @@ module Aspera
133
152
  elements.push(options.get_option(:username, mandatory: true)) unless wizard_result[:preset_value].key?(:link) rescue nil
134
153
  wiz_preset_name = elements.join('_').strip.downcase.gsub(/[^a-z0-9]/, '_').squeeze('_')
135
154
  end
136
- # test mode does not change conf file
137
- return Main.result_single_object(wizard_result) if options.get_option(:test_mode)
138
155
  # Write configuration file
139
156
  formatter.display_status("Preparing preset: #{wiz_preset_name}")
140
- # init defaults if necessary
141
- @config_presets[CONF_PRESET_DEFAULTS] ||= {}
157
+ # Init defaults if necessary
142
158
  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
159
+ option_default = options.get_option(:default, mandatory: true)
160
+ config.defaults_set(identification[:product], wiz_preset_name, wizard_result[:preset_value].stringify_keys, option_default, option_override)
148
161
  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
162
+ test_args = "-P#{wiz_preset_name} #{test_args}" unless option_default
155
163
  # TODO: actually test the command
156
- return Main.result_status("You can test with:\n#{Info::CMD_NAME} #{identification[:product]} #{test_args}")
164
+ test_cmd = "#{Info::CMD_NAME} #{identification[:product]} #{test_args}"
165
+ return Main.result_status("You can test with:\n#{test_cmd.red}")
157
166
  end
158
167
  end
159
168
  end
data/lib/aspera/colors.rb CHANGED
@@ -57,10 +57,16 @@ class String
57
57
  define_method(name){self}
58
58
  end
59
59
  end
60
+
60
61
  # Transform capitalized to snake case
61
62
  def capital_to_snake
62
63
  return gsub(/([a-z\d])([A-Z])/, '\1_\2')
63
64
  .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
64
65
  .downcase
65
66
  end
67
+
68
+ # Transform snake case to capitalized
69
+ def snake_to_capital
70
+ split('_').map(&:capitalize).join
71
+ end
66
72
  end
@@ -4,43 +4,31 @@ require 'aspera/log'
4
4
  require 'aspera/assert'
5
5
  require 'yaml'
6
6
  module Aspera
7
- # helper class to build command line from a parameter list (key-value hash)
8
- # constructor takes hash: { 'param1':'value1', ...}
9
- # process_param is called repeatedly with all known parameters
10
- # add_env_args is called to get resulting param list and env var (also checks that all params were used)
7
+ # Helper class to build command line from a parameter list (key-value hash)
8
+ # Constructor takes hash: `{ 'param1':'value1', ...}`
9
+ # `process_param` is called repeatedly with all known parameters
10
+ # `add_env_args` is called to get resulting param list and env var (also checks that all params were used)
11
11
  class CommandLineBuilder
12
- # description [String] Description
13
- # type [String,Array] Accepted type(s) for non-enum
14
- # default [String] Default value if not specified
15
- # enum [Array] Set with list of values for enum types accepted in transfer spec
16
- # items [Array]
17
- # properties [Array]
18
- # x-cli-envvar [String] Name of env var
19
- # x-cli-option [String] Command line option (starts with "-")
20
- # x-cli-switch [Bool] true if option has no arg, else by default option has a value
21
- # x-cli-special [Bool] true if special handling (defered)
22
- # x-cli-convert [String,Hash] Method name for Convert object or Conversion for enum ts to arg
23
- # x-agents [Array] Supported agents (for doc only), if not specified: all
24
- # x-ts-name [Bool,String] (async) true if same name in transfer spec, else real name in transfer spec, else ignored
25
- # x-ts-convert [String] (async) Method name for Convert object
26
- # x-deprecation [String] Deprecation message for doc
27
- PROPERTY_KEYS = %w[
28
- description
29
- type
30
- default
31
- enum
32
- items
33
- properties
34
- required
35
- $comment
36
- x-cli-envvar
37
- x-cli-option
38
- x-cli-switch
39
- x-cli-special
40
- x-cli-convert
41
- x-agents
42
- x-ts-name
43
- x-deprecation
12
+ # Supported keys in JSON schema
13
+ PROPERTY_KEYS = [
14
+ 'description', # [String] Description
15
+ 'type', # [String,Array] Accepted type(s) for non-enum
16
+ 'default', # [String] Default value if not specified
17
+ 'enum', # [Array] Set with list of values for enum types accepted in transfer spec
18
+ 'items', # [Array]
19
+ 'properties', # [Array]
20
+ 'required', # [Array]
21
+ '$comment', # [String]
22
+ 'x-cli-envvar', # [String] Name of env var
23
+ 'x-cli-option', # [String] Command line option (starts with "-")
24
+ 'x-cli-short', # [String] Command line option (starts with "-")
25
+ 'x-cli-switch', # [Bool] `true` if option has no arg, else by default option has a value
26
+ 'x-cli-special', # [Bool] `true` if special handling (deferred)
27
+ 'x-cli-convert', # [String,Hash] Method name for Convert object or Conversion for enum ts to arg
28
+ 'x-agents', # [Array] Supported agents (for doc only), if not specified: all
29
+ 'x-ts-name', # [Bool,String] (async) true if same name in transfer spec, else real name in transfer spec, else ignored
30
+ 'x-ts-convert', # [String] (async) Name of methods to convert value from transfer spec to `conf` API.
31
+ 'x-deprecation' # [String] Deprecation message for doc
44
32
  ].freeze
45
33
 
46
34
  CLI_AGENT = 'direct'
@@ -53,17 +41,23 @@ module Aspera
53
41
  !properties.key?('x-agents') || properties['x-agents'].include?(agent)
54
42
  end
55
43
 
56
- # fill default values
57
- def adjust_properties_defaults(properties)
58
- properties.each do |name, info|
44
+ # Fill default values for some fields in the schema
45
+ # @param schema [Hash] The JSON schema
46
+ def validate_schema(schema, ascp: false)
47
+ schema['properties'].each do |name, info|
59
48
  Aspera.assert_type(info, Hash){"#{info.class} for #{name}"}
60
49
  unsupported_keys = info.keys - PROPERTY_KEYS
61
50
  Aspera.assert(unsupported_keys.empty?){"Unsupported definition keys: #{unsupported_keys}"}
62
- # by default : string, unless it's without arg
63
- info['type'] ||= info['x-cli-switch'] ? 'boolean' : 'string'
64
- # add default cli option name if not present, and if supported in "direct".
65
- info['x-cli-option'] = "--#{name.to_s.tr('_', '-')}" if !info.key?('x-cli-option') && !info['x-cli-envvar'] && (info.key?('x-cli-switch') || supported_by_agent(CLI_AGENT, info))
51
+ # By default : string, unless it's without arg (switch)
52
+ # info['type'] ||= info['x-cli-switch'] ? 'boolean' : 'string'
53
+ Aspera.assert(info.key?('type') || info.key?('enum')){"Missing type for #{name} in #{schema['description']}"}
54
+ Aspera.assert(info['type'].eql?('boolean')){"switch must be bool: #{name}"} if info['x-cli-switch']
55
+ # Add default cli option name if not present, and if supported in "direct".
56
+ info['x-cli-option'] = "--#{name.to_s.tr('_', '-')}" if info['x-cli-option'].eql?(true) || (info['x-cli-switch'].eql?(true) && !info.key?('x-cli-option'))
57
+ Aspera.assert(%w[x-cli-option x-cli-envvar x-cli-special].any?{ |i| info.key?(i)}, type: :warn){name} if ascp && supported_by_agent(CLI_AGENT, info)
58
+ # info['x-cli-option'] = "--#{name.to_s.tr('_', '-')}" if ascp && !info.key?('x-cli-option') && !info['x-cli-envvar'] && (info.key?('x-cli-switch') || supported_by_agent(CLI_AGENT, info))
66
59
  info.freeze
60
+ validate_schema(info, ascp: ascp) if info['type'].eql?('object') && info['properties']
67
61
  end
68
62
  end
69
63
 
@@ -98,10 +92,10 @@ module Aspera
98
92
  # Add processed parameters to env and args, warns about unused parameters
99
93
  # @param [Hash] env_args with :env and :args
100
94
  def add_env_args(env_args)
101
- Log.log.debug{"add_env_args: ENV=#{@result[:env]}, ARGS=#{@result[:args]}"}
95
+ Log.dump(:env_args, @result)
102
96
  # warn about non translated arguments
103
97
  @object.each_pair do |name, value|
104
- Log.log.warn{raise "Unknown transfer spec parameter: #{name} = \"#{value}\""} unless @processed_parameters.include?(name)
98
+ Log.log.warn{"Unknown transfer spec parameter: #{name} = \"#{value}\""} unless @processed_parameters.include?(name)
105
99
  end
106
100
  # set result
107
101
  env_args[:env].merge!(@result[:env])
@@ -109,9 +103,10 @@ module Aspera
109
103
  return
110
104
  end
111
105
 
112
- # add options directly to command line
113
- def add_command_line_options(options)
114
- return if options.nil?
106
+ # Add options directly to command line
107
+ def add_command_line_options(*options)
108
+ options = options.first if options.first.is_a?(Array) && options.length.eql?(1)
109
+ Aspera.assert_type(options, Array)
115
110
  options.each{ |o| @result[:args].push(o.to_s)}
116
111
  end
117
112
 
@@ -198,13 +193,13 @@ module Aspera
198
193
  else Aspera.error_unexpected_value(parameter_value){name}
199
194
  end
200
195
  # add_param = !add_param if properties[:add_on_false]
201
- add_command_line_options([properties['x-cli-option']]) if add_param
196
+ add_command_line_options(properties['x-cli-option']) if add_param
202
197
  else
203
198
  # transform into command line option with value
204
199
  # parameter_value=parameter_value.to_s if parameter_value.is_a?(Integer)
205
200
  parameter_value = [parameter_value] unless parameter_value.is_a?(Array)
206
201
  # if transfer_spec value is an array, applies option many times
207
- parameter_value.each{ |v| add_command_line_options([properties['x-cli-option'], v])}
202
+ parameter_value.each{ |v| add_command_line_options(properties['x-cli-option'], v)}
208
203
  end
209
204
  end
210
205
  end
@@ -23,8 +23,9 @@ module Aspera
23
23
  end
24
24
  end
25
25
 
26
+ # Kbps to bps
26
27
  def kbps_to_bps(value)
27
- 1000 * value
28
+ 1000 * value.to_i
28
29
  end
29
30
  end
30
31
  end
@@ -5,7 +5,7 @@ if ENV.key?('ENABLE_COVERAGE')
5
5
  require 'simplecov'
6
6
  require 'securerandom'
7
7
  # compute development top folder based on this source location
8
- development_root = 3.times.inject(File.realpath(__FILE__)){ |p, _| File.dirname(p)}
8
+ development_root = File.dirname(File.realpath(__FILE__), 3)
9
9
  SimpleCov.root(development_root)
10
10
  SimpleCov.enable_for_subprocesses if SimpleCov.respond_to?(:enable_for_subprocesses)
11
11
  # keep cache data for 1 day (must be longer that time to run the whole test suite)
@@ -19,7 +19,7 @@ module Aspera
19
19
  # @return [String] decoded data
20
20
  def item(name)
21
21
  index = ELEMENTS.index(name)
22
- raise ArgumentError, "unknown data item #{name} (#{name.class})" unless index
22
+ raise ParameterError, "Unknown data item #{name} (#{name.class})" unless index
23
23
  raw_data = data(START_INDEX + index)
24
24
  case name
25
25
  when :dsa, :rsa
@@ -38,6 +38,8 @@ module Aspera
38
38
  WINDOWS_FILENAME_INVALID_CHARACTERS = '<>:"/\\|?*'
39
39
  REPLACE_CHARACTER = '_'
40
40
 
41
+ RB_EXT = '.rb'
42
+
41
43
  class << self
42
44
  def ruby_version
43
45
  return RbConfig::CONFIG['RUBY_PROGRAM_VERSION']
@@ -186,7 +188,7 @@ module Aspera
186
188
  end
187
189
  end
188
190
  attr_accessor :url_method, :file_illegal_characters
189
- attr_reader :os, :cpu, :executable_extension, :default_gui_mode
191
+ attr_reader :os, :cpu, :default_gui_mode
190
192
 
191
193
  def initialize
192
194
  initialize_fields
@@ -218,7 +220,7 @@ module Aspera
218
220
  CPU_ARM64
219
221
  else Aspera.error_unexpected_value(RbConfig::CONFIG['host_cpu']){'host_cpu'}
220
222
  end
221
- @executable_extension = @os.eql?(OS_WINDOWS) ? 'exe' : nil
223
+ @executable_extension = @os.eql?(OS_WINDOWS) ? '.exe' : nil
222
224
  # :text or :graphical depending on the environment
223
225
  @default_gui_mode =
224
226
  if [Environment::OS_WINDOWS, Environment::OS_MACOS].include?(os) ||
@@ -239,8 +241,10 @@ module Aspera
239
241
  "#{@os}-#{@cpu}"
240
242
  end
241
243
 
242
- # executable file extension for current OS
243
- def exe_file(name)
244
+ # Add executable file extension (e.g. ".exe") for current OS
245
+ # @param name [String,nil] Path or file name
246
+ # @return [String] Executable name with extension
247
+ def exe_file(name = nil)
244
248
  return name unless @executable_extension
245
249
  return "#{name}#{@executable_extension}"
246
250
  end
@@ -311,11 +315,11 @@ module Aspera
311
315
  # @return [String] A file name safe to use on file system
312
316
  def sanitized_filename(filename)
313
317
  safe_char = safe_filename_character
314
- # Windows does not allow file name ending with space or dot
315
- # nor control characters anywhere.
316
- filename = filename
317
- .gsub(/[\. ]+$/, safe_char)
318
- .gsub(/[\x00-\x1F\x7F]/, safe_char)
318
+ # Windows does not allow file name:
319
+ # - with control characters anywhere
320
+ # - ending with space or dot
321
+ filename = filename.gsub(/[\x00-\x1F\x7F]/, safe_char)
322
+ filename = filename.chop while filename.end_with?(' ', '.')
319
323
  if @file_illegal_characters&.size.to_i >= 2
320
324
  # replace all illegal characters with safe_char
321
325
  filename = filename.tr(@file_illegal_characters[1..-1], safe_char)
@@ -8,10 +8,12 @@ require 'json'
8
8
  module Aspera
9
9
  # Simulate the Faspex 4 /send API and creates a package on Aspera on Cloud or Faspex 5
10
10
  class Faspex4GWServlet < WEBrick::HTTPServlet::AbstractServlet
11
+ AOC_API = 'Aspera::Api::AoC'
12
+ FX_API = 'Aspera::Api::Faspex'
11
13
  # @param app_api [Rest] API object
12
14
  # @param app_context [String] workspace id (aoc only)
13
15
  def initialize(server, app_api, app_context)
14
- Aspera.assert_values(app_api.class.name, ['Aspera::Api::AoC', 'Aspera::Rest'])
16
+ Aspera.assert_values(app_api.class.name, [AOC_API, FX_API])
15
17
  super(server)
16
18
  @app_api = app_api
17
19
  @app_context = app_context
@@ -49,7 +51,7 @@ module Aspera
49
51
  transfer_spec = @app_api.call(
50
52
  operation: 'POST',
51
53
  subpath: "packages/#{package['id']}/transfer_spec/upload",
52
- query: {transfer_type: Cli::Plugins::Faspex5::TRANSFER_CONNECT},
54
+ query: {transfer_type: Api::Faspex::TRANSFER_CONNECT},
53
55
  content_type: Rest::MIME_JSON,
54
56
  body: {paths: [{'destination'=>'/'}]},
55
57
  headers: {'Accept' => Rest::MIME_JSON}
@@ -72,9 +74,9 @@ module Aspera
72
74
  # compare string, as class is not yet known here
73
75
  faspex_package_create_result =
74
76
  case @app_api.class.name
75
- when 'Aspera::Api::AoC'
77
+ when AOC_API
76
78
  faspex4_send_to_aoc(faspex_pkg_parameters)
77
- when 'Aspera::Rest'
79
+ when FX_API
78
80
  faspex4_send_to_faspex5(faspex_pkg_parameters)
79
81
  else Aspera.error_unexpected_value(@app_api.class.name)
80
82
  end
@@ -8,7 +8,7 @@ require 'aspera/log'
8
8
  require 'aspera/assert'
9
9
 
10
10
  module Aspera
11
- # this class answers the Faspex /send API and creates a package on Aspera on Cloud
11
+ # Start a Faspex-4 style post-processing script using Faspex-5 webhook
12
12
  class Faspex4PostProcServlet < WEBrick::HTTPServlet::AbstractServlet
13
13
  ALLOWED_PARAMETERS = %i[root script_folder fail_on_error timeout_seconds].freeze
14
14
  def initialize(server, parameters)
@@ -73,7 +73,7 @@ module Aspera
73
73
  end
74
74
 
75
75
  def list(options = {})
76
- Aspera.assert_values(options[:domain], DOMAINS, type: ArgumentError){'domain'} unless options[:domain].nil?
76
+ Aspera.assert_values(options[:domain], DOMAINS, type: ParameterError){'domain'} unless options[:domain].nil?
77
77
  key_chains(execute('list-keychains', options, LIST_OPTIONS))
78
78
  end
79
79