aspera-cli 4.26.1 → 4.26.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 (86) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +19 -1
  4. data/CONTRIBUTING.md +2 -5
  5. data/bin/ascli +2 -2
  6. data/lib/aspera/agent/direct.rb +1 -1
  7. data/lib/aspera/agent/factory.rb +4 -0
  8. data/lib/aspera/agent/httpgw.rb +1 -1
  9. data/lib/aspera/agent/node.rb +2 -2
  10. data/lib/aspera/api/aoc.rb +43 -36
  11. data/lib/aspera/api/cos_node.rb +1 -1
  12. data/lib/aspera/api/faspex.rb +7 -6
  13. data/lib/aspera/api/node.rb +7 -7
  14. data/lib/aspera/ascmd.rb +4 -4
  15. data/lib/aspera/ascp/installation.rb +6 -2
  16. data/lib/aspera/cli/context.rb +58 -0
  17. data/lib/aspera/cli/extended_value.rb +3 -2
  18. data/lib/aspera/cli/formatter.rb +83 -184
  19. data/lib/aspera/cli/http.rb +167 -0
  20. data/lib/aspera/cli/manager.rb +3 -2
  21. data/lib/aspera/cli/plugins/alee.rb +2 -2
  22. data/lib/aspera/cli/plugins/aoc.rb +78 -67
  23. data/lib/aspera/cli/plugins/ats.rb +16 -16
  24. data/lib/aspera/cli/plugins/base.rb +18 -12
  25. data/lib/aspera/cli/plugins/config.rb +166 -438
  26. data/lib/aspera/cli/plugins/console.rb +7 -7
  27. data/lib/aspera/cli/plugins/cos.rb +2 -2
  28. data/lib/aspera/cli/plugins/factory.rb +3 -0
  29. data/lib/aspera/cli/plugins/faspex.rb +21 -21
  30. data/lib/aspera/cli/plugins/faspex5.rb +64 -44
  31. data/lib/aspera/cli/plugins/faspio.rb +1 -1
  32. data/lib/aspera/cli/plugins/httpgw.rb +2 -2
  33. data/lib/aspera/cli/plugins/node.rb +79 -79
  34. data/lib/aspera/cli/plugins/orchestrator.rb +21 -26
  35. data/lib/aspera/cli/plugins/preview.rb +9 -9
  36. data/lib/aspera/cli/plugins/server.rb +7 -7
  37. data/lib/aspera/cli/plugins/shares.rb +2 -2
  38. data/lib/aspera/cli/preset_manager.rb +235 -0
  39. data/lib/aspera/cli/result.rb +310 -0
  40. data/lib/aspera/cli/{main.rb → runner.rb} +14 -155
  41. data/lib/aspera/cli/sync_actions.rb +14 -10
  42. data/lib/aspera/cli/terminal_formatter.rb +65 -0
  43. data/lib/aspera/cli/transfer_progress.rb +6 -6
  44. data/lib/aspera/cli/version.rb +1 -1
  45. data/lib/aspera/cli/wizard.rb +5 -6
  46. data/lib/aspera/command_line_builder.rb +1 -1
  47. data/lib/aspera/data_repository.rb +4 -0
  48. data/lib/aspera/dot_container.rb +1 -1
  49. data/lib/aspera/environment.rb +22 -3
  50. data/lib/aspera/faspex_gw.rb +1 -1
  51. data/lib/aspera/json_rpc.rb +6 -5
  52. data/lib/aspera/keychain/base.rb +1 -1
  53. data/lib/aspera/keychain/encrypted_hash.rb +1 -1
  54. data/lib/aspera/keychain/factory.rb +1 -1
  55. data/lib/aspera/keychain/macos_security.rb +1 -1
  56. data/lib/aspera/log.rb +6 -2
  57. data/lib/aspera/nagios.rb +2 -2
  58. data/lib/aspera/oauth/base.rb +5 -5
  59. data/lib/aspera/oauth/boot.rb +43 -0
  60. data/lib/aspera/oauth/factory.rb +37 -13
  61. data/lib/aspera/oauth/web.rb +2 -2
  62. data/lib/aspera/oauth.rb +1 -0
  63. data/lib/aspera/persistency_action_once.rb +2 -2
  64. data/lib/aspera/preview/file_types.rb +4 -0
  65. data/lib/aspera/products/connect.rb +3 -0
  66. data/lib/aspera/products/transferd.rb +2 -2
  67. data/lib/aspera/proxy_auto_config.rb +3 -3
  68. data/lib/aspera/rest.rb +8 -4
  69. data/lib/aspera/rest_error_analyzer.rb +4 -0
  70. data/lib/aspera/rest_list.rb +10 -3
  71. data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +14 -4
  72. data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +827 -88
  73. data/lib/aspera/schema/async_tables.yaml +361 -0
  74. data/lib/aspera/schema/reader.rb +1 -1
  75. data/lib/aspera/schema/registry.rb +13 -6
  76. data/lib/aspera/secret_hider.rb +4 -0
  77. data/lib/aspera/sync/database.rb +14 -10
  78. data/lib/aspera/sync/operations.rb +5 -5
  79. data/lib/aspera/temp_file_manager.rb +4 -0
  80. data/lib/aspera/transfer/parameters.rb +4 -4
  81. data/lib/aspera/transfer/resumer.rb +1 -1
  82. data/lib/aspera/transfer/spec.schema.yaml +17 -8
  83. data/lib/aspera/transfer/uri.rb +1 -1
  84. data.tar.gz.sig +0 -0
  85. metadata +21 -14
  86. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bffa626f5f4970bf91f90b15e594315d3c81a1ed92c8eff2f9533f7faef71aab
