aspera-cli 4.10.0 → 4.12.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 (97) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/BUGS.md +19 -0
  4. data/CHANGELOG.md +528 -0
  5. data/CONTRIBUTING.md +143 -0
  6. data/README.md +977 -589
  7. data/bin/ascli +4 -4
  8. data/bin/asession +12 -12
  9. data/docs/test_env.conf +29 -19
  10. data/examples/aoc.rb +6 -6
  11. data/examples/dascli +18 -16
  12. data/examples/faspex4.rb +15 -15
  13. data/examples/node.rb +12 -12
  14. data/examples/proxy.pac +2 -2
  15. data/examples/server.rb +12 -12
  16. data/lib/aspera/aoc.rb +344 -272
  17. data/lib/aspera/ascmd.rb +56 -54
  18. data/lib/aspera/ats_api.rb +4 -4
  19. data/lib/aspera/cli/basic_auth_plugin.rb +15 -12
  20. data/lib/aspera/cli/extended_value.rb +9 -9
  21. data/lib/aspera/cli/{formater.rb → formatter.rb} +69 -69
  22. data/lib/aspera/cli/listener/line_dump.rb +1 -1
  23. data/lib/aspera/cli/listener/logger.rb +1 -1
  24. data/lib/aspera/cli/listener/progress.rb +5 -6
  25. data/lib/aspera/cli/listener/progress_multi.rb +16 -21
  26. data/lib/aspera/cli/main.rb +72 -73
  27. data/lib/aspera/cli/manager.rb +112 -112
  28. data/lib/aspera/cli/plugin.rb +68 -48
  29. data/lib/aspera/cli/plugins/alee.rb +4 -4
  30. data/lib/aspera/cli/plugins/aoc.rb +322 -720
  31. data/lib/aspera/cli/plugins/ats.rb +50 -52
  32. data/lib/aspera/cli/plugins/bss.rb +10 -10
  33. data/lib/aspera/cli/plugins/config.rb +514 -410
  34. data/lib/aspera/cli/plugins/console.rb +12 -12
  35. data/lib/aspera/cli/plugins/cos.rb +18 -20
  36. data/lib/aspera/cli/plugins/faspex.rb +134 -136
  37. data/lib/aspera/cli/plugins/faspex5.rb +235 -70
  38. data/lib/aspera/cli/plugins/node.rb +378 -309
  39. data/lib/aspera/cli/plugins/orchestrator.rb +52 -49
  40. data/lib/aspera/cli/plugins/preview.rb +129 -120
  41. data/lib/aspera/cli/plugins/server.rb +137 -83
  42. data/lib/aspera/cli/plugins/shares.rb +77 -52
  43. data/lib/aspera/cli/plugins/sync.rb +13 -33
  44. data/lib/aspera/cli/transfer_agent.rb +61 -61
  45. data/lib/aspera/cli/version.rb +2 -1
  46. data/lib/aspera/colors.rb +3 -3
  47. data/lib/aspera/command_line_builder.rb +78 -74
  48. data/lib/aspera/cos_node.rb +31 -29
  49. data/lib/aspera/data_repository.rb +1 -1
  50. data/lib/aspera/environment.rb +30 -28
  51. data/lib/aspera/fasp/agent_base.rb +17 -15
  52. data/lib/aspera/fasp/agent_connect.rb +34 -32
  53. data/lib/aspera/fasp/agent_direct.rb +70 -73
  54. data/lib/aspera/fasp/agent_httpgw.rb +79 -74
  55. data/lib/aspera/fasp/agent_node.rb +26 -26
  56. data/lib/aspera/fasp/agent_trsdk.rb +20 -20
  57. data/lib/aspera/fasp/error.rb +3 -2
  58. data/lib/aspera/fasp/error_info.rb +11 -8
  59. data/lib/aspera/fasp/installation.rb +80 -80
  60. data/lib/aspera/fasp/listener.rb +2 -2
  61. data/lib/aspera/fasp/parameters.rb +103 -92
  62. data/lib/aspera/fasp/parameters.yaml +313 -214
  63. data/lib/aspera/fasp/resume_policy.rb +10 -10
  64. data/lib/aspera/fasp/transfer_spec.rb +22 -2
  65. data/lib/aspera/fasp/uri.rb +7 -7
  66. data/lib/aspera/faspex_gw.rb +80 -159
  67. data/lib/aspera/faspex_postproc.rb +77 -0
  68. data/lib/aspera/hash_ext.rb +3 -3
  69. data/lib/aspera/id_generator.rb +5 -5
  70. data/lib/aspera/keychain/encrypted_hash.rb +23 -28
  71. data/lib/aspera/keychain/macos_security.rb +21 -20
  72. data/lib/aspera/log.rb +13 -13
  73. data/lib/aspera/nagios.rb +24 -23
  74. data/lib/aspera/node.rb +217 -38
  75. data/lib/aspera/oauth.rb +78 -74
  76. data/lib/aspera/open_application.rb +19 -11
  77. data/lib/aspera/persistency_action_once.rb +4 -4
  78. data/lib/aspera/persistency_folder.rb +13 -13
  79. data/lib/aspera/preview/file_types.rb +8 -8
  80. data/lib/aspera/preview/generator.rb +67 -67
  81. data/lib/aspera/preview/utils.rb +27 -27
  82. data/lib/aspera/proxy_auto_config.js +63 -63
  83. data/lib/aspera/proxy_auto_config.rb +19 -19
  84. data/lib/aspera/rest.rb +65 -67
  85. data/lib/aspera/rest_call_error.rb +2 -1
  86. data/lib/aspera/rest_error_analyzer.rb +22 -21
  87. data/lib/aspera/rest_errors_aspera.rb +16 -16
  88. data/lib/aspera/secret_hider.rb +17 -14
  89. data/lib/aspera/ssh.rb +15 -14
  90. data/lib/aspera/sync.rb +177 -62
  91. data/lib/aspera/temp_file_manager.rb +2 -2
  92. data/lib/aspera/uri_reader.rb +4 -4
  93. data/lib/aspera/web_auth.rb +13 -64
  94. data/lib/aspera/web_server_simple.rb +76 -0
  95. data.tar.gz.sig +0 -0
  96. metadata +11 -6
  97. metadata.gz.sig +0 -0
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'aspera/cli/basic_auth_plugin'
4
+ require 'aspera/cli/plugins/sync'
4
5
  require 'aspera/nagios'
5
6
  require 'aspera/hash_ext'
6
7
  require 'aspera/id_generator'
7
8
  require 'aspera/node'
9
+ require 'aspera/aoc'
8
10
  require 'aspera/fasp/transfer_spec'
9
11
  require 'base64'
10
12
  require 'zlib'
@@ -12,7 +14,7 @@ require 'zlib'
12
14
  module Aspera
13
15
  module Cli
14
16
  module Plugins
15
- class Node < BasicAuthPlugin
17
+ class Node < Aspera::Cli::BasicAuthPlugin
16
18
  class << self
17
19
  def detect(base_url)
18
20
  api = Rest.new({ base_url: base_url})
@@ -22,55 +24,101 @@ module Aspera
22
24
  end
23
25
  return nil
24
26
  end
27
+
28
+ def register_node_options(env)
29
+ env[:options].add_opt_simple(:validator, 'identifier of validator (optional for central)')
30
+ env[:options].add_opt_simple(:asperabrowserurl, 'URL for simple aspera web ui')
31
+ env[:options].add_opt_simple(:sync_name, 'sync name')
32
+ env[:options].add_opt_simple(:path, 'file or folder path for gen4 operation "file"')
33
+ env[:options].add_opt_list(:token_type, %i[aspera basic hybrid], 'Type of token used for transfers')
34
+ env[:options].add_opt_boolean(:default_ports, 'use standard FASP ports or get from node api (gen4)')
35
+ env[:options].set_option(:asperabrowserurl, 'https://asperabrowser.mybluemix.net')
36
+ env[:options].set_option(:token_type, :aspera)
37
+ env[:options].set_option(:default_ports, :yes)
38
+ env[:options].parse_options!
39
+ Aspera::Node.use_standard_ports = env[:options].get_option(:default_ports)
40
+ end
25
41
  end
