aspera-cli 4.19.0 → 4.20.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 (80) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +20 -0
  4. data/CONTRIBUTING.md +16 -4
  5. data/README.md +344 -164
  6. data/bin/asession +26 -19
  7. data/examples/build_exec +65 -76
  8. data/examples/build_exec_rubyc +40 -0
  9. data/examples/get_proto_file.rb +7 -0
  10. data/lib/aspera/agent/alpha.rb +8 -8
  11. data/lib/aspera/agent/base.rb +2 -18
  12. data/lib/aspera/agent/connect.rb +14 -13
  13. data/lib/aspera/agent/direct.rb +23 -24
  14. data/lib/aspera/agent/httpgw.rb +2 -3
  15. data/lib/aspera/agent/node.rb +10 -10
  16. data/lib/aspera/agent/trsdk.rb +17 -20
  17. data/lib/aspera/api/alee.rb +15 -0
  18. data/lib/aspera/api/aoc.rb +126 -97
  19. data/lib/aspera/api/ats.rb +1 -1
  20. data/lib/aspera/api/cos_node.rb +1 -1
  21. data/lib/aspera/api/httpgw.rb +15 -10
  22. data/lib/aspera/api/node.rb +33 -12
  23. data/lib/aspera/ascmd.rb +56 -48
  24. data/lib/aspera/ascp/installation.rb +99 -42
  25. data/lib/aspera/ascp/management.rb +3 -2
  26. data/lib/aspera/ascp/products.rb +12 -0
  27. data/lib/aspera/assert.rb +10 -5
  28. data/lib/aspera/cli/formatter.rb +27 -17
  29. data/lib/aspera/cli/hints.rb +2 -1
  30. data/lib/aspera/cli/info.rb +12 -10
  31. data/lib/aspera/cli/main.rb +16 -13
  32. data/lib/aspera/cli/manager.rb +5 -0
  33. data/lib/aspera/cli/plugin.rb +15 -29
  34. data/lib/aspera/cli/plugins/alee.rb +3 -3
  35. data/lib/aspera/cli/plugins/aoc.rb +222 -194
  36. data/lib/aspera/cli/plugins/ats.rb +16 -14
  37. data/lib/aspera/cli/plugins/config.rb +53 -45
  38. data/lib/aspera/cli/plugins/console.rb +3 -3
  39. data/lib/aspera/cli/plugins/faspex.rb +11 -21
  40. data/lib/aspera/cli/plugins/faspex5.rb +44 -42
  41. data/lib/aspera/cli/plugins/faspio.rb +2 -2
  42. data/lib/aspera/cli/plugins/httpgw.rb +1 -1
  43. data/lib/aspera/cli/plugins/node.rb +153 -95
  44. data/lib/aspera/cli/plugins/orchestrator.rb +14 -13
  45. data/lib/aspera/cli/plugins/preview.rb +8 -9
  46. data/lib/aspera/cli/plugins/server.rb +5 -9
  47. data/lib/aspera/cli/plugins/shares.rb +2 -2
  48. data/lib/aspera/cli/sync_actions.rb +2 -2
  49. data/lib/aspera/cli/transfer_agent.rb +12 -14
  50. data/lib/aspera/cli/transfer_progress.rb +35 -17
  51. data/lib/aspera/cli/version.rb +1 -1
  52. data/lib/aspera/command_line_builder.rb +3 -4
  53. data/lib/aspera/coverage.rb +13 -1
  54. data/lib/aspera/environment.rb +34 -18
  55. data/lib/aspera/faspex_gw.rb +2 -2
  56. data/lib/aspera/json_rpc.rb +1 -1
  57. data/lib/aspera/keychain/macos_security.rb +7 -12
  58. data/lib/aspera/log.rb +3 -4
  59. data/lib/aspera/oauth/base.rb +39 -45
  60. data/lib/aspera/oauth/factory.rb +11 -4
  61. data/lib/aspera/oauth/generic.rb +4 -8
  62. data/lib/aspera/oauth/jwt.rb +3 -3
  63. data/lib/aspera/oauth/url_json.rb +1 -2
  64. data/lib/aspera/oauth/web.rb +5 -2
  65. data/lib/aspera/persistency_action_once.rb +16 -8
  66. data/lib/aspera/preview/utils.rb +5 -16
  67. data/lib/aspera/rest.rb +100 -76
  68. data/lib/aspera/transfer/faux_file.rb +4 -4
  69. data/lib/aspera/transfer/parameters.rb +14 -16
  70. data/lib/aspera/transfer/spec.rb +12 -12
  71. data/lib/aspera/transfer/sync.rb +1 -5
  72. data/lib/aspera/transfer/uri.rb +1 -1
  73. data/lib/aspera/uri_reader.rb +1 -1
  74. data/lib/aspera/web_auth.rb +166 -17
  75. data/lib/aspera/web_server_simple.rb +4 -3
  76. data/lib/transfer_pb.rb +84 -0
  77. data/lib/transfer_services_pb.rb +82 -0
  78. data.tar.gz.sig +0 -0
  79. metadata +24 -5
  80. metadata.gz.sig +0 -0
