aspera-cli 4.27.5 → 4.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +160 -4
  4. data/CONTRIBUTING.md +4 -2
  5. data/README.md +1 -1
  6. data/bin/asession +1 -1
  7. data/docs/README.md +1086 -331
  8. data/docs/test-mcp-with-ai.md +5 -3
  9. data/lib/aspera/agent/direct.rb +15 -4
  10. data/lib/aspera/agent/factory.rb +2 -1
  11. data/lib/aspera/agent/node.rb +1 -1
  12. data/lib/aspera/api/cos_node.rb +1 -1
  13. data/lib/aspera/api/faspex.rb +1 -1
  14. data/lib/aspera/api/node.rb +6 -2
  15. data/lib/aspera/ascp/installation.rb +47 -11
  16. data/lib/aspera/ascp/management.rb +89 -43
  17. data/lib/aspera/cli/ascp_actions.rb +5 -19
  18. data/lib/aspera/cli/bootstrapper.rb +1 -2
  19. data/lib/aspera/cli/command_registry.rb +43 -3
  20. data/lib/aspera/cli/command_spec.rb +3 -0
  21. data/lib/aspera/cli/completion/ascli.bash +39 -5
  22. data/lib/aspera/cli/completion/ascli.fish +16 -8
  23. data/lib/aspera/cli/completion/ascli.ps1 +41 -0
  24. data/lib/aspera/cli/completion/ascli.zsh +20 -4
  25. data/lib/aspera/cli/completion_actions.rb +360 -0
  26. data/lib/aspera/cli/error.rb +42 -0
  27. data/lib/aspera/cli/extended_value.rb +30 -5
  28. data/lib/aspera/cli/formatter.rb +118 -56
  29. data/lib/aspera/cli/hints.rb +0 -1
  30. data/lib/aspera/cli/http.rb +6 -5
  31. data/lib/aspera/cli/info.rb +10 -4
  32. data/lib/aspera/cli/mailer.rb +1 -1
  33. data/lib/aspera/cli/mcp/http_servlet.rb +156 -0
  34. data/lib/aspera/cli/mcp/secrets.rb +143 -0
  35. data/lib/aspera/cli/mcp/tool.rb +231 -0
  36. data/lib/aspera/cli/option_value.rb +9 -1
  37. data/lib/aspera/cli/options.schema.yaml +390 -39
  38. data/lib/aspera/cli/parser.rb +14 -5
  39. data/lib/aspera/cli/plugins/aoc.rb +8 -11
  40. data/lib/aspera/cli/plugins/base.rb +36 -15
  41. data/lib/aspera/cli/plugins/config.rb +15 -44
  42. data/lib/aspera/cli/plugins/console.rb +3 -1
  43. data/lib/aspera/cli/plugins/factory.rb +1 -1
  44. data/lib/aspera/cli/plugins/faspex5.rb +9 -19
  45. data/lib/aspera/cli/plugins/faspio.rb +36 -5
  46. data/lib/aspera/cli/plugins/httpgw.rb +1 -0
  47. data/lib/aspera/cli/plugins/mcp.rb +177 -118
  48. data/lib/aspera/cli/plugins/node.rb +14 -8
  49. data/lib/aspera/cli/plugins/orchestrator.rb +351 -38
  50. data/lib/aspera/cli/plugins/server.rb +3 -1
  51. data/lib/aspera/cli/plugins/shares.rb +205 -35
  52. data/lib/aspera/cli/preset_actions.rb +15 -7
  53. data/lib/aspera/cli/preset_manager.rb +24 -2
  54. data/lib/aspera/cli/prompt.rb +3 -1
  55. data/lib/aspera/cli/result.rb +17 -4
  56. data/lib/aspera/cli/runner.rb +133 -43
  57. data/lib/aspera/cli/security.rb +134 -0
  58. data/lib/aspera/cli/table_fitter.rb +116 -0
  59. data/lib/aspera/cli/terminal_formatter.rb +23 -34
  60. data/lib/aspera/cli/transfer_agent.rb +4 -1
  61. data/lib/aspera/cli/vault_manager.rb +6 -5
  62. data/lib/aspera/cli/version.rb +1 -1
  63. data/lib/aspera/cli/web_secret.html.erb +61 -0
  64. data/lib/aspera/cli/web_secret.rb +220 -0
  65. data/lib/aspera/cli/wizard.rb +19 -9
  66. data/lib/aspera/command_line_builder.rb +26 -0
  67. data/lib/aspera/data_repository.rb +38 -24
  68. data/lib/aspera/duration.rb +42 -0
  69. data/lib/aspera/environment.rb +88 -24
  70. data/lib/aspera/exec_spec.rb +6 -5
  71. data/lib/aspera/faspex_gw.rb +98 -32
  72. data/lib/aspera/faspex_postproc.rb +187 -44
  73. data/lib/aspera/formatter_interface.rb +0 -1
  74. data/lib/aspera/id_generator.rb +2 -1
  75. data/lib/aspera/json_rpc/client.rb +51 -32
  76. data/lib/aspera/json_rpc/version.rb +1 -0
  77. data/lib/aspera/keychain/base.rb +5 -0
  78. data/lib/aspera/keychain/encrypted_hash.rb +6 -2
  79. data/lib/aspera/keychain/factory.rb +2 -3
  80. data/lib/aspera/keychain/hashicorp_vault.rb +7 -4
  81. data/lib/aspera/keychain/macos_security.rb +119 -13
  82. data/lib/aspera/keychain/one_password_api.rb +8 -3
  83. data/lib/aspera/keychain/one_password_base.rb +14 -9
  84. data/lib/aspera/keychain/one_password_cli.rb +4 -2
  85. data/lib/aspera/log.rb +3 -6
  86. data/lib/aspera/markdown.rb +152 -115
  87. data/lib/aspera/mime.rb +6 -0
  88. data/lib/aspera/nagios.rb +3 -1
  89. data/lib/aspera/node_emulator.rb +98 -101
  90. data/lib/aspera/oauth/{base.rb → client/base.rb} +5 -4
  91. data/lib/aspera/oauth/{boot.rb → client/boot.rb} +3 -2
  92. data/lib/aspera/oauth/{factory.rb → client/factory.rb} +7 -0
  93. data/lib/aspera/oauth/{generic.rb → client/generic.rb} +1 -1
  94. data/lib/aspera/oauth/{json_credentials.rb → client/json_credentials.rb} +1 -1
  95. data/lib/aspera/oauth/{jwt.rb → client/jwt.rb} +1 -1
  96. data/lib/aspera/oauth/{url_json.rb → client/url_json.rb} +1 -1
  97. data/lib/aspera/oauth/{web.rb → client/web.rb} +2 -2
  98. data/lib/aspera/oauth/server/authorize.html.erb +34 -0
  99. data/lib/aspera/oauth/server/pkce.rb +426 -0
  100. data/lib/aspera/orchestrator_workflow_checker.rb +561 -0
  101. data/lib/aspera/preview/terminal.rb +3 -2
  102. data/lib/aspera/rainbow.rb +27 -1
  103. data/lib/aspera/rest/aspera_errors.rb +51 -59
  104. data/lib/aspera/rest/client.rb +21 -14
  105. data/lib/aspera/rest/error_analyzer.rb +9 -0
  106. data/lib/aspera/rest/list.rb +6 -6
  107. data/lib/aspera/rest/util.rb +7 -1
  108. data/lib/aspera/rest.rb +0 -1
  109. data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +0 -2
  110. data/lib/aspera/schema/IBM Aspera Orchestrator API-v1-enhanced.yaml +4001 -0
  111. data/lib/aspera/schema/IBM Aspera faspio Gateway API-1.0.0-enhanced.yaml +1097 -0
  112. data/lib/aspera/schema/IBM Aspera on Cloud Automation API-1.0.5-enhanced.yaml +35 -35
  113. data/lib/aspera/schema/IBM_Aspera_Shares.yaml +819 -7
  114. data/lib/aspera/schema/documentation.rb +50 -78
  115. data/lib/aspera/schema/reader.rb +39 -11
  116. data/lib/aspera/schema/registry.rb +8 -2
  117. data/lib/aspera/secret_hider.rb +94 -15
  118. data/lib/aspera/secret_strength.rb +114 -0
  119. data/lib/aspera/servlet_helpers.rb +111 -0
  120. data/lib/aspera/sync/args.schema.yaml +3 -1
  121. data/lib/aspera/sync/conf.schema.yaml +16 -6
  122. data/lib/aspera/sync/operations.rb +21 -1
  123. data/lib/aspera/temp_file_manager.rb +79 -20
  124. data/lib/aspera/transfer/parameters.rb +5 -0
  125. data/lib/aspera/transfer/spec.rb +52 -12
  126. data/lib/aspera/transfer/spec.schema.yaml +8 -5
  127. data/lib/aspera/web_auth/index.html.erb +16 -0
  128. data/lib/aspera/web_auth/server.rb +18 -10
  129. data/lib/aspera/web_page.html.erb +262 -0
  130. data/lib/aspera/web_page.rb +61 -0
  131. data/lib/aspera/web_server_simple.rb +76 -10
  132. data.tar.gz.sig +0 -0
  133. metadata +47 -32
  134. metadata.gz.sig +0 -0
  135. data/lib/aspera/cli/mcp_tool.rb +0 -189
  136. data/lib/aspera/oauth.rb +0 -9
  137. data/lib/aspera/schema/IBM Aspera Orchestrator API-v1.yaml +0 -1784
  138. data/lib/aspera/schema/IBM Aspera faspio Gateway API-1.0.0.yaml +0 -249
  139. data/lib/aspera/web_auth/index.html +0 -147
