aspera-cli 4.21.2 → 4.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/BUGS.md +1 -1
  4. data/CHANGELOG.md +34 -16
  5. data/CONTRIBUTING.md +6 -10
  6. data/README.md +805 -574
  7. data/examples/get_proto_file.rb +1 -1
  8. data/lib/aspera/agent/base.rb +9 -5
  9. data/lib/aspera/agent/connect.rb +30 -28
  10. data/lib/aspera/agent/desktop.rb +29 -25
  11. data/lib/aspera/agent/direct.rb +137 -125
  12. data/lib/aspera/agent/httpgw.rb +22 -26
  13. data/lib/aspera/agent/node.rb +14 -11
  14. data/lib/aspera/agent/transferd.rb +6 -2
  15. data/lib/aspera/api/aoc.rb +6 -6
  16. data/lib/aspera/api/cos_node.rb +1 -1
  17. data/lib/aspera/api/httpgw.rb +7 -3
  18. data/lib/aspera/api/node.rb +6 -4
  19. data/lib/aspera/ascmd.rb +3 -3
  20. data/lib/aspera/ascp/installation.rb +15 -16
  21. data/lib/aspera/ascp/management.rb +1 -1
  22. data/lib/aspera/assert.rb +11 -2
  23. data/lib/aspera/cli/error.rb +2 -2
  24. data/lib/aspera/cli/extended_value.rb +38 -19
  25. data/lib/aspera/cli/formatter.rb +48 -48
  26. data/lib/aspera/cli/hints.rb +1 -1
  27. data/lib/aspera/cli/main.rb +190 -168
  28. data/lib/aspera/cli/manager.rb +15 -15
  29. data/lib/aspera/cli/plugin.rb +23 -20
  30. data/lib/aspera/cli/plugin_factory.rb +1 -1
  31. data/lib/aspera/cli/plugins/alee.rb +1 -1
  32. data/lib/aspera/cli/plugins/aoc.rb +144 -107
  33. data/lib/aspera/cli/plugins/ats.rb +19 -17
  34. data/lib/aspera/cli/plugins/config.rb +67 -83
  35. data/lib/aspera/cli/plugins/console.rb +5 -3
  36. data/lib/aspera/cli/plugins/faspex.rb +39 -35
  37. data/lib/aspera/cli/plugins/faspex5.rb +104 -80
  38. data/lib/aspera/cli/plugins/faspio.rb +13 -1
  39. data/lib/aspera/cli/plugins/httpgw.rb +13 -1
  40. data/lib/aspera/cli/plugins/node.rb +306 -179
  41. data/lib/aspera/cli/plugins/orchestrator.rb +34 -40
  42. data/lib/aspera/cli/plugins/preview.rb +3 -3
  43. data/lib/aspera/cli/plugins/server.rb +6 -6
  44. data/lib/aspera/cli/plugins/shares.rb +5 -5
  45. data/lib/aspera/cli/sync_actions.rb +19 -18
  46. data/lib/aspera/cli/transfer_agent.rb +5 -5
  47. data/lib/aspera/cli/transfer_progress.rb +2 -2
  48. data/lib/aspera/cli/version.rb +1 -1
  49. data/lib/aspera/command_line_builder.rb +116 -95
  50. data/lib/aspera/coverage.rb +4 -3
  51. data/lib/aspera/environment.rb +6 -6
  52. data/lib/aspera/faspex_gw.rb +14 -14
  53. data/lib/aspera/faspex_postproc.rb +7 -6
  54. data/lib/aspera/hash_ext.rb +2 -2
  55. data/lib/aspera/json_rpc.rb +1 -1
  56. data/lib/aspera/keychain/encrypted_hash.rb +47 -34
  57. data/lib/aspera/keychain/factory.rb +41 -0
  58. data/lib/aspera/keychain/hashicorp_vault.rb +71 -0
  59. data/lib/aspera/keychain/macos_security.rb +19 -11
  60. data/lib/aspera/log.rb +28 -34
  61. data/lib/aspera/nagios.rb +6 -6
  62. data/lib/aspera/node_simulator.rb +8 -8
  63. data/lib/aspera/oauth/base.rb +8 -6
  64. data/lib/aspera/oauth/factory.rb +5 -6
  65. data/lib/aspera/oauth/url_json.rb +6 -6
  66. data/lib/aspera/persistency_action_once.rb +6 -4
  67. data/lib/aspera/persistency_folder.rb +2 -2
  68. data/lib/aspera/preview/generator.rb +1 -1
  69. data/lib/aspera/preview/options.rb +16 -16
  70. data/lib/aspera/preview/terminal.rb +3 -3
  71. data/lib/aspera/preview/utils.rb +11 -13
  72. data/lib/aspera/products/connect.rb +1 -1
  73. data/lib/aspera/products/desktop.rb +1 -1
  74. data/lib/aspera/products/transferd.rb +1 -1
  75. data/lib/aspera/proxy_auto_config.rb +2 -2
  76. data/lib/aspera/rest.rb +52 -43
  77. data/lib/aspera/rest_errors_aspera.rb +1 -1
  78. data/lib/aspera/secret_hider.rb +5 -5
  79. data/lib/aspera/ssh.rb +4 -4
  80. data/lib/aspera/transfer/convert.rb +29 -0
  81. data/lib/aspera/transfer/error_info.rb +66 -66
  82. data/lib/aspera/transfer/parameters.rb +13 -68
  83. data/lib/aspera/transfer/spec.rb +5 -6
  84. data/lib/aspera/transfer/spec.schema.yaml +753 -0
  85. data/lib/aspera/transfer/spec_doc.rb +62 -0
  86. data/lib/aspera/transfer/sync.rb +23 -72
  87. data/lib/aspera/transfer/sync_instance.schema.yaml +13 -0
  88. data/lib/aspera/transfer/sync_session.schema.yaml +79 -0
  89. data/lib/aspera/transfer/uri.rb +6 -6
  90. data/lib/aspera/uri_reader.rb +1 -1
  91. data/lib/aspera/web_auth.rb +1 -1
  92. data/lib/aspera/web_server_simple.rb +53 -44
  93. data.tar.gz.sig +1 -2
  94. metadata +37 -4
  95. metadata.gz.sig +0 -0
  96. data/examples/build_package.sh +0 -28
  97. data/lib/aspera/transfer/spec.yaml +0 -718
