aspera-cli 4.26.0 → 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 (99) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +36 -4
  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 +45 -37
  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 +9 -9
  14. data/lib/aspera/ascmd.rb +4 -4
  15. data/lib/aspera/ascp/installation.rb +13 -6
  16. data/lib/aspera/assert.rb +17 -13
  17. data/lib/aspera/cli/context.rb +58 -0
  18. data/lib/aspera/cli/extended_value.rb +9 -4
  19. data/lib/aspera/cli/formatter.rb +91 -187
  20. data/lib/aspera/cli/http.rb +167 -0
  21. data/lib/aspera/cli/manager.rb +132 -77
  22. data/lib/aspera/cli/options.schema.yaml +82 -0
  23. data/lib/aspera/cli/plugins/alee.rb +2 -2
  24. data/lib/aspera/cli/plugins/aoc.rb +113 -77
  25. data/lib/aspera/cli/plugins/ats.rb +16 -16
  26. data/lib/aspera/cli/plugins/base.rb +64 -49
  27. data/lib/aspera/cli/plugins/config.rb +172 -444
  28. data/lib/aspera/cli/plugins/console.rb +7 -7
  29. data/lib/aspera/cli/plugins/cos.rb +2 -2
  30. data/lib/aspera/cli/plugins/factory.rb +3 -0
  31. data/lib/aspera/cli/plugins/faspex.rb +22 -22
  32. data/lib/aspera/cli/plugins/faspex5.rb +68 -49
  33. data/lib/aspera/cli/plugins/faspio.rb +1 -1
  34. data/lib/aspera/cli/plugins/httpgw.rb +2 -2
  35. data/lib/aspera/cli/plugins/node.rb +80 -80
  36. data/lib/aspera/cli/plugins/orchestrator.rb +21 -26
  37. data/lib/aspera/cli/plugins/preview.rb +9 -9
  38. data/lib/aspera/cli/plugins/server.rb +7 -7
  39. data/lib/aspera/cli/plugins/shares.rb +2 -2
  40. data/lib/aspera/cli/preset_manager.rb +235 -0
  41. data/lib/aspera/cli/result.rb +310 -0
  42. data/lib/aspera/cli/{main.rb → runner.rb} +48 -130
  43. data/lib/aspera/cli/sync_actions.rb +15 -11
  44. data/lib/aspera/cli/terminal_formatter.rb +65 -0
  45. data/lib/aspera/cli/transfer_agent.rb +17 -15
  46. data/lib/aspera/cli/transfer_progress.rb +6 -6
  47. data/lib/aspera/cli/version.rb +1 -1
  48. data/lib/aspera/cli/wizard.rb +5 -6
  49. data/lib/aspera/command_line_builder.rb +22 -18
  50. data/lib/aspera/data_repository.rb +4 -0
  51. data/lib/aspera/dot_container.rb +1 -1
  52. data/lib/aspera/environment.rb +25 -6
  53. data/lib/aspera/faspex_gw.rb +1 -1
  54. data/lib/aspera/formatter_interface.rb +14 -0
  55. data/lib/aspera/hash_ext.rb +6 -0
  56. data/lib/aspera/json_rpc.rb +6 -5
  57. data/lib/aspera/keychain/base.rb +1 -1
  58. data/lib/aspera/keychain/encrypted_hash.rb +1 -1
  59. data/lib/aspera/keychain/factory.rb +1 -1
  60. data/lib/aspera/keychain/macos_security.rb +1 -1
  61. data/lib/aspera/log.rb +10 -5
  62. data/lib/aspera/markdown.rb +4 -1
  63. data/lib/aspera/nagios.rb +2 -2
  64. data/lib/aspera/oauth/base.rb +5 -5
  65. data/lib/aspera/oauth/boot.rb +43 -0
  66. data/lib/aspera/oauth/factory.rb +38 -14
  67. data/lib/aspera/oauth/web.rb +2 -2
  68. data/lib/aspera/oauth.rb +1 -0
  69. data/lib/aspera/persistency_action_once.rb +2 -2
  70. data/lib/aspera/preview/file_types.rb +4 -0
  71. data/lib/aspera/products/connect.rb +3 -0
  72. data/lib/aspera/products/transferd.rb +2 -2
  73. data/lib/aspera/proxy_auto_config.rb +6 -3
  74. data/lib/aspera/rest.rb +9 -5
  75. data/lib/aspera/rest_error_analyzer.rb +4 -0
  76. data/lib/aspera/rest_list.rb +10 -3
  77. data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +62811 -0
  78. data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +9637 -0
  79. data/lib/aspera/schema/async_tables.yaml +361 -0
  80. data/lib/aspera/schema/documentation.rb +107 -0
  81. data/lib/aspera/schema/reader.rb +75 -0
  82. data/lib/aspera/schema/registry.rb +70 -0
  83. data/lib/aspera/secret_hider.rb +4 -0
  84. data/lib/aspera/sync/conf.schema.yaml +0 -26
  85. data/lib/aspera/sync/database.rb +14 -10
  86. data/lib/aspera/sync/operations.rb +14 -10
  87. data/lib/aspera/temp_file_manager.rb +4 -0
  88. data/lib/aspera/transfer/faux_file.rb +1 -1
  89. data/lib/aspera/transfer/parameters.rb +4 -4
  90. data/lib/aspera/transfer/resumer.rb +2 -2
  91. data/lib/aspera/transfer/spec.rb +3 -3
  92. data/lib/aspera/transfer/spec.schema.yaml +18 -9
  93. data/lib/aspera/transfer/uri.rb +1 -1
  94. data/lib/aspera/uri_reader.rb +1 -1
  95. data/lib/aspera/yaml.rb +4 -2
  96. data.tar.gz.sig +0 -0
  97. metadata +28 -15
  98. metadata.gz.sig +1 -1
  99. data/lib/aspera/transfer/spec_doc.rb +0 -76
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5dcaa66a7401bb325ae68b4f51ef44e7820332159203a7a5247fc3c2d447d9e5
4
- data.tar.gz: cf0a6734797c0803d77daa01b547daccf4dc4af0c17ff7872d186ad3fbffe62f
3
+ metadata.gz: 3959459dc4ecd6fbc28e485b7665744293691d275d7597375b9ee2e80dfb9477
4
+ data.tar.gz: 62fe6561296d5a6f35473e7c7e831a3089e54fecdbd364a4042e01f993db180e
5
5
  SHA512:
