aspera-cli 4.13.0 → 4.15.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.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +81 -7
  4. data/CONTRIBUTING.md +22 -6
  5. data/README.md +2038 -1080
  6. data/bin/ascli +18 -9
  7. data/bin/asession +12 -14
  8. data/examples/dascli +1 -1
  9. data/examples/proxy.pac +1 -1
  10. data/examples/rubyc +24 -0
  11. data/lib/aspera/aoc.rb +219 -159
  12. data/lib/aspera/ascmd.rb +25 -14
  13. data/lib/aspera/cli/basic_auth_plugin.rb +12 -9
  14. data/lib/aspera/cli/error.rb +17 -0
  15. data/lib/aspera/cli/extended_value.rb +47 -12
  16. data/lib/aspera/cli/formatter.rb +260 -179
  17. data/lib/aspera/cli/hints.rb +80 -0
  18. data/lib/aspera/cli/main.rb +104 -156
  19. data/lib/aspera/cli/manager.rb +259 -209
  20. data/lib/aspera/cli/plugin.rb +123 -63
  21. data/lib/aspera/cli/plugins/alee.rb +2 -3
  22. data/lib/aspera/cli/plugins/aoc.rb +341 -261
  23. data/lib/aspera/cli/plugins/ats.rb +22 -21
  24. data/lib/aspera/cli/plugins/bss.rb +5 -5
  25. data/lib/aspera/cli/plugins/config.rb +578 -627
  26. data/lib/aspera/cli/plugins/console.rb +44 -6
  27. data/lib/aspera/cli/plugins/cos.rb +15 -17
  28. data/lib/aspera/cli/plugins/faspex.rb +114 -100
  29. data/lib/aspera/cli/plugins/faspex5.rb +411 -264
  30. data/lib/aspera/cli/plugins/node.rb +354 -259
  31. data/lib/aspera/cli/plugins/orchestrator.rb +61 -29
  32. data/lib/aspera/cli/plugins/preview.rb +82 -90
  33. data/lib/aspera/cli/plugins/server.rb +79 -32
  34. data/lib/aspera/cli/plugins/shares.rb +55 -42
  35. data/lib/aspera/cli/sync_actions.rb +68 -0
  36. data/lib/aspera/cli/transfer_agent.rb +66 -73
  37. data/lib/aspera/cli/transfer_progress.rb +74 -0
  38. data/lib/aspera/cli/version.rb +1 -1
  39. data/lib/aspera/colors.rb +12 -8
  40. data/lib/aspera/command_line_builder.rb +14 -11
  41. data/lib/aspera/cos_node.rb +3 -2
  42. data/lib/aspera/data/6 +0 -0
  43. data/lib/aspera/environment.rb +24 -9
  44. data/lib/aspera/fasp/agent_aspera.rb +126 -0
  45. data/lib/aspera/fasp/agent_base.rb +31 -77
  46. data/lib/aspera/fasp/agent_connect.rb +25 -21
  47. data/lib/aspera/fasp/agent_direct.rb +89 -103
  48. data/lib/aspera/fasp/agent_httpgw.rb +231 -149
  49. data/lib/aspera/fasp/agent_node.rb +41 -34
  50. data/lib/aspera/fasp/agent_trsdk.rb +75 -32
  51. data/lib/aspera/fasp/error_info.rb +4 -2
  52. data/lib/aspera/fasp/faux_file.rb +52 -0
  53. data/lib/aspera/fasp/installation.rb +53 -195
  54. data/lib/aspera/fasp/management.rb +244 -0
  55. data/lib/aspera/fasp/parameters.rb +71 -37
  56. data/lib/aspera/fasp/parameters.yaml +76 -8
  57. data/lib/aspera/fasp/products.rb +162 -0
  58. data/lib/aspera/fasp/resume_policy.rb +3 -3
  59. data/lib/aspera/fasp/transfer_spec.rb +7 -6
  60. data/lib/aspera/fasp/uri.rb +26 -24
  61. data/lib/aspera/faspex_gw.rb +2 -2
  62. data/lib/aspera/faspex_postproc.rb +2 -2
  63. data/lib/aspera/hash_ext.rb +14 -4
  64. data/lib/aspera/json_rpc.rb +49 -0
  65. data/lib/aspera/keychain/macos_security.rb +13 -13
  66. data/lib/aspera/line_logger.rb +23 -0
  67. data/lib/aspera/log.rb +58 -16
  68. data/lib/aspera/node.rb +157 -92
  69. data/lib/aspera/oauth.rb +37 -19
  70. data/lib/aspera/open_application.rb +4 -4
  71. data/lib/aspera/persistency_action_once.rb +1 -1
  72. data/lib/aspera/persistency_folder.rb +2 -2
  73. data/lib/aspera/preview/file_types.rb +4 -2
  74. data/lib/aspera/preview/generator.rb +22 -35
  75. data/lib/aspera/preview/options.rb +2 -0
  76. data/lib/aspera/preview/terminal.rb +73 -16
  77. data/lib/aspera/preview/utils.rb +21 -28
  78. data/lib/aspera/proxy_auto_config.js +2 -2
  79. data/lib/aspera/rest.rb +136 -68
  80. data/lib/aspera/rest_call_error.rb +1 -1
  81. data/lib/aspera/rest_error_analyzer.rb +15 -14
  82. data/lib/aspera/rest_errors_aspera.rb +37 -34
  83. data/lib/aspera/secret_hider.rb +18 -15
  84. data/lib/aspera/ssh.rb +5 -2
  85. data/lib/aspera/sync.rb +127 -119
  86. data/lib/aspera/temp_file_manager.rb +10 -3
  87. data/lib/aspera/web_auth.rb +10 -7
  88. data/lib/aspera/web_server_simple.rb +9 -4
  89. data.tar.gz.sig +0 -0
  90. metadata +34 -17
  91. metadata.gz.sig +0 -0
  92. data/docs/test_env.conf +0 -186
  93. data/lib/aspera/cli/listener/line_dump.rb +0 -19
  94. data/lib/aspera/cli/listener/logger.rb +0 -22
  95. data/lib/aspera/cli/listener/progress.rb +0 -50
  96. data/lib/aspera/cli/listener/progress_multi.rb +0 -84
  97. data/lib/aspera/cli/plugins/sync.rb +0 -44
  98. data/lib/aspera/data/7 +0 -0
  99. data/lib/aspera/fasp/listener.rb +0 -13
