aspera-cli 4.17.0 → 4.18.1

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 (85) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +3 -4
  3. data/CHANGELOG.md +33 -0
  4. data/CONTRIBUTING.md +15 -1
  5. data/README.md +711 -432
  6. data/bin/ascli +5 -0
  7. data/bin/asession +2 -2
  8. data/examples/build_package.sh +28 -0
  9. data/lib/aspera/agent/alpha.rb +10 -8
  10. data/lib/aspera/agent/base.rb +9 -6
  11. data/lib/aspera/agent/connect.rb +7 -8
  12. data/lib/aspera/agent/direct.rb +56 -37
  13. data/lib/aspera/agent/httpgw.rb +23 -324
  14. data/lib/aspera/agent/node.rb +19 -20
  15. data/lib/aspera/agent/trsdk.rb +19 -20
  16. data/lib/aspera/api/aoc.rb +17 -14
  17. data/lib/aspera/api/cos_node.rb +4 -4
  18. data/lib/aspera/api/httpgw.rb +342 -0
  19. data/lib/aspera/api/node.rb +135 -89
  20. data/lib/aspera/ascmd.rb +4 -3
  21. data/lib/aspera/ascp/installation.rb +15 -7
  22. data/lib/aspera/ascp/management.rb +2 -2
  23. data/lib/aspera/ascp/products.rb +1 -1
  24. data/lib/aspera/cli/basic_auth_plugin.rb +5 -9
  25. data/lib/aspera/cli/extended_value.rb +35 -16
  26. data/lib/aspera/cli/formatter.rb +161 -70
  27. data/lib/aspera/cli/hints.rb +18 -0
  28. data/lib/aspera/cli/main.rb +32 -39
  29. data/lib/aspera/cli/manager.rb +151 -119
  30. data/lib/aspera/cli/plugin.rb +27 -21
  31. data/lib/aspera/cli/plugin_factory.rb +31 -20
  32. data/lib/aspera/cli/plugins/alee.rb +14 -2
  33. data/lib/aspera/cli/plugins/aoc.rb +152 -141
  34. data/lib/aspera/cli/plugins/ats.rb +1 -1
  35. data/lib/aspera/cli/plugins/config.rb +72 -65
  36. data/lib/aspera/cli/plugins/console.rb +8 -5
  37. data/lib/aspera/cli/plugins/faspex.rb +32 -23
  38. data/lib/aspera/cli/plugins/faspex5.rb +232 -156
  39. data/lib/aspera/cli/plugins/faspio.rb +85 -0
  40. data/lib/aspera/cli/plugins/httpgw.rb +55 -0
  41. data/lib/aspera/cli/plugins/node.rb +129 -64
  42. data/lib/aspera/cli/plugins/orchestrator.rb +33 -30
  43. data/lib/aspera/cli/plugins/preview.rb +7 -3
  44. data/lib/aspera/cli/plugins/server.rb +6 -6
  45. data/lib/aspera/cli/plugins/shares.rb +16 -14
  46. data/lib/aspera/cli/special_values.rb +13 -0
  47. data/lib/aspera/cli/sync_actions.rb +10 -10
  48. data/lib/aspera/cli/transfer_agent.rb +7 -6
  49. data/lib/aspera/cli/version.rb +1 -1
  50. data/lib/aspera/environment.rb +70 -9
  51. data/lib/aspera/faspex_gw.rb +5 -4
  52. data/lib/aspera/faspex_postproc.rb +2 -2
  53. data/lib/aspera/log.rb +6 -3
  54. data/lib/aspera/node_simulator.rb +2 -2
  55. data/lib/aspera/oauth/base.rb +31 -19
  56. data/lib/aspera/oauth/factory.rb +12 -13
  57. data/lib/aspera/oauth/generic.rb +1 -0
  58. data/lib/aspera/oauth/jwt.rb +18 -15
  59. data/lib/aspera/oauth/url_json.rb +8 -6
  60. data/lib/aspera/oauth/web.rb +2 -2
  61. data/lib/aspera/persistency_folder.rb +2 -2
  62. data/lib/aspera/preview/generator.rb +3 -3
  63. data/lib/aspera/preview/options.rb +3 -3
  64. data/lib/aspera/preview/terminal.rb +4 -4
  65. data/lib/aspera/preview/utils.rb +3 -3
  66. data/lib/aspera/proxy_auto_config.rb +5 -1
  67. data/lib/aspera/rest.rb +105 -88
  68. data/lib/aspera/rest_call_error.rb +1 -1
  69. data/lib/aspera/rest_error_analyzer.rb +2 -2
  70. data/lib/aspera/rest_errors_aspera.rb +1 -1
  71. data/lib/aspera/resumer.rb +1 -1
  72. data/lib/aspera/secret_hider.rb +2 -4
  73. data/lib/aspera/ssh.rb +1 -1
  74. data/lib/aspera/transfer/parameters.rb +39 -36
  75. data/lib/aspera/transfer/spec.rb +2 -0
  76. data/lib/aspera/transfer/sync.rb +2 -1
  77. data/lib/aspera/transfer/uri.rb +1 -1
  78. data/lib/aspera/uri_reader.rb +5 -4
  79. data/lib/aspera/web_auth.rb +1 -1
  80. data/lib/aspera/web_server_simple.rb +4 -3
  81. data.tar.gz.sig +0 -0
  82. metadata +7 -4
  83. metadata.gz.sig +0 -0
  84. data/lib/aspera/cli/plugins/bss.rb +0 -71
  85. data/lib/aspera/open_application.rb +0 -71
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aspera/rest'
4
+ require 'aspera/api/httpgw'
5
+ require 'aspera/nagios'
6
+
7
+ module Aspera
8
+ module Cli
9
+ module Plugins
10
+ class Httpgw < Plugin
11
+ class << self
12
+ def application_name
13
+ 'HTTP Gateway'
14
+ end
15
+
16
+ def detect(base_url)
17
+ api = Api::Httpgw.new(url: base_url)
18
+ api_info = api.info
19
+ return {
20
+ url: base_url,
21
+ version: api_info['version']
22
+ } if api_info.is_a?(Hash) && api_info.key?('download_endpoint')
23
+ return nil
24
+ end
25
+ end
26
+ ACTIONS = %i[health info].freeze
27
+
28
+ def initialize(**env)
29
+ super
30
+ options.declare(:url, 'URL of application, e.g. https://app.example.com/aspera/app')
31
+ options.parse_options!
32
+ end
33
+
34
+ def execute_action
35
+ base_url = options.get_option(:url, mandatory: true)
36
+ command = options.get_next_command(ACTIONS)
37
+ case command
38
+ when :health
39
+ nagios = Nagios.new
40
+ begin
41
+ Api::Httpgw.new(url: base_url)
42
+ nagios.add_ok('api', 'answered ok')
43
+ rescue StandardError => e
44
+ nagios.add_critical('api', e.to_s)
45
+ end
46
+ return nagios.result
47
+ when :info
48
+ api_v1 = Api::Httpgw.new(url: base_url)
49
+ return {type: :single_object, data: api_v1.info}
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -3,6 +3,7 @@
3
3
  # cspell:ignore snid fnid bidi ssync asyncs rund asnodeadmin mkfile mklink asperabrowser asperabrowserurl watchfolders watchfolderd entsrv
