aspera-cli 4.11.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 (67) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/BUGS.md +0 -1
  4. data/CHANGELOG.md +71 -52
  5. data/CONTRIBUTING.md +31 -6
  6. data/README.md +404 -259
  7. data/bin/asession +2 -2
  8. data/docs/test_env.conf +1 -0
  9. data/examples/aoc.rb +2 -2
  10. data/examples/dascli +11 -11
  11. data/examples/faspex4.rb +7 -7
  12. data/examples/node.rb +1 -1
  13. data/examples/proxy.pac +2 -2
  14. data/examples/server.rb +3 -3
  15. data/lib/aspera/aoc.rb +105 -40
  16. data/lib/aspera/cli/extended_value.rb +4 -4
  17. data/lib/aspera/cli/{formater.rb → formatter.rb} +7 -7
  18. data/lib/aspera/cli/listener/progress.rb +1 -1
  19. data/lib/aspera/cli/listener/progress_multi.rb +2 -2
  20. data/lib/aspera/cli/main.rb +18 -18
  21. data/lib/aspera/cli/manager.rb +5 -5
  22. data/lib/aspera/cli/plugin.rb +23 -20
  23. data/lib/aspera/cli/plugins/aoc.rb +75 -112
  24. data/lib/aspera/cli/plugins/ats.rb +6 -6
  25. data/lib/aspera/cli/plugins/config.rb +84 -83
  26. data/lib/aspera/cli/plugins/cos.rb +1 -1
  27. data/lib/aspera/cli/plugins/faspex.rb +38 -38
  28. data/lib/aspera/cli/plugins/faspex5.rb +187 -43
  29. data/lib/aspera/cli/plugins/node.rb +30 -37
  30. data/lib/aspera/cli/plugins/orchestrator.rb +7 -4
  31. data/lib/aspera/cli/plugins/preview.rb +10 -9
  32. data/lib/aspera/cli/plugins/server.rb +1 -1
  33. data/lib/aspera/cli/plugins/shares.rb +67 -43
  34. data/lib/aspera/cli/transfer_agent.rb +16 -16
  35. data/lib/aspera/cli/version.rb +2 -1
  36. data/lib/aspera/command_line_builder.rb +70 -66
  37. data/lib/aspera/cos_node.rb +9 -9
  38. data/lib/aspera/fasp/agent_base.rb +3 -1
  39. data/lib/aspera/fasp/agent_connect.rb +23 -23
  40. data/lib/aspera/fasp/agent_direct.rb +13 -14
  41. data/lib/aspera/fasp/agent_httpgw.rb +20 -19
  42. data/lib/aspera/fasp/agent_node.rb +13 -15
  43. data/lib/aspera/fasp/agent_trsdk.rb +1 -1
  44. data/lib/aspera/fasp/installation.rb +5 -5
  45. data/lib/aspera/fasp/listener.rb +1 -1
  46. data/lib/aspera/fasp/parameters.rb +49 -41
  47. data/lib/aspera/fasp/parameters.yaml +311 -212
  48. data/lib/aspera/fasp/resume_policy.rb +2 -2
  49. data/lib/aspera/fasp/transfer_spec.rb +0 -13
  50. data/lib/aspera/faspex_gw.rb +80 -161
  51. data/lib/aspera/faspex_postproc.rb +77 -0
  52. data/lib/aspera/log.rb +7 -7
  53. data/lib/aspera/nagios.rb +6 -6
  54. data/lib/aspera/node.rb +24 -19
  55. data/lib/aspera/oauth.rb +50 -47
  56. data/lib/aspera/proxy_auto_config.js +22 -22
  57. data/lib/aspera/proxy_auto_config.rb +3 -3
  58. data/lib/aspera/rest.rb +12 -10
  59. data/lib/aspera/rest_error_analyzer.rb +5 -5
  60. data/lib/aspera/secret_hider.rb +4 -3
  61. data/lib/aspera/ssh.rb +4 -4
  62. data/lib/aspera/sync.rb +37 -36
  63. data/lib/aspera/web_auth.rb +7 -59
  64. data/lib/aspera/web_server_simple.rb +76 -0
  65. data.tar.gz.sig +0 -0
  66. metadata +6 -4
  67. metadata.gz.sig +0 -0