26
- SAMPLE_SOAP_CALL = '<?xml version="1.0" encoding="UTF-8"?>'\
42
+
43
+ # spellchecker: disable
44
+ # SOAP API call to test central API
45
+ CENTRAL_SOAP_API_TEST = '<?xml version="1.0" encoding="UTF-8"?>'\
27
46
  '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="urn:Aspera:XML:FASPSessionNET:2009/11:Types">'\
28
47
  '<soapenv:Header></soapenv:Header>'\
29
48
  '<soapenv:Body><typ:GetSessionInfoRequest><SessionFilter><SessionStatus>running</SessionStatus></SessionFilter></typ:GetSessionInfoRequest></soapenv:Body>'\
30
49
  '</soapenv:Envelope>'
31
- SEARCH_REMOVE_FIELDS=%w[basename permissions].freeze
32
- private_constant :SAMPLE_SOAP_CALL,:SEARCH_REMOVE_FIELDS
50
+ # spellchecker: enable
51
+
52
+ # fields removed in result of search
53
+ SEARCH_REMOVE_FIELDS = %w[basename permissions].freeze
54
+
55
+ # actions in execute_command_gen3
56
+ COMMANDS_GEN3 = %i[search space mkdir mklink mkfile rename delete browse upload download]
57
+
58
+ BASE_ACTIONS = %i[api_details].concat(COMMANDS_GEN3).freeze
59
+
60
+ SPECIAL_ACTIONS = %i[health events info license].freeze
61
+
62
+ # actions available in v3 in gen4
63
+ V3_IN_V4_ACTIONS = %i[access_key].concat(BASE_ACTIONS).concat(SPECIAL_ACTIONS).freeze
64
+
65
+ # actions used commonly when a node is involved
66
+ COMMON_ACTIONS = %i[access_key].concat(BASE_ACTIONS).concat(SPECIAL_ACTIONS).freeze
67
+
68
+ private_constant(*%i[CENTRAL_SOAP_API_TEST SEARCH_REMOVE_FIELDS BASE_ACTIONS SPECIAL_ACTIONS V3_IN_V4_ACTIONS COMMON_ACTIONS])
69
+
70
+ # used in aoc
71
+ NODE4_READ_ACTIONS = %i[bearer_token_node node_info browse find].freeze
72
+
73
+ # commands for execute_command_gen4
74
+ COMMANDS_GEN4 = %i[mkdir rename delete upload download sync http_node_download file v3].concat(NODE4_READ_ACTIONS).freeze
75
+
76
+ COMMANDS_COS = %i[upload download info access_key api_details transfer].freeze
77
+ COMMANDS_SHARES = (BASE_ACTIONS - %i[search]).freeze
78
+ COMMANDS_FASPEX = COMMON_ACTIONS
33
79
 
34
80
  def initialize(env)
35
81
  super(env)
36
- # this is added to transfer spec, for instance to add tags (COS)
37
- @add_request_param = env[:add_request_param] || {}
38
- options.add_opt_simple(:validator,'identifier of validator (optional for central)')
39
- options.add_opt_simple(:asperabrowserurl,'URL for simple aspera web ui')
40
- options.add_opt_simple(:sync_name,'sync name')
41
- options.add_opt_list(:token_type,%i[aspera basic hybrid],'Type of token used for transfers')
42
- options.set_option(:asperabrowserurl,'https://asperabrowser.mybluemix.net')
43
- options.set_option(:token_type,:aspera)
44
- options.parse_options!
82
+ self.class.register_node_options(env) unless env[:skip_node_options]
45
83
  return if env[:man_only]
46
84
  @api_node =
47
- if env.has_key?(:node_api)
85
+ if env.key?(:node_api)
86
+ # this can be Aspera::Node or Aspera::Rest (shares)
48
87
  env[:node_api]
49
- elsif options.get_option(:password,is_type: :mandatory).start_with?('Bearer ')
88
+ elsif options.get_option(:password, is_type: :mandatory).start_with?('Bearer ')
50
89
  # info is provided like node_info of aoc