4
4
  require 'aspera/cli/basic_auth_plugin'
5
5
  require 'aspera/cli/sync_actions'
6
+ require 'aspera/cli/special_values'
6
7
  require 'aspera/transfer/spec'
7
8
  require 'aspera/nagios'
8
9
  require 'aspera/hash_ext'
@@ -44,7 +45,8 @@ module Aspera
44
45
  next unless result[:http].body.eql?('')
45
46
  url_length = -2 - test_endpoint.length
46
47
  return {
47
- url: result[:http].uri.to_s[0..url_length]
48
+ url: result[:http].uri.to_s[0..url_length],
49
+ version: 'requires authentication'
48
50
  }
49
51
  rescue StandardError => e
50
52
  error = e
@@ -92,7 +94,7 @@ module Aspera
92
94
  SEARCH_REMOVE_FIELDS = %w[basename permissions].freeze
93
95
 
94
96
  # actions in execute_command_gen3
95
- COMMANDS_GEN3 = %i[search space mkdir mklink mkfile rename delete browse upload download http_node_download sync]
97
+ COMMANDS_GEN3 = %i[search space mkdir mklink mkfile rename delete browse upload download http_node_download sync transport]
96
98
 
97
99
  BASE_ACTIONS = %i[api_details].concat(COMMANDS_GEN3).freeze
98
100
 
@@ -161,7 +163,7 @@ module Aspera
161
163
  # translates paths results into CLI result, and removes prefix
162
164
  def c_result_translate_rem_prefix(response, type, success_msg, path_prefix)
163
165
  errors = []
164
- final_result = { data: [], type: :object_list, fields: [type, 'result']}
166
+ final_result = {type: :object_list, data: [], fields: [type, 'result']}
165
167
  JSON.parse(response[:http].body)['paths'].each do |p|
166
168
  result = success_msg
167
169
  if p.key?('error')
@@ -178,24 +180,78 @@ module Aspera
178
180
  return c_result_remove_prefix_path(final_result, type, path_prefix)
179
181
  end
180
182
 