@@ -32,12 +32,12 @@ module Aspera
32
32
  # calls block a number of times (resumes) until success or limit reached
33
33
  # this is re-entrant, one resumer can handle multiple transfers in //
34
34
  def execute_with_resume
35
- raise 'block manndatory' unless block_given?
35
+ raise 'block mandatory' unless block_given?
36
36
  # maximum of retry
37
37
  remaining_resumes = @parameters[:iter_max]
38
38
  sleep_seconds = @parameters[:sleep_initial]
39
39
  Log.log.debug{"retries=#{remaining_resumes}"}
40
- # try to send the file until ascp is succesful
40
+ # try to send the file until ascp is successful
41
41
  loop do
42
42
  Log.log.debug('transfer starting')
43
43
  begin
@@ -23,19 +23,6 @@ module Aspera
23
23
  end
24
24
  end
25
25
  class << self
26
- def ascp_opts_to_ts(tspec, opts)
27
- return if opts.nil?
28
- raise 'ascp options must be an Array' unless opts.is_a?(Array)
29
- raise 'transfer spec must be a Hash' unless tspec.is_a?(Hash)
30
- raise 'ascp options must be an Array or Strings' if opts.any?{|o|!o.is_a?(String)}
31
- tspec['EX_ascp_args'] ||= []
32
- raise 'EX_ascp_args must be an Array' unless tspec['EX_ascp_args'].is_a?(Array)
33
- # TODO: translate command line args into transfer spec
34
- # non translatable args are left in special ts parameter
35
- tspec['EX_ascp_args'] = tspec['EX_ascp_args'].concat(opts)
36
- return tspec
37
- end
38
-
39
26
  def action_to_direction(tspec, command)
40
27
  raise 'transfer spec must be a Hash' unless tspec.is_a?(Hash)
41
28
  tspec['direction'] = case command.to_sym
@@ -1,176 +1,95 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'aspera/web_server_simple'
3
4
  require 'aspera/log'
4
- require 'aspera/aoc'
5
- require 'aspera/fasp/transfer_spec'
6
- require 'aspera/cli/main'
7
- require 'webrick'
8
- require 'webrick/https'
9
- require 'securerandom'
10
- require 'openssl'
11
5
  require 'json'
12
6
 
13
7
  module Aspera
14
8
  # this class answers the Faspex /send API and creates a package on Aspera on Cloud
