aspera-cli 4.23.0 → 4.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +37 -1
  4. data/CONTRIBUTING.md +86 -29
  5. data/README.md +2109 -1300
  6. data/bin/ascli +2 -1
  7. data/bin/asession +4 -4
  8. data/lib/aspera/agent/base.rb +4 -0
  9. data/lib/aspera/agent/connect.rb +20 -18
  10. data/lib/aspera/agent/desktop.rb +14 -11
  11. data/lib/aspera/agent/direct.rb +39 -31
  12. data/lib/aspera/agent/httpgw.rb +2 -2
  13. data/lib/aspera/agent/node.rb +9 -11
  14. data/lib/aspera/agent/transferd.rb +18 -11
  15. data/lib/aspera/api/aoc.rb +44 -31
  16. data/lib/aspera/api/cos_node.rb +7 -5
  17. data/lib/aspera/api/httpgw.rb +15 -18
  18. data/lib/aspera/api/node.rb +104 -22
  19. data/lib/aspera/ascmd.rb +22 -16
  20. data/lib/aspera/ascp/installation.rb +37 -40
  21. data/lib/aspera/ascp/management.rb +5 -4
  22. data/lib/aspera/assert.rb +54 -23
  23. data/lib/aspera/cli/basic_auth_plugin.rb +8 -7
  24. data/lib/aspera/cli/error.rb +1 -1
  25. data/lib/aspera/cli/extended_value.rb +28 -29
  26. data/lib/aspera/cli/formatter.rb +191 -168
  27. data/lib/aspera/cli/hints.rb +29 -3
  28. data/lib/aspera/cli/main.rb +138 -107
  29. data/lib/aspera/cli/manager.rb +50 -30
  30. data/lib/aspera/cli/plugin.rb +148 -77
  31. data/lib/aspera/cli/plugin_factory.rb +2 -2
  32. data/lib/aspera/cli/plugins/aoc.rb +189 -70
  33. data/lib/aspera/cli/plugins/ats.rb +15 -13
  34. data/lib/aspera/cli/plugins/config.rb +100 -214
  35. data/lib/aspera/cli/plugins/console.rb +49 -18
  36. data/lib/aspera/cli/plugins/cos.rb +4 -4
  37. data/lib/aspera/cli/plugins/faspex.rb +45 -51
  38. data/lib/aspera/cli/plugins/faspex5.rb +164 -165
  39. data/lib/aspera/cli/plugins/faspio.rb +6 -5
  40. data/lib/aspera/cli/plugins/httpgw.rb +2 -2
  41. data/lib/aspera/cli/plugins/node.rb +144 -162
  42. data/lib/aspera/cli/plugins/orchestrator.rb +10 -14
  43. data/lib/aspera/cli/plugins/preview.rb +26 -29
  44. data/lib/aspera/cli/plugins/server.rb +28 -28
  45. data/lib/aspera/cli/plugins/shares.rb +40 -28
  46. data/lib/aspera/cli/sync_actions.rb +101 -80
  47. data/lib/aspera/cli/transfer_agent.rb +51 -50
  48. data/lib/aspera/cli/transfer_progress.rb +29 -20
  49. data/lib/aspera/cli/version.rb +1 -1
  50. data/lib/aspera/cli/wizard.rb +157 -0
  51. data/lib/aspera/colors.rb +13 -8
  52. data/lib/aspera/command_line_builder.rb +28 -22
  53. data/lib/aspera/command_line_converter.rb +31 -0
  54. data/lib/aspera/environment.rb +145 -101
  55. data/lib/aspera/faspex_gw.rb +1 -1
  56. data/lib/aspera/faspex_postproc.rb +3 -2
  57. data/lib/aspera/hash_ext.rb +1 -1
  58. data/lib/aspera/id_generator.rb +10 -10
  59. data/lib/aspera/keychain/base.rb +18 -0
  60. data/lib/aspera/keychain/encrypted_hash.rb +6 -12
  61. data/lib/aspera/keychain/factory.rb +9 -3
  62. data/lib/aspera/keychain/hashicorp_vault.rb +9 -6
  63. data/lib/aspera/keychain/macos_security.rb +13 -13
  64. data/lib/aspera/log.rb +91 -19
  65. data/lib/aspera/nagios.rb +5 -6
  66. data/lib/aspera/node_simulator.rb +12 -7
  67. data/lib/aspera/oauth/base.rb +5 -3
  68. data/lib/aspera/oauth/factory.rb +24 -18
  69. data/lib/aspera/oauth/jwt.rb +13 -1
  70. data/lib/aspera/oauth/url_json.rb +3 -3
  71. data/lib/aspera/oauth/web.rb +5 -3
  72. data/lib/aspera/persistency_folder.rb +2 -2
  73. data/lib/aspera/preview/file_types.rb +4 -3
  74. data/lib/aspera/preview/generator.rb +25 -12
  75. data/lib/aspera/preview/terminal.rb +10 -7
  76. data/lib/aspera/preview/utils.rb +11 -9
  77. data/lib/aspera/products/connect.rb +1 -1
  78. data/lib/aspera/products/desktop.rb +1 -1
  79. data/lib/aspera/products/other.rb +2 -2
  80. data/lib/aspera/products/transferd.rb +8 -6
  81. data/lib/aspera/proxy_auto_config.rb +1 -1
  82. data/lib/aspera/rest.rb +29 -22
  83. data/lib/aspera/rest_call_error.rb +1 -1
  84. data/lib/aspera/resumer.rb +1 -1
  85. data/lib/aspera/secret_hider.rb +46 -40
  86. data/lib/aspera/ssh.rb +13 -3
  87. data/lib/aspera/sync/args.schema.yaml +102 -0
  88. data/lib/aspera/sync/conf.schema.yaml +701 -0
  89. data/lib/aspera/sync/database.rb +83 -0
  90. data/lib/aspera/sync/operations.rb +296 -0
  91. data/lib/aspera/temp_file_manager.rb +3 -2
  92. data/lib/aspera/transfer/error.rb +1 -1
  93. data/lib/aspera/transfer/error_info.rb +1 -2
  94. data/lib/aspera/transfer/faux_file.rb +11 -10
  95. data/lib/aspera/transfer/parameters.rb +6 -5
  96. data/lib/aspera/transfer/spec.rb +15 -1
  97. data/lib/aspera/transfer/spec.schema.yaml +316 -293
  98. data/lib/aspera/transfer/spec_doc.rb +34 -16
  99. data/lib/aspera/transfer/uri.rb +5 -5
  100. data/lib/aspera/uri_reader.rb +14 -10
  101. data/lib/aspera/web_auth.rb +2 -2
  102. data/lib/aspera/web_server_simple.rb +2 -2
  103. data.tar.gz.sig +0 -0
  104. metadata +15 -13
  105. metadata.gz.sig +0 -0
  106. data/lib/aspera/transfer/async_conf.schema.yaml +0 -716
  107. data/lib/aspera/transfer/convert.rb +0 -29
  108. data/lib/aspera/transfer/sync.rb +0 -232
  109. data/lib/aspera/transfer/sync_instance.schema.yaml +0 -20
  110. data/lib/aspera/transfer/sync_session.schema.yaml +0 -86