@@ -7,6 +7,7 @@ require 'aspera/cli/result'
7
7
  require 'aspera/cli/command_registry'
8
8
  require 'aspera/cli/option_declarator'
9
9
  require 'aspera/cli/special_values'
10
+ require 'aspera/rest/list'
10
11
  require 'aspera/schema/registry'
11
12
 
12
13
  module Aspera
@@ -104,12 +105,26 @@ module Aspera
104
105
  # @param entity [String, Symbol] REST sub-path, or ctx key Symbol resolved at runtime
105
106
  # @param operations [Array<Symbol>] Verbs to expose; defaults to Operations::ALL
106
107
  # @param name [String, nil] Singular display name; defaults to last segment of entity (static only)
107
- # @param lookup [Symbol, nil] Instance method for percent-selector resolution
108
+ # @param lookup [Symbol, Proc, Array<String>, nil] Percent-selector resolution: instance method, lambda,
109
+ # or fields with unique values, looked up in the list of entities (plain Array of Hash)
108
110
  # @param id_name [Symbol, nil] Name of identifier argument; defaults to <name>_id, or id_as_arg field, or :id
111
+ # @param schema_entity [String, nil] REST sub-path in the API schema (body_component), when entity is a ctx key Symbol
109
112
  # @param kwargs [Hash] Shared params forwarded to every per-verb method