15
- class FaspexGW
16
- class FxGwServlet < WEBrick::HTTPServlet::AbstractServlet
17
- def initialize(_server, a_aoc_api_user, a_workspace_id)
18
- super
19
- @aoc_api_user = a_aoc_api_user
20
- @aoc_workspace_id = a_workspace_id
21
- end
22
-
23
- # parameters from user to Faspex API call
24
- # {"delivery":{"use_encryption_at_rest":false,"note":"note","sources":[{"paths":["file1"]}],"title":"my title","recipients":["email1"],"send_upload_result":true}}
25
- # {
26
- # "delivery"=>{
27
- # "use_encryption_at_rest"=>false,
28
- # "note"=>"note",
29
- # "sources"=>[{"paths"=>["file1"]}],
30
- # "title"=>"my title",
31
- # "recipients"=>["email1"],
32
- # "send_upload_result"=>true
33
- # }
34
- # }
35
- def process_faspex_send(request, response)
36
- raise 'no payload' if request.body.nil?
37
-
38
- faspex_pkg_parameters = JSON.parse(request.body)
39
- faspex_pkg_delivery = faspex_pkg_parameters['delivery']
40
- Log.log.debug{"faspex pkg create parameters=#{faspex_pkg_parameters}"}
41
-
42
- # get recipient ids
43
- files_pkg_recipients = []
44
- faspex_pkg_delivery['recipients'].each do |recipient_email|
45
- user_lookup = @aoc_api_user.read(
46
- 'contacts',
47
- { 'current_workspace_id' => @aoc_workspace_id, 'q' => recipient_email })[:data]
48
- raise StandardError,
49
- "no such unique user: #{recipient_email} / #{user_lookup}" unless !user_lookup.nil? && user_lookup.length.eql?(1)
50
-
51
- recipient_user_info = user_lookup.first
52
- files_pkg_recipients.push({
53
- 'id' => recipient_user_info['source_id'],
54
- 'type' => recipient_user_info['source_type']
55
- })
56
- end
57
-
58
- # create a new package with one file
59
- the_package = @aoc_api_user.create('packages', {
60
- 'file_names' => faspex_pkg_delivery['sources'][0]['paths'],
61
- 'name' => faspex_pkg_delivery['title'],
62
- 'note' => faspex_pkg_delivery['note'],
63
- 'recipients' => files_pkg_recipients,
64
- 'workspace_id' => @aoc_workspace_id
65
- })[:data]
66
-
67
- # get node information for the node on which package must be created
68
- node_info = @aoc_api_user.read("nodes/#{the_package['node_id']}")[:data]
69
-
70
- # get transfer token (for node)
71
- node_auth_bearer_token = @aoc_api_user.oauth_token(scope: AoC.node_scope(
72
- node_info['access_key'],
73
- AoC::SCOPE_NODE_USER))
74
-
75
- # tell Files what to expect in package: 1 transfer (can also be done after transfer)
76
- @aoc_api_user.update("packages/#{the_package['id']}", { 'sent' => true, 'transfers_expected' => 1 })
77
-
78
- # to return an error:
79
- # response.status=400
80
- # return 'ERROR HERE'
81
-
82
- # TODO: check about xfer_*
83
- ts_tags = {
84
- 'aspera' => {
85
- 'files' => { 'package_id' => the_package['id'], 'package_operation' => 'upload' },
86
- 'node' => { 'access_key' => node_info['access_key'], 'file_id' => the_package['contents_file_id'] },
87
- 'xfer_id' => SecureRandom.uuid,
88
- 'xfer_retry' => 3600
89
- }
90
- }
91
- # this transfer spec is for transfer to AoC
92
- faspex_transfer_spec = {
93
- 'direction' => 'send',
94
- 'remote_host' => node_info['host'],
95
- 'remote_user' => Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER,
96
- 'ssh_port' => Fasp::TransferSpec::SSH_PORT,
97
- 'fasp_port' => Fasp::TransferSpec::UDP_PORT,
98
- 'tags' => ts_tags,
99
- 'token' => node_auth_bearer_token,
100
- 'paths' => [{ 'destination' => '/' }],
101
- 'cookie' => 'unused',
102
- 'create_dir' => true,
103
- 'rate_policy' => 'fair',
104
- 'rate_policy_allowed' => 'fixed',
105
- 'min_rate_cap_kbps' => nil,
106
- 'min_rate_kbps' => 0,
107
- 'target_rate_percentage' => nil,
108
- 'lock_target_rate' => nil,
109
- 'fasp_url' => 'unused',
110
- 'lock_min_rate' => true,
111
- 'lock_rate_policy' => true,
112
- 'source_root' => '',
113
- 'content_protection' => nil,
114
- 'target_rate_cap_kbps' => 20_000, # TODO: is this value useful ?
115
- 'target_rate_kbps' => 10_000, # TODO: get from where?
116
- 'cipher' => 'aes-128',
117
- 'cipher_allowed' => nil,
118
- 'http_fallback' => false,
119
- 'http_fallback_port' => nil,
120
- 'https_fallback_port' => nil,
121
- 'destination_root' => '/'
122
- }
123
- # but we place it in a Faspex package creation response
124
- faspex_package_create_result = {
125
- 'links' => { 'status' => 'unused' },
126
- 'xfer_sessions' => [faspex_transfer_spec]
127
- }
128
- Log.log.info{"faspex_package_create_result=#{faspex_package_create_result}"}
129
- response.status = 200
130
- response.content_type = 'application/json'
131
- response.body = JSON.generate(faspex_package_create_result)
132
- end
133
-
134
- def do_GET(request, response)
135
- case request.path
136
- when '/aspera/faspex/send'
137
- process_faspex_send(request, response)
138
- else
139
- response.status = 400
140
- 'ERROR HERE'
141
- end
142
- end
143
- end # FxGwServlet
9
+ class Faspex4GWServlet < WEBrick::HTTPServlet::AbstractServlet
10
+ # @param app_api [Aspera::AoC]
11
+ # @param app_context [String]
12
+ def initialize(server, app_api, app_context)
13
+ super(server)
14
+ # typed: Aspera::AoC
15
+ @app_api = app_api
16
+ @app_context = app_context
17
+ end
144
18
 