181
- # get path arguments from command line, and add prefix
182
- def get_next_arg_add_prefix(path_prefix, name, number=:single)
183
- path_or_list = options.get_next_argument(name, expected: number)
184
- return path_or_list if path_prefix.nil?
185
- return File.join(path_prefix, path_or_list) if path_or_list.is_a?(String)
186
- return path_or_list.map {|p| File.join(path_prefix, p)} if path_or_list.is_a?(Array)
187
- raise StandardError, 'expect: nil, String or Array'
183
+ # directory: node, container: shares
184
+ FOLDER_TYPE = %w[directory container].freeze
185
+
186
+ def browse_gen3(prefix_path)
187
+ folders_to_process = [get_one_argument_with_prefix(prefix_path, 'path')]
188
+ query = options.get_option(:query, default: {})
189
+ # special parameter: max number of entries in result
190
+ max_items = query.delete('max')
191
+ # special parameter: recursive browsing
192
+ recursive = query.delete('recursive')
193
+ # special parameter: only return one entry for the path, even if folder
194
+ only_path = query.delete('self')
195
+ # allow user to specify a single call, and not recursive
196
+ single_call = query.key?('skip')
197
+ # API default is 100, so use 1000 for default
198
+ query['count'] ||= 1000
199
+ raise Cli::BadArgument, 'options recursive and skip cannot be used together' if recursive && single_call
200
+ all_items = []
201
+ until folders_to_process.empty?
202
+ path = folders_to_process.shift
203
+ query['path'] = path
204
+ offset = 0
205
+ total_count = nil
206
+ result = nil
207
+ loop do
208
+ # example: send_result={'items'=>[{'file'=>"filename1","permissions"=>[{'name'=>'read'},{'name'=>'write'}]}]}
209
+ response = @api_node.call(
210
+ operation: 'POST',
211
+ subpath: 'files/browse',
212
+ headers: {'Accept' => 'application/json'},
213
+ body: query,
214
+ body_type: :json)
215
+ # 'file','symbolic_link'
216
+ if only_path || !FOLDER_TYPE.include?(response[:data]['self']['type'])
217
+ result = { type: :single_object, data: response[:data]['self']}
218
+ break
219
+ end
220
+ items = response[:data]['items']
221
+ total_count ||= response[:data]['total_count']
222
+ all_items.concat(items)
223
+ if single_call
224
+ formatter.display_item_count(response[:data]['item_count'], total_count)
225
+ break
226
+ end
227
+ if recursive
228
+ folders_to_process.concat(items.select{|i|FOLDER_TYPE.include?(i['type'])}.map{|i|i['path']})
229
+ end
230
+ if !max_items.nil? && (all_items.count >= max_items)
231
+ all_items = all_items.slice(0, max_items) if all_items.count > max_items
232
+ break
233
+ end
234
+ break if all_items.count >= total_count
235
+ offset += items.count
236
+ query['skip'] = offset
237
+ formatter.long_operation_running(all_items.count)
238
+ end
239
+ query.delete('skip')
240
+ end
241
+ result ||= {type: :object_list, data: all_items}
242
+ return c_result_remove_prefix_path(result, 'path', prefix_path)
188
243
  end
189
244
 
190
245
  # file and folder related commands
191
246
  def execute_command_gen3(command, prefix_path)
192
247
  case command
193
248
  when :delete
194
- paths_to_delete = get_next_arg_add_prefix(prefix_path, 'file list', :multiple)
249
+ # TODO: add query for recursive
250
+ paths_to_delete = get_all_arguments_with_prefix(prefix_path, 'file list')
195
251
  resp = @api_node.create('files/delete', { paths: paths_to_delete.map{|i| {'path' => i.start_with?('/') ? i : "/#{i}"} }})
196
252
  return c_result_translate_rem_prefix(resp, 'file', 'deleted', prefix_path)
197
253
  when :search
198
- search_root = get_next_arg_add_prefix(prefix_path, 'search root')
254
+ search_root = get_one_argument_with_prefix(prefix_path, 'search root')
199
255
  parameters = {'path' => search_root}
200
256
  other_options = query_option
201
257
  parameters.merge!(other_options) unless other_options.nil?