4
- data.tar.gz: 552c9b1d9225d3c1360f83d920bface08022314c7227bf36e7af439310e51c75
3
+ metadata.gz: 3959459dc4ecd6fbc28e485b7665744293691d275d7597375b9ee2e80dfb9477
4
+ data.tar.gz: 62fe6561296d5a6f35473e7c7e831a3089e54fecdbd364a4042e01f993db180e
5
5
  SHA512:
6
- metadata.gz: 2b655e7eaeee027e393fc7136c3f3b1a8d679a0825fb7d92baa6fa854d5cd68b11c01ecbeb9c32c457044f6eef3ae2bdbf22afa324b3d79643ec169aaad8f632
7
- data.tar.gz: eeb29fc5c14cbe809e31ee229b79cd650632987d93af8a892001e49aea06aa68ace8f7f660345dc27edf0a1ad6350a85e285ba449d8cc39774f9f4c1d4832a8a
6
+ metadata.gz: 95a052413af5c03a31e74c5b71f359b4dcd7f2af34e59be7c5c2fb49af862b45ce76be92d464419da1c9c944e1d140cd5b957a72916f53384e95317faf08fbcf
7
+ data.tar.gz: ed1aaa7ac2079839f69b506044ca7129e16cc4eb2c371d3311deb598aa91c6ed22c15bac03f8398062768cbdfc8131dc4fcafe84b637ce2a8357fb8c3af5c515
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -1,6 +1,24 @@
1
1
  # Changes (Release notes)
2
2
 
3
- <!-- markdownlint-configure-file { "no-duplicate-heading": { "siblings_only": true } } -->
3
+ ## 4.26.2
4
+
5
+ Released: 2026-07-17
6
+
7
+ ### New Features
8
+
9
+ * `faspex5`: New commands `admin file_processing` and `packages file_processing`.
10
+ * `aoc`: Command `user settings` now support `create`, `delete`, `list`, `show`, `modify`.
11
+ * `aoc`: New command `network_policy` and `configuration_policy` in `admin`.
12
+ * `sync` : New command `admin query` to execute an arbitrary SQL command on the snap DB.
13
+ * `sync` : Option `sql` to add arbitrary SQL suffixes to sync actions with snapshot DB.
14
+
15
+ ### Issues Fixed
16
+
17
+ * `aoc`: Fixed `admin application membership create`.
18
+
19
+ ### Breaking Changes
20
+
21
+ **global**: Replaced option `--bash-comp` with `config completion bash`.
4
22
 
5
23
  ## 4.26.1
6
24
 
data/CONTRIBUTING.md CHANGED
@@ -72,7 +72,7 @@ The `aspera-cli` architecture is designed to be modular and extensible.
72
72
 
73
73
  - **Entry Point**:
74
74
 
75
- `lib/aspera/cli/main.rb` contains the core CLI startup logic.
75
+ `lib/aspera/cli/runner.rb` contains the core CLI startup logic.
76
76
 
77
77
  - **Plugins**:
78
78
 
@@ -112,15 +112,12 @@ The following environment variables and macros control specific build behaviors:
112
112
  | `LOG_SECRETS` | `yes`/`no` | Toggles the logging of secrets in `rake` tasks. |
113
113
  | `LOG_LEVEL` | `debug`, ... | Sets the logging verbosity for `rake` tasks. |
114
114
  | `ENABLE_COVERAGE` | set/unset | Enables test coverage analysis when defined. |