data/lib/aspera/ascmd.rb CHANGED
@@ -30,10 +30,16 @@ module Aspera
30
30
  # Description of result structures (see ascmdtypes.h).
31
31
  # Base types are big endian
32
32
  # key = name of type
33
- # index in array `fields` is the type (minus ENUM_START)
33
+ # index in array `fields` is: the numerical type of TLV - `ENUM_START`. i.e. add `ENUM_START` to index, to get `T`
34
34
  # decoding always start at `result`
35
35
  # some fields have special handling indicated by `special`
36
36
  # field_list, list_tlv_list, list_tlv_restart are composed with a list of TLV
37
+ # decode:
38
+ # - :base : value
39
+ # - :buffer_list : an array of {btype,buffer}
40
+ # - :field_list : a hash, or array
41
+ # :check: ???
42
+ # rubocop:disable Layout/FirstHashElementLineBreak
37
43
  TYPES_DESCR = {
38
44
  result: {decode: :field_list,
39
45
  fields: [{name: :file, is_a: :stat}, {name: :dir, is_a: :stat, special: :list_tlv_list}, {name: :size, is_a: :size}, {name: :error, is_a: :error},
@@ -67,6 +73,7 @@ module Aspera
67
73
  zstr: {decode: :base, unpack: 'Z*'},
68
74
  blist: {decode: :buffer_list}
69
75
  }.freeze
76
+ # rubocop:enable Layout/FirstHashElementLineBreak
70
77
 
71
78
  private_constant :TYPES_DESCR, :ENUM_START, :OPS_ARGS
72
79
 
@@ -122,11 +129,11 @@ module Aspera
122
129
  stdin_input = command_lines.join("\n")
123
130
  Log.log.trace1{"execute_single:#{stdin_input}"}
124
131
  # execute, get binary output
125
- byte_buffer = @command_executor.execute(remote_cmd, stdin_input).unpack('C*')
126
- raise 'ERROR: empty answer from server' if byte_buffer.empty?
132
+ byte_buffer = @command_executor.execute(remote_cmd, input: stdin_input).unpack('C*')
133
+ Aspera.assert(!byte_buffer.empty?){'empty answer from server'}
127
134
  # get hash or table result
128
135
  result = self.class.parse(byte_buffer, :result)
129
- raise 'ERROR: unparsed bytes remaining' unless byte_buffer.empty?
136
+ Aspera.assert(byte_buffer.empty?){'unparsed bytes remaining'}
130
137
  # get and delete info,always present in results
131
138
  system_info = result[:info]
132
139
  result.delete(:info)
@@ -137,7 +144,7 @@ module Aspera
137
144
  result[:dir].each do |file|
138
145
  if file.key?(:smode)
139
146
  # Converts the first character of the file mode (see 'man ls') into a type.
140
- file[:type] = case file[:smode][0, 1]; when 'd' then:directory; when '-' then:file; when 'l' then:link; else; :other; end # rubocop:disable Style/Semicolon
147
+ file[:type] = case file[:smode][0, 1]; when 'd' then:directory; when '-' then:file; when 'l' then:link; else; :other; end
141
148
  end
142
149
  end
143
150
  end
@@ -153,27 +160,26 @@ module Aspera
153
160
  return result
154
161
  end
155
162
 
156
- # This exception is raised when +ascmd+ returns an error.
163
+ # This exception is raised when `ascmd` returns an error.
157
164
  class Error < StandardError
158
- def initialize(errno, errstr, cmd, arguments)
159
- super(); @errno = errno; @errstr = errstr; @command = cmd; @arguments = arguments; end # rubocop:disable Style/Semicolon
160
-
165
+ # rubocop:disable Style/Semicolon
166
+ def initialize(errno, errstr, cmd, arguments); super(); @errno = errno; @errstr = errstr; @command = cmd; @arguments = arguments; end
161
167
  def message; "ascmd: #{@errstr} (#{@errno})"; end
162
168
  def extended_message; "ascmd: errno=#{@errno} errstr=\"#{@errstr}\" command=#{@command} arguments=#{@arguments&.join(',')}"; end
169
+ # rubocop:enable Style/Semicolon
163
170
  end
164
171
 
165
172
  class << self
166
- # get description of structure's field, @param struct_name, @param typed_buffer provides field name
173
+ # Get description of structure's field, @param struct_name, @param typed_buffer provides field name
167
174
  def field_description(struct_name, typed_buffer)
168
175
  result = TYPES_DESCR[struct_name][:fields][typed_buffer[:btype] - ENUM_START]
169
176
  raise "Unrecognized field for #{struct_name}: #{typed_buffer[:btype]}\n#{typed_buffer[:buffer]}" if result.nil?
170
177
  return result
171
178
  end
172
179
 
173
- # decodes the provided buffer as provided type name
174
- # @return a decoded type.
175
- # :base : value, :buffer_list : an array of {btype,buffer}, :field_list : a hash, or array
176
- def parse(buffer, type_name, indent_level=nil)
180
+ # Decodes the provided buffer as provided type name
181
+ # @return a decoded type: single, Array, or Hash
182
+ def parse(buffer, type_name, indent_level = nil)
177
183
  indent_level = (indent_level || -1) + 1
178
184
  type_descr = TYPES_DESCR[type_name]
179
185
  raise "Unexpected type #{type_name}" if type_descr.nil?
@@ -182,7 +188,7 @@ module Aspera
182
188
  case type_descr[:decode]
183
189
  when :base
184
190
  num_bytes = type_name.eql?(:zstr) ? buffer.length : type_descr[:size]
185
- raise 'ERROR:not enough bytes' if buffer.length < num_bytes
191
+ Aspera.assert(buffer.length >= num_bytes){'not enough bytes'}
186
192
  byte_array = buffer.shift(num_bytes)
187
193
  byte_array = [byte_array] unless byte_array.is_a?(Array)
188
194
  result = byte_array.pack('C*').unpack1(type_descr[:unpack])
@@ -195,7 +201,7 @@ module Aspera
195
201
  until buffer.empty?
196
202
  btype = parse(buffer, :int8, indent_level)
197
203
  length = parse(buffer, :int32, indent_level)
198
- raise 'ERROR:not enough bytes' if buffer.length < length
204
+ Aspera.assert(buffer.length >= length){'not enough bytes'}
199
205
  value = buffer.shift(length)
200
206
  result.push({btype: btype, buffer: value})
201
207
  Log.log.trace1{"#{' .' * indent_level}:buffer_list[#{result.length - 1}] #{result.last}"}
@@ -44,12 +44,12 @@ module Aspera
44
44
  </default>
45
45
  </CONF>
46
46
  END_OF_CONFIG_FILE
47
- # all ascp files (in SDK)
48
- EXE_FILES = %i[ascp ascp4 async].freeze
49
- FILES = %i[transferd ssh_private_dsa ssh_private_rsa aspera_license aspera_conf fallback_certificate fallback_private_key].unshift(*EXE_FILES).freeze
50
- TRANSFER_SDK_LOCATION_URL = 'https://ibm.biz/sdk_location'
47
+ # all executable files from SDK
48
+ EXE_FILES = %i[ascp ascp4 async transferd].freeze
49
+ SDK_FILES = %i[ssh_private_dsa ssh_private_rsa aspera_license aspera_conf fallback_certificate fallback_private_key].unshift(*EXE_FILES).freeze
50
+ TRANSFERD_ARCHIVE_LOCATION_URL = 'https://ibm.biz/sdk_location'
51
51
  # filename for ascp with optional extension (Windows)
52
- private_constant :DEFAULT_ASPERA_CONF, :FILES, :TRANSFER_SDK_LOCATION_URL
52
+ private_constant :DEFAULT_ASPERA_CONF, :SDK_FILES, :TRANSFERD_ARCHIVE_LOCATION_URL
53
53
  # options for SSH client private key
54
54
  CLIENT_SSH_KEY_OPTIONS = %i{dsa_rsa rsa per_client}.freeze
55
55
 
@@ -99,7 +99,7 @@ module Aspera
99
99
 
100
100
  # @return [Hash] with key = file name (String), and value = path to file
101
101
  def file_paths
102
- return FILES.each_with_object({}) do |v, m|
102
+ return SDK_FILES.each_with_object({}) do |v, m|
103
103
  m[v.to_s] =
104
104
  begin
105
105
  path(v)
@@ -115,19 +115,23 @@ module Aspera
115
115
  return Environment.write_file_restricted(File.join(Products::Transferd.sdk_directory, filename), force: force, mode: 0o644, &block)
116
116
  end
117
117
 
118
- # get path of one resource file of currently activated product
118
+ # Get path of one resource file of currently activated product
119
119
  # keys and certs are generated locally... (they are well known values, arch. independent)
120
+ # @param k [Symbol] key of the resource file
121
+ # @return [String, nil] Full path to the resource file or nil if not found
120
122
  def path(k)
121
- file_is_optional = false
123
+ file_is_required = true
122
124
  case k
123
125
  when *EXE_FILES
124
- file_is_optional = k.eql?(:async)
125
- use_ascp_from_product(FIRST_FOUND) if @path_to_ascp.nil?
126
- # NOTE: that there might be a .exe at the end
127
- file = @path_to_ascp.gsub('ascp', k.to_s)
128
- when :transferd
129
- file_is_optional = true
130
- file = Products::Transferd.transferd_path
126
+ file_is_required = k.eql?(:ascp)
127
+ file = if k.eql?(:transferd)
128
+ Products::Transferd.transferd_path
129
+ else
130
+ # ensure at least ascp is found
131
+ use_ascp_from_product(FIRST_FOUND) if @path_to_ascp.nil?
132
+ # NOTE: that there might be a .exe at the end
133
+ @path_to_ascp.gsub('ascp', k.to_s)
134
+ end
131
135
  when :ssh_private_dsa, :ssh_private_rsa
132
136
  # assume last 3 letters are type
133
137
  type = k.to_s[-3..-1].to_sym
@@ -150,8 +154,8 @@ module Aspera
150
154
  file = k.eql?(:fallback_certificate) ? file_cert : file_key
151
155
  else Aspera.error_unexpected_value(k)
152
156
  end
153
- return nil if file_is_optional && !File.exist?(file)
154
- Aspera.assert(File.exist?(file), exception_class: Errno::ENOENT){"#{k} not found (#{file})"}
157
+ return unless file_is_required || File.exist?(file)
158
+ Aspera.assert(File.exist?(file), type: Errno::ENOENT){"#{k} not found (#{file})"}
155
159
  return file
156
160
  end
157
161
 
@@ -168,7 +172,7 @@ module Aspera
168
172
  when :dsa_rsa, :rsa
169
173
  types.to_s.split('_').map{ |i| Installation.instance.path("ssh_private_#{i}".to_sym)}
170
174
  when :per_client
171
- raise 'Not yet implemented'
175
+ Aspera.error_not_implemented
172
176
  end
173
177
  end
174
178
 
@@ -179,8 +183,9 @@ module Aspera
179
183
 
180
184
  # Check that specified path is ascp and get version
181
185
  def get_exe_version(exe_path, vers_arg)
182
- raise 'ERROR: nil arg' if exe_path.nil?
183
- return nil unless File.exist?(exe_path)
186
+ Aspera.assert_type(exe_path, String)
187
+ Aspera.assert_type(vers_arg, String)
188
+ return unless File.exist?(exe_path)
184
189
  exe_version = nil
185
190
  cmd_out = %x("#{exe_path}" #{vers_arg})
186
191
  raise "An error occurred when testing #{exe_path}: #{cmd_out}" unless $CHILD_STATUS == 0
@@ -217,9 +222,7 @@ module Aspera
217
222
  data['ascp_version'] = Regexp.last_match(1)
218
223
  end
219
224
  end
220
- if !thread.value.exitstatus.eql?(1) && !data.key?('root')
221
- raise last_line
222
- end
225
+ raise last_line if !thread.value.exitstatus.eql?(1) && !data.key?('root')
223
226
  end
224
227
  return data
225
228
  end
@@ -248,7 +251,7 @@ module Aspera
248
251
  # @return the url for download of SDK archive for the given platform and version
249
252
  def sdk_url_for_platform(platform: nil, version: nil)
250
253
  locations = sdk_locations
251
- platform = Environment.architecture if platform.nil?
254
+ platform = Environment.instance.architecture if platform.nil?
252
255
  locations = locations.select{ |l| l['platform'].eql?(platform)}
253
256
  raise "No SDK for platform: #{platform}" if locations.empty?
254
257
  version = locations.max_by{ |entry| Gem::Version.new(entry['version'])}['version'] if version.nil?
@@ -259,7 +262,7 @@ module Aspera
259
262
 
260
263
  # @param &block called with entry information
261
264
  def extract_archive_files(sdk_archive_path)
262
- raise 'missing block' unless block_given?
265
+ Aspera.assert(block_given?){'missing block'}
263
266
  case sdk_archive_path
264
267
  # Windows and Mac use zip
265
268
  when /\.zip$/
@@ -330,21 +333,15 @@ module Aspera
330
333
  end
331
334
  end
332
335
  return unless with_exe
333
- # ensure license file are generated so that ascp invocation for version works
334
- path(:aspera_license)
335
- path(:aspera_conf)
336
- sdk_ascp_file = Environment.exe_file('ascp')
337
- sdk_ascp_path = File.join(folder, sdk_ascp_file)
338
- raise "No #{sdk_ascp_file} found in SDK archive" unless File.exist?(sdk_ascp_path)
339
- EXE_FILES.each do |exe_sym|
340
- exe_path = sdk_ascp_path.gsub('ascp', exe_sym.to_s)
341
- Environment.restrict_file_access(exe_path, mode: 0o755) if File.exist?(exe_path)
336
+ # ensure necessary files are there, or generate them
337
+ SDK_FILES.each do |file_id_sym|
338
+ file_path = path(file_id_sym)
339
+ if file_path && EXE_FILES.include?(file_id_sym)
340
+ Environment.restrict_file_access(file_path, mode: 0o755) if File.exist?(file_path)
341
+ end
342
342
  end
343
- sdk_ascp_version = get_ascp_version(sdk_ascp_path)
344
- transferd_exe_path = Products::Transferd.transferd_path
345
- Log.log.warn{"No #{transferd_exe_path} in SDK archive"} unless File.exist?(transferd_exe_path)
346
- Environment.restrict_file_access(transferd_exe_path, mode: 0o755) if File.exist?(transferd_exe_path)
347
- transferd_version = get_exe_version(transferd_exe_path, 'version')
343
+ sdk_ascp_version = get_ascp_version(path(:ascp))
344
+ transferd_version = get_exe_version(path(:transferd), 'version')
348
345
  sdk_name = 'IBM Aspera Transfer SDK'
349
346
  sdk_version = transferd_version || sdk_ascp_version
350
347
  File.write(File.join(folder, Products::Other::INFO_META_FILE), "<product><name>#{sdk_name}</name><version>#{sdk_version}</version></product>")
@@ -362,7 +359,7 @@ module Aspera
362
359
  @path_to_ascp = nil
363
360
  @sdk_dir = nil
364
361
  @found_products = nil
365
- @transferd_urls = TRANSFER_SDK_LOCATION_URL
362
+ @transferd_urls = TRANSFERD_ARCHIVE_LOCATION_URL
366
363
  end
367
364
 
368
365
  public
@@ -184,7 +184,8 @@ module Aspera
184
184
  ChunkSize
185
185
  PostTransferValidation
186
186
  OverwritePolicyCap
187
- ExtraCreatePolicy]
187
+ ExtraCreatePolicy
188
+ ]
188
189
  # Management port start message
189
190
  MGT_HEADER = 'FASPMGR 2'
190
191
  # empty line is separator to end event information
@@ -252,17 +253,17 @@ module Aspera
252
253
  # begin event
253
254
  @event_build = {}
254
255
  when /^([^:]+): (.*)$/
255
- raise 'mgt port: unexpected line: data without header' if @event_build.nil?
256
+ Aspera.assert_type(@event_build, Hash){'mgt port: unexpected line: data without header'}
256
257
  # event field
257
258
  @event_build[Regexp.last_match(1)] = Regexp.last_match(2)
258
259
  when MGT_FRAME_SEPARATOR
259
- raise 'mgt port: unexpected line: end frame without header' if @event_build.nil?
260
+ Aspera.assert_type(@event_build, Hash){'mgt port: unexpected line: end frame without header'}
260
261
  @last_event = @event_build
261
262
  @event_build = nil
262
263
  return @last_event
263
264
  else Aspera.error_unexpected_value(line){'mgt port'}
264
265
  end
265
- return nil
266
+ return
266
267
  end
267
268
  end
268
269
  end
data/lib/aspera/assert.rb CHANGED
@@ -1,55 +1,86 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Aspera
4
- class InternalError < StandardError
4
+ # Generic error in gem
5
+ class Error < StandardError
5
6
  end
6
7
 
7
- class AssertError < StandardError
8
+ # Error that shall not happen, else it's a bug
9
+ class InternalError < Error
10
+ end
11
+
12
+ # An expected condition was not met
13
+ class AssertError < Error
8
14
  end
9
15
  class << self
10
- # the block is executed in the context of the Aspera module
11
- def assert(assertion, info = nil, exception_class: AssertError)
16
+ # Replace `raise`, allows sending exception, or just error log, when type is `:error`
17
+ # @param type [Exception,Symbol] Send to log if symbol, else raise exception
18
+ # @param message [String] Message for error.
19
+ def report_error(type, message)
20
+ if type.is_a?(Symbol)
21
+ Log.log.send(type, message)
22
+ else
23
+ raise type, message
24
+ end
25
+ end
26
+
27
+ # Assert that a condition is true, else raise exception
28
+ # @param assertion [Bool] Must be true
29
+ # @param info [String,nil] Fixed message in case assert fails, else use `block`
30
+ # @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
31
+ # @param block [Proc] Produces a string that desribes the problem for complex messages
32
+ # The block is executed in the context of the Aspera module
33
+ def assert(assertion, info = nil, type: AssertError)
12
34
  raise InternalError, 'bad assert: both info and block given' unless info.nil? || !block_given?
13
35
  return if assertion
14
36
  message = 'assertion failed'
15
37
  info = yield if block_given?
16
38
  message = "#{message}: #{info}" if info
17
39
  message = "#{message}: #{caller.find{ |call| !call.start_with?(__FILE__)}}"
18
- raise exception_class, message
40
+ report_error(type, message)
19
41
  end
20
42
 
21
- # assert that value has the given type
22
- # @param value [Object] the value to check
23
- # @param type [Class] the expected type
24
- def assert_type(value, type, exception_class: AssertError)
25
- assert(value.is_a?(type), exception_class: exception_class){"#{"#{yield}: " if block_given?}expecting #{type}, but have #{value.inspect}"}
43
+ # Assert that value has the given type
44
+ # @param value [Object] The value to check
45
+ # @param classes [Class, Array] The expected type(s)
46
+ # @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
47
+ # @param block [Proc] Additional description in front of message
48
+ def assert_type(value, *classes, type: AssertError)
49
+ assert(classes.any?{ |k| value.is_a?(k)}, type: type){"#{"#{yield}: " if block_given?}expecting #{classes.join(', ')}, but have #{value.inspect}"}
26
50
  end
27
51
 
28
- # assert that value is one of the given values
29
- # @param value value to check
30
- # @param values accepted values
31
- # @param exception_class exception in case of no match
32
- def assert_values(value, values, exception_class: AssertError)
33
- assert(values.include?(value), exception_class: exception_class) do
52
+ # Assert that value is one of the given values
53
+ # @param value [any] value to check
54
+ # @param values [Array] accepted values
55
+ # @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
56
+ # @param block [Proc] Additional description in front of message
57
+ def assert_values(value, values, type: AssertError)
58
+ assert(values.include?(value), type: type) do
34
59
  val_list = values.inspect
35
60
  val_list = "one of #{val_list}" if values.is_a?(Array)
36
61
  "#{"#{yield}: " if block_given?}expecting #{val_list}, but have #{value.inspect}"
37
62
  end
38
63
  end
39
64
 
40
- # the line with this shall never be reached
65
+ # The value is not one of the expected values
66
+ # @param value [any] The wrong value
67
+ # @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
68
+ # @param block [Proc] Additional description in front of message
69
+ def error_unexpected_value(value, type: InternalError)
70
+ report_error(type, "#{"#{yield}: " if block_given?}unexpected value: #{value.inspect}")
71
+ end
72
+
73
+ # The line with this shall never be reached
41
74
  def error_unreachable_line
42
75
  raise InternalError, "unreachable line reached: #{caller(2..2).first}"
43
76
  end
44
77
 
45
- # The value is not one of the expected values
46
- # @param value the wrong value
47
- # @param exception_class exception to raise
48
- # @param block additional description in front
49
- def error_unexpected_value(value, exception_class: InternalError)
50
- raise exception_class, "#{"#{yield}: " if block_given?}unexpected value: #{value.inspect}"
78
+ # Not implemented error
79
+ def error_not_implemented
80
+ raise Error, 'Feature not yet implemented'
51
81
  end
52
82
 
83
+ # Use in superclass to require the given method in subclass.
53
84
  def require_method!(name)
54
85
  define_method(name) do |*_args|
55
86
  raise NotImplementedError, "#{self.class} must implement the #{name} method"
@@ -9,20 +9,20 @@ module Aspera
9
9
  class BasicAuthPlugin < Cli::Plugin
10
10
  class << self
11
11
  def declare_options(options)
12
- options.declare(:url, 'URL of application, e.g. https://faspex.example.com/aspera/faspex')
13
- options.declare(:username, "User's name to log in")
12
+ options.declare(:url, 'URL of application, e.g. https://app.example.com/aspera/app')
13
+ options.declare(:username, "User's identifier")
14
14
  options.declare(:password, "User's password")
15
15
  options.parse_options!
16
16
  end
17
17
  end
18
18
 
19
- def initialize(basic_options: true, **env)
20
- super(**env)
19
+ def initialize(context:, basic_options: true)
20
+ super(context: context)
21
21
  BasicAuthPlugin.declare_options(options) if basic_options
22
22
  end
23
23
 
24
24
  # returns a Rest object with basic auth
25
- def basic_auth_params(subpath=nil)
25
+ def basic_auth_params(subpath = nil)
26
26
  api_url = options.get_option(:url, mandatory: true)
27
27
  api_url = "#{api_url}/#{subpath}" unless subpath.nil?
28
28
  return {
@@ -31,10 +31,11 @@ module Aspera
31
31
  type: :basic,
32
32
  username: options.get_option(:username, mandatory: true),
33
33
  password: options.get_option(:password, mandatory: true)
34
- }}
34
+ }
35
+ }
35
36
  end
36
37
 
37
- def basic_auth_api(subpath=nil)
38
+ def basic_auth_api(subpath = nil)
38
39
  return Rest.new(**basic_auth_params(subpath))
39
40
  end
40
41
  end
@@ -4,7 +4,7 @@ module Aspera
4
4
  module Cli
5
5
  # CLI base exception
6
6
  class Error < StandardError; end
7
- # raised when an unexpected argument is provided
7
+ # Raised when an unexpected argument is provided.
8
8
  class BadArgument < Error; end
9
9
  class NoSuchElement < Error; end
10
10
 
@@ -41,12 +41,12 @@ module Aspera
41
41
  hash_array.push(col_titles.zip(values).to_h)
42
42
  end
43
43
  end
44
- Log.log.warn('Titled CSV file without any line') if hash_array.empty?
44
+ Log.log.warn('Titled CSV file without any row') if hash_array.empty?
45
45
  return hash_array
46
46
  end
47
47
 
48
- def assert_no_value(v, what)
49
- raise "no value allowed for extended value type: #{what}" unless v.empty?
48
+ def assert_no_value(value, what)
49
+ raise "no value allowed for extended value type: #{what}" unless value.empty?
50
50
  end
51
51
  end
52
52
 
@@ -56,25 +56,25 @@ module Aspera
56
56
  # base handlers
57
57
  # other handlers can be set using set_handler, e.g. `preset` is reader in config plugin
58
58
  @handlers = {
59
- val: lambda{ |v| v},
60
- base64: lambda{ |v| Base64.decode64(v)},
61
- csvt: lambda{ |v| ExtendedValue.decode_csvt(v)},
62
- env: lambda{ |v| ENV.fetch(v, nil)},
63
- file: lambda{ |v| File.read(File.expand_path(v))},
64
- uri: lambda{ |v| UriReader.read(v)},
65
- json: lambda{ |v| JSON_parse(v)},
66
- lines: lambda{ |v| v.split("\n")},
67
- list: lambda{ |v| v[1..-1].split(v[0])},
68
- none: lambda{ |v| ExtendedValue.assert_no_value(v, :none); nil}, # rubocop:disable Style/Semicolon
69
- path: lambda{ |v| File.expand_path(v)},
70
- re: lambda{ |v| Regexp.new(v, Regexp::MULTILINE)},
71
- ruby: lambda{ |v| Environment.secure_eval(v, __FILE__, __LINE__)},
72
- secret: lambda{ |v| prompt = v.empty? ? 'secret' : v; $stdin.getpass("#{prompt}> ")}, # rubocop:disable Style/Semicolon
73
- stdin: lambda{ |v| ExtendedValue.assert_no_value(v, :stdin); $stdin.read}, # rubocop:disable Style/Semicolon
74
- stdbin: lambda{ |v| ExtendedValue.assert_no_value(v, :stdbin); $stdin.binmode.read}, # rubocop:disable Style/Semicolon
75
- yaml: lambda{ |v| YAML.load(v)},
76
- zlib: lambda{ |v| Zlib::Inflate.inflate(v)},
77
- extend: lambda{ |v| ExtendedValue.instance.evaluate_all(v)}
59
+ val: lambda{ |i| i},
60
+ base64: lambda{ |i| Base64.decode64(i)},
61
+ csvt: lambda{ |i| ExtendedValue.decode_csvt(i)},
62
+ env: lambda{ |i| ENV.fetch(i, nil)},
63
+ file: lambda{ |i| File.read(File.expand_path(i))},
64
+ uri: lambda{ |i| UriReader.read(i)},
65
+ json: lambda{ |i| JSON_parse(i)},
66
+ lines: lambda{ |i| i.split("\n")},
67
+ list: lambda{ |i| i[1..-1].split(i[0])},
68
+ none: lambda{ |i| ExtendedValue.assert_no_value(i, :none); nil}, # rubocop:disable Style/Semicolon
69
+ path: lambda{ |i| File.expand_path(i)},
70
+ re: lambda{ |i| Regexp.new(i, Regexp::MULTILINE)},
71
+ ruby: lambda{ |i| Environment.secure_eval(i, __FILE__, __LINE__)},
72
+ secret: lambda{ |i| prompt = i.empty? ? 'secret' : i; $stdin.getpass("#{prompt}> ")}, # rubocop:disable Style/Semicolon
73
+ stdin: lambda{ |i| ExtendedValue.assert_no_value(i, :stdin); $stdin.read}, # rubocop:disable Style/Semicolon
74
+ stdbin: lambda{ |i| ExtendedValue.assert_no_value(i, :stdbin); $stdin.binmode.read}, # rubocop:disable Style/Semicolon
75
+ yaml: lambda{ |i| YAML.load(i)},
76
+ zlib: lambda{ |i| Zlib::Inflate.inflate(i)},
77
+ extend: lambda{ |i| ExtendedValue.instance.evaluate_all(i)}
78
78
  }
79
79
  @default_decoder = nil
80
80
  end
@@ -85,14 +85,15 @@ module Aspera
85
85
  end
86
86
 
87
87
  # JSON Parser, with more information on error location
88
- def JSON_parse(v)
89
- JSON.parse(v)
88
+ # :reek:UncommunicativeMethodName
89
+ def JSON_parse(value) # rubocop:disable Naming/MethodName
90
+ JSON.parse(value)
90
91
  rescue JSON::ParserError => e
91
92
  m = /at line (\d+) column (\d+)/.match(e.message)
92
93
  raise if m.nil?
93
94
  line = m[1].to_i - 1
94
95
  column = m[2].to_i - 1
95
- lines = v.lines
96
+ lines = value.lines
96
97
  raise if line >= lines.size
97
98
  error_line = lines[line].chomp
98
99
  context_col_beg = [column - 10, 0].max
@@ -145,9 +146,7 @@ module Aspera
145
146
  # parse string value as extended value
146
147
  # use default decoder if none is specified
147
148
  def evaluate_with_default(value)
148
- if value.is_a?(String) && value.match(/^#{handler_regex_string}.*$/).nil? && !@default_decoder.nil?
149
- value = [MARKER_START, @default_decoder, MARKER_END, value].join
150
- end
149
+ value = [MARKER_START, @default_decoder, MARKER_END, value].join if value.is_a?(String) && value.match(/^#{handler_regex_string}.*$/).nil? && !@default_decoder.nil?
151
150
  return evaluate(value)
152
151
  end
153
152
 
@@ -157,7 +156,7 @@ module Aspera
157
156
  while (m = value.match(regex))
158
157
  sub_value = "@#{m[2]}:#{m[3]}"
159
158
  Log.log.debug{"evaluating #{sub_value}"}
160
- value = m[1] + evaluate(sub_value) + m[4]
159
+ value = "#{m[1]}#{evaluate(sub_value)}#{m[4]}"
161
160
  end
162
161
  return value
163
162
  end