aspera-cli 4.27.2 → 4.27.4

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 (145) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +133 -0
  4. data/CONTRIBUTING.md +5 -2
  5. data/bin/ascli +3 -1
  6. data/docs/README.md +1006 -766
  7. data/lib/aspera/agent/base.rb +7 -2
  8. data/lib/aspera/agent/connect.rb +6 -8
  9. data/lib/aspera/agent/desktop.rb +2 -6
  10. data/lib/aspera/agent/direct.rb +52 -22
  11. data/lib/aspera/agent/node.rb +9 -8
  12. data/lib/aspera/agent/transferd.rb +2 -2
  13. data/lib/aspera/api/alee.rb +1 -1
  14. data/lib/aspera/api/aoc.rb +14 -12
  15. data/lib/aspera/api/ats.rb +1 -1
  16. data/lib/aspera/api/cos_node.rb +2 -2
  17. data/lib/aspera/api/faspex.rb +11 -7
  18. data/lib/aspera/api/httpgw.rb +38 -35
  19. data/lib/aspera/api/node.rb +39 -34
  20. data/lib/aspera/ascmd.rb +3 -1
  21. data/lib/aspera/ascp/installation.rb +63 -28
  22. data/lib/aspera/ascp/management.rb +1 -0
  23. data/lib/aspera/assert.rb +4 -0
  24. data/lib/aspera/cli/ascp_actions.rb +20 -41
  25. data/lib/aspera/cli/async_transfer_store.rb +12 -11
  26. data/lib/aspera/cli/bootstrapper.rb +14 -16
  27. data/lib/aspera/cli/command_line.rb +252 -0
  28. data/lib/aspera/cli/command_registry.rb +215 -37
  29. data/lib/aspera/cli/command_spec.rb +104 -15
  30. data/lib/aspera/cli/completion/ascli.bash +12 -0
  31. data/lib/aspera/cli/completion/ascli.fish +16 -0
  32. data/lib/aspera/cli/completion/ascli.zsh +19 -0
  33. data/lib/aspera/cli/context.rb +3 -0
  34. data/lib/aspera/cli/deprecation.rb +37 -0
  35. data/lib/aspera/cli/extended_value.rb +6 -3
  36. data/lib/aspera/cli/formatter.rb +94 -80
  37. data/lib/aspera/cli/gem_checker.rb +1 -1
  38. data/lib/aspera/cli/hints.rb +7 -6
  39. data/lib/aspera/cli/http.rb +22 -34
  40. data/lib/aspera/cli/info.rb +3 -0
  41. data/lib/aspera/cli/mcp_tool.rb +47 -83
  42. data/lib/aspera/cli/option_declarator.rb +33 -42
  43. data/lib/aspera/cli/option_registry.rb +69 -0
  44. data/lib/aspera/cli/option_types.rb +105 -0
  45. data/lib/aspera/cli/option_value.rb +278 -0
  46. data/lib/aspera/cli/options.schema.yaml +124 -15
  47. data/lib/aspera/cli/parser.rb +333 -862
  48. data/lib/aspera/cli/plugins/alee.rb +7 -4
  49. data/lib/aspera/cli/plugins/aoc.rb +545 -518
  50. data/lib/aspera/cli/plugins/ats.rb +59 -80
  51. data/lib/aspera/cli/plugins/base.rb +221 -265
  52. data/lib/aspera/cli/plugins/basic_auth.rb +2 -10
  53. data/lib/aspera/cli/plugins/config.rb +263 -184
  54. data/lib/aspera/cli/plugins/console.rb +103 -39
  55. data/lib/aspera/cli/plugins/cos.rb +6 -23
  56. data/lib/aspera/cli/plugins/factory.rb +3 -0
  57. data/lib/aspera/cli/plugins/faspex5.rb +204 -182
  58. data/lib/aspera/cli/plugins/faspio.rb +6 -11
  59. data/lib/aspera/cli/plugins/httpgw.rb +8 -11
  60. data/lib/aspera/cli/plugins/mcp.rb +20 -55
  61. data/lib/aspera/cli/plugins/node.rb +300 -327
  62. data/lib/aspera/cli/plugins/orchestrator.rb +152 -110
  63. data/lib/aspera/cli/plugins/preview.rb +96 -105
  64. data/lib/aspera/cli/plugins/server.rb +78 -53
  65. data/lib/aspera/cli/plugins/shares.rb +80 -131
  66. data/lib/aspera/cli/preset_actions.rb +44 -27
  67. data/lib/aspera/cli/preset_manager.rb +44 -19
  68. data/lib/aspera/cli/prompt.rb +36 -0
  69. data/lib/aspera/cli/result.rb +42 -36
  70. data/lib/aspera/cli/runner.rb +32 -59
  71. data/lib/aspera/cli/special_values.rb +5 -0
  72. data/lib/aspera/cli/sync_actions.rb +51 -46
  73. data/lib/aspera/cli/terminal_formatter.rb +9 -3
  74. data/lib/aspera/cli/transfer_actions.rb +14 -9
  75. data/lib/aspera/cli/transfer_agent.rb +34 -38
  76. data/lib/aspera/cli/transfer_progress.rb +290 -55
  77. data/lib/aspera/cli/vault_manager.rb +0 -17
  78. data/lib/aspera/cli/version.rb +1 -1
  79. data/lib/aspera/cli/wizard.rb +5 -3
  80. data/lib/aspera/coverage.rb +1 -1
  81. data/lib/aspera/environment.rb +35 -5
  82. data/lib/aspera/faspex_gw.rb +2 -1
  83. data/lib/aspera/faspex_postproc.rb +1 -0
  84. data/lib/aspera/graphql.rb +5 -5
  85. data/lib/aspera/json_rpc/client.rb +5 -5
  86. data/lib/aspera/keychain/encrypted_hash.rb +2 -2
  87. data/lib/aspera/keychain/factory.rb +2 -1
  88. data/lib/aspera/keychain/one_password_api.rb +1 -1
  89. data/lib/aspera/link_header.rb +2 -2
  90. data/lib/aspera/log.rb +47 -27
  91. data/lib/aspera/markdown.rb +2 -0
  92. data/lib/aspera/mime.rb +25 -0
  93. data/lib/aspera/node_emulator.rb +759 -0
  94. data/lib/aspera/oauth/base.rb +37 -26
  95. data/lib/aspera/oauth/factory.rb +7 -3
  96. data/lib/aspera/oauth/generic.rb +1 -1
  97. data/lib/aspera/oauth/json_credentials.rb +34 -0
  98. data/lib/aspera/oauth/jwt.rb +4 -5
  99. data/lib/aspera/oauth/web.rb +9 -8
  100. data/lib/aspera/oauth.rb +1 -0
  101. data/lib/aspera/persistency_folder.rb +1 -3
  102. data/lib/aspera/preview/file_types.rb +4 -4
  103. data/lib/aspera/preview/generator.rb +11 -1
  104. data/lib/aspera/preview/options.schema.yaml +119 -0
  105. data/lib/aspera/preview/terminal.rb +4 -3
  106. data/lib/aspera/preview/utils.rb +9 -6
  107. data/lib/aspera/products/connect.rb +1 -1
  108. data/lib/aspera/rainbow.rb +7 -0
  109. data/lib/aspera/rest/aspera_errors.rb +72 -0
  110. data/lib/aspera/rest/call_error.rb +27 -0
  111. data/lib/aspera/rest/client.rb +523 -0
  112. data/lib/aspera/rest/error_analyzer.rb +113 -0
  113. data/lib/aspera/rest/list.rb +149 -0
  114. data/lib/aspera/rest/parameters.rb +55 -0
  115. data/lib/aspera/rest/util.rb +176 -0
  116. data/lib/aspera/rest.rb +7 -621
  117. data/lib/aspera/schema/IBM Aspera Console-enhanced.yaml +1125 -0
  118. data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +0 -20
  119. data/lib/aspera/schema/IBM Aspera Orchestrator API-v1.yaml +1784 -0
  120. data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +1230 -137
  121. data/lib/aspera/schema/IBM Aspera on Cloud Automation API-1.0.5-enhanced.yaml +2395 -0
  122. data/lib/aspera/schema/IBM_Aspera_Shares.yaml +14 -13
  123. data/lib/aspera/schema/documentation.rb +13 -3
  124. data/lib/aspera/schema/reader.rb +12 -18
  125. data/lib/aspera/schema/registry.rb +23 -1
  126. data/lib/aspera/schema/validator.rb +92 -0
  127. data/lib/aspera/secret_hider.rb +36 -25
  128. data/lib/aspera/string_ext.rb +15 -0
  129. data/lib/aspera/temp_file_manager.rb +6 -5
  130. data/lib/aspera/transfer/parameters.rb +2 -0
  131. data/lib/aspera/transfer/spec.rb +1 -0
  132. data/lib/aspera/uri_reader.rb +11 -11
  133. data/lib/aspera/web_auth/index.html +147 -0
  134. data/lib/aspera/web_auth/server.rb +81 -0
  135. data.tar.gz.sig +0 -0
  136. metadata +43 -9
  137. metadata.gz.sig +0 -0
  138. data/lib/aspera/colors.rb +0 -79
  139. data/lib/aspera/node_simulator.rb +0 -344
  140. data/lib/aspera/preview/options.rb +0 -45
  141. data/lib/aspera/rest_call_error.rb +0 -25
  142. data/lib/aspera/rest_error_analyzer.rb +0 -111
  143. data/lib/aspera/rest_errors_aspera.rb +0 -58
  144. data/lib/aspera/rest_list.rb +0 -136
  145. data/lib/aspera/web_auth.rb +0 -211