51
- Rest.new({
52
- base_url: options.get_option(:url,is_type: :mandatory),
90
+ Aspera::Node.new(params: {
91
+ base_url: options.get_option(:url, is_type: :mandatory),
53
92
  headers: {
54
- 'X-Aspera-AccessKey' => options.get_option(:username,is_type: :mandatory),
55
- 'Authorization' => options.get_option(:password,is_type: :mandatory)
93
+ Aspera::Node::HEADER_X_ASPERA_ACCESS_KEY => options.get_option(:username, is_type: :mandatory),
94
+ 'Authorization' => options.get_option(:password, is_type: :mandatory)
56
95
  }
57
96
  })
58
97
  else
59
98
  # this is normal case
60
- basic_auth_api
99
+ Aspera::Node.new(params: {
100
+ base_url: options.get_option(:url, is_type: :mandatory),
101
+ auth: {
102
+ type: :basic,
103
+ username: options.get_option(:username, is_type: :mandatory),
104
+ password: options.get_option(:password, is_type: :mandatory)
105
+ }})
61
106
  end
62
107
  end
63
108
 
64
109
  def c_textify_browse(table_data)
65
- return table_data.map {|i| i['permissions'] = i['permissions'].map { |x| x['name'] }.join(','); i }
110
+ return table_data.map do |i|
111
+ i['permissions'] = i['permissions'].map { |x| x['name'] }.join(',')
112
+ i
113
+ end
66
114
  end
67
115
 
68
116
  # key/value is defined in main in hash_table
69
- def c_textify_bool_list_result(list,name_list)
117
+ def c_textify_bool_list_result(list, name_list)
70
118
  list.each_index do |i|
71
119
  next unless name_list.include?(list[i]['key'])
72
120
  list[i]['value'].each do |item|
73
- list.push({'key' => item['name'],'value' => item['value']})
121
+ list.push({'key' => item['name'], 'value' => item['value']})
74
122
  end
75
123
  list.delete_at(i)
76
124
  # continue at same index because we delete current one
@@ -79,7 +127,7 @@ module Aspera
79
127
  end
80
128
 
81
129
  # reduce the path from a result on given named column
82
- def c_result_remove_prefix_path(result,column,path_prefix)
130
+ def c_result_remove_prefix_path(result, column, path_prefix)
83
131
  if !path_prefix.nil?
84
132
  case result[:type]
85
133
  when :object_list
@@ -95,337 +143,368 @@ module Aspera
95
143
  end
96
144
 
97
145
  # translates paths results into CLI result, and removes prefix
98
- def c_result_translate_rem_prefix(resp,type,success_msg,path_prefix)
99
- resres = { data: [], type: :object_list, fields: [type,'result']}
100
- JSON.parse(resp[:http].body)['paths'].each do |p|
146
+ def c_result_translate_rem_prefix(response, type, success_msg, path_prefix)
147
+ errors = []
148
+ resres = { data: [], type: :object_list, fields: [type, 'result']}
149
+ JSON.parse(response[:http].body)['paths'].each do |p|
101
150
  result = success_msg
102
- if p.has_key?('error')
103
- Log.log.error("#{p['error']['user_message']} : #{p['path']}")
151
+ if p.key?('error')
152
+ Log.log.error{"#{p['error']['user_message']} : #{p['path']}"}
104
153
  result = 'ERROR: ' + p['error']['user_message']
154
+ errors.push([p['path'], p['error']['user_message']])
105
155
  end
106
- resres[:data].push({type => p['path'],'result' => result})
156
+ resres[:data].push({type => p['path'], 'result' => result})
157
+ end
158
+ # one error make all fail
159
+ unless errors.empty?
160
+ raise errors.map{|i|"#{i.first}: #{i.last}"}.join(', ')
107
161
  end
108
- return c_result_remove_prefix_path(resres,type,path_prefix)
162
+ return c_result_remove_prefix_path(resres, type, path_prefix)
109
163
  end
110
164
 
111
165
  # get path arguments from command line, and add prefix
112
- def get_next_arg_add_prefix(path_prefix,name,number=:single)
113
- thepath = options.get_next_argument(name,expected: number)
114
- return thepath if path_prefix.nil?
115
- return File.join(path_prefix,thepath) if thepath.is_a?(String)
116
- return thepath.map {|p| File.join(path_prefix,p)} if thepath.is_a?(Array)
117
- raise StandardError,'expect: nil, String or Array'
166
+ def get_next_arg_add_prefix(path_prefix, name, number=:single)
167
+ path_or_list = options.get_next_argument(name, expected: number)
168
+ return path_or_list if path_prefix.nil?
169
+ return File.join(path_prefix, path_or_list) if path_or_list.is_a?(String)
170
+ return path_or_list.map {|p| File.join(path_prefix, p)} if path_or_list.is_a?(Array)
171
+ raise StandardError, 'expect: nil, String or Array'
118
172
  end
119
173
 
120
- SIMPLE_ACTIONS = %i[health events space info license mkdir mklink mkfile rename delete search].freeze
121
-
122
- COMMON_ACTIONS = %i[browse upload download api_details].concat(SIMPLE_ACTIONS).freeze
123
-
124
- # common API to node and Shares
125
- # prefix_path is used to list remote sources in Faspex
126
- def execute_simple_common(command,prefix_path)
174
+ # file and folder related commands
175
+ def execute_command_gen3(command, prefix_path)
127
176
  case command
128
- when :health
129
- nagios = Nagios.new
130
- begin
131
- info = @api_node.read('info')[:data]
132
- nagios.add_ok('node api','accessible')
133
- nagios.check_time_offset(info['current_time'],'node api')
134
- nagios.check_product_version('node api','entsrv', info['version'])
135
- rescue StandardError => e
136
- nagios.add_critical('node api',e.to_s)
137
- end
138
- begin
139
- @api_node.call(
140
- operation: 'POST',
141
- subpath: 'services/soap/Transfer-201210',
142
- headers: {'Content-Type' => 'text/xml;charset=UTF-8','SOAPAction' => 'FASPSessionNET-200911#GetSessionInfo'},
143
- text_body_params: SAMPLE_SOAP_CALL)[:http].body
144
- nagios.add_ok('central','accessible by node')
145
- rescue StandardError => e
146
- nagios.add_critical('central',e.to_s)
147
- end
148
- return nagios.result
149
- when :events
150
- events = @api_node.read('events',options.get_option(:value))[:data]
151
- return { type: :object_list, data: events}
152
- when :info
153
- node_info = @api_node.read('info')[:data]
154
- return { type: :single_object, data: node_info, textify: lambda { |table_data| c_textify_bool_list_result(table_data,%w[capabilities settings])}}
155
- when :license # requires: asnodeadmin -mu <node user> --acl-add=internal --internal
156
- node_license = @api_node.read('license')[:data]
157
- if node_license['failure'].is_a?(String) && node_license['failure'].include?('ACL')
158
- Log.log.error('server must have: asnodeadmin -mu <node user> --acl-add=internal --internal')
159
- end
160
- return { type: :single_object, data: node_license}
161
177
  when :delete
162
- paths_to_delete = get_next_arg_add_prefix(prefix_path,'file list',:multiple)
163
- resp = @api_node.create('files/delete',{ paths: paths_to_delete.map{|i| {'path' => i.start_with?('/') ? i : '/' + i} }})
164
- return c_result_translate_rem_prefix(resp,'file','deleted',prefix_path)
178
+ paths_to_delete = get_next_arg_add_prefix(prefix_path, 'file list', :multiple)
179
+ resp = @api_node.create('files/delete', { paths: paths_to_delete.map{|i| {'path' => i.start_with?('/') ? i : '/' + i} }})
180
+ return c_result_translate_rem_prefix(resp, 'file', 'deleted', prefix_path)
165
181
  when :search
166
- search_root = get_next_arg_add_prefix(prefix_path,'search root')
182
+ search_root = get_next_arg_add_prefix(prefix_path, 'search root')
167
183
  parameters = {'path' => search_root}
168
184
  other_options = options.get_option(:value)
169
185
  parameters.merge!(other_options) unless other_options.nil?
170
- resp = @api_node.create('files/search',parameters)
186
+ resp = @api_node.create('files/search', parameters)
171
187
  result = { type: :object_list, data: resp[:data]['items']}
172
188
  return Main.result_empty if result[:data].empty?
173
189
  result[:fields] = result[:data].first.keys.reject{|i|SEARCH_REMOVE_FIELDS.include?(i)}
174
- self.format.display_status("Items: #{resp[:data]['item_count']}/#{resp[:data]['total_count']}")
175
- self.format.display_status("params: #{resp[:data]['parameters'].keys.map{|k|"#{k}:#{resp[:data]['parameters'][k]}"}.join(',')}")
176
- return c_result_remove_prefix_path(result,'path',prefix_path)
190
+ formatter.display_status("Items: #{resp[:data]['item_count']}/#{resp[:data]['total_count']}")
191
+ formatter.display_status("params: #{resp[:data]['parameters'].keys.map{|k|"#{k}:#{resp[:data]['parameters'][k]}"}.join(',')}")
192
+ return c_result_remove_prefix_path(result, 'path', prefix_path)
177
193
  when :space
178
- # TODO: could be a list of path
179
- path_list = get_next_arg_add_prefix(prefix_path,'folder path or ext.val. list')
194
+ path_list = get_next_arg_add_prefix(prefix_path, 'folder path or ext.val. list')
180
195
  path_list = [path_list] unless path_list.is_a?(Array)
181
- resp = @api_node.create('space',{ 'paths' => path_list.map {|i| { path: i} } })
196
+ resp = @api_node.create('space', { 'paths' => path_list.map {|i| { path: i} } })
182
197
  result = { data: resp[:data]['paths'], type: :object_list}
183
- #return c_result_translate_rem_prefix(resp,'folder','created',prefix_path)
184
- return c_result_remove_prefix_path(result,'path',prefix_path)
198
+ # return c_result_translate_rem_prefix(resp,'folder','created',prefix_path)
199
+ return c_result_remove_prefix_path(result, 'path', prefix_path)
185
200
  when :mkdir
186
- path_list = get_next_arg_add_prefix(prefix_path,'folder path or ext.val. list')
201
+ path_list = get_next_arg_add_prefix(prefix_path, 'folder path or ext.val. list')
187
202
  path_list = [path_list] unless path_list.is_a?(Array)
188
- #TODO: a command for that ?
189
- #resp=@api_node.create('space',{ "paths" => path_list.map {|i| { type: :directory, path: i} } } )
190
- resp = @api_node.create('files/create',{ 'paths' => [{ type: :directory, path: path_list }] })
191
- return c_result_translate_rem_prefix(resp,'folder','created',prefix_path)
203
+ resp = @api_node.create('files/create', { 'paths' => [{ type: :directory, path: path_list }] })
204
+ return c_result_translate_rem_prefix(resp, 'folder', 'created', prefix_path)
192
205
  when :mklink
193
- target = get_next_arg_add_prefix(prefix_path,'target')
194
- path_list = get_next_arg_add_prefix(prefix_path,'link path')
195
- resp = @api_node.create('files/create',{ 'paths' => [{ type: :symbolic_link, path: path_list, target: { path: target} }] })
196
- return c_result_translate_rem_prefix(resp,'folder','created',prefix_path)
206
+ target = get_next_arg_add_prefix(prefix_path, 'target')
207
+ path_list = get_next_arg_add_prefix(prefix_path, 'link path')
208
+ resp = @api_node.create('files/create', { 'paths' => [{ type: :symbolic_link, path: path_list, target: { path: target} }] })
209
+ return c_result_translate_rem_prefix(resp, 'folder', 'created', prefix_path)
197
210
  when :mkfile
198
- path_list = get_next_arg_add_prefix(prefix_path,'file path')
211
+ path_list = get_next_arg_add_prefix(prefix_path, 'file path')
199
212
  contents64 = Base64.strict_encode64(options.get_next_argument('contents'))
200
- resp = @api_node.create('files/create',{ 'paths' => [{ type: :file, path: path_list, contents: contents64 }] })
201
- return c_result_translate_rem_prefix(resp,'folder','created',prefix_path)
213
+ resp = @api_node.create('files/create', { 'paths' => [{ type: :file, path: path_list, contents: contents64 }] })
214
+ return c_result_translate_rem_prefix(resp, 'folder', 'created', prefix_path)
202
215
  when :rename
203
- path_base = get_next_arg_add_prefix(prefix_path,'path_base')
204
- path_src = get_next_arg_add_prefix(prefix_path,'path_src')
205
- path_dst = get_next_arg_add_prefix(prefix_path,'path_dst')
206
- resp = @api_node.create('files/rename',{ 'paths' => [{ 'path' => path_base, 'source' => path_src, 'destination' => path_dst }] })
207
- return c_result_translate_rem_prefix(resp,'entry','moved',prefix_path)
216
+ path_base = get_next_arg_add_prefix(prefix_path, 'path_base')
217
+ path_src = get_next_arg_add_prefix(prefix_path, 'path_src')
218
+ path_dst = get_next_arg_add_prefix(prefix_path, 'path_dst')
219
+ resp = @api_node.create('files/rename', { 'paths' => [{ 'path' => path_base, 'source' => path_src, 'destination' => path_dst }] })
220
+ return c_result_translate_rem_prefix(resp, 'entry', 'moved', prefix_path)
208
221
  when :browse
209
- thepath = get_next_arg_add_prefix(prefix_path,'path')
210
- query = { path: thepath}
222
+ query = { path: get_next_arg_add_prefix(prefix_path, 'path')}
211
223
  additional_query = options.get_option(:query)
212
224
  query.merge!(additional_query) unless additional_query.nil?
213
225
  send_result = @api_node.create('files/browse', query)[:data]
214
- #example: send_result={'items'=>[{'file'=>"filename1","permissions"=>[{'name'=>'read'},{'name'=>'write'}]}]}
226
+ # example: send_result={'items'=>[{'file'=>"filename1","permissions"=>[{'name'=>'read'},{'name'=>'write'}]}]}
215
227
  # if there is no items
216
228
  case send_result['self']['type']
217
- when 'directory','container' # directory: node, container: shares
229
+ when 'directory', 'container' # directory: node, container: shares
218
230
  result = { data: send_result['items'], type: :object_list, textify: lambda { |table_data| c_textify_browse(table_data) } }
219
- self.format.display_status("Items: #{send_result['item_count']}/#{send_result['total_count']}")
231
+ formatter.display_status("Items: #{send_result['item_count']}/#{send_result['total_count']}")
220
232
  else # 'file','symbolic_link'
221
233
  result = { data: send_result['self'], type: :single_object}
222
- #result={ data: [send_result['self']] , type: :object_list, textify: lambda { |table_data| c_textify_browse(table_data) } }
223
- #raise "unknown type: #{send_result['self']['type']}"
234
+ # result={ data: [send_result['self']] , type: :object_list, textify: lambda { |table_data| c_textify_browse(table_data) } }
235
+ # raise "unknown type: #{send_result['self']['type']}"
224
236
  end
225
- return c_result_remove_prefix_path(result,'path',prefix_path)
226
- when :upload,:download
237
+ return c_result_remove_prefix_path(result, 'path', prefix_path)
238
+ when :upload, :download
227
239
  token_type = options.get_option(:token_type)
228
240
  # nil if Shares 1.x
229
241
  token_type = :aspera if token_type.nil?
230
242
  case token_type
231
- when :aspera,:hybrid
243
+ when :aspera, :hybrid
232
244
  # empty transfer spec for authorization request
233
- request_transfer_spec={}
245
+ request_transfer_spec = {}
234
246
  # set requested paths depending on direction
235
247
  request_transfer_spec[:paths] = command.eql?(:download) ? transfer.ts_source_paths : [{ destination: transfer.destination_folder('send') }]
236
248
  # add fixed parameters if any (for COS)
237
- request_transfer_spec.deep_merge!(@add_request_param)
249
+ @api_node.add_tspec_info(request_transfer_spec) if @api_node.respond_to?(:add_tspec_info)
238
250
  # prepare payload for single request
239
- setup_payload={transfer_requests: [{transfer_request: request_transfer_spec}]}
251
+ setup_payload = {transfer_requests: [{transfer_request: request_transfer_spec}]}
240
252
  # only one request, so only one answer
241
- transfer_spec = @api_node.create("files/#{command}_setup",setup_payload)[:data]['transfer_specs'].first['transfer_spec']
253
+ transfer_spec = @api_node.create("files/#{command}_setup", setup_payload)[:data]['transfer_specs'].first['transfer_spec']
242
254
  # delete this part, as the returned value contains only destination, and not sources
243
255
  transfer_spec.delete('paths') if command.eql?(:upload)
244
256
  when :basic
245
257
  raise 'shall have auth' unless @api_node.params[:auth].is_a?(Hash)
246
258
  raise 'shall be basic auth' unless @api_node.params[:auth][:type].eql?(:basic)
247
- ts_direction =
248
- case command
249
- when :upload then Fasp::TransferSpec::DIRECTION_SEND
250
- when :download then Fasp::TransferSpec::DIRECTION_RECEIVE
251
- else raise 'Error: need upload or download'
252
- end
253
- transfer_spec = {
254
- 'remote_host' => URI.parse(@api_node.params[:base_url]).host,
255
- 'remote_user' => Aspera::Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER,
256
- 'ssh_port' => Aspera::Fasp::TransferSpec::SSH_PORT,
257
- 'direction' => ts_direction,
258
- 'destination_root' => transfer.destination_folder(ts_direction)
259
- }.deep_merge(@add_request_param)
259
+ transfer_spec = {}.merge(Aspera::Fasp::TransferSpec::AK_TSPEC_BASE)
260
+ transfer_spec['remote_host'] = URI.parse(@api_node.params[:base_url]).host
261
+ Fasp::TransferSpec.action_to_direction(transfer_spec, command)
262
+ transfer_spec['destination_root'] = transfer.destination_folder(transfer_spec['direction'])
263
+ @api_node.add_tspec_info(transfer_spec) if @api_node.respond_to?(:add_tspec_info)
260
264
  else raise "ERROR: token_type #{tt}"
261
265
  end
262
266
  if %i[basic hybrid].include?(token_type)
263
- Aspera::Node.set_ak_basic_token(transfer_spec,@api_node.params[:auth][:username],@api_node.params[:auth][:password])
267
+ @api_node.ts_basic_token(transfer_spec)
268
+ end
269
+ return Main.result_transfer(transfer.start(transfer_spec))
270
+ end
271
+ raise 'INTERNAL ERROR'
272
+ end
273
+
274
+ # common API to node and Shares
275
+ # prefix_path is used to list remote sources in Faspex
276
+ def execute_simple_common(command, prefix_path)
277
+ case command
278
+ when *COMMANDS_GEN3
279
+ execute_command_gen3(command, prefix_path)
280
+ when :access_key
281
+ ak_command = options.get_next_command([:do].concat(Plugin::ALL_OPS))
282
+ case ak_command
283
+ when *Plugin::ALL_OPS then return entity_command(ak_command, @api_node, 'access_keys', id_default: 'self')
284
+ when :do
285
+ access_key = options.get_next_argument('access key id')
286
+ ak_info = @api_node.read("access_keys/#{access_key}")[:data]
287
+ # change API credentials if different access key
288
+ if !access_key.eql?('self')
289
+ @api_node.params[:auth][:username] = ak_info['id']
290
+ @api_node.params[:auth][:password] = config.lookup_secret(url: @api_node.params[:base_url], username: ak_info['id'], mandatory: true)
291
+ end
292
+ command_repo = options.get_next_command(COMMANDS_GEN4)
293
+ return execute_command_gen4(command_repo, ak_info['root_file_id'])
294
+ end
295
+ when :health
296
+ nagios = Nagios.new
297
+ begin
298
+ info = @api_node.read('info')[:data]
299
+ nagios.add_ok('node api', 'accessible')
300
+ nagios.check_time_offset(info['current_time'], 'node api')
301
+ nagios.check_product_version('node api', 'entsrv', info['version'])
302
+ rescue StandardError => e
303
+ nagios.add_critical('node api', e.to_s)
304
+ end
305
+ begin
306
+ @api_node.call(
307
+ operation: 'POST',
308
+ subpath: 'services/soap/Transfer-201210',
309
+ headers: {'Content-Type' => 'text/xml;charset=UTF-8', 'SOAPAction' => 'FASPSessionNET-200911#GetSessionInfo'},
310
+ text_body_params: CENTRAL_SOAP_API_TEST)[:http].body
311
+ nagios.add_ok('central', 'accessible by node')
312
+ rescue StandardError => e
313
+ nagios.add_critical('central', e.to_s)
314
+ end
315
+ return nagios.result
316
+ when :events
317
+ events = @api_node.read('events', options.get_option(:value))[:data]
318
+ return { type: :object_list, data: events}
319
+ when :info
320
+ nd_info = @api_node.read('info')[:data]
321
+ return { type: :single_object, data: nd_info, textify: lambda { |table_data| c_textify_bool_list_result(table_data, %w[capabilities settings])}}
322
+ when :license
323
+ # requires: asnodeadmin -mu <node user> --acl-add=internal --internal
324
+ node_license = @api_node.read('license')[:data]
325
+ if node_license['failure'].is_a?(String) && node_license['failure'].include?('ACL')
326
+ Log.log.error('server must have: asnodeadmin -mu <node user> --acl-add=internal --internal')
264
327
  end
265
- return Main.result_transfer(transfer.start(transfer_spec,{ src: :node_gen3}))
328
+ return { type: :single_object, data: node_license}
266
329
  when :api_details
267
330
  return { type: :single_object, data: @api_node.params }
268
331
  end
269
332
  end
270
333
 
271
- # navigate the path from given file id
272
- # @param id initial file id
273
- # @param path file path
274
- # @return {.api,.file_id}
275
- def resolve_api_fid(id, path)
276
- # TODO: implement
277
- Log.log.debug("TODO #{path}")
278
- return {api: @api_node, file_id: id}
334
+ def execute_node_gen4_file_command(command_node_file, top_file_id)
335
+ file_path = options.get_option(:path)
336
+ apifid =
337
+ if file_path.nil?
338
+ {api: @api_node, file_id: instance_identifier}
339
+ else
340
+ @api_node.resolve_api_fid(top_file_id, file_path) # TODO: allow follow link ?
341
+ end
342
+ case command_node_file
343
+ when :show
344
+ items = apifid[:api].read("files/#{apifid[:file_id]}")[:data]
345
+ return {type: :single_object, data: items}
346
+ when :modify
347
+ update_param = options.get_next_argument('update data', type: Hash)
348
+ apifid[:api].update("files/#{apifid[:file_id]}", update_param)[:data]
349
+ return Main.result_status('Done')
350
+ when :permission
351
+ command_perm = options.get_next_command(%i[list create delete])
352
+ case command_perm
353
+ when :list
354
+ # generic options : TODO: as arg ? option_url_query
355
+ list_options ||= {'include' => [Rest::ARRAY_PARAMS, 'access_level', 'permission_count']}
356
+ # add which one to get
357
+ list_options['file_id'] = apifid[:file_id]
358
+ list_options['inherited'] ||= false
359
+ items = apifid[:api].read('permissions', list_options)[:data]
360
+ return {type: :object_list, data: items}
361
+ when :delete
362
+ perm_id = instance_identifier
363
+ return do_bulk_operation(perm_id, 'deleted') do |one_id|
364
+ # TODO: notify event ?
365
+ apifid[:api].delete("permissions/#{perm_id}")
366
+ {'id' => one_id}
367
+ end
368
+ when :create
369
+ create_param = options.get_next_argument('creation data', type: Hash)
370
+ raise 'no file_id' if create_param.key?('file_id')
371
+ create_param['file_id'] = apifid[:file_id]
372
+ create_param['access_levels'] = Aspera::Node::ACCESS_LEVELS unless create_param.key?('access_levels')
373
+ # add application specific tags (AoC)
374
+ the_app = apifid[:api].app_info
375
+ the_app[:api].permissions_create_params(create_param: create_param, app_info: the_app) unless the_app.nil?
376
+ # create permission
377
+ created_data = apifid[:api].create('permissions', create_param)[:data]
378
+ # bnotify application of creation
379
+ the_app[:api].permissions_create_event(created_data: created_data, app_info: the_app) unless the_app.nil?
380
+ return { type: :single_object, data: created_data}
381
+ else raise "internal error:shall not reach here (#{command_perm})"
382
+ end
383
+ else raise "internal error:shall not reach here (#{command_node_file})"
384
+ end
279
385
  end
280
386
 
281
- NODE4_COMMANDS = %i[browse find mkdir rename delete upload download http_node_download file permission bearer_token_node node_info].freeze
282
-
283
- def execute_node_gen4_command(command_repo, top_file_id)
284
- #@api_node
387
+ def execute_command_gen4(command_repo, top_file_id)
285
388
  case command_repo
286
- when :node_info
287
- thepath = options.get_next_argument('path')
288
- apifid = resolve_api_fid(top_file_id,thepath)
289
- apifid[:api] = aoc_api.get_node_api(apifid[:node_info], use_secret: false)
290
- return {type: :single_object,data: {
291
- url: apifid[:node_info]['url'],
292
- username: apifid[:node_info]['access_key'],
293
- password: apifid[:api].oauth_token,
294
- root_id: apifid[:file_id]
295
- }}
389
+ when :v3
390
+ # NOTE: other common actions are unauthorized with user scope
391
+ command_legacy = options.get_next_command(V3_IN_V4_ACTIONS)
392
+ # TODO: shall we support all methods here ? what if there is a link ?
393
+ apifid = @api_node.resolve_api_fid(top_file_id, '')
394
+ return Node.new(@agents.merge(skip_basic_auth_options: true, skip_node_options: true, node_api: apifid[:api])).execute_action(command_legacy)
395
+ when :node_info, :bearer_token_node
396
+ apifid = @api_node.resolve_api_fid(top_file_id, options.get_next_argument('path'))
397
+ result = {
398
+ url: apifid[:api].params[:base_url],
399
+ root_id: apifid[:file_id]
400
+ }
401
+ raise 'No auth for node' if apifid[:api].params[:auth].nil?
402
+ case apifid[:api].params[:auth][:type]
403
+ when :basic
404
+ result[:username] = apifid[:api].params[:auth][:username]
405
+ result[:password] = apifid[:api].params[:auth][:password]
406
+ when :oauth2
407
+ result[:username] = apifid[:api].params[:headers][Aspera::Node::HEADER_X_ASPERA_ACCESS_KEY]
408
+ result[:password] = apifid[:api].oauth_token
409
+ else raise 'unknown'
410
+ end
411
+ return {type: :single_object, data: result} if command_repo.eql?(:node_info)
412
+ raise 'not bearer token' unless result[:password].start_with?('Bearer ')
413
+ return Main.result_status(result[:password])
296
414
  when :browse
297
- thepath = options.get_next_argument('path')
298
- apifid = resolve_api_fid(top_file_id,thepath)
415
+ apifid = @api_node.resolve_api_fid(top_file_id, options.get_next_argument('path'))
299
416
  file_info = apifid[:api].read("files/#{apifid[:file_id]}")[:data]
300
417
  if file_info['type'].eql?('folder')
301
- result = apifid[:api].read("files/#{apifid[:file_id]}/files",options.get_option(:value))
418
+ result = apifid[:api].read("files/#{apifid[:file_id]}/files", options.get_option(:value))
302
419
  items = result[:data]
303
- self.format.display_status("Items: #{result[:data].length}/#{result[:http]['X-Total-Count']}")
420
+ formatter.display_status("Items: #{result[:data].length}/#{result[:http]['X-Total-Count']}")
304
421
  else
305
422
  items = [file_info]
306
423
  end
307
- return {type: :object_list,data: items,fields: %w[name type recursive_size size modified_time access_level]}
424
+ return {type: :object_list, data: items, fields: %w[name type recursive_size size modified_time access_level]}
308
425
  when :find
309
- thepath = options.get_next_argument('path')
310
- apifid = resolve_api_fid(top_file_id,thepath)
426
+ apifid = @api_node.resolve_api_fid(top_file_id, options.get_next_argument('path'))
311
427
  test_block = Aspera::Node.file_matcher(options.get_option(:value))
312
- return {type: :object_list,data: aoc_api.find_files(apifid,test_block),fields: ['path']}
428
+ return {type: :object_list, data: @api_node.find_files(apifid[:file_id], test_block), fields: ['path']}
313
429
  when :mkdir
314
- thepath = options.get_next_argument('path')
315
- containing_folder_path = thepath.split(AoC::PATH_SEPARATOR)
430
+ containing_folder_path = options.get_next_argument('path').split(Aspera::Node::PATH_SEPARATOR)
316
431
  new_folder = containing_folder_path.pop
317
- apifid = resolve_api_fid(top_file_id,containing_folder_path.join(AoC::PATH_SEPARATOR))
318
- result = apifid[:api].create("files/#{apifid[:file_id]}/files",{name: new_folder,type: :folder})[:data]
432
+ apifid = @api_node.resolve_api_fid(top_file_id, containing_folder_path.join(Aspera::Node::PATH_SEPARATOR))
433
+ result = apifid[:api].create("files/#{apifid[:file_id]}/files", {name: new_folder, type: :folder})[:data]
319
434
  return Main.result_status("created: #{result['name']} (id=#{result['id']})")
320
435
  when :rename
321
- thepath = options.get_next_argument('source path')
436
+ file_path = options.get_next_argument('source path')
322
437
  newname = options.get_next_argument('new name')
323
- apifid = resolve_api_fid(top_file_id,thepath)
324
- result = apifid[:api].update("files/#{apifid[:file_id]}",{name: newname})[:data]
325
- return Main.result_status("renamed #{thepath} to #{newname}")
438
+ apifid = @api_node.resolve_api_fid(top_file_id, file_path)
439
+ result = apifid[:api].update("files/#{apifid[:file_id]}", {name: newname})[:data]
440
+ return Main.result_status("renamed #{file_path} to #{newname}")
326
441
  when :delete
327
- thepath = options.get_next_argument('path')
328
- return do_bulk_operation(thepath,'deleted','path') do |l_path|
442
+ return do_bulk_operation(options.get_next_argument('path'), 'deleted', id_result: 'path') do |l_path|
329
443
  raise "expecting String (path), got #{l_path.class.name} (#{l_path})" unless l_path.is_a?(String)
330
- apifid = resolve_api_fid(top_file_id,l_path)
444
+ apifid = @api_node.resolve_api_fid(top_file_id, l_path)
331
445
  result = apifid[:api].delete("files/#{apifid[:file_id]}")[:data]
332
446
  {'path' => l_path}
333
447
  end
448
+ when :sync
449
+ # remote is specified by option to_folder
450
+ apifid = @api_node.resolve_api_fid(top_file_id, transfer.destination_folder(Fasp::TransferSpec::DIRECTION_SEND))
451
+ transfer_spec = apifid[:api].transfer_spec_gen4(apifid[:file_id], Fasp::TransferSpec::DIRECTION_SEND)
452
+ Log.dump(:ts, transfer_spec)
453
+ sync_plugin = Plugins::Sync.new(@agents, transfer_spec: transfer_spec)
454
+ return sync_plugin.execute_action
334
455
  when :upload
335
- apifid = resolve_api_fid(top_file_id,transfer.destination_folder(Fasp::TransferSpec::DIRECTION_SEND))
336
- add_ts = {'tags' => {'aspera' => {'files' => {'parentCwd' => "#{apifid[:node_info]['id']}:#{apifid[:file_id]}"}}}}
337
- return Main.result_transfer(transfer_start(AoC::FILES_APP,Fasp::TransferSpec::DIRECTION_SEND,apifid,add_ts))
456
+ apifid = @api_node.resolve_api_fid(top_file_id, transfer.destination_folder(Fasp::TransferSpec::DIRECTION_SEND))
457
+ return Main.result_transfer(transfer.start(apifid[:api].transfer_spec_gen4(apifid[:file_id], Fasp::TransferSpec::DIRECTION_SEND)))
338
458
  when :download
339
459
  source_paths = transfer.ts_source_paths
340
460
  # special case for AoC : all files must be in same folder
341
461
  source_folder = source_paths.shift['source']
342
462
  # if a single file: split into folder and path
463
+ apifid = @api_node.resolve_api_fid(top_file_id, source_folder)
343
464
  if source_paths.empty?
344
- source_folder = source_folder.split(AoC::PATH_SEPARATOR)
345
- source_paths = [{'source' => source_folder.pop}]
346
- source_folder = source_folder.join(AoC::PATH_SEPARATOR)
465
+ file_info = apifid[:api].read("files/#{apifid[:file_id]}")[:data]
466
+ case file_info['type']
467
+ when 'file'
468
+ # if the single source is a file, we need to split into folder path and filename
469
+ src_dir_elements = source_folder.split(Aspera::Node::PATH_SEPARATOR)
470
+ # filename is the last one
471
+ source_paths = [{'source' => src_dir_elements.pop}]
472
+ # source folder is what remains
473
+ source_folder = src_dir_elements.join(Aspera::Node::PATH_SEPARATOR)
474
+ # TODO: instead of creating a new object, use the same, and change file id with parent folder id ? possible ?
475
+ apifid = @api_node.resolve_api_fid(top_file_id, source_folder)
476
+ when 'link', 'folder'
477
+ # single source is 'folder' or 'link'
478
+ # TODO: add this ? , 'destination'=>file_info['name']
479
+ source_paths = [{'source' => '.'}]
480
+ else
481
+ raise "Unknown source type: #{file_info['type']}"
482
+ end
347
483
  end
348
- apifid = resolve_api_fid(top_file_id,source_folder)
349
- # override paths with just filename
350
- add_ts = {'tags' => {'aspera' => {'files' => {'parentCwd' => "#{apifid[:node_info]['id']}:#{apifid[:file_id]}"}}}}
351
- add_ts['paths'] = source_paths
352
- return Main.result_transfer(transfer_start(AoC::FILES_APP,Fasp::TransferSpec::DIRECTION_RECEIVE,apifid,add_ts))
484
+ return Main.result_transfer(transfer.start(apifid[:api].transfer_spec_gen4(apifid[:file_id], Fasp::TransferSpec::DIRECTION_RECEIVE, {'paths'=>source_paths})))
353
485
  when :http_node_download
354
486
  source_paths = transfer.ts_source_paths
355
487
  source_folder = source_paths.shift['source']
356
488
  if source_paths.empty?
357
- source_folder = source_folder.split(AoC::PATH_SEPARATOR)
489
+ source_folder = source_folder.split(Aspera::Node::PATH_SEPARATOR)
358
490
  source_paths = [{'source' => source_folder.pop}]
359
- source_folder = source_folder.join(AoC::PATH_SEPARATOR)
491
+ source_folder = source_folder.join(Aspera::Node::PATH_SEPARATOR)
360
492
  end
361
- raise CliBadArgument,'one file at a time only in HTTP mode' if source_paths.length > 1
493
+ raise CliBadArgument, 'one file at a time only in HTTP mode' if source_paths.length > 1
362
494
  file_name = source_paths.first['source']
363
- apifid = resolve_api_fid(top_file_id,File.join(source_folder,file_name))
495
+ apifid = @api_node.resolve_api_fid(top_file_id, File.join(source_folder, file_name))
364
496
  apifid[:api].call(
365
497
  operation: 'GET',
366
498
  subpath: "files/#{apifid[:file_id]}/content",
367
- save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTION_RECEIVE),file_name))
499
+ save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTION_RECEIVE), file_name))
368
500
  return Main.result_status("downloaded: #{file_name}")