110
- def crud_commands(api:, entity:, operations: nil, name: nil, lookup: nil, id_name: nil, **kwargs)
113
+ def crud_commands(api:, entity:, operations: nil, name: nil, lookup: nil, id_name: nil, schema_entity: nil, **kwargs)
111
114
  name ||= entity_noun(entity, singular: !kwargs[:is_singleton]) unless entity.is_a?(Symbol)
112
115
  operations ||= Operations::ALL
116
+ schema_entity ||= entity if entity.is_a?(String)
117
+ if lookup.is_a?(Array)
118
+ unique_fields = lookup
119
+ # `%id:<id>` is the id itself (no lookup), other fields are looked up in the list of entities
120
+ lookup = lambda do |field, value, **ctx|
121
+ return value if field.eql?('id')
122
+ resolved_entity = entity.is_a?(Symbol) ? ctx.fetch(entity) : entity
123
+ label = name || resolved_entity
124
+ Aspera.assert_values(field, unique_fields, type: Cli::BadArgument) { "#{label} search field" }
125
+ Rest::List.lookup_entity_generic(field: field, value: value, entity: label) { crud_api(api).read(resolved_entity) }['id']
126
+ end
127
+ end
113
128
  # Body argument is named after the entity, e.g. <access_key>, and passed as data: to entity_<verb>
114
129
  data_name = name ? name.downcase.tr(' ', '_').to_sym : :data
