aspera-cli 4.25.1 → 4.25.3
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +456 -405
- data/CONTRIBUTING.md +22 -18
- data/README.md +33 -9741
- data/bin/asession +111 -88
- data/lib/aspera/agent/connect.rb +1 -1
- data/lib/aspera/agent/desktop.rb +1 -1
- data/lib/aspera/agent/direct.rb +19 -18
- data/lib/aspera/agent/node.rb +1 -1
- data/lib/aspera/api/aoc.rb +44 -20
- data/lib/aspera/api/faspex.rb +25 -6
- data/lib/aspera/api/node.rb +20 -16
- data/lib/aspera/ascp/installation.rb +32 -51
- data/lib/aspera/assert.rb +2 -2
- data/lib/aspera/cli/extended_value.rb +1 -0
- data/lib/aspera/cli/formatter.rb +0 -4
- data/lib/aspera/cli/hints.rb +18 -4
- data/lib/aspera/cli/main.rb +3 -6
- data/lib/aspera/cli/manager.rb +46 -30
- data/lib/aspera/cli/plugins/aoc.rb +155 -131
- data/lib/aspera/cli/plugins/base.rb +15 -18
- data/lib/aspera/cli/plugins/config.rb +50 -87
- data/lib/aspera/cli/plugins/factory.rb +2 -2
- data/lib/aspera/cli/plugins/faspex.rb +4 -4
- data/lib/aspera/cli/plugins/faspex5.rb +74 -76
- data/lib/aspera/cli/plugins/node.rb +3 -5
- data/lib/aspera/cli/plugins/oauth.rb +26 -25
- data/lib/aspera/cli/plugins/preview.rb +9 -14
- data/lib/aspera/cli/plugins/shares.rb +15 -7
- data/lib/aspera/cli/transfer_agent.rb +2 -2
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/colors.rb +7 -0
- data/lib/aspera/environment.rb +30 -16
- data/lib/aspera/faspex_gw.rb +6 -6
- data/lib/aspera/faspex_postproc.rb +20 -14
- data/lib/aspera/hash_ext.rb +8 -0
- data/lib/aspera/log.rb +15 -15
- data/lib/aspera/markdown.rb +22 -0
- data/lib/aspera/node_simulator.rb +1 -1
- data/lib/aspera/oauth/base.rb +2 -2
- data/lib/aspera/oauth/url_json.rb +2 -2
- data/lib/aspera/oauth/web.rb +1 -1
- data/lib/aspera/preview/generator.rb +9 -9
- data/lib/aspera/rest.rb +44 -37
- data/lib/aspera/rest_call_error.rb +16 -8
- data/lib/aspera/rest_error_analyzer.rb +38 -36
- data/lib/aspera/rest_errors_aspera.rb +19 -18
- data/lib/aspera/transfer/resumer.rb +2 -2
- data/lib/aspera/yaml.rb +49 -0
- data.tar.gz.sig +0 -0
- metadata +17 -3
- metadata.gz.sig +0 -0
- data/release_notes.md +0 -8
data/lib/aspera/rest.rb
CHANGED
|
@@ -53,14 +53,24 @@ module Aspera
|
|
|
53
53
|
class EntityNotFound < Error
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
module Mime
|
|
57
|
+
JSON = 'application/json'
|
|
58
|
+
WWW = 'application/x-www-form-urlencoded'
|
|
59
|
+
TEXT = 'text/plain'
|
|
60
|
+
# Check if a MIME type is JSON, including parameters, e.g. application/json; charset=utf-8
|
|
61
|
+
def json?(mime)
|
|
62
|
+
JSON_LIST.include?(mime)
|
|
63
|
+
end
|
|
64
|
+
module_function :json?
|
|
65
|
+
# Content-Type that are JSON
|
|
66
|
+
JSON_LIST = [JSON, 'application/vnd.api+json', 'application/x-javascript'].freeze
|
|
67
|
+
private_constant :JSON_LIST
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Make HTTP calls, equivalent to rest-client
|
|
57
71
|
# rest call errors are raised as exception RestCallError
|
|
58
72
|
# and error are analyzed in RestErrorAnalyzer
|
|
59
73
|
class Rest
|
|
60
|
-
MIME_JSON = 'application/json'
|
|
61
|
-
MIME_WWW = 'application/x-www-form-urlencoded'
|
|
62
|
-
MIME_TEXT = 'text/plain'
|
|
63
|
-
|
|
64
74
|
# Special query parameter: max number of items for list command
|
|
65
75
|
MAX_ITEMS = 'max'
|
|
66
76
|
# Special query parameter: max number of pages for list command
|
|
@@ -143,8 +153,8 @@ module Aspera
|
|
|
143
153
|
end
|
|
144
154
|
|
|
145
155
|
# Start a HTTP/S session, also used for web sockets
|
|
146
|
-
# @param base_url [String]
|
|
147
|
-
# @return [Net::HTTP]
|
|
156
|
+
# @param base_url [String] Base url of HTTP/S session
|
|
157
|
+
# @return [Net::HTTP] A started HTTP session
|
|
148
158
|
def start_http_session(base_url)
|
|
149
159
|
uri = URI.parse(base_url)
|
|
150
160
|
Aspera.assert_values(uri.scheme, %w[http https]){'URI scheme'}
|
|
@@ -302,16 +312,16 @@ module Aspera
|
|
|
302
312
|
# HTTP/S REST call
|
|
303
313
|
# @param operation [String] HTTP operation (GET, POST, PUT, DELETE)
|
|
304
314
|
# @param subpath [String] subpath of REST API
|
|
305
|
-
# @param query [Hash] URL parameters
|
|
315
|
+
# @param query [Hash{String,Symbol => Object}] URL parameters
|
|
306
316
|
# @param content_type [String, nil] Type of body parameters (one of MIME_*) and serialization, else use headers
|
|
307
|
-
# @param body
|
|
308
|
-
# @param headers [Hash]
|
|
309
|
-
# @param save_to_file [String, nil]
|
|
310
|
-
# @param exception [Boolean]
|
|
311
|
-
# @param ret
|
|
312
|
-
# @return [(HTTPResponse
|
|
313
|
-
# @return [HTTPResponse]
|
|
314
|
-
# @return [Hash]
|
|
317
|
+
# @param body [Hash, String, nil] Body parameters
|
|
318
|
+
# @param headers [Hash{String => String}] Additional headers (override Content-Type)
|
|
319
|
+
# @param save_to_file [String, nil] File path to save response body
|
|
320
|
+
# @param exception [Boolean] Whether to raise an exception on HTTP error
|
|
321
|
+
# @param ret [Symbol] One of :data, :resp, :both — controls return value
|
|
322
|
+
# @return [Array(Hash, Net::HTTPResponse)] When `ret` is :both
|
|
323
|
+
# @return [Net::HTTPResponse] When `ret` is :resp
|
|
324
|
+
# @return [Hash] When `ret` is :data
|
|
315
325
|
# @raise [RestCallError] on error if `exception` is true
|
|
316
326
|
def call(
|
|
317
327
|
operation:,
|
|
@@ -373,15 +383,15 @@ module Aspera
|
|
|
373
383
|
end
|
|
374
384
|
case content_type
|
|
375
385
|
when nil # ignore
|
|
376
|
-
when
|
|
386
|
+
when Mime::JSON
|
|
377
387
|
req.body = JSON.generate(body) # , ascii_only: true
|
|
378
|
-
req['Content-Type'] =
|
|
379
|
-
when
|
|
388
|
+
req['Content-Type'] = Mime::JSON
|
|
389
|
+
when Mime::WWW
|
|
380
390
|
req.body = URI.encode_www_form(body)
|
|
381
|
-
req['Content-Type'] =
|
|
382
|
-
when
|
|
391
|
+
req['Content-Type'] = Mime::WWW
|
|
392
|
+
when Mime::TEXT
|
|
383
393
|
req.body = body
|
|
384
|
-
req['Content-Type'] =
|
|
394
|
+
req['Content-Type'] = Mime::TEXT
|
|
385
395
|
else Aspera.error_unexpected_value(content_type){'body type'}
|
|
386
396
|
end
|
|
387
397
|
# set headers
|
|
@@ -401,12 +411,12 @@ module Aspera
|
|
|
401
411
|
# make http request (pipelined)
|
|
402
412
|
http_session.request(req) do |response|
|
|
403
413
|
result_http = response
|
|
404
|
-
result_mime = self.class.parse_header(result_http['Content-Type'] ||
|
|
414
|
+
result_mime = self.class.parse_header(result_http['Content-Type'] || Mime::TEXT)[:type]
|
|
405
415
|
Log.log.debug{"response: code=#{result_http.code}, mime=#{result_mime}, mime2= #{response['Content-Type']}"}
|
|
406
416
|
# JSON data needs to be parsed, in case it contains an error code
|
|
407
417
|
if !save_to_file.nil? &&
|
|
408
418
|
result_http.code.to_s.start_with?('2') &&
|
|
409
|
-
!
|
|
419
|
+
!Mime.json?(result_mime)
|
|
410
420
|
total_size = result_http['Content-Length']&.to_i
|
|
411
421
|
Log.log.debug('before write file')
|
|
412
422
|
target_file = save_to_file
|
|
@@ -439,14 +449,14 @@ module Aspera
|
|
|
439
449
|
end
|
|
440
450
|
end
|
|
441
451
|
Log.log.debug{"result: code=#{result_http.code} mime=#{result_mime}"}
|
|
442
|
-
# sometimes there is a UTF8 char (e.g.
|
|
452
|
+
# sometimes there is a UTF8 char (e.g. © )
|
|
453
|
+
# TODO : related to mime type encoding ?
|
|
443
454
|
# result_http.body.force_encoding('UTF-8') if result_http.body.is_a?(String)
|
|
444
455
|
# Log.log.debug{"result: body=#{result_http.body}"}
|
|
445
|
-
|
|
446
|
-
when *JSON_DECODE
|
|
456
|
+
if Mime.json?(result_mime)
|
|
447
457
|
result_data = JSON.parse(result_http.body) rescue result_http.body
|
|
448
458
|
Log.dump(:result_data, result_data)
|
|
449
|
-
else #
|
|
459
|
+
else # Mime::TEXT
|
|
450
460
|
result_data = result_http.body
|
|
451
461
|
end
|
|
452
462
|
RestErrorAnalyzer.instance.raise_on_error(req, result_data, result_http)
|
|
@@ -526,27 +536,27 @@ module Aspera
|
|
|
526
536
|
|
|
527
537
|
# Create: `POST`
|
|
528
538
|
def create(subpath, params, **kwargs)
|
|
529
|
-
return call(operation: 'POST', subpath: subpath, headers: {'Accept' =>
|
|
539
|
+
return call(operation: 'POST', subpath: subpath, headers: {'Accept' => Mime::JSON}, body: params, content_type: Mime::JSON, **kwargs)
|
|
530
540
|
end
|
|
531
541
|
|
|
532
542
|
# Read: `GET`
|
|
533
543
|
def read(subpath, query = nil, **kwargs)
|
|
534
|
-
return call(operation: 'GET', subpath: subpath, headers: {'Accept' =>
|
|
544
|
+
return call(operation: 'GET', subpath: subpath, headers: {'Accept' => Mime::JSON}, query: query, **kwargs)
|
|
535
545
|
end
|
|
536
546
|
|
|
537
547
|
# Update: `PUT`
|
|
538
548
|
def update(subpath, params, **kwargs)
|
|
539
|
-
return call(operation: 'PUT', subpath: subpath, headers: {'Accept' =>
|
|
549
|
+
return call(operation: 'PUT', subpath: subpath, headers: {'Accept' => Mime::JSON}, body: params, content_type: Mime::JSON, **kwargs)
|
|
540
550
|
end
|
|
541
551
|
|
|
542
552
|
# Delete: `DELETE`
|
|
543
553
|
def delete(subpath, params = nil, **kwargs)
|
|
544
|
-
return call(operation: 'DELETE', subpath: subpath, headers: {'Accept' =>
|
|
554
|
+
return call(operation: 'DELETE', subpath: subpath, headers: {'Accept' => Mime::JSON}, query: params, **kwargs)
|
|
545
555
|
end
|
|
546
556
|
|
|
547
557
|
# Cancel: `CANCEL`
|
|
548
558
|
def cancel(subpath, **kwargs)
|
|
549
|
-
return call(operation: 'CANCEL', subpath: subpath, headers: {'Accept' =>
|
|
559
|
+
return call(operation: 'CANCEL', subpath: subpath, headers: {'Accept' => Mime::JSON}, **kwargs)
|
|
550
560
|
end
|
|
551
561
|
|
|
552
562
|
# Query entity by general search (read with parameter `q`)
|
|
@@ -577,11 +587,8 @@ module Aspera
|
|
|
577
587
|
end
|
|
578
588
|
end
|
|
579
589
|
|
|
580
|
-
# Content-Type that are JSON
|
|
581
|
-
JSON_DECODE = [MIME_JSON, 'application/vnd.api+json', 'application/x-javascript'].freeze
|
|
582
|
-
|
|
583
590
|
UNAVAILABLE_CODES = ['503']
|
|
584
591
|
|
|
585
|
-
private_constant :
|
|
592
|
+
private_constant :UNAVAILABLE_CODES
|
|
586
593
|
end
|
|
587
594
|
end
|
|
@@ -3,15 +3,23 @@
|
|
|
3
3
|
module Aspera
|
|
4
4
|
# raised on error after REST call
|
|
5
5
|
class RestCallError < StandardError
|
|
6
|
-
|
|
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
|
|
7
17
|
|
|
8
|
-
# @param
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@
|
|
13
|
-
@response = resp
|
|
14
|
-
super(msg)
|
|
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"))
|
|
15
23
|
end
|
|
16
24
|
end
|
|
17
25
|
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require 'aspera/rest_call_error'
|
|
4
4
|
require 'aspera/log'
|
|
5
5
|
require 'singleton'
|
|
6
|
+
require 'net/http'
|
|
6
7
|
|
|
7
8
|
module Aspera
|
|
8
9
|
# analyze error codes returned by REST calls and raise ruby exception
|
|
@@ -16,10 +17,10 @@ module Aspera
|
|
|
16
17
|
# list of handlers
|
|
17
18
|
@error_handlers = []
|
|
18
19
|
@log_file = nil
|
|
19
|
-
add_handler('Type Generic') do |type,
|
|
20
|
-
if !
|
|
20
|
+
add_handler('Type Generic') do |type, context|
|
|
21
|
+
if !context[:response].code.start_with?('2')
|
|
21
22
|
# add generic information
|
|
22
|
-
RestErrorAnalyzer.add_error(
|
|
23
|
+
RestErrorAnalyzer.add_error(context, type, "#{context[:request]['host']} #{context[:response].code} #{context[:response].message}")
|
|
23
24
|
end
|
|
24
25
|
end
|
|
25
26
|
end
|
|
@@ -27,9 +28,12 @@ module Aspera
|
|
|
27
28
|
# Use this method to analyze a EST result and raise an exception
|
|
28
29
|
# Analyzes REST call response and raises a RestCallError exception
|
|
29
30
|
# if HTTP result code is not 2XX
|
|
31
|
+
# @param req [Net::HTTPRequest]
|
|
32
|
+
# @param data [Object]
|
|
33
|
+
# @param http [Net::HTTPResponse]
|
|
30
34
|
def raise_on_error(req, data, http)
|
|
31
35
|
Log.log.debug{"raise_on_error #{req.method} #{req.path} #{http.code}"}
|
|
32
|
-
|
|
36
|
+
context = {
|
|
33
37
|
messages: [],
|
|
34
38
|
request: req,
|
|
35
39
|
response: http,
|
|
@@ -39,44 +43,42 @@ module Aspera
|
|
|
39
43
|
# analyze errors from provided handlers
|
|
40
44
|
# note that there can be an error even if code is 2XX
|
|
41
45
|
@error_handlers.each do |handler|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
rescue StandardError => e
|
|
46
|
-
Log.log.error{"ERROR in handler:\n#{e.message}\n#{e.backtrace}"}
|
|
47
|
-
end
|
|
46
|
+
handler[:block].call(handler[:name], context)
|
|
47
|
+
rescue StandardError => e
|
|
48
|
+
Log.log.error{"ERROR in handler:\n#{e.message}\n#{e.backtrace}"}
|
|
48
49
|
end
|
|
49
|
-
raise RestCallError
|
|
50
|
+
raise RestCallError, context unless context[:messages].empty?
|
|
50
51
|
end
|
|
51
52
|
|
|
52
|
-
#
|
|
53
|
-
# @param name
|
|
54
|
-
# @param block
|
|
53
|
+
# Add a new error handler (done at application initialization)
|
|
54
|
+
# @param name [String] name of error handler (for logs)
|
|
55
|
+
# @param block [Proc] processing of response: takes two parameters: `name`, `context`
|
|
55
56
|
# name is the one provided here
|
|
56
|
-
#
|
|
57
|
+
# context is built in method raise_on_error
|
|
57
58
|
def add_handler(name, &block)
|
|
58
59
|
@error_handlers.unshift({name: name, block: block})
|
|
60
|
+
nil
|
|
59
61
|
end
|
|
60
62
|
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
# @param name
|
|
65
|
-
# @param always [
|
|
66
|
-
# @param path
|
|
63
|
+
# Add a simple error handler
|
|
64
|
+
# Check that key exists and is string under specified path (hash)
|
|
65
|
+
# Adds other keys as secondary information
|
|
66
|
+
# @param name [String] name of error handler (for logs)
|
|
67
|
+
# @param always [Boolean] if true, always add error message, even if response code is 2XX
|
|
68
|
+
# @param path [Array] path to error message in response
|
|
67
69
|
def add_simple_handler(name:, always: false, path:)
|
|
68
70
|
path.freeze
|
|
69
|
-
add_handler(name) do |type,
|
|
70
|
-
if
|
|
71
|
+
add_handler(name) do |type, context|
|
|
72
|
+
if context[:data].is_a?(Hash) && (!context[:response].code.start_with?('2') || always)
|
|
71
73
|
# Log.log.debug{"simple_handler: #{type} #{path} #{path.last}"}
|
|
72
74
|
# dig and find hash containing error message
|
|
73
|
-
error_struct = path.length.eql?(1) ?
|
|
75
|
+
error_struct = path.length.eql?(1) ? context[:data] : context[:data].dig(*path[0..-2])
|
|
74
76
|
# Log.log.debug{"found: #{error_struct.class} #{error_struct}"}
|
|
75
77
|
if error_struct.is_a?(Hash) && error_struct[path.last].is_a?(String)
|
|
76
|
-
RestErrorAnalyzer.add_error(
|
|
78
|
+
RestErrorAnalyzer.add_error(context, type, error_struct[path.last])
|
|
77
79
|
error_struct.each do |k, v|
|
|
78
80
|
next if k.eql?(path.last)
|
|
79
|
-
RestErrorAnalyzer.add_error(
|
|
81
|
+
RestErrorAnalyzer.add_error(context, "#{type}(sub)", "#{k}: #{v}") if [String, Integer].include?(v.class)
|
|
80
82
|
end
|
|
81
83
|
end
|
|
82
84
|
end
|
|
@@ -84,20 +86,20 @@ module Aspera
|
|
|
84
86
|
end
|
|
85
87
|
|
|
86
88
|
class << self
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
# @param
|
|
90
|
-
# @param type
|
|
91
|
-
# @param
|
|
92
|
-
def add_error(
|
|
93
|
-
|
|
94
|
-
Log.log.trace1{"Found error: #{type}: #{
|
|
89
|
+
# Used by handler to add an error description to list of errors
|
|
90
|
+
# For logging and tracing : collect error descriptions (create file to activate)
|
|
91
|
+
# @param context [Hash] the result context, provided to handler
|
|
92
|
+
# @param type [String] type of exception, for logging purpose
|
|
93
|
+
# @param message [String] one error message to add to list
|
|
94
|
+
def add_error(context, type, message)
|
|
95
|
+
context[:messages].push(message)
|
|
96
|
+
Log.log.trace1{"Found error: #{type}: #{message}"}
|
|
95
97
|
log_file = instance.log_file
|
|
96
98
|
# log error for further analysis (file must exist to activate)
|
|
97
99
|
return if log_file.nil? || !File.exist?(log_file)
|
|
98
100
|
File.open(log_file, 'a+') do |f|
|
|
99
|
-
f.write("\n=#{type}=====\n#{
|
|
100
|
-
"#{JSON.generate(
|
|
101
|
+
f.write("\n=#{type}=====\n#{context[:request].method} #{context[:request].path}\n#{context[:response].code}\n" \
|
|
102
|
+
"#{JSON.generate(context[:data])}\n#{context[:messages].join("\n")}")
|
|
101
103
|
end
|
|
102
104
|
end
|
|
103
105
|
end
|
|
@@ -17,48 +17,49 @@ module Aspera
|
|
|
17
17
|
RestErrorAnalyzer.instance.add_simple_handler(name: 'Type 3: error:internal_message', path: %w[error internal_message])
|
|
18
18
|
RestErrorAnalyzer.instance.add_simple_handler(name: 'Type 5', path: ['error_description'])
|
|
19
19
|
RestErrorAnalyzer.instance.add_simple_handler(name: 'Type 6', path: ['message'])
|
|
20
|
+
RestErrorAnalyzer.instance.add_simple_handler(name: 'Type 6', path: ['failure'], always: true)
|
|
20
21
|
# AoC Automation
|
|
21
22
|
RestErrorAnalyzer.instance.add_simple_handler(name: 'AoC Automation', path: ['error'])
|
|
22
|
-
RestErrorAnalyzer.instance.add_handler('Type 7: errors[]') do |type,
|
|
23
|
-
next unless
|
|
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)
|
|
24
25
|
# special for Shares: false positive ? (update global transfer_settings)
|
|
25
|
-
next if
|
|
26
|
-
|
|
27
|
-
RestErrorAnalyzer.add_error(
|
|
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}")
|
|
28
29
|
end
|
|
29
30
|
end
|
|
30
31
|
# call to upload_setup and download_setup of node api
|
|
31
|
-
RestErrorAnalyzer.instance.add_handler('T8:node: *_setup') do |type,
|
|
32
|
-
next unless
|
|
33
|
-
d_t_s =
|
|
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']
|
|
34
35
|
next unless d_t_s.is_a?(Array)
|
|
35
36
|
d_t_s.each do |res|
|
|
36
37
|
r_err = res.dig(*%w[transfer_spec error]) || res['error']
|
|
37
38
|
next unless r_err.is_a?(Hash)
|
|
38
|
-
RestErrorAnalyzer.add_error(
|
|
39
|
+
RestErrorAnalyzer.add_error(context, type, r_err.values.join(': '))
|
|
39
40
|
end
|
|
40
41
|
end
|
|
41
42
|
RestErrorAnalyzer.instance.add_simple_handler(name: 'T9:IBM cloud IAM', path: ['errorMessage'])
|
|
42
43
|
RestErrorAnalyzer.instance.add_simple_handler(name: 'T10:faspex v4', path: ['user_message'])
|
|
43
|
-
RestErrorAnalyzer.instance.add_handler('bss graphql') do |type,
|
|
44
|
-
next unless
|
|
45
|
-
d_t_s =
|
|
44
|
+
RestErrorAnalyzer.instance.add_handler('bss graphql') do |type, context|
|
|
45
|
+
next unless context[:data].is_a?(Hash)
|
|
46
|
+
d_t_s = context[:data]['errors']
|
|
46
47
|
next unless d_t_s.is_a?(Array)
|
|
47
48
|
d_t_s.each do |res|
|
|
48
49
|
r_err = res['message']
|
|
49
50
|
next unless r_err.is_a?(String)
|
|
50
|
-
RestErrorAnalyzer.add_error(
|
|
51
|
+
RestErrorAnalyzer.add_error(context, type, r_err)
|
|
51
52
|
end
|
|
52
53
|
end
|
|
53
|
-
RestErrorAnalyzer.instance.add_handler('Orchestrator') do |type,
|
|
54
|
-
next if
|
|
55
|
-
data =
|
|
54
|
+
RestErrorAnalyzer.instance.add_handler('Orchestrator') do |type, context|
|
|
55
|
+
next if context[:response].code.start_with?('2')
|
|
56
|
+
data = context[:data]
|
|
56
57
|
next unless data.is_a?(Hash)
|
|
57
58
|
work_order = data['work_order']
|
|
58
59
|
next unless work_order.is_a?(Hash)
|
|
59
|
-
RestErrorAnalyzer.add_error(
|
|
60
|
+
RestErrorAnalyzer.add_error(context, type, work_order['statusDetails'])
|
|
60
61
|
data['missing_parameters']&.each do |param|
|
|
61
|
-
RestErrorAnalyzer.add_error(
|
|
62
|
+
RestErrorAnalyzer.add_error(context, type, "missing parameter: #{param}")
|
|
62
63
|
end
|
|
63
64
|
end
|
|
64
65
|
end
|
|
@@ -41,14 +41,14 @@ module Aspera
|
|
|
41
41
|
Log.log.debug{"retries=#{remaining_resumes}"}
|
|
42
42
|
# try to send the file until ascp is successful
|
|
43
43
|
loop do
|
|
44
|
-
Log.log.debug('
|
|
44
|
+
Log.log.debug('Starting task execution')
|
|
45
45
|
begin
|
|
46
46
|
# Call provided block: execute transfer
|
|
47
47
|
yield
|
|
48
48
|
# Exit retry loop if success
|
|
49
49
|
break
|
|
50
50
|
rescue Error => e
|
|
51
|
-
Log.log.warn{"
|
|
51
|
+
Log.log.warn{"An error occurred during task: #{e.message}"}
|
|
52
52
|
Log.log.debug{"Retryable ? #{e.retryable?}"}
|
|
53
53
|
# do not retry non-retryable
|
|
54
54
|
raise unless e.retryable?
|
data/lib/aspera/yaml.rb
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
|
|
5
|
+
module Aspera
|
|
6
|
+
module Yaml
|
|
7
|
+
# @param node [Psych::Nodes::Node] YAML node
|
|
8
|
+
# @param parent_path [Array<String>] Path of parent keys
|
|
9
|
+
# @param duplicate_keys [Array<Hash>] Accumulated duplicate keys
|
|
10
|
+
# @return [Array<String>] List of duplicate keys with their paths and occurrences
|
|
11
|
+
def find_duplicate_keys(node, parent_path = nil, duplicate_keys = nil)
|
|
12
|
+
duplicate_keys ||= []
|
|
13
|
+
parent_path ||= []
|
|
14
|
+
return duplicate_keys unless node.respond_to?(:children)
|
|
15
|
+
if node.is_a?(Psych::Nodes::Mapping)
|
|
16
|
+
counts = Hash.new(0)
|
|
17
|
+
key_nodes = Hash.new{ |h, k| h[k] = []}
|
|
18
|
+
node.children.each_slice(2) do |key_node, value_node|
|
|
19
|
+
if key_node&.value
|
|
20
|
+
counts[key_node.value] += 1
|
|
21
|
+
key_nodes[key_node.value] << key_node
|
|
22
|
+
find_duplicate_keys(value_node, parent_path + [key_node.value], duplicate_keys)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
counts.each do |key_str, count|
|
|
26
|
+
next if count <= 1
|
|
27
|
+
path = (parent_path + [key_str]).join('.')
|
|
28
|
+
occurrences = key_nodes[key_str].map{ |kn| kn.start_line ? kn.start_line + 1 : 'unknown'}.map(&:to_s).join(', ')
|
|
29
|
+
duplicate_keys << "#{path}: #{occurrences}"
|
|
30
|
+
end
|
|
31
|
+
else
|
|
32
|
+
node.children.to_a.each{ |child| find_duplicate_keys(child, parent_path, duplicate_keys)}
|
|
33
|
+
end
|
|
34
|
+
duplicate_keys
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Safely load YAML content, raising an error if duplicate keys are found
|
|
38
|
+
# @param yaml [String] YAML content
|
|
39
|
+
# @return [Object] Parsed YAML content
|
|
40
|
+
# @raise [RuntimeError] If duplicate keys are found
|
|
41
|
+
def safe_load(yaml)
|
|
42
|
+
duplicate_keys = find_duplicate_keys(Psych.parse_stream(yaml))
|
|
43
|
+
raise "Duplicate keys: #{duplicate_keys.join('; ')}" unless duplicate_keys.empty?
|
|
44
|
+
YAML.safe_load(yaml)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
module_function :find_duplicate_keys, :safe_load
|
|
48
|
+
end
|
|
49
|
+
end
|
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.25.
|
|
4
|
+
version: 4.25.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Laurent Martin
|
|
@@ -108,6 +108,20 @@ dependencies:
|
|
|
108
108
|
- - "~>"
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
110
|
version: '3.0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: marcel
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - "~>"
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '1.1'
|
|
118
|
+
type: :runtime
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - "~>"
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '1.1'
|
|
111
125
|
- !ruby/object:Gem::Dependency
|
|
112
126
|
name: mime-types
|
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -430,9 +444,9 @@ files:
|
|
|
430
444
|
- lib/aspera/uri_reader.rb
|
|
431
445
|
- lib/aspera/web_auth.rb
|
|
432
446
|
- lib/aspera/web_server_simple.rb
|
|
447
|
+
- lib/aspera/yaml.rb
|
|
433
448
|
- lib/transferd_pb.rb
|
|
434
449
|
- lib/transferd_services_pb.rb
|
|
435
|
-
- release_notes.md
|
|
436
450
|
homepage: https://github.com/IBM/aspera-cli
|
|
437
451
|
licenses:
|
|
438
452
|
- Apache-2.0
|
|
@@ -440,7 +454,7 @@ metadata:
|
|
|
440
454
|
allowed_push_host: https://rubygems.org
|
|
441
455
|
homepage_uri: https://github.com/IBM/aspera-cli
|
|
442
456
|
source_code_uri: https://github.com/IBM/aspera-cli/tree/main/lib/aspera
|
|
443
|
-
changelog_uri: https://github.com/IBM/aspera-cli/CHANGELOG.md
|
|
457
|
+
changelog_uri: https://github.com/IBM/aspera-cli/blob/main/CHANGELOG.md
|
|
444
458
|
rubygems_uri: https://rubygems.org/gems/aspera-cli
|
|
445
459
|
documentation_uri: https://www.rubydoc.info/gems/aspera-cli
|
|
446
460
|
rdoc_options: []
|
metadata.gz.sig
CHANGED
|
Binary file
|