369
- when :permission
370
- command_perm = options.get_next_command(%i[list create])
371
- case command_perm
372
- when :list
373
- # generic options : TODO: as arg ? option_url_query
374
- list_options ||= {'include' => ['[]','access_level','permission_count']}
375
- # special value: ALL will show all permissions
376
- if !VAL_ALL.eql?(apifid[:file_id])
377
- # add which one to get
378
- list_options['file_id'] = apifid[:file_id]
379
- list_options['inherited'] ||= false
380
- end
381
- items = apifid[:api].read('permissions',list_options)[:data]
382
- return {type: :object_list,data: items}
383
- when :create
384
- #create_param=self.options.get_next_argument('creation data (Hash)')
385
- set_workspace_info
386
- access_id = "#{ID_AK_ADMIN}_WS_#{@workspace_id}"
387
- apifid[:node_info]
388
- params = {
389
- 'file_id' => apifid[:file_id], # mandatory
390
- 'access_type' => 'user', # mandatory: user or group
391
- 'access_id' => access_id, # id of user or group
392
- 'access_levels' => Aspera::Node::ACCESS_LEVELS,
393
- 'tags' => {'aspera' => {'files' => {'workspace' => {
394
- 'id' => @workspace_id,
395
- 'workspace_name' => @workspace_name,
396
- 'user_name' => aoc_api.user_info['name'],
397
- 'shared_by_user_id' => aoc_api.user_info['id'],
398
- 'shared_by_name' => aoc_api.user_info['name'],
399
- 'shared_by_email' => aoc_api.user_info['email'],
400
- 'shared_with_name' => access_id,
401
- 'access_key' => apifid[:node_info]['access_key'],
402
- 'node' => apifid[:node_info]['name']}}}}}
403
- item = apifid[:api].create('permissions',params)[:data]
404
- return {type: :single_object,data: item}
405
- else raise "internal error:shall not reach here (#{command_perm})"
406
- end
407
501
  when :file