@@ -1,23 +1,55 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'aspera/cli/plugins/node'
3
+ require 'aspera/cli/basic_auth_plugin'
4
+ require 'aspera/nagios'
4
5
  require 'xmlsimple'
5
6
 
6
7
  module Aspera
7
8
  module Cli
8
9
  module Plugins
9
10
  class Orchestrator < Aspera::Cli::BasicAuthPlugin
11
+ class << self
12
+ STANDARD_PATH = '/aspera/orchestrator'
13
+ def detect(address_or_url)
14
+ address_or_url = "https://#{address_or_url}" unless address_or_url.match?(%r{^[a-z]{1,6}://})
15
+ urls = [address_or_url]
16
+ urls.push("#{address_or_url}#{STANDARD_PATH}") unless address_or_url.end_with?(STANDARD_PATH)
17
+ urls.each do |base_url|
18
+ next unless base_url.match?('https?://')
19
+ api = Rest.new(base_url: base_url)
20
+ test_endpoint = 'api/remote_node_ping'
21
+ result = api.read(test_endpoint, {format: :json})
22
+ next unless result[:data]['remote_orchestrator_info']
23
+ url = result[:http].uri.to_s
24
+ return {
25
+ version: result[:data]['remote_orchestrator_info']['orchestrator-version'],
26
+ url: url[0..url.index(test_endpoint) - 2]
27
+ }
28
+ rescue StandardError => e
29
+ Log.log.debug{"detect error: #{e}"}
30
+ end
31
+ return nil
32
+ end
33
+
34
+ def wizard(object:, private_key_path: nil, pub_key_pem: nil)
35
+ options = object.options
36
+ return {
37
+ preset_value: {
38
+ url: options.get_option(:url, mandatory: true),
39
+ username: options.get_option(:username, mandatory: true),
40
+ password: options.get_option(:password, mandatory: true)
41
+ },
42
+ test_args: 'workflow list'
43
+ }
44
+ end
45
+ end
46
+
10
47
  def initialize(env)
11
48
  super(env)
12
- options.add_opt_simple(:params, 'parameters hash table, use @json:{"param":"value"}')
13
- options.add_opt_simple(:result, "specify result value as: 'work step:parameter'")
14
- options.add_opt_boolean(:synchronous, 'work step:parameter expected as result')
15
- options.add_opt_list(:ret_style, %i[header arg ext], 'how return type is requested in api')
16
- options.add_opt_list(:auth_style, %i[arg_pass head_basic apikey], 'authentication type')
17
- options.set_option(:params, {})
18
- options.set_option(:synchronous, :no)
19
- options.set_option(:ret_style, :arg)
20
- options.set_option(:auth_style, :head_basic)
49
+ options.declare(:result, "Specify result value as: 'work_step:parameter'")
50
+ options.declare(:synchronous, 'Wait for completion', values: :bool, default: :no)
51
+ options.declare(:ret_style, 'How return type is requested in api', values: %i[header arg ext], default: :arg)
52
+ options.declare(:auth_style, 'Authentication type', values: %i[arg_pass head_basic apikey], default: :head_basic)
21
53
  options.parse_options!
22
54
  end
23
55
 
@@ -49,7 +81,7 @@ module Aspera
49
81
  call_args[:subpath] = "#{opt[:prefix]}/#{call_args[:subpath]}" unless opt[:prefix].nil?
50
82
  # specify id if necessary
51
83
  call_args[:subpath] = "#{call_args[:subpath]}/#{opt[:id]}" if opt.key?(:id)
52
- call_type = options.get_option(:ret_style, is_type: :mandatory)
84
+ call_type = options.get_option(:ret_style, mandatory: true)
53
85
  call_type = opt[:ret_style] if opt.key?(:ret_style)
54
86
  format = 'json'
55
87
  format = opt[:format] if opt.key?(:format)
@@ -68,24 +100,24 @@ module Aspera
68
100
  end
69
101
  result = @api_orch.call(call_args)
70
102
  result[:data] = XmlSimple.xml_in(result[:http].body, opt[:xml_opt] || {'ForceArray' => true}) if format.eql?('xml')
71
- Log.dump(:data, result[:data])
103
+ Log.log.debug{Log.dump(:data, result[:data])}
72
104
  return result
73
105
  end
74
106
 
75
107
  def execute_action
76
- rest_params = {base_url: options.get_option(:url, is_type: :mandatory)}
77
- case options.get_option(:auth_style, is_type: :mandatory)
108
+ rest_params = {base_url: options.get_option(:url, mandatory: true)}
109
+ case options.get_option(:auth_style, mandatory: true)
78
110
  when :arg_pass
79
111
  rest_params[:auth] = {
80
112
  type: :url,
81
- url_creds: {
82
- 'login' => options.get_option(:username, is_type: :mandatory),
83
- 'password' => options.get_option(:password, is_type: :mandatory) }}
113
+ url_query: {
114
+ 'login' => options.get_option(:username, mandatory: true),
115
+ 'password' => options.get_option(:password, mandatory: true) }}
84
116
  when :head_basic
85
117
  rest_params[:auth] = {
86
118
  type: :basic,
87
- username: options.get_option(:username, is_type: :mandatory),
88
- password: options.get_option(:password, is_type: :mandatory) }
119
+ username: options.get_option(:username, mandatory: true),
120
+ password: options.get_option(:password, mandatory: true) }
89
121
  when :apikey
