aspera-cli 4.26.1 → 4.27.0
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 +300 -22
- data/CONTRIBUTING.md +100 -12
- data/bin/ascli +5 -5
- data/docs/README.md +10757 -0
- data/docs/test-mcp-with-ai.md +202 -0
- data/lib/aspera/agent/base.rb +20 -6
- data/lib/aspera/agent/connect.rb +53 -16
- data/lib/aspera/agent/desktop.rb +52 -17
- data/lib/aspera/agent/direct.rb +27 -25
- data/lib/aspera/agent/factory.rb +4 -0
- data/lib/aspera/agent/httpgw.rb +2 -2
- data/lib/aspera/agent/node.rb +34 -2
- data/lib/aspera/agent/transferd.rb +39 -3
- data/lib/aspera/api/alee.rb +1 -1
- data/lib/aspera/api/aoc.rb +56 -51
- data/lib/aspera/api/cos_node.rb +2 -2
- data/lib/aspera/api/faspex.rb +22 -20
- data/lib/aspera/api/httpgw.rb +4 -4
- data/lib/aspera/api/node.rb +33 -30
- data/lib/aspera/api/queries/bss_subscription_account.graphql +18 -0
- data/lib/aspera/api/queries/bss_subscription_usage.graphql +18 -0
- data/lib/aspera/ascmd.rb +25 -23
- data/lib/aspera/ascp/installation.rb +43 -40
- data/lib/aspera/ascp/management.rb +2 -3
- data/lib/aspera/cli/ascp_actions.rb +155 -0
- data/lib/aspera/cli/async_transfer_store.rb +81 -0
- data/lib/aspera/cli/bootstrapper.rb +197 -0
- data/lib/aspera/cli/command_registry.rb +145 -0
- data/lib/aspera/cli/command_spec.rb +138 -0
- data/lib/aspera/cli/context.rb +71 -0
- data/lib/aspera/cli/error.rb +16 -0
- data/lib/aspera/cli/extended_value.rb +5 -3
- data/lib/aspera/cli/formatter.rb +122 -209
- data/lib/aspera/cli/gem_checker.rb +65 -0
- data/lib/aspera/cli/hints.rb +2 -2
- data/lib/aspera/cli/http.rb +218 -0
- data/lib/aspera/cli/info.rb +2 -0
- data/lib/aspera/cli/mailer.rb +97 -0
- data/lib/aspera/cli/mcp_tool.rb +198 -0
- data/lib/aspera/cli/options.schema.yaml +364 -7
- data/lib/aspera/cli/{manager.rb → parser.rb} +337 -144
- data/lib/aspera/cli/plugins/alee.rb +20 -22
- data/lib/aspera/cli/plugins/aoc.rb +1173 -849
- data/lib/aspera/cli/plugins/ats.rb +200 -161
- data/lib/aspera/cli/plugins/base.rb +470 -107
- data/lib/aspera/cli/plugins/basic_auth.rb +14 -4
- data/lib/aspera/cli/plugins/config.rb +434 -1032
- data/lib/aspera/cli/plugins/console.rb +106 -64
- data/lib/aspera/cli/plugins/cos.rb +44 -32
- data/lib/aspera/cli/plugins/factory.rb +7 -4
- data/lib/aspera/cli/plugins/faspex.rb +296 -259
- data/lib/aspera/cli/plugins/faspex5.rb +592 -344
- data/lib/aspera/cli/plugins/faspio.rb +49 -51
- data/lib/aspera/cli/plugins/httpgw.rb +18 -25
- data/lib/aspera/cli/plugins/mcp.rb +279 -0
- data/lib/aspera/cli/plugins/node.rb +1001 -797
- data/lib/aspera/cli/plugins/oauth.rb +7 -10
- data/lib/aspera/cli/plugins/orchestrator.rb +111 -139
- data/lib/aspera/cli/plugins/preview.rb +183 -142
- data/lib/aspera/cli/plugins/server.rb +125 -80
- data/lib/aspera/cli/plugins/shares.rb +301 -107
- data/lib/aspera/cli/preset_actions.rb +139 -0
- data/lib/aspera/cli/preset_manager.rb +236 -0
- data/lib/aspera/cli/result.rb +360 -0
- data/lib/aspera/cli/runner.rb +473 -0
- data/lib/aspera/cli/secret_finder.rb +40 -0
- data/lib/aspera/cli/special_values.rb +1 -0
- data/lib/aspera/cli/sync_actions.rb +84 -56
- data/lib/aspera/cli/terminal_formatter.rb +65 -0
- data/lib/aspera/cli/transfer_actions.rb +83 -0
- data/lib/aspera/cli/transfer_agent.rb +116 -51
- data/lib/aspera/cli/transfer_progress.rb +9 -9
- data/lib/aspera/cli/vault_manager.rb +57 -0
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +26 -26
- data/lib/aspera/command_line_builder.rb +23 -23
- data/lib/aspera/data_repository.rb +6 -1
- data/lib/aspera/dot_container.rb +5 -5
- data/lib/aspera/environment.rb +39 -13
- data/lib/aspera/exec_spec.rb +13 -0
- data/lib/aspera/faspex_gw.rb +2 -2
- data/lib/aspera/faspex_postproc.rb +4 -3
- data/lib/aspera/graphql.rb +35 -0
- data/lib/aspera/hash_ext.rb +6 -0
- data/lib/aspera/json_rpc/client.rb +62 -0
- data/lib/aspera/json_rpc/version.rb +7 -0
- data/lib/aspera/keychain/base.rb +1 -1
- data/lib/aspera/keychain/encrypted_hash.rb +2 -2
- data/lib/aspera/keychain/factory.rb +4 -4
- data/lib/aspera/keychain/macos_security.rb +4 -4
- data/lib/aspera/link_header.rb +82 -0
- data/lib/aspera/log.rb +23 -5
- data/lib/aspera/markdown.rb +85 -2
- data/lib/aspera/nagios.rb +2 -2
- data/lib/aspera/node_simulator.rb +3 -1
- data/lib/aspera/oauth/base.rb +10 -10
- data/lib/aspera/oauth/boot.rb +43 -0
- data/lib/aspera/oauth/factory.rb +41 -15
- data/lib/aspera/oauth/jwt.rb +4 -4
- data/lib/aspera/oauth/web.rb +4 -4
- data/lib/aspera/oauth.rb +1 -0
- data/lib/aspera/persistency_action_once.rb +10 -10
- data/lib/aspera/persistency_folder.rb +10 -3
- data/lib/aspera/preview/file_types.rb +7 -3
- data/lib/aspera/preview/generator.rb +3 -3
- data/lib/aspera/preview/utils.rb +1 -1
- data/lib/aspera/products/connect.rb +4 -1
- data/lib/aspera/products/other.rb +1 -1
- data/lib/aspera/products/transferd.rb +3 -3
- data/lib/aspera/proxy_auto_config.rb +10 -9
- data/lib/aspera/rest.rb +18 -40
- data/lib/aspera/rest_error_analyzer.rb +7 -3
- data/lib/aspera/rest_errors_aspera.rb +0 -10
- data/lib/aspera/rest_list.rb +13 -6
- data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +14 -4
- data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +2730 -648
- data/lib/aspera/schema/async_tables.yaml +361 -0
- data/lib/aspera/schema/documentation.rb +57 -26
- data/lib/aspera/schema/reader.rb +60 -11
- data/lib/aspera/schema/registry.rb +50 -10
- data/lib/aspera/secret_hider.rb +11 -2
- data/lib/aspera/ssl.rb +4 -4
- data/lib/aspera/sync/conf.schema.yaml +2 -2
- data/lib/aspera/sync/database.rb +14 -10
- data/lib/aspera/sync/operations.rb +15 -18
- data/lib/aspera/temp_file_manager.rb +5 -1
- data/lib/aspera/timer_limiter.rb +1 -1
- data/lib/aspera/transfer/faux_file.rb +24 -11
- data/lib/aspera/transfer/parameters.rb +27 -25
- data/lib/aspera/transfer/result.rb +74 -0
- data/lib/aspera/transfer/resumer.rb +7 -7
- data/lib/aspera/transfer/spec.rb +18 -1
- data/lib/aspera/transfer/spec.schema.yaml +26 -8
- data/lib/aspera/transfer/uri.rb +1 -1
- data/lib/aspera/uri_reader.rb +2 -2
- data/lib/aspera/web_auth.rb +2 -2
- data/lib/aspera/web_server_simple.rb +8 -5
- data/lib/aspera/yaml.rb +2 -1
- data.tar.gz.sig +0 -0
- metadata +45 -16
- metadata.gz.sig +0 -0
- data/lib/aspera/cli/main.rb +0 -480
- data/lib/aspera/json_rpc.rb +0 -51
data/lib/aspera/cli/formatter.rb
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# cspell:ignore jsonpp
|
|
4
|
+
require 'aspera/schema/registry'
|
|
4
5
|
require 'aspera/cli/special_values'
|
|
6
|
+
require 'aspera/cli/terminal_formatter'
|
|
5
7
|
require 'aspera/preview/terminal'
|
|
6
8
|
require 'aspera/secret_hider'
|
|
7
9
|
require 'aspera/environment'
|
|
@@ -9,7 +11,6 @@ require 'aspera/log'
|
|
|
9
11
|
require 'aspera/assert'
|
|
10
12
|
require 'aspera/markdown'
|
|
11
13
|
require 'aspera/dot_container'
|
|
12
|
-
require 'aspera/formatter_interface'
|
|
13
14
|
require 'terminal-table'
|
|
14
15
|
require 'tty-spinner'
|
|
15
16
|
require 'yaml'
|
|
@@ -19,75 +20,8 @@ require 'word_wrap'
|
|
|
19
20
|
|
|
20
21
|
module Aspera
|
|
21
22
|
module Cli
|
|
22
|
-
# Terminal formatter with ANSI colors and Unicode support
|
|
23
|
-
# @see FormatterInterface
|
|
24
|
-
# @see MarkdownFormatter (in build/lib/doc_helper.rb)
|
|
25
|
-
module TerminalFormatter
|
|
26
|
-
include FormatterInterface
|
|
27
|
-
|
|
28
|
-
# Format boolean with colored symbol (✓/✗ or Y/ )
|
|
29
|
-
def tick(yes)
|
|
30
|
-
result =
|
|
31
|
-
if Environment.terminal_supports_unicode?
|
|
32
|
-
yes ? "\u2713" : "\u2717"
|
|
33
|
-
else
|
|
34
|
-
yes ? 'Y' : ' '
|
|
35
|
-
end
|
|
36
|
-
return result.green if yes
|
|
37
|
-
return result.red
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Format special values with colors (dim for empty, reverse for others)
|
|
41
|
-
def special_format(what)
|
|
42
|
-
result = "<#{what}>"
|
|
43
|
-
return %w[null empty].any?{ |s| what.include?(s)} ? result.dim : result.reverse_color
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Prepare table row for terminal display (word wrap arrays)
|
|
47
|
-
def check_row(row)
|
|
48
|
-
row.each_key do |k|
|
|
49
|
-
row[k] = row[k].map{ |i| WordWrap.ww(i.to_s, 120).chomp}.join("\n") if row[k].is_a?(Array)
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# Convert Markdown to terminal format (**bold** -> blue, `code` -> bold)
|
|
54
|
-
# @param match [MatchData, String]
|
|
55
|
-
def markdown_text(match)
|
|
56
|
-
if match.is_a?(String)
|
|
57
|
-
match = Markdown::FORMATS.match(match)
|
|
58
|
-
Aspera.assert(match)
|
|
59
|
-
end
|
|
60
|
-
Aspera.assert_type(match, MatchData)
|
|
61
|
-
if match[:entity]
|
|
62
|
-
Aspera.assert_values(match[:entity], %w[bsol])
|
|
63
|
-
'\\'
|
|
64
|
-
elsif match[:bold]
|
|
65
|
-
match[:bold].to_s.blue
|
|
66
|
-
elsif match[:code]
|
|
67
|
-
match[:code].to_s.bold
|
|
68
|
-
else
|
|
69
|
-
Aspera.error_unexpected_value(match.to_s)
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
module_function :tick, :special_format, :check_row, :markdown_text
|
|
74
|
-
end
|
|
75
|
-
|
|
76
23
|
# Take care of CLI output on terminal
|
|
77
24
|
class Formatter
|
|
78
|
-
# remove a fields from the list
|
|
79
|
-
FIELDS_LESS = '-'
|
|
80
|
-
# supported output formats
|
|
81
|
-
DISPLAY_FORMATS = %i[text nagios ruby json jsonpp yaml table csv image].freeze
|
|
82
|
-
# user output levels
|
|
83
|
-
DISPLAY_LEVELS = %i[info data error].freeze
|
|
84
|
-
# column names for single object display in table
|
|
85
|
-
SINGLE_OBJECT_COLUMN_NAMES = %i[field value].freeze
|
|
86
|
-
# Terminal: vertical separator for list of strings.
|
|
87
|
-
STR_LST_SEP_VERT = "\n"
|
|
88
|
-
|
|
89
|
-
private_constant :FIELDS_LESS, :DISPLAY_FORMATS, :DISPLAY_LEVELS, :SINGLE_OBJECT_COLUMN_NAMES, :STR_LST_SEP_VERT
|
|
90
|
-
|
|
91
25
|
class << self
|
|
92
26
|
# Replace special values with a readable version on terminal
|
|
93
27
|
def replace_specific_for_terminal(input_hash, string_list_separator)
|
|
@@ -134,6 +68,17 @@ module Aspera
|
|
|
134
68
|
@spinner = nil
|
|
135
69
|
end
|
|
136
70
|
|
|
71
|
+
# Display item count if total is provided
|
|
72
|
+
def display_item_count(count, total)
|
|
73
|
+
return if total.nil?
|
|
74
|
+
count = count.to_i
|
|
75
|
+
total = total.to_i
|
|
76
|
+
return if total.eql?(0) && count.eql?(0)
|
|
77
|
+
count_msg = "Items: #{count}/#{total}"
|
|
78
|
+
count_msg = count_msg.bg_red unless count.eql?(total)
|
|
79
|
+
display_status(count_msg)
|
|
80
|
+
end
|
|
81
|
+
|
|
137
82
|
def long_operation(title = nil, action: :spin)
|
|
138
83
|
return unless Environment.terminal?
|
|
139
84
|
return if %i[error data].include?(@options[:display])
|
|
@@ -164,28 +109,41 @@ module Aspera
|
|
|
164
109
|
end
|
|
165
110
|
end
|
|
166
111
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
options
|
|
172
|
-
:
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
:
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
112
|
+
class << self
|
|
113
|
+
# Declare all formatter CLI options (metadata only - no handler binding yet).
|
|
114
|
+
# @param options [Aspera::Cli::Parser]
|
|
115
|
+
# @return [nil]
|
|
116
|
+
def declare_options(options)
|
|
117
|
+
options.declare(:display, description: 'Output only some information', allowed: DISPLAY_LEVELS, default: :data)
|
|
118
|
+
options.declare(:format, description: 'Output format', allowed: DISPLAY_FORMATS, default: :table)
|
|
119
|
+
options.declare(:output, description: 'Destination for results')
|
|
120
|
+
options.declare(:fields, description: "Comma separated list of: fields, or #{SpecialValues::ALL}, or #{SpecialValues::DEF}", allowed: [String, Array, Regexp, Proc], default: SpecialValues::DEF)
|
|
121
|
+
options.declare(:select, description: 'Select only some items in lists: column, value', allowed: [Hash, Proc])
|
|
122
|
+
options.declare(:table_style, description: '(Table) Display style', allowed: [Hash])
|
|
123
|
+
options.declare(:flat_hash, description: '(Table) Display deep values as additional keys', allowed: Allowed::TYPES_BOOLEAN, default: true)
|
|
124
|
+
options.declare(:multi_single, description: '(Table) Control how object list is displayed as single table, or multiple objects', allowed: %i[no yes single], default: :no)
|
|
125
|
+
options.declare(:show_secrets, description: 'Show secrets on command output', allowed: Allowed::TYPES_BOOLEAN, default: false)
|
|
126
|
+
options.declare(:image, schema: Schema::Registry::IMAGE_OPTIONS)
|
|
127
|
+
nil
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Bind all formatter options to this instance using set_handler.
|
|
132
|
+
# Called from Runner after Formatter.new.
|
|
133
|
+
# @param options [Aspera::Cli::Parser]
|
|
134
|
+
# @return [nil]
|
|
135
|
+
def bind_options(options)
|
|
136
|
+
%i[display format output fields select table_style flat_hash multi_single show_secrets image].each do |opt|
|
|
137
|
+
options.set_handler(opt, object: self, method: :option_handler)
|
|
138
|
+
end
|
|
139
|
+
nil
|
|
185
140
|
end
|
|
186
141
|
|
|
187
|
-
#
|
|
188
|
-
#
|
|
142
|
+
# Getter/setter handler called by the option manager for all formatter options
|
|
143
|
+
# @param option_symbol [Symbol] Option name (one of :format, :output, :display, :fields, :select, :table_style, :flat_hash, :multi_single, :show_secrets, :image)
|
|
144
|
+
# @param operation [Symbol] :get or :set
|
|
145
|
+
# @param value [Object, nil] Value to set (only used when operation is :set)
|
|
146
|
+
# @return [Object, nil] Current option value when operation is :get; nil otherwise
|
|
189
147
|
def option_handler(option_symbol, operation, value = nil)
|
|
190
148
|
Aspera.assert_values(operation, %i[set get])
|
|
191
149
|
case operation
|
|
@@ -206,7 +164,7 @@ module Aspera
|
|
|
206
164
|
allowed_options = Preview::Terminal.method(:build).parameters.select{ |i| i[0].eql?(:key)}.map{ |i| i[1]}
|
|
207
165
|
# check that only supported options are given
|
|
208
166
|
unknown_options = value.keys.map(&:to_sym) - allowed_options
|
|
209
|
-
|
|
167
|
+
Aspera.assert(unknown_options.empty?){"Invalid parameter(s) for option image: #{unknown_options.join(', ')}, use #{allowed_options.join(', ')}"}
|
|
210
168
|
end
|
|
211
169
|
when :get then return @options[option_symbol]
|
|
212
170
|
else Aspera.error_unreachable_line
|
|
@@ -214,10 +172,16 @@ module Aspera
|
|
|
214
172
|
nil
|
|
215
173
|
end
|
|
216
174
|
|
|
217
|
-
# Main output method
|
|
218
|
-
#
|
|
219
|
-
#
|
|
220
|
-
#
|
|
175
|
+
# Main output method for displaying messages to the user
|
|
176
|
+
# This method is used by Result classes to output formatted data
|
|
177
|
+
# @param message_level [Symbol] The level of the message - must be one of: :data, :info, :error
|
|
178
|
+
# @param message [String] The message to display
|
|
179
|
+
# @param hide_secrets [Boolean] Whether to hide secrets in the message (default: true)
|
|
180
|
+
# @return [nil]
|
|
181
|
+
# @note Message display behavior depends on the message_level:
|
|
182
|
+
# - +:data+ messages are displayed unless display level is +:error+
|
|
183
|
+
# - +:info+ messages are only displayed when display level is +:info+
|
|
184
|
+
# - +:error+ messages are always displayed on stderr
|
|
221
185
|
def display_message(message_level, message, hide_secrets: true)
|
|
222
186
|
message = SecretHider.instance.hide_secrets_in_string(message) if hide_secrets && message.is_a?(String) && hide_secrets?
|
|
223
187
|
case message_level
|
|
@@ -232,15 +196,7 @@ module Aspera
|
|
|
232
196
|
display_message(:info, status, **kwargs)
|
|
233
197
|
end
|
|
234
198
|
|
|
235
|
-
|
|
236
|
-
number = number.to_i
|
|
237
|
-
total = total.to_i
|
|
238
|
-
return if total.eql?(0) && number.eql?(0)
|
|
239
|
-
count_msg = "Items: #{number}/#{total}"
|
|
240
|
-
count_msg = count_msg.bg_red unless number.eql?(total)
|
|
241
|
-
display_status(count_msg)
|
|
242
|
-
end
|
|
243
|
-
|
|
199
|
+
# Check if secrets should be hidden
|
|
244
200
|
def hide_secrets?
|
|
245
201
|
!@options[:show_secrets] && !@options[:display].eql?(:data)
|
|
246
202
|
end
|
|
@@ -250,118 +206,62 @@ module Aspera
|
|
|
250
206
|
SecretHider.instance.deep_remove_secret(data) if hide_secrets?
|
|
251
207
|
end
|
|
252
208
|
|
|
253
|
-
# Display results
|
|
254
|
-
#
|
|
255
|
-
#
|
|
256
|
-
# @param
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
Log.
|
|
262
|
-
Log.dump(:
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
hide_secrets(data)
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
when :text
|
|
271
|
-
display_message(:data, data.to_s)
|
|
272
|
-
when :nagios
|
|
273
|
-
Nagios.process(data)
|
|
274
|
-
when :ruby
|
|
275
|
-
display_message(:data, PP.pp(filter_list_on_fields(data), +''))
|
|
276
|
-
when :json
|
|
277
|
-
display_message(:data, JSON.generate(filter_list_on_fields(data)))
|
|
278
|
-
when :jsonpp
|
|
279
|
-
display_message(:data, JSON.pretty_generate(filter_list_on_fields(data)))
|
|
280
|
-
when :yaml
|
|
281
|
-
display_message(:data, YAML.dump(filter_list_on_fields(data)))
|
|
282
|
-
when :image
|
|
283
|
-
# if object or list, then must be a single
|
|
284
|
-
case type
|
|
285
|
-
when :single_object, :object_list
|
|
286
|
-
data = [data] if type.eql?(:single_object)
|
|
287
|
-
raise BadArgument, 'image display requires a single result' unless data.length == 1
|
|
288
|
-
fields = compute_fields(data, fields)
|
|
289
|
-
raise BadArgument, 'select a single field to display' unless fields.length == 1
|
|
290
|
-
data = data.first
|
|
291
|
-
raise BadArgument, 'no such field' unless data.key?(fields.first)
|
|
292
|
-
data = data[fields.first]
|
|
293
|
-
end
|
|
294
|
-
Aspera.assert_type(data, String){'URL or blob for image'}
|
|
295
|
-
# Check if URL
|
|
296
|
-
data =
|
|
297
|
-
begin
|
|
298
|
-
# just validate
|
|
299
|
-
URI.parse(data)
|
|
300
|
-
if Environment.instance.url_method.eql?(:text)
|
|
301
|
-
UriReader.read(data)
|
|
302
|
-
else
|
|
303
|
-
Environment.instance.open_uri(data)
|
|
304
|
-
display_message(:info, "Opened URL in browser: #{data}")
|
|
305
|
-
:done
|
|
306
|
-
end
|
|
307
|
-
rescue URI::InvalidURIError
|
|
308
|
-
data
|
|
309
|
-
end
|
|
310
|
-
# try base64
|
|
311
|
-
data = begin
|
|
312
|
-
Base64.strict_decode64(data)
|
|
313
|
-
rescue
|
|
314
|
-
data
|
|
315
|
-
end
|
|
316
|
-
# here, data is the image blob
|
|
317
|
-
display_message(:data, Preview::Terminal.build(data, **@options[:image].symbolize_keys)) unless data.eql?(:done)
|
|
318
|
-
when :table, :csv
|
|
319
|
-
case type
|
|
320
|
-
when :single_object
|
|
321
|
-
# :single_object is a Hash, where key=column name
|
|
322
|
-
Aspera.assert_type(data, Hash){'result'}
|
|
323
|
-
if data.empty?
|
|
324
|
-
display_message(:data, TerminalFormatter.special_format('empty dict'))
|
|
325
|
-
else
|
|
326
|
-
data = DotContainer.new(data).to_dotted if @options[:flat_hash]
|
|
327
|
-
display_table([data], compute_fields([data], fields), single: true)
|
|
328
|
-
end
|
|
329
|
-
when :object_list
|
|
330
|
-
# :object_list is an Array of Hash, where key=column name
|
|
331
|
-
Aspera.assert_array_all(data, Hash){'result'}
|
|
332
|
-
data = data.map{ |obj| DotContainer.new(obj).to_dotted} if @options[:flat_hash]
|
|
333
|
-
display_table(data, compute_fields(data, fields), single: type.eql?(:single_object))
|
|
334
|
-
when :value_list
|
|
335
|
-
# :value_list is a simple array of values, name of column provided in `name`
|
|
336
|
-
display_table(data.map{ |i| {name => i}}, [name])
|
|
337
|
-
when :special # no table
|
|
338
|
-
if data.eql?(:nothing)
|
|
339
|
-
Log.log.debug('no result expected')
|
|
340
|
-
return
|
|
341
|
-
end
|
|
342
|
-
display_message(:info, TerminalFormatter.special_format(data.to_s))
|
|
343
|
-
return
|
|
344
|
-
when :status # no table
|
|
345
|
-
# :status displays a simple message
|
|
346
|
-
display_message(:info, data)
|
|
347
|
-
when :text # no table
|
|
348
|
-
# :status displays a simple message
|
|
349
|
-
display_message(:data, data)
|
|
350
|
-
else Aspera.error_unexpected_value(type){'data type'}
|
|
351
|
-
end
|
|
352
|
-
else Aspera.error_unexpected_value(@options[:format]){'format'}
|
|
353
|
-
end
|
|
209
|
+
# Display results using the Visitor pattern
|
|
210
|
+
# Each Result subclass knows how to format itself by calling the appropriate
|
|
211
|
+
# formatter method, eliminating the need for type checking in the formatter.
|
|
212
|
+
# @param result [Result] Result object to display
|
|
213
|
+
def display_results(result)
|
|
214
|
+
require 'aspera/cli/result'
|
|
215
|
+
Aspera.assert_type(result, Cli::Result){'result must be a Result object'}
|
|
216
|
+
|
|
217
|
+
Log.log.debug{"display_results: result class=#{result.class.name}"}
|
|
218
|
+
Log.dump(:data, result.data, level: :trace1)
|
|
219
|
+
Log.dump(:fields, result.fields, level: :trace1)
|
|
220
|
+
|
|
221
|
+
# Hide secrets in data
|
|
222
|
+
hide_secrets(result.data)
|
|
223
|
+
|
|
224
|
+
# Use the Visitor pattern: delegate formatting to the result object
|
|
225
|
+
result.format(self)
|
|
354
226
|
end
|
|
355
|
-
|
|
227
|
+
# Public methods exposed to Result classes for formatting
|
|
356
228
|
|
|
357
|
-
|
|
229
|
+
# Get the current format type
|
|
230
|
+
def format_type
|
|
231
|
+
@options[:format]
|
|
232
|
+
end
|
|
358
233
|
|
|
359
|
-
#
|
|
234
|
+
# Set the format type (used by Image result)
|
|
235
|
+
def format_type=(format)
|
|
236
|
+
@options[:format] = format
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Hide secrets in a string
|
|
240
|
+
def hide_secrets_in_string(string)
|
|
241
|
+
SecretHider.instance.hide_secrets_in_string(string)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Get special format string
|
|
245
|
+
def special_format(text)
|
|
246
|
+
TerminalFormatter.special_format(text)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Check if flat_hash option is enabled
|
|
250
|
+
def flat_hash?
|
|
251
|
+
@options[:flat_hash]
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# Get image options
|
|
255
|
+
def image_options
|
|
256
|
+
@options[:image].symbolize_keys
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# @return [Array<String>] all fields of all objects in list of objects
|
|
360
260
|
def all_fields(data)
|
|
361
261
|
data.each_with_object({}){ |v, m| v.each_key{ |c| m[c] = true}}.keys
|
|
362
262
|
end
|
|
363
263
|
|
|
364
|
-
# @return the list of fields to display
|
|
264
|
+
# @return [Array<String>] the list of fields to display
|
|
365
265
|
# @param data [Array<Hash>] data to display
|
|
366
266
|
# @param default [Array<String>, Proc] list of fields to display by default (may contain special values)
|
|
367
267
|
def compute_fields(data, default)
|
|
@@ -427,7 +327,7 @@ module Aspera
|
|
|
427
327
|
when Proc
|
|
428
328
|
begin
|
|
429
329
|
data.select!{ |i| @options[:select].call(i)}
|
|
430
|
-
rescue
|
|
330
|
+
rescue StandardError => e
|
|
431
331
|
raise Cli::BadArgument, "Error in user-provided ruby lambda code during select: #{e.message}"
|
|
432
332
|
end
|
|
433
333
|
when Hash
|
|
@@ -470,7 +370,7 @@ module Aspera
|
|
|
470
370
|
final_table_rows = object_array.map{ |r| fields.map{ |c| r[c].to_s}}
|
|
471
371
|
# remove empty rows
|
|
472
372
|
final_table_rows.select!{ |i| !(i.is_a?(Hash) && i.empty?)}
|
|
473
|
-
#
|
|
373
|
+
# fields: list of column names to display
|
|
474
374
|
case @options[:format]
|
|
475
375
|
when :table
|
|
476
376
|
format_style[:border] = :unicode_round if Environment.terminal_supports_unicode?
|
|
@@ -506,6 +406,19 @@ module Aspera
|
|
|
506
406
|
end
|
|
507
407
|
nil
|
|
508
408
|
end
|
|
409
|
+
|
|
410
|
+
# remove a fields from the list
|
|
411
|
+
FIELDS_LESS = '-'
|
|
412
|
+
# supported output formats
|
|
413
|
+
DISPLAY_FORMATS = %i[text nagios ruby json jsonpp yaml table csv image].freeze
|
|
414
|
+
# user output levels
|
|
415
|
+
DISPLAY_LEVELS = %i[info data error].freeze
|
|
416
|
+
# column names for single object display in table
|
|
417
|
+
SINGLE_OBJECT_COLUMN_NAMES = %i[field value].freeze
|
|
418
|
+
# Terminal: vertical separator for list of strings.
|
|
419
|
+
STR_LST_SEP_VERT = "\n"
|
|
420
|
+
|
|
421
|
+
private_constant :FIELDS_LESS, :DISPLAY_FORMATS, :DISPLAY_LEVELS, :SINGLE_OBJECT_COLUMN_NAMES, :STR_LST_SEP_VERT
|
|
509
422
|
end
|
|
510
423
|
end
|
|
511
424
|
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'aspera/cli/version'
|
|
4
|
+
require 'aspera/cli/info'
|
|
5
|
+
require 'aspera/environment'
|
|
6
|
+
require 'aspera/persistency_action_once'
|
|
7
|
+
require 'aspera/rest'
|
|
8
|
+
require 'aspera/log'
|
|
9
|
+
require 'date'
|
|
10
|
+
|
|
11
|
+
module Aspera
|
|
12
|
+
module Cli
|
|
13
|
+
# Mixin providing gem version-check functionality to Plugin::Config.
|
|
14
|
+
# Depends on `options` and `persistency` being available in the including class.
|
|
15
|
+
module GemChecker
|
|
16
|
+
GEM_CHECK_DATE_FMT = '%Y/%m/%d'
|
|
17
|
+
|
|
18
|
+
# @return [Hash] current vs latest gem version info
|
|
19
|
+
def check_gem_version
|
|
20
|
+
latest_version =
|
|
21
|
+
begin
|
|
22
|
+
Rest.new(base_url: 'https://rubygems.org/api/v1').read("versions/#{Info::GEM_NAME}/latest.json")['version']
|
|
23
|
+
rescue StandardError
|
|
24
|
+
Log.log.warn('Could not retrieve latest gem version on rubygems.')
|
|
25
|
+
'0'
|
|
26
|
+
end
|
|
27
|
+
if Gem::Version.new(Environment.ruby_version) < Gem::Version.new(Info::RUBY_FUTURE_MINIMUM_VERSION)
|
|
28
|
+
Log.log.warn do
|
|
29
|
+
"Note that a future version will require Ruby version #{Info::RUBY_FUTURE_MINIMUM_VERSION} at minimum, " \
|
|
30
|
+
"you are using #{Environment.ruby_version}"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
return {
|
|
34
|
+
name: Info::GEM_NAME,
|
|
35
|
+
current: Cli::VERSION,
|
|
36
|
+
latest: latest_version,
|
|
37
|
+
need_update: Gem::Version.new(Cli::VERSION) < Gem::Version.new(latest_version)
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Check periodically if a newer gem version is available; log a warning if so.
|
|
42
|
+
# Called once per run by Runner, before command execution.
|
|
43
|
+
def periodic_check_newer_gem_version
|
|
44
|
+
delay_days = options.get_option(:version_check_days, mandatory: true).to_i
|
|
45
|
+
return if delay_days.eql?(0)
|
|
46
|
+
last_check_array = []
|
|
47
|
+
check_date_persist = PersistencyActionOnce.new(
|
|
48
|
+
manager: persistency,
|
|
49
|
+
data: last_check_array,
|
|
50
|
+
id: 'version_last_check'
|
|
51
|
+
)
|
|
52
|
+
current_date = Date.today
|
|
53
|
+
last_check_days = (current_date - Date.strptime(last_check_array.first, GEM_CHECK_DATE_FMT)) rescue nil
|
|
54
|
+
Log.log.debug{"gem check new version: #{delay_days}, #{last_check_days}, #{current_date}, #{last_check_array}"}
|
|
55
|
+
return if !last_check_days.nil? && last_check_days < delay_days
|
|
56
|
+
last_check_array[0] = current_date.strftime(GEM_CHECK_DATE_FMT)
|
|
57
|
+
check_date_persist.save
|
|
58
|
+
check_data = check_gem_version
|
|
59
|
+
Log.log.warn do
|
|
60
|
+
"A new version is available: #{check_data[:latest]}. You have #{check_data[:current]}. Upgrade with: gem update #{check_data[:name]}"
|
|
61
|
+
end if check_data[:need_update]
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/lib/aspera/cli/hints.rb
CHANGED
|
@@ -129,6 +129,7 @@ module Aspera
|
|
|
129
129
|
class << self
|
|
130
130
|
# @param error [Exception] exception object
|
|
131
131
|
def hint_for(error, formatter)
|
|
132
|
+
# Loop on known error hints
|
|
132
133
|
ERROR_HINTS.each do |hint|
|
|
133
134
|
next unless error.is_a?(hint[:exception])
|
|
134
135
|
message = error.message
|
|
@@ -141,12 +142,11 @@ module Aspera
|
|
|
141
142
|
next unless message.eql?(m)
|
|
142
143
|
when Regexp
|
|
143
144
|
next unless message.match?(m)
|
|
144
|
-
else Aspera.error_unexpected_value(m)
|
|
145
|
+
else Aspera.error_unexpected_value(m){'hint match'}
|
|
145
146
|
end
|
|
146
147
|
hint[:remediation].each do |r|
|
|
147
148
|
Log.log.info{"#{'HINT:'.bg_green.gray.blink.freeze} #{r}"}
|
|
148
149
|
end
|
|
149
|
-
break
|
|
150
150
|
end
|
|
151
151
|
end
|
|
152
152
|
end
|