@@ -207,48 +263,34 @@ module Aspera
207
263
  formatter.display_status("params: #{resp[:data]['parameters'].keys.map{|k|"#{k}:#{resp[:data]['parameters'][k]}"}.join(',')}")
208
264
  return c_result_remove_prefix_path(result, 'path', prefix_path)
209
265
  when :space
210
- path_list = get_next_arg_add_prefix(prefix_path, 'folder path or ext.val. list')
211
- path_list = [path_list] unless path_list.is_a?(Array)
266
+ path_list = get_all_arguments_with_prefix(prefix_path, 'folder path or ext.val. list')
212
267
  resp = @api_node.create('space', { 'paths' => path_list.map {|i| { path: i} } })
213
- result = { data: resp[:data]['paths'], type: :object_list}
268
+ result = { type: :object_list, data: resp[:data]['paths']}
214
269
  # return c_result_translate_rem_prefix(resp,'folder','created',prefix_path)
215
270
  return c_result_remove_prefix_path(result, 'path', prefix_path)
216
271
  when :mkdir
217
- path_list = get_next_arg_add_prefix(prefix_path, 'folder path or ext.val. list')
218
- path_list = [path_list] unless path_list.is_a?(Array)
219
- resp = @api_node.create('files/create', { 'paths' => [{ type: :directory, path: path_list }] })
272
+ path_list = get_all_arguments_with_prefix(prefix_path, 'folder path or ext.val. list')
273
+ resp = @api_node.create('files/create', { 'paths' => path_list.map{|i|{ type: :directory, path: i }}})
220
274
  return c_result_translate_rem_prefix(resp, 'folder', 'created', prefix_path)
221
275
  when :mklink
222
- target = get_next_arg_add_prefix(prefix_path, 'target')
223
- path_list = get_next_arg_add_prefix(prefix_path, 'link path')
224
- resp = @api_node.create('files/create', { 'paths' => [{ type: :symbolic_link, path: path_list, target: { path: target} }] })
276
+ target = get_one_argument_with_prefix(prefix_path, 'target')
277
+ one_path = get_one_argument_with_prefix(prefix_path, 'link path')
278
+ resp = @api_node.create('files/create', { 'paths' => [{ type: :symbolic_link, path: one_path, target: { path: target} }] })
225
279
  return c_result_translate_rem_prefix(resp, 'folder', 'created', prefix_path)
226
280
  when :mkfile
227
- path_list = get_next_arg_add_prefix(prefix_path, 'file path')
281
+ one_path = get_one_argument_with_prefix(prefix_path, 'file path')
228
282
  contents64 = Base64.strict_encode64(options.get_next_argument('contents'))
229
- resp = @api_node.create('files/create', { 'paths' => [{ type: :file, path: path_list, contents: contents64 }] })
283
+ resp = @api_node.create('files/create', { 'paths' => [{ type: :file, path: one_path, contents: contents64 }] })
230
284
  return c_result_translate_rem_prefix(resp, 'folder', 'created', prefix_path)
231
285
  when :rename
232
- path_base = get_next_arg_add_prefix(prefix_path, 'path_base')
233
- path_src = get_next_arg_add_prefix(prefix_path, 'path_src')
234
- path_dst = get_next_arg_add_prefix(prefix_path, 'path_dst')
286
+ # TODO: multiple ?
287
+ path_base = get_one_argument_with_prefix(prefix_path, 'path_base')
288
+ path_src = get_one_argument_with_prefix(prefix_path, 'path_src')
289
+ path_dst = get_one_argument_with_prefix(prefix_path, 'path_dst')
235
290
  resp = @api_node.create('files/rename', { 'paths' => [{ 'path' => path_base, 'source' => path_src, 'destination' => path_dst }] })
236
291
  return c_result_translate_rem_prefix(resp, 'entry', 'moved', prefix_path)
237
292
  when :browse
238
- query = { path: get_next_arg_add_prefix(prefix_path, 'path')}
239
- additional_query = options.get_option(:query)
240
- query.merge!(additional_query) unless additional_query.nil?
241
- send_result = @api_node.create('files/browse', query)[:data]
242
- # example: send_result={'items'=>[{'file'=>"filename1","permissions"=>[{'name'=>'read'},{'name'=>'write'}]}]}
243
- # if there is no items
244
- case send_result['self']['type']
245
- when 'directory', 'container' # directory: node, container: shares
246
- result = { data: send_result['items'], type: :object_list }
247
- formatter.display_item_count(send_result['item_count'], send_result['total_count'])
248
- else # 'file','symbolic_link'
249
- result = { data: send_result['self'], type: :single_object}
250
- end
251
- return c_result_remove_prefix_path(result, 'path', prefix_path)
293
+ return browse_gen3(prefix_path)
252
294
  when :sync
253
295
  return execute_sync_action do |sync_direction, local_path, remote_path|
254
296
  # Gen3 API
@@ -296,13 +338,15 @@ module Aspera
296
338
  transfer_spec.delete('paths') if command.eql?(:upload)
297
339
  return Main.result_transfer(transfer.start(transfer_spec))
298
340
  when :http_node_download
299
- remote_path = get_next_arg_add_prefix(prefix_path, 'remote path')
341
+ remote_path = get_one_argument_with_prefix(prefix_path, 'remote path')
300
342
  file_name = File.basename(remote_path)
301
343
  @api_node.call(
302
344
  operation: 'GET',
303
345
  subpath: "files/#{URI.encode_www_form_component(remote_path)}/contents",
304
346
  save_to_file: File.join(transfer.destination_folder(Transfer::Spec::DIRECTION_RECEIVE), file_name))
305
347
  return Main.result_status("downloaded: #{file_name}")
348
+ when :transport
349
+ return {type: :single_object, data: @api_node.transport_params}
306
350
  end
307
351
  Aspera.error_unreachable_line
308
352
  end
@@ -338,7 +382,7 @@ module Aspera
338
382
  when :set_bearer_key
339
383
  access_key_id = options.get_next_argument('access key id')
340
384
  access_key_id = @api_node.read('access_keys/self')[:data]['id'] if access_key_id.eql?('self')
341
- bearer_key_pem = options.get_next_argument('public or private RSA key PEM value', type: String)
385
+ bearer_key_pem = options.get_next_argument('public or private RSA key PEM value', validation: String)
342
386
  key = OpenSSL::PKey.read(bearer_key_pem)
343
387
  key = key.public_key if key.private?
344
388
  bearer_key_pem = key.to_pem
@@ -358,9 +402,10 @@ module Aspera
358
402
  begin
359
403
  @api_node.call(
360
404
  operation: 'POST',
361
- subpath: 'services/soap/Transfer-201210',
362
- headers: {'Content-Type' => 'text/xml;charset=UTF-8', 'SOAPAction' => 'FASPSessionNET-200911#GetSessionInfo'},
363
- text_body_params: CENTRAL_SOAP_API_TEST)[:http].body
405
+ subpath: 'services/soap/Transfer-201210',
406
+ headers: {'Content-Type' => 'text/xml;charset=UTF-8', 'SOAPAction' => 'FASPSessionNET-200911#GetSessionInfo'},
407
+ body: CENTRAL_SOAP_API_TEST,
408
+ body_type: :text)[:http].body
364
409
  nagios.add_ok('central', 'accessible by node')