115
130
  # Identifier argument is named after the entity, e.g. <access_key_id>, and passed as id: to entity_<verb>
@@ -121,10 +136,10 @@ module Aspera
121
136
  operations.each do |verb|
122
137
  id_arg = ({name: id_name, type: :identifier, lookup: lookup} if Operations::INSTANCE.include?(verb) && !kwargs[:is_singleton])
123
138
  schema_val =
124
- if kwargs[:body_component] && entity.is_a?(String)
139
+ if kwargs[:body_component] && schema_entity
125
140
  case verb
126
- when :create then Schema::Registry.req_body(kwargs[:body_component], "#{entity}.post")
127
- when :modify then Schema::Registry.req_body(kwargs[:body_component], "#{entity}/{id}.put")
141
+ when :create then Schema::Registry.req_body(kwargs[:body_component], "#{schema_entity}.post")
142
+ when :modify then Schema::Registry.req_body(kwargs[:body_component], "#{schema_entity}/{id}.put")
128
143
  end
129
144
  end
130
145
  args =
@@ -139,18 +154,10 @@ module Aspera
139
154
  id_arg ? [id_arg] : nil
140
155
  end
141
156
  action_proc = lambda do |**ctx|
142
- resolved_api =
143
- if api.is_a?(Symbol)
144
- api.to_s.start_with?('@') ? instance_variable_get(api) : send(api)
145
- elsif api.is_a?(Proc)
146
- instance_exec(&api)
147
- else
148
- api
149
- end
150
157
  resolved_entity = entity.is_a?(Symbol) ? ctx.fetch(entity) : entity
151
158
  ctx = ctx.merge(data: ctx[data_name]) if ctx.key?(data_name)
152
159
  ctx = ctx.merge(id: ctx[id_name]) if ctx.key?(id_name)
153
- send(:"entity_#{verb}", api: resolved_api, entity: resolved_entity, **kwargs, **ctx)
160
+ send(:"entity_#{verb}", api: crud_api(api), entity: resolved_entity, **kwargs, **ctx)
154
161
  end
155
162
  cmd_attrs = {description: operation_description(verb, name || entity.inspect), action: action_proc}
156
163
  cmd_attrs[:arguments] = args if args
@@ -555,7 +562,8 @@ module Aspera
555
562
  validation: arg_spec.type,
556
563
  accept_list: arg_spec.allowed,
557
564
  default: arg_spec.interactive ? nil : arg_spec.default,
558
- schema: arg_spec.schema
565
+ schema: arg_spec.schema,
566
+ raw: arg_spec.raw
559
567
  )
560
568
  end
561
569
  end
@@ -603,6 +611,19 @@ module Aspera
603
611
  )
604
612
  end
605
613
 
614
+ # Resolve the `api:` reference given to `crud_commands`.
615
+ # @param api [Symbol, Proc, Object] `:@ivar`, method name, Proc (instance_exec), or the API object itself
616
+ # @return [Rest::Client]
617
+ def crud_api(api)
618
+ if api.is_a?(Symbol)
619
+ api.to_s.start_with?('@') ? instance_variable_get(api) : send(api)
620
+ elsif api.is_a?(Proc)
621
+ instance_exec(&api)
622
+ else
623
+ api
624
+ end
625
+ end
626
+
606
627
  # --- Per-verb entity action methods ---
607
628
  # Each method handles exactly one CRUD verb.
608
629
  # The resource id (when needed) is received as `id:` from ctx — it must be
@@ -14,6 +14,7 @@ require 'aspera/cli/wizard'
14
14
  require 'aspera/cli/sync_actions'
15
15
  require 'aspera/cli/ascp_actions'
16
16
  require 'aspera/cli/preset_actions'
17
+ require 'aspera/cli/completion_actions'
17
18
  require 'aspera/cli/vault_manager'
18
19
  require 'aspera/cli/transfer_actions'
19
20
  require 'aspera/cli/gem_checker'
@@ -28,7 +29,7 @@ require 'aspera/persistency_action_once'
28
29
  require 'aspera/id_generator'
29
30
  require 'aspera/data_repository'
30
31
  require 'aspera/rest'
