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
@@ -0,0 +1,361 @@
1
+ # Schema for the SQLite tables inside the Async snap database (snap.db).
2
+ # Sources:
3
+ # include/async/session/Sqlite3_SSDB.h (SYNC_SQLITE3_META_SCHEMA, SYNC_SQLITE3_COUNTERS_SCHEMA, SYNC_SQLITE3_SCHEMA_1_0)
4
+ # include/async/common/Sync_types.h (SSSTATE values)
5
+ # Verified against a live snap.db using: ascli config sync admin overview <path>
6
+ $schema: https://json-schema.org/draft/2020-12/schema
7
+ $id: https://github.com/IBM/aspera-cli/tree/main/lib/aspera/schema/async_tables.yaml
8
+ title: Async snap database tables
9
+ description: Schemas for the SQLite tables stored inside the Async snap database (`snap.db`).
10
+ type: object
11
+ properties:
12
+ meta:
13
+ title: sync_snapmeta_table
14
+ description: One row per session, written at start and updated at stop.
15
+ type: object
16
+ properties:
17
+ dbversion:
18
+ description: Database schema version.
19
+ type: string
20
+ pairname:
21
+ description: Sync session name.
22
+ type: string
23
+ ldir:
24
+ description: Local directory path.
25
+ type: string
26
+ rhost:
27
+ description: Remote host.
28
+ type: string
29
+ rdir:
30
+ description: Remote directory path.
31
+ type: string
32
+ direction:
33
+ description: Sync direction.
34
+ type: string
35
+ enum:
36
+ - push
37
+ - pull
38
+ - bidi
39
+ inode_format:
40
+ description: Inode format used by the session.
41
+ type: string
42
+ checksum:
43
+ description: Checksum algorithm.
44
+ type: string
45
+ enum:
46
+ - sha1
47
+ - md5
48
+ - sha1_sparse
49
+ - md5_sparse
50
+ - none
51
+ start_time:
52
+ description: Session start timestamp.
53
+ type: string
54
+ format: date-time
55
+ stop_time:
56
+ description: Session stop timestamp (written when session ends).
57
+ type: string
58
+ format: date-time
59
+ condition:
60
+ description: Last known session condition.
61
+ type: string
62
+ enum:
63
+ - running
64
+ - stopped
65
+ scn_true_val:
66
+ description: Scan counter true value (internal).
67
+ type: integer
68
+ sync_point:
69
+ description: Synchronization point sequence number.
70
+ type: integer
71
+ max_recid:
72
+ description: Highest record ID allocated in the snapshot table.
73
+ type: integer
74
+ sync_uuid:
75
+ description: UUID identifying this sync pair.
76
+ type: string
77
+ cip_true_val:
78
+ description: Candidate-in-progress counter true value (internal).
79
+ type: integer
80
+ exclude_dirs_older_than:
81
+ description: Exclude directories with modification time older than this epoch value (0 = disabled).
82
+ type: integer
83
+ path_synced:
84
+ description: Number of paths successfully synchronized.
85
+ type: integer
86
+ byte_synced:
87
+ description: Number of bytes successfully synchronized.
88
+ type: integer
89
+ filters:
90
+ description: Active include/exclude filter patterns.
91
+ type: string
92
+ path_peerxfer:
93
+ description: Number of paths transferred by the peer.
94
+ type: integer
95
+ byte_peerxfer:
96
+ description: Number of bytes transferred by the peer.
97
+ type: integer
98
+ path_xferres:
99
+ description: Number of paths for which transfer was resumed.
100
+ type: integer
101
+ byte_xferres:
102
+ description: Number of bytes for which transfer was resumed.
103
+ type: integer
104
+ file_synced:
105
+ description: Number of files successfully synchronized.
106
+ type: integer
107
+ file_peerxfer:
108
+ description: Number of files transferred by the peer.
109
+ type: integer
110
+ file_xferres:
111
+ description: Number of files for which transfer was resumed.
112
+ type: integer
113
+ ascp_sids:
114
+ description: Last known FASP (ascp) session IDs.
115
+ type: string
116
+ counters:
117
+ title: sync_snap_counters_table
118
+ description: One row, updated live during the session with path and file counts per state.
119
+ type: object
120
+ properties:
121
+ syncd:
122
+ description: Number of paths in the `Syncd` state.
123
+ type: integer
124
+ conflict:
125
+ description: Number of paths in the `Conflict` state.
126
+ type: integer
127
+ potential_conflict:
128
+ description: Number of paths in the `Potential conflict` state.
129
+ type: integer
130
+ error:
131
+ description: Number of paths in the `Error` state.
132
+ type: integer
133
+ pending:
134
+ description: Number of paths still pending synchronization.
135
+ type: integer
136
+ unused:
137
+ description: Reserved (unused counter slot).
138
+ type: integer
139
+ files_syncd:
140
+ description: Number of files in the `Syncd` state.
141
+ type: integer
142
+ files_conflict:
143
+ description: Number of files in the `Conflict` state.
144
+ type: integer
145
+ files_potential_conflict:
146
+ description: Number of files in the `Potential conflict` state.
147
+ type: integer
148
+ files_error:
149
+ description: Number of files in the `Error` state.
150
+ type: integer
151
+ files_pending:
152
+ description: Number of files still pending synchronization.
153
+ type: integer
154
+ file_info:
155
+ title: sync_snapdb_table
156
+ description: One row per tracked file or directory. Primary source for the `file_info` command.
157
+ type: object
158
+ properties:
159
+ record_id:
160
+ description: Primary key (auto-increment).
161
+ type: integer
162
+ nodeid:
163
+ description: Local filesystem node ID.
164
+ type: integer
165
+ peer_rid:
166
+ description: Peer record ID for this entry.
167
+ type: integer
168
+ state:
169
+ description: |
170
+ Sync state of the entry.
171
+
172
+ | Value | Name |
173
+ |-------|-------------------|
174
+ | 0 | Nil |
175
+ | 1–18 | Pending (variant) |
176
+ | 19 | Syncd |
177
+ | 20 | Error |
178
+ | 21 | Conflict |
179
+ | 22 | PotentialConflict |
180
+ | 23–24 | Pending (variant) |
181
+ type: integer
182
+ version:
183
+ description: Schema version field (not used).
184
+ type: integer
185
+ c_meta_type:
186
+ description: Current metadata — file type (file, directory, …).
187
+ type: integer
188
+ c_meta_parentrid:
189
+ description: Current metadata — parent record ID.
190
+ type: integer
191
+ c_meta_path:
192
+ description: Current metadata — file path as known to the snapshot.
193
+ type: string
194
+ c_meta_attrs:
195
+ description: Current metadata — file attributes.
196
+ type: string
197
+ c_meta_uid:
198
+ description: Current metadata — user ID (Unix).
199
+ type: integer
200
+ c_meta_gid:
201
+ description: Current metadata — group ID (Unix).
202
+ type: integer
203
+ c_meta_mtime:
204
+ description: Current metadata — modification time (epoch).
205
+ type: integer
206
+ c_meta_atime:
207
+ description: Current metadata — access time (epoch).
208
+ type: integer
209
+ c_meta_ctime:
210
+ description: Current metadata — change time (epoch).
211
+ type: integer
212
+ c_meta_xattrs:
213
+ description: Current metadata — extended attributes (not used).
214
+ type: integer
215
+ c_meta_xattrs_cksum:
216
+ description: Current metadata — checksum of extended attributes.
217
+ type: string
218
+ c_meta_ol_legal_hold:
219
+ description: Current metadata — object lock legal hold flag.
220
+ type: integer
221
+ c_meta_ol_mode:
222
+ description: Current metadata — object lock mode.
223
+ type: integer
224
+ c_meta_ol_retain_until:
225
+ description: Current metadata — object lock retain-until date (epoch).
226
+ type: integer
227
+ c_meta_object_metadata:
228
+ description: Current metadata — object metadata (JSON).
229
+ type: string
230
+ c_cont_size:
231
+ description: Current content — file size in bytes.
232
+ type: integer
233
+ c_cont_csum:
234
+ description: Current content — checksum.
235
+ type: string
236
+ c_cont_mtime:
237
+ description: Current content — modification time (epoch).
238
+ type: integer
239
+ c_cont_timestamp:
240
+ description: Current content — internal timestamp.
241
+ type: integer
242
+ p_cont_size:
243
+ description: Pending content — file size in bytes.
244
+ type: integer
245
+ p_cont_csum:
246
+ description: Pending content — checksum.
247
+ type: string
248
+ p_cont_mtime:
249
+ description: Pending content — modification time (epoch).
250
+ type: integer
251
+ p_cont_timestamp:
252
+ description: Pending content — internal timestamp.
253
+ type: integer
254
+ f_meta_type:
255
+ description: Filesystem metadata — file type.
256
+ type: integer
257
+ f_meta_parentrid:
258
+ description: Filesystem metadata — parent record ID.
259
+ type: integer
260
+ f_meta_path:
261
+ description: Filesystem metadata — file path as seen on disk (displayed by `file_info`).
262
+ type: string
263
+ f_meta_attrs:
264
+ description: Filesystem metadata — file attributes.
265
+ type: string
266
+ f_meta_uid:
267
+ description: Filesystem metadata — user ID (Unix).
268
+ type: integer
269
+ f_meta_gid:
270
+ description: Filesystem metadata — group ID (Unix).
271
+ type: integer
272
+ f_meta_mtime:
273
+ description: Filesystem metadata — modification time (epoch).
274
+ type: integer
275
+ f_meta_atime:
276
+ description: Filesystem metadata — access time (epoch).
277
+ type: integer
278
+ f_meta_ctime:
279
+ description: Filesystem metadata — change time (epoch).
280
+ type: integer
281
+ f_meta_xattrs:
282
+ description: Filesystem metadata — extended attributes (not used).
283
+ type: integer
284
+ f_meta_xattrs_cksum:
285
+ description: Filesystem metadata — checksum of extended attributes.
286
+ type: string
287
+ f_meta_ol_legal_hold:
288
+ description: Filesystem metadata — object lock legal hold flag.
289
+ type: integer
290
+ f_meta_ol_mode:
291
+ description: Filesystem metadata — object lock mode.
292
+ type: integer
293
+ f_meta_ol_retain_until:
294
+ description: Filesystem metadata — object lock retain-until date (epoch).
295
+ type: integer
296
+ f_meta_object_metadata:
297
+ description: Filesystem metadata — object metadata (JSON).
298
+ type: string
299
+ f_cont_size:
300
+ description: Filesystem content — file size in bytes.
301
+ type: integer
302
+ f_cont_csum:
303
+ description: Filesystem content — checksum.
304
+ type: string
305
+ f_cont_mtime:
306
+ description: Filesystem content — modification time (epoch).
307
+ type: integer
308
+ f_cont_timestamp:
309
+ description: Filesystem content — internal timestamp.
310
+ type: integer
311
+ pcr_reqno:
312
+ description: Peer content request sequence number.
313
+ type: integer
314
+ pp_cont_size:
315
+ description: Peer pending content — file size in bytes.
316
+ type: integer
317
+ pp_cont_csum:
318
+ description: Peer pending content — checksum.
319
+ type: string
320
+ pp_cont_mtime:
321
+ description: Peer pending content — modification time (epoch).
322
+ type: integer
323
+ pp_cont_timestamp:
324
+ description: Peer pending content — internal timestamp.
325
+ type: integer
326
+ tobe_deleted:
327
+ description: Set to 1 when the record is flagged for deletion (garbage collection pending).
328
+ type: integer
329
+ scn_not_visited:
330
+ description: Set to 1 when the scanner has not yet visited this entry in the current scan pass.
331
+ type: integer
332
+ scn_required:
333
+ description: Set to 1 when a rescan of this entry is required.
334
+ type: integer
335
+ scn_completed:
336
+ description: Set to 1 when the scan of this directory entry has completed.
337
+ type: integer
338
+ candidate_in_prog:
339
+ description: Set to 1 when this record is currently in the candidate transfer list.
340
+ type: integer
341
+ meta_lastreq_no:
342
+ description: Sequence number of the last metadata request sent.
343
+ type: integer
344
+ meta_lastresp_no:
345
+ description: Sequence number of the last metadata response received.
346
+ type: integer
347
+ commit_version:
348
+ description: Local commit version for this entry.
349
+ type: integer
350
+ peer_commit_version:
351
+ description: Peer commit version for this entry.
352
+ type: integer
353
+ peer_nodeid:
354
+ description: Peer filesystem node ID.
355
+ type: integer
356
+ recursive_mtime:
357
+ description: Most recent modification time across all descendants (directories only).
358
+ type: integer
359
+ message:
360
+ description: Error or status message associated with this entry.
361
+ type: string
@@ -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?('#/')){"schema $ref must start with '#/': #{ref}"}
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,70 @@
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
+ # @!method self.instance
11
+ # Returns the singleton instance of Registry
12
+ # @return [Registry] the singleton instance
13
+ class Registry
14
+ include Singleton
15
+
16
+ class << self
17
+ def known?(sym)
18
+ LOCATIONS.key?(sym)
19
+ end
20
+
21
+ # Get path to request body, no check if it exists
22
+ # @return [String] path to request body
23
+ def req_body(component, endpoint)
24
+ "#{component}:paths./#{endpoint}.requestBody.content.application/json.schema"
25
+ end
26
+ end
27
+
28
+ LOCATIONS = {
29
+ spec: 'aspera/transfer/spec.schema.yaml',
30
+ args: 'aspera/sync/args.schema.yaml',
31
+ conf: 'aspera/sync/conf.schema.yaml',
32
+ opts: 'aspera/cli/options.schema.yaml',
33
+ aoc: 'aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml',
34
+ faspex: 'aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml',
35
+ async_tables: 'aspera/schema/async_tables.yaml'
36
+ }
37
+
38
+ OPTIONS = 'opts'
39
+ TRANSFER_SPEC = 'spec'
40
+ SYNC_CONF = 'conf'
41
+ SYNC_ARGS = 'args'
42
+ AOC = 'aoc'
43
+ FASPEX = 'faspex'
44
+ ASYNC_TABLES = 'async_tables'
45
+ TRANSFER_INFO = "#{OPTIONS}:components.schemas.TransferInfo"
46
+
47
+ REQ_BODY = '.requestBody.content.application/json.schema'
48
+
49
+ def initialize
50
+ @cache = {}
51
+ @main_folder = File.expand_path('../..', __dir__)
52
+ end
53
+
54
+ # Read schema from file or from cache
55
+ # @param name_path [String] one of the keys in LOCATIONS, with optional :<path> suffix
56
+ # @return [Reader] schema
57
+ def reader(name_path)
58
+ name, path = name_path.split(':', 2)
59
+ sym = name.to_sym
60
+ Aspera.assert(Registry.known?(sym)){"schema: #{sym}"}
61
+ spec_file = File.join(@main_folder, LOCATIONS[sym])
62
+ @cache[sym] = Yaml.safe_load(File.read(spec_file)) if spec_file.end_with?('.yaml') && !@cache.key?(sym)
63
+ @cache[sym] = JSON.parse(File.read(spec_file)) if spec_file.end_with?('.json') && !@cache.key?(sym)
64
+ reader = Reader.new(@cache[sym])
65
+ return reader unless path
66
+ reader.dig(*path.split('.'))
67
+ end
68
+ end
69
+ end
70
+ end
@@ -6,6 +6,10 @@ require 'singleton'
6
6
 
7
7
  module Aspera
8
8
  # remove secret from logs and output
9
+ #
10
+ # @!method self.instance
11
+ # Returns the singleton instance of SecretHider
12
+ # @return [SecretHider] the singleton instance
9
13
  class SecretHider
10
14
  include Singleton
11
15