aspera-cli 4.19.0 → 4.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +46 -0
  4. data/CONTRIBUTING.md +18 -4
  5. data/README.md +886 -510
  6. data/bin/asession +27 -20
  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 +18 -24
  11. data/lib/aspera/agent/base.rb +2 -18
  12. data/lib/aspera/agent/connect.rb +34 -15
  13. data/lib/aspera/agent/direct.rb +44 -54
  14. data/lib/aspera/agent/httpgw.rb +2 -3
  15. data/lib/aspera/agent/node.rb +11 -21
  16. data/lib/aspera/agent/{trsdk.rb → transferd.rb} +27 -51
  17. data/lib/aspera/api/alee.rb +15 -0
  18. data/lib/aspera/api/aoc.rb +139 -105
  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 +70 -32
  23. data/lib/aspera/ascmd.rb +56 -48
  24. data/lib/aspera/ascp/installation.rb +166 -70
  25. data/lib/aspera/ascp/management.rb +30 -8
  26. data/lib/aspera/assert.rb +10 -5
  27. data/lib/aspera/cli/formatter.rb +166 -162
  28. data/lib/aspera/cli/hints.rb +2 -1
  29. data/lib/aspera/cli/info.rb +12 -10
  30. data/lib/aspera/cli/main.rb +28 -13
  31. data/lib/aspera/cli/manager.rb +7 -2
  32. data/lib/aspera/cli/plugin.rb +17 -31
  33. data/lib/aspera/cli/plugins/alee.rb +3 -3
  34. data/lib/aspera/cli/plugins/aoc.rb +246 -208
  35. data/lib/aspera/cli/plugins/ats.rb +16 -14
  36. data/lib/aspera/cli/plugins/config.rb +154 -94
  37. data/lib/aspera/cli/plugins/console.rb +3 -3
  38. data/lib/aspera/cli/plugins/cos.rb +1 -0
  39. data/lib/aspera/cli/plugins/faspex.rb +15 -23
  40. data/lib/aspera/cli/plugins/faspex5.rb +64 -50
  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 +174 -109
  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 +37 -17
  51. data/lib/aspera/cli/version.rb +1 -1
  52. data/lib/aspera/command_line_builder.rb +4 -5
  53. data/lib/aspera/coverage.rb +13 -1
  54. data/lib/aspera/environment.rb +75 -25
  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/node_simulator.rb +230 -112
  60. data/lib/aspera/oauth/base.rb +64 -83
  61. data/lib/aspera/oauth/factory.rb +52 -6
  62. data/lib/aspera/oauth/generic.rb +4 -8
  63. data/lib/aspera/oauth/jwt.rb +6 -3
  64. data/lib/aspera/oauth/url_json.rb +1 -2
  65. data/lib/aspera/oauth/web.rb +5 -2
  66. data/lib/aspera/persistency_action_once.rb +16 -8
  67. data/lib/aspera/persistency_folder.rb +20 -2
  68. data/lib/aspera/preview/generator.rb +1 -1
  69. data/lib/aspera/preview/utils.rb +11 -17
  70. data/lib/aspera/products/alpha.rb +30 -0
  71. data/lib/aspera/products/connect.rb +48 -0
  72. data/lib/aspera/products/other.rb +82 -0
  73. data/lib/aspera/products/transferd.rb +54 -0
  74. data/lib/aspera/rest.rb +116 -87
  75. data/lib/aspera/secret_hider.rb +2 -2
  76. data/lib/aspera/ssh.rb +31 -24
  77. data/lib/aspera/transfer/faux_file.rb +4 -4
  78. data/lib/aspera/transfer/parameters.rb +16 -17
  79. data/lib/aspera/transfer/spec.rb +12 -12
  80. data/lib/aspera/transfer/spec.yaml +22 -20
  81. data/lib/aspera/transfer/sync.rb +2 -10
  82. data/lib/aspera/transfer/uri.rb +3 -3
  83. data/lib/aspera/uri_reader.rb +1 -1
  84. data/lib/aspera/web_auth.rb +166 -17
  85. data/lib/aspera/web_server_simple.rb +4 -3
  86. data/lib/transferd_pb.rb +86 -0
  87. data/lib/transferd_services_pb.rb +84 -0
  88. data.tar.gz.sig +0 -0
  89. metadata +58 -22
  90. metadata.gz.sig +0 -0
  91. data/lib/aspera/ascp/products.rb +0 -156
@@ -63,17 +63,17 @@ module Aspera
63
63
  # no protocol ?