@@ -1,344 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'aspera/assert'
4
- require 'aspera/ascp/installation'
5
- require 'aspera/agent/direct'
6
- require 'aspera/log'
7
- require 'webrick'
8
- require 'json'
9
-
10
- module Aspera
11
- class NodeSimulator
12
- def initialize
13
- @agent = Agent::Direct.new(management_cb: ->(event) { process_event(event) })
14
- @sessions = {}
15
- end
16
-
17
- def start(ts)
18
- @agent.start_transfer(ts)
19
- end
20
-
21
- def all_sessions
22
- @agent.sessions.map { |session| session[:job_id] }.uniq.each.map { |job_id| job_to_transfer(job_id) }
23
- end
24
-
25
- # status: ('waiting', 'partially_completed', 'unknown', 'waiting(read error)',] 'running', 'completed', 'failed'
26
- def job_to_transfer(job_id)
27
- jobs = @agent.sessions_by_job(job_id)
28
- ts = nil
29
- sessions = jobs.map do |j|
30
- ts ||= j[:ts]
31
- {
32
- id: j[:id],
33
- client_node_id: '',
34
- server_node_id: '2bbdcc39-f789-4d47-8163-6767fc14f421',
35
- client_ip_address: '192.168.0.100',
36
- server_ip_address: '5.10.114.4',
37
- status: 'running',
38
- retry_timeout: 3600,
39
- retry_count: 0,
40
- start_time_usec: 1701094040000000,
41
- end_time_usec: nil,
42
- elapsed_usec: 405312,
43
- bytes_transferred: 26,
44
- bytes_written: 26,
45
- bytes_lost: 0,
46
- files_completed: 1,
47
- directories_completed: 0,
48
- target_rate_kbps: 500000,
49
- min_rate_kbps: 0,
50
- calc_rate_kbps: 9900,
51
- network_delay_usec: 40000,
52
- avg_rate_kbps: 0.51,
53
- error_code: 0,
54
- error_desc: '',
55
- source_statistics: {
56
- args_scan_attempted: 1,
57
- args_scan_completed: 1,
58
- paths_scan_attempted: 1,
59
- paths_scan_failed: 0,
60
- paths_scan_skipped: 0,
61
- paths_scan_excluded: 0,
62
- dirs_scan_completed: 0,
63
- files_scan_completed: 1,
64
- dirs_xfer_attempted: 0,
65
- dirs_xfer_fail: 0,
66
- files_xfer_attempted: 1,
67
- files_xfer_fail: 0,
68
- files_xfer_noxfer: 0
69
- },
70
- precalc: {
71
- enabled: true,
72
- status: 'ready',
73
- bytes_expected: 0,
74
- directories_expected: 0,
75
- files_expected: 0,
76
- files_excluded: 0,
77
- files_special: 0,
78
- files_failed: 1
79
- }
80
- }
81
- end
82
- ts ||= {}
83
- result = {
84
- id: job_id,
85
- status: 'running',
86
- start_spec: ts,
87
- sessions: sessions,
88
- bytes_transferred: 26,
89
- bytes_written: 26,
90
- bytes_lost: 0,
91
- avg_rate_kbps: 0.51,
92
- files_completed: 1,
93
- files_skipped: 0,
94
- directories_completed: 0,
95
- start_time_usec: 1701094040000000,
96
- end_time_usec: 1701094040405312,
97
- elapsed_usec: 405312,
98
- error_code: 0,
99
- error_desc: '',
100
- precalc: {
101
- status: 'ready',
102
- bytes_expected: 0,
103
- files_expected: 0,
104
- directories_expected: 0,
105
- files_special: 0,
106
- files_failed: 1
107
- },
108
- files: [{
109
- id: 'd1b5c112-82b75425-860745fc-93851671-64541bdd',
110
- path: '/workspaces/45071/packages/bYA_ilq73g.asp-package/contents/data_file.bin',
111
- start_time_usec: 1701094040000000,
112
- elapsed_usec: 105616,
113
- end_time_usec: 1701094040001355,
114
- status: 'completed',
115
- error_code: 0,
116
- error_desc: '',
117
- size: 26,
118
- type: 'file',
119
- checksum_type: 'none',
120
- checksum: nil,
121
- start_byte: 0,
122
- bytes_written: 26,
123
- session_id: 'bafc72b8-366c-4501-8095-47208183d6b8'
124
- }]
125
- }
126
- Log.dump(:job, result, level: :trace2)
127
- return result
128
- end
129
-
130
- # Process event from management port
131
- def process_event(event)
132
- # Code duplicated on purpose, as implementation goes, blocks will be replaced
133
- case event['Type']
134
- when 'NOP' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
135
- # rubocop:disable Lint/DuplicateBranch
136
- when 'START' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
137
- when 'QUERY' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
138
- when 'QUERYRSP' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
139
- when 'STATS' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
140
- when 'STOP' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
141
- when 'ERROR' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
142
- when 'CANCEL' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
143
- when 'DONE' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
144
- when 'RATE' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
145
- when 'FILEERROR' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
146
- when 'SESSION' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
147
- when 'NOTIFICATION' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
148
- when 'INIT' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
149
- when 'VLINK' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
150
- when 'PUT' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
151
- when 'WRITE' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
152
- when 'CLOSE' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
153
- when 'SKIP' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
154
- when 'ARGSTOP' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
155
- # rubocop:enable Lint/DuplicateBranch
156
- else Aspera.error_unreachable_line
157
- end
158
- end
159
- end
160
-
161
- # this class answers the Faspex /send API and creates a package on Aspera on Cloud
162
- # a new instance is created for each request
163
- class NodeSimulatorServlet < WEBrick::HTTPServlet::AbstractServlet
164
- PATH_TRANSFERS = '/ops/transfers'
165
- PATH_ONE_TRANSFER = %r{/ops/transfers/(.+)$}
166
- PATH_BROWSE = '/files/browse'
167
- # @param credentials [Hash]
168
- # @param simulator [NodeSimulator]
169
- def initialize(server, credentials, simulator)
170
- super(server)
171
- @credentials = credentials
172
- @simulator = simulator
173
- # Resolve once at startup; default to current working directory
174
- @browse_root = File.realpath(credentials[:browse_root] || Dir.pwd)
175
- end
176
-
177
- require 'json'
178
- require 'time'
179
-
180
- def folder_to_structure(folder_path)
181
- # Resolve and confine to browse_root (prevents path traversal via client-supplied path)
182
- resolved = File.realpath(folder_path)
183
- Aspera.assert(resolved.start_with?("#{@browse_root}/") || resolved.eql?(@browse_root)) { 'Browse path traversal attempt detected' }
184
- Aspera.assert(Dir.exist?(resolved)) { "Path does not exist or is not a directory: #{resolved}" }
185
- folder_path = resolved
186
-
187
- # Build self structure
188
- folder_stat = File.stat(folder_path)
189
- structure = {
190
- 'self' => {
191
- 'path' => folder_path,
192
- 'basename' => File.basename(folder_path),
193
- 'type' => 'directory',
194
- 'size' => folder_stat.size,
195
- 'mtime' => folder_stat.mtime.utc.iso8601,
196
- 'permissions' => [
197
- {'name' => 'view'},
198
- {'name' => 'edit'},
199
- {'name' => 'delete'}
200
- ]
201
- },
202
- 'items' => []
203
- }
204
-
205
- # Iterate over folder contents
206
- Dir.foreach(folder_path) do |entry|
207
- next if entry == '.' || entry == '..' # Skip current and parent directory
208
-
209
- item_path = File.join(folder_path, entry)
210
- item_type = File.ftype(item_path) rescue 'unknown' # Get the type of file
211
- item_stat = File.lstat(item_path) # Use lstat to handle symbolic links correctly
212
-
213
- item = {
214
- 'path' => item_path,
215
- 'basename' => entry,
216
- 'type' => item_type,
217
- 'size' => item_stat.size,
218
- 'mtime' => item_stat.mtime.utc.iso8601,
219
- 'permissions' => [
220
- {'name' => 'view'},
221
- {'name' => 'edit'},
222
- {'name' => 'delete'}
223
- ]
224
- }
225
-
226
- # Add additional details for specific types
227
- case item_type
228
- when 'file'
229
- item['partial_file'] = false
230
- when 'link'
231
- item['target'] = File.readlink(item_path) rescue nil # Add the target of the symlink
232
- when 'unknown'
233
- item['note'] = 'File type could not be determined'
234
- end
235
-
236
- structure['items'] << item
237
- end
238
-
239
- structure
240
- end
241
-
242
- def do_POST(request, response)
243
- case request.path
244
- when PATH_TRANSFERS
245
- job_id = @simulator.start(JSON.parse(request.body))
246
- sleep(0.5)
247
- set_json_response(request, response, @simulator.job_to_transfer(job_id))
248
- when PATH_BROWSE
249
- req = JSON.parse(request.body)
250
- # req['count']
251
- set_json_response(request, response, folder_to_structure(req['path'] || @browse_root))
252
- else
253
- set_json_response(request, response, [{error: 'Bad request'}], code: 400)
254
- end
255
- end
256
-
257
- def do_GET(request, response)
258
- case request.path
259
- when '/info'
260
- info = Ascp::Installation.instance.ascp_info
261
- set_json_response(request, response, {
262
- application: 'node',
263
- current_time: Time.now.utc.iso8601(0),
264
- version: info['sdk_ascp_version'].gsub(/ .*$/, ''),
265
- license_expiration_date: info['expiration_date'],
266
- license_max_rate: info['maximum_bandwidth'],
267
- os: %x(uname -srv).chomp,
268
- aej_status: 'disconnected',
269
- async_reporting: 'no',
270
- transfer_activity_reporting: 'no',
271
- transfer_user: 'xfer',
272
- docroot: 'file:////data/aoc/eudemo-sedemo',
273
- node_id: '2bbdcc39-f789-4d47-8163-6767fc14f421',
274
- cluster_id: '6dae2844-d1a9-47a5-916d-9b3eac3ea466',
275
- acls: ['impersonation'],
276
- access_key_configuration_capabilities: {
277
- transfer: %w[
278
- cipher
279
- policy
280
- target_rate_cap_kbps
281
- target_rate_kbps
282
- preserve_timestamps
283
- content_protection_secret
284
- aggressiveness
285
- token_encryption_key
286
- byok_enabled
287
- bandwidth_flow_network_rc_module
288
- file_checksum_type
289
- ],
290
- server: %w[
291
- activity_event_logging
292
- activity_file_event_logging
293
- recursive_counts
294
- aej_logging
295
- wss_enabled
296
- activity_transfer_ignore_skipped_files
297
- activity_files_max
298
- access_key_credentials_encryption_type
299
- discovery
300
- auto_delete
301
- allow
302
- deny
303
- ]
304
- },
305
- capabilities: [
306
- {name: 'sync', value: true},
307
- {name: 'watchfolder', value: true},
308
- {name: 'symbolic_links', value: true},
309
- {name: 'move_file', value: true},
310
- {name: 'move_directory', value: true},
311
- {name: 'filelock', value: false},
312
- {name: 'ssh_fingerprint', value: false},
313
- {name: 'aej_version', value: '1.0'},
314
- {name: 'page', value: true},
315
- {name: 'file_id_version', value: '2.0'},
316
- {name: 'auto_delete', value: false}
317
- ],
318
- settings: [
319
- {name: 'content_protection_required', value: false},
320
- {name: 'content_protection_strong_pass_required', value: false},
321
- {name: 'filelock_restriction', value: 'none'},
322
- {name: 'ssh_fingerprint', value: nil},
323
- {name: 'wss_enabled', value: false},
324
- {name: 'wss_port', value: 443}
325
- ]
326
- })
327
- when PATH_TRANSFERS
328
- set_json_response(request, response, @simulator.all_sessions)
329
- when PATH_ONE_TRANSFER
330
- job_id = request.path.match(PATH_ONE_TRANSFER)[1]
331
- set_json_response(request, response, @simulator.job_to_transfer(job_id))
332
- else
333
- set_json_response(request, response, [{error: 'Unknown request'}], code: 400)
334
- end
335
- end
336
-
337
- def set_json_response(request, response, json, code: 200)
338
- response.status = code
339
- response['Content-Type'] = Mime::JSON
340
- response.body = json.to_json
341
- Log.log.trace1 { Log.obj_dump("response for #{request.request_method} #{request.path}", json) }
342
- end
343
- end
344
- end
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # cspell:ignore reencode conv transframes pauseframes
4
-
5
- module Aspera
6
- module Preview
7
- # generator options. Used as parameter to preview generator object.
8
- # also settable by command line.
9
- class Options
10
- # types of generation for video files
11
- VIDEO_CONVERSION_METHODS = %i[reencode blend clips].freeze
12
- VIDEO_THUMBNAIL_METHODS = %i[fixed animated].freeze
13
- # methods for office document conversion
14
- OFFICE_CONVERSION_METHODS = %i[soffice unoconv].freeze
15
- # options used in generator
16
- # for scaling see: https://trac.ffmpeg.org/wiki/Scaling
17
- # iw/ih : input width or height
18
- # -x : keep aspect ratio, having value a multiple of x
19
- DESCRIPTIONS = [
20
- {name: :max_size, default: 1 << 24, description: 'maximum size (in bytes) of preview file'},
21
- {name: :thumb_vid_scale, default: '-1:min(ih,100)', description: 'png: video: size (ffmpeg scale argument)'},
22
- {name: :thumb_vid_fraction, default: 0.1, description: 'png: video: time percent position of snapshot'},
23
- {name: :thumb_img_size, default: 800, description: 'png: non-video: height (and width)'},
24
- {name: :thumb_text_font, default: 'Courier', description: 'png: plaintext: font for text rendering: `magick identify -list font`'},
25
- {name: :office_conversion, default: :soffice, description: 'office: method for office document conversion', values: OFFICE_CONVERSION_METHODS},
26
- {name: :video_conversion, default: :reencode, description: 'mp4: method for preview generation', values: VIDEO_CONVERSION_METHODS},
27
- {name: :video_png_conv, default: :fixed, description: 'mp4: method for thumbnail generation', values: VIDEO_THUMBNAIL_METHODS},
28
- {name: :video_scale, default: 'min(iw,360):-2', description: 'mp4: all: video scale (ffmpeg scale argument)'},
29
- {name: :video_start_sec, default: 10, description: 'mp4: all: start offset (seconds) of video preview'},
30
- {name: :video_codec, default: nil, description: 'mp4: reencode: video codec for ffmpeg (e.g. libx264, h264_videotoolbox, h264_nvenc), default: auto-detect'},
31
- {name: :reencode_ffmpeg, default: {}, description: 'mp4: reencode: options to ffmpeg, keys: `in`, `out`'},
32
- {name: :blend_keyframes, default: 30, description: 'mp4: blend: # key frames'},
33
- {name: :blend_pauseframes, default: 3, description: 'mp4: blend: # pause frames'},
34
- {name: :blend_transframes, default: 5, description: 'mp4: blend: # transition blend frames'},
35
- {name: :blend_fps, default: 15, description: 'mp4: blend: frame per second'},
36
- {name: :clips_count, default: 5, description: 'mp4: clips: number of clips'},
37
- {name: :clips_length, default: 5, description: 'mp4: clips: length in seconds of each clips'}
38
- ].freeze
39
- # add accessors
40
- DESCRIPTIONS.each do |opt|
41
- attr_accessor opt[:name]
42
- end
43
- end
44
- end
45
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Aspera
4
- # raised on error after REST call
5
- class RestCallError < StandardError
6
- def request
7
- @context[:request]
8
- end
9
-
10
- def response
11
- @context[:response]
12
- end
13
-
14
- def data
15
- @context[:data]
16
- end
17
-
18
- # @param context [Hash,String] with keys :messages, :request, :response, :data
19
- def initialize(context)
20
- context = {messages: [context]} if context.is_a?(String)
21
- @context = context
22
- super(@context[:messages].join("\n"))
23
- end
24
- end
25
- end
@@ -1,111 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'aspera/rest_call_error'
4
- require 'aspera/log'
5
- require 'singleton'
6
- require 'net/http'
7
-
8
- module Aspera
9
- # analyze error codes returned by REST calls and raise ruby exception
10
- #
11
- # @!method self.instance
12
- # Returns the singleton instance of RestErrorAnalyzer
13
- # @return [RestErrorAnalyzer] the singleton instance
14
- class RestErrorAnalyzer
15
- include Singleton
16
-
17
- attr_accessor :log_file
18
-
19
- # the singleton object is registered with application specific handlers
20
- def initialize
21
- # list of handlers
22
- @error_handlers = []
23
- @log_file = nil
24
- add_handler('Type Generic') do |type, context|
25
- if !context[:response].code.start_with?('2')
26
- # add generic information
27
- RestErrorAnalyzer.add_error(context, type, "#{context[:request]['host']} #{context[:response].code} #{context[:response].message}")
28
- end
29
- end
30
- end
31
-
32
- # Use this method to analyze a EST result and raise an exception
33
- # Analyzes REST call response and raises a RestCallError exception
34
- # if HTTP result code is not 2XX
35
- # @param req [Net::HTTPRequest] the HTTP request
36
- # @param data [Object] the parsed response body
37
- # @param http [Net::HTTPResponse] the raw HTTP response
38
- def raise_on_error(req, data, http)
39
- Log.log.debug { "raise_on_error #{req.method} #{req.path} #{http.code}" }
40
- context = {
41
- messages: [],
42
- request: req,
43
- response: http,
44
- data: data
45
- }
46
- # multiple error messages can be found
47
- # analyze errors from provided handlers
48
- # note that there can be an error even if code is 2XX
49
- @error_handlers.each do |handler|
50
- handler[:block].call(handler[:name], context)
51
- rescue StandardError => e
52
- Log.log.error { "ERROR in handler:\n#{e.message}\n#{e.backtrace}" }
53
- end
54
- raise RestCallError, context unless context[:messages].empty?
55
- end
56
-
57
- # Add a new error handler (done at application initialization)
58
- # @param name [String] name of error handler (for logs)
59
- # @param block [Proc] processing of response: takes two parameters: `name`, `context`
60
- # name is the one provided here
61
- # context is built in method raise_on_error
62
- def add_handler(name, &block)
63
- @error_handlers.unshift({name: name, block: block})
64
- nil
65
- end
66
-
67
- # Add a simple error handler
68
- # Check that key exists and is string under specified path (hash)
69
- # Adds other keys as secondary information
70
- # @param name [String] name of error handler (for logs)
71
- # @param always [Boolean] if true, always add error message, even if response code is 2XX
72
- # @param path [Array] path to error message in response
73
- def add_simple_handler(name:, always: false, path:)
74
- path.freeze
75
- add_handler(name) do |type, context|
76
- if context[:data].is_a?(Hash) && (!context[:response].code.start_with?('2') || always)
77
- # Log.log.debug{"simple_handler: #{type} #{path} #{path.last}"}
78
- # dig and find hash containing error message
79
- error_struct = path.length.eql?(1) ? context[:data] : context[:data].dig(*path[0..-2])
80
- # Log.log.debug{"found: #{error_struct.class} #{error_struct}"}
81
- if error_struct.is_a?(Hash) && error_struct[path.last].is_a?(String)
82
- RestErrorAnalyzer.add_error(context, type, error_struct[path.last])
83
- error_struct.each do |k, v|
84
- next if k.eql?(path.last)
85
- RestErrorAnalyzer.add_error(context, "#{type}(sub)", "#{k}: #{v}") if [String, Integer].include?(v.class)
86
- end
87
- end
88
- end
89
- end
90
- end
91
-
92
- class << self
93
- # Used by handler to add an error description to list of errors
94
- # For logging and tracing : collect error descriptions (create file to activate)
95
- # @param context [Hash] the result context, provided to handler
96
- # @param type [String] type of exception, for logging purpose
97
- # @param message [String] one error message to add to list
98
- def add_error(context, type, message)
99
- context[:messages].push(message)
100
- Log.log.trace1 { "Found error: #{type}: #{message}" }
101
- log_file = instance.log_file
102
- # log error for further analysis (file must exist to activate)
103
- return if log_file.nil? || !File.exist?(log_file)
104
- File.open(log_file, 'a+') do |f|
105
- f.write("\n=#{type}=====\n#{context[:request].method} #{context[:request].path}\n#{context[:response].code}\n" \
106
- "#{JSON.generate(context[:data])}\n#{context[:messages].join("\n")}")
107
- end
108
- end
109
- end
110
- end
111
- end
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'aspera/rest_error_analyzer'
4
- require 'aspera/log'
5
-
6
- module Aspera
7
- # REST error handlers for various Aspera REST APIs
8
- class RestErrorsAspera
9
- class << self
10
- # handlers should probably be defined by plugins for modularity
11
- def register_handlers
12
- Log.log.debug('registering Aspera REST error handlers')
13
- # Faspex 4: both user_message and internal_message, and code 200
14
- # example: missing meta data on package creation
15
- RestErrorAnalyzer.instance.add_simple_handler(name: 'Type 1: error:user_message', path: %w[error user_message], always: true)
16
- RestErrorAnalyzer.instance.add_simple_handler(name: 'Type 2: error:description', path: %w[error description])
17
- RestErrorAnalyzer.instance.add_simple_handler(name: 'Type 3: error:internal_message', path: %w[error internal_message])
18
- RestErrorAnalyzer.instance.add_simple_handler(name: 'Type 5', path: ['error_description'])
19
- RestErrorAnalyzer.instance.add_simple_handler(name: 'Type 6', path: ['message'])
20
- RestErrorAnalyzer.instance.add_simple_handler(name: 'Type 6', path: ['failure'], always: true)
21
- # AoC Automation
22
- RestErrorAnalyzer.instance.add_simple_handler(name: 'AoC Automation', path: ['error'])
23
- RestErrorAnalyzer.instance.add_handler('Type 7: errors[]') do |type, context|
24
- next unless context[:data].is_a?(Hash) && context[:data]['errors'].is_a?(Hash)
25
- # special for Shares: false positive ? (update global transfer_settings)
26
- next if context[:data].key?('min_connect_version')
27
- context[:data]['errors'].each do |k, v|
28
- RestErrorAnalyzer.add_error(context, type, "#{k}: #{v}")
29
- end
30
- end
31
- # call to upload_setup and download_setup of node api
32
- RestErrorAnalyzer.instance.add_handler('T8:node: *_setup') do |type, context|
33
- next unless context[:data].is_a?(Hash)
34
- d_t_s = context[:data]['transfer_specs']
35
- next unless d_t_s.is_a?(Array)
36
- d_t_s.each do |res|
37
- r_err = res.dig(*%w[transfer_spec error]) || res['error']
38
- next unless r_err.is_a?(Hash)
39
- RestErrorAnalyzer.add_error(context, type, r_err.values.join(': '))
40
- end
41
- end
42
- RestErrorAnalyzer.instance.add_simple_handler(name: 'T9:IBM cloud IAM', path: ['errorMessage'])
43
- RestErrorAnalyzer.instance.add_simple_handler(name: 'T10:faspex v4', path: ['user_message'])
44
- RestErrorAnalyzer.instance.add_handler('Orchestrator') do |type, context|
45
- next if context[:response].code.start_with?('2')
46
- data = context[:data]
47
- next unless data.is_a?(Hash)
48
- work_order = data['work_order']
49
- next unless work_order.is_a?(Hash)
50
- RestErrorAnalyzer.add_error(context, type, work_order['statusDetails'])
51
- data['missing_parameters']&.each do |param|
52
- RestErrorAnalyzer.add_error(context, type, "missing parameter: #{param}")
53
- end
54
- end
55
- end
56
- end
57
- end
58
- end