@@ -7,6 +7,7 @@ require 'aspera/log'
7
7
  require 'aspera/assert'
8
8
  require 'json'
9
9
  require 'uri'
10
+ require 'transfer_services_pb'
10
11
 
11
12
  module Aspera
12
13
  module Agent
@@ -48,14 +49,12 @@ module Aspera
48
49
  **base_options
49
50
  )
50
51
  super(**base_options)
51
- is_local_auto_port = @url.eql?(AUTO_LOCAL_TCP_PORT)
52
- raise 'Cannot use options `keep` or `external` with port zero' if is_local_auto_port && (@keep || @external)
53
- # load SDK stub class on demand, as it's an optional gem
54
- $LOAD_PATH.unshift(Ascp::Installation.instance.sdk_ruby_folder)
55
- require 'transfer_services_pb'
52
+ @keep = keep
53
+ is_local_auto_port = url.eql?(AUTO_LOCAL_TCP_PORT)
54
+ raise 'Cannot use options `keep` or `external` with port zero' if is_local_auto_port && (@keep || external)
56
55
  # keep PID for optional shutdown
57
56
  @daemon_pid = nil
58
- daemon_endpoint = @url
57
+ daemon_endpoint = url
59
58
  Log.log.debug{Log.dump(:daemon_endpoint, daemon_endpoint)}
60
59
  # retry loop
61
60
  begin
@@ -66,16 +65,14 @@ module Aspera
66
65
  # Initiate actual connection
67
66
  get_info_response = @transfer_client.get_info(Transfersdk::InstanceInfoRequest.new)
68
67
  Log.log.debug{"Daemon info: #{get_info_response}"}
69
- Log.log.warn{'Attached to existing daemon'} unless @daemon_pid || @external || @keep
68
+ Log.log.warn{'Attached to existing daemon'} unless @daemon_pid || external || @keep
70
69
  at_exit{shutdown}
71
70
  rescue GRPC::Unavailable => e
72
71
  # if transferd is external: do not start it, or other error
73
- raise if @external || !e.message.include?('failed to connect')
72
+ raise if external || !e.message.include?('failed to connect')
74
73
  # we already tried to start a daemon, but it failed
75
74
  Aspera.assert(@daemon_pid.nil?){"Daemon started with PID #{@daemon_pid}, but connection failed to #{daemon_endpoint}}"}
76
- Log.log.warn('no daemon present, starting daemon...') if @external
77
- # location of daemon binary
78
- sdk_folder = File.realpath(File.join(Ascp::Installation.instance.sdk_ruby_folder, '..'))
75
+ Log.log.warn('no daemon present, starting daemon...') if external
79
76
  # transferd only supports local ip and port
80
77
  daemon_uri = URI.parse("ipv4://#{daemon_endpoint}")
81
78
  Aspera.assert(daemon_uri.scheme.eql?('ipv4')){"Invalid scheme daemon URI #{daemon_endpoint}"}
@@ -86,8 +83,8 @@ module Aspera
86
83
  fasp_runtime: {
87
84
  use_embedded: false,
88
85
  user_defined: {
89
- bin: sdk_folder,
90
- etc: sdk_folder
86
+ bin: Ascp::Installation.instance.sdk_folder,
87
+ etc: Ascp::Installation.instance.sdk_folder
91
88
  }
92
89
  }
93
90
  }
@@ -143,24 +140,24 @@ module Aspera
143
140
  case response.status
144
141
  when :RUNNING
145
142
  if !session_started
146
- notify_progress(session_id: @transfer_id, type: :session_start)
143
+ notify_progress(:session_start, session_id: @transfer_id)
147
144
  session_started = true
148
145
  end
149
146
  if bytes_expected.nil? &&
150
147
  !response.sessionInfo.preTransferBytes.eql?(0)
151
148
  bytes_expected = response.sessionInfo.preTransferBytes
152
- notify_progress(type: :session_size, session_id: @transfer_id, info: bytes_expected)
149
+ notify_progress(:session_size, session_id: @transfer_id, info: bytes_expected)
153
150
  end
