aspera-cli 4.26.0 → 4.26.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +17 -3
  4. data/lib/aspera/api/aoc.rb +2 -1
  5. data/lib/aspera/api/node.rb +2 -2
  6. data/lib/aspera/ascp/installation.rb +7 -4
  7. data/lib/aspera/assert.rb +17 -13
  8. data/lib/aspera/cli/extended_value.rb +6 -2
  9. data/lib/aspera/cli/formatter.rb +65 -60
  10. data/lib/aspera/cli/main.rb +69 -10
  11. data/lib/aspera/cli/manager.rb +130 -76
  12. data/lib/aspera/cli/options.schema.yaml +82 -0
  13. data/lib/aspera/cli/plugins/aoc.rb +36 -11
  14. data/lib/aspera/cli/plugins/base.rb +46 -37
  15. data/lib/aspera/cli/plugins/config.rb +9 -9
  16. data/lib/aspera/cli/plugins/faspex.rb +1 -1
  17. data/lib/aspera/cli/plugins/faspex5.rb +4 -5
  18. data/lib/aspera/cli/plugins/node.rb +1 -1
  19. data/lib/aspera/cli/sync_actions.rb +1 -1
  20. data/lib/aspera/cli/transfer_agent.rb +17 -15
  21. data/lib/aspera/cli/version.rb +1 -1
  22. data/lib/aspera/command_line_builder.rb +22 -18
  23. data/lib/aspera/environment.rb +3 -3
  24. data/lib/aspera/formatter_interface.rb +14 -0
  25. data/lib/aspera/hash_ext.rb +6 -0
  26. data/lib/aspera/log.rb +4 -3
  27. data/lib/aspera/markdown.rb +4 -1
  28. data/lib/aspera/oauth/factory.rb +1 -1
  29. data/lib/aspera/proxy_auto_config.rb +3 -0
  30. data/lib/aspera/rest.rb +1 -1
  31. data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +62801 -0
  32. data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +8898 -0
  33. data/lib/aspera/schema/documentation.rb +107 -0
  34. data/lib/aspera/schema/reader.rb +75 -0
  35. data/lib/aspera/schema/registry.rb +63 -0
  36. data/lib/aspera/sync/conf.schema.yaml +0 -26
  37. data/lib/aspera/sync/operations.rb +9 -5
  38. data/lib/aspera/transfer/faux_file.rb +1 -1
  39. data/lib/aspera/transfer/resumer.rb +1 -1
  40. data/lib/aspera/transfer/spec.rb +3 -3
  41. data/lib/aspera/transfer/spec.schema.yaml +1 -1
  42. data/lib/aspera/uri_reader.rb +1 -1
  43. data/lib/aspera/yaml.rb +4 -2
  44. data.tar.gz.sig +0 -0
  45. metadata +9 -3
  46. metadata.gz.sig +0 -0
  47. data/lib/aspera/transfer/spec_doc.rb +0 -76
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aspera/agent/factory'
4
+ require 'aspera/markdown'
5
+
6
+ module Aspera
7
+ module Schema
8
+ # Generate documentation from Schema, for Transfer Spec, or async Conf spec
9
+ class Documentation
10
+ # @param formatter [Cli::Formatter] Formatter instance with methods: markdown_text, tick, check_row
11
+ # @param schema [Reader]
12
+ # @param include_option [Boolean] `true`: include CLI options (switches, env vars) in descriptions
13
+ # @param agent_columns [Boolean] `true`: add separate columns for each transfer agent compatibility
14
+ # @param code_highlight [Boolean] `true`: format name and type as code
15
+ def initialize(formatter, schema, include_option: false, agent_columns: false, code_highlight: false)
16
+ @formatter = formatter
17
+ @schema = schema
18
+ @include_option = include_option
19
+ @agent_columns = agent_columns
20
+ @code_highlight = code_highlight
21
+ @columns = %i[name type description]
22
+ @columns.insert(-2, *Agent::Factory::ALL.values.map{ |i| i[:short]}.sort) if @agent_columns
23
+ # @type [Array<Hash<Symbol,String>>]
24
+ @rows = []
25
+ end
26
+
27
+ def rows
28
+ @rows.sort_by{ |i| i[:name]}
29
+ end
30
+
31
+ # @return [Array<String>]
32
+ def columns
33
+ @columns.map(&:to_s)
34
+ end
35
+
36
+ # First row is the titles
37
+ # @return [Array<Array<String>>]
38
+ def table
39
+ [@columns.map(&:to_s)] + @rows.sort_by{ |i| i[:name]}.map{ |row| @columns.map{ |field| row[field]}}
40
+ end
41
+
42
+ # Generate a documentation table from a JSON schema for transfer specifications
43
+ #
44
+ # Recursively processes a JSON schema to create a formatted table for manual documentation.
45
+ # Handles nested objects, arrays, and extracts metadata (descriptions, types, enums, deprecations).
46
+ #
47
+ # @param schema [Reader] The JSON schema to process
48
+ # @return [Documentation]
49
+ def build(schema = nil)
50
+ code = @code_highlight ? ->(c){"`#{c}`"} : ->(c){c}
51
+ schema ||= @schema
52
+ schema.each_property do |property_schema, _name, property_full_name|
53
+ node = property_schema.current
54
+ # Manual table
55
+ item = {
56
+ name: code.call(property_full_name),
57
+ type: code.call(node['type']),
58
+ description: []
59
+ }
60
+ # Render Markdown formatting and split lines
61
+ item[:description] =
62
+ node['description']
63
+ .gsub(Markdown::FORMATS){@formatter.markdown_text(Regexp.last_match)}
64
+ .split("\n") if node.key?('description')
65
+ item[:description].unshift("DEPRECATED: #{node['x-deprecation']}") if node.key?('x-deprecation')
66
+ # Add flags for supported agents in doc
67
+ agents = []
68
+ Agent::Factory::ALL.each_key do |sym|
69
+ agents.push(sym) if node['x-agents'].nil? || node['x-agents'].include?(sym.to_s)
70
+ end
71
+ Aspera.assert(agents.include?(:direct)){"#{name}: x-cli-option requires agent direct (or nil)"} if node['x-cli-option']
72
+ if @agent_columns
73
+ Agent::Factory::ALL.each do |sym, names|
74
+ item[names[:short]] = @formatter.tick(agents.include?(sym))
75
+ end
76
+ else
77
+ item[:description].push("(#{agents.map{ |i| Agent::Factory::ALL[i][:short].to_s.upcase}.sort.join(', ')})") unless agents.length.eql?(Agent::Factory::ALL.length)
78
+ end
79
+ # Only keep lines that are usable in supported agents
80
+ next false if agents.empty?
81
+ item[:description].push("Allowed values: #{node['enum'].map{ |v| @formatter.markdown_text("`#{v}`")}.join(', ')}.") if node.key?('enum')
82
+ item[:description].push("Default: #{code.call(node['default'])}.") if node.key?('default')
83
+ if @include_option
84
+ envvar_prefix = ''
85
+ cli_option =
86
+ if node.key?('x-cli-envvar')
87
+ envvar_prefix = 'env:'
88
+ node['x-cli-envvar']
89
+ elsif node['x-cli-switch']
90
+ node['x-cli-option']
91
+ elsif node['x-cli-option']
92
+ arg_type = node.key?('enum') ? '{enum}' : "{#{[node['type']].flatten.join('|')}}"
93
+ # conversion_tag = node['x-cli-convert']
94
+ conversion_tag = node.key?('x-cli-convert') ? 'conversion' : nil
95
+ sep = node['x-cli-option'].start_with?('--') ? '=' : ' '
96
+ "#{node['x-cli-option']}#{sep}#{"(#{conversion_tag})" if conversion_tag}#{arg_type}"
97
+ end
98
+ short = node.key?('x-cli-short') ? "(#{node['x-cli-short']})" : nil
99
+ item[:description].push("(#{'special:' if node['x-cli-special']}#{envvar_prefix}#{@formatter.markdown_text("`#{cli_option}`")})#{short}") if cli_option
100
+ end
101
+ @rows.push(@formatter.check_row(item))
102
+ end
103
+ self
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aspera
4
+ # base class for plugins modules
5
+ module Schema
6
+ # JSON schema reader
7
+ class Reader
8
+ attr_reader :current
9
+
10
+ # Shortcut to access current value at path
11
+ # @param x [String] path element
12
+ # @return [Hash, Array, String, Integer] current value at path
13
+ def [](x)
14
+ @current[x]
15
+ end
16
+
17
+ # Find sub path relative to current
18
+ # Honors $ref
19
+ def dig(*path)
20
+ current = @current
21
+ path.each do |p|
22
+ Aspera.assert(current.key?(p)){"schema: #{p} in #{path}"}
23
+ current = current[p]
24
+ Aspera.assert_type(current, Hash){'schema'}
25
+ if current.key?('$ref')
26
+ ref = current['$ref']
27
+ Aspera.assert(ref.start_with?('#/'))
28
+ current = @root.dig(*ref[2..].split('/'))
29
+ end
30
+ end
31
+ Reader.new(@root, current)
32
+ end
33
+
34
+ # Read schema from file or from cache
35
+ # @param root [Hash] root schema
36
+ # @param current [Hash, nil] current position in
37
+ # @return [Hash, nil] schema
38
+ def initialize(root, current = nil)
39
+ @root = root
40
+ @current = current || root
41
+ end
42
+
43
+ # Recursively traverse schema properties with a block
44
+ # Handles nested objects and arrays automatically
45
+ # @param prefix [String] Prefix for property names (e.g., 'parent.child.')
46
+ # @yield [property_schema, name, full_name] Yields property info to block
47
+ # @yieldparam property_schema [Reader] Schema reader for this property (use .current to get node hash)
48
+ # @yieldparam name [String] Property name
49
+ # @yieldparam full_name [String] Full property name with prefix
50
+ # @return [nil]
51
+ def each_property(prefix = '', &block)
52
+ properties = dig('properties')
53
+ properties.current.each_key do |name|
54
+ property_full_name = "#{prefix}#{name}"
55
+ property_schema = properties.dig(name)
56
+ node = property_schema.current
57
+
58
+ # Yield current property to block
59
+ yield(property_schema, name, property_full_name)
60
+
61
+ # Recursively process nested structures
62
+ case node['type']
63
+ when 'object'
64
+ property_schema.each_property("#{property_full_name}.", &block) if node['properties']
65
+ when 'array'
66
+ if node['items']
67
+ array_item_schema = property_schema.dig('items')
68
+ array_item_schema.each_property("#{property_full_name}[].", &block) if array_item_schema.current['properties']
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require 'aspera/yaml'
5
+ require 'aspera/schema/reader'
6
+
7
+ module Aspera
8
+ # base class for plugins modules
9
+ module Schema
10
+ class Registry
11
+ include Singleton
12
+
13
+ class << self
14
+ def known?(sym)
15
+ LOCATIONS.key?(sym)
16
+ end
17
+
18
+ def req_body(component, endpoint)
19
+ "#{component}:paths./#{endpoint}.requestBody.content.application/json.schema"
20
+ end
21
+ end
22
+
23
+ LOCATIONS = {
24
+ spec: 'aspera/transfer/spec.schema.yaml',
25
+ args: 'aspera/sync/args.schema.yaml',
26
+ conf: 'aspera/sync/conf.schema.yaml',
27
+ opts: 'aspera/cli/options.schema.yaml',
28
+ aoc: 'aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml',
29
+ faspex: 'aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml'
30
+ }
31
+
32
+ OPTIONS = 'opts'
33
+ TRANSFER_SPEC = 'spec'
34
+ SYNC_CONF = 'conf'
35
+ SYNC_ARGS = 'args'
36
+ AOC = 'aoc'
37
+ FASPEX = 'faspex'
38
+ TRANSFER_INFO = "#{OPTIONS}:components.schemas.TransferInfo"
39
+
40
+ REQ_BODY = '.requestBody.content.application/json.schema'
41
+
42
+ def initialize
43
+ @cache = {}
44
+ @main_folder = File.expand_path('../..', __dir__)
45
+ end
46
+
47
+ # Read schema from file or from cache
48
+ # @param name_path [String] one of the keys in LOCATIONS, with optional :<path> suffix
49
+ # @return [Reader] schema
50
+ def reader(name_path)
51
+ name, path = name_path.split(':', 2)
52
+ sym = name.to_sym
53
+ Aspera.assert(Registry.known?(sym)){"schema: #{sym}"}
54
+ spec_file = File.join(@main_folder, LOCATIONS[sym])
55
+ @cache[sym] = Yaml.safe_load(File.read(spec_file)) if spec_file.end_with?('.yaml') && !@cache.key?(sym)
56
+ @cache[sym] = JSON.parse(File.read(spec_file)) if spec_file.end_with?('.json') && !@cache.key?(sym)
57
+ reader = Reader.new(@cache[sym])
58
+ return reader unless path
59
+ reader.dig(*path.split('.'))
60
+ end
61
+ end
62
+ end
63
+ end
@@ -16,7 +16,6 @@ properties:
16
16
  ascp_dir:
17
17
  description: Directory containing ascp executable to use.
18
18
  type: string
19
- default: ""
20
19
  assume_no_mods:
21
20
  description: Assume that the directory structure has not been modified.
22
21
  type: boolean
@@ -52,7 +51,6 @@ properties:
52
51
  cookie:
53
52
  description: User-defined identification string.
54
53
  type: string
55
- default: ""
56
54
  x-cli-option: true
57
55
  cooloff_max_seconds:
58
56
  description:
@@ -153,7 +151,6 @@ properties:
153
151
  absolute:
154
152
  description: UTC timestamp. Empty value for disabled.
155
153
  type: string
156
- default: ""
157
154
  relative_seconds:
158
155
  description: Relative to async start time. `-1` for disabled.
159
156
  type: integer
@@ -185,7 +182,6 @@ properties:
185
182
  pass:
186
183
  description: Authenticate the local async with the specified password.
187
184
  type: string
188
- default: ""
189
185
  path:
190
186
  description: The directory to be synchronized on the local host.
191
187
  type: string
@@ -201,7 +197,6 @@ properties:
201
197
  description: Use the specified database directory on the local host.
202
198
  Default is `.private-asp` at the root level of the synchronized directory.
203
199
  type: string
204
- default: ""
205
200
  x-cli-option: true
206
201
  x-cli-short: -b
207
202
  local_checksum_threads:
@@ -215,7 +210,6 @@ properties:
215
210
  Store/Restore the database to/from the specified directory on the local host.
216
211
  The value can be an absolute path, an URI or - (use the local sync dir)
217
212
  type: string
218
- default: ""
219
213
  x-cli-option: true
220
214
  local_force_stat:
221
215
  description: Forces the local async to retrieve file information even when no changes are detected by the scanner or monitor.
@@ -233,12 +227,10 @@ properties:
233
227
  local_keep_dir:
234
228
  description: Move deleted files into the specified directory on the local host.
235
229
  type: string
236
- default: ""
237
230
  x-cli-option: --keep-dir-local
238
231
  local_mount_signature:
239
232
  description: Verify that the file system is mounted by the existence of this file on the local host.
240
233
  type: string
241
- default: ""
242
234
  x-cli-option: true
243
235
  local_move_cache_timeout_seconds:
244
236
  description:
@@ -299,13 +291,11 @@ properties:
299
291
  local_dir:
300
292
  description: Use the specified logging directory on the local host.
301
293
  type: string
302
- default: ""
303
294
  x-cli-option: --alt-logdir
304
295
  x-cli-short: -L
305
296
  remote_dir:
306
297
  description: Use the specified logging directory on the remote host.
307
298
  type: string
308
- default: ""
309
299
  x-cli-option: --remote-logdir
310
300
  x-cli-short: -R
311
301
  manifest_path:
@@ -313,7 +303,6 @@ properties:
313
303
  A directory path where ascp will create manifest TEXT files (passed
314
304
  to ascp as --file-manifest-path)
315
305
  type: string
316
- default: ""
317
306
  mirror:
318
307
  description:
319
308
  Force the pulling side to be exactly like the pushing side, removing
@@ -352,7 +341,6 @@ properties:
352
341
  description: Suppress log messages for ITEM.
353
342
  The only currently supported ITEM is 'stats', which suppresses both STATS and PROG log messages.
354
343
  type: string
355
- default: ""
356
344
  x-cli-option: true
357
345
  no_preserve_root_attrs:
358
346
  description: Disable the preservation of attributes on the Sync root.
@@ -455,7 +443,6 @@ properties:
455
443
  fingerprint:
456
444
  description: Check it against server SSH host key fingerprint.
457
445
  type: string
458
- default: ""
459
446
  x-ts-name: sshfp
460
447
  host:
461
448
  description: Use the specified host name or address of the remote host.
@@ -465,7 +452,6 @@ properties:
465
452
  pass:
466
453
  description: Authenticate the transfer with the specified password.
467
454
  type: string
468
- default: ""
469
455
  x-ts-name: remote_password
470
456
  x-cli-option: true
471
457
  x-cli-short: -w
@@ -516,11 +502,9 @@ properties:
516
502
  host:
517
503
  description: Use the specified host name or address of the proxy.
518
504
  type: string
519
- default: ""
520
505
  pass:
521
506
  description: Authenticate to the proxy with the specified password.
522
507
  type: string
523
- default: ""
524
508
  port:
525
509
  description: Use the specified port, default is 9091 for dnat, 9092.
526
510
  for dnats
@@ -536,18 +520,15 @@ properties:
536
520
  user:
537
521
  description: Authenticate to the proxy with the specified username.
538
522
  type: string
539
- default: ""
540
523
  token:
541
524
  description: Token string passed to server's authentication service.
542
525
  type: string
543
- default: ""
544
526
  x-ts-name: token
545
527
  x-cli-short: -W
546
528
  token_node_user:
547
529
  description: Node API user identity associated with the token.
548
530
  Required for node user bearer tokens
549
531
  type: string
550
- default: ""
551
532
  user:
552
533
  description: Authenticate the transfer with the specified username.
553
534
  type: string
@@ -563,7 +544,6 @@ properties:
563
544
  description: Use the specified database directory on the remote host.
564
545
  Default is `.private-asp` at the root level of the synchronized directory.
565
546
  type: string
566
- default: ""
567
547
  x-cli-option: true
568
548
  x-cli-short: -B
569
549
  remote_db_store_dir:
@@ -571,7 +551,6 @@ properties:
571
551
  Store/Restore the database to/from the specified directory on the remote host.
572
552
  The value can be an absolute path, an URI or - (use the remote sync dir).
573
553
  type: string
574
- default: ""
575
554
  x-cli-option: true
576
555
  remote_force_stat:
577
556
  description: Forces the remote async to retrieve file information even when no changes are detected by the scanner or monitor.
@@ -589,12 +568,10 @@ properties:
589
568
  remote_keep_dir:
590
569
  description: Move deleted files into the specified directory on the remote host.
591
570
  type: string
592
- default: ""
593
571
  x-cli-option: --keep-dir-remote
594
572
  remote_mount_signature:
595
573
  description: Verify that the file system is mounted by the existence of this file on the remote host.
596
574
  type: string
597
- default: ""
598
575
  x-cli-option: true
599
576
  remote_move_cache_timeout_seconds:
600
577
  description:
@@ -868,7 +845,6 @@ properties:
868
845
  host:
869
846
  description: Use the specified host name or address to connect to the datastore.
870
847
  type: string
871
- default: ""
872
848
  port:
873
849
  description: Use the specified port.
874
850
  type: integer
@@ -876,10 +852,8 @@ properties:
876
852
  write_gid:
877
853
  description: Try to write files as the specified group.
878
854
  type: string
879
- default: ""
880
855
  x-cli-option: true
881
856
  write_uid:
882
857
  description: Try to write files as the specified user.
883
858
  type: string
884
- default: ""
885
859
  x-cli-option: true
@@ -9,6 +9,7 @@ require 'aspera/command_line_builder'
9
9
  require 'aspera/log'
10
10
  require 'aspera/assert'
11
11
  require 'aspera/environment'
12
+ require 'aspera/schema/registry'
12
13
  require 'json'
13
14
  require 'base64'
14
15
  require 'open3'
@@ -81,7 +82,10 @@ module Aspera
81
82
  # Start the sync process
82
83
  # @param sync_info [Hash] Sync parameters, old or new format
83
84
  # @param opt_ts [Hash] Optional transfer spec
84
- # @param &block [nil, Proc] block to generate transfer spec, takes: `direction` (one of DIRECTIONS), `local_dir`, `remote_dir`
85
+ # @yieldparam direction [Symbol] Sync direction (one of DIRECTIONS: :push, :pull, :bidi)
86
+ # @yieldparam local_dir [String] Local directory path
87
+ # @yieldparam remote_dir [String] Remote directory path
88
+ # @yieldreturn [Hash] Transfer spec to use for authentication
85
89
  def start(sync_info, opt_ts = nil)
86
90
  Log.dump(:sync_params_initial, sync_info)
87
91
  Aspera.assert_type(sync_info, Hash)
@@ -336,10 +340,10 @@ module Aspera
336
340
  end
337
341
  # Private stuff:
338
342
  # Read JSON schema and mapping to command line options
339
- ARGS_INSTANCE_SCHEMA = CommandLineBuilder.read_schema(__dir__, 'args')
340
- ARGS_SESSION_SCHEMA = ARGS_INSTANCE_SCHEMA['properties']['sessions']['items']
341
- ARGS_INSTANCE_SCHEMA['properties'].delete('sessions')
342
- CONF_SCHEMA = CommandLineBuilder.read_schema(__dir__, 'conf')
343
+ ARGS_INSTANCE_SCHEMA = CommandLineBuilder.read_schema(Schema::Registry::SYNC_ARGS)
344
+ ARGS_SESSION_SCHEMA = ARGS_INSTANCE_SCHEMA.dig(*%w[properties sessions items])
345
+ ARGS_INSTANCE_SCHEMA.dig('properties').current.delete('sessions')
346
+ CONF_SCHEMA = CommandLineBuilder.read_schema(Schema::Registry::SYNC_CONF)
343
347
  CMDLINE_PARAMS_KEYS = %w[instance sessions].freeze
344
348
  ASYNC_ADMIN_EXECUTABLE = 'asyncadmin'
345
349
  PRIVATE_FOLDER = "#{Environment.instance.os.eql?(Environment::OS_WINDOWS) ? '~' : '.'}private-asp"
@@ -14,7 +14,7 @@ module Aspera
14
14
  # @return nil if not a faux: scheme, else a FauxFile instance
15
15
  def create(name)
16
16
  return unless name.start_with?(PREFIX)
17
- name_params = name[PREFIX.length..-1].split('?', 2)
17
+ name_params = name.delete_prefix(PREFIX).split('?', 2)
18
18
  raise Error, 'Format: #{PREFIX}<file path>?<size>' unless name_params.length.eql?(2)
19
19
  raise Error, "Format: <integer>[#{SIZE_UNITS.join(',')}]" unless (m = name_params[1].downcase.match(/^(\d+)([#{SIZE_UNITS.join('')}])$/))
20
20
  size = m[1].to_i
@@ -32,7 +32,7 @@ module Aspera
32
32
  # Calls block a number of times (resumes) until success or limit reached
33
33
  # This is re-entrant, one resumer can handle multiple transfers in //
34
34
  #
35
- # @param block [Proc]
35
+ # @yieldreturn [void] Executes the transfer operation
36
36
  def execute_with_resume
37
37
  Aspera.assert(block_given?)
38
38
  # maximum of retry
@@ -50,9 +50,9 @@ module Aspera
50
50
  transfer_spec['resume_policy'] = POLICY_FIX[transfer_spec['resume_policy']] if transfer_spec.key?('resume_policy')
51
51
  end
52
52
  end
53
- SCHEMA = CommandLineBuilder.read_schema(__dir__, 'spec', ascp: true)
54
- # define constants for enums of parameters: <parameter>_<enum>, e.g. CIPHER_AES_128, DIRECTION_SEND, ...
55
- SCHEMA['properties'].each do |name, description|
53
+ SCHEMA = CommandLineBuilder.read_schema(Schema::Registry::TRANSFER_SPEC, ascp: true)
54
+ # Define constants for enums of parameters: <parameter>_<enum>, e.g. CIPHER_AES_128, DIRECTION_SEND, ...
55
+ SCHEMA.current['properties'].each do |name, description|
56
56
  next unless description['enum'].is_a?(Array)
57
57
  const_set(:"#{name.to_s.upcase}_ENUM_VALUES", description['enum'])
58
58
  description['enum'].each do |enum|
@@ -8,7 +8,7 @@ $comment: >-
8
8
  x-cli-envvar [String] Name of env var
9
9
  x-cli-option [String] Command line option (starts with "-"), or `true`: same as ts, or `false`: not an option
10
10
  x-cli-switch [Boolean] `true` if option has no arg, else by default option has a value
11
- x-cli-special [Boolean] `true` if not anaged by command line generator (special handling: option or argument)
11
+ x-cli-special [Boolean] `true` if not managed by command line generator (special handling: option or argument)
12
12
  x-cli-convert [String,Hash] Method name for Convert object or Conversion Hash for enum: ts to arg
13
13
  x-agents [Array] Supported agents (for doc only), if not specified: all
14
14
  x-deprecation [String] Deprecation message for doc
@@ -27,7 +27,7 @@ module Aspera
27
27
  # @return [String] the path of a file:// URL
28
28
  def file_path(url)
29
29
  Aspera.assert(file?(url)){"use format: #{file_url('<path>')}"}
30
- File.expand_path(url[SCHEME_FILE_PFX2.length..-1])
30
+ File.expand_path(url.delete_prefix(SCHEME_FILE_PFX2))
31
31
  end
32
32
 
33
33
  # Read some content from some URI, support file: , http: and https: schemes
data/lib/aspera/yaml.rb CHANGED
@@ -34,14 +34,16 @@ module Aspera
34
34
  duplicate_keys
35
35
  end
36
36
 
37
- # Safely load YAML content, raising an error if duplicate keys are found
37
+ # Safely load YAML content
38
+ # raising an error if duplicate keys are found
39
+ # Validates the yaml, and then call `YAML.safe_load`
38
40
  # @param yaml [String] YAML content
39
41
  # @return [Object] Parsed YAML content
40
42
  # @raise [RuntimeError] If duplicate keys are found
41
43
  def safe_load(yaml)
42
44
  duplicate_keys = find_duplicate_keys(Psych.parse_stream(yaml))
43
45
  raise "Duplicate keys: #{duplicate_keys.join('; ')}" unless duplicate_keys.empty?
44
- YAML.safe_load(yaml)
46
+ YAML.safe_load(yaml, permitted_classes: [Time, Date, Symbol])
45
47
  end
46
48
 
47
49
  module_function :find_duplicate_keys, :safe_load
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aspera-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.26.0
4
+ version: 4.26.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Laurent Martin
@@ -345,6 +345,7 @@ files:
345
345
  - lib/aspera/cli/info.rb
346
346
  - lib/aspera/cli/main.rb
347
347
  - lib/aspera/cli/manager.rb
348
+ - lib/aspera/cli/options.schema.yaml
348
349
  - lib/aspera/cli/plugins/alee.rb
349
350
  - lib/aspera/cli/plugins/aoc.rb
350
351
  - lib/aspera/cli/plugins/ats.rb
@@ -385,6 +386,7 @@ files:
385
386
  - lib/aspera/environment.rb
386
387
  - lib/aspera/faspex_gw.rb
387
388
  - lib/aspera/faspex_postproc.rb
389
+ - lib/aspera/formatter_interface.rb
388
390
  - lib/aspera/hash_ext.rb
389
391
  - lib/aspera/id_generator.rb
390
392
  - lib/aspera/json_rpc.rb
@@ -425,6 +427,11 @@ files:
425
427
  - lib/aspera/rest_error_analyzer.rb
426
428
  - lib/aspera/rest_errors_aspera.rb
427
429
  - lib/aspera/rest_list.rb
430
+ - lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml
431
+ - lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml
432
+ - lib/aspera/schema/documentation.rb
433
+ - lib/aspera/schema/reader.rb
434
+ - lib/aspera/schema/registry.rb
428
435
  - lib/aspera/secret_hider.rb
429
436
  - lib/aspera/ssh.rb
430
437
  - lib/aspera/ssl.rb
@@ -440,7 +447,6 @@ files:
440
447
  - lib/aspera/transfer/resumer.rb
441
448
  - lib/aspera/transfer/spec.rb
442
449
  - lib/aspera/transfer/spec.schema.yaml
443
- - lib/aspera/transfer/spec_doc.rb
444
450
  - lib/aspera/transfer/uri.rb
445
451
  - lib/aspera/uri_reader.rb
446
452
  - lib/aspera/web_auth.rb
@@ -473,7 +479,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
473
479
  version: '0'
474
480
  requirements:
475
481
  - Read the manual for any requirement
476
- rubygems_version: 4.0.6
482
+ rubygems_version: 4.0.10
477
483
  specification_version: 4
478
484
  summary: 'Execute actions using command line on IBM Aspera Server products: Aspera
479
485
  on Cloud, Faspex, Shares, Node, Console, Orchestrator, High Speed Transfer Server'
metadata.gz.sig CHANGED
Binary file
@@ -1,76 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'aspera/agent/factory'
4
- require 'aspera/markdown'
5
-
6
- module Aspera
7
- module Transfer
8
- # Generate documentation from Schema, for Transfer Spec, or async Conf spec
9
- class SpecDoc
10
- class << self
11
- # @param formatter [Cli::Formatter] Formatter to use, methods: markdown_text, tick, check_row
12
- # @param include_option [Boolean] `true` : include CLI options
13
- # @param agent_columns [Boolean] `true` : include agents columns
14
- # @param schema [Hash] The JSON spec
15
- # @return [Array] a table suitable to display in manual
16
- def man_table(formatter, include_option: false, agent_columns: true, schema: Spec::SCHEMA)
17
- cols = %i[name type description]
18
- cols.insert(-2, *Agent::Factory::ALL.values.map{ |i| i[:short]}.sort) if agent_columns
19
- rows = []
20
- schema['properties'].each do |name, info|
21
- rows.concat(man_table(formatter, include_option: include_option, agent_columns: agent_columns, schema: info).last.map{ |h| h.merge(name: "#{name}.#{h[:name]}")}) if info['type'].eql?('object') && info['properties']
22
- rows.concat(man_table(formatter, include_option: include_option, agent_columns: agent_columns, schema: info['items']).last.map{ |h| h.merge(name: "#{name}[].#{h[:name]}")}) if info['type'].eql?('array') && info['items'] && info['items']['properties']
23
- # Manual table
24
- columns = {
25
- name: name,
26
- type: info['type'],
27
- description: []
28
- }
29
- # Render Markdown formatting and split lines
30
- columns[:description] =
31
- info['description']
32
- .gsub(Markdown::FORMATS){formatter.markdown_text(Regexp.last_match)}
33
- .split("\n") if info.key?('description')
34
- columns[:description].unshift("DEPRECATED: #{info['x-deprecation']}") if info.key?('x-deprecation')
35
- # Add flags for supported agents in doc
36
- agents = []
37
- Agent::Factory::ALL.each_key do |sym|
38
- agents.push(sym) if info['x-agents'].nil? || info['x-agents'].include?(sym.to_s)
39
- end
40
- Aspera.assert(agents.include?(:direct)){"#{name}: x-cli-option requires agent direct (or nil)"} if info['x-cli-option']
41
- if agent_columns
42
- Agent::Factory::ALL.each do |sym, names|
43
- columns[names[:short]] = formatter.tick(agents.include?(sym))
44
- end
45
- else
46
- columns[:description].push("(#{agents.map{ |i| Agent::Factory::ALL[i][:short].to_s.upcase}.sort.join(', ')})") unless agents.length.eql?(Agent::Factory::ALL.length)
47
- end
48
- # Only keep lines that are usable in supported agents
49
- next false if agents.empty?
50
- columns[:description].push("Allowed values: #{info['enum'].map{ |v| formatter.markdown_text("`#{v}`")}.join(', ')}") if info.key?('enum')
51
- if include_option
52
- envvar_prefix = ''
53
- cli_option =
54
- if info.key?('x-cli-envvar')
55
- envvar_prefix = 'env:'
56
- info['x-cli-envvar']
57
- elsif info['x-cli-switch']
58
- info['x-cli-option']
59
- elsif info['x-cli-option']
60
- arg_type = info.key?('enum') ? '{enum}' : "{#{[info['type']].flatten.join('|')}}"
61
- # conversion_tag = info['x-cli-convert']
62
- conversion_tag = info.key?('x-cli-convert') ? 'conversion' : nil
63
- sep = info['x-cli-option'].start_with?('--') ? '=' : ' '
64
- "#{info['x-cli-option']}#{sep}#{"(#{conversion_tag})" if conversion_tag}#{arg_type}"
65
- end
66
- short = info.key?('x-cli-short') ? "(#{info['x-cli-short']})" : nil
67
- columns[:description].push("(#{'special:' if info['x-cli-special']}#{envvar_prefix}#{formatter.markdown_text("`#{cli_option}`")})#{short}") if cli_option
68
- end
69
- rows.push(formatter.check_row(columns))
70
- end
71
- [cols, rows.sort_by{ |i| i[:name]}]
72
- end
73
- end
74
- end
75
- end
76
- end