408
- command_node_file = options.get_next_command(%i[show modify])
409
- file_path = options.get_option(:path)
410
- apifid =
411
- if !file_path.nil?
412
- resolve_api_fid(top_file_id,file_path) # TODO: allow follow link ?
413
- else
414
- {node_info: top_file_id[:node_info],file_id: instance_identifier}
415
- end
416
- case command_node_file
417
- when :show
418
- items = apifid[:api].read("files/#{apifid[:file_id]}")[:data]
419
- return {type: :single_object,data: items}
420
- when :modify
421
- update_param = options.get_next_argument('update data (Hash)')
422
- res = apifid[:api].update("files/#{apifid[:file_id]}",update_param)[:data]
423
- return {type: :single_object,data: res}
424
- else raise "internal error:shall not reach here (#{command_node_file})"
425
- end
502
+ command_node_file = options.get_next_command(%i[show modify permission])
503
+ return execute_node_gen4_file_command(command_node_file, top_file_id)
504
+ else raise "INTERNAL ERROR: no case for #{command_repo}"
426
505
  end # command_repo
427
- raise 'ERR'
428
- end # execute_node_gen4_command
506
+ # raise 'INTERNAL ERROR: missing return'
507
+ end # execute_command_gen4
429
508
 
430
509
  # This is older API