154
- notify_progress(type: :transfer, session_id: @transfer_id, info: response.transferInfo.bytesTransferred)
151
+ notify_progress(:transfer, session_id: @transfer_id, info: response.transferInfo.bytesTransferred)
155
152
  when :COMPLETED
156
- notify_progress(type: :transfer, session_id: @transfer_id, info: bytes_expected) if bytes_expected
157
- notify_progress(type: :end, session_id: @transfer_id)
153
+ notify_progress(:transfer, session_id: @transfer_id, info: bytes_expected) if bytes_expected
154
+ notify_progress(:end, session_id: @transfer_id)
158
155
  break
159
156
  when :FAILED, :CANCELED
160
- notify_progress(type: :end, session_id: @transfer_id)
157
+ notify_progress(:end, session_id: @transfer_id)
161
158
  raise Transfer::Error, JSON.parse(response.message)['Description']
162
159
  when :QUEUED, :UNKNOWN_STATUS, :PAUSED, :ORPHANED
163
- notify_progress(session_id: nil, type: :pre_start, info: response.status.to_s.downcase)
160
+ notify_progress(:pre_start, session_id: nil, info: response.status.to_s.downcase)
164
161
  else
165
162
  Log.log.error{"unknown status#{response.status}"}
166
163
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aspera/api/aoc.rb'
4
+ module Aspera
5
+ module Api
6
+ class Alee < Aspera::Rest
7
+ def initialize(entitlement_id, customer_id, api_domain: AoC::SAAS_DOMAIN_PROD, version: 'v1')
8
+ super(
9
+ base_url: "https://api.#{api_domain}/metering/#{version}",
10
+ headers: {'X-Aspera-Entitlement-Authorization' => Rest.basic_token(entitlement_id, customer_id)}
11
+ )
12
+ end
13
+ end
14
+ end
15
+ end
@@ -12,18 +12,9 @@ require 'cgi'
12
12
 
13
13
  module Aspera
14
14
  module Api
15
- SAAS_DOMAIN_PROD = 'ibmaspera.com'
16
- class Alee < Aspera::Rest
17
- def initialize(entitlement_id, customer_id, api_domain: SAAS_DOMAIN_PROD, version: 'v1')
18
- super(
19
- base_url: "https://api.#{api_domain}/metering/#{version}",
20
- headers: {'X-Aspera-Entitlement-Authorization' => Rest.basic_token(entitlement_id, customer_id)}
21
- )
22
- end
23
- end
24
-
25
15
  class AoC < Aspera::Rest
26
16
  PRODUCT_NAME = 'Aspera on Cloud'
17
+ # use default workspace if it is set, else none
27
18
  DEFAULT_WORKSPACE = ''
28
19
  # Production domain of AoC
29
20
  SAAS_DOMAIN_PROD = 'ibmaspera.com' # cspell:disable-line
@@ -78,42 +69,50 @@ module Aspera
78
69
  end
79
70
 
80
71
  # split host of http://myorg.asperafiles.com in org and domain
81
- def url_parts(uri)
72
+ def split_org_domain(uri)
82
73
  raise "No host found in URL.Please check URL format: https://myorg.#{SAAS_DOMAIN_PROD}" if uri.host.nil?
83
74
  parts = uri.host.split('.', 2)
84
75
  Aspera.assert(parts.length == 2){"expecting a public FQDN for #{PRODUCT_NAME}"}
85
- return parts
76
+ parts[0] = nil if parts[0].eql?('api')
77
+ return %i{organization domain}.zip(parts).to_h
86
78
  end
87
79
 
88
80
  # @param url [String] URL of AoC public link
89
81
  # @return [Hash] information about public link, or nil if not a public link
90
82
  def link_info(url)
91
- final_uri = Rest.new(base_url: url, redirect_max: MAX_AOC_URL_REDIRECT).read('')[:http].uri
92
- raise 'AoC shall redirect to login page' if final_uri.query.nil?
93
- decoded_query = Rest.decode_query(final_uri.query)
83
+ final_uri = Rest.new(base_url: url, redirect_max: MAX_AOC_URL_REDIRECT).call(operation: 'GET')[:http].uri
84
+ Log.log.trace1{Log.dump(:final_uri, final_uri)}
85
+ org_domain = split_org_domain(final_uri)
86
+ if (m = final_uri.path.match(%r{/oauth2/([^/]+)/login$}))
87
+ org_domain[:organization] = m[1] if org_domain[:organization].nil?
88
+ else
89
+ Log.log.debug{"path=#{final_uri.path} does not end with /login"}
90
+ end
91
+ raise 'AoC shall redirect to login page with a query' if final_uri.query.nil?
92
+ query = Rest.query_to_h(final_uri.query)
93
+ Log.log.trace1{Log.dump(:query, query)}
94
94
  # is that a public link ?