365
410
  rescue StandardError => e
366
411
  nagios.add_critical('central', e.to_s)
@@ -420,7 +465,7 @@ module Aspera
420
465
  result[:password] = apifid[:api].auth_params[:password]
421
466
  when :oauth2
422
467
  result[:username] = apifid[:api].params[:headers][Api::Node::HEADER_X_ASPERA_ACCESS_KEY]
423
- result[:password] = apifid[:api].oauth_token
468
+ result[:password] = apifid[:api].oauth.token
424
469
  else Aspera.error_unreachable_line
425
470
  end
426
471
  return {type: :single_object, data: result} if command_repo.eql?(:node_info)
@@ -431,7 +476,7 @@ module Aspera
431
476
  apifid = @api_node.resolve_api_fid(top_file_id, options.get_next_argument('path'))
432
477
  file_info = apifid[:api].read("files/#{apifid[:file_id]}")[:data]
433
478
  if file_info['type'].eql?('folder')
434
- result = apifid[:api].read("files/#{apifid[:file_id]}/files", old_query_read_delete)
479
+ result = apifid[:api].read("files/#{apifid[:file_id]}/files", query_read_delete)
435
480
  items = result[:data]
436
481
  formatter.display_item_count(result[:data].length, result[:http]['X-Total-Count'])
437
482
  else
@@ -528,7 +573,7 @@ module Aspera
528
573
  return {type: :single_object, data: items}
529
574
  when :modify
530
575
  apifid = apifid_from_next_arg(top_file_id)
531
- update_param = options.get_next_argument('update data', type: Hash)
576
+ update_param = options.get_next_argument('update data', validation: Hash)
532
577
  apifid[:api].update("files/#{apifid[:file_id]}", update_param)[:data]
533
578
  return Main.result_status('Done')
534
579
  when :thumbnail
@@ -538,7 +583,7 @@ module Aspera
538
583
  subpath: "files/#{apifid[:file_id]}/preview",
539
584
  headers: {'Accept' => 'image/png'}
540
585
  )
541
- return Main.result_picture_in_terminal(options, result[:http].body)
586
+ return Main.result_image(result[:http].body, formatter: formatter)
542
587
  when :permission
543
588
  apifid = apifid_from_next_arg(top_file_id)
544
589
  command_perm = options.get_next_command(%i[list create delete])
@@ -559,7 +604,7 @@ module Aspera
559
604
  {'id' => one_id}
560
605
  end
561
606
  when :create
562
- create_param = options.get_next_argument('creation data', type: Hash)
607
+ create_param = options.get_next_argument('creation data', validation: Hash)
563
608
  raise 'no file_id' if create_param.key?('file_id')
564
609
  create_param['file_id'] = apifid[:file_id]
565
610
  create_param['access_levels'] = Api::Node::ACCESS_LEVELS unless create_param.key?('access_levels')
@@ -574,9 +619,9 @@ module Aspera
574
619
  else Aspera.error_unreachable_line
575
620
  end
576
621
  else Aspera.error_unreachable_line
577
- end # command_repo
622
+ end
578
623
  Aspera.error_unreachable_line
579
- end # execute_command_gen4
624
+ end
580
625
 
581
626
  # This is older API
582
627
  def execute_async
@@ -585,7 +630,7 @@ module Aspera
585
630
  async_name = options.get_option(:sync_name)
586
631
  if async_name.nil?
587
632
  async_id = instance_identifier
588
- if async_id.eql?(ExtendedValue::ALL) && %i[show delete].include?(command)
633
+ if async_id.eql?(SpecialValues::ALL) && %i[show delete].include?(command)
589
634
  async_ids = @api_node.read('async/list')[:data]['sync_ids']
590
635
  else
591
636
  Integer(async_id) # must be integer
@@ -608,7 +653,7 @@ module Aspera
608
653
  when :show
609
654
  resp = @api_node.create('async/summary', post_data)[:data]['sync_summaries']
610
655
  return Main.result_empty if resp.empty?
611
- return { type: :object_list, data: resp, fields: %w[snid name local_dir remote_dir] } if async_id.eql?(ExtendedValue::ALL)
656
+ return { type: :object_list, data: resp, fields: %w[snid name local_dir remote_dir] } if async_id.eql?(SpecialValues::ALL)
612
657
  return { type: :single_object, data: resp.first }