@@ -53,7 +53,9 @@ module Aspera
53
53
  'completed' => true}.freeze
54
54
  # options and parameters for Api::AoC.new
55
55
  OPTIONS_NEW = %i[url auth client_id client_secret scope redirect_uri private_key passphrase username password workspace].freeze
56
- private_constant :REDIRECT_LOCALHOST, :STD_AUTH_TYPES, :ADMIN_OBJECTS, :PACKAGE_RECEIVED_BASE_QUERY, :OPTIONS_NEW
56
+ PACKAGE_LIST_DEFAULT_FIELDS = %w[id name created_at files_completed bytes_transferred].freeze
57
+
58
+ private_constant :REDIRECT_LOCALHOST, :STD_AUTH_TYPES, :ADMIN_OBJECTS, :PACKAGE_RECEIVED_BASE_QUERY, :OPTIONS_NEW, :PACKAGE_LIST_DEFAULT_FIELDS
57
59
  class << self
58
60
  def application_name
59
61
  'Aspera on Cloud'
@@ -78,15 +80,16 @@ module Aspera
78
80
  }
79
81
  end
80
82
 
81
- # @param [String] url : url to check
83
+ # @param url [String] url to check
82
84
  # @return [Bool] true if private key is required for the url (i.e. no passcode)
83
85
  def private_key_required?(url)
84
86
  # pub link do not need private key
85
87
  return Api::AoC.link_info(url)[:token].nil?
86
88
  end
87
89
 
88
- # @param [Hash] env : options, formatter
89
- # @param [Hash] params : plugin_sym, instance_url
90
+ # @param object [Plugin] An instance of this class
91
+ # @param private_key_path [String] path to private key
92
+ # @param pub_key_pem [String] PEM of public key
90
93
  # @return [Hash] :preset_value, :test_args
91
94
  def wizard(object:, private_key_path: nil, pub_key_pem: nil)
92
95
  # set vars to look like object
@@ -121,7 +124,7 @@ module Aspera
121
124
  formatter.display_status('Please Login to your Aspera on Cloud instance.')
122
125
  formatter.display_status('Navigate to: 👤 → Account Settings → Profile → Public Key')
123
126
  formatter.display_status('Check or update the value to:'.red.blink)
124
- formatter.display_status(pub_key_pem)
127
+ formatter.display_status(pub_key_pem, hide_secrets: false)
125
128
  if !options.get_option(:test_mode)
126
129
  formatter.display_status('Once updated or validated, press enter.')
127
130
  Environment.instance.open_uri(instance_url)
@@ -203,7 +206,7 @@ module Aspera
203
206
  def api_from_options(new_base_path)
204
207
  create_values = {subpath: new_base_path, secret_finder: config}
205
208
  # create an API object with the same options, but with a different subpath