145
- class NewUserServlet < WEBrick::HTTPServlet::AbstractServlet
146
- def do_GET(request, response)
147
- case request.path
148
- when '/newuser'
149
- response.status = 200
150
- response.content_type = 'text/html'
151
- response.body = '<html><body>hello world</body></html>'
152
- else
153
- raise "unsupported path: [#{request.path}]"
154
- end
155
- end
19
+ # Map Faspex 4 /send API to AoC package create
20
+ # parameters from user to Faspex API call
21
+ # https://developer.ibm.com/apis/catalog/aspera--aspera-faspex-client-sdk/Sending%20Packages%20(API%20v.3)
22
+ def faspex4_send_to_aoc(faspex_pkg_parameters)
23
+ faspex_pkg_delivery = faspex_pkg_parameters['delivery']
24
+ package_data = {
25
+ # 'file_names' => faspex_pkg_delivery['sources'][0]['paths'],
26
+ 'name' => faspex_pkg_delivery['title'],
27
+ 'note' => faspex_pkg_delivery['note'],
28
+ 'recipients' => faspex_pkg_delivery['recipients'],
29
+ 'workspace_id' => @app_context
30
+ }
31
+ created_package = @app_api.create_package_simple(package_data, true, @new_user_option)
32
+ # but we place it in a Faspex package creation response
33
+ return {
34
+ 'links' => { 'status' => 'unused' },
35
+ 'xfer_sessions' => [created_package[:spec]]
36
+ }
156
37
  end
157
38
 