31
- require 'aspera/oauth/jwt'
32
+ require 'aspera/oauth/client/jwt'
32
33
  require 'aspera/log'
33
34
  require 'aspera/assert'
34
35
  require 'aspera/string_ext'
@@ -48,6 +49,7 @@ module Aspera
48
49
  include AscpActions
49
50
  include PresetActions
50
51
  include TransferActions
52
+ include CompletionActions
51
53
 
52
54
  class << self
53
55
  # Folder containing plugins in the gem's main folder
@@ -62,11 +64,6 @@ module Aspera
62
64
  File.expand_path(Module.nesting[2].to_s.gsub('::', '/').gsub(%r{[^/]+}, '..'), gem_plugins_folder)
63
65
  end
64
66
 
65
- # Deep clone hash so that it does not get modified in case of display and secret hide
66
- def deep_clone(val)
67
- return Marshal.load(Marshal.dump(val))
68
- end
69
-
70
67
  # @return [String] product family folder (~/.aspera)
71
68
  def module_family_folder
72
69
  user_home_folder = Dir.home
@@ -83,16 +80,13 @@ module Aspera
83
80
  end
84
81
 
85
82
  DEFAULT_CHECK_NEW_VERSION_DAYS = 7
86
- # Shells with a completion script in COMPLETION_FOLDER
87
- COMPLETION_SHELLS = %i[bash zsh fish].freeze
88
- COMPLETION_FOLDER = File.expand_path('../completion', __dir__)
89
- private_constant :DEFAULT_CHECK_NEW_VERSION_DAYS, :COMPLETION_SHELLS, :COMPLETION_FOLDER
83
+ private_constant :DEFAULT_CHECK_NEW_VERSION_DAYS
90
84
 
91
85
  option :preset, description: 'Load the named option preset from current config file', short: 'P', on_set: :load_preset
92
86
  option :version_check_days, description: 'Period in days to check new version (zero to disable)', allowed: Type::INTEGER, default: DEFAULT_CHECK_NEW_VERSION_DAYS
93
87
  option :plugin_folder, description: 'Folder where to find additional plugins', on_set: :add_plugin_folders
94
88
  option :sdk_url, description: 'Ascp: URL to get Aspera Transfer Executables', default: SpecialValues::DEF
95
- option :locations_url, description: 'Ascp: URL to get download locations of Aspera Transfer Daemon', default: Ascp::Installation.instance.transferd_urls, on_set: Ascp::Installation.instance.method(:transferd_urls=)
89
+ option :locations_url, description: 'Ascp: URLs to get download locations of Aspera Transfer Daemon', allowed: Type::STRING_ARRAY, default: Ascp::Installation.instance.transferd_urls, on_set: Ascp::Installation.instance.method(:transferd_urls=)
96
90
  option :sdk_folder, description: 'Ascp: Path to folder with ascp (or product with "product:")', on_set: Ascp::Installation.instance.method(:sdk_folder=)
97
91
  option :smtp, schema: Schema::Registry::SMTP_OPTIONS
98
92
  option :notify_to, description: 'Email: Recipient for notification of transfers'
@@ -244,7 +238,7 @@ module Aspera
244
238
  command :delete, description: 'Delete a secret by label (or id)',
245
239
  arguments: [{name: :label}, {name: :id, mandatory: false, default: nil}]