613
658
  when :delete
614
659
  resp = @api_node.create('async/delete', post_data)[:data]
@@ -695,11 +740,15 @@ module Aspera
695
740
  when *Plugin::ALL_OPS then return entity_command(sync_command, @api_node, 'asyncs', item_list_key: 'ids'){|field, value|ssync_lookup(field, value)}
696
741
  else
697
742
  asyncs_id = instance_identifier {|field, value|ssync_lookup(field, value)}
698
- parameters = nil
699
743
  if %i[start stop].include?(sync_command)
700
- @api_node.create("asyncs/#{asyncs_id}/#{sync_command}", parameters)
744
+ @api_node.call(
745
+ operation: 'POST',
746
+ subpath: "asyncs/#{asyncs_id}/#{sync_command}",
747
+ body: '',
748
+ body_type: :text)[:http].body
701
749
  return Main.result_status('Done')
702
750
  end
751
+ parameters = nil
703
752
  parameters = query_option(default: {}) if %i[bandwidth counters files].include?(sync_command)
704
753
  return { type: :single_object, data: @api_node.read("asyncs/#{asyncs_id}/#{sync_command}", parameters)[:data] }
705
754
  end
@@ -707,7 +756,7 @@ module Aspera
707
756
  command = options.get_next_command(%i[list create show modify cancel])
708
757
  case command
709
758
  when :list
710
- resp = @api_node.read('ops/transfers', old_query_read_delete)
759
+ resp = @api_node.read('ops/transfers', query_read_delete)
711
760
  return { type: :object_list, data: resp[:data], fields: %w[id status] } # TODO: useful?
712
761
  when :create
713
762
  resp = @api_node.create('streams', value_create_modify(command: command))
@@ -758,7 +807,7 @@ module Aspera
758
807
  resp = @api_node.read(one_res_path)
759
808
  return { type: :other_struct, data: resp[:data] }
760
809
  when :modify
761
- resp = @api_node.update(one_res_path, options.get_next_argument('update value', type: Hash))
810
+ resp = @api_node.update(one_res_path, options.get_next_argument('update value', validation: Hash))
762
811
  return { type: :other_struct, data: resp[:data] }
763
812
  when :bandwidth_average
764
813
  transfers_data = @api_node.read(res_class_path, query_read_delete)[:data]
@@ -819,7 +868,7 @@ module Aspera
819
868
  return { type: :object_list, data: resp[:data]['services'] }
820
869
  when :create
821
870
  # @json:'{"type":"WATCHFOLDERD","run_as":{"user":"user1"}}'
822
- params = options.get_next_argument('Run creation data (structure)')
871
+ params = options.get_next_argument('creation data', validation: Hash)
823
872
  resp = @api_node.create('rund/services', params)
824
873
  return Main.result_status("#{resp[:data]['id']} created")
825
874
  when :delete
@@ -841,7 +890,7 @@ module Aspera
841
890
  resp = @api_node.create(res_class_path, value_create_modify(command: command))
842
891
  return Main.result_status("#{resp[:data]['id']} created")
843
892
  when :list
844
- resp = @api_node.read(res_class_path, old_query_read_delete)
893
+ resp = @api_node.read(res_class_path, query_read_delete)
845
894
  return { type: :value_list, data: resp[:data]['ids'], name: 'id' }
846
895
  when :show
847
896
  return { type: :single_object, data: @api_node.read(one_res_path)[:data]}
@@ -895,13 +944,13 @@ module Aspera
895
944
  }
896
945
  # encode parameters so that it looks good in url
897
946
  encoded_params = Base64.strict_encode64(Zlib::Deflate.deflate(JSON.generate(browse_params))).gsub(/=+$/, '').tr('+/', '-_').reverse
898
- OpenApplication.instance.uri("#{options.get_option(:asperabrowserurl)}?goto=#{encoded_params}")
947
+ Environment.instance.open_uri("#{options.get_option(:asperabrowserurl)}?goto=#{encoded_params}")
899
948
  return Main.result_status('done')
900
949
  when :basic_token
901
950
  return Main.result_status(Rest.basic_token(options.get_option(:username, mandatory: true), options.get_option(:password, mandatory: true)))
902
951
  when :bearer_token
903
- private_key = OpenSSL::PKey::RSA.new(options.get_next_argument('private RSA key PEM value', type: String))
904
- token_info = options.get_next_argument('user and group identification', type: Hash)
952
+ private_key = OpenSSL::PKey::RSA.new(options.get_next_argument('private RSA key PEM value', validation: String))
953
+ token_info = options.get_next_argument('user and group identification', validation: Hash)
905
954
  access_key = options.get_option(:username, mandatory: true)
906
955
  return Main.result_status(Api::Node.bearer_token(payload: token_info, access_key: access_key, private_key: private_key))
907
956
  when :simulator