64
64
  base_url = "https://#{base_url}" unless base_url.match?(%r{^[a-z]{1,6}://})
65
65
  # only org provided ?
66
- base_url = "#{base_url}.#{Api::SAAS_DOMAIN_PROD}" unless base_url.include?('.')
66
+ base_url = "#{base_url}.#{Api::AoC::SAAS_DOMAIN_PROD}" unless base_url.include?('.')
67
67
  # AoC is only https
68
68
  return nil unless base_url.start_with?('https://')
69
- result = Rest.new(base_url: base_url, redirect_max: 10).read('')
70
- # Any AoC is on this domain
71
- return nil unless result[:http].uri.host.end_with?(Api::SAAS_DOMAIN_PROD)
72
- Log.log.debug{"AoC Main page: #{result[:http].body.include?(Api::AoC::PRODUCT_NAME)}"}
73
- base_url = result[:http].uri.to_s if result[:http].uri.path.include?('/public')
69
+ res_http = Rest.new(base_url: base_url, redirect_max: 0).call(operation: 'GET', subpath: 'auth/ping', return_error: true)[:http]
70
+ return nil if res_http['Location'].nil?
71
+ redirect_uri = URI.parse(res_http['Location'])
72
+ od = Api::AoC.split_org_domain(URI.parse(base_url))
73
+ return nil unless redirect_uri.path.end_with?("oauth2/#{od[:organization]}/login")
74
74
  # either in standard domain, or product name in page
75
75
  return {
76
- version: 'SaaS',
76
+ version: Api::AoC.saas_url?(base_url) ? 'SaaS' : 'Self-managed',
77
77
  url: base_url
78
78
  }
79
79
  end
@@ -92,13 +92,11 @@ module Aspera
92
92
  # set vars to look like object
93
93
  options = object.options
94
94
  formatter = object.formatter
95
- options.declare(:use_generic_client, 'Wizard: AoC: use global or org specific jwt client id', values: :bool, default: true)
96
- options.parse_options!
97
95
  instance_url = options.get_option(:url, mandatory: true)
98
96
  pub_link_info = Api::AoC.link_info(instance_url)
99
97
  if !pub_link_info[:token].nil?
100
98
  pub_api = Rest.new(base_url: "https://#{URI.parse(pub_link_info[:url]).host}/api/v1")
101
- pub_info = pub_api.read('env/url_token_check', {token: pub_link_info[:token]})[:data]
99
+ pub_info = pub_api.read('env/url_token_check', {token: pub_link_info[:token]})
102
100
  preset_value = {
103
101
  link: instance_url
104
102
  }
@@ -108,6 +106,8 @@ module Aspera
108
106
  test_args: 'organization'
109
107
  }
110
108
  end
109
+ options.declare(:use_generic_client, 'Wizard: AoC: use global or org specific jwt client id', values: :bool, default: Api::AoC.saas_url?(instance_url))
110
+ options.parse_options!
111
111
  # make username mandatory for jwt, this triggers interactive input
112
112
  wiz_username = options.get_option(:username, mandatory: true)
113
113
  raise "Username shall be an email in AoC: #{wiz_username}" if !(wiz_username =~ /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i)
@@ -133,15 +133,15 @@ module Aspera
133
133
  formatter.display_status('Please login to your Aspera on Cloud instance.'.red)
134
134
  formatter.display_status('Navigate to: 𓃑 → Admin → Integrations → API Clients')
135
135
  formatter.display_status('Check or create in integration:')
136
- formatter.display_status("- name: #{@info[:name]}")
136
+ formatter.display_status('- name: cli')
137
137
  formatter.display_status("- redirect uri: #{REDIRECT_LOCALHOST}")
138
138
  formatter.display_status('- origin: localhost')
139
139
  formatter.display_status('Use the generated client id and secret in the following prompts.'.red)
140
140
  end
141
- Environment.instance.open_uri("#{instance_url}/admin/api-clients")
141
+ Environment.instance.open_uri("#{instance_url}/admin/integrations/api-clients")
142
142
  options.get_option(:client_id, mandatory: true)
143
143
  options.get_option(:client_secret, mandatory: true)
144
- use_browser_authentication = true
144
+ # use_browser_authentication = true
145
145
  end
146
146
  if use_browser_authentication
147
147
  formatter.display_status('We will use web authentication to bootstrap.')
@@ -152,7 +152,7 @@ module Aspera
152
152
  # aoc_api.oauth.scope = Api::AoC::SCOPE_FILES_ADMIN
153
153
  # aoc_api.oauth.specific_parameters[:redirect_uri] = REDIRECT_LOCALHOST
154
154
  end
155
- myself = object.aoc_api.read('self')[:data]
155
+ myself = object.aoc_api.read('self')
156
156
  if auto_set_pub_key
157
157
  Aspera.assert(myself['public_key'].empty?, exception_class: Cli::Error){'Public key is already set in profile (use --override=yes)'} unless option_override
158
158
  formatter.display_status('Updating profile with the public key.')
@@ -214,7 +214,7 @@ module Aspera
214
214
  def aoc_api
215
215
  if @cache_api_aoc.nil?
216
216
  @cache_api_aoc = api_from_options(Api::AoC::API_V1)
217
- organization = @cache_api_aoc.read('organization')[:data]
217
+ organization = @cache_api_aoc.read('organization')
218
218
  if organization['http_gateway_enabled'] && organization['http_gateway_server_url']
219
219
  transfer.httpgw_url_cb = lambda { organization['http_gateway_server_url'] }
220
220
  # @cache_api_aoc.current_user_info['connect_disabled']
@@ -223,7 +223,8 @@ module Aspera
223
223
  return @cache_api_aoc
224
224
  end
225
225
 
226
- # get identifier or name from command line
226
+ # Get resource identifier from command line, either directly or from name.
227
+ # @param resource_class_path url path for resource
227
228
  # @return identifier
228
229
  def get_resource_id_from_args(resource_class_path)
229
230
  return instance_identifier do |field, value|
@@ -232,11 +233,13 @@ module Aspera
232
233
  end
233
234
  end
234
235
 
236
+ # Get resource path from command line
235
237
  def get_resource_path_from_args(resource_class_path)
236
238
  return "#{resource_class_path}/#{get_resource_id_from_args(resource_class_path)}"
237
239
  end
238
240
 
239
241
  # Call block with same query using paging and response information
242
+ # block must return a hash with :data and :http keys
240
243
  # @return [Hash] {data: , total: }
241
244
  def api_call_paging(base_query={})
242
245
  Aspera.assert_type(base_query, Hash){'query'}
@@ -254,6 +257,8 @@ module Aspera
254
257
  query = base_query.clone
255
258
  query['page'] = current_page
256
259
  result = yield(query)
260
+ Aspera.assert(result[:data])
261
+ Aspera.assert(result[:http])
257
262
  total_count = result[:http]['X-Total-Count']
258
263
  page_count += 1
259
264
  current_page += 1
@@ -272,18 +277,22 @@ module Aspera
272
277
  # @return [Hash] {data: , total: }
273
278
  def api_read_all(resource_class_path, base_query={})
274
279
  return api_call_paging(base_query) do |query|
275
- aoc_api.read(resource_class_path, query)
280
+ aoc_api.call(operation: 'GET', subpath: resource_class_path, headers: {'Accept' => 'application/json'}, query: query)
276
281
  end
277
282
  end
278
283
 
279
284
  # list all entities, given additional, default and user's queries
285
+ # @param resource_class_path path to query on API
286
+ # @param fields fields to display
287
+ # @param base_query a query applied always
288
+ # @param default_query default query unless overriden by user
280
289
  def result_list(resource_class_path, fields: nil, base_query: {}, default_query: {})
281
290
  Aspera.assert_type(base_query, Hash)
282
291
  Aspera.assert_type(default_query, Hash)
283
292
  user_query = query_read_delete(default: default_query)
284
293
  # caller may add specific modifications or checks
285
294
  yield(user_query) if block_given?
286
- return {type: :object_list, fields: fields}.merge(api_read_all(resource_class_path, base_query.merge(user_query)))
295
+ return {type: :object_list, fields: fields}.merge(api_read_all(resource_class_path, base_query.merge(user_query).compact))
287
296
  end
288
297
 
289
298
  def resolve_dropbox_name_default_ws_id(query)
@@ -294,7 +303,7 @@ module Aspera
294
303
  query['dropbox_id'] = aoc_api.lookup_by_name('dropboxes', query['dropbox_name'])['id']
295
304
  query.delete('dropbox_name')
296
305
  end
297
- query['workspace_id'] ||= aoc_api.context[:workspace_id] unless aoc_api.context[:workspace_id].eql?(:undefined)
306
+ query['workspace_id'] ||= aoc_api.workspace[:id] unless aoc_api.workspace[:id].eql?(:undefined)
298
307
  # by default show dropbox packages only for dropboxes
299
308
  query['exclude_dropbox_packages'] = !query.key?('dropbox_id') unless query.key?('exclude_dropbox_packages')
300
309
  end
@@ -307,11 +316,11 @@ module Aspera
307
316
  def execute_nodegen4_command(command_repo, node_id, file_id: nil, scope: nil)
308
317
  top_node_api = aoc_api.node_api_from(
309
318
  node_id: node_id,
310
- workspace_id: aoc_api.context[:workspace_id],
311
- workspace_name: aoc_api.context[:workspace_name],
319
+ workspace_id: aoc_api.workspace[:id],
320
+ workspace_name: aoc_api.workspace[:name],
312
321
  scope: scope
313
322
  )
314
- file_id = top_node_api.read("access_keys/#{top_node_api.app_info[:node_info]['access_key']}")[:data]['root_file_id'] if file_id.nil?
323
+ file_id = top_node_api.read("access_keys/#{top_node_api.app_info[:node_info]['access_key']}")['root_file_id'] if file_id.nil?
315
324
  node_plugin = Node.new(**init_params, api: top_node_api)
316
325
  case command_repo
317
326
  when *Node::COMMANDS_GEN4
@@ -321,7 +330,7 @@ module Aspera
321
330
  # server side is transfer server
322
331
  # in same workspace
323
332
  push_pull = options.get_next_argument('direction', accept_list: %i[push pull])
324
- source_folder = options.get_next_argument('folder of source files', validation: String)
333
+ source_folder = options.get_next_argument('folder or source files', validation: String)
325
334
  case push_pull
326
335
  when :push
327
336
  client_direction = Transfer::Spec::DIRECTION_SEND
@@ -390,7 +399,7 @@ module Aspera
390
399
  # TODO: report inconsistency: creation url is !=, and does not return id.
391
400
  resource_class_path = 'admin/client_registration/token' if resource_class_path.eql?('admin/client_registration_tokens')
392
401
  return do_bulk_operation(command: command, descr: 'creation data', id_result: id_result) do |params|
393
- aoc_api.create(resource_class_path, params)[:data]
402
+ aoc_api.create(resource_class_path, params)
394
403
  end
395
404
  when :list
396
405
  default_fields = ['id']
@@ -406,12 +415,12 @@ module Aspera
406
415
  when :operation then default_fields = nil
407
416
  when :short_link then default_fields.push('short_url', 'data.url_token_data.purpose')
408
417
  when :user then default_fields.push('name', 'email')
409
- when :group_membership then default_fields.push(*%w[group_id member_type member_id])
410
- when :workspace_membership then default_fields.push(*%w[workspace_id member_type member_id])
418
+ when :group_membership then default_fields.push('group_id', 'member_type', 'member_id')
419
+ when :workspace_membership then default_fields.push('workspace_id', 'member_type', 'member_id')
411
420
  end
412
421
  return result_list(resource_class_path, fields: default_fields, default_query: default_query)
413
422
  when :show
414
- object = aoc_api.read(resource_instance_path)[:data]
423
+ object = aoc_api.read(resource_instance_path)
415
424
  # default: show all, but certificate
416
425
  fields = object.keys.reject{|k|k.eql?('certificate')}
417
426
  return { type: :single_object, data: object, fields: fields }
@@ -435,7 +444,7 @@ module Aspera
435
444
  when :do
436
445
  command_repo = options.get_next_command(NODE4_EXT_COMMANDS)
437
446
  # init context
438
- aoc_api.context(:files)
447
+ aoc_api.context = :files
439
448
  return execute_nodegen4_command(command_repo, res_id)
440
449
  else Aspera.error_unexpected_value(command)
441
450
  end
@@ -462,59 +471,61 @@ module Aspera
462
471
  raise 'not implemented'
463
472
  end
464
473
  when :subscription
465
- org = aoc_api.read('organization')[:data]
466
- bss_api = api_from_options('bss/platform')
474
+ org = aoc_api.read('organization')
475
+ bss_graphql = api_from_options('bss/platform/graphql')
467
476
  # cspell:disable
468
- graphql_query = "
469
- query ($organization_id: ID!) {
470
- aoc (organization_id: $organization_id) {
471
- bssSubscription {
472
- endDate
473
- startDate
474
- termMonths
475
- plan
476
- trial
477
- termType
478
- instances {
479
- id
480
- entitlements {
481
- maxUsageMb
482
- }
483
- }
484
- additionalStorageVolumeGb
485
- additionalEgressVolumeGb
486
- additionalUsers
487
- term {
488
- startDate
477
+ graphql_query = <<-GRAPHQL
478
+ query ($organization_id: ID!) {
479
+ aoc (organization_id: $organization_id) {
480
+ bssSubscription {
481
+ aocVersion
489
482
  endDate
490
- transferVolumeGb
491
- egressVolumeGb
492
- storageVolumeGb
493
- }
494
- paygoRate {
495
- rate
496
- currency
497
- }
498
- aocPlanData {
499
- tier
483
+ startDate
484
+ termMonths
485
+ plan
500
486
  trial
501
- workspaces { max }
502
- users {
503
- planAmount
504
- max
487
+ termType
488
+ aocOrganizations {
489
+ id
490
+ }
491
+ additionalStorageVolumeGb
492
+ additionalEgressVolumeGb
493
+ term {
494
+ startDate
495
+ endDate
496
+ transferVolumeGb
497
+ egressVolumeGb
498
+ storageVolumeGb
499
+ transferVolumeOffsetGb
500
+ }
501
+ paygoRate {
502
+ transferRate
503
+ storageRate
504
+ currency
505
+ }
506
+ aocPlanData {
507
+ tier
508
+ trial
509
+ workspaces { max }
510
+ users {
511
+ planAmount
512
+ max
513
+ }
514
+ samlIntegration
515
+ activity
516
+ sharedInboxes
517
+ uniqueUrls
518
+ support
519
+ watermarking
520
+ byok
521
+ automation { planAmount, max }
505
522
  }
506
- samlIntegration
507
- activity
508
- sharedInboxes
509
- uniqueUrls
510
- support
511
523
  }
512
524
  }
513
525
  }
514
- }
515
- "
526
+ GRAPHQL
516
527
  # cspell:enable
517
- result = bss_api.create('graphql', {'variables' => {'organization_id' => org['id']}, 'query' => graphql_query})[:data]['data']
528
+ result = bss_graphql.create(nil, {query: graphql_query, variables: {organization_id: org['id']}})['data']
518
529
  return {type: :single_object, data: result['aoc']['bssSubscription']}
519
530
  when :ats
520
531
  ats_api = Rest.new(**aoc_api.params.deep_merge({
@@ -531,7 +542,7 @@ module Aspera
531
542
  case command_analytics
532
543
  when :application_events
533
544
  event_type = command_analytics.to_s
534
- events = analytics_api.read("organizations/#{aoc_api.current_user_info['organization_id']}/#{event_type}")[:data][event_type]
545
+ events = analytics_api.read("organizations/#{aoc_api.current_user_info['organization_id']}/#{event_type}")[event_type]
535
546
  return {type: :object_list, data: events}
536
547
  when :transfers
537
548
  event_type = command_analytics.to_s
@@ -546,6 +557,7 @@ module Aspera
546
557
  filter = options.get_option(:query) || {}
547
558
  filter['limit'] ||= 100
548
559
  if options.get_option(:once_only, mandatory: true)
560
+ aoc_api.context = :files
549
561
  saved_date = []
550
562
  start_date_persistency = PersistencyActionOnce.new(
551
563
  manager: persistency,
@@ -553,7 +565,7 @@ module Aspera
553
565
  id: IdGenerator.from_list([
554
566
  'aoc_ana_date',
555
567
  options.get_option(:url, mandatory: true),
556
- aoc_api.context(:files)[:workspace_name],
568
+ aoc_api.workspace[:name],
557
569
  filter_resource.to_s,
558
570
  filter_id
559
571
  ]))
@@ -565,7 +577,7 @@ module Aspera
565
577
  filter['start_time'] = start_date_time unless start_date_time.nil?
566
578
  filter['stop_time'] = stop_date_time
567
579
  end
568
- events = analytics_api.read("#{filter_resource}/#{filter_id}/#{event_type}", query_read_delete(default: filter))[:data][event_type]
580
+ events = analytics_api.read("#{filter_resource}/#{filter_id}/#{event_type}", query_read_delete(default: filter))[event_type]
569
581
  start_date_persistency&.save
570
582
  if !options.get_option(:notify_to).nil?
571
583
  events.each do |tr_event|
@@ -575,7 +587,83 @@ module Aspera
575
587
  return {type: :object_list, data: events}
576
588
  end
577
589
  when :usage_reports
578
- return result_list('usage_reports', base_query: {workspace_id: aoc_api.context(:files)[:workspace_id]})
590
+ aoc_api.context = :files
591
+ return result_list('usage_reports', base_query: {workspace_id: aoc_api.workspace[:id]})
592
+ end
593
+ end
594
+
595
+ # Create a shared link for the given entity
596
+ # @param shared_data [Hash] information for shared data
597
+ # @param block [Proc] Optional: called on creation
598
+ def short_link_command(shared_data, purpose_public:)
599
+ link_type = options.get_next_argument('link type', accept_list: %i[public private])
600
+ purpose_local = case link_type
601
+ when :public
602
+ case purpose_public
603
+ when /package/ then 'send_package_to_dropbox'
604
+ when /shared/ then 'token_auth_redirection'
605
+ else raise 'error'
606
+ end
607
+ when :private then 'shared_folder_auth_link'
608
+ else Aspera.error_unreachable_line
609
+ end
610
+ case options.get_next_command(%i[create delete list])
611
+ when :create
612
+ creation_params = {
613
+ purpose: purpose_local,
614
+ user_selected_name: nil
615
+ }
616
+ case link_type
617
+ when :private
618
+ creation_params[:data] = shared_data
619
+ when :public
620
+ creation_params[:expires_at] = nil
621
+ creation_params[:password_enabled] = false
622
+ shared_data[:name] = ''
623
+ creation_params[:data] = {
624
+ aoc: true,
625
+ url_token_data: {
626
+ data: shared_data,
627
+ purpose: purpose_public
628
+ }
629
+ }
630
+ end
631
+ result_create_short_link = aoc_api.create('short_links', creation_params)
632
+ # public: Creation: permission on node
633
+ yield(result_create_short_link['resource_id']) if block_given? && link_type.eql?(:public)
634
+ return {type: :single_object, data: result_create_short_link}
635
+ when :list
636
+ query = if link_type.eql?(:private)
637
+ shared_data
638
+ else
639
+ {
640
+ url_token_data: {
641
+ data: shared_data,
642
+ purpose: purpose_public
643
+ }
644
+ }
645
+ end
646
+ list_params = {
647
+ json_query: query.to_json,
648
+ purpose: purpose_local,
649
+ edit_access: true,
650
+ # embed: 'updated_by_user',
651
+ sort: '-created_at'
652
+ }
653
+ return result_list('short_links', fields: Formatter.all_but('data'), base_query: list_params)
654
+ when :delete
655
+ one_id = instance_identifier
656
+ shared_data.delete(:workspace_id)
657
+ delete_params = {
658
+ edit_access: true,
659
+ json_query: shared_data.to_json
660
+ }
661
+ aoc_api.delete("short_links/#{one_id}", delete_params)
662
+ if link_type.eql?(:public)
663
+ # TODO: get permission id..
664
+ # shared_apfid[:api].delete('permissions', {ids: })
665
+ end
666
+ return Main.result_status('deleted')
579
667
  end
580
668
  end
581
669
 
@@ -586,10 +674,10 @@ module Aspera
586
674
  command = options.get_next_command(ACTIONS)
587
675
  if %i[files packages].include?(command)
588
676
  default_flag = ' (default)' if options.get_option(:workspace).eql?(:default)
589
- app_context = aoc_api.context(command)
590
- formatter.display_status("Workspace: #{app_context[:workspace_name].to_s.red}#{default_flag}")
677
+ aoc_api.context = command
678
+ formatter.display_status("Workspace: #{aoc_api.workspace[:name].to_s.red}#{default_flag}")
591
679
  if !aoc_api.private_link.nil?
592
- folder_name = aoc_api.node_api_from(node_id: app_context[:home_node_id]).read("files/#{app_context[:home_file_id]}")[:data]['name']
680
+ folder_name = aoc_api.node_api_from(node_id: aoc_api.home[:node_id]).read("files/#{aoc_api.home[:file_id]}")['name']
593
681
  formatter.display_status("Private Folder: #{folder_name}")
594
682
  end
595
683
  end
@@ -597,26 +685,27 @@ module Aspera
597
685
  when :reminder
598
686
  # send an email reminder with list of orgs
599
687
  user_email = options.get_option(:username, mandatory: true)
600
- Rest.new(base_url: "#{Api::AoC.api_base_url}/#{Api::AoC::API_V1}").create('organization_reminders', {email: user_email})[:data]
688
+ Rest.new(base_url: "#{Api::AoC.api_base_url}/#{Api::AoC::API_V1}").create('organization_reminders', {email: user_email})
601
689
  return Main.result_status("List of organizations user is member of, has been sent by e-mail to #{user_email}")
602
690
  when :servers
603
- return {type: :object_list, data: Rest.new(base_url: "#{Api::AoC.api_base_url}/#{Api::AoC::API_V1}").read('servers')[:data]}
691
+ return {type: :object_list, data: Rest.new(base_url: "#{Api::AoC.api_base_url}/#{Api::AoC::API_V1}").read('servers')}
604
692
  when :bearer_token
605
693
  return {type: :text, data: aoc_api.oauth.token}
606
694
  when :organization
607
- return { type: :single_object, data: aoc_api.read('organization')[:data] }
695
+ return { type: :single_object, data: aoc_api.read('organization') }
608
696
  when :tier_restrictions
609
- return { type: :single_object, data: aoc_api.read('tier_restrictions')[:data] }
697
+ return { type: :single_object, data: aoc_api.read('tier_restrictions') }
610
698
  when :user
611
699
  case options.get_next_command(%i[workspaces profile preferences])
612
700
  # when :settings
613
- # return {type: :object_list,data: aoc_api.read('client_settings/')[:data]}
701
+ # return {type: :object_list,data: aoc_api.read('client_settings/')}
614
702
  when :workspaces
615
703
  case options.get_next_command(%i[list current])
616
704
  when :list
617
705
  return result_list('workspaces', fields: %w[id name])
618
706
  when :current
619
- return { type: :single_object, data: aoc_api.read("workspaces/#{aoc_api.context(:files)[:workspace_id]}")[:data] }
707
+ aoc_api.context = :files
708
+ return { type: :single_object, data: aoc_api.read("workspaces/#{aoc_api.workspace[:id]}") }
620
709
  end
621
710
  when :profile
622
711
  case options.get_next_command(%i[show modify])
@@ -630,7 +719,7 @@ module Aspera
630
719
  user_preferences_res = "users/#{aoc_api.current_user_info(exception: true)['id']}/user_interaction_preferences"
631
720
  case options.get_next_command(%i[show modify])
632
721
  when :show
633
- return { type: :single_object, data: aoc_api.read(user_preferences_res)[:data] }
722
+ return { type: :single_object, data: aoc_api.read(user_preferences_res) }
634
723
  when :modify
635
724
  aoc_api.update(user_preferences_res, options.get_next_argument('properties', validation: Hash))
636
725
  return Main.result_status('modified')
@@ -640,20 +729,28 @@ module Aspera
640
729
  package_command = options.get_next_command(%i[shared_inboxes send receive list show delete].concat(Node::NODE4_READ_ACTIONS), aliases: {recv: :receive})
641
730
  case package_command
642
731
  when :shared_inboxes
643
- case options.get_next_command(%i[list show])
732
+ case options.get_next_command(%i[list show short_link])
644
733
  when :list
645
734
  default_query = {'embed[]' => 'dropbox', 'aggregate_permissions_by_dropbox' => true, 'sort' => 'dropbox_name'}
646
- default_query['workspace_id'] = aoc_api.context[:workspace_id] unless aoc_api.context[:workspace_id].eql?(:undefined)
735
+ default_query['workspace_id'] = aoc_api.workspace[:id] unless aoc_api.workspace[:id].eql?(:undefined)
647
736
  return result_list('dropbox_memberships', fields: %w[dropbox_id dropbox.name], default_query: default_query)
648
737
  when :show
649
- return {type: :single_object, data: aoc_api.read(get_resource_path_from_args('dropboxes'), query)[:data]}
738
+ return {type: :single_object, data: aoc_api.read(get_resource_path_from_args('dropboxes'))}
739
+ when :short_link
740
+ return short_link_command(
741
+ {
742
+ workspace_id: aoc_api.workspace[:id],
743
+ dropbox_id: get_resource_id_from_args('dropboxes'),
744
+ name: ''
745
+ },
746
+ purpose_public: 'send_package_to_dropbox')
650
747
  end
651
748
  when :send
652
749
  package_data = value_create_modify(command: package_command)
653
750
  new_user_option = options.get_option(:new_user_option)
654
751
  option_validate = options.get_option(:validate_metadata)
655
752
  # works for both normal usr auth and link auth
656
- package_data['workspace_id'] ||= aoc_api.context[:workspace_id]
753
+ package_data['workspace_id'] ||= aoc_api.workspace[:id]
657
754
 
658
755
  if !aoc_api.public_link.nil?
659
756
  aoc_api.assert_public_link_types(%w[send_package_to_user send_package_to_dropbox])
@@ -687,7 +784,7 @@ module Aspera
687
784
  id: IdGenerator.from_list(
688
785
  ['aoc_recv',
689
786
  options.get_option(:url, mandatory: true),
690
- aoc_api.context[:workspace_id]
787
+ aoc_api.workspace[:id]
691
788
  ].concat(aoc_api.additional_persistence_ids)))
692
789
  end
693
790
  case ids_to_download
@@ -708,22 +805,28 @@ module Aspera
708
805
  else
709
806
  ids_to_download = [ids_to_download] unless ids_to_download.is_a?(Array)
710
807
  end
808
+ file_list =
809
+ begin
810
+ transfer.source_list.map{|i|{'source'=>i}}
811
+ rescue Cli::BadArgument
812
+ [{'source' => '.'}]
813
+ end
711
814
  # list here
712
815
  result_transfer = []
713
816
  formatter.display_status("found #{ids_to_download.length} package(s).")
714
817
  ids_to_download.each do |package_id|
715
- package_info = aoc_api.read("packages/#{package_id}")[:data]
818
+ package_info = aoc_api.read("packages/#{package_id}")
716
819
  formatter.display_status("downloading package: [#{package_info['id']}] #{package_info['name']}")
717
820
  package_node_api = aoc_api.node_api_from(
718
821
  node_id: package_info['node_id'],
719
- workspace_id: aoc_api.context[:workspace_id],
720
- workspace_name: aoc_api.context[:workspace_name],
822
+ workspace_id: aoc_api.workspace[:id],
823
+ workspace_name: aoc_api.workspace[:name],
721
824
  package_info: package_info)
722
825
  statuses = transfer.start(
723
826
  package_node_api.transfer_spec_gen4(
724
827
  package_info['contents_file_id'],
725
828
  Transfer::Spec::DIRECTION_RECEIVE,
726
- {'paths'=> [{'source' => '.'}]}),
829
+ {'paths'=> file_list}),
727
830
  rest_token: package_node_api)
728
831
  result_transfer.push({'package' => package_id, Main::STATUS_FIELD => statuses})
729
832
  # update skip list only if all transfer sessions completed
@@ -735,136 +838,70 @@ module Aspera
735
838
  return Main.result_transfer_multiple(result_transfer)
736
839
  when :show
737
840
  package_id = instance_identifier
738
- package_info = aoc_api.read("packages/#{package_id}")[:data]
841
+ package_info = aoc_api.read("packages/#{package_id}")
739
842
  return { type: :single_object, data: package_info }
740
843
  when :list
741
844
  display_fields = %w[id name bytes_transferred]
742
- display_fields.push('workspace_id') if aoc_api.context[:workspace_id].eql?(:undefined)
845
+ display_fields.push('workspace_id') if aoc_api.workspace[:id].eql?(:undefined)
743
846
  return result_list('packages', fields: display_fields, base_query: PACKAGE_RECEIVED_BASE_QUERY) do |query|
744
847
  resolve_dropbox_name_default_ws_id(query)
745
848
  end
746
849
  when :delete
747
- return do_bulk_operation(command: package_command, descr: 'identifier', values: identifier) do |id|
850
+ return do_bulk_operation(command: package_command, descr: 'identifier', values: instance_identifier) do |id|
748
851
  Aspera.assert_values(id.class, [String, Integer]){'identifier'}
749
- aoc_api.delete("packages/#{id}")[:data]
852
+ aoc_api.delete("packages/#{id}")
750
853
  end
751
854
  when *Node::NODE4_READ_ACTIONS
752
855
  package_id = instance_identifier
753
- package_info = aoc_api.read("packages/#{package_id}")[:data]
754
- return execute_nodegen4_command(package_command, package_info['node_id'], file_id: package_info['file_id'], scope: Api::Node::SCOPE_USER)
856
+ package_info = aoc_api.read("packages/#{package_id}")
857
+ return execute_nodegen4_command(package_command, package_info['node_id'], file_id: package_info['contents_file_id'], scope: Api::Node::SCOPE_USER)
755
858
  end
756
859
  when :files
757
860
  command_repo = options.get_next_command([:short_link].concat(NODE4_EXT_COMMANDS))
758
861
  case command_repo
759
862
  when *NODE4_EXT_COMMANDS
760
- return execute_nodegen4_command(command_repo, aoc_api.context[:home_node_id], file_id: aoc_api.context[:home_file_id], scope: Api::Node::SCOPE_USER)
863
+ return execute_nodegen4_command(command_repo, aoc_api.home[:node_id], file_id: aoc_api.home[:file_id], scope: Api::Node::SCOPE_USER)
761
864
  when :short_link
762
- link_type = options.get_next_argument('link type', accept_list: %i[public private])
763
- short_link_command = options.get_next_command(%i[create delete list])
764
865
  folder_dest = options.get_next_argument('path', validation: String)
765
866
  home_node_api = aoc_api.node_api_from(
766
- node_id: aoc_api.context[:home_node_id],
767
- workspace_id: aoc_api.context[:workspace_id],
768
- workspace_name: aoc_api.context[:workspace_name])
769
- shared_apfid = home_node_api.resolve_api_fid(aoc_api.context[:home_file_id], folder_dest)
770
- folder_info = {
771
- node_id: shared_apfid[:api].app_info[:node_info]['id'],
772
- file_id: shared_apfid[:file_id],
773
- workspace_id: aoc_api.context[:workspace_id]
774
- }
775
- purpose = case link_type
776
- when :public then 'token_auth_redirection'
777
- when :private then 'shared_folder_auth_link'
778
- else Aspera.error_unreachable_line
779
- end
780
- case short_link_command
781
- when :delete
782
- one_id = instance_identifier
783
- folder_info.delete(:workspace_id)
784
- delete_params = {
785
- edit_access: true,
786
- json_query: folder_info.to_json
787
- }
788
- aoc_api.delete("short_links/#{one_id}", delete_params)
789
- if link_type.eql?(:public)
790
- # TODO: get permission id..
791
- # shared_apfid[:api].delete('permissions', {ids: })[:data]
792
- end
793
- return Main.result_status('deleted')
794
- when :list
795
- query = if link_type.eql?(:private)
796
- folder_info
797
- else
798
- {
799
- url_token_data: {
800
- data: folder_info,
801
- purpose: 'view_shared_file'
802
- }
803
- }
804
- end
805
- list_params = {
806
- json_query: query.to_json,
807
- purpose: purpose,
808
- edit_access: true,
809
- # embed: 'updated_by_user',
810
- sort: '-created_at'
811
- }
812
- return result_list('short_links', fields: Formatter.all_but('data'), base_query: list_params)
813
- when :create
814
- creation_params = {
815
- purpose: purpose,
816
- user_selected_name: nil
817
- }
818
- case link_type
819
- when :private
820
- creation_params[:data] = folder_info
821
- when :public
822
- creation_params[:expires_at] = nil
823
- creation_params[:password_enabled] = false
824
- folder_info[:name] = ''
825
- creation_params[:data] = {
826
- aoc: true,
827
- url_token_data: {
828
- data: folder_info,
829
- purpose: 'view_shared_file'
830
- }
831
- }
832
- end
833
- result_create_short_link = aoc_api.create('short_links', creation_params)[:data]
834
- # public: Creation: permission on node
835
- if link_type.eql?(:public)
836
- # TODO: merge with node permissions ?
837
- # TODO: access level as arg
838
- access_levels = Api::Node::ACCESS_LEVELS # ['delete','list','mkdir','preview','read','rename','write']
839
- folder_name = File.basename(folder_dest)
840
- perm_data = {
841
- 'file_id' => shared_apfid[:file_id],
842
- 'access_id' => result_create_short_link['resource_id'],
843
- 'access_type' => 'user',
844
- 'access_levels' => access_levels,
845
- 'tags' => {
846
- 'url_token' => true,
847
- 'workspace_id' => aoc_api.context[:workspace_id],
848
- 'workspace_name' => aoc_api.context[:workspace_name],
849
- 'folder_name' => folder_name,
850
- 'created_by_name' => aoc_api.current_user_info['name'],
851
- 'created_by_email' => aoc_api.current_user_info['email'],
852
- 'access_key' => shared_apfid[:api].app_info[:node_info]['access_key'],
853
- 'node' => shared_apfid[:api].app_info[:node_info]['host']
854
- }
855
- }
856
- created_data = shared_apfid[:api].create('permissions', perm_data)[:data]
857
- aoc_api.permissions_send_event(created_data: created_data, app_info: shared_apfid[:api].app_info)
858
- # TODO: event ?
859
- end
860
- return {type: :single_object, data: result_create_short_link}
861
- end
867
+ node_id: aoc_api.home[:node_id],
868
+ workspace_id: aoc_api.workspace[:id],
869
+ workspace_name: aoc_api.workspace[:name])
870
+ shared_apfid = home_node_api.resolve_api_fid(aoc_api.home[:file_id], folder_dest)
871
+ return short_link_command(
872
+ {
873
+ workspace_id: aoc_api.workspace[:id],
874
+ node_id: shared_apfid[:api].app_info[:node_info]['id'],
875
+ file_id: shared_apfid[:file_id]
876
+ }, purpose_public: 'view_shared_file') do |resource_id|
877
+ # TODO: merge with node permissions ?
878
+ # TODO: access level as arg
879
+ access_levels = Api::Node::ACCESS_LEVELS # ['delete','list','mkdir','preview','read','rename','write']
880
+ perm_data = {
881
+ 'file_id' => shared_apfid[:file_id],
882
+ 'access_id' => resource_id,
883
+ 'access_type' => 'user',
884
+ 'access_levels' => access_levels,
885
+ 'tags' => {
886
+ # TODO: really just here ? not in tags.aspera.files.workspace ?
887
+ 'url_token' => true,
888
+ 'workspace_id' => aoc_api.workspace[:id],
889
+ 'workspace_name' => aoc_api.workspace[:name],
890
+ 'folder_name' => File.basename(folder_dest),
891
+ 'created_by_name' => aoc_api.current_user_info['name'],
892
+ 'created_by_email' => aoc_api.current_user_info['email'],
893
+ 'access_key' => shared_apfid[:api].app_info[:node_info]['access_key'],
894
+ 'node' => shared_apfid[:api].app_info[:node_info]['host']
895
+ }
896
+ }
897
+ created_data = shared_apfid[:api].create('permissions', perm_data)
898
+ aoc_api.permissions_send_event(event_data: created_data, app_info: shared_apfid[:api].app_info)
899
+ end
862
900
  end
863
- raise 'Error: shall not reach this line'
864
901
  when :automation
865
902
  Log.log.warn('BETA: work under progress')
866
903
  # automation api is not in the same place
867
- automation_api = Rest.new(**aoc_api.params.merge(base_url: aoc_api.base_url.gsub('/api/', '/automation/')))
904
+ automation_api = Rest.new(**aoc_api.params, base_url: aoc_api.base_url.gsub('/api/', '/automation/'))
868
905
  command_automation = options.get_next_command(%i[workflows instances])
869
906
  case command_automation
870
907
  when :instances
@@ -876,18 +913,18 @@ module Aspera
876
913
  return entity_command(wf_command, automation_api, 'workflows')
877
914
  when :launch
878
915
  wf_id = instance_identifier
879
- data = automation_api.create("workflows/#{wf_id}/launch", {})[:data]
916
+ data = automation_api.create("workflows/#{wf_id}/launch", {})
880
917
  return {type: :single_object, data: data}
881
918
  when :action
882
919
  # TODO: not complete
883
920
  wf_id = instance_identifier
884
921
  wf_action_cmd = options.get_next_command(%i[list create show])
885
922
  Log.log.warn{"Not implemented: #{wf_action_cmd}"}
886
- step = automation_api.create('steps', {'workflow_id' => wf_id})[:data]
923
+ step = automation_api.create('steps', {'workflow_id' => wf_id})
887
924
  automation_api.update("workflows/#{wf_id}", {'step_order' => [step['id']]})
888
- action = automation_api.create('actions', {'step_id' => step['id'], 'type' => 'manual'})[:data]
925
+ action = automation_api.create('actions', {'step_id' => step['id'], 'type' => 'manual'})
889
926
  automation_api.update("steps/#{step['id']}", {'action_order' => [action['id']]})
890
- wf = automation_api.read("workflows/#{wf_id}")[:data]
927
+ wf = automation_api.read("workflows/#{wf_id}")
891
928
  return {type: :single_object, data: wf}
892
929
  end
893
930
  end
@@ -898,7 +935,8 @@ module Aspera
898
935
  url = value_create_modify(command: command, type: String)
899
936
  uri = URI.parse(url)
900
937
  server = WebServerSimple.new(uri)
901
- server.mount(uri.path, Faspex4GWServlet, aoc_api, aoc_api.context(:files)[:workspace_id])
938
+ aoc_api.context = :files
939
+ server.mount(uri.path, Faspex4GWServlet, aoc_api, aoc_api.workspace[:id])
902
940
  server.start
903
941
  return Main.result_status('Gateway terminated')
904
942
  else Aspera.error_unreachable_line