246
240
  command(
247
- :password, description: 'Change the vault password',
241
+ :password, description: 'Set a new password for the vault',
248
242
  arguments: [{name: :new_password}],
249
243
  action: lambda do |new_password:, **|
250
244
  Aspera.assert(vault_required.respond_to?(:change_password), 'Vault does not support password change')
@@ -335,8 +329,9 @@ module Aspera
335
329
  action: ->(version: nil, **) { install_transfer_sdk(version: version) }
336
330
  command(
337
331
  :spec, description: 'Show the transfer spec schema',
338
- action: lambda do |**|
339
- builder = Schema::Documentation.new(TerminalFormatter, Transfer::Spec::SCHEMA, include_option: true, agent_columns: true).build
332
+ arguments: [{name: :agent_name, allowed: Agent::Factory::ALL.keys + [Transfer::Spec::DOC_OVERVIEW], mandatory: false, default: nil}],
333
+ action: lambda do |agent_name: nil, **|
334
+ builder = Schema::Documentation.new(TerminalFormatter, Transfer::Spec::SCHEMA, **Transfer::Spec.doc_options(agent_name)).build
340
335
  Result::ObjectList.new(builder.rows, fields: builder.columns)
341
336
  end
342
337
  )
@@ -393,7 +388,7 @@ module Aspera
393
388
  command(
394
389
  :spec, description: 'Show the sync configuration schema',
395
390
  action: lambda do |**|
396
- builder = Schema::Documentation.new(TerminalFormatter, Sync::Operations::CONF_SCHEMA, include_option: true).build
391
+ builder = Schema::Documentation.new(TerminalFormatter, Sync::Operations::CONF_SCHEMA, annotate: CommandLineBuilder.method(:doc_lines)).build
397
392
  Result::ObjectList.new(builder.rows, fields: builder.columns)
398
393
  end
399
394
  )
@@ -429,9 +424,11 @@ module Aspera
429
424
 
430
425
  # completion sub-commands
431
426
  commands_under :completion do
432
- command :words, description: 'List words that can follow the given words (used by completion scripts)',
433
- arguments: [{name: :words, multiple: true, mandatory: false}]
434
- COMPLETION_SHELLS.each do |shell|
427
+ command :words, description: 'List words that can follow the given words',
428
+ arguments: [{name: :words, multiple: true, mandatory: false, raw: true}]
429
+ command :candidates, description: 'Complete the last word, with descriptions and a directive for the shell (used by completion scripts)',
430
+ arguments: [{name: :words, multiple: true, mandatory: false, raw: true}]
431
+ COMPLETION_SHELLS.each_key do |shell|
435
432
  command shell, description: "Generate #{shell} completion script, to evaluate in shell",
436
433
  action: ->(**) { completion_script(shell) }
437
434
  end
@@ -635,32 +632,6 @@ module Aspera
635
632
  Result::ObjectList.new(rows, fields: %w[option description allowed deprecated replacement])
636
633
  end
637
634
 
638
- def action_completion_words(words: nil, **)
639
- if words.nil? || words.empty?
640
- # Level 0: propose plugin names
641
- Plugins::Factory.instance.plugin_list.each { |p| puts p }
642
- else
643
- plugin_sym = words.first.to_sym
644
- plugin_class = begin
645
- Plugins::Factory.instance.plugin_class(plugin_sym)
646
- rescue StandardError
647
- Process.exit(0)
648
- end
649
- # Navigate into the registry using remaining words as path
650
- path = words[1..].map(&:to_sym)
651
- plugin_class.command_registry.children_of(path).each_key { |k| puts k }
652
- end
653
- Process.exit(0)
654
- end
655
-
656
- # Display the completion script for the given shell.
657
- # Raw output, not formatted, so that it can be evaluated by the shell.
658
- # @param shell [Symbol] One of `COMPLETION_SHELLS`
659
- def completion_script(shell)
660
- $stdout.write(File.read(File.join(COMPLETION_FOLDER, "#{Info::CMD_NAME}.#{shell}")))
661
- Process.exit(0)
662
- end
663
-
664
635
  # Folder in $HOME for application files (~/.aspera)
665
636
  ASPERA_HOME_FOLDER_NAME = '.aspera'
666
637
  # Default config file name
@@ -57,10 +57,12 @@ module Aspera
57
57
  # @param app_url [String] Tested URL
58
58
  # @return [Hash] :preset_value, :test_args
59
59
  def wizard(wizard, app_url)
60
+ username = options.get_option(:username, mandatory: true)
60
61
  return {
62
+ preset_ids: [username],
61
63
  preset_value: {
62
64
  url: app_url,
63
- username: options.get_option(:username, mandatory: true),
65
+ username: username,
64
66
  password: options.get_option(:password, mandatory: true)
65
67
  },
66
68
  test_args: 'transfer current list'
@@ -42,7 +42,7 @@ module Aspera
42
42
  next unless File.directory?(folder)
43
43
  # TODO: add gem root to load path ? and require short folder ?
44
44
  # $LOAD_PATH.push(folder) if i[:add_path]
45
- Dir.entries(folder).each do |source|
45
+ Dir.children(folder).each do |source|
46
46
  next unless source.end_with?(Environment::RB_EXT)
47
47
  path = File.join(folder, source)
48
48
  plugin_symbol = File.basename(path, Environment::RB_EXT).to_sym
@@ -69,7 +69,7 @@ module Aspera
69
69
  formatter.display_status('Ask the ascli client id and secret to your Administrator.'.red)
70
70
  formatter.display_status("Log in as an admin user at: #{app_url}")
71
71
  Environment.instance.open_uri(app_url)
72
- formatter.display_status('Navigate to: 𓃑 → Admin → Configurations → API clients')
72
+ wizard.display_navigation(Environment.char('𓃑'), 'Admin', 'Configurations', 'API clients')
73
73
  formatter.display_status('Create an API client with:')
74
74
  formatter.display_status('- name: ascli')
75
75
  formatter.display_status('- JWT: enabled')
@@ -82,9 +82,10 @@ module Aspera
82
82
  private_key_path = wizard.ask_private_key(
83
83
  user: wiz_username,
84
84
  url: app_url,
85
- page: '👤 → Account Settings → Preferences → Public Key in PEM'
85
+ page: [Environment.char('👤'), 'Account Settings', 'Preferences', 'Public Key in PEM']
86
86
  )
87
87
  return {
88
+ preset_ids: [wiz_username, client_id].compact,
88
89
  preset_value: {
89
90
  url: app_url,
90
91
  username: wiz_username,
@@ -321,8 +322,7 @@ module Aspera
321
322
  )
322
323
  # well, we asked a TS for connect, but we actually want a generic one
323
324
  transfer_spec.delete('authentication')
324
- Runner.result_transfer(transfer.start(transfer_spec))
325
- return Result::SingleObject.new(package)
325
+ return Runner.result_object_transfer(package, transfer.start(transfer_spec))
326
326
  else
327
327
  # send from remote shared folder
328
328
  if (m = Parser.percent_selector(shared_folder))
@@ -529,9 +529,9 @@ module Aspera
529
529
  command :user, description: 'Manage current user'
530
530
  command :shared_folders, description: 'Browse shared folders'
531
531
  command :gateway, description: 'Start Faspex 4 gateway emulation',
532
- arguments: [{name: :parameters, type: Hash, mandatory: false, default: {}}]
532
+ arguments: [{name: :parameters, type: Hash, mandatory: false, default: {}, schema: Schema::Registry::GATEWAY_OPTIONS}]
533
533
  command :postprocessing, description: 'Start Faspex 4 post-processing server',
534
- arguments: [{name: :parameters, type: Hash, mandatory: false, default: {}}]
534
+ arguments: [{name: :parameters, type: Hash, mandatory: false, default: {}, schema: Schema::Registry::POSTPROC_OPTIONS}]
535
535
  command :invitations, description: 'Manage invitations'
536
536
 
537
537
  commands_under :invitations do
@@ -978,24 +978,14 @@ module Aspera
978
978
 
979
979
  def action_gateway(parameters: {}, **)
980
980
  require 'aspera/faspex_gw'
981
- parameters = parameters.symbolize_keys
982
- uri = URI.parse(parameters.delete(:url) { WebServerSimple::DEFAULT_URL })
983
- server = WebServerSimple.new(uri, **parameters.slice(*WebServerSimple::PARAMS))
984
- Aspera.assert(parameters.except(*WebServerSimple::PARAMS).empty?) { "unexpected parameters: #{parameters.except(*WebServerSimple::PARAMS).keys}" }
985
- server.mount(uri.path, Faspex4GWServlet, api_v5, nil)
986
- server.start
981
+ Faspex4GWServlet.serve(parameters.symbolize_keys, type: Cli::BadArgument) { [api_v5, nil] }
987
982
  Result::Status.new('Gateway terminated')
988
983
  end
989
984
 
990
985
  def action_postprocessing(parameters: {}, **)
991
986
  require 'aspera/faspex_postproc' # cspell:disable-line
992
- parameters = parameters.symbolize_keys
993
- uri = URI.parse(parameters.delete(:url) { WebServerSimple::DEFAULT_URL })
994
- parameters[:root] = uri.path
995
- server = WebServerSimple.new(uri, **parameters.slice(*WebServerSimple::PARAMS))
996
- server.mount(uri.path, Faspex4PostProcServlet, parameters.except(*WebServerSimple::PARAMS))
997
- server.start
998
- Result::Status.new('Gateway terminated')
987
+ Faspex4PostProcServlet.serve(parameters.symbolize_keys, type: Cli::BadArgument)
988
+ Result::Status.new('Post-processing terminated')
999
989
  end
1000
990
  ACCOUNT_TYPES = %w{local_user saml_user self_registered_user external_user}.freeze
1001
991
  WORKGROUP_TYPES = %w{workgroup shared_inbox}.freeze
@@ -29,23 +29,48 @@ module Aspera
29
29
  # @param app_url [String] Tested URL
30
30
  # @return [Hash] :preset_value, :test_args
31
31
  def wizard(wizard, app_url)
32
+ auth_type = options.get_option(:auth, mandatory: wizard.required) || :basic
33
+ preset_value = {
34
+ url: app_url,
35
+ auth: auth_type.to_s
36
+ }
37
+ preset_ids = []
38
+ case auth_type
39
+ when :none
40
+ # No credentials needed
41
+ when :basic
42
+ username = options.get_option(:username, mandatory: true)
43
+ preset_value[:username] = username
44
+ preset_value[:password] = options.get_option(:password, mandatory: true)
45
+ preset_ids.push(username)
46
+ when :jwt
47
+ client_id = options.get_option(:client_id, mandatory: true)
48
+ preset_value[:client_id] = client_id
49
+ key_path = options.get_option(:key_path)
50
+ preset_value[:private_key] = key_path ? "@file:#{key_path}" : options.get_option(:private_key, mandatory: true)
51
+ passphrase = options.get_option(:passphrase)
52
+ preset_value[:passphrase] = passphrase unless passphrase.nil?
53
+ preset_ids.push(client_id)
54
+ end
32
55
  return {
33
- preset_value: {
34
- url: app_url
35
- },
56
+ preset_ids: preset_ids,
57
+ preset_value: preset_value,
36
58
  test_args: 'bridges list'
37
59
  }
38
60
  end
39
61
 
40
- command :health, description: 'Check faspio Gateway health'
62
+ command :health, description: 'Check faspio Gateway health'
63
+ command :metrics, description: 'Get Prometheus metrics'
41
64
 
42
65
  commands_under :bridges do
43
66
  crud_commands entity: 'bridges',
44
67
  api: :build_api,
68
+ operations: %i[create list show delete],
69
+ lookup: %w[name],
45
70
  body_component: Schema::Registry::FASPIO
46
71
  end
47
72
 
48
- option :auth, description: 'OAuth type of authentication', allowed: %i[jwt basic]
73
+ option :auth, description: 'Type of authentication', allowed: %i[basic jwt none]
49
74
  option :client_id, description: 'OAuth client identifier'
50
75
  option :private_key, description: 'OAuth JWT RSA private key PEM **value**'
51
76
  option :passphrase, description: 'OAuth JWT RSA private key passphrase'
@@ -55,6 +80,8 @@ module Aspera
55
80
  def build_api
56
81
  base_url = options.get_option(:url, mandatory: true)
57
82
  case options.get_option(:auth, mandatory: true)
83
+ when :none
84
+ Rest::Client.new(base_url: base_url)
58
85
  when :basic
59
86
  basic_auth_api
60
87
  when :jwt
@@ -94,6 +121,10 @@ module Aspera
94
121
  end
95
122
  Result::ObjectList.new(nagios.status_list)
96
123
  end
124
+
125
+ def action_metrics(**)
126
+ Result::Text.new(build_api.read('metrics', headers: {'Accept' => 'text/plain'}))
127
+ end
97
128
  end
98
129
  end
99
130
  end
@@ -29,6 +29,7 @@ module Aspera
29
29
  # @return [Hash] :preset_value, :test_args
30
30
  def wizard(wizard, app_url)
31
31
  return {
32
+ preset_ids: [],
32
33
  preset_value: {
33
34
  url: app_url
34
35
  },