431
510
  def execute_async
@@ -434,7 +513,7 @@ module Aspera
434
513
  asyncname = options.get_option(:sync_name)
435
514
  if asyncname.nil?
436
515
  asyncid = instance_identifier
437
- if asyncid.eql?('ALL') && %i[show delete].include?(command)
516
+ if asyncid.eql?(VAL_ALL) && %i[show delete].include?(command)
438
517
  asyncids = @api_node.read('async/list')[:data]['sync_ids']
439
518
  else
440
519
  Integer(asyncid) # must be integer
@@ -442,7 +521,7 @@ module Aspera
442
521
  end
443
522
  else
444
523
  asyncids = @api_node.read('async/list')[:data]['sync_ids']
445
- summaries = @api_node.create('async/summary',{'syncs' => asyncids})[:data]['sync_summaries']
524
+ summaries = @api_node.create('async/summary', {'syncs' => asyncids})[:data]['sync_summaries']
446
525
  selected = summaries.find{|s|s['name'].eql?(asyncname)}
447
526
  raise "no such sync: #{asyncname}" if selected.nil?
448
527
  asyncid = selected['snid']
@@ -455,16 +534,16 @@ module Aspera
455
534
  resp = @api_node.read('async/list')[:data]['sync_ids']
456
535
  return { type: :value_list, data: resp, name: 'id' }