115
- | `SIGNING_KEY` | File path | Path to the signing key used for building the gem file. |
116
- | `SIGNING_KEY_PEM` | PEM Value | The PEM content of the signing key. |
115
+ | `SIGNING_KEY` | File path or PEM | Path to the signing key file, or PEM content (starts with `-----BEGIN`). |
117
116
  | `DRY_RUN` | `1` | Simulates execution without performing actual operations (`git`, `gh`). |
118
117
  | `DEBUG` | `1` | Shows stack trace on errors during documentation generation. |
119
118
 
120
119
  These values can be set as standard environment variables or passed directly to the `rake` command.
121
120
 
122
- Setting `SIGNING_KEY_PEM` automatically generates a file at `$HOME/.gem/signing_key.pem` and sets the `SIGNING_KEY` variable accordingly.
123
-
124
121
  > [!NOTE]
125
122
  > `ASPERA_CLI_TEST_CONF_URL` is typically defined in your shell profile for development, while others are usually for ad-hoc command-line use.
126
123
 
data/bin/ascli CHANGED
@@ -31,6 +31,6 @@ end
31
31
 
32
32
  require 'aspera/coverage'
33
33
  require 'aspera/environment'
34
- require 'aspera/cli/main'
34
+ require 'aspera/cli/runner'
35
35
  Aspera::Environment.instance.fix_home
36
- Aspera::Cli::Main.new(ARGV).process_command_line
36
+ Aspera::Cli::Runner.new(ARGV).run
@@ -294,7 +294,7 @@ module Aspera
294
294
  Log.log.debug{"before select, timeout: #{@spawn_timeout_sec}"}
295
295
  readable, _, _ = IO.select([mgt_server_socket], nil, nil, @spawn_timeout_sec)
296
296
  Log.log.debug('after select, before accept')
297
- Aspera.assert(readable, type: Transfer::Error){'timeout waiting mgt port connect (select not readable)'}
297
+ Aspera.assert(readable, 'timeout waiting mgt port connect (select not readable)', type: Transfer::Error)
298
298
  # There is a connection to accept
299
299
  client_socket, _client_addrinfo = mgt_server_socket.accept
300
300
  Log.log.debug('after accept')
@@ -6,6 +6,10 @@ require 'aspera/environment'
6
6
  module Aspera
7
7
  module Agent
8
8
  # Factory for Agents
9
+ #
10
+ # @!method self.instance
11
+ # Returns the singleton instance of Factory
12
+ # @return [Factory] the singleton instance
9
13
  class Factory
10
14
  include Singleton
11
15
 
@@ -32,7 +32,7 @@ module Aspera
32
32
  # HTTP download only supports file list
33
33
  # :reek:UnusedParameters token_regenerator
34
34
  def start_transfer(transfer_spec, token_regenerator: nil)
35
- Aspera.assert(!@gw_api.nil?){'GW URL must be set'}
35
+ Aspera.assert(!@gw_api.nil?, 'GW URL must be set')
36
36
  Aspera.assert_type(transfer_spec['paths'], Array){'paths'}
37
37
  Aspera.assert_type(transfer_spec['token'], String){'only token based transfer is supported in GW'}
38
38
  Log.dump(:user_spec, transfer_spec)
@@ -30,7 +30,7 @@ module Aspera
30
30
  @root_id = root_id
31
31
  rest_params = {base_url: url}
32
32
  if OAuth::Factory.bearer_auth?(password)
33
- Aspera.assert(!@root_id.nil?){'root_id not allowed for access key'}
33
+ Aspera.assert(!@root_id.nil?, 'root_id not allowed for access key')
34
34
  rest_params[:headers] = Api::Node.bearer_headers(password, access_key: username)
35
35
  else