95
- if decoded_query.key?('token')
95
+ if query.key?('token')
96
96
  Log.log.warn{"Unknown pub link path: #{final_uri.path}"} unless PUBLIC_LINK_PATHS.include?(final_uri.path)
97
97
  # ok we get it !
98
98
  return {
99
- instance_domain: url_parts(final_uri)[1],
99
+ instance_domain: org_domain[:domain],
100
100
  url: "https://#{final_uri.host}",
101
- token: decoded_query['token']
101
+ token: query['token']
102
102
  }
103
103
  end
104
- Log.log.debug{"path=#{final_uri.path} does not end with /login"} unless final_uri.path.end_with?('/login')
105
- if decoded_query['state']
104
+ if query['state']
106
105
  # can be a private link
107
- state_uri = URI.parse(decoded_query['state'])
108
- if state_uri.query && decoded_query['redirect_uri']
109
- decoded_state = Rest.decode_query(state_uri.query)
106
+ state_uri = URI.parse(query['state'])
107
+ if state_uri.query && query['redirect_uri']
108
+ decoded_state = Rest.query_to_h(state_uri.query)
110
109
  if decoded_state.key?('short_link_url')
111
110
  if (m = state_uri.path.match(%r{/files/workspaces/([0-9]+)/all/([0-9]+):([0-9]+)}))