90
122
  raise 'Not implemented'
91
123
  end
@@ -122,9 +154,9 @@ module Aspera
122
154
  end
123
155
  case command
124
156
  when :status
125
- options = {}
126
- options[:id] = wf_id unless wf_id.eql?(VAL_ALL)
127
- result = call_ao('workflows_status', options)[:data]
157
+ call_opts = {}
158
+ call_opts[:id] = wf_id unless wf_id.eql?(ExtendedValue::ALL)
159
+ result = call_ao('workflows_status', call_opts)[:data]
128
160
  return {type: :object_list, data: result['workflows']['workflow']}
129
161
  when :list
130
162
  result = call_ao('workflows_list', id: 0)[:data]
@@ -149,18 +181,18 @@ module Aspera
149
181
  }
150
182
  call_params = {format: :json}
151
183
  override_accept = nil
152
- # set external parameters if any
153
- self.options.get_option(:params, is_type: :mandatory).each do |name, value|
184
+ # get external parameters if any
185
+ options.get_next_argument('external_parameters', mandatory: false, type: Hash, default: {}).each do |name, value|
154
186
  call_params["external_parameters[#{name}]"] = value
155
187
  end
156
188
  # synchronous call ?
157
- call_params['synchronous'] = true if self.options.get_option(:synchronous, is_type: :mandatory)
189
+ call_params['synchronous'] = true if options.get_option(:synchronous, mandatory: true)
158
190
  # expected result for synchro call ?