206
- return Api::AoC.new(**OPTIONS_NEW.each_with_object(create_values) { |i, m|m[i] = options.get_option(i) unless options.get_option(i).nil?})
209
+ return Api::AoC.new(**OPTIONS_NEW.each_with_object(create_values){ |i, m| m[i] = options.get_option(i) unless options.get_option(i).nil?})
207
210
  rescue ArgumentError => e
208
211
  if (m = e.message.match(/missing keyword: :(.*)$/))
209
212
  raise Cli::Error, "Missing option: #{m[1]}"
@@ -216,13 +219,31 @@ module Aspera
216
219
  @cache_api_aoc = api_from_options(Api::AoC::API_V1)
217
220
  organization = @cache_api_aoc.read('organization')
218
221
  if organization['http_gateway_enabled'] && organization['http_gateway_server_url']
219
- transfer.httpgw_url_cb = lambda { organization['http_gateway_server_url'] }
222
+ transfer.httpgw_url_cb = lambda{organization['http_gateway_server_url']}
220
223
  # @cache_api_aoc.current_user_info['connect_disabled']
221
224
  end
222
225
  end
223
226
  return @cache_api_aoc
224
227
  end
225
228
 
229
+ # Generate or update Hash with workspace id and name (option), if not already set
230
+ # @param hash [Hash, Nil] set in provided hash
231
+ # @param string [Bool] true to set key as string, else as symbol
232
+ # @param name [Bool] include name
233
+ # @return [Hash] with key `workspace_[id,name]` (symbol or string) only if defined
234
+ def workspace_id_hash(hash: nil, string: false, name: false)
235
+ info = aoc_api.workspace
236
+ hash = {} if hash.nil?
237
+ fields = %i[id]
238
+ fields.push(:name) if name
239
+ fields.each do |i|
240
+ k = "workspace_#{i}"
241
+ k = k.to_sym unless string
242
+ hash[k] = info[i] unless info[i].nil? || hash.key?(k)
243
+ end
244
+ return hash
245
+ end
246
+
226
247
  # Get resource identifier from command line, either directly or from name.
227
248
  # @param resource_class_path url path for resource
228
249
  # @return identifier
@@ -240,7 +261,7 @@ module Aspera
240
261
 
241
262
  # Call block with same query using paging and response information
242
263
  # block must return a hash with :data and :http keys
243
- # @return [Hash] {data: , total: }
264
+ # @return [Hash] {items: , total: }
244
265
  def api_call_paging(base_query={})
245
266
  Aspera.assert_type(base_query, Hash){'query'}
246
267
  Aspera.assert(block_given?)
@@ -268,46 +289,59 @@ module Aspera
268
289
  item_list += add_items
269
290
  break if !max_items.nil? && item_list.count >= max_items
270
291
  break if !max_pages.nil? && page_count >= max_pages
292
+ formatter.long_operation_running("#{item_list.count} / #{total_count}") unless total_count.eql?(item_list.count.to_s)
271
293
  end
294
+ formatter.long_operation_terminated
272
295
  item_list = item_list[0..max_items - 1] if !max_items.nil? && item_list.count > max_items
273
- return {data: item_list, total: total_count}
296
+ return {items: item_list, total: total_count}
274
297
  end
275
298
 
276
299
  # read using the query and paging
277
300
  # @return [Hash] {data: , total: }
278
301
  def api_read_all(resource_class_path, base_query={})
279
302
  return api_call_paging(base_query) do |query|
280
- aoc_api.call(operation: 'GET', subpath: resource_class_path, headers: {'Accept' => 'application/json'}, query: query)
303
+ aoc_api.call(operation: 'GET', subpath: resource_class_path, headers: {'Accept' => Rest::MIME_JSON}, query: query)
281
304
  end
282
305
  end
283
306
 
284
- # list all entities, given additional, default and user's queries
307
+ # List all entities, given additional, default and user's queries
285
308
  # @param resource_class_path path to query on API
286
309
  # @param fields fields to display
287
310
  # @param base_query a query applied always
288
311
  # @param default_query default query unless overriden by user
312
+ # @param &block (Optional) calls block with user's or default query
289
313
  def result_list(resource_class_path, fields: nil, base_query: {}, default_query: {})
290
314
  Aspera.assert_type(base_query, Hash)
291
315
  Aspera.assert_type(default_query, Hash)
292
- user_query = query_read_delete(default: default_query)
293
- # caller may add specific modifications or checks
294
- yield(user_query) if block_given?
295
- return {type: :object_list, fields: fields}.merge(api_read_all(resource_class_path, base_query.merge(user_query).compact))
316
+ query = query_read_delete(default: default_query)
317
+ # caller may add specific modifications or checks to query
318
+ yield(query) if block_given?
319
+ result = api_read_all(resource_class_path, base_query.merge(query).compact)
320
+ return Main.result_object_list(result[:items], fields: fields, total: result[:total])
296
321
  end