@@ -914,9 +963,25 @@ module Aspera
914
963
  server.mount(uri.path, NodeSimulatorServlet, parameters[:credentials], transfer)
915
964
  server.start
916
965
  return Main.result_status('Simulator terminated')
917
- end # case command
966
+ end
918
967
  raise 'ERROR: shall not reach this line'
919
968
  end
969
+
970
+ private
971
+
972
+ # get remaining path arguments from command line, and add prefix
973
+ def get_all_arguments_with_prefix(path_prefix, name)
974
+ path_args = options.get_next_argument(name, multiple: true)
975
+ return path_args if path_prefix.nil?
976
+ return path_args.map {|p| File.join(path_prefix, p)}
977
+ end
978
+
979
+ # get next path argument from command line, and add prefix
980
+ def get_one_argument_with_prefix(path_prefix, name)
981
+ path_arg = options.get_next_argument(name, validation: String)
982
+ return path_arg if path_prefix.nil?
983
+ return File.join(path_prefix, path_arg)
984
+ end
920
985
  end
921
986
  end
922
987
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'aspera/cli/basic_auth_plugin'
4
+ require 'aspera/cli/special_values'
4
5
  require 'aspera/nagios'
5
6
  require 'aspera/log'
6
7
  require 'aspera/assert'
@@ -60,33 +61,37 @@ module Aspera
60
61
 
61
62
  ACTIONS = %i[health info workflow plugins processes].freeze
62
63
 
63
- def call_ao(endpoint, opt={})
64
- opt[:prefix] = 'api' unless opt.key?(:prefix)
64
+ # call orchestrator api
65
+ # @param endpoint [String] the endpoint to call
66
+ # @param prefix [String] the prefix to add to the endpoint
67
+ # @param id [String] the id to add to the endpoint
68
+ # @param ret_style [Symbol] the return style, :header, :arg, :ext(extension)
69
+ # @param format [String] the format to request, 'json', 'xml', nil
70
+ # @param args [Hash] the arguments to pass
71
+ # @param xml_arrays [Boolean] if true, force arrays in xml parsing
72
+ def call_ao(endpoint, prefix: 'api', id: nil, ret_style: nil, format: 'json', args: nil, xml_arrays: true)
65
73
  # calls are GET
66
74
  call_args = {operation: 'GET', subpath: endpoint}
67
75
  # specify prefix if necessary
68
- call_args[:subpath] = "#{opt[:prefix]}/#{call_args[:subpath]}" unless opt[:prefix].nil?
76
+ call_args[:subpath] = "#{prefix}/#{call_args[:subpath]}" unless prefix.nil?
69
77
  # specify id if necessary
70
- call_args[:subpath] = "#{call_args[:subpath]}/#{opt[:id]}" if opt.key?(:id)
71
- call_type = options.get_option(:ret_style, mandatory: true)
72
- call_type = opt[:ret_style] if opt.key?(:ret_style)
73
- format = 'json'
74
- format = opt[:format] if opt.key?(:format)
75
- call_args[:url_params] = opt[:args] unless opt[:args].nil?
78
+ call_args[:subpath] = "#{call_args[:subpath]}/#{id}" unless id.nil?
79
+ ret_style = options.get_option(:ret_style, mandatory: true) if ret_style.nil?
80
+ call_args[:query] = args unless args.nil?
76
81
  unless format.nil?
77
- case call_type
82
+ case ret_style
78
83
  when :header
79
84
  call_args[:headers] = {'Accept' => "application/#{format}" }
80
85
  when :arg
81
- call_args[:url_params] ||= {}
82
- call_args[:url_params][:format] = format
86
+ call_args[:query] ||= {}
87
+ call_args[:query][:format] = format
83
88
  when :ext
84
89
  call_args[:subpath] = "#{call_args[:subpath]}.#{format}"
85
- else Aspera.error_unexpected_value(call_type)
90
+ else Aspera.error_unexpected_value(ret_style)
86
91
  end
87
92
  end
88
93
  result = @api_orch.call(**call_args)
89
- result[:data] = XmlSimple.xml_in(result[:http].body, opt[:xml_opt] || {'ForceArray' => true}) if format.eql?('xml')
94
+ result[:data] = XmlSimple.xml_in(result[:http].body, {'ForceArray' => xml_arrays}) if format.eql?('xml')
90
95
  Log.log.debug{Log.dump(:data, result[:data])}
91
96
  return result
92
97
  end
@@ -121,7 +126,7 @@ module Aspera
121
126
  when :health
122
127
  nagios = Nagios.new
123
128
  begin
124
- info = call_ao('remote_node_ping', format: 'xml', xml_opt: {'ForceArray' => false})[:data]
129
+ info = call_ao('remote_node_ping', format: 'xml', xml_arrays: false)[:data]
125
130
  nagios.add_ok('api', 'accessible')
126
131
  nagios.check_product_version('api', 'orchestrator', info['orchestrator-version'])
127
132
  rescue StandardError => e
@@ -129,7 +134,7 @@ module Aspera
129
134
  end