159
- expected = self.options.get_option(:result)
160
- unless expected.nil?
191
+ result_location = options.get_option(:result)
192
+ unless result_location.nil?
161
193
  result[:type] = :status
162
- fields = expected.split(':')
163
- raise "Expects: work_step:result_name format, but got #{expected}" if fields.length != 2
194
+ fields = result_location.split(':')
195
+ raise Cli::BadArgument, "Expects: work_step:result_name : #{result_location}" if fields.length != 2
164
196
  call_params['explicit_output_step'] = fields[0]
165
197
  call_params['explicit_output_variable'] = fields[1]
166
198
  # implicitly, call is synchronous
@@ -1,17 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # cspell:ignore trevents
3
4
  require 'aspera/cli/basic_auth_plugin'
4
5
  require 'aspera/preview/generator'
5
6
  require 'aspera/preview/options'
6
7
  require 'aspera/preview/utils'
7
8
  require 'aspera/preview/file_types'
9
+ require 'aspera/preview/terminal'
8
10
  require 'aspera/fasp/transfer_spec'
9
11
  require 'aspera/persistency_action_once'
10
12
  require 'aspera/node'
11
13
  require 'aspera/hash_ext'
12
14
  require 'aspera/timer_limiter'
13
15
  require 'aspera/id_generator'
14
- require 'date'
15
16
  require 'securerandom'
16
17
 
17
18
  module Aspera
@@ -26,8 +27,11 @@ module Aspera
26
27
  PREVIEW_BASENAME = 'preview'
27
28
  # subfolder in system tmp folder
28
29
  TMP_DIR_PREFIX = 'prev_tmp'
30
+ # same value as in aspera.conf
29
31
  DEFAULT_PREVIEWS_FOLDER = 'previews'
32
+ # mark that this is used by a particular access key
30
33
  AK_MARKER_FILE = '.aspera_access_key'
34
+ # URL prefix for local storage
31
35
  PVCL_LOCAL_STORAGE = 'file:///'
32
36
  LOG_LIMITER_SEC = 30.0
33
37
  private_constant :PREV_GEN_TAG,
@@ -53,44 +57,45 @@ module Aspera
53
57
  @gen_options = Aspera::Preview::Options.new
54
58
  # used to trigger periodic processing
55
59
  @periodic = TimerLimiter.new(LOG_LIMITER_SEC)
60
+ # Proc
61
+ @filter_block = nil
56
62
  # link CLI options to gen_info attributes