297
322
 
323
+ # Translates `dropbox_name` to `dropbox_id` and fills current workspace_id
298
324
  def resolve_dropbox_name_default_ws_id(query)
299
325
  if query.key?('dropbox_name')
300
326
  # convenience: specify name instead of id
301
- raise 'not both dropbox_name and dropbox_id' if query.key?('dropbox_id')
327
+ raise 'Use field dropbox_name or dropbox_id, not both' if query.key?('dropbox_id')
302
328
  # TODO : craft a query that looks for dropbox only in current workspace
303
- query['dropbox_id'] = aoc_api.lookup_by_name('dropboxes', query['dropbox_name'])['id']
304
- query.delete('dropbox_name')
329
+ query['dropbox_id'] = aoc_api.lookup_by_name('dropboxes', query.delete('dropbox_name'))['id']
305
330
  end
306
- query['workspace_id'] ||= aoc_api.workspace[:id] unless aoc_api.workspace[:id].eql?(:undefined)
331
+ workspace_id_hash(hash: query, string: true)
307
332
  # by default show dropbox packages only for dropboxes
308
333
  query['exclude_dropbox_packages'] = !query.key?('dropbox_id') unless query.key?('exclude_dropbox_packages')
309
334
  end
310
335
 
336
+ # @return [Hash] {items,total} with all packages according to combination of user's query and default query
337
+ def list_all_packages_with_query
338
+ query = query_read_delete(default: {})
339
+ Aspera.assert_type(query, Hash){'query'}
340
+ PACKAGE_RECEIVED_BASE_QUERY.each{ |k, v| query[k] = v unless query.key?(k)}
341
+ resolve_dropbox_name_default_ws_id(query)
342
+ return api_read_all('packages', query.compact)
343
+ end
344
+
311
345
  NODE4_EXT_COMMANDS = %i[transfer].concat(Node::COMMANDS_GEN4).freeze
312
346
  private_constant :NODE4_EXT_COMMANDS
313
347
 
@@ -316,9 +350,8 @@ module Aspera
316
350
  def execute_nodegen4_command(command_repo, node_id, file_id: nil, scope: nil)
317
351
  top_node_api = aoc_api.node_api_from(
318
352
  node_id: node_id,
319
- workspace_id: aoc_api.workspace[:id],
320
- workspace_name: aoc_api.workspace[:name],
321
- scope: scope
353
+ scope: scope,
354
+ **workspace_id_hash(name: true)
322
355
  )
323
356
  file_id = top_node_api.read("access_keys/#{top_node_api.app_info[:node_info]['access_key']}")['root_file_id'] if file_id.nil?
324
357
  node_plugin = Node.new(**init_params, api: top_node_api)
@@ -422,8 +455,8 @@ module Aspera
422
455
  when :show
423
456
  object = aoc_api.read(resource_instance_path)
424
457
  # default: show all, but certificate
425
- fields = object.keys.reject{|k|k.eql?('certificate')}
426
- return { type: :single_object, data: object, fields: fields }
458
+ fields = object.keys.reject{ |k| k.eql?('certificate')}
459
+ return Main.result_single_object(object, fields: fields)
427
460
  when :modify
428
461
  changes = options.get_next_argument('properties', validation: Hash)
429
462
  return do_bulk_operation(command: command, descr: 'identifier', values: res_id) do |one_id|
@@ -529,7 +562,7 @@ module Aspera
529
562
  GRAPHQL
530
563
  # cspell:enable
531
564
  result = bss_graphql.create(nil, {query: graphql_query, variables: {organization_id: org['id']}})['data']
532
- return {type: :single_object, data: result['aoc']['bssSubscription']}
565
+ return Main.result_single_object(result['aoc']['bssSubscription'])
533
566
  when :usage
534
567
  # cspell:disable
535
568
  graphql_query = <<-GRAPHQL
@@ -576,7 +609,7 @@ module Aspera
576
609
  aggregate: aggregate,
577
610
  startDate: start_date,
578
611
  endDate: end_date}})['data']
579
- return {type: :single_object, data: result['aoc']}
612
+ return Main.result_single_object(result['aoc'])
580
613
  end
581
614
  when :ats