36
36
  rest_params[:auth] = {
@@ -117,7 +117,7 @@ module Aspera
117
117
 
118
118
  # used internally to ensure node api is set before using.
119
119
  def node_api_
120
- Aspera.assert(!@node_api.nil?){'Before using this object, set the node_api attribute to a Aspera::Rest object'}
120
+ Aspera.assert(!@node_api.nil?, 'Before using this object, set the node_api attribute to a Aspera::Rest object')
121
121
  return @node_api
122
122
  end
123
123
  end
@@ -113,8 +113,10 @@ module Aspera
113
113
  return client_key, DataRepository.instance.item(client_key)
114
114
  end
115
115
 
116
- # base API url depends on domain, which could be "qa.xxx" or self-managed domain
117
- def api_base_url(api_domain: SAAS_DOMAIN_PROD)
116
+ # Base API url depends on domain, which could be "qa.xxx" or self-managed domain
117
+ # @param api_domain [String] the AoC domain (e.g. "ibmaspera.com" (SAAS_DOMAIN_PROD) or a self-managed domain)
118
+ # @return [String] the base URL for the AoC API
119
+ def api_base_url(api_domain)
118
120
  return "https://api.#{api_domain}"
119
121
  end
120
122
 
@@ -197,7 +199,7 @@ module Aspera
197
199
  # @option return [Integer] :total The total number of items
198
200
  def call_paging(query: {})
199
201
  Aspera.assert_type(query, Hash){'query'}
200
- Aspera.assert(block_given?)
202
+ Aspera.assert(block_given?, 'block required for call_paging')
201
203
  # set default large page if user does not specify own parameters. AoC Caps to 1000 anyway
202
204
  query['per_page'] = 1000 unless query.key?('per_page')
203
205
  max_items = query.delete(RestList::MAX_ITEMS)
@@ -211,7 +213,7 @@ module Aspera
211
213
  new_query = query.clone
212
214
  new_query['page'] = current_page
213
215
  result_data, result_http = yield(new_query)
214
- Aspera.assert(result_http)
216
+ Aspera.assert(result_http, 'expected HTTP result from paging block')
215
217
  total_count = result_http[HEADER_X_TOTAL_COUNT]&.to_i
216
218
  page_count += 1
217
219
  current_page += 1
@@ -267,11 +269,12 @@ module Aspera
267
269
  attr_accessor :ws_ids
268
270
 
269
271
  # By default: no workspace
272
+ # @param
270
273
  def initialize(
271
274
  scope: nil,
272
275
  subpath: API_V1,
273
276
  secret_finder: nil,
274
- # below: OAuth::AUTH_OPTIONS
277
+ # parameters below: OAuth::AUTH_OPTIONS
275
278
  url:,
276
279
  auth:,
277
280
  client_id: nil,
@@ -284,7 +287,14 @@ module Aspera
284
287
  )
285
288
  # Test here because link may set url
286
289
  Aspera.assert(url, 'Missing mandatory option: url', type: ParameterError)
287
- Aspera.assert(scope, 'Missing mandatory option: scope', type: ParameterError)
290
+ # analyze type of url
291
+ url_info = self.class.link_info(url)
292
+ Log.dump(:url_info, url_info)
293
+ @private_link = url_info[:private_link]
294
+ Aspera.assert(auth, 'Missing mandatory option: auth', type: ParameterError) unless url_info.key?(:token)
295
+ # this is the base API url
296
+ api_url_base = self.class.api_base_url(url_info[:instance_domain])
297
+ Aspera.assert(scope, 'Missing mandatory option: scope', type: ParameterError) unless auth.eql?(:none)
288
298
  # default values for client id
289
299
  client_id, client_secret = self.class.get_client_info if client_id.nil?
290
300
  # access key secrets are provided out of band to get node api access
@@ -298,28 +308,21 @@ module Aspera
298
308
  # Used only for init: provided by user
299
309
  @ws_ids = {id: nil, name: nil}
300
310
  @home_info = nil
301
- auth_params = {
302
- type: :oauth2,
303
- params: {
304
- client_id: client_id,
305
- client_secret: client_secret,
306
- scope: scope
311
+ base_args = {
312
+ base_url: "#{api_url_base}/#{subpath}",
313
+ auth: {
314
+ type: :oauth2,
315
+ grant_method: url_info.key?(:token) ? :url_json : auth,
316
+ base_url: "#{api_url_base}/#{OAUTH_API_SUBPATH}/#{url_info[:organization]}",
317
+ params: {
318
+ client_id: client_id,
319
+ client_secret: client_secret,
320
+ scope: scope
321
+ }
307
322
  }
308
323
  }
309
- # analyze type of url
310
- url_info = AoC.link_info(url)
311
- Log.dump(:url_info, url_info)
312
- @private_link = url_info[:private_link]
313
- auth_params[:grant_method] = if url_info.key?(:token)
314
- :url_json
315
- else
316
- Aspera.assert(auth, 'Missing mandatory option: auth', type: ParameterError)
317
- auth
318
- end
319
- # this is the base API url
320
- api_url_base = self.class.api_base_url(api_domain: url_info[:instance_domain])
321
- # auth URL
322
- auth_params[:base_url] = "#{api_url_base}/#{OAUTH_API_SUBPATH}/#{url_info[:organization]}"
324
+ # shortcut alias
325
+ auth_params = base_args[:auth]
323
326
  # fill other auth parameters based on OAuth method
324
327
  case auth_params[:grant_method]
325
328
  when :web
@@ -344,12 +347,16 @@ module Aspera
344
347
  auth_params[:json][:password] = password unless password.nil?
345
348
  # basic auth required for /token
346
349
  auth_params[:auth] = {type: :basic, username: client_id, password: client_secret}
347
- else Aspera.error_unexpected_value(auth_params[:grant_method]){'auth, use one of: :web, :jwt'}
350
+ when :boot
351
+ # cookie is optional: if absent, existing cache is used
352
+ auth_params[:cookie] = password unless password.nil?
353
+ auth_params[:username] = username unless username.nil?
354
+ when :none
355
+ auth_params.clear
356
+ auth_params[:type] = :none
357
+ else Aspera.error_unexpected_value(auth_params[:grant_method]){'auth, use one of: web, jwt, boot'}
348
358
  end
349
- super(
350
- base_url: "#{api_url_base}/#{subpath}",
351
- auth: auth_params
352
- )
359
+ super(**base_args)
353
360
  end
354
361
 
355
362
  # Read using the query and paging
@@ -483,7 +490,7 @@ module Aspera
483
490
  ak_secret = @secret_finder&.lookup_secret(url: node_info['url'], username: node_info['access_key'])
484
491
  # If secret is available, or no scope, use basic auth
485
492
  if scope.nil? || ak_secret
486
- Aspera.assert(ak_secret, "Secret not found for access key #{node_info['access_key']}@#{node_info['url']}", type: Error)
493
+ Aspera.assert(ak_secret, type: Error){"Secret not found for access key #{node_info['access_key']}@#{node_info['url']}"}
487
494
  node_params[:auth] = {
488
495
  type: :basic,
489
496
  username: node_info['access_key'],
@@ -520,10 +527,10 @@ module Aspera
520
527
  Log.dump(:metadata, pkg_meta)
521
528
  pkg_meta.each do |field|
522
529
  Aspera.assert_type(field, Hash){'metadata field'}
523
- Aspera.assert(field.key?('name')){'metadata field must have name'}
524
- Aspera.assert(field.key?('values')){'metadata field must have values'}
530
+ Aspera.assert(field.key?('name'), 'metadata field must have name')
531
+ Aspera.assert(field.key?('values'), 'metadata field must have values')
525
532
  Aspera.assert_type(field['values'], Array){'metadata field values'}
526
- Aspera.assert(!meta_schema.none?{ |i| i['name'].eql?(field['name'])}){"unknown metadata field: #{field['name']}"}
533
+ Aspera.assert(meta_schema.any?{ |i| i['name'].eql?(field['name'])}){"unknown metadata field: #{field['name']}"}
527
534
  end
528
535
  meta_schema.each do |field|
529
536
  provided = pkg_meta.select{ |i| i['name'].eql?(field['name'])}
@@ -749,7 +756,7 @@ module Aspera
749
756
  # @param types [Array] event types
750
757
  def permissions_send_event(event_data:, app_info:, types: PERMISSIONS_CREATED)
751
758
  Aspera.assert_type(types, Array)
752
- Aspera.assert(!types.empty?)
759
+ Aspera.assert(!types.empty?, 'types must not be empty')
753
760
  event_creation = {
754
761
  'types' => types,
755
762
  'node_id' => app_info.node_info['id'],
@@ -63,7 +63,7 @@ module Aspera
63
63
  ats_info = XmlSimple.xml_in(xml_result_text, {'ForceArray' => false})
64
64
  Log.dump(:ats_info, ats_info)
65
65
  Aspera.assert_hash_all(ats_info, String, nil){'ats_info'}
66
- Aspera.assert((FASP_INFO_KEYS - ats_info.keys).empty?){'ats_info missing required keys'}
66
+ Aspera.assert((FASP_INFO_KEYS - ats_info.keys).empty?, 'ats_info missing required keys')
67
67
  Aspera.assert_hash_all(ats_info['AccessKey'], String, String){'ats_info'}
68
68
  @storage_credentials = {
69
69
  'type' => 'token',
@@ -45,7 +45,7 @@ module Aspera
45
45
  info = Rest.query_to_h(URI.parse(http['Location']).query)
46
46
  Log.dump(:info, info)
47
47
  raise Error, info['action_message'] if info['action_message']
48
- Aspera.assert(info['code']){'Missing code in answer'}
48
+ Aspera.assert(info['code'], 'Missing code in answer')
49
49
  # Exchange code for token
50
50
  return create_token_call(base_params.merge(
51
51
  grant_type: 'authorization_code',
@@ -87,6 +87,7 @@ module Aspera
87
87
  email_notifications
88
88
  alternate_addresses
89
89
  webhooks
90
+ file_processing
90
91
  ].freeze
91
92
  # states for jobs not in final state
92
93
  JOB_RUNNING = %w[queued working].freeze
@@ -193,15 +194,15 @@ module Aspera
193
194
  }
194
195
  # old: headers: {'Passcode' => @pub_link_context['passcode']}
195
196
  when :boot
196
- Aspera.assert(password, type: ParameterError){'Missing password'}
197
+ Aspera.assert(password, 'Missing password', type: ParameterError)
197
198
  # the password here is the token copied directly from browser in developer mode
198
199
  {
199
200
  base_url: "#{url}/#{root}",
200
201
  headers: {'Authorization' => password}
201
202
  }
202
203
  when :web
203
- Aspera.assert(client_id, type: ParameterError){'Missing client_id'}
204
- Aspera.assert(redirect_uri, type: ParameterError){'Missing redirect_uri'}
204
+ Aspera.assert(client_id, 'Missing client_id', type: ParameterError)
205
+ Aspera.assert(redirect_uri, 'Missing redirect_uri', type: ParameterError)
205
206
  # opens a browser and ask user to auth using web
206
207
  {
207
208
  base_url: "#{url}/#{root}",
@@ -216,8 +217,8 @@ module Aspera
216
217
  }
217
218
  }
218
219
  when :jwt
219
- Aspera.assert(client_id, type: ParameterError){'Missing client_id'}
220
- Aspera.assert(private_key, type: ParameterError){'Missing private_key'}
220
+ Aspera.assert(client_id, 'Missing client_id', type: ParameterError)
221
+ Aspera.assert(private_key, 'Missing private_key', type: ParameterError)
221
222
  {
222
223
  base_url: "#{url}/#{root}",
223
224
  auth: {
@@ -174,9 +174,9 @@ module Aspera
174
174
  # @param private_key [OpenSSL::PKey::RSA] Private key to sign the token
175
175
  def bearer_token(access_key:, payload:, private_key:)
176
176
  Aspera.assert_type(payload, Hash)
177
- Aspera.assert(payload.key?('user_id'))
177
+ Aspera.assert(payload.key?('user_id'), 'payload must have user_id')
178
178
  Aspera.assert_type(payload['user_id'], String)
179
- Aspera.assert(!payload['user_id'].empty?)
179
+ Aspera.assert(!payload['user_id'].empty?, 'user_id must not be empty')
180
180
  Aspera.assert_type(private_key, OpenSSL::PKey::RSA)
181
181
  # Manage convenience parameters
182
182
  expiration_sec = payload['_validity'] || BEARER_TOKEN_VALIDITY_DEFAULT
@@ -205,7 +205,7 @@ module Aspera
205
205
  # If username is not provided, use the access key from the token
206
206
  if access_key.nil?
207
207
  access_key = Node.decode_scope(Node.decode_bearer_token(OAuth::Factory.bearer_token(bearer_auth))['scope'])[:access_key]
208
- Aspera.assert(!access_key.nil?)
208
+ Aspera.assert(!access_key.nil?, 'access_key could not be determined from token')
209
209
  end
210
210
  return {
211
211
  HEADER_X_ASPERA_ACCESS_KEY => access_key,
@@ -337,7 +337,7 @@ module Aspera
337
337
  # @param top_file_path [String] path of top folder (default = /)
338
338
  # @para query [Hash, nil] optional query for `read`
339
339
  def process_folder_tree(method_sym:, state:, top_file_id:, top_file_path: '/', query: nil)
340
- Aspera.assert(!top_file_path.nil?){'top_file_path not set'}
340
+ Aspera.assert(!top_file_path.nil?, 'top_file_path not set')
341
341
  Log.log.debug{"process_folder_tree: node=#{@app_info ? @app_info.node_info['id'] : 'nil'}, file id=#{top_file_id}, path=#{top_file_path}"}
342
342
  # Start at top folder
343
343
  folders_to_explore = [{id: top_file_id, path: top_file_path}]
@@ -405,7 +405,7 @@ module Aspera
405
405
  # - 'destination' [String]: Destination path (optional)
406
406
  def resolve_api_fid_paths(top_file_id, paths)
407
407
  Aspera.assert_type(paths, Array)
408
- Aspera.assert(paths.size.positive?)
408
+ Aspera.assert(paths.size.positive?, 'paths must not be empty')
409
409
  split_sources = paths.map{ |p| Pathname(p['source']).each_filename.to_a}
410
410
  root = []
411
411
  split_sources.map(&:size).min.times do |i|
@@ -490,7 +490,7 @@ module Aspera
490
490
  case auth_params[:type]
491
491
  when :basic
492
492
  ak_name = auth_params[:username]
493
- Aspera.assert(auth_params[:password]){'no secret in node object'}
493
+ Aspera.assert(auth_params[:password], 'no secret in node object')
494
494
  ak_token = Rest.basic_authorization(auth_params[:username], auth_params[:password])
495
495
  when :oauth2
496
496
  ak_name = params[:headers][HEADER_X_ASPERA_ACCESS_KEY]
@@ -551,7 +551,7 @@ module Aspera
551
551
  def read_with_paging(subpath, query = nil, iteration: nil, **call_args)
552
552
  Aspera.assert_type(iteration, Array, NilClass){'iteration'}
553
553
  Aspera.assert_type(query, Hash, NilClass){'query'}
554
- Aspera.assert(!call_args.key?(:query))
554
+ Aspera.assert(!call_args.key?(:query), ':query must not be in call_args (use query parameter)')
555
555
  query = {} if query.nil?
556
556
  query[:iteration_token] = iteration[0] unless iteration.nil? || iteration[0].nil?
557
557
  max = query.delete(RestList::MAX_ITEMS)
data/lib/aspera/ascmd.rb CHANGED
@@ -129,10 +129,10 @@ module Aspera
129
129
  Log.log.trace1{"execute_single:#{stdin_input}"}
130
130
  # execute, get binary output
131
131
  byte_buffer = @command_executor.execute(remote_cmd, input: stdin_input).unpack('C*')
132
- Aspera.assert(!byte_buffer.empty?){'empty answer from server'}
132
+ Aspera.assert(!byte_buffer.empty?, 'empty answer from server')
133
133
  # get hash or table result
134
134
  result = self.class.parse(byte_buffer, :result)
135
- Aspera.assert(byte_buffer.empty?){'unparsed bytes remaining'}
135
+ Aspera.assert(byte_buffer.empty?, 'unparsed bytes remaining')
136
136
  # get and delete info,always present in results
137
137
  system_info = result[:info]
138
138
  result.delete(:info)
@@ -187,7 +187,7 @@ module Aspera
187
187
  case type_descr[:decode]
188
188
  when :base
189
189
  num_bytes = type_name.eql?(:zstr) ? buffer.length : type_descr[:size]
190
- Aspera.assert(buffer.length >= num_bytes){'not enough bytes'}
190
+ Aspera.assert(buffer.length >= num_bytes, 'not enough bytes')
191
191
  byte_array = buffer.shift(num_bytes)
192
192
  byte_array = [byte_array] unless byte_array.is_a?(Array)
193
193
  result = byte_array.pack('C*').unpack1(type_descr[:unpack])
@@ -200,7 +200,7 @@ module Aspera
200
200
  until buffer.empty?
201
201
  btype = parse(buffer, :int8, indent_level)
202
202
  length = parse(buffer, :int32, indent_level)
203
- Aspera.assert(buffer.length >= length){'not enough bytes'}
203
+ Aspera.assert(buffer.length >= length, 'not enough bytes')
204
204
  value = buffer.shift(length)
205
205
  result.push({btype: btype, buffer: value})
206
206
  Log.log.trace1{"#{' .' * indent_level}:buffer_list[#{result.length - 1}] #{result.last}"}
@@ -27,6 +27,10 @@ module Aspera
27
27
  # It is used by object : AgentDirect to find necessary resources
28
28
  # By default it takes the first Aspera product found
29
29
  # The user can specify `ascp` location by calling: `sdk_folder=` method
30
+ #
31
+ # @!method self.instance
32
+ # Returns the singleton instance of Installation
33
+ # @return [Installation] the singleton instance
30
34
  class Installation
31
35
  include Singleton
32
36
 
@@ -57,7 +61,7 @@ module Aspera
57
61
  # - "product:FIRST" to use ascp from first found product
58
62
  def sdk_folder=(ascp_location)
59
63
  Aspera.assert_type(ascp_location, String){'ascp_location'}
60
- Aspera.assert(!ascp_location.empty?){'ascp location cannot be empty: check your config file'}
64
+ Aspera.assert(!ascp_location.empty?, 'ascp location cannot be empty: check your config file')
61
65
  folder =
62
66
  if ascp_location.start_with?(USE_PRODUCT_PREFIX)
63
67
  product_name = ascp_location.delete_prefix(USE_PRODUCT_PREFIX)
@@ -247,7 +251,7 @@ module Aspera
247
251
  # @yieldparam entry_stream [IO, Gem::Package::TarReader::Entry] Data stream
248
252
  # @yieldparam link_target [String, nil] Link target if symlink, nil otherwise
249
253
  def extract_archive_files(sdk_archive_path)
250
- Aspera.assert(block_given?){'missing block'}
254
+ Aspera.assert(block_given?, 'missing block')
251
255
  case sdk_archive_path
252
256
  # Windows and Mac use zip
253
257
  when /\.zip$/
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aspera/assert'
4
+
5
+ module Aspera
6
+ module Cli
7
+ # Global objects shared with plugins
8
+ class Context
9
+ # @type [Array<Symbol>]
10
+ MEMBERS = %i[options transfer config formatter persistency man_header presets http_config].freeze
11
+ # @!attribute [rw] options
12
+ # @return [Manager] the command line options manager
13
+ # @!attribute [rw] transfer
14
+ # @return [TransferAgent] the transfer agent, used by transfer plugins
15
+ # @!attribute [rw] config
16
+ # @return [Plugins::Config] the configuration plugin, used by plugins to get configuration values and presets
17
+ # @!attribute [rw] formatter
18
+ # @return [Formatter] the formatter, used by plugins to display results and messages
19
+ # @!attribute [rw] persistency
20
+ # @return [Object] # whatever the type is
21
+ # @!attribute [rw] man_header
22
+ # @return [Boolean] whether to display the manual header in plugin help
23
+ # @!attribute [rw] presets
24
+ # @return [PresetManager] manages the YAML config file and preset resolution
25
+ # @!attribute [rw] http_config
26
+ # @return [Http] manages HTTP/S and TLS runtime options
27
+ attr_accessor(*MEMBERS)
28
+
29
+ # Initialize all members to nil, so that they are defined and can be validated later
30
+ # @return [nil]
31
+ def initialize
32
+ MEMBERS.each{ |i| instance_variable_set(:"@#{i}", nil)}
33
+ end
34
+
35
+ # Validate that all members are set, raise exception if not
36
+ # @raise [Aspera::AssertionError] if any member is not set
37
+ # @return [nil]
38
+ def validate
39
+ MEMBERS.each do |i|
40
+ Aspera.assert(instance_variable_defined?(:"@#{i}")){"context member @#{i} is not defined"}
41
+ Aspera.assert(!instance_variable_get(:"@#{i}").nil?){"context member @#{i} is nil"}
42
+ end
43
+ end
44
+
45
+ # Check if the context is in manual-only mode
46
+ # @return [Boolean] true if in manual-only mode
47
+ def only_manual?
48
+ transfer.eql?(:only_manual)
49
+ end
50
+
51
+ # Set the context to manual-only mode
52
+ # @return [Symbol] :only_manual
53
+ def only_manual!
54
+ @transfer = :only_manual
55
+ end
56
+ end
57
+ end
58
+ end
@@ -86,6 +86,7 @@ module Aspera
86
86
  # Base handlers
87
87
  # Other handlers can be set using `on`
88
88
  # e.g. `preset` is reader in config plugin
89
+ # @type [Hash{Symbol => Proc}]
89
90
  @handlers = {
90
91
  val: lambda{ |i| i},
91
92
  base64: lambda{ |i| Base64.decode64(i)},
@@ -100,7 +101,7 @@ module Aspera
100
101
  path: lambda{ |i| File.expand_path(i)},
101
102
  re: lambda{ |i| Regexp.new(i, Regexp::MULTILINE)},
102
103
  ruby: lambda{ |i| Environment.secure_eval(i, __FILE__, __LINE__)},
103
- s: lambda{ |i| i.to_s},
104
+ s: lambda(&:to_s),
104
105
  secret: lambda{ |i| prompt = i.empty? ? 'secret' : i; $stdin.getpass("#{prompt}> ")}, # rubocop:disable Style/Semicolon
105
106
  stdin: lambda{ |i| ExtendedValue.read_stdin(i)},
106
107
  yaml: lambda{ |i| YAML.load(i)},
@@ -137,7 +138,7 @@ module Aspera
137
138
  # Add a new handler
138
139
  def on(name, &block)
139
140
  Aspera.assert_type(name, Symbol){'name'}
140
- Aspera.assert(block)
141
+ Aspera.assert(block){"block required when registering handler for #{name}"}
141
142
  Log.log.debug{"Setting handler for #{name}"}
142
143
  @handlers[name] = block
143
144
  update_regex