aspera-cli 4.27.2 → 4.27.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 +50 -0
- data/bin/ascli +2 -1
- data/docs/README.md +804 -746
- data/lib/aspera/agent/connect.rb +6 -4
- data/lib/aspera/agent/desktop.rb +2 -2
- data/lib/aspera/agent/direct.rb +3 -1
- data/lib/aspera/agent/node.rb +3 -3
- data/lib/aspera/api/alee.rb +1 -1
- data/lib/aspera/api/aoc.rb +14 -12
- data/lib/aspera/api/ats.rb +1 -1
- data/lib/aspera/api/cos_node.rb +2 -2
- data/lib/aspera/api/faspex.rb +9 -7
- data/lib/aspera/api/httpgw.rb +37 -33
- data/lib/aspera/api/node.rb +38 -33
- data/lib/aspera/ascmd.rb +3 -1
- data/lib/aspera/ascp/installation.rb +62 -27
- data/lib/aspera/ascp/management.rb +1 -0
- data/lib/aspera/assert.rb +4 -0
- data/lib/aspera/cli/ascp_actions.rb +20 -41
- data/lib/aspera/cli/async_transfer_store.rb +2 -2
- data/lib/aspera/cli/bootstrapper.rb +11 -15
- data/lib/aspera/cli/command_line.rb +252 -0
- data/lib/aspera/cli/command_registry.rb +149 -33
- data/lib/aspera/cli/command_spec.rb +103 -14
- data/lib/aspera/cli/completion/ascli.bash +12 -0
- data/lib/aspera/cli/completion/ascli.fish +16 -0
- data/lib/aspera/cli/completion/ascli.zsh +19 -0
- data/lib/aspera/cli/context.rb +3 -0
- data/lib/aspera/cli/deprecation.rb +37 -0
- data/lib/aspera/cli/extended_value.rb +2 -0
- data/lib/aspera/cli/formatter.rb +87 -75
- data/lib/aspera/cli/gem_checker.rb +1 -1
- data/lib/aspera/cli/hints.rb +7 -6
- data/lib/aspera/cli/http.rb +21 -21
- data/lib/aspera/cli/info.rb +3 -0
- data/lib/aspera/cli/mcp_tool.rb +47 -83
- data/lib/aspera/cli/option_declarator.rb +33 -42
- data/lib/aspera/cli/option_registry.rb +69 -0
- data/lib/aspera/cli/option_types.rb +103 -0
- data/lib/aspera/cli/option_value.rb +281 -0
- data/lib/aspera/cli/options.schema.yaml +38 -5
- data/lib/aspera/cli/parser.rb +307 -848
- data/lib/aspera/cli/plugins/alee.rb +7 -4
- data/lib/aspera/cli/plugins/aoc.rb +430 -380
- data/lib/aspera/cli/plugins/ats.rb +58 -73
- data/lib/aspera/cli/plugins/base.rb +190 -240
- data/lib/aspera/cli/plugins/basic_auth.rb +2 -10
- data/lib/aspera/cli/plugins/config.rb +244 -178
- data/lib/aspera/cli/plugins/console.rb +102 -38
- data/lib/aspera/cli/plugins/cos.rb +6 -23
- data/lib/aspera/cli/plugins/factory.rb +3 -0
- data/lib/aspera/cli/plugins/faspex5.rb +176 -173
- data/lib/aspera/cli/plugins/faspio.rb +5 -10
- data/lib/aspera/cli/plugins/httpgw.rb +8 -11
- data/lib/aspera/cli/plugins/mcp.rb +20 -55
- data/lib/aspera/cli/plugins/node.rb +277 -311
- data/lib/aspera/cli/plugins/orchestrator.rb +90 -77
- data/lib/aspera/cli/plugins/preview.rb +79 -90
- data/lib/aspera/cli/plugins/server.rb +76 -50
- data/lib/aspera/cli/plugins/shares.rb +68 -116
- data/lib/aspera/cli/preset_actions.rb +17 -10
- data/lib/aspera/cli/preset_manager.rb +12 -2
- data/lib/aspera/cli/prompt.rb +35 -0
- data/lib/aspera/cli/result.rb +13 -18
- data/lib/aspera/cli/runner.rb +31 -54
- data/lib/aspera/cli/special_values.rb +5 -0
- data/lib/aspera/cli/sync_actions.rb +41 -37
- data/lib/aspera/cli/terminal_formatter.rb +9 -3
- data/lib/aspera/cli/transfer_actions.rb +0 -6
- data/lib/aspera/cli/transfer_agent.rb +29 -35
- data/lib/aspera/cli/vault_manager.rb +0 -17
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +4 -2
- data/lib/aspera/coverage.rb +1 -0
- data/lib/aspera/environment.rb +6 -0
- data/lib/aspera/faspex_gw.rb +2 -1
- data/lib/aspera/faspex_postproc.rb +1 -0
- data/lib/aspera/graphql.rb +5 -5
- data/lib/aspera/json_rpc/client.rb +5 -5
- data/lib/aspera/keychain/encrypted_hash.rb +1 -1
- data/lib/aspera/keychain/one_password_api.rb +1 -1
- data/lib/aspera/link_header.rb +2 -2
- data/lib/aspera/log.rb +22 -25
- data/lib/aspera/markdown.rb +2 -0
- data/lib/aspera/mime.rb +25 -0
- data/lib/aspera/node_simulator.rb +1 -0
- data/lib/aspera/oauth/base.rb +35 -25
- data/lib/aspera/oauth/factory.rb +1 -0
- data/lib/aspera/oauth/generic.rb +1 -1
- data/lib/aspera/oauth/jwt.rb +1 -1
- data/lib/aspera/oauth/web.rb +9 -8
- data/lib/aspera/preview/file_types.rb +4 -4
- data/lib/aspera/preview/generator.rb +7 -0
- data/lib/aspera/preview/options.rb +4 -4
- data/lib/aspera/preview/terminal.rb +4 -3
- data/lib/aspera/preview/utils.rb +9 -6
- data/lib/aspera/products/connect.rb +1 -1
- data/lib/aspera/rainbow.rb +7 -0
- data/lib/aspera/rest/aspera_errors.rb +60 -0
- data/lib/aspera/rest/call_error.rb +27 -0
- data/lib/aspera/rest/client.rb +514 -0
- data/lib/aspera/rest/error_analyzer.rb +113 -0
- data/lib/aspera/rest/list.rb +143 -0
- data/lib/aspera/rest/parameters.rb +55 -0
- data/lib/aspera/rest/util.rb +176 -0
- data/lib/aspera/rest.rb +7 -621
- data/lib/aspera/schema/IBM Aspera Console-enhanced.yaml +1125 -0
- data/lib/aspera/schema/IBM Aspera on Cloud Automation API-1.0.5-enhanced.yaml +2395 -0
- data/lib/aspera/schema/documentation.rb +13 -3
- data/lib/aspera/schema/registry.rb +18 -1
- data/lib/aspera/schema/validator.rb +92 -0
- data/lib/aspera/secret_hider.rb +36 -25
- data/lib/aspera/string_ext.rb +15 -0
- data/lib/aspera/temp_file_manager.rb +6 -5
- data/lib/aspera/transfer/parameters.rb +2 -0
- data/lib/aspera/transfer/spec.rb +1 -0
- data/lib/aspera/uri_reader.rb +11 -11
- data/lib/aspera/web_auth/index.html +147 -0
- data/lib/aspera/web_auth/server.rb +81 -0
- data.tar.gz.sig +0 -0
- metadata +39 -7
- metadata.gz.sig +0 -0
- data/lib/aspera/colors.rb +0 -79
- data/lib/aspera/rest_call_error.rb +0 -25
- data/lib/aspera/rest_error_analyzer.rb +0 -111
- data/lib/aspera/rest_errors_aspera.rb +0 -58
- data/lib/aspera/rest_list.rb +0 -136
- data/lib/aspera/web_auth.rb +0 -211
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'aspera/web_server_simple'
|
|
4
|
+
require 'aspera/assert'
|
|
5
|
+
require 'erb'
|
|
6
|
+
|
|
7
|
+
module Aspera
|
|
8
|
+
module WebAuth
|
|
9
|
+
# servlet called on callback: it records the callback request
|
|
10
|
+
class Servlet < WEBrick::HTTPServlet::AbstractServlet
|
|
11
|
+
# @param server [WEBrick::HTTPServer] the HTTP server instance
|
|
12
|
+
# @param web_auth [Server] the Server instance to record the callback
|
|
13
|
+
def initialize(server, web_auth)
|
|
14
|
+
Log.log.debug('Servlet initialize')
|
|
15
|
+
super(server)
|
|
16
|
+
@web_auth = web_auth
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# called on request
|
|
20
|
+
|
|
21
|
+
def service(request, response)
|
|
22
|
+
Log.log.debug { "received request from browser #{request.request_method} #{request.path}" }
|
|
23
|
+
Aspera.assert_values(request.request_method, ['GET'], type: WEBrick::HTTPStatus::MethodNotAllowed) { "Unsupported method: #{req.request_method}" }
|
|
24
|
+
Aspera.assert_values(request.path, ['/'], type: WEBrick::HTTPStatus::NotFound) { "Unsupported path: #{req.request_method}" }
|
|
25
|
+
# response to browser
|
|
26
|
+
response.status = 200
|
|
27
|
+
response.content_type = 'text/html'
|
|
28
|
+
response.body = File.read("#{__dir__}/index.html").gsub('__ADD_INFO__', @web_auth.info_html)
|
|
29
|
+
# continue internal processing
|
|
30
|
+
@web_auth.signal_request(request)
|
|
31
|
+
nil
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# start a local web server
|
|
36
|
+
# then start a browser that will callback the local server upon authentication
|
|
37
|
+
# store the final query
|
|
38
|
+
class Server < WebServerSimple
|
|
39
|
+
attr_reader :info_html
|
|
40
|
+
|
|
41
|
+
# @param endpoint_url [String] e.g. 'https://127.0.0.1:12345'
|
|
42
|
+
# @param additional_info [String] Information in web page
|
|
43
|
+
def initialize(endpoint_url, additional_info = nil)
|
|
44
|
+
uri = URI.parse(endpoint_url)
|
|
45
|
+
super(uri)
|
|
46
|
+
@mutex = Mutex.new
|
|
47
|
+
@cond = ConditionVariable.new
|
|
48
|
+
@expected_path = uri.path.empty? ? '/' : uri.path
|
|
49
|
+
@query = nil
|
|
50
|
+
@info_html = additional_info.to_s
|
|
51
|
+
@info_html = "<p>#{ERB::Util.html_escape(@info_html)}</p>" unless @info_html.empty?
|
|
52
|
+
# last argument (self) is provided to constructor of servlet
|
|
53
|
+
mount(@expected_path, Servlet, self)
|
|
54
|
+
# server runs in thread
|
|
55
|
+
Thread.new { start }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Called by web server thread on received request
|
|
59
|
+
# @return [nil]
|
|
60
|
+
def signal_request(request)
|
|
61
|
+
raise WEBrick::HTTPStatus::NotFound, "unexpected path: #{request.path}" unless request.path.eql?(@expected_path)
|
|
62
|
+
# acquire lock and signal change
|
|
63
|
+
@mutex.synchronize do
|
|
64
|
+
@query = request.query
|
|
65
|
+
@cond.signal
|
|
66
|
+
end
|
|
67
|
+
nil
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# wait for request on web server (main thread)
|
|
71
|
+
# @return [Hash] the query
|
|
72
|
+
def received_request
|
|
73
|
+
# wait for signal from thread
|
|
74
|
+
@mutex.synchronize { @cond.wait(@mutex) }
|
|
75
|
+
# tell server thread to stop
|
|
76
|
+
shutdown
|
|
77
|
+
return @query
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
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.27.
|
|
4
|
+
version: 4.27.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Laurent Martin
|
|
@@ -94,6 +94,20 @@ dependencies:
|
|
|
94
94
|
- - "~>"
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
96
|
version: '2.0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: json_schemer
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - "~>"
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '2.4'
|
|
104
|
+
type: :runtime
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - "~>"
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '2.4'
|
|
97
111
|
- !ruby/object:Gem::Dependency
|
|
98
112
|
name: jwt
|
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -346,9 +360,14 @@ files:
|
|
|
346
360
|
- lib/aspera/cli/ascp_actions.rb
|
|
347
361
|
- lib/aspera/cli/async_transfer_store.rb
|
|
348
362
|
- lib/aspera/cli/bootstrapper.rb
|
|
363
|
+
- lib/aspera/cli/command_line.rb
|
|
349
364
|
- lib/aspera/cli/command_registry.rb
|
|
350
365
|
- lib/aspera/cli/command_spec.rb
|
|
366
|
+
- lib/aspera/cli/completion/ascli.bash
|
|
367
|
+
- lib/aspera/cli/completion/ascli.fish
|
|
368
|
+
- lib/aspera/cli/completion/ascli.zsh
|
|
351
369
|
- lib/aspera/cli/context.rb
|
|
370
|
+
- lib/aspera/cli/deprecation.rb
|
|
352
371
|
- lib/aspera/cli/error.rb
|
|
353
372
|
- lib/aspera/cli/extended_value.rb
|
|
354
373
|
- lib/aspera/cli/formatter.rb
|
|
@@ -359,6 +378,9 @@ files:
|
|
|
359
378
|
- lib/aspera/cli/mailer.rb
|
|
360
379
|
- lib/aspera/cli/mcp_tool.rb
|
|
361
380
|
- lib/aspera/cli/option_declarator.rb
|
|
381
|
+
- lib/aspera/cli/option_registry.rb
|
|
382
|
+
- lib/aspera/cli/option_types.rb
|
|
383
|
+
- lib/aspera/cli/option_value.rb
|
|
362
384
|
- lib/aspera/cli/options.schema.yaml
|
|
363
385
|
- lib/aspera/cli/parser.rb
|
|
364
386
|
- lib/aspera/cli/plugins/alee.rb
|
|
@@ -382,6 +404,7 @@ files:
|
|
|
382
404
|
- lib/aspera/cli/plugins/shares.rb
|
|
383
405
|
- lib/aspera/cli/preset_actions.rb
|
|
384
406
|
- lib/aspera/cli/preset_manager.rb
|
|
407
|
+
- lib/aspera/cli/prompt.rb
|
|
385
408
|
- lib/aspera/cli/result.rb
|
|
386
409
|
- lib/aspera/cli/runner.rb
|
|
387
410
|
- lib/aspera/cli/secret_finder.rb
|
|
@@ -394,7 +417,6 @@ files:
|
|
|
394
417
|
- lib/aspera/cli/vault_manager.rb
|
|
395
418
|
- lib/aspera/cli/version.rb
|
|
396
419
|
- lib/aspera/cli/wizard.rb
|
|
397
|
-
- lib/aspera/colors.rb
|
|
398
420
|
- lib/aspera/command_line_builder.rb
|
|
399
421
|
- lib/aspera/command_line_converter.rb
|
|
400
422
|
- lib/aspera/coverage.rb
|
|
@@ -428,6 +450,7 @@ files:
|
|
|
428
450
|
- lib/aspera/link_header.rb
|
|
429
451
|
- lib/aspera/log.rb
|
|
430
452
|
- lib/aspera/markdown.rb
|
|
453
|
+
- lib/aspera/mime.rb
|
|
431
454
|
- lib/aspera/nagios.rb
|
|
432
455
|
- lib/aspera/node_simulator.rb
|
|
433
456
|
- lib/aspera/oauth.rb
|
|
@@ -453,23 +476,31 @@ files:
|
|
|
453
476
|
- lib/aspera/products/transferd.rb
|
|
454
477
|
- lib/aspera/proxy_auto_config.js
|
|
455
478
|
- lib/aspera/proxy_auto_config.rb
|
|
479
|
+
- lib/aspera/rainbow.rb
|
|
456
480
|
- lib/aspera/rest.rb
|
|
457
|
-
- lib/aspera/
|
|
458
|
-
- lib/aspera/
|
|
459
|
-
- lib/aspera/
|
|
460
|
-
- lib/aspera/
|
|
481
|
+
- lib/aspera/rest/aspera_errors.rb
|
|
482
|
+
- lib/aspera/rest/call_error.rb
|
|
483
|
+
- lib/aspera/rest/client.rb
|
|
484
|
+
- lib/aspera/rest/error_analyzer.rb
|
|
485
|
+
- lib/aspera/rest/list.rb
|
|
486
|
+
- lib/aspera/rest/parameters.rb
|
|
487
|
+
- lib/aspera/rest/util.rb
|
|
488
|
+
- lib/aspera/schema/IBM Aspera Console-enhanced.yaml
|
|
461
489
|
- lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml
|
|
462
490
|
- lib/aspera/schema/IBM Aspera Node API-4.4.6.yaml
|
|
463
491
|
- lib/aspera/schema/IBM Aspera faspio Gateway API-1.0.0.yaml
|
|
464
492
|
- lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml
|
|
493
|
+
- lib/aspera/schema/IBM Aspera on Cloud Automation API-1.0.5-enhanced.yaml
|
|
465
494
|
- lib/aspera/schema/IBM_Aspera_Shares.yaml
|
|
466
495
|
- lib/aspera/schema/async_tables.yaml
|
|
467
496
|
- lib/aspera/schema/documentation.rb
|
|
468
497
|
- lib/aspera/schema/reader.rb
|
|
469
498
|
- lib/aspera/schema/registry.rb
|
|
499
|
+
- lib/aspera/schema/validator.rb
|
|
470
500
|
- lib/aspera/secret_hider.rb
|
|
471
501
|
- lib/aspera/ssh.rb
|
|
472
502
|
- lib/aspera/ssl.rb
|
|
503
|
+
- lib/aspera/string_ext.rb
|
|
473
504
|
- lib/aspera/sync/args.schema.yaml
|
|
474
505
|
- lib/aspera/sync/conf.schema.yaml
|
|
475
506
|
- lib/aspera/sync/database.rb
|
|
@@ -484,7 +515,8 @@ files:
|
|
|
484
515
|
- lib/aspera/transfer/spec.rb
|
|
485
516
|
- lib/aspera/transfer/spec.schema.yaml
|
|
486
517
|
- lib/aspera/uri_reader.rb
|
|
487
|
-
- lib/aspera/web_auth.
|
|
518
|
+
- lib/aspera/web_auth/index.html
|
|
519
|
+
- lib/aspera/web_auth/server.rb
|
|
488
520
|
- lib/aspera/web_server_simple.rb
|
|
489
521
|
- lib/aspera/yaml.rb
|
|
490
522
|
- lib/transferd_pb.rb
|
metadata.gz.sig
CHANGED
|
Binary file
|
data/lib/aspera/colors.rb
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# cspell:words
|
|
4
|
-
|
|
5
|
-
# simple vt100 colors
|
|
6
|
-
class String
|
|
7
|
-
class << self
|
|
8
|
-
private
|
|
9
|
-
|
|
10
|
-
def vt_cmd(code); "\e[#{code}m"; end
|
|
11
|
-
end
|
|
12
|
-
# see https://en.wikipedia.org/wiki/ANSI_escape_code
|
|
13
|
-
# symbol is the method name added to String, e.g. "hello".bold
|
|
14
|
-
# it adds control chars to set color (and reset at the end).
|
|
15
|
-
VT_STYLES = {
|
|
16
|
-
bold: 1,
|
|
17
|
-
dim: 2,
|
|
18
|
-
italic: 3,
|
|
19
|
-
underline: 4,
|
|
20
|
-
blink: 5,
|
|
21
|
-
reverse_color: 7,
|
|
22
|
-
invisible: 8,
|
|
23
|
-
strike: 9,
|
|
24
|
-
black: 30,
|
|
25
|
-
red: 31,
|
|
26
|
-
green: 32,
|
|
27
|
-
brown: 33,
|
|
28
|
-
blue: 34,
|
|
29
|
-
magenta: 35,
|
|
30
|
-
cyan: 36,
|
|
31
|
-
gray: 37,
|
|
32
|
-
bg_black: 40,
|
|
33
|
-
bg_red: 41,
|
|
34
|
-
bg_green: 42,
|
|
35
|
-
bg_brown: 43,
|
|
36
|
-
bg_blue: 44,
|
|
37
|
-
bg_magenta: 45,
|
|
38
|
-
bg_cyan: 46,
|
|
39
|
-
bg_gray: 47
|
|
40
|
-
}.freeze
|
|
41
|
-
private_constant :VT_STYLES
|
|
42
|
-
# Defines methods to String, one per entry in VT_STYLES
|
|
43
|
-
VT_STYLES.each do |name, code|
|
|
44
|
-
if $stdout.tty?
|
|
45
|
-
begin_seq = vt_cmd(code)
|
|
46
|
-
end_seq = vt_cmd(
|
|
47
|
-
if code <= 2 then 22
|
|
48
|
-
elsif code <= 8 then code + 20
|
|
49
|
-
elsif code <= 37 then 39
|
|
50
|
-
elsif code <= 47 then 49
|
|
51
|
-
else
|
|
52
|
-
0 # by default reset all
|
|
53
|
-
end
|
|
54
|
-
)
|
|
55
|
-
define_method(name) { "#{begin_seq}#{self}#{end_seq}" }
|
|
56
|
-
else
|
|
57
|
-
define_method(name) { self }
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# Applies the provided list of string decoration (colors).
|
|
62
|
-
# @param colors [Array<Symbol>] List of decorations.
|
|
63
|
-
# @return [String] Enhanced String.
|
|
64
|
-
def apply(*colors)
|
|
65
|
-
colors.reduce(self) { |s, c| s.public_send(c) }
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Transform capitalized to snake case
|
|
69
|
-
def capital_to_snake
|
|
70
|
-
return gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
71
|
-
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
72
|
-
.downcase
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# Transform snake case to capitalized
|
|
76
|
-
def snake_to_capital
|
|
77
|
-
split('_').map(&:capitalize).join
|
|
78
|
-
end
|
|
79
|
-
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
|
data/lib/aspera/rest_list.rb
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Aspera
|
|
4
|
-
# List and lookup methods for Rest
|
|
5
|
-
# To be included in classes inheriting Rest that require those methods.
|
|
6
|
-
module RestList
|
|
7
|
-
# `max`: special query parameter: max number of items for list command
|
|
8
|
-
MAX_ITEMS = 'max'
|
|
9
|
-
# `pmax`: special query parameter: max number of pages for list command
|
|
10
|
-
MAX_PAGES = 'pmax'
|
|
11
|
-
|
|
12
|
-
# Query entity by general search (read with parameter `q`)
|
|
13
|
-
#
|
|
14
|
-
# @param entity [String] Path of entity in API
|
|
15
|
-
# @param value [String] Value of field of searched entity
|
|
16
|
-
# @param field [String] Field of searched entity
|
|
17
|
-
# @param query [Hash] Optional additional search query parameters
|
|
18
|
-
# @return [Hash] A single entity matching the search, or an exception if not found or multiple found
|
|
19
|
-
def lookup_with_q(entity, value:, field: 'name', query: {})
|
|
20
|
-
Aspera.assert_type(query, Hash) { 'query' }
|
|
21
|
-
Aspera.assert_type(field, String) { 'field' }
|
|
22
|
-
# returns entities matching the query (it matches against several fields in case insensitive way)
|
|
23
|
-
# We don't do paging, as anyway, we look for only one match
|
|
24
|
-
matching_items = read(entity, query.merge({'q' => value}))
|
|
25
|
-
# API style: {totalcount:, ...} cspell: disable-line: TODO: is that total_count ?
|
|
26
|
-
# @type [Array<Hash{String => String}>]
|
|
27
|
-
matching_items = matching_items[entity] if matching_items.is_a?(Hash)
|
|
28
|
-
Aspera.assert_type(matching_items, Array)
|
|
29
|
-
case matching_items.length
|
|
30
|
-
when 1 then return matching_items.first
|
|
31
|
-
when 0 then raise EntityNotFound, %Q{No such #{entity}: "#{value}"}
|
|
32
|
-
else
|
|
33
|
-
# multiple case insensitive partial matches, try case insensitive full match
|
|
34
|
-
# (anyway AoC does not allow creation of 2 entities with same case insensitive field value)
|
|
35
|
-
value_matches = matching_items.select { |i| i[field].casecmp?(value) }
|
|
36
|
-
case value_matches.length
|
|
37
|
-
when 1 then return value_matches.first
|
|
38
|
-
when 0 then raise Error, "#{entity}: Multiple case insensitive partial match for: \"#{value}\" in #{matching_items.map { |i| i[field] }.join(', ')} but no case insensitive full match. Please be more specific or give exact #{field}."
|
|
39
|
-
else raise Error, "Two entities cannot have the same case insensitive #{field}: #{value_matches.map { |i| i[field] }}"
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# Get a (full or partial) list of all entities of a given type.
|
|
45
|
-
# Using query: `offset` + `limit`
|
|
46
|
-
# And response `total_count`
|
|
47
|
-
# @param entity [String,Symbol] API endpoint of entity to list
|
|
48
|
-
# @param items_key [String] Key in the result to get the list of items (Default: same as `entity`)
|
|
49
|
-
# @param query [Hash,nil] Additional query parameters
|
|
50
|
-
# @return [Array<(Array<Hash>, Integer)>] items, total_count
|
|
51
|
-
def list_entities_limit_offset_total_count(
|
|
52
|
-
entity:,
|
|
53
|
-
operation: 'GET',
|
|
54
|
-
items_key: nil,
|
|
55
|
-
query: nil
|
|
56
|
-
)
|
|
57
|
-
entity = entity.to_s if entity.is_a?(Symbol)
|
|
58
|
-
items_key = entity.split('/').last if items_key.nil?
|
|
59
|
-
query = {} if query.nil?
|
|
60
|
-
Aspera.assert_type(entity, String)
|
|
61
|
-
Aspera.assert_type(items_key, String)
|
|
62
|
-
Aspera.assert_type(query, Hash)
|
|
63
|
-
Log.log.debug { "list_entities t=#{entity} k=#{items_key}" }
|
|
64
|
-
Log.dump(:query, query)
|
|
65
|
-
result = []
|
|
66
|
-
offset = 0
|
|
67
|
-
max_items = query.delete(MAX_ITEMS)
|
|
68
|
-
remain_pages = query.delete(MAX_PAGES)
|
|
69
|
-
# Merge default parameters, by default 100 per page
|
|
70
|
-
query = {'limit'=> PER_PAGE_DEFAULT}.merge(query)
|
|
71
|
-
total_count = nil
|
|
72
|
-
call_args = {
|
|
73
|
-
operation: operation,
|
|
74
|
-
subpath: entity,
|
|
75
|
-
query: query,
|
|
76
|
-
headers: {'Accept' => Mime::JSON}
|
|
77
|
-
}
|
|
78
|
-
loop do
|
|
79
|
-
query['offset'] = offset
|
|
80
|
-
page_result = call(**call_args)
|
|
81
|
-
Aspera.assert_type(page_result[items_key], Array)
|
|
82
|
-
result.concat(page_result[items_key])
|
|
83
|
-
# Reach the limit set by user ?
|
|
84
|
-
if !max_items.nil? && (result.length >= max_items)
|
|
85
|
-
result = result.slice(0, max_items)
|
|
86
|
-
break
|
|
87
|
-
end
|
|
88
|
-
total_count ||= page_result['total_count']
|
|
89
|
-
break if result.length >= total_count
|
|
90
|
-
remain_pages -= 1 unless remain_pages.nil?
|
|
91
|
-
break if remain_pages == 0
|
|
92
|
-
offset += page_result[items_key].length
|
|
93
|
-
RestParameters.instance.spinner_cb.call("#{result.length} / #{total_count || '?'}")
|
|
94
|
-
end
|
|
95
|
-
RestParameters.instance.spinner_cb.call(action: :success)
|
|
96
|
-
return result, total_count
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Lookup an entity id from its name.
|
|
100
|
-
# Uses query `q` if `query` is `:default` and `field` is `name`.
|
|
101
|
-
# @param entity [String] Type of entity to lookup, by default it is the path, and it is also the field name in result
|
|
102
|
-
# @param value [String] Value to lookup
|
|
103
|
-
# @param field [String] Field to match, by default it is `'name'`
|
|
104
|
-
# @param items_key [String] Key in the result to get the list of items (override entity)
|
|
105
|
-
# @param query [Hash] Additional query parameters (Default: `:default`)
|
|
106
|
-
def lookup_entity_by_field(entity:, value:, field: 'name', items_key: nil, query: :default)
|
|
107
|
-
if query.eql?(:default)
|
|
108
|
-
Aspera.assert_values(field, ['name']) { 'Default query field' }
|
|
109
|
-
query = {'q'=> value}
|
|
110
|
-
end
|
|
111
|
-
lookup_entity_generic(entity: entity, field: field, value: value) { list_entities_limit_offset_total_count(entity: entity, items_key: items_key, query: query).first }
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
# Lookup entity by field and value.
|
|
115
|
-
# Extracts a single result from the list returned by the block.
|
|
116
|
-
#
|
|
117
|
-
# @param entity [String] Type of entity to lookup (path, and by default it is also the field name in result)
|
|
118
|
-
# @param value [String] Value to match against the field.
|
|
119
|
-
# @param field [String] Field to match in the hashes (defaults to 'name').
|
|
120
|
-
# @yield [] A mandatory block that returns an Array of Hashes.
|
|
121
|
-
# @return [Hash] The unique matching object.
|
|
122
|
-
# @raise [Cli::BadIdentifier] If 0 or >1 matches are found.
|
|
123
|
-
def lookup_entity_generic(entity:, value:, field: 'name')
|
|
124
|
-
Aspera.assert(block_given?, 'block required for lookup_entity_generic')
|
|
125
|
-
found = yield
|
|
126
|
-
Aspera.assert_array_all(found, Hash)
|
|
127
|
-
found = found.select { |i| i[field].eql?(value) }
|
|
128
|
-
return found.first if found.length.eql?(1)
|
|
129
|
-
raise Cli::BadIdentifier.new(entity, value, field: field, count: found.length)
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
PER_PAGE_DEFAULT = 1000
|
|
133
|
-
private_constant :PER_PAGE_DEFAULT
|
|
134
|
-
module_function :lookup_entity_generic
|
|
135
|
-
end
|
|
136
|
-
end
|