582
615
  ats_api = Rest.new(**aoc_api.params.deep_merge({
@@ -594,7 +627,7 @@ module Aspera
594
627
  when :application_events
595
628
  event_type = command_analytics.to_s
596
629
  events = analytics_api.read("organizations/#{aoc_api.current_user_info['organization_id']}/#{event_type}")[event_type]
597
- return {type: :object_list, data: events}
630
+ return Main.result_object_list(events)
598
631
  when :transfers
599
632
  event_type = command_analytics.to_s
600
633
  filter_resource = options.get_next_argument('resource', accept_list: %i[organizations users nodes])
@@ -635,25 +668,26 @@ module Aspera
635
668
  config.send_email_template(values: {ev: tr_event})
636
669
  end
637
670
  end
638
- return {type: :object_list, data: events}
671
+ return Main.result_object_list(events)
639
672
  end
640
673
  when :usage_reports
641
674
  aoc_api.context = :files
642
- return result_list('usage_reports', base_query: {workspace_id: aoc_api.workspace[:id]})
675
+ return result_list('usage_reports', base_query: workspace_id_hash)
643
676
  end
644
677
  end
645
678
 
646
679
  # Create a shared link for the given entity
647
- # @param shared_data [Hash] information for shared data
648
- # @param block [Proc] Optional: called on creation
649
- def short_link_command(shared_data, purpose_public:)
680
+ # @param purpose_public [Symbol]
681
+ # @param shared_data [Hash] information for shared data
682
+ # @param block [Proc] Optional: called on creation
683
+ def short_link_command(purpose_public:, **shared_data)
650
684
  link_type = options.get_next_argument('link type', accept_list: %i[public private])
651
685
  purpose_local = case link_type
652
686
  when :public
653
687
  case purpose_public
654
688
  when /package/ then 'send_package_to_dropbox'
655
689
  when /shared/ then 'token_auth_redirection'
656
- else raise 'error'
690
+ else Aspera.error_unexpected_value(purpose_public){'public link purpose'}
657
691
  end
658
692
  when :private then 'shared_folder_auth_link'
659
693
  else Aspera.error_unreachable_line
@@ -682,7 +716,7 @@ module Aspera
682
716
  result_create_short_link = aoc_api.create('short_links', creation_params)
683
717
  # public: Creation: permission on node
684
718
  yield(result_create_short_link['resource_id']) if block_given? && link_type.eql?(:public)
685
- return {type: :single_object, data: result_create_short_link}
719
+ return Main.result_single_object(result_create_short_link)
686
720
  when :list
687
721
  query = if link_type.eql?(:private)
688
722
  shared_data
@@ -718,6 +752,26 @@ module Aspera
718
752
  end
719
753
  end
720
754
 
755
+ # @return persistency object if option `once_only` is used.
756
+ def package_persistency
757
+ return nil unless options.get_option(:once_only, mandatory: true)
758
+ # TODO: add query info to id
759
+ PersistencyActionOnce.new(
760
+ manager: persistency,
761
+ data: [],
762
+ id: IdGenerator.from_list(
763
+ ['aoc_recv',
764
+ options.get_option(:url, mandatory: true),
765
+ aoc_api.workspace[:id]
766
+ ].concat(aoc_api.additional_persistence_ids)))
767
+ end
768
+
769
+ def reject_packages_from_persistency(all_packages, skip_ids_persistency)
770
+ return if skip_ids_persistency.nil?
771
+ skip_package = skip_ids_persistency.data.each_with_object({}){ |i, m| m[i] = true}
772
+ all_packages.reject!{ |pkg| skip_package[pkg['id']]}
773
+ end
774
+
721
775
  # must be public
722
776
  ACTIONS = %i[reminder servers bearer_token organization tier_restrictions user packages files admin automation gateway].freeze
723
777
 
@@ -739,29 +793,29 @@ module Aspera
739
793
  Rest.new(base_url: "#{Api::AoC.api_base_url}/#{Api::AoC::API_V1}").create('organization_reminders', {email: user_email})
740
794
  return Main.result_status("List of organizations user is member of, has been sent by e-mail to #{user_email}")
741
795
  when :servers
742
- return {type: :object_list, data: Rest.new(base_url: "#{Api::AoC.api_base_url}/#{Api::AoC::API_V1}").read('servers')}
796
+ return Main.result_object_list(Rest.new(base_url: "#{Api::AoC.api_base_url}/#{Api::AoC::API_V1}").read('servers'))
743
797
  when :bearer_token
744
- return {type: :text, data: aoc_api.oauth.authorization}
798
+ return Main.result_text(aoc_api.oauth.authorization)
745
799
  when :organization
746
- return { type: :single_object, data: aoc_api.read('organization') }
800
+ return Main.result_single_object(aoc_api.read('organization'))
747
801
  when :tier_restrictions
748
- return { type: :single_object, data: aoc_api.read('tier_restrictions') }
802
+ return Main.result_single_object(aoc_api.read('tier_restrictions'))
749
803
  when :user
750
804
  case options.get_next_command(%i[workspaces profile preferences])
751
805
  # when :settings
752
- # return {type: :object_list,data: aoc_api.read('client_settings/')}
806
+ # return Main.result_object_list(aoc_api.read('client_settings/'))
753
807
  when :workspaces
754
808
  case options.get_next_command(%i[list current])
755
809
  when :list
756
810
  return result_list('workspaces', fields: %w[id name])
757
811
  when :current
758
812
  aoc_api.context = :files
759
- return { type: :single_object, data: aoc_api.read("workspaces/#{aoc_api.workspace[:id]}") }
813
+ return Main.result_single_object(aoc_api.workspace)
760
814
  end
761
815
  when :profile
762
816
  case options.get_next_command(%i[show modify])
763
817
  when :show
764
- return { type: :single_object, data: aoc_api.current_user_info(exception: true) }
818
+ return Main.result_single_object(aoc_api.current_user_info(exception: true))
765
819
  when :modify
766
820
  aoc_api.update("users/#{aoc_api.current_user_info(exception: true)['id']}", options.get_next_argument('properties', validation: Hash))
767
821
  return Main.result_status('modified')
@@ -770,7 +824,7 @@ module Aspera
770
824
  user_preferences_res = "users/#{aoc_api.current_user_info(exception: true)['id']}/user_interaction_preferences"
771
825
  case options.get_next_command(%i[show modify])
772
826
  when :show
773
- return { type: :single_object, data: aoc_api.read(user_preferences_res) }
827
+ return Main.result_single_object(aoc_api.read(user_preferences_res))
774
828
  when :modify
775
829
  aoc_api.update(user_preferences_res, options.get_next_argument('properties', validation: Hash))
776
830
  return Main.result_status('modified')
@@ -783,26 +837,24 @@ module Aspera
783
837
  case options.get_next_command(%i[list show short_link])
784
838
  when :list
785
839
  default_query = {'embed[]' => 'dropbox', 'aggregate_permissions_by_dropbox' => true, 'sort' => 'dropbox_name'}
786
- default_query['workspace_id'] = aoc_api.workspace[:id] unless aoc_api.workspace[:id].eql?(:undefined)
840
+ workspace_id_hash(hash: default_query, string: true)
787
841
  return result_list('dropbox_memberships', fields: %w[dropbox_id dropbox.name], default_query: default_query)
788
842
  when :show
789
- return {type: :single_object, data: aoc_api.read(get_resource_path_from_args('dropboxes'))}
843
+ return Main.result_single_object(aoc_api.read(get_resource_path_from_args('dropboxes')))
790
844
  when :short_link
791
845
  return short_link_command(
792
- {
793
- workspace_id: aoc_api.workspace[:id],
794
- dropbox_id: get_resource_id_from_args('dropboxes'),
795
- name: ''
796
- },
797
- purpose_public: 'send_package_to_dropbox')
846
+ purpose_public: 'send_package_to_dropbox',
847
+ dropbox_id: get_resource_id_from_args('dropboxes'),
848
+ name: '',
849
+ **workspace_id_hash
850
+ )
798
851
  end
799
852
  when :send
800
853
  package_data = value_create_modify(command: package_command)
801
854
  new_user_option = options.get_option(:new_user_option)
802
855
  option_validate = options.get_option(:validate_metadata)
803
- # works for both normal usr auth and link auth
804
- package_data['workspace_id'] ||= aoc_api.workspace[:id]
805
-
856
+ # works for both normal user auth and link auth
857
+ workspace_id_hash(hash: package_data, string: true) unless package_data.key?('workspace_id')
806
858
  if !aoc_api.public_link.nil?
807
859
  aoc_api.assert_public_link_types(%w[send_package_to_user send_package_to_dropbox])
808
860
  box_type = aoc_api.public_link['purpose'].split('_').last
@@ -815,64 +867,49 @@ module Aspera
815
867
  created_package = aoc_api.create_package_simple(package_data, option_validate, new_user_option)
816
868
  Main.result_transfer(transfer.start(created_package[:spec], rest_token: created_package[:node]))
817
869
  # return all info on package (especially package id)
818
- return { type: :single_object, data: created_package[:info]}
870
+ return Main.result_single_object(created_package[:info])
819
871
  when :receive
820
872
  ids_to_download = nil
821
873
  if !aoc_api.public_link.nil?
822
874
  aoc_api.assert_public_link_types(['view_received_package'])
823
- # set the package id, it will
875
+ # set the package id from link
824
876
  ids_to_download = aoc_api.public_link['data']['package_id']
825
877
  end
826
878
  # get from command line unless it was a public link
827
879
  ids_to_download ||= instance_identifier
828
- skip_ids_data = []
829
- skip_ids_persistency = nil
830
- if options.get_option(:once_only, mandatory: true)
831
- # TODO: add query info to id
832
- skip_ids_persistency = PersistencyActionOnce.new(
833
- manager: persistency,
834
- data: skip_ids_data,
835
- id: IdGenerator.from_list(
836
- ['aoc_recv',
837
- options.get_option(:url, mandatory: true),
838
- aoc_api.workspace[:id]
839
- ].concat(aoc_api.additional_persistence_ids)))
840
- end
880
+ skip_ids_persistency = package_persistency
841
881
  case ids_to_download
842
882
  when SpecialValues::ALL, SpecialValues::INIT
843
- query = query_read_delete(default: PACKAGE_RECEIVED_BASE_QUERY)
844
- Aspera.assert_type(query, Hash){'query'}
845
- resolve_dropbox_name_default_ws_id(query)
846
- # remove from list the ones already downloaded
847
- all_ids = api_read_all('packages', query)[:data].map{|e|e['id']}
883
+ all_packages = list_all_packages_with_query[:items]
848
884
  if ids_to_download.eql?(SpecialValues::INIT)
849
- Aspera.assert(skip_ids_persistency){'Only with option once_only'}
850
- skip_ids_persistency.data.clear.concat(all_ids)
885
+ Aspera.assert(skip_ids_persistency){'INIT requires option once_only'}
886
+ skip_ids_persistency.data.clear.concat(all_packages.map{ |e| e['id']})
851
887
  skip_ids_persistency.save
852
888
  return Main.result_status("Initialized skip for #{skip_ids_persistency.data.count} package(s)")
853
889
  end
854
- # array here
855
- ids_to_download = all_ids.reject{|id|skip_ids_data.include?(id)}
890
+ # remove from list the ones already downloaded
891
+ reject_packages_from_persistency(all_packages, skip_ids_persistency)
892
+ ids_to_download = all_packages.map{ |e| e['id']}
856
893
  else
894
+ # single id to array
857
895
  ids_to_download = [ids_to_download] unless ids_to_download.is_a?(Array)
858
896
  end
859
897
  file_list =
860
898
  begin
861
- transfer.source_list.map{|i|{'source'=>i}}
899
+ transfer.source_list.map{ |i| {'source'=>i}}
862
900
  rescue Cli::BadArgument
863
901
  [{'source' => '.'}]
864
902
  end
865
903
  # list here
866
904
  result_transfer = []
867
- formatter.display_status("found #{ids_to_download.length} package(s).")
905
+ formatter.display_status("Found #{ids_to_download.length} package(s).")
868
906
  ids_to_download.each do |package_id|
869
907
  package_info = aoc_api.read("packages/#{package_id}")
870
908
  formatter.display_status("downloading package: [#{package_info['id']}] #{package_info['name']}")
871
909
  package_node_api = aoc_api.node_api_from(
872
910
  node_id: package_info['node_id'],
873
- workspace_id: aoc_api.workspace[:id],
874
- workspace_name: aoc_api.workspace[:name],
875
- package_info: package_info)
911
+ package_info: package_info,
912
+ **workspace_id_hash(name: true))
876
913
  statuses = transfer.start(
877
914
  package_node_api.transfer_spec_gen4(
878
915
  package_info['contents_file_id'],
@@ -881,22 +918,23 @@ module Aspera
881
918
  rest_token: package_node_api)
882
919
  result_transfer.push({'package' => package_id, Main::STATUS_FIELD => statuses})
883
920
  # update skip list only if all transfer sessions completed
884
- if TransferAgent.session_status(statuses).eql?(:success)
885
- skip_ids_data.push(package_id)
886
- skip_ids_persistency&.save
921
+ if skip_ids_persistency && TransferAgent.session_status(statuses).eql?(:success)
922
+ skip_ids_persistency.data.push(package_id)
923
+ skip_ids_persistency.save
887
924
  end
888
925
  end
889
926
  return Main.result_transfer_multiple(result_transfer)
890
927
  when :show
891
928
  package_id = instance_identifier
892
929
  package_info = aoc_api.read("packages/#{package_id}")
893
- return { type: :single_object, data: package_info }
930
+ return Main.result_single_object(package_info)
894
931
  when :list
895
- display_fields = %w[id name bytes_transferred]
896
- display_fields.push('workspace_id') if aoc_api.workspace[:id].eql?(:undefined)
897
- return result_list('packages', fields: display_fields, base_query: PACKAGE_RECEIVED_BASE_QUERY) do |query|
898
- resolve_dropbox_name_default_ws_id(query)
899
- end
932
+ result = list_all_packages_with_query
933
+ skip_ids_persistency = package_persistency
934
+ reject_packages_from_persistency(result[:items], skip_ids_persistency)
935
+ display_fields = PACKAGE_LIST_DEFAULT_FIELDS
936
+ display_fields += ['workspace_id'] if aoc_api.workspace[:id].nil?
937
+ return Main.result_object_list(result[:items], fields: display_fields, total: result[:total])
900
938
  when :delete
901
939
  return do_bulk_operation(command: package_command, descr: 'identifier', values: instance_identifier) do |id|
902
940
  Aspera.assert_values(id.class, [String, Integer]){'identifier'}
@@ -915,16 +953,15 @@ module Aspera
915
953
  when :short_link
916
954
  folder_dest = options.get_next_argument('path', validation: String)
917
955
  home_node_api = aoc_api.node_api_from(
918
- node_id: aoc_api.home[:node_id],
919
- workspace_id: aoc_api.workspace[:id],
920
- workspace_name: aoc_api.workspace[:name])
956
+ node_id: aoc_api.home[:node_id],
957
+ **workspace_id_hash(name: true))
921
958
  shared_apfid = home_node_api.resolve_api_fid(aoc_api.home[:file_id], folder_dest)
922
959
  return short_link_command(
923
- {
924
- workspace_id: aoc_api.workspace[:id],
925
- node_id: shared_apfid[:api].app_info[:node_info]['id'],
926
- file_id: shared_apfid[:file_id]
927
- }, purpose_public: 'view_shared_file') do |resource_id|
960
+ purpose_public: 'view_shared_file',
961
+ node_id: shared_apfid[:api].app_info[:node_info]['id'],
962
+ file_id: shared_apfid[:file_id],
963
+ **workspace_id_hash
964
+ ) do |resource_id|
928
965
  # TODO: merge with node permissions ?