457
536
  when :show
458
- resp = @api_node.create('async/summary',pdata)[:data]['sync_summaries']
537
+ resp = @api_node.create('async/summary', pdata)[:data]['sync_summaries']
459
538
  return Main.result_empty if resp.empty?
460
- return { type: :object_list, data: resp, fields: %w[snid name local_dir remote_dir] } if asyncid.eql?('ALL')
539
+ return { type: :object_list, data: resp, fields: %w[snid name local_dir remote_dir] } if asyncid.eql?(VAL_ALL)
461
540
  return { type: :single_object, data: resp.first }
462
541
  when :delete
463
- resp = @api_node.create('async/delete',pdata)[:data]
542
+ resp = @api_node.create('async/delete', pdata)[:data]
464
543
  return { type: :single_object, data: resp, name: 'id' }
465
544
  when :bandwidth
466
545
  pdata['seconds'] = 100 # TODO: as parameter with --value
467
- resp = @api_node.create('async/bandwidth',pdata)[:data]
546
+ resp = @api_node.create('async/bandwidth', pdata)[:data]
468
547
  data = resp['bandwidth_data']
469
548
  return Main.result_empty if data.empty?
470
549
  data = data.first[asyncid]['data']
@@ -476,19 +555,19 @@ module Aspera
476
555
  # status int
477
556
  filter = options.get_option(:value)
478
557
  pdata.merge!(filter) unless filter.nil?
479
- resp = @api_node.create('async/files',pdata)[:data]
558
+ resp = @api_node.create('async/files', pdata)[:data]
480
559
  data = resp['sync_files']
481
560
  data = data.first[asyncid] unless data.empty?
482
561
  iteration_data = []
483
562
  skip_ids_persistency = nil
484
- if options.get_option(:once_only,is_type: :mandatory)
563
+ if options.get_option(:once_only, is_type: :mandatory)
485
564
  skip_ids_persistency = PersistencyActionOnce.new(
486
565
  manager: @agents[:persistency],
487
566
  data: iteration_data,
488
567
  id: IdGenerator.from_list([
489
568
  'sync_files',
490
- options.get_option(:url,is_type: :mandatory),
491
- options.get_option(:username,is_type: :mandatory),
569
+ options.get_option(:url, is_type: :mandatory),
570
+ options.get_option(:username, is_type: :mandatory),
492
571
  asyncid]))
493
572
  unless iteration_data.first.nil?
494
573
  data.select!{|l| l['fnid'].to_i > iteration_data.first}
@@ -499,53 +578,59 @@ module Aspera
499
578
  skip_ids_persistency&.save
500
579
  return { type: :object_list, data: data, name: 'id' }
501
580
  when :counters
502
- resp = @api_node.create('async/counters',pdata)[:data]['sync_counters'].first[asyncid].last
581
+ resp = @api_node.create('async/counters', pdata)[:data]['sync_counters'].first[asyncid].last
503
582
  return Main.result_empty if resp.nil?
504
583
  return { type: :single_object, data: resp }
505
584
  end
506
585
  end
507
586
 
508
- ACTIONS = %i[postprocess stream transfer cleanup forward access_key watch_folder service async sync central asperabrowser
509
- basic_token].concat(COMMON_ACTIONS).freeze
587
+ ACTIONS = %i[
588
+ async
589
+ sync
590
+ stream
591
+ transfer
592
+ service
593
+ watch_folder
594
+ central
595
+ asperabrowser
596
+ basic_token].concat(COMMON_ACTIONS).freeze
510
597
 
511
- def execute_action(command=nil,prefix_path=nil)
598
+ def execute_action(command=nil, prefix_path=nil)
512
599
  command ||= options.get_next_command(ACTIONS)
513
600
  case command
514
- when *COMMON_ACTIONS then return execute_simple_common(command,prefix_path)
601
+ when *COMMON_ACTIONS then return execute_simple_common(command, prefix_path)
515
602
  when :async then return execute_async
516
603
  when :sync
517
- sync_command = options.get_next_command([Plugin::ALL_OPS,%i[bandwidth counters files start state stop summary]].flatten-%i[modify])
604
+ # newer api
605
+ sync_command = options.get_next_command(%i[bandwidth counters files start state stop summary].concat(Plugin::ALL_OPS) - %i[modify])
518
606
  case sync_command
519
- when *Plugin::ALL_OPS then return entity_command(sync_command,@api_node,'asyncs',item_list_key: 'ids')
607
+ when *Plugin::ALL_OPS then return entity_command(sync_command, @api_node, 'asyncs', item_list_key: 'ids')
520
608
  else
521
609
  parameters = options.get_option(:value)
522
- asyncs_id=instance_identifier
610
+ asyncs_id = instance_identifier
523
611
  if %i[start stop].include?(sync_command)
524
- @api_node.create("asyncs/#{asyncs_id}/#{sync_command}",parameters)
612
+ @api_node.create("asyncs/#{asyncs_id}/#{sync_command}", parameters)
525
613
  return Main.result_status('ok')