158
- def initialize(a_aoc_api_user, a_workspace_id)
159
- webrick_options = {
160
- Port: 9443,
161
- Logger: Log.log,
162
- SSLEnable: true,
163
- SSLCertName: [['CN', WEBrick::Utils.getservername]]
39
+ def faspex4_send_to_faspex5(faspex_pkg_parameters)
40
+ faspex_pkg_delivery = faspex_pkg_parameters['delivery']
41
+ package_data = {
42
+ 'title' => faspex_pkg_delivery['title'],
43
+ 'note' => faspex_pkg_delivery['note'],
44
+ 'recipients' => faspex_pkg_delivery['recipients'].map{|name|{'name'=>name}}
45
+ }
46
+ package = @app_api.create('packages', package_data)[:data]
47
+ # TODO: option to send from remote source or httpgw
48
+ transfer_spec = @app_api.call(
49
+ operation: 'POST',
50
+ subpath: "packages/#{package['id']}/transfer_spec/upload",
51
+ headers: {'Accept' => 'application/json'},
52
+ url_params: {transfer_type: Aspera::Cli::Plugins::Faspex5::TRANSFER_CONNECT},
53
+ json_params: {paths: [{'destination'=>'/'}]}
54
+ )[:data]
55
+ transfer_spec.delete('authentication')
56
+ # but we place it in a Faspex package creation response
57
+ return {
58
+ 'links' => { 'status' => 'unused' },
59
+ 'xfer_sessions' => [transfer_spec]
164
60
  }
165
- Log.log.info{"Server started on port #{webrick_options[:Port]}"}
166
- @server = WEBrick::HTTPServer.new(webrick_options)
167
- @server.mount('/aspera/faspex', FxGwServlet, a_aoc_api_user, a_workspace_id)
168
- @server.mount('/newuser', NewUserServlet)
169
- trap('INT') { @server.shutdown }
170
61
  end
171
62
 
172
- def start_server
173
- @server.start
63
+ def do_POST(request, response)
64
+ case request.path
65
+ when '/aspera/faspex/send'
66
+ begin
67
+ raise 'no payload' if request.body.nil?
68
+ faspex_pkg_parameters = JSON.parse(request.body)
69
+ Log.log.debug{"faspex pkg create parameters=#{faspex_pkg_parameters}"}
70
+ faspex_package_create_result =
71
+ if @app_api.is_a?(Aspera::AoC)
72
+ faspex4_send_to_aoc(faspex_pkg_parameters)
73
+ elsif @app_api.is_a?(Aspera::Rest)
74
+ faspex4_send_to_faspex5(faspex_pkg_parameters)
75
+ else
76
+ raise "No such adapter: #{@app_api.class}"
77
+ end
78
+ Log.log.info{"faspex_package_create_result=#{faspex_package_create_result}"}
79
+ response.status = 200
80
+ response.content_type = 'application/json'
81
+ response.body = JSON.generate(faspex_package_create_result)
82
+ rescue => e
83
+ response.status = 500
84
+ response['Content-Type'] = 'application/json'
85
+ response.body = {error: e.message}.to_json
86
+ Log.log.error(e.message)
87
+ end
88
+ else
89
+ response.status = 400
90
+ response['Content-Type'] = 'application/json'
91
+ response.body = {error: 'Bad request'}.to_json
92
+ end
174
93
  end
175
- end # FaspexGW
94
+ end # Faspex4GWServlet
176
95
  end # AsperaLm
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ require 'aspera/web_server_simple'
5
+ require 'aspera/log'
6
+ require 'json'
7
+ require 'timeout'
8
+
9
+ module Aspera
10
+ # this class answers the Faspex /send API and creates a package on Aspera on Cloud
11
+ class Faspex4PostProcServlet < WEBrick::HTTPServlet::AbstractServlet
12
+ ALLOWED_PARAMETERS = %i[root script_folder fail_on_error timeout_seconds].freeze
13
+ def initialize(server, parameters)
14
+ raise 'parameters must be Hash' unless parameters.is_a?(Hash)
15
+ @parameters = parameters.symbolize_keys
16
+ Log.dump(:postproc_parameters, @parameters)
17
+ raise "unexpected key in parameters config: only: #{ALLOWED_PARAMETERS.join(', ')}" if @parameters.keys.any?{|k|!ALLOWED_PARAMETERS.include?(k)}
18
+ @parameters[:script_folder] ||= '.'
19
+ @parameters[:fail_on_error] ||= false
20
+ @parameters[:timeout_seconds] ||= 60
21
+ super(server)
22
+ Log.log.debug{"Faspex4PostProcServlet initialized"}
23
+ end
24
+
25
+ def do_POST(request, response)
26
+ Log.log.debug{"request=#{request.path}"}
27
+ begin
28
+ # only accept requests on the root
29
+ if !request.path.start_with?(@parameters[:root])
30
+ response.status = 400
31
+ response['Content-Type'] = 'application/json'
32
+ response.body = {status: 'error', message: 'Request outside domain'}.to_json
33
+ return
34
+ end
35
+ if request.body.nil?
36
+ response.status = 400
37
+ response['Content-Type'] = 'application/json'
38
+ response.body = {status: 'error', message: 'Empty request'}.to_json
39
+ return
40
+ end
41
+ # build script path by removing domain, and adding script folder
42
+ script_file = request.path[@parameters[:root].size .. ]
43
+ Log.log.debug{"script file=#{script_file}"}
44
+ script_path = File.join(@parameters[:script_folder], script_file)
45
+ Log.log.debug{"script=#{script_path}"}
46
+ webhook_parameters = JSON.parse(request.body)
47
+ Log.dump(:webhook_parameters, webhook_parameters)
48
+ # env expects only strings
49
+ environment = webhook_parameters.each_with_object({}) { |(k, v), h| h[k] = v.to_s }
50
+ post_proc_pid = Process.spawn(environment, [script_path, script_path])
51
+ Log.log.debug{"pid=#{post_proc_pid}"}
52
+ raise 'no pid' if post_proc_pid.nil?
53
+ # "wait" for process to avoid zombie
54
+ Timeout.timeout(@parameters[:timeout_seconds]) do
55
+ Process.wait(post_proc_pid)
56
+ post_proc_pid = nil
57
+ end
58
+ process_status = $CHILD_STATUS
59
+ raise "script #{script_path} failed with code #{process_status.exitstatus}" if !process_status.success? && @parameters[:fail_on_error]
60
+ response.status = 200
61
+ response.content_type = 'application/json'
62
+ response.body = JSON.generate({status: 'success', script: script_path, exit_code: process_status.exitstatus})
63
+ Log.log.debug{'Script executed successfully'}
64
+ rescue => e
65
+ Log.log.error("Script failed: #{e.class}:#{e.message}")
66
+ if !post_proc_pid.nil?
67
+ Process.kill('SIGKILL', post_proc_pid)
68
+ Process.wait(post_proc_pid)
69
+ Log.log.error("Killed process: #{post_proc_pid}")
70
+ end
71
+ response.status = 500
72
+ response['Content-Type'] = 'application/json'
73
+ response.body = {status: 'error', script: script_path, message: e.message}.to_json
74
+ end
75
+ end
76
+ end # Faspex4PostProcServlet
77
+ end # AsperaLm
data/lib/aspera/log.rb CHANGED
@@ -11,14 +11,13 @@ module Aspera
11
11
  # Singleton object for logging
12
12
  class Log
13
13
  include Singleton
14
+ # where logs are sent to
15
+ LOG_TYPES = %i[stderr stdout syslog].freeze
14
16
  # class methods
15
17
  class << self
16
18
  # levels are :debug,:info,:warn,:error,fatal,:unknown
17
19
  def levels; Logger::Severity.constants.sort{|a, b|Logger::Severity.const_get(a) <=> Logger::Severity.const_get(b)}.map{|c|c.downcase.to_sym}; end
18
20
 
19
- # where logs are sent to
20
- def logtypes; %i[stderr stdout syslog]; end
21
-
22
21
  # get the logger object of singleton
23
22
  def log; instance.logger; end
24
23
 
@@ -68,12 +67,13 @@ module Aspera
68
67
  end
69
68
 
70
69
  # change underlying logger, but keep log level
71
- def logger_type=(new_logtype)
70
+ def logger_type=(new_log_type)
72
71
  current_severity_integer = @logger.level unless @logger.nil?
73
72
  current_severity_integer = ENV['AS_LOG_LEVEL'] if current_severity_integer.nil? && ENV.key?('AS_LOG_LEVEL')
74
73
  current_severity_integer = Logger::Severity::WARN if current_severity_integer.nil?
75
- case new_logtype
74
+ case new_log_type
76
75
  when :stderr
76
+ # typed: Logger
77
77
  @logger = Logger.new($stderr)
78
78
  when :stdout
79
79
  @logger = Logger.new($stdout)
@@ -81,10 +81,10 @@ module Aspera
81
81
  require 'syslog/logger'
82
82
  @logger = Syslog::Logger.new(@program_name, Syslog::LOG_LOCAL2)
83
83
  else
84
- raise "unknown log type: #{new_logtype.class} #{new_logtype}"
84
+ raise "unknown log type: #{new_log_type.class} #{new_log_type}"
85
85
  end
86
86
  @logger.level = current_severity_integer
87
- @logger_type = new_logtype
87
+ @logger_type = new_log_type
88
88
  # update formatter with password hiding
89
89
  @logger.formatter = SecretHider.log_formatter(@logger.formatter)
90
90
  end
data/lib/aspera/nagios.rb CHANGED
@@ -50,14 +50,14 @@ module Aspera
50
50
  @data = []
51
51
  end
52
52
 
53
- # comparte remote time with local time
53
+ # compare remote time with local time
54
54
  def check_time_offset(remote_date, component)
55
55
  # check date if specified : 2015-10-13T07:32:01Z
56
- rtime = DateTime.strptime(remote_date)
57
- diff_time = (rtime - DateTime.now).abs
58
- diff_disp = diff_time.round(-2)
59
- Log.log.debug{"DATE: #{remote_date} #{rtime} diff=#{diff_disp}"}
60
- msg = "offset #{diff_disp} sec"
56
+ remote_time = DateTime.strptime(remote_date)
57
+ diff_time = (remote_time - DateTime.now).abs
58
+ diff_rounded = diff_time.round(-2)
59
+ Log.log.debug{"DATE: #{remote_date} #{remote_time} diff=#{diff_rounded}"}
60
+ msg = "offset #{diff_rounded} sec"
61
61
  if diff_time >= DATE_CRIT_OFFSET
62
62
  add_critical(component, msg)
63
63
  elsif diff_time >= DATE_WARN_OFFSET
data/lib/aspera/node.rb CHANGED
@@ -15,7 +15,7 @@ module Aspera
15
15
  ACCESS_LEVELS = %w[delete list mkdir preview read rename write].freeze
16
16
  # prefix for ruby code for filter
17
17
  MATCH_EXEC_PREFIX = 'exec:'
18
- X_ASPERA_ACCESSKEY = 'X-Aspera-AccessKey'
18
+ HEADER_X_ASPERA_ACCESS_KEY = 'X-Aspera-AccessKey'
19
19
  PATH_SEPARATOR = '/'
20
20
 
21
21
  # register node special token decoder
@@ -30,7 +30,7 @@ module Aspera
30
30
  # for access keys: provide expression to match entry in folder
31
31
  # if no prefix: regex
32
32
  # if prefix: ruby code
33
- # if filder is nil, then always match
33
+ # if expression is nil, then always match
34
34
  def file_matcher(match_expression)
35
35
  match_expression ||= "#{MATCH_EXEC_PREFIX}true"
36
36
  if match_expression.start_with?(MATCH_EXEC_PREFIX)
@@ -40,8 +40,8 @@ module Aspera
40
40
  end
41
41
  end
42
42
 
43
- REQUIRED_APP_INFO_FIELDS = %i[node_info app api plugin].freeze
44
- REQUIRED_APP_API_METHODS = %i[node_id_to_api add_ts_tags].freeze
43
+ REQUIRED_APP_INFO_FIELDS = %i[node_info app api workspace_info].freeze
44
+ REQUIRED_APP_API_METHODS = %i[node_api_from add_ts_tags].freeze
45
45
  private_constant :REQUIRED_APP_INFO_FIELDS, :REQUIRED_APP_API_METHODS
46
46
 
47
47
  attr_reader :app_info
@@ -72,13 +72,13 @@ module Aspera
72
72
  # @returns [Aspera::Node] a Node or nil
73
73
  def node_id_to_node(node_id)
74
74
  return self if !@app_info.nil? && @app_info[:node_info]['id'].eql?(node_id)
75
- return @app_info[:api].node_id_to_api(node_id) unless @app_info.nil?
75
+ return @app_info[:api].node_api_from(node_id: node_id, workspace_info: @app_info[workspace_info]) unless @app_info.nil?
76
76
  Log.log.warn{"cannot resolve link with node id #{node_id}"}
77
77
  return nil
78
78
  end
79
79
 
80
80
  # recursively browse in a folder (with non-recursive method)
81
- # subfolders a processed if the processing method returns true
81
+ # sub folders are processed if the processing method returns true
82
82
  # @param state [Object] state object sent to processing method
83
83
  # @param method [Symbol] processing method name
84
84
  # @param top_file_id [String] file id to start at (default = access key root file id)
@@ -86,29 +86,29 @@ module Aspera
86
86
  def process_folder_tree(state:, method:, top_file_id:, top_file_path: '/')
87
87
  raise 'INTERNAL ERROR: top_file_path not set' if top_file_path.nil?
88
88
  raise "INTERNAL ERROR: Missing method #{method}" unless respond_to?(method)
89
- folders_to_explore = [{id: top_file_id, relpath: top_file_path}]
89
+ folders_to_explore = [{id: top_file_id, path: top_file_path}]
90
90
  Log.dump(:folders_to_explore, folders_to_explore)
91
91
  until folders_to_explore.empty?
92
92
  current_item = folders_to_explore.shift
93
- Log.log.debug{"searching #{current_item[:relpath]}".bg_green}
93
+ Log.log.debug{"searching #{current_item[:path]}".bg_green}
94
94
  # get folder content
95
95
  folder_contents =
96
96
  begin
97
97
  read("files/#{current_item[:id]}/files")[:data]
98
98
  rescue StandardError => e
99
- Log.log.warn{"#{current_item[:relpath]}: #{e.class} #{e.message}"}
99
+ Log.log.warn{"#{current_item[:path]}: #{e.class} #{e.message}"}
100
100
  []
101
101
  end
102
102
  Log.dump(:folder_contents, folder_contents)
103
103
  folder_contents.each do |entry|
104
- relative_path = File.join(current_item[:relpath], entry['name'])
104
+ relative_path = File.join(current_item[:path], entry['name'])
105
105
  Log.log.debug{"looking #{relative_path}".bg_green}
106
106
  # continue only if method returns true
107
107
  next unless send(method, entry, relative_path, state)
108
108
  # entry type is file, folder or link
109
109
  case entry['type']
110
110
  when 'folder'
111
- folders_to_explore.push({id: entry['id'], relpath: relative_path})
111
+ folders_to_explore.push({id: entry['id'], path: relative_path})
112
112
  when 'link'
113
113
  node_id_to_node(entry['target_node_id'])&.process_folder_tree(
114
114
  state: state,
@@ -156,9 +156,9 @@ module Aspera
156
156
  end
157
157
 
158
158
  # Navigate the path from given file id
159
- # @param id initial file id
160
- # @param path file path
161
- # @return {.api,.file_id}
159
+ # @param top_file_id [String] id initial file id
160
+ # @param path [String] file path
161
+ # @return [Hash] {.api,.file_id}
162
162
  def resolve_api_fid(top_file_id, path)
163
163
  raise 'file id shall be String' unless top_file_id.is_a?(String)
164
164
  path_elements = path.split(PATH_SEPARATOR).reject(&:empty?)
@@ -170,6 +170,7 @@ module Aspera
170
170
  end
171
171
 
172
172
  # add entry to list if test block is success
173
+ # @return [TrueClass,FalseClass]
173
174
  def process_find_files(entry, path, state)
174
175
  begin
175
176
  # add to result if match filter
@@ -187,12 +188,16 @@ module Aspera
187
188
  end
188
189
 
189
190
  def find_files(top_file_id, test_block)
190
- Log.log.debug{"find_files: fileid=#{top_file_id}"}
191
+ Log.log.debug{"find_files: file id=#{top_file_id}"}
191
192
  find_state = {found: [], test_block: test_block}
192
193
  process_folder_tree(state: find_state, method: :process_find_files, top_file_id: top_file_id)
193
194
  return find_state[:found]
194
195
  end
195
196
 
197
+ def refreshed_transfer_token
198
+ return oauth_token(force_refresh: true)
199
+ end
200
+
196
201
  # Create transfer spec for gen4
197
202
  def transfer_spec_gen4(file_id, direction, ts_merge=nil)
198
203
  ak_name = nil
@@ -201,10 +206,10 @@ module Aspera
201
206
  when :basic
202
207
  ak_name = params[:auth][:username]
203
208
  when :oauth2
204
- ak_name = params[:headers][X_ASPERA_ACCESSKEY]
205
- token_generation_lambda = lambda{|do_refresh|oauth_token(force_refresh: do_refresh)}
206
- ak_token = token_generation_lambda.call(false) # first time, use cache
207
- @app_info[:plugin].transfer.token_regenerator = token_generation_lambda unless @app_info.nil?
209
+ ak_name = params[:headers][HEADER_X_ASPERA_ACCESS_KEY]
210
+ # TODO: token_generation_lambda = lambda{|do_refresh|oauth_token(force_refresh: do_refresh)}
211
+ # get bearer token, possibly use cache
212
+ ak_token = oauth_token(force_refresh: false)
208
213
  else raise "Unsupported auth method for node gen4: #{params[:auth][:type]}"
209
214
  end
210
215
  transfer_spec = {