929
966
  # TODO: access level as arg
930
967
  access_levels = Api::Node::ACCESS_LEVELS # ['delete','list','mkdir','preview','read','rename','write']
@@ -936,13 +973,12 @@ module Aspera
936
973
  'tags' => {
937
974
  # TODO: really just here ? not in tags.aspera.files.workspace ?
938
975
  'url_token' => true,
939
- 'workspace_id' => aoc_api.workspace[:id],
940
- 'workspace_name' => aoc_api.workspace[:name],
941
976
  'folder_name' => File.basename(folder_dest),
942
977
  'created_by_name' => aoc_api.current_user_info['name'],
943
978
  'created_by_email' => aoc_api.current_user_info['email'],
944
979
  'access_key' => shared_apfid[:api].app_info[:node_info]['access_key'],
945
- 'node' => shared_apfid[:api].app_info[:node_info]['host']
980
+ 'node' => shared_apfid[:api].app_info[:node_info]['host'],
981
+ **workspace_id_hash(string: true, name: true)
946
982
  }
947
983
  }
948
984
  created_data = shared_apfid[:api].create('permissions', perm_data)
@@ -965,7 +1001,7 @@ module Aspera
965
1001
  when :launch
966
1002
  wf_id = instance_identifier
967
1003
  data = automation_api.create("workflows/#{wf_id}/launch", {})