57
- options.set_obj_attr(:skip_format, self, :option_skip_format, []) # no skip
58
- options.set_obj_attr(:folder_reset_cache, self, :option_folder_reset_cache, :no)
59
- options.set_obj_attr(:skip_types, self, :option_skip_types)
60
- options.set_obj_attr(:previews_folder, self, :option_previews_folder, DEFAULT_PREVIEWS_FOLDER)
61
- options.set_obj_attr(:skip_folders, self, :option_skip_folders, []) # no skip
62
- options.set_obj_attr(:overwrite, self, :option_overwrite, :mtime)
63
- options.set_obj_attr(:file_access, self, :option_file_access, :local)
64
- options.add_opt_list(:skip_format, Aspera::Preview::Generator::PREVIEW_FORMATS, 'skip this preview format (multiple possible)')
65
- options.add_opt_list(:folder_reset_cache, %i[no header read], 'force detection of generated preview by refresh cache')
66
- options.add_opt_simple(:skip_types, 'skip types in comma separated list')
67
- options.add_opt_simple(:previews_folder, 'preview folder in storage root')
68
- options.add_opt_simple(:temp_folder, 'path to temp folder')
69
- options.add_opt_simple(:skip_folders, 'list of folder to skip')
70
- options.add_opt_simple(:case, 'basename of output for for test')
71
- options.add_opt_simple(:scan_path, 'subpath in folder id to start scan in (default=/)')
72
- options.add_opt_simple(:scan_id, 'folder id in storage to start scan in, default is access key main folder id')
73
- options.add_opt_boolean(:mimemagic, 'use Mime type detection of gem mimemagic')
74
- options.add_opt_list(:overwrite, %i[always never mtime], 'when to overwrite result file')
75
- options.add_opt_list(:file_access, %i[local remote], 'how to read and write files in repository')
76
- options.set_option(:temp_folder, Dir.tmpdir)
77
- options.set_option(:mimemagic, false)
63
+ options.declare(
64
+ :skip_format, 'Skip this preview format (multiple possible)', values: Aspera::Preview::Generator::PREVIEW_FORMATS,
65
+ handler: {o: self, m: :option_skip_format}, default: [])
66
+ options.declare(
67
+ :folder_reset_cache, 'Force detection of generated preview by refresh cache',
68
+ values: %i[no header read],
69
+ handler: {o: self, m: :option_folder_reset_cache},
70
+ default: :no)
71
+ options.declare(:skip_types, 'Skip types in comma separated list', handler: {o: self, m: :option_skip_types})
72
+ options.declare(:previews_folder, 'Preview folder in storage root', handler: {o: self, m: :option_previews_folder}, default: DEFAULT_PREVIEWS_FOLDER)
73
+ options.declare(:temp_folder, 'Path to temp folder', default: Dir.tmpdir)
74
+ options.declare(:skip_folders, 'List of folder to skip', handler: {o: self, m: :option_skip_folders}, default: [])
75
+ options.declare(:base, 'Basename of output for for test')
76
+ options.declare(:scan_path, 'Subpath in folder id to start scan in (default=/)')
77
+ options.declare(:scan_id, 'Folder id in storage to start scan in, default is access key main folder id')
78
+ options.declare(:mimemagic, 'Use Mime type detection of gem mimemagic', values: :bool, default: false)
79
+ options.declare(:overwrite, 'When to overwrite result file', values: %i[always never mtime], handler: {o: self, m: :option_overwrite}, default: :mtime)
80
+ options.declare(
81
+ :file_access, 'How to read and write files in repository',
82
+ values: %i[local remote],
83
+ handler: {o: self, m: :option_file_access},
84
+ default: :local)
78
85
 
79
86
  # add other options for generator (and set default values)
80
87
  Aspera::Preview::Options::DESCRIPTIONS.each do |opt|
81
- options.set_obj_attr(opt[:name], @gen_options, opt[:name], opt[:default])
82
- if opt.key?(:values)
83
- options.add_opt_list(opt[:name], opt[:values], opt[:description])
88
+ values = if opt.key?(:values)
89
+ opt[:values]
84
90
  elsif Cli::Manager::BOOLEAN_SIMPLE.include?(opt[:default])
85
- options.add_opt_boolean(opt[:name], opt[:description])
86
- else
87
- options.add_opt_simple(opt[:name], opt[:description])
91
+ :bool
88
92
  end
93
+ options.declare(opt[:name], opt[:description].capitalize, values: values, handler: {o: @gen_options, m: opt[:name]}, default: opt[:default])
89
94
  end
90
95
 
91
96
  options.parse_options!
92
97
  raise 'skip_folder shall be an Array, use @json:[...]' unless @option_skip_folders.is_a?(Array)
93
- @tmp_folder = File.join(options.get_option(:temp_folder, is_type: :mandatory), "#{TMP_DIR_PREFIX}.#{SecureRandom.uuid}")
98
+ @tmp_folder = File.join(options.get_option(:temp_folder, mandatory: true), "#{TMP_DIR_PREFIX}.#{SecureRandom.uuid}")
94
99
  FileUtils.mkdir_p(@tmp_folder)
95
100
  Log.log.debug{"tmpdir: #{@tmp_folder}"}
96
101
  end
@@ -206,34 +211,12 @@ module Aspera
206
211
  end
207
212
 
208
213
  def do_transfer(direction, folder_id, source_filename, destination='/')