112
- redirect_uri = URI.parse(decoded_query['redirect_uri'])
113
- parts = url_parts(redirect_uri)
111
+ redirect_uri = URI.parse(query['redirect_uri'])
112
+ org_domain = split_org_domain(redirect_uri)
114
113
  return {
115
- instance_domain: parts[1],
116
- organization: parts[0],
114
+ instance_domain: org_domain[:domain],
115
+ organization: org_domain[:organization],
117
116
  url: "https://#{redirect_uri.host}",
118
117
  private_link: {
119
118
  workspace_id: m[1],
@@ -125,10 +124,10 @@ module Aspera
125
124
  end
126
125
  end
127
126
  end
128
- parts = url_parts(URI.parse(url))
127
+ Log.log.debug{Log.dump(:org_domain, org_domain)}
129
128
  return {
130
- instance_domain: parts[1],
131
- organization: parts[0]
129
+ instance_domain: org_domain[:domain],
130
+ organization: org_domain[:organization]
132
131
  }
133
132
  end
134
133
  end
@@ -149,7 +148,8 @@ module Aspera
149
148
  @workspace_name = workspace
150
149
  @cache_user_info = nil
151
150
  @cache_url_token_info = nil
152
- @context_cache = nil
151
+ @workspace_info = nil
152
+ @home_info = nil
153
153
  auth_params = {
154
154
  type: :oauth2,
155
155
  client_id: client_id,
@@ -206,7 +206,7 @@ module Aspera
206
206
  return nil unless auth_params[:grant_method].eql?(:url_json)
207
207
  return @cache_url_token_info unless @cache_url_token_info.nil?
208
208
  # TODO: can there be several in list ?
209
- @cache_url_token_info = read('url_tokens')[:data].first
209
+ @cache_url_token_info = read('url_tokens').first
210
210
  return @cache_url_token_info
211
211
  end
212
212
 
@@ -225,7 +225,7 @@ module Aspera
225
225
  # get our user's default information
226
226
  @cache_user_info =
227
227
  begin
228
- read('self')[:data]
228
+ read('self')
229
229
  rescue StandardError => e
230
230
  raise e if exception
231
231
  Log.log.debug{"ignoring error: #{e}"}
@@ -235,11 +235,20 @@ module Aspera
235
235
  return @cache_user_info
236
236
  end
237
237
 
238
- # @param application [Symbol] :files or :packages
238
+ def workspace
239
+ raise 'internal error: AoC workspace context is not set' if @workspace_info.nil?
240
+ @workspace_info
241
+ end
242
+
243
+ def home
244
+ raise 'internal error: AoC home context is not set' if @home_info.nil?
245
+ @home_info
246
+ end
247
+
248
+ # Set the application context
249
+ # @param application [Symbol,NilClass] :files or :packages
239
250
  # @return [Hash] current context information: workspace, and home node/file if app is "Files"
240
- def context(application = nil)
241
- return @context_cache unless @context_cache.nil?
242
- Aspera.assert(!application.nil?){'application must be set once'}
251
+ def context=(application)
243
252
  Aspera.assert_values(application, %i[files packages])
244
253
  ws_id =
245
254
  if !public_link.nil?
@@ -249,9 +258,10 @@ module Aspera
249
258
  Log.log.debug('Using workspace of private link')
250
259
  private_link[:workspace_id]
251
260
  elsif @workspace_name.eql?(DEFAULT_WORKSPACE)
252
- Log.log.debug('Using default workspace'.green)
253
- raise 'User does not have default workspace, please specify workspace' if current_user_info['default_workspace_id'].nil?
254
- current_user_info['default_workspace_id']
261
+ if !current_user_info['default_workspace_id'].nil?
262
+ Log.log.debug('Using default workspace'.green)
263
+ current_user_info['default_workspace_id']
264
+ end
255
265
  elsif @workspace_name.nil?
256
266
  nil
257
267
  else
@@ -261,39 +271,49 @@ module Aspera
261
271
  if ws_id.nil?
262
272
  nil
263
273
  else
264
- read("workspaces/#{ws_id}")[:data]
274
+ read("workspaces/#{ws_id}")
265
275
  end
266
- @context_cache = if ws_info.nil?
267
- {
268
- workspace_id: nil,
269
- workspace_name: 'Shared folders'
270
- }
271
- else
272
- {
273
- workspace_id: ws_info['id'],
274
- workspace_name: ws_info['name']
275
- }
276
- end
277
- return @context_cache unless application.eql?(:files)
278
- if !public_link.nil?
279
- assert_public_link_types(['view_shared_file'])
280
- @context_cache[:home_node_id] = public_link['data']['node_id']
281
- @context_cache[:home_file_id] = public_link['data']['file_id']
282
- elsif !private_link.nil?
283
- @context_cache[:home_node_id] = private_link[:node_id]
284
- @context_cache[:home_file_id] = private_link[:file_id]
285
- elsif ws_info
286
- @context_cache[:home_node_id] = ws_info['home_node_id']
287
- @context_cache[:home_file_id] = ws_info['home_file_id']
288
- else
289
- # not part of any workspace, but has some folder shared
290
- user_info = current_user_info(exception: true) rescue {'read_only_home_node_id' => nil, 'read_only_home_file_id' => nil}
291
- @context_cache[:home_node_id] = user_info['read_only_home_node_id']
292
- @context_cache[:home_file_id] = user_info['read_only_home_file_id']
293
- end
294
- raise "Cannot get user's home node id, check your default workspace or specify one" if @context_cache[:home_node_id].to_s.empty?
295
- Log.log.debug{Log.dump(:context, @context_cache)}
296
- return @context_cache
276
+ @workspace_info =
277
+ if ws_info.nil?
278
+ {
279
+ id: nil,
280
+ name: 'Shared folders'
281
+ }
282
+ else
283
+ {
284
+ id: ws_info['id'],
285
+ name: ws_info['name']
286
+ }
287
+ end
288
+ Log.log.debug{Log.dump(:context, @workspace_info)}
289
+ return nil unless application.eql?(:files)
290
+ @home_info =
291
+ if !public_link.nil?
292
+ assert_public_link_types(['view_shared_file'])
293
+ {
294
+ node_id: public_link['data']['node_id'],
295
+ file_id: public_link['data']['file_id']
296
+ }
297
+ elsif !private_link.nil?
298
+ {
299
+ node_id: private_link[:node_id],
300
+ file_id: private_link[:file_id]
301
+ }
302
+ elsif ws_info
303
+ {
304
+ node_id: ws_info['home_node_id'],
305
+ file_id: ws_info['home_file_id']
306
+ }
307
+ else
308
+ # not part of any workspace, but has some folder shared
309
+ user_info = current_user_info(exception: true) rescue {'read_only_home_node_id' => nil, 'read_only_home_file_id' => nil}
310
+ {
311
+ node_id: user_info['read_only_home_node_id'],
312
+ file_id: user_info['read_only_home_file_id']
313
+ }
314
+ end
315
+ raise "Cannot get user's home node id, check your default workspace or specify one" if @home_info[:node_id].to_s.empty?
316
+ Log.log.debug{Log.dump(:context, @home_info)}
297
317
  end
298
318
 
299
319
  # @param node_id [String] identifier of node in AoC
@@ -304,9 +324,9 @@ module Aspera
304
324
  # @returns [Node] a node API for access key
305
325
  def node_api_from(node_id:, workspace_id: nil, workspace_name: nil, scope: Node::SCOPE_USER, package_info: nil)
306
326
  Aspera.assert_type(node_id, String)
307
- node_info = read("nodes/#{node_id}")[:data]
327
+ node_info = read("nodes/#{node_id}")
308
328
  if workspace_name.nil? && !workspace_id.nil?
309
- workspace_name = read("workspaces/#{workspace_id}")[:data]['name']
329
+ workspace_name = read("workspaces/#{workspace_id}")['name']
310
330
  end
311
331
  app_info = {
312
332
  api: self, # for callback
@@ -346,7 +366,7 @@ module Aspera
346
366
  pkg_data['recipients'].first.is_a?(Hash) &&
347
367
  pkg_data['recipients'].first.key?('type') &&
348
368
  pkg_data['recipients'].first['type'].eql?('dropbox')
349
- meta_schema = read("dropboxes/#{pkg_data['recipients'].first['id']}")[:data]['metadata_schema']
369
+ meta_schema = read("dropboxes/#{pkg_data['recipients'].first['id']}")['metadata_schema']
350
370
  if meta_schema.nil? || meta_schema.empty?
351
371
  Log.log.debug('no metadata in shared inbox')
352
372
  return
@@ -390,7 +410,7 @@ module Aspera
390
410
  # email: user, else dropbox
391
411
  entity_type = short_recipient_info.include?('@') ? 'contacts' : 'dropboxes'
392
412
  begin
393
- full_recipient_info = lookup_by_name(entity_type, short_recipient_info, {'current_workspace_id' => ws_id})
413
+ full_recipient_info = lookup_by_name(entity_type, short_recipient_info, query: {'current_workspace_id' => ws_id})
394
414
  rescue RuntimeError => e
395
415
  raise e unless e.message.start_with?(ENTITY_NOT_FOUND)
396
416
  # dropboxes cannot be created on the fly
@@ -399,7 +419,7 @@ module Aspera
399
419
  full_recipient_info = create('contacts', {
400
420
  'current_workspace_id' => ws_id,
401
421
  'email' => short_recipient_info
402
- }.merge(new_user_option))[:data]
422
+ }.merge(new_user_option))
403
423
  end
404
424
  short_recipient_info = if entity_type.eql?('dropboxes')
405
425
  {'id' => full_recipient_info['id'], 'type' => 'dropbox'}
@@ -453,7 +473,7 @@ module Aspera
453
473
  validate_metadata(package_data) if validate_meta
454
474
 
455
475
  # create a new package container
456
- created_package = create('packages', package_data)[:data]
476
+ created_package = create('packages', package_data)
457
477
 
458
478
  package_node_api = node_api_from(
459
479
  node_id: created_package['node_id'],
@@ -463,7 +483,7 @@ module Aspera
463
483
  # tell AoC what to expect in package: 1 transfer (can also be done after transfer)
464
484
  # TODO: if multi session was used we should probably tell
465
485
  # also, currently no "multi-source" , i.e. only from client-side files, unless "node" agent is used
466
- update("packages/#{created_package['id']}", {'sent' => true, 'transfers_expected' => 1})[:data]
486
+ update("packages/#{created_package['id']}", {'sent' => true, 'transfers_expected' => 1})
467
487
 
468
488
  return {
469
489
  spec: package_node_api.transfer_spec_gen4(created_package['contents_file_id'], Transfer::Spec::DIRECTION_SEND),
@@ -524,14 +544,14 @@ module Aspera
524
544
  ID_AK_ADMIN = 'ASPERA_ACCESS_KEY_ADMIN'
525
545
  # Callback from Plugins::Node
526
546
  # add application specific tags to permissions creation
527
- # @param create_param [Hash] parameters for creating permissions
547
+ # @param perm_data [Hash] parameters for creating permissions
528
548
  # @param app_info [Hash] application information
529
- def permissions_set_create_params(create_param:, app_info:)
549
+ def permissions_set_create_params(perm_data:, app_info:)
530
550
  # workspace shared folder:
531
551
  # access_id = "#{ID_AK_ADMIN}_WS_#{app_info[:workspace_id]}"
532
- default_params = {
552
+ defaults = {
533
553
  # 'access_type' => 'user', # mandatory: user or group
534
- # 'access_id' => access_id, # id of user or group
554
+ # 'access_id' => access_id, # id of user or group or special
535
555
  'tags' => {
536
556
  Transfer::Spec::TAG_RESERVED => {
537
557
  'files' => {
@@ -543,6 +563,7 @@ module Aspera
543
563
  'shared_by_name' => current_user_info['name'],
544
564
  'shared_by_email' => current_user_info['email'],
545
565
  # 'shared_with_name' => access_id,
566
+ # 'share_as' => new_name_for_folder,
546
567
  'access_key' => app_info[:node_info]['access_key'],
547
568
  'node' => app_info[:node_info]['name']
548
569
  }
@@ -550,34 +571,42 @@ module Aspera
550
571
  }
551
572
  }
552
573
  }
553
- create_param.deep_merge!(default_params)
554
- if create_param.key?('with')
555
- contact_info = lookup_by_name(
556
- 'contacts',
557
- create_param['with'],
558
- {'current_workspace_id' => app_info[:workspace_id], 'context' => 'share_folder'})
559
- create_param.delete('with')
560
- create_param['access_type'] = contact_info['source_type']
561
- create_param['access_id'] = contact_info['source_id']
562
- create_param['tags'][Transfer::Spec::TAG_RESERVED]['files']['workspace']['shared_with_name'] = contact_info['email']
574
+ perm_data.deep_merge!(defaults)
575
+ tag_workspace = perm_data['tags'][Transfer::Spec::TAG_RESERVED]['files']['workspace']
576
+ case perm_data['with']
577
+ when NilClass
578
+ when ''
579
+ perm_data['access_type'] = 'user'
580
+ perm_data['access_id'] = "#{ID_AK_ADMIN}_WS_#{app_info[:workspace_id]}"
581
+ tag_workspace['shared_with_name'] = perm_data['access_id']
582
+ else
583
+ entity_info = lookup_by_name('contacts', perm_data['with'], query: {'current_workspace_id' => app_info[:workspace_id]})
584
+ perm_data['access_type'] = entity_info['source_type']
585
+ perm_data['access_id'] = entity_info['source_id']
586
+ tag_workspace['shared_with_name'] = entity_info['email']
587
+ end
588
+ perm_data.delete('with')
589
+ if perm_data.key?('as')
590
+ tag_workspace['share_as'] = perm_data['as']
591
+ perm_data.delete('as')
563
592
  end
564
593
  # optional
565
- app_info[:opt_link_name] = create_param.delete('link_name')
594
+ app_info[:opt_link_name] = perm_data.delete('link_name')
566
595
  end
567
596
 
568
597
  # Callback from Plugins::Node
569
598
  # send shared folder event to AoC
570
- # @param created_data [Hash] response from permission creation
599
+ # @param event_data [Hash] response from permission creation
571
600
  # @param app_info [Hash] hash with app info
572
601
  # @param types [Array] event types
573
- def permissions_send_event(created_data:, app_info:, types: PERMISSIONS_CREATED)
602
+ def permissions_send_event(event_data:, app_info:, types: PERMISSIONS_CREATED)
574
603
  Aspera.assert_type(types, Array)
575
604
  Aspera.assert(!types.empty?)
576
605
  event_creation = {
577
606
  'types' => types,
578
607
  'node_id' => app_info[:node_info]['id'],
579
608
  'workspace_id' => app_info[:workspace_id],
580
- 'data' => created_data
609
+ 'data' => event_data
581
610
  }
582
611
  # (optional). The name of the folder to be displayed to the destination user.
583
612
  # Use it if its value is different from the "share_as" field.
@@ -34,7 +34,7 @@ module Aspera
34
34
  if @all_servers_cache.nil?
35
35
  @all_servers_cache = []
36
36
  CLOUD_NAME.each_key do |name|
37
- read("servers/#{name.to_s.upcase}")[:data].each do |i|
37
+ read("servers/#{name.to_s.upcase}").each do |i|
38
38
  @all_servers_cache.push(i)
39
39
  end
40
40
  end
@@ -19,7 +19,7 @@ module Aspera
19
19
  Aspera.assert(service_credentials.key?(field)){"service_credentials must have a field: #{field}"}
20
20
  end
21
21
  # read endpoints from service provided in service credentials
22
- endpoints = Aspera::Rest.new(base_url: service_credentials['endpoints']).read('')[:data]
22
+ endpoints = Aspera::Rest.new(base_url: service_credentials['endpoints']).read('')
23
23
  Aspera::Log.dump('endpoints', endpoints)
24
24
  endpoint = endpoints.dig('service-endpoints', 'regional', bucket_region, 'public', bucket_region)
25
25
  raise "no such region: #{bucket_region}" if endpoint.nil?
@@ -138,7 +138,7 @@ module Aspera
138
138
  def upload(transfer_spec)
139
139
  # identify this session uniquely
140
140
  session_id = SecureRandom.uuid
141
- @notify_cb&.call(session_id: nil, type: :pre_start, info: 'starting')
141
+ @notify_cb&.call(:pre_start, session_id: nil, info: 'starting')
142
142
  # process files to send, modify `paths` in transfer_spec
143
143
  files_to_send = process_upload_list(transfer_spec)
144
144
  # total size of all files is last element
@@ -147,7 +147,7 @@ module Aspera
147
147
  Log.log.trace1{Log.dump(:files_to_send, files_to_send)}
148
148
  # TODO: check that this is available in endpoints: @api_info['endpoints']
149
149
  upload_url = File.join(@gw_root_url, @upload_version, 'upload')
150
- @notify_cb&.call(session_id: nil, type: :pre_start, info: 'connecting wss')
150
+ @notify_cb&.call(:pre_start, session_id: nil, info: 'connecting wss')
151
151
  # open web socket to end point (equivalent to Net::HTTP.start)
152
152
  http_session = Rest.start_http_session(upload_url)
153
153
  # get the underlying socket i/o
@@ -172,11 +172,11 @@ module Aspera
172
172
  }
173
173
  # start read thread after handshake
174
174
  @ws_read_thread = Thread.new {process_read_thread}
175
- @notify_cb&.call(session_id: session_id, type: :session_start)
176
- @notify_cb&.call(session_id: session_id, type: :session_size, info: total_bytes_to_transfer)
175
+ @notify_cb&.call(:session_start, session_id: session_id)
176
+ @notify_cb&.call(:session_size, session_id: session_id, info: total_bytes_to_transfer)
177
177
  sleep(1)
178
178
  # notify progress bar
179
- @notify_cb&.call(type: :session_size, session_id: session_id, info: total_bytes_to_transfer)
179
+ @notify_cb&.call(:session_size, session_id: session_id, info: total_bytes_to_transfer)
180
180
  # first step send transfer spec
181
181
  ws_snd_json(MSG_SEND_TRANSFER_SPEC, transfer_spec)
182
182
  # current file index
@@ -223,7 +223,7 @@ module Aspera
223
223
  raise e
224
224
  end
225
225
  session_sent_bytes += slice_bin_data.length
226
- @notify_cb&.call(type: :transfer, session_id: session_id, info: session_sent_bytes)
226
+ @notify_cb&.call(:transfer, session_id: session_id, info: session_sent_bytes)
227
227
  slice_info[:slice] += 1
228
228
  end
229
229
  ensure
@@ -232,8 +232,8 @@ module Aspera
232
232
  file_index += 1
233
233
  end
234
234
  # throttling may have skipped last one
235
- @notify_cb&.call(type: :transfer, session_id: session_id, info: session_sent_bytes)
236
- @notify_cb&.call(type: :end, session_id: session_id)
235
+ @notify_cb&.call(:transfer, session_id: session_id, info: session_sent_bytes)
236
+ @notify_cb&.call(:end, session_id: session_id)
237
237
  ws_send(ws_type: :close, data: nil)
238
238
  Log.log.debug("Finished upload, waiting for end of #{THR_RECV} thread.")
239
239
  @ws_read_thread.join
@@ -256,7 +256,7 @@ module Aspera
256
256
  end
257
257
  transfer_spec['download_name'] = download_name
258
258
  end
259
- creation = create('download', {'transfer_spec' => transfer_spec})[:data]
259
+ creation = create('download', {'transfer_spec' => transfer_spec})
260
260
  transfer_uuid = creation['url'].split('/').last
261
261
  file_name =
262
262
  if transfer_spec['zip_required'] || transfer_spec['paths'].length > 1
@@ -274,6 +274,11 @@ module Aspera
274
274
  return @api_info
275
275
  end
276
276
 
277
+ # @return the base url of the gateway
278
+ def base_url
279
+ return @gw_root_url
280
+ end
281
+
277
282
  # @param url [String] URL of the HTTP Gateway, without version
278
283
  def initialize(
279
284
  url:,
@@ -299,7 +304,7 @@ module Aspera
299
304
  @synchronous = synchronous
300
305
  @notify_cb = notify_cb
301
306
  # get API info
302
- @api_info = read('info')[:data].freeze
307
+ @api_info = read('info').freeze
303
308
  Log.log.debug{Log.dump(:api_info, @api_info)}
304
309
  # web socket endpoint: by default use v2 (newer gateways), without base64 encoding
305
310
  # is the latest supported? else revert to old api