6
- metadata.gz: e3c8fb9d31eca016b0c88ef324aa307e73535b34672cd2f42d54c4695b7d12fa8d18e98cdbff7115a65856d161ba5869fb3e3e83eef97293411189d7c8f5a454
7
- data.tar.gz: bfee651a574ceaa6c6e10ea54b51d50e3d58cf33e0e89b909ad71bff7969dcb01c45707b3ae2cc4bccd553bb69c42ab0f288a043943df01fd9ca9379d6650872
6
+ metadata.gz: 95a052413af5c03a31e74c5b71f359b4dcd7f2af34e59be7c5c2fb49af862b45ce76be92d464419da1c9c944e1d140cd5b957a72916f53384e95317faf08fbcf
7
+ data.tar.gz: ed1aaa7ac2079839f69b506044ca7129e16cc4eb2c371d3311deb598aa91c6ed22c15bac03f8398062768cbdfc8131dc4fcafe84b637ce2a8357fb8c3af5c515
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -1,6 +1,38 @@
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`.
22
+
23
+ ## 4.26.1
24
+
25
+ Released: 2026-06-11
26
+
27
+ ### New Features
28
+
29
+ * `aoc`: Added new command `admin workspace shared_folder <workspace ID> node <shared folder ID>` to provide direct access to node operations from shared folders.
30
+ * **global**: Schema for Extended Value for option or command argument can now be retrieved using the special value `help` (e.g., `--ts=help` or `package send help`). Those are generated from JSON schema descriptions.
31
+ * **doc**: Some tables in documentation are now generated from JSON schema descriptions.
32
+
33
+ ### Issues Fixed
34
+
35
+ * **global**: Resolved issue where supported enumerated values were not properly listed when no value was provided (e.g., for command parameters).
4
36
 
5
37
  ## 4.26.0
6
38
 
@@ -17,7 +49,7 @@ Released: 2026-05-28
17
49
  * `aoc`: Support selection of workspace using percent selectors: `%name:` and `%id:`.
18
50
  * **global**: Added download URLs for `transferd` 1.1.8.
19
51
  * **global**: New Extended Value modifier: `s` converts to `String`.
20
- * **global**: Parameter `str_lst_sep` or option `table_style` allows setting separator for list of strings.
52
+ * **global**: Parameter `str_lst_sep` of option `table_style` allows setting separator for list of strings.
21
53
 
22
54
  ### Issues Fixed
23
55
 
@@ -57,7 +89,7 @@ Released: 2026-03-04
57
89
 
58
90
  ### New Features
59
91
 
60
- * **general**: If `@:` is used, then marker `END` optionally marks the end of collected arguments.
92
+ * **global**: If `@:` is used, then marker `END` optionally marks the end of collected arguments.
61
93
  * `format`: `display` defaults to `info` only if `format` is set to `table`, else defaults to `data`.
62
94
  * `node`: Parameter `accept_v4` of option `node_api` (boolean, defaults to `true`) allows using gen4 browsing with `Accept-Version: 4.0` for best performance when there are thousands of files.
63
95
 
@@ -440,7 +472,7 @@ Released: 2024-07-13
440
472
 
441
473
  ### Breaking Changes
442
474
 
443
- * `config`: Command `remote_certificate` now takes a sub-command.
475
+ * `config`: Command `remote_certificate` now takes a command.
444
476
  * **global**: Moved a few internal classes in new/renamed modules.
445
477
  * **global**: Deprecated pseudo transfer specification parameters starting with `EX_`:
446
478
  * `EX_ssh_key_paths`: Use spec `ssh_private_key` or option `transfer_info={"ascp_args":["-i","..."]}`
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
@@ -362,6 +369,7 @@ module Aspera
362
369
  end
363
370
  end
364
371
 
372
+ # @param expected [Array<String>] Link types
365
373
  def assert_public_link_types(expected)
366
374
  Aspera.assert_values(public_link['purpose'], expected){'public link type'}
367
375
  end
@@ -482,7 +490,7 @@ module Aspera
482
490
  ak_secret = @secret_finder&.lookup_secret(url: node_info['url'], username: node_info['access_key'])
483
491
  # If secret is available, or no scope, use basic auth
484
492
  if scope.nil? || ak_secret
485
- 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']}"}
486
494
  node_params[:auth] = {
487
495
  type: :basic,
488
496
  username: node_info['access_key'],
@@ -519,10 +527,10 @@ module Aspera
519
527
  Log.dump(:metadata, pkg_meta)
520
528
  pkg_meta.each do |field|
521
529
  Aspera.assert_type(field, Hash){'metadata field'}
522
- Aspera.assert(field.key?('name')){'metadata field must have name'}
523
- 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')
524
532
  Aspera.assert_type(field['values'], Array){'metadata field values'}
525
- 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']}"}
526
534
  end
527
535
  meta_schema.each do |field|
528
536
  provided = pkg_meta.select{ |i| i['name'].eql?(field['name'])}
@@ -557,7 +565,7 @@ module Aspera
557
565
  full_recipient_info = lookup_with_q(entity_type, value: short_recipient_info, query: {'workspace_id' => ws_id})
558
566
  rescue EntityNotFound
559
567
  # dropboxes cannot be created on the fly
560
- Aspera.assert_values(entity_type, 'contacts', type: Error){"No such shared inbox in workspace #{ws_id}"}
568
+ Aspera.assert_values(entity_type, %w[contacts], type: Error){"No such shared inbox in workspace #{ws_id}"}
561
569
  # unknown user: create it as external user
562
570
  full_recipient_info = create('contacts', {
563
571
  'current_workspace_id' => ws_id,
@@ -748,7 +756,7 @@ module Aspera
748
756
  # @param types [Array] event types
749
757
  def permissions_send_event(event_data:, app_info:, types: PERMISSIONS_CREATED)
750
758
  Aspera.assert_type(types, Array)
751
- Aspera.assert(!types.empty?)
759
+ Aspera.assert(!types.empty?, 'types must not be empty')
752
760
  event_creation = {
753
761
  'types' => types,
754
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: {
@@ -165,7 +165,7 @@ module Aspera
165
165
  items = scope.split(Scope::SEPARATOR, 2)
166
166
  Aspera.assert(items.length.eql?(2)){"invalid scope: #{scope}"}
167
167
  Aspera.assert(items[0].start_with?(Scope::NODE_PREFIX)){"invalid scope: #{scope}"}
168
- return {access_key: items[0][Scope::NODE_PREFIX.length..-1], scope: items[1]}
168
+ return {access_key: items[0].delete_prefix(Scope::NODE_PREFIX), scope: items[1]}
169
169
  end
170
170
 
171
171
  # Create an Aspera Node bearer token
@@ -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]
@@ -540,7 +540,7 @@ module Aspera
540
540
  else
541
541
  transfer_spec.merge!(transport_params)
542
542
  end
543
- Aspera.assert_values(transfer_spec['remote_user'], Transfer::Spec::ACCESS_KEY_TRANSFER_USER, type: :warn){'transfer user'}
543
+ Aspera.assert_values(transfer_spec['remote_user'], [Transfer::Spec::ACCESS_KEY_TRANSFER_USER], type: :warn){'transfer user'}
544
544
  return transfer_spec
545
545
  end
546
546
 
@@ -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,10 +61,10 @@ 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
- product_name = ascp_location[USE_PRODUCT_PREFIX.length..-1]
67
+ product_name = ascp_location.delete_prefix(USE_PRODUCT_PREFIX)
64
68
  if product_name.eql?(FIRST_FOUND)
65
69
  pl = installed_products.first
66
70
  raise "No Aspera transfer module or SDK found.\nRefer to the manual or install SDK with command:\nascli conf transferd install" if pl.nil?
@@ -113,7 +117,7 @@ module Aspera
113
117
  file = File.join(File.dirname(file), Environment.instance.exe_file(k.to_s)) unless k.eql?(:transferd)
114
118
  when :ssh_private_dsa, :ssh_private_rsa
115
119
  # assume last 3 letters are type
116
- type = k.to_s[-3..-1].to_sym
120
+ type = k.to_s[-3..].to_sym
117
121
  file = check_or_create_sdk_file("aspera_bypass_#{type}.pem"){DataRepository.instance.item(type)}
118
122
  when :aspera_license
119
123
  file = check_or_create_sdk_file('aspera-license'){DataRepository.instance.item(:license)}
@@ -243,9 +247,11 @@ module Aspera
243
247
  end
244
248
 
245
249
  # @param sdk_archive_path [String] path to SDK archive
246
- # @param &block called with: file path, data stream, link target if link?
250
+ # @yieldparam entry_name [String] File path in archive
251
+ # @yieldparam entry_stream [IO, Gem::Package::TarReader::Entry] Data stream
252
+ # @yieldparam link_target [String, nil] Link target if symlink, nil otherwise
247
253
  def extract_archive_files(sdk_archive_path)
248
- Aspera.assert(block_given?){'missing block'}
254
+ Aspera.assert(block_given?, 'missing block')
249
255
  case sdk_archive_path
250
256
  # Windows and Mac use zip
251
257
  when /\.zip$/
@@ -281,7 +287,8 @@ module Aspera
281
287
  # @param url [nil, String] URL to SDK archive, if nil: default url for version
282
288
  # @param version [nil, String] Specific version, if nil: latest version
283
289
  # @param backup [Boolean] If destination folder exists, then rename
284
- # @param &block [nil, Proc] A lambda that receives a file path from archive and tells destination sub folder(end with /) or file, or nil to not extract
290
+ # @yieldparam entry_name [String] File path from archive
291
+ # @yieldreturn [String, nil] Destination sub folder (end with /) or file, or nil to not extract
285
292
  # @return [Array] name, ascp version (from execution), folder
286
293
  def install_sdk(folder:, url: nil, version: nil, backup: true)
287
294
  url ||= sdk_url_for_platform(version: version)
data/lib/aspera/assert.rb CHANGED
@@ -20,21 +20,24 @@ module Aspera
20
20
  class << self
21
21
  # Replaces `raise` in assertion
22
22
  # Allows sending exception, or just error log, when type is `:error`
23
- # @param type [Exception,Symbol] Send to log if symbol, else raise exception
23
+ # @param type [Exception, Symbol] Send to log if symbol, else raise exception
24
24
  # @param message [String] Message for error.
25
+ # @raise [Exception]
26
+ # @return [nil]
25
27
  def report_error(type, message)
26
28
  if type.is_a?(Symbol)
27
29
  Log.log.send(type, message)
28
30
  else
29
31
  raise type, message
30
32
  end
33
+ nil
31
34
  end
32
35
 
33
36
  # Assert that a condition is true, else raise exception
34
37
  # @param assertion [TrueClass, FalseClass] Must be true
35
- # @param info [String,nil] Fixed message in case assert fails, else use `block`
38
+ # @param info [String,nil] Fixed message in case assert fails, else use block
36
39
  # @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
37
- # @param block [Proc] Produces a string that describes the problem for complex messages
40
+ # @yieldreturn [String] A string that describes the problem for complex messages
38
41
  # The block is executed in the context of the Aspera module
39
42
  def assert(assertion, info = nil, type: AssertError)
40
43
  raise InternalError, 'bad assert: both info and block given' unless info.nil? || !block_given?
@@ -50,18 +53,18 @@ module Aspera
50
53
  # @param value [Object] The value to check
51
54
  # @param classes [Class, Array] The expected type(s)
52
55
  # @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
53
- # @param block [Proc] Additional description in front of message
56
+ # @yieldreturn [String] Additional description to prepend to the error message
54
57
  def assert_type(value, *classes, type: AssertError)
55
- assert(classes.any?{ |k| value.is_a?(k)}, type: type){"#{"#{yield}: " if block_given?}expecting #{classes.join(', ')}, but have (#{value.class})#{value.inspect}"}
58
+ assert(classes.any?{ |k| value.is_a?(k)}, type: type){"#{"#{yield}: " if block_given?}expecting type #{classes.join(', ')}, but have (#{value.class})#{value.inspect}"}
56
59
  end
57
60
 
58
61
  # Assert that all value of array are of the same specified type.
59
62
  # @param array [Array] The array to check
60
63
  # @param klass [Class] The expected type of elements
61
64
  # @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
62
- # @param block [Proc] Additional description in front of message
65
+ # @yieldreturn [String] Additional description to prepend to the error message
63
66
  def assert_array_all(array, klass, type: AssertError)
64
- assert_type(array, Array, type: type)
67
+ assert_type(array, Array, type: AssertError){'array'}
65
68
  assert(array.all?(klass), type: type){"#{"#{yield}: " if block_given?}expecting all as #{klass}, but have #{array.map(&:class).uniq}"}
66
69
  end
67
70
 
@@ -70,19 +73,20 @@ module Aspera
70
73
  # @param key_class [Class] The expected type of keys (or nil)
71
74
  # @param value_class [Class] The expected type of values (or nil)
72
75
  # @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
73
- # @param block [Proc] Additional description in front of message
76
+ # @yieldreturn [String] Additional description to prepend to the error message
74
77
  def assert_hash_all(hash, key_class, value_class, type: AssertError)
75
- assert_type(hash, Hash, type: type)
76
- assert_array_all(hash.keys, key_class, type: AssertError){"#{"#{yield}: " if block_given?}keys"} unless key_class.nil?
77
- assert_array_all(hash.values, value_class, type: AssertError){"#{"#{yield}: " if block_given?}values"} unless value_class.nil?
78
+ assert_type(hash, Hash, type: AssertError){'hash'}
79
+ assert_array_all(hash.keys, key_class, type: type){"#{"#{yield}: " if block_given?}keys"} unless key_class.nil?
80
+ assert_array_all(hash.values, value_class, type: type){"#{"#{yield}: " if block_given?}values"} unless value_class.nil?
78
81
  end
79
82
 
80
83
  # Assert that value is one of the given values
81
84
  # @param value [Object] Value to check
82
85
  # @param values [Array] Accepted values
83
86
  # @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
84
- # @param block [Proc] Additional description in front of message
87
+ # @yieldreturn [String] Additional description to prepend to the error message
85
88
  def assert_values(value, values, type: AssertError)
89
+ assert_type(values, Array, type: AssertError){'values'}
86
90
  assert(values.include?(value), type: type) do
87
91
  val_list = values.inspect
88
92
  val_list = "one of #{val_list}" if values.is_a?(Array)
@@ -93,7 +97,7 @@ module Aspera
93
97
  # The value is not one of the expected values
94
98
  # @param value [Object] The wrong value
95
99
  # @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
96
- # @param &block [Proc] Additional description in front of message
100
+ # @yieldreturn [String] Additional description to prepend to the error message
97
101
  def error_unexpected_value(value, type: InternalError)
98
102
  report_error(type, "#{"#{yield}: " if block_given?}unexpected value: #{value.inspect}")
99
103
  end