130
135
  return nagios.result
131
136
  when :info
132
- result = call_ao('remote_node_ping', format: 'xml', xml_opt: {'ForceArray' => false})[:data]
137
+ result = call_ao('remote_node_ping', format: 'xml', xml_arrays: false)[:data]
133
138
  return {type: :single_object, data: result}
134
139
  when :processes
135
140
  # TODO: Bug ? API has only XML format
@@ -146,9 +151,8 @@ module Aspera
146
151
  end
147
152
  case command
148
153
  when :status
149
- call_opts = {}
150
- call_opts[:id] = wf_id unless wf_id.eql?(ExtendedValue::ALL)
151
- result = call_ao('workflows_status', call_opts)[:data]
154
+ wf_id = nil if wf_id.eql?(SpecialValues::ALL)
155
+ result = call_ao('workflows_status', id: wf_id)[:data]
152
156
  return {type: :object_list, data: result['workflows']['workflow']}
153
157
  when :list
154
158
  result = call_ao('workflows_list', id: 0)[:data]
@@ -172,9 +176,8 @@ module Aspera
172
176
  data: nil
173
177
  }
174
178
  call_params = {format: :json}
175
- override_accept = nil
176
179
  # get external parameters if any
177
- options.get_next_argument('external_parameters', mandatory: false, type: Hash, default: {}).each do |name, value|
180
+ options.get_next_argument('external_parameters', mandatory: false, validation: Hash, default: {}).each do |name, value|
178
181
  call_params["external_parameters[#{name}]"] = value
179
182
  end
180
183
  # synchronous call ?
@@ -192,15 +195,15 @@ module Aspera
192
195
  end
193
196
  if call_params['synchronous']
194
197
  result[:type] = :text
195
- override_accept = 'text/plain'
196
198
  end
197
- result[:data] = call_ao('initiate', id: wf_id, args: call_params, accept: override_accept)[:data]
199
+ result[:data] = call_ao('initiate', id: wf_id, args: call_params)[:data]
198
200
  return result
199
- end # wf command
201
+ end
200
202
  else Aspera.error_unexpected_value(command)
201
- end # case command
202
- end # execute_action
203
- end # Orchestrator
204
- end # Plugins
205
- end # Cli
206
- end # Aspera
203
+ end
204
+ end
205
+ private :call_ao
206
+ end
207
+ end
208
+ end
209
+ end
@@ -128,7 +128,11 @@ module Aspera
128
128
  def get_folder_entries(file_id, request_args=nil)
129
129
  headers = {'Accept' => 'application/json'}
130
130
  headers['X-Aspera-Cache-Control'] = 'no-cache' if @option_folder_reset_cache.eql?(:header)
131
- return @api_node.call(operation: 'GET', subpath: "files/#{file_id}/files", headers: headers, url_params: request_args)[:data]
131
+ return @api_node.call(
132
+ operation: 'GET',
133
+ subpath: "files/#{file_id}/files",
134
+ headers: headers,
135
+ query: request_args)[:data]
132
136
  # return @api_node.read("files/#{file_id}/files",request_args)[:data]
133
137
  end
134
138
 
@@ -338,7 +342,7 @@ module Aspera
338
342
  rescue StandardError => e
339
343
  Log.log.error{"Ignore: #{e.message}"}
340
344
  Log.log.debug(e.backtrace.join("\n").red)
341
- end # generate_preview
345
+ end
342
346
 
343
347
  # scan all files in provided folder entry
344
348
  # @param top_path subpath to start folder scan inside
@@ -485,7 +489,7 @@ module Aspera
485
489
  return Main.result_status('Tools validated')
486
490
  when :test, :show
487
491
  source = options.get_next_argument('source file')
488
- format = options.get_next_argument('format', expected: Aspera::Preview::Generator::PREVIEW_FORMATS, default: :png)
492
+ format = options.get_next_argument('format', accept_list: Aspera::Preview::Generator::PREVIEW_FORMATS, default: :png)
489
493
  generated_file_path = preview_filename(format, options.get_option(:base))
490
494
  g = Aspera::Preview::Generator.new(source, generated_file_path, @gen_options, @tmp_folder, nil)
491
495
  g.generate
@@ -232,7 +232,7 @@ module Aspera
232
232
  when *TRANSFER_COMMANDS
233
233
  return execute_transfer(command, server_transfer_spec)
234
234
  when *AsCmd::OPERATIONS
235
- command_arguments = options.get_next_argument('ascmd command arguments', expected: :multiple, mandatory: false)
235
+ command_arguments = options.get_next_argument('ascmd command arguments', multiple: true, mandatory: false)
236
236
  ascmd = AsCmd.new(ascmd_executor)
237
237
  begin
238
238
  result = ascmd.execute_single(command, command_arguments)
@@ -251,8 +251,8 @@ module Aspera
251
251
  end
252
252
  else Aspera.error_unreachable_line
253
253
  end
254
- end # execute_action
255
- end # Server
256
- end # Plugins
257
- end # Cli
258
- end # Aspera
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end