209
- raise 'error' if destination.nil? && direction.eql?(Fasp::TransferSpec::DIRECTION_RECEIVE)
210
- if @default_transfer_spec.nil?
211
- # make a dummy call to get some default transfer parameters
212
- res = @api_node.create('files/upload_setup', {'transfer_requests' => [{'transfer_request' => {'paths' => [{}], 'destination_root' => '/'}}]})
213
- template_ts = res[:data]['transfer_specs'].first['transfer_spec']
214
- # get ports, anyway that should be 33001 for both. add remote_user ?
215
- @default_transfer_spec = %w[ssh_port fasp_port].each_with_object({}){|e, h|h[e] = template_ts[e]; }
216
- if !@default_transfer_spec['remote_user'].eql?(Aspera::Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER)
217
- Log.log.warn('remote_user shall be xfer')
218
- @default_transfer_spec['remote_user'] = Aspera::Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER
219
- end
220
- @api_node.ts_basic_token(@default_transfer_spec)
221
- # NOTE: we use the same address for ascp than for node api instead of the one from upload_setup
222
- # TODO: configurable ? useful ?
223
- @default_transfer_spec['remote_host'] = @transfer_server_address
224
- end
225
- t_spec = @default_transfer_spec.merge({
226
- 'direction' => direction,
227
- 'paths' => [{'source' => source_filename}],
228
- 'tags' => {
229
- Fasp::TransferSpec::TAG_RESERVED => {
230
- PREV_GEN_TAG => true,
231
- 'node' => {
232
- 'access_key' => @access_key_self['id'],
233
- 'file_id' => folder_id }}}
214
+ raise 'Internal ERROR' if destination.nil? && direction.eql?(Fasp::TransferSpec::DIRECTION_RECEIVE)
215
+ t_spec = @api_node.transfer_spec_gen4(folder_id, direction, {
216
+ 'paths' => [{'source' => source_filename}],
217
+ 'tags' => {Fasp::TransferSpec::TAG_RESERVED => {PREV_GEN_TAG => true}}
234
218
  })
235
- # force destination
236
- # t_spec['destination_root']=destination
219
+ # force destination, need to set this in transfer agent else it gets overwritten, not do: t_spec['destination_root']=destination
237
220
  transfer.option_transfer_spec_deep_merge({'destination_root' => destination})
238
221
  Main.result_transfer(transfer.start(t_spec))
239
222
  end
@@ -255,6 +238,7 @@ module Aspera
255
238
  def get_infos_remote(gen_infos, entry)
256
239
  # store source directly here
257
240
  local_original_filepath = File.join(@tmp_folder, entry['name'])
241
+ # require 'date'
258
242
  # original_mtime=DateTime.parse(entry['modified_time'])
259
243
  # out: where previews are generated
260
244
  local_entry_preview_dir = File.join(@tmp_folder, entry_preview_folder_name(entry))
@@ -278,9 +262,10 @@ module Aspera
278
262
  "#{entry['id']}#{PREVIEW_FOLDER_SUFFIX}"
279
263
  end
280
264
 
281
- def preview_filename(preview_format, filename=nil)
282
- filename ||= PREVIEW_BASENAME
283
- return "#{filename}.#{preview_format}"
265
+ # Generate a file name based on basename and format (extension)
266
+ def preview_filename(preview_format, base_name=nil)
267
+ base_name ||= PREVIEW_BASENAME
268
+ return "#{base_name}.#{preview_format}"
284
269
  end
285
270
 
286
271
  # generate preview files for one folder entry (file) if necessary
@@ -313,10 +298,13 @@ module Aspera
313
298
  next false if gen_info[:preview_newer_than_original]
314
299
  end
315
300
  end
316
- # need generator for further checks
317
- gen_info[:generator] = Aspera::Preview::Generator.new(@gen_options, gen_info[:src], gen_info[:dst], @tmp_folder, entry['content_type'])
318
- # get conversion_type (if known) and check if supported
319
- next false unless gen_info[:generator].supported?
301
+ begin
302
+ # need generator for further checks
303
+ gen_info[:generator] = Aspera::Preview::Generator.new(gen_info[:src], gen_info[:dst], @gen_options, @tmp_folder, entry['content_type'])
304
+ rescue
305
+ # no conversion supported
306
+ next false
307
+ end
320
308
  # shall we skip it ?
321
309
  next false if @skip_types.include?(gen_info[:generator].conversion_type)
322
310
  # ok we need to generate
@@ -330,7 +318,7 @@ module Aspera
330
318
  # download original file to temp folder
331
319
  do_transfer(Fasp::TransferSpec::DIRECTION_RECEIVE, entry['parent_file_id'], entry['name'], @tmp_folder)
332
320
  end
333
- Log.log.info{"source: #{entry['id']}: #{entry['path']})"}
321
+ Log.log.info{"source: #{entry['id']}: #{entry['path']}"}
334
322
  gen_infos.each do |gen_info|
335
323
  gen_info[:generator].generate rescue nil
336
324
  end
@@ -351,24 +339,22 @@ module Aspera
351
339
  end # generate_preview
352
340
 
353
341
  # scan all files in provided folder entry
354
- # @param scan_start subpath to start folder scan inside
355
- def scan_folder_files(top_entry, scan_start=nil)
356
- if !scan_start.nil?
342
+ # @param top_path subpath to start folder scan inside
343
+ def scan_folder_files(top_entry, top_path=nil)
344
+ unless top_path.nil?
357
345
  # canonical path: start with / and ends with /
358
- scan_start = '/' + scan_start.split('/').reject(&:empty?).join('/')
359
- scan_start = "#{scan_start}/" # unless scan_start.end_with?('/')
346
+ top_path = '/' + top_path.split('/').reject(&:empty?).join('/') + '/'
360
347
  end
361
- filter_block = Aspera::Node.file_matcher(options.get_option(:value))
362
- Log.log.debug{"scan: #{top_entry} : #{scan_start}".green}
348
+ Log.log.debug{"scan: #{top_entry} : #{top_path}".green}
363
349
  # don't use recursive call, use list instead
364
350
  entries_to_process = [top_entry]
365
351
  until entries_to_process.empty?
366
352
  entry = entries_to_process.shift
367
- # process this entry only if it is within the scan_start
353
+ # process this entry only if it is within the top_path
368
354
  entry_path_with_slash = entry['path']
369
355
  Log.log.info{"processing entry #{entry_path_with_slash}"} if @periodic.trigger?
370
356
  entry_path_with_slash = "#{entry_path_with_slash}/" unless entry_path_with_slash.end_with?('/')
371
- if !scan_start.nil? && !scan_start.start_with?(entry_path_with_slash) && !entry_path_with_slash.start_with?(scan_start)
357
+ if !top_path.nil? && !top_path.start_with?(entry_path_with_slash) && !entry_path_with_slash.start_with?(top_path)
372
358
  Log.log.debug{"#{entry['path']} folder (skip start)".bg_red}
373
359
  next
374
360
  end
@@ -376,7 +362,7 @@ module Aspera
376
362
  begin
377
363
  case entry['type']
378
364
  when 'file'
379
- if filter_block.call(entry)
365
+ if @filter_block.call(entry)
380
366
  generate_preview(entry)
381
367
  else
382
368
  Log.log.debug('skip by filter')
@@ -409,11 +395,11 @@ module Aspera
409
395
  end
410
396
  end
411
397
 
412
- ACTIONS = %i[scan events trevents check test].freeze
398
+ ACTIONS = %i[scan events trevents check test show].freeze
413
399
 
414
400
  def execute_action
415
401
  command = options.get_next_command(ACTIONS)
416
- unless %i[check test].include?(command)
402
+ unless %i[check test show].include?(command)
417
403
  # this will use node api
418
404
  @api_node = Aspera::Node.new(params: basic_auth_params)
419
405
  @transfer_server_address = URI.parse(@api_node.params[:base_url]).host
@@ -431,7 +417,7 @@ module Aspera
431
417
  if @access_remote
432
418
  # NOTE: the filter "name", it's why we take the first one
433
419
  @previews_folder_entry = get_folder_entries(@access_key_self['root_file_id'], {name: @option_previews_folder}).first
434
- raise CliError, "Folder #{@option_previews_folder} does not exist on node. "\
420
+ raise Cli::Error, "Folder #{@option_previews_folder} does not exist on node. " \
435
421
  'Please create it in the storage root, or specify an alternate name.' if @previews_folder_entry.nil?
436
422
  else
437
423
  raise 'only local storage allowed in this mode' unless @access_key_self['storage']['type'].eql?('local')
@@ -440,9 +426,9 @@ module Aspera
440
426
  @local_storage_root = @local_storage_root[PVCL_LOCAL_STORAGE.length..-1] if @local_storage_root.start_with?(PVCL_LOCAL_STORAGE)
441
427
  # TODO: windows could have "C:" ?
442
428
  raise "not local storage: #{@local_storage_root}" unless @local_storage_root.start_with?('/')
443
- raise CliError, "Local storage root folder #{@local_storage_root} does not exist." unless File.directory?(@local_storage_root)
429
+ raise Cli::Error, "Local storage root folder #{@local_storage_root} does not exist." unless File.directory?(@local_storage_root)
444
430
  @local_preview_folder = File.join(@local_storage_root, @option_previews_folder)
445
- raise CliError, "Folder #{@local_preview_folder} does not exist locally. "\
431
+ raise Cli::Error, "Folder #{@local_preview_folder} does not exist locally. " \
446
432
  'Please create it, or specify an alternate name.' unless File.directory?(@local_preview_folder)
447
433
  # protection to avoid clash of file id for two different access keys
448
434
  marker_file = File.join(@local_preview_folder, AK_MARKER_FILE)
@@ -455,7 +441,7 @@ module Aspera
455
441
  end
456
442
  end
457
443
  end
458
- Aspera::Preview::FileTypes.instance.use_mimemagic = options.get_option(:mimemagic, is_type: :mandatory)
444
+ Aspera::Preview::FileTypes.instance.use_mimemagic = options.get_option(:mimemagic, mandatory: true)
459
445
  # check tools that are anyway required for all cases
460
446
  Aspera::Preview::Utils.check_tools(@skip_types)
461
447
  case command
@@ -473,19 +459,21 @@ module Aspera
473
459
  else
474
460
  @api_node.read("files/#{scan_id}")[:data]
475
461
  end
462
+ @filter_block = Aspera::Node.file_matcher_from_argument(options)
476
463
  scan_folder_files(folder_info, scan_path)
477
464
  return Main.result_status('scan finished')
478
465
  when :events, :trevents
466
+ @filter_block = Aspera::Node.file_matcher_from_argument(options)
479
467
  iteration_persistency = nil
480
- if options.get_option(:once_only, is_type: :mandatory)
468
+ if options.get_option(:once_only, mandatory: true)
481
469
  iteration_persistency = PersistencyActionOnce.new(
482
470
  manager: @agents[:persistency],
483
471
  data: [],
484
472
  id: IdGenerator.from_list([
485
473
  'preview_iteration',
486
474
  command.to_s,
487
- options.get_option(:url, is_type: :mandatory),
488
- options.get_option(:username, is_type: :mandatory)
475
+ options.get_option(:url, mandatory: true),
476
+ options.get_option(:username, mandatory: true)
489
477
  ]))
490
478
  end
491
479
  # call processing method specified by command line command
@@ -493,19 +481,23 @@ module Aspera
493
481
  return Main.result_status("#{command} finished")
494
482
  when :check
495
483
  return Main.result_status('Tools validated')
496
- when :test
497
- format = options.get_next_argument('format', expected: Aspera::Preview::Generator::PREVIEW_FORMATS)
484
+ when :test, :show
498
485
  source = options.get_next_argument('source file')
499
- dest = preview_filename(format, options.get_option(:case))
500
- g = Aspera::Preview::Generator.new(@gen_options, source, dest, @tmp_folder, nil)
501
- raise "cannot find file type for #{source}" if g.conversion_type.nil?
502
- raise "out format #{format} not supported" unless g.supported?
486
+ format = options.get_next_argument('format', expected: Aspera::Preview::Generator::PREVIEW_FORMATS, default: :png)
487
+ generated_file_path = preview_filename(format, options.get_option(:base))
488
+ g = Aspera::Preview::Generator.new(source, generated_file_path, @gen_options, @tmp_folder, nil)
503
489
  g.generate
504
- return Main.result_status("generated: #{dest}")
490
+ if command.eql?(:show)
491
+ terminal_options = options.get_option(:query, default: {}).symbolize_keys
492
+ Log.log.debug{"preview: #{generated_file_path}"}
493
+ formatter.display_status(Aspera::Preview::Terminal.build(File.read(generated_file_path), **terminal_options))
494
+ end
495
+ return Main.result_status("generated: #{generated_file_path}")
505
496
  else
506
497
  raise 'error'
507
498
  end
508
499
  ensure
500
+ Log.log.debug{"cleaning up temp folder #{@tmp_folder}"}
509
501
  FileUtils.rm_rf(@tmp_folder)
510
502
  end # execute_action
511
503
  end # Preview