968
- return {type: :single_object, data: data}
1004
+ return Main.result_single_object(data)
969
1005
  when :action
970
1006
  # TODO: not complete
971
1007
  wf_id = instance_identifier
@@ -976,16 +1012,17 @@ module Aspera
976
1012
  action = automation_api.create('actions', {'step_id' => step['id'], 'type' => 'manual'})
977
1013
  automation_api.update("steps/#{step['id']}", {'action_order' => [action['id']]})
978
1014
  wf = automation_api.read("workflows/#{wf_id}")
979
- return {type: :single_object, data: wf}
1015
+ return Main.result_single_object(wf)
980
1016
  end
981
1017
  end
982
1018
  when :admin
983
1019
  return execute_admin_action
984
1020
  when :gateway
985
1021
  require 'aspera/faspex_gw'
986
- url = value_create_modify(command: command, type: String)
987
- uri = URI.parse(url)
988
- server = WebServerSimple.new(uri)
1022
+ parameters = value_create_modify(command: command, default: {}).symbolize_keys
1023
+ uri = URI.parse(parameters.delete(:url){WebServerSimple::DEFAULT_URL})
1024
+ server = WebServerSimple.new(uri, **parameters.slice(*WebServerSimple::PARAMS))
1025
+ Aspera.assert(parameters.except(*WebServerSimple::PARAMS).empty?)
989
1026
  aoc_api.context = :files
990
1027
  server.mount(uri.path, Faspex4GWServlet, aoc_api, aoc_api.workspace[:id])
991
1028
  server.start