526
614
  end
527
- return { type: :single_object, data: @api_node.read("asyncs/#{asyncs_id}/#{sync_command}",parameters)[:data] }
615
+ return { type: :single_object, data: @api_node.read("asyncs/#{asyncs_id}/#{sync_command}", parameters)[:data] }
528
616
  end
529
617
  when :stream
530
618
  command = options.get_next_command(%i[list create show modify cancel])
531
619
  case command
532
620
  when :list
533
- resp = @api_node.read('ops/transfers',options.get_option(:value))
621
+ resp = @api_node.read('ops/transfers', options.get_option(:value))
534
622
  return { type: :object_list, data: resp[:data], fields: %w[id status] } # TODO: useful?
535
623
  when :create
536
- resp = @api_node.create('streams',options.get_option(:value,is_type: :mandatory))
624
+ resp = @api_node.create('streams', options.get_option(:value, is_type: :mandatory))
537
625
  return { type: :single_object, data: resp[:data] }
538
626
  when :show
539
- trid = options.get_next_argument('transfer id')
540
- resp = @api_node.read('ops/transfers/' + trid)
627
+ resp = @api_node.read("ops/transfers/#{options.get_next_argument('transfer id')}")
541
628
  return { type: :other_struct, data: resp[:data] }
542
629
  when :modify
543
- trid = options.get_next_argument('transfer id')
544
- resp = @api_node.update('streams/' + trid,options.get_option(:value,is_type: :mandatory))
630
+ resp = @api_node.update("streams/#{options.get_next_argument('transfer id')}", options.get_option(:value, is_type: :mandatory))
545
631
  return { type: :other_struct, data: resp[:data] }
546
632
  when :cancel
547
- trid = options.get_next_argument('transfer id')
548
- resp = @api_node.cancel('streams/' + trid)
633
+ resp = @api_node.cancel("streams/#{options.get_next_argument('transfer id')}")
549
634
  return { type: :other_struct, data: resp[:data] }
550
635
  else
551
636
  raise 'error'
@@ -560,9 +645,9 @@ module Aspera
560
645
  case command
561
646
  when :list
562
647
  # could use ? subpath: 'transfers'
563
- query=options.get_option(:value) || options.get_option(:query)
648
+ query = options.get_option(:value) || options.get_option(:query)
564
649
  raise 'Query must be a Hash' unless query.nil? || query.is_a?(Hash)
565
- resp = @api_node.read(res_class_path,query)
650
+ resp = @api_node.read(res_class_path, query)
566
651
  return {
567
652
  type: :object_list,
568
653
  data: resp[:data],
@@ -577,22 +662,6 @@ module Aspera
577
662
  else
578
663
  raise 'error'
579
664
  end
580
- when :access_key
581
- ak_command = options.get_next_command([Plugin::ALL_OPS,:do].flatten)
582
- case ak_command
583
- when *Plugin::ALL_OPS then return entity_command(ak_command,@api_node,'access_keys',id_default: 'self')
584
- when :do
585
- access_key = options.get_next_argument('access key id')
586
- ak_info=@api_node.read("access_keys/#{access_key}")[:data]
587
- # change API if needed
588
- if !access_key.eql?('self')
589
- secret=config.vault.get(username: access_key)[:secret] #, url: @api_node.params[:base_url] : TODO: better handle vault
590
- @api_node.params[:auth][:username]=access_key
591
- @api_node.params[:auth][:password]=secret
592
- end
593
- command_repo = options.get_next_command(NODE4_COMMANDS)
594
- return execute_node_gen4_command(command_repo,ak_info['root_file_id'])
595
- end
596
665
  when :service
597
666
  command = options.get_next_command(%i[list create delete])
598
667
  if [:delete].include?(command)
@@ -605,7 +674,7 @@ module Aspera
605
674
  when :create
606
675
  # @json:'{"type":"WATCHFOLDERD","run_as":{"user":"user1"}}'
607
676
  params = options.get_next_argument('Run creation data (structure)')
608
- resp = @api_node.create('rund/services',params)
677
+ resp = @api_node.create('rund/services', params)
609
678
  return Main.result_status("#{resp[:data]['id']} created")
610
679
  when :delete
611
680
  @api_node.delete("rund/services/#{svcid}")
@@ -623,15 +692,15 @@ module Aspera
623
692
  @api_node.params[:headers]['X-aspera-WF-version'] = '2017_10_23'
624
693
  case command
625
694
  when :create
626
- resp = @api_node.create(res_class_path,options.get_option(:value,is_type: :mandatory))
695
+ resp = @api_node.create(res_class_path, options.get_option(:value, is_type: :mandatory))
627
696
  return Main.result_status("#{resp[:data]['id']} created")
628
697
  when :list
629
- resp = @api_node.read(res_class_path,options.get_option(:value))
698
+ resp = @api_node.read(res_class_path, options.get_option(:value))
630
699
  return { type: :value_list, data: resp[:data]['ids'], name: 'id' }
631
700
  when :show
632
701
  return { type: :single_object, data: @api_node.read(one_res_path)[:data]}
633
702
  when :modify
634
- @api_node.update(one_res_path,options.get_option(:value,is_type: :mandatory))
703
+ @api_node.update(one_res_path, options.get_option(:value, is_type: :mandatory))
635
704
  return Main.result_status("#{one_res_id} updated")
636
705
  when :delete
637
706
  @api_node.delete(one_res_path)
@@ -651,7 +720,7 @@ module Aspera
651
720
  case command
652
721
  when :list
653
722
  request_data.deep_merge!({'validation' => validation}) unless validation.nil?
654
- resp = @api_node.create('services/rest/transfers/v1/sessions',request_data)
723
+ resp = @api_node.create('services/rest/transfers/v1/sessions', request_data)
655
724
  return {
656
725
  type: :object_list,
657
726
  data: resp[:data]['session_info_result']['session_info'],
@@ -663,28 +732,28 @@ module Aspera
663
732
  case command
664
733
  when :list
665
734
  request_data.deep_merge!({'validation' => validation}) unless validation.nil?
666
- resp = @api_node.create('services/rest/transfers/v1/files',request_data)[:data]
735
+ resp = @api_node.create('services/rest/transfers/v1/files', request_data)[:data]
667
736
  resp = JSON.parse(resp) if resp.is_a?(String)
668
- Log.dump(:resp,resp)
737
+ Log.dump(:resp, resp)
669
738
  return { type: :object_list, data: resp['file_transfer_info_result']['file_transfer_info'], fields: %w[session_uuid file_id status path]}
670
739
  when :modify
671
740
  request_data.deep_merge!(validation) unless validation.nil?
672
- @api_node.update('services/rest/transfers/v1/files',request_data)
741
+ @api_node.update('services/rest/transfers/v1/files', request_data)
673
742
  return Main.result_status('updated')
674
743
  end
675
744
  end
676
745
  when :asperabrowser
677
746
  browse_params = {
678
- 'nodeUser' => options.get_option(:username,is_type: :mandatory),
679
- 'nodePW' => options.get_option(:password,is_type: :mandatory),
680
- 'nodeURL' => options.get_option(:url,is_type: :mandatory)
747
+ 'nodeUser' => options.get_option(:username, is_type: :mandatory),
748
+ 'nodePW' => options.get_option(:password, is_type: :mandatory),
749
+ 'nodeURL' => options.get_option(:url, is_type: :mandatory)
681
750
  }
682
751
  # encode parameters so that it looks good in url
683
752
  encoded_params = Base64.strict_encode64(Zlib::Deflate.deflate(JSON.generate(browse_params))).gsub(/=+$/, '').tr('+/', '-_').reverse
684
753
  OpenApplication.instance.uri(options.get_option(:asperabrowserurl) + '?goto=' + encoded_params)
685
754
  return Main.result_status('done')
686
755
  when :basic_token
687
- return Main.result_status(Rest.basic_creds(options.get_option(:username,is_type: :mandatory),options.get_option(:password,is_type: :mandatory)))
756
+ return Main.result_status(Rest.basic_creds(options.get_option(:username, is_type: :mandatory), options.get_option(:password, is_type: :mandatory)))
688
757
  end # case command
689
758
  raise 'ERROR: shall not reach this line'
690
759
  end # execute_action