aspera-cli 4.15.0 → 4.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/BUGS.md +29 -3
  4. data/CHANGELOG.md +375 -280
  5. data/CONTRIBUTING.md +71 -18
  6. data/README.md +1978 -1656
  7. data/bin/ascli +13 -31
  8. data/bin/asession +32 -22
  9. data/examples/dascli +2 -2
  10. data/lib/aspera/agent/alpha.rb +117 -0
  11. data/lib/aspera/agent/base.rb +61 -0
  12. data/lib/aspera/{fasp/agent_connect.rb → agent/connect.rb} +13 -11
  13. data/lib/aspera/{fasp/agent_direct.rb → agent/direct.rb} +116 -116
  14. data/lib/aspera/{fasp/agent_httpgw.rb → agent/httpgw.rb} +21 -19
  15. data/lib/aspera/{fasp/agent_node.rb → agent/node.rb} +21 -33
  16. data/lib/aspera/agent/trsdk.rb +188 -0
  17. data/lib/aspera/api/aoc.rb +586 -0
  18. data/lib/aspera/api/ats.rb +46 -0
  19. data/lib/aspera/api/cos_node.rb +95 -0
  20. data/lib/aspera/api/node.rb +344 -0
  21. data/lib/aspera/ascmd.rb +47 -14
  22. data/lib/aspera/{fasp → ascp}/installation.rb +54 -15
  23. data/lib/aspera/{fasp → ascp}/management.rb +14 -14
  24. data/lib/aspera/{fasp → ascp}/products.rb +1 -1
  25. data/lib/aspera/assert.rb +45 -0
  26. data/lib/aspera/cli/basic_auth_plugin.rb +11 -10
  27. data/lib/aspera/cli/extended_value.rb +5 -5
  28. data/lib/aspera/cli/formatter.rb +27 -14
  29. data/lib/aspera/cli/hints.rb +7 -6
  30. data/lib/aspera/cli/main.rb +49 -29
  31. data/lib/aspera/cli/manager.rb +46 -36
  32. data/lib/aspera/cli/plugin.rb +34 -20
  33. data/lib/aspera/cli/plugin_factory.rb +61 -0
  34. data/lib/aspera/cli/plugins/alee.rb +7 -7
  35. data/lib/aspera/cli/plugins/aoc.rb +168 -132
  36. data/lib/aspera/cli/plugins/ats.rb +33 -33
  37. data/lib/aspera/cli/plugins/bss.rb +3 -4
  38. data/lib/aspera/cli/plugins/config.rb +250 -272
  39. data/lib/aspera/cli/plugins/console.rb +8 -6
  40. data/lib/aspera/cli/plugins/cos.rb +20 -19
  41. data/lib/aspera/cli/plugins/faspex.rb +71 -60
  42. data/lib/aspera/cli/plugins/faspex5.rb +212 -133
  43. data/lib/aspera/cli/plugins/node.rb +83 -75
  44. data/lib/aspera/cli/plugins/orchestrator.rb +36 -44
  45. data/lib/aspera/cli/plugins/preview.rb +33 -31
  46. data/lib/aspera/cli/plugins/server.rb +33 -32
  47. data/lib/aspera/cli/plugins/shares.rb +39 -33
  48. data/lib/aspera/cli/sync_actions.rb +9 -9
  49. data/lib/aspera/cli/transfer_agent.rb +45 -25
  50. data/lib/aspera/cli/transfer_progress.rb +2 -3
  51. data/lib/aspera/cli/version.rb +1 -1
  52. data/lib/aspera/colors.rb +5 -0
  53. data/lib/aspera/command_line_builder.rb +16 -14
  54. data/lib/aspera/coverage.rb +21 -0
  55. data/lib/aspera/data_repository.rb +33 -2
  56. data/lib/aspera/environment.rb +5 -4
  57. data/lib/aspera/faspex_gw.rb +13 -11
  58. data/lib/aspera/faspex_postproc.rb +6 -5
  59. data/lib/aspera/id_generator.rb +4 -2
  60. data/lib/aspera/json_rpc.rb +10 -8
  61. data/lib/aspera/keychain/encrypted_hash.rb +46 -11
  62. data/lib/aspera/keychain/macos_security.rb +29 -22
  63. data/lib/aspera/log.rb +5 -4
  64. data/lib/aspera/nagios.rb +7 -2
  65. data/lib/aspera/node_simulator.rb +213 -0
  66. data/lib/aspera/oauth/base.rb +143 -0
  67. data/lib/aspera/oauth/factory.rb +124 -0
  68. data/lib/aspera/oauth/generic.rb +34 -0
  69. data/lib/aspera/oauth/jwt.rb +51 -0
  70. data/lib/aspera/oauth/url_json.rb +31 -0
  71. data/lib/aspera/oauth/web.rb +50 -0
  72. data/lib/aspera/oauth.rb +5 -328
  73. data/lib/aspera/open_application.rb +7 -7
  74. data/lib/aspera/persistency_action_once.rb +13 -14
  75. data/lib/aspera/persistency_folder.rb +3 -2
  76. data/lib/aspera/preview/file_types.rb +53 -267
  77. data/lib/aspera/preview/generator.rb +7 -5
  78. data/lib/aspera/preview/terminal.rb +17 -7
  79. data/lib/aspera/preview/utils.rb +8 -7
  80. data/lib/aspera/proxy_auto_config.rb +6 -3
  81. data/lib/aspera/rest.rb +187 -140
  82. data/lib/aspera/rest_error_analyzer.rb +1 -0
  83. data/lib/aspera/rest_errors_aspera.rb +5 -3
  84. data/lib/aspera/resumer.rb +77 -0
  85. data/lib/aspera/secret_hider.rb +5 -2
  86. data/lib/aspera/ssh.rb +15 -8
  87. data/lib/aspera/temp_file_manager.rb +1 -1
  88. data/lib/aspera/{fasp → transfer}/error.rb +3 -3
  89. data/lib/aspera/{fasp → transfer}/error_info.rb +1 -1
  90. data/lib/aspera/{fasp → transfer}/faux_file.rb +1 -1
  91. data/lib/aspera/{fasp → transfer}/parameters.rb +95 -120
  92. data/lib/aspera/{fasp/transfer_spec.rb → transfer/spec.rb} +23 -19
  93. data/lib/aspera/{fasp/parameters.yaml → transfer/spec.yaml} +4 -99
  94. data/lib/aspera/transfer/sync.rb +273 -0
  95. data/lib/aspera/{fasp → transfer}/uri.rb +10 -9
  96. data/lib/aspera/web_server_simple.rb +12 -3
  97. data.tar.gz.sig +0 -0
  98. metadata +92 -68
  99. metadata.gz.sig +0 -0
  100. data/lib/aspera/aoc.rb +0 -606
  101. data/lib/aspera/ats_api.rb +0 -47
  102. data/lib/aspera/cos_node.rb +0 -93
  103. data/lib/aspera/fasp/agent_aspera.rb +0 -126
  104. data/lib/aspera/fasp/agent_base.rb +0 -48
  105. data/lib/aspera/fasp/agent_trsdk.rb +0 -146
  106. data/lib/aspera/fasp/resume_policy.rb +0 -77
  107. data/lib/aspera/node.rb +0 -338
  108. data/lib/aspera/sync.rb +0 -219
@@ -4,23 +4,26 @@ require 'aspera/cli/plugins/node'
4
4
  require 'aspera/cli/plugins/ats'
5
5
  require 'aspera/cli/basic_auth_plugin'
6
6
  require 'aspera/cli/transfer_agent'
7
- require 'aspera/fasp/agent_node'
8
- require 'aspera/fasp/transfer_spec'
9
- require 'aspera/aoc'
10
- require 'aspera/node'
7
+ require 'aspera/agent/node'
8
+ require 'aspera/transfer/spec'
9
+ require 'aspera/api/aoc'
10
+ require 'aspera/api/node'
11
11
  require 'aspera/persistency_action_once'
12
12
  require 'aspera/id_generator'
13
+ require 'aspera/assert'
13
14
  require 'securerandom'
14
15
  require 'date'
15
16
 
16
17
  module Aspera
17
18
  module Cli
18
19
  module Plugins
19
- class Aoc < Aspera::Cli::BasicAuthPlugin
20
+ class Aoc < Cli::BasicAuthPlugin
20
21
  AOC_PATH_API_CLIENTS = 'admin/api-clients'
21
22
  # default redirect for AoC web auth
22
- DEFAULT_REDIRECT = 'http://localhost:12345'
23
- private_constant :AOC_PATH_API_CLIENTS, :DEFAULT_REDIRECT
23
+ REDIRECT_LOCALHOST = 'http://localhost:12345'
24
+ # OAuth methods supported
25
+ STD_AUTH_TYPES = %i[web jwt].freeze
26
+ private_constant :AOC_PATH_API_CLIENTS, :REDIRECT_LOCALHOST, :STD_AUTH_TYPES
24
27
  class << self
25
28
  def application_name
26
29
  'Aspera on Cloud'
@@ -30,13 +33,13 @@ module Aspera
30
33
  # no protocol ?
31
34
  base_url = "https://#{base_url}" unless base_url.match?(%r{^[a-z]{1,6}://})
32
35
  # only org provided ?
33
- base_url = "#{base_url}.#{Aspera::AoC::PROD_DOMAIN}" unless base_url.include?('.')
36
+ base_url = "#{base_url}.#{Api::AoC::PROD_DOMAIN}" unless base_url.include?('.')
34
37
  # AoC is only https
35
38
  return nil unless base_url.start_with?('https://')
36
- result = Rest.new({base_url: base_url, redirect_max: 10}).read('')
39
+ result = Rest.new(base_url: base_url, redirect_max: 10).read('')
37
40
  # Any AoC is on this domain
38
- return nil unless result[:http].uri.host.end_with?(Aspera::AoC::PROD_DOMAIN)
39
- Log.log.debug{'AoC Main page: #{result[:http].body.include?(Aspera::AoC::PRODUCT_NAME)}'}
41
+ return nil unless result[:http].uri.host.end_with?(Api::AoC::PROD_DOMAIN)
42
+ Log.log.debug{"AoC Main page: #{result[:http].body.include?(Api::AoC::PRODUCT_NAME)}"}
40
43
  base_url = result[:http].uri.to_s if result[:http].uri.path.include?('/public')
41
44
  # either in standard domain, or product name in page
42
45
  return {
@@ -47,7 +50,7 @@ module Aspera
47
50
 
48
51
  def private_key_required?(url)
49
52
  # pub link do not need private key
50
- return AoC.link_info(url)[:token].nil?
53
+ return Api::AoC.link_info(url)[:token].nil?
51
54
  end
52
55
 
53
56
  # @param [Hash] env : options, formatter
@@ -60,9 +63,9 @@ module Aspera
60
63
  options.declare(:use_generic_client, 'Wizard: AoC: use global or org specific jwt client id', values: :bool, default: true)
61
64
  options.parse_options!
62
65
  instance_url = options.get_option(:url, mandatory: true)
63
- pub_link_info = AoC.link_info(instance_url)
66
+ pub_link_info = Api::AoC.link_info(instance_url)
64
67
  if !pub_link_info[:token].nil?
65
- pub_api = Rest.new({base_url: "https://#{URI.parse(pub_link_info[:url]).host}/api/v1"})
68
+ pub_api = Rest.new(base_url: "https://#{URI.parse(pub_link_info[:url]).host}/api/v1")
66
69
  pub_info = pub_api.read('env/url_token_check', {token: pub_link_info[:token]})[:data]
67
70
  preset_value = {
68
71
  link: instance_url
@@ -99,7 +102,7 @@ module Aspera
99
102
  formatter.display_status('Navigate to: 𓃑 → Admin → Integrations → API Clients')
100
103
  formatter.display_status('Check or create in integration:')
101
104
  formatter.display_status("- name: #{@info[:name]}")
102
- formatter.display_status("- redirect uri: #{DEFAULT_REDIRECT}")
105
+ formatter.display_status("- redirect uri: #{REDIRECT_LOCALHOST}")
103
106
  formatter.display_status('- origin: localhost')
104
107
  formatter.display_status('Use the generated client id and secret in the following prompts.'.red)
105
108
  end
@@ -112,13 +115,14 @@ module Aspera
112
115
  formatter.display_status('We will use web authentication to bootstrap.')
113
116
  auto_set_pub_key = true
114
117
  auto_set_jwt = true
115
- aoc_api.oauth.generic_parameters[:grant_method] = :web
116
- aoc_api.oauth.generic_parameters[:scope] = AoC::SCOPE_FILES_ADMIN
117
- aoc_api.oauth.specific_parameters[:redirect_uri] = DEFAULT_REDIRECT
118
+ raise 'TODO'
119
+ # aoc_api.oauth.grant_method = :web
120
+ # aoc_api.oauth.scope = Api::AoC::SCOPE_FILES_ADMIN
121
+ # aoc_api.oauth.specific_parameters[:redirect_uri] = REDIRECT_LOCALHOST
118
122
  end
119
123
  myself = object.aoc_api.read('self')[:data]
120
124
  if auto_set_pub_key
121
- raise Cli::Error, 'Public key is already set in profile (use --override=yes)' unless myself['public_key'].empty? || option_override
125
+ Aspera.assert(myself['public_key'].empty?, exception_class: Cli::Error){'Public key is already set in profile (use --override=yes)'} unless option_override
122
126
  formatter.display_status('Updating profile with the public key.')
123
127
  aoc_api.update("users/#{myself['id']}", {'public_key' => pub_key_pem})
124
128
  end
@@ -165,21 +169,25 @@ module Aspera
165
169
  client_registration_token
166
170
  client_access_key
167
171
  kms_profile].freeze
168
- PACKAGE_QUERY_DEFAULT = {'archived' => false, 'exclude_dropbox_packages' => true, 'has_content' => true, 'received' => true}.freeze
172
+ PACKAGE_RECEIVED_BASE_QUERY = {
173
+ 'archived' => false,
174
+ 'has_content' => true,
175
+ 'received' => true,
176
+ 'completed' => true}.freeze
169
177
 
170
- def initialize(env)
171
- super(env)
178
+ def initialize(**env)
179
+ super
172
180
  @cache_workspace_info = nil
173
181
  @cache_home_node_file = nil
174
182
  @cache_api_aoc = nil
175
- options.declare(:auth, 'OAuth type of authentication', values: Oauth::STD_AUTH_TYPES, default: :jwt)
183
+ options.declare(:auth, 'OAuth type of authentication', values: STD_AUTH_TYPES, default: :jwt)
176
184
  options.declare(:client_id, 'OAuth API client identifier')
177
185
  options.declare(:client_secret, 'OAuth API client secret')
178
- options.declare(:scope, 'OAuth scope for AoC API calls', default: AoC::SCOPE_FILES_USER)
186
+ options.declare(:scope, 'OAuth scope for AoC API calls', default: Api::AoC::SCOPE_FILES_USER)
179
187
  options.declare(:redirect_uri, 'OAuth API client redirect URI')
180
188
  options.declare(:private_key, 'OAuth JWT RSA private key PEM value (prefix file path with @file:)')
181
189
  options.declare(:passphrase, 'RSA private key passphrase')
182
- options.declare(:workspace, 'Name of workspace', types: [String, NilClass], default: Aspera::AoC::DEFAULT_WORKSPACE)
190
+ options.declare(:workspace, 'Name of workspace', types: [String, NilClass], default: Api::AoC::DEFAULT_WORKSPACE)
183
191
  options.declare(:new_user_option, 'New user creation option for unknown package recipients')
184
192
  options.declare(:validate_metadata, 'Validate shared inbox metadata', values: :bool, default: true)
185
193
  options.parse_options!
@@ -190,9 +198,9 @@ module Aspera
190
198
  OPTIONS_NEW = %i[url auth client_id client_secret scope redirect_uri private_key passphrase username password workspace].freeze
191
199
 
192
200
  def api_from_options(new_base_path)
193
- create_values = {subpath: new_base_path, secret_finder: @agents[:config]}
201
+ create_values = {subpath: new_base_path, secret_finder: config}
194
202
  # create an API object with the same options, but with a different subpath
195
- return Aspera::AoC.new(**OPTIONS_NEW.each_with_object(create_values) { |i, m|m[i] = options.get_option(i) unless options.get_option(i).nil?})
203
+ 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?})
196
204
  rescue ArgumentError => e
197
205
  if (m = e.message.match(/missing keyword: :(.*)$/))
198
206
  raise Cli::Error, "Missing option: #{m[1]}"
@@ -201,7 +209,14 @@ module Aspera
201
209
  end
202
210
 
203
211
  def aoc_api
204
- @cache_api_aoc = api_from_options(AoC::API_V1) if @cache_api_aoc.nil?
212
+ if @cache_api_aoc.nil?
213
+ @cache_api_aoc = api_from_options(Api::AoC::API_V1)
214
+ organization = @cache_api_aoc.read('organization')[:data]
215
+ if organization['http_gateway_enabled'] && organization['http_gateway_server_url']
216
+ transfer.httpgw_url_cb = lambda { organization['http_gateway_server_url'] }
217
+ # @cache_api_aoc.current_user_info['connect_disabled']
218
+ end
219
+ end
205
220
  return @cache_api_aoc
206
221
  end
207
222
 
@@ -209,7 +224,7 @@ module Aspera
209
224
  # @return identifier
210
225
  def get_resource_id_from_args(resource_class_path)
211
226
  return instance_identifier do |field, value|
212
- raise Cli::BadArgument, 'only selection by name is supported' unless field.eql?('name')
227
+ Aspera.assert(field.eql?('name'), exception_class: Cli::BadArgument){'only selection by name is supported'}
213
228
  aoc_api.lookup_by_name(resource_class_path, value)['id']
214
229
  end
215
230
  end
@@ -218,11 +233,11 @@ module Aspera
218
233
  return "#{resource_class_path}/#{get_resource_id_from_args(resource_class_path)}"
219
234
  end
220
235
 
221
- # Call aoc_api.read with same parameters.
222
- # Use paging if necessary to get all results
223
- # @return [Hash] {list: , total: }
224
- def read_with_paging(resource_class_path, base_query)
225
- raise 'Query must be Hash' unless base_query.is_a?(Hash)
236
+ # Call block with same query using paging and response information
237
+ # @return [Hash] {data: , total: }
238
+ def api_call_paging(base_query={})
239
+ Aspera.assert_type(base_query, Hash){'query'}
240
+ Aspera.assert(block_given?)
226
241
  # set default large page if user does not specify own parameters. AoC Caps to 1000 anyway
227
242
  base_query['per_page'] = 1000 unless base_query.key?('per_page')
228
243
  max_items = base_query.delete(MAX_ITEMS)
@@ -235,7 +250,7 @@ module Aspera
235
250
  loop do
236
251
  query = base_query.clone
237
252
  query['page'] = current_page
238
- result = aoc_api.read(resource_class_path, query)
253
+ result = yield(query)
239
254
  total_count = result[:http]['X-Total-Count']
240
255
  page_count += 1
241
256
  current_page += 1
@@ -247,7 +262,38 @@ module Aspera
247
262
  break if !max_pages.nil? && page_count >= max_pages
248
263
  end
249
264
  item_list = item_list[0..max_items - 1] if !max_items.nil? && item_list.count > max_items
250
- return {list: item_list, total: total_count}
265
+ return {data: item_list, total: total_count}
266
+ end
267
+
268
+ # read using the query and paging
269
+ # @return [Hash] {data: , total: }
270
+ def api_read_all(resource_class_path, base_query={})
271
+ return api_call_paging(base_query) do |query|
272
+ aoc_api.read(resource_class_path, query)
273
+ end
274
+ end
275
+
276
+ # list all entities, given additional, default and user's queries
277
+ def result_list(resource_class_path, fields: nil, base_query: {}, default_query: {})
278
+ Aspera.assert_type(base_query, Hash)
279
+ Aspera.assert_type(default_query, Hash)
280
+ user_query = query_read_delete(default: default_query)
281
+ # caller may add specific modifications or checks
282
+ yield(user_query) if block_given?
283
+ return {type: :object_list, fields: fields}.merge(api_read_all(resource_class_path, base_query.merge(user_query)))
284
+ end
285
+
286
+ def resolve_dropbox_name_default_ws_id(query)
287
+ if query.key?('dropbox_name')
288
+ # convenience: specify name instead of id
289
+ raise 'not both dropbox_name and dropbox_id' if query.key?('dropbox_id')
290
+ # TODO : craft a query that looks for dropbox only in current workspace
291
+ query['dropbox_id'] = aoc_api.lookup_by_name('dropboxes', query['dropbox_name'])['id']
292
+ query.delete('dropbox_name')
293
+ end
294
+ query['workspace_id'] ||= aoc_api.context[:workspace_id] unless aoc_api.context[:workspace_id].eql?(:undefined)
295
+ # by default show dropbox packages only for dropboxes
296
+ query['exclude_dropbox_packages'] = !query.key?('dropbox_id') unless query.key?('exclude_dropbox_packages')
251
297
  end
252
298
 
253
299
  NODE4_EXT_COMMANDS = %i[transfer].concat(Node::COMMANDS_GEN4).freeze
@@ -263,7 +309,7 @@ module Aspera
263
309
  scope: scope
264
310
  )
265
311
  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?
266
- node_plugin = Node.new(@agents, api: top_node_api)
312
+ node_plugin = Node.new(**init_params, api: top_node_api)
267
313
  case command_repo
268
314
  when *Node::COMMANDS_GEN4
269
315
  return node_plugin.execute_command_gen4(command_repo, file_id)
@@ -275,20 +321,20 @@ module Aspera
275
321
  source_folder = options.get_next_argument('folder of source files', type: String)
276
322
  case push_pull
277
323
  when :push
278
- client_direction = Fasp::TransferSpec::DIRECTION_SEND
324
+ client_direction = Transfer::Spec::DIRECTION_SEND
279
325
  client_folder = source_folder
280
326
  server_folder = transfer.destination_folder(client_direction)
281
327
  when :pull
282
- client_direction = Fasp::TransferSpec::DIRECTION_RECEIVE
328
+ client_direction = Transfer::Spec::DIRECTION_RECEIVE
283
329
  client_folder = transfer.destination_folder(client_direction)
284
330
  server_folder = source_folder
285
- else raise 'internal error'
331
+ else Aspera.error_unreachable_line
286
332
  end
287
333
  client_apfid = top_node_api.resolve_api_fid(file_id, client_folder)
288
334
  server_apfid = top_node_api.resolve_api_fid(file_id, server_folder)
289
335
  # force node as transfer agent
290
- @agents[:transfer].agent_instance = Fasp::AgentNode.new({
291
- url: client_apfid[:api].params[:base_url],
336
+ transfer.agent_instance = Agent::Node.new({
337
+ url: client_apfid[:api].base_url,
292
338
  username: client_apfid[:api].app_info[:node_info]['access_key'],
293
339
  password: client_apfid[:api].oauth_token,
294
340
  root_id: client_apfid[:file_id]
@@ -303,22 +349,21 @@ module Aspera
303
349
  server_apfid[:file_id],
304
350
  client_direction,
305
351
  add_ts)))
306
- else raise "INTERNAL ERROR: Missing case: #{command_repo}"
352
+ else Aspera.error_unreachable_line
307
353
  end # command_repo
308
- # raise 'internal error:shall not reach here'
354
+ Aspera.error_unreachable_line
309
355
  end # execute_nodegen4_command
310
356
 
311
357
  def execute_admin_action
312
358
  # upgrade scope to admin
313
- aoc_api.oauth.generic_parameters[:scope] = AoC::SCOPE_FILES_ADMIN
359
+ aoc_api.oauth.scope = Api::AoC::SCOPE_FILES_ADMIN
314
360
  command_admin = options.get_next_command(%i[ats resource usage_reports analytics subscription auth_providers])
315
361
  case command_admin
316
362
  when :auth_providers
317
363
  command_auth_prov = options.get_next_command(%i[list update])
318
364
  case command_auth_prov
319
365
  when :list
320
- providers = aoc_api.read('admin/auth_providers')[:data]
321
- return {type: :object_list, data: providers}
366
+ return result_list('admin/auth_providers')
322
367
  when :update
323
368
  raise 'not implemented'
324
369
  end
@@ -378,15 +423,15 @@ module Aspera
378
423
  result = bss_api.create('graphql', {'variables' => {'organization_id' => org['id']}, 'query' => graphql_query})[:data]['data']
379
424
  return {type: :single_object, data: result['aoc']['bssSubscription']}
380
425
  when :ats
381
- ats_api = Rest.new(aoc_api.params.deep_merge({
382
- base_url: "#{aoc_api.params[:base_url]}/admin/ats/pub/v1",
383
- auth: {scope: AoC::SCOPE_FILES_ADMIN_USER}
426
+ ats_api = Rest.new(**aoc_api.params.deep_merge({
427
+ base_url: "#{aoc_api.base_url}/admin/ats/pub/v1",
428
+ auth: {scope: Api::AoC::SCOPE_FILES_ADMIN_USER}
384
429
  }))
385
- return Ats.new(@agents).execute_action_gen(ats_api)
430
+ return Ats.new(**init_params).execute_action_gen(ats_api)
386
431
  when :analytics
387
- analytics_api = Rest.new(aoc_api.params.deep_merge({
388
- base_url: "#{aoc_api.params[:base_url].gsub('/api/v1', '')}/analytics/v2",
389
- auth: {scope: AoC::SCOPE_FILES_ADMIN_USER}
432
+ analytics_api = Rest.new(**aoc_api.params.deep_merge({
433
+ base_url: "#{aoc_api.base_url.gsub('/api/v1', '')}/analytics/v2",
434
+ auth: {scope: Api::AoC::SCOPE_FILES_ADMIN_USER}
390
435
  }))
391
436
  command_analytics = options.get_next_command(%i[application_events transfers])
392
437
  case command_analytics
@@ -402,18 +447,22 @@ module Aspera
402
447
  when :organizations then aoc_api.current_user_info['organization_id']
403
448
  when :users then aoc_api.current_user_info['id']
404
449
  when :nodes then aoc_api.current_user_info['id'] # TODO: consistent ? # rubocop:disable Lint/DuplicateBranch
405
- else raise 'Internal error'
450
+ else Aspera.error_unreachable_line
406
451
  end
407
452
  filter = options.get_option(:query) || {}
408
453
  filter['limit'] ||= 100
409
454
  if options.get_option(:once_only, mandatory: true)
410
455
  saved_date = []
411
456
  start_date_persistency = PersistencyActionOnce.new(
412
- manager: @agents[:persistency],
457
+ manager: persistency,
413
458
  data: saved_date,
414
- ids: IdGenerator.from_list(['aoc_ana_date', options.get_option(:url, mandatory: true), aoc_api.context[:workspace_name]].push(
459
+ id: IdGenerator.from_list([
460
+ 'aoc_ana_date',
461
+ options.get_option(:url, mandatory: true),
462
+ aoc_api.context(:files)[:workspace_name],
415
463
  filter_resource.to_s,
416
- filter_id)))
464
+ filter_id
465
+ ]))
417
466
  start_date_time = saved_date.first
418
467
  stop_date_time = Time.now.utc.strftime('%FT%T.%LZ')
419
468
  # Log.log().error("start: #{start_date_time}")
@@ -484,9 +533,7 @@ module Aspera
484
533
  when :group_membership then default_fields.push(*%w[group_id member_type member_id])
485
534
  when :workspace_membership then default_fields.push(*%w[workspace_id member_type member_id])
486
535
  end
487
- items = read_with_paging(resource_class_path, query_read_delete(default: default_query))
488
- formatter.display_item_count(items[:list].length, items[:total])
489
- return {type: :object_list, data: items[:list], fields: default_fields}
536
+ return result_list(resource_class_path, fields: default_fields, default_query: default_query)
490
537
  when :show
491
538
  object = aoc_api.read(resource_instance_path)[:data]
492
539
  # default: show all, but certificate
@@ -511,12 +558,13 @@ module Aspera
511
558
  return Main.result_success
512
559
  when :do
513
560
  command_repo = options.get_next_command(NODE4_EXT_COMMANDS)
514
- aoc_api.context(:none)
561
+ # init context
562
+ aoc_api.context(:files)
515
563
  return execute_nodegen4_command(command_repo, res_id)
516
- else raise 'unknown command'
564
+ else Aspera.error_unexpected_value(command)
517
565
  end
518
566
  when :usage_reports
519
- return {type: :object_list, data: aoc_api.read('usage_reports', {workspace_id: aoc_api.context(:none)[:workspace_id]})[:data]}
567
+ return result_list('usage_reports', base_query: {workspace_id: aoc_api.context(:files)[:workspace_id]})
520
568
  end
521
569
  end
522
570
 
@@ -538,10 +586,10 @@ module Aspera
538
586
  when :reminder
539
587
  # send an email reminder with list of orgs
540
588
  user_email = options.get_option(:username, mandatory: true)
541
- Rest.new(base_url: "#{AoC.api_base_url}/#{AoC::API_V1}").create('organization_reminders', {email: user_email})[:data]
589
+ Rest.new(base_url: "#{Api::AoC.api_base_url}/#{Api::AoC::API_V1}").create('organization_reminders', {email: user_email})[:data]
542
590
  return Main.result_status("List of organizations user is member of, has been sent by e-mail to #{user_email}")
543
591
  when :servers
544
- return {type: :object_list, data: Rest.new(base_url: "#{AoC.api_base_url}/#{AoC::API_V1}").read('servers')[:data]}
592
+ return {type: :object_list, data: Rest.new(base_url: "#{Api::AoC.api_base_url}/#{Api::AoC::API_V1}").read('servers')[:data]}
545
593
  when :bearer_token
546
594
  return {type: :text, data: aoc_api.oauth_token}
547
595
  when :organization
@@ -549,15 +597,15 @@ module Aspera
549
597
  when :tier_restrictions
550
598
  return { type: :single_object, data: aoc_api.read('tier_restrictions')[:data] }
551
599
  when :user
552
- case options.get_next_command(%i[workspaces profile])
600
+ case options.get_next_command(%i[workspaces profile preferences])
553
601
  # when :settings
554
602
  # return {type: :object_list,data: aoc_api.read('client_settings/')[:data]}
555
603
  when :workspaces
556
604
  case options.get_next_command(%i[list current])
557
605
  when :list
558
- return {type: :object_list, data: aoc_api.read('workspaces')[:data], fields: %w[id name]}
606
+ return result_list('workspaces', fields: %w[id name])
559
607
  when :current
560
- return { type: :single_object, data: aoc_api.read("workspaces/#{aoc_api.context(:none)[:workspace_id]}")[:data] }
608
+ return { type: :single_object, data: aoc_api.read("workspaces/#{aoc_api.context(:files)[:workspace_id]}")[:data] }
561
609
  end
562
610
  when :profile
563
611
  case options.get_next_command(%i[show modify])
@@ -567,19 +615,25 @@ module Aspera
567
615
  aoc_api.update("users/#{aoc_api.current_user_info(exception: true)['id']}", options.get_next_argument('properties', type: Hash))
568
616
  return Main.result_status('modified')
569
617
  end
618
+ when :preferences
619
+ user_preferences_res = "users/#{aoc_api.current_user_info(exception: true)['id']}/user_interaction_preferences"
620
+ case options.get_next_command(%i[show modify])
621
+ when :show
622
+ return { type: :single_object, data: aoc_api.read(user_preferences_res)[:data] }
623
+ when :modify
624
+ aoc_api.update(user_preferences_res, options.get_next_argument('properties', type: Hash))
625
+ return Main.result_status('modified')
626
+ end
570
627
  end
571
628
  when :packages
572
- package_command = options.get_next_command(%i[shared_inboxes send recv list show delete].concat(Node::NODE4_READ_ACTIONS))
629
+ package_command = options.get_next_command(%i[shared_inboxes send receive list show delete].concat(Node::NODE4_READ_ACTIONS), aliases: {recv: :receive})
573
630
  case package_command
574
631
  when :shared_inboxes
575
632
  case options.get_next_command(%i[list show])
576
633
  when :list
577
- query = query_read_delete
578
- if query.nil?
579
- query = {'embed[]' => 'dropbox', 'aggregate_permissions_by_dropbox' => true, 'sort' => 'dropbox_name'}
580
- query['workspace_id'] = aoc_api.context[:workspace_id] unless aoc_api.context[:workspace_id].eql?(:undefined)
581
- end
582
- return {type: :object_list, data: aoc_api.read('dropbox_memberships', query)[:data], fields: ['dropbox_id', 'dropbox.name']}
634
+ default_query = {'embed[]' => 'dropbox', 'aggregate_permissions_by_dropbox' => true, 'sort' => 'dropbox_name'}
635
+ default_query['workspace_id'] = aoc_api.context[:workspace_id] unless aoc_api.context[:workspace_id].eql?(:undefined)
636
+ return result_list('dropbox_memberships', fields: %w[dropbox_id dropbox.name], default_query: default_query)
583
637
  when :show
584
638
  return {type: :single_object, data: aoc_api.read(get_resource_path_from_args('dropboxes'), query)[:data]}
585
639
  end
@@ -603,7 +657,7 @@ module Aspera
603
657
  Main.result_transfer(transfer.start(created_package[:spec], rest_token: created_package[:node]))
604
658
  # return all info on package (especially package id)
605
659
  return { type: :single_object, data: created_package[:info]}
606
- when :recv
660
+ when :receive
607
661
  ids_to_download = nil
608
662
  if !aoc_api.public_link.nil?
609
663
  aoc_api.assert_public_link_types(['view_received_package'])
@@ -615,8 +669,9 @@ module Aspera
615
669
  skip_ids_data = []
616
670
  skip_ids_persistency = nil
617
671
  if options.get_option(:once_only, mandatory: true)
672
+ # TODO: add query info to id
618
673
  skip_ids_persistency = PersistencyActionOnce.new(
619
- manager: @agents[:persistency],
674
+ manager: persistency,
620
675
  data: skip_ids_data,
621
676
  id: IdGenerator.from_list(
622
677
  ['aoc_recv',
@@ -624,30 +679,30 @@ module Aspera
624
679
  aoc_api.context[:workspace_id]
625
680
  ].concat(aoc_api.additional_persistence_ids)))
626
681
  end
627
- if ExtendedValue::ALL.eql?(ids_to_download)
628
- query = query_read_delete(default: PACKAGE_QUERY_DEFAULT)
629
- raise 'option query must be Hash' unless query.is_a?(Hash)
630
- if query.key?('dropbox_name')
631
- # convenience: specify name instead of id
632
- raise 'not both dropbox_name and dropbox_id' if query.key?('dropbox_id')
633
- query['dropbox_id'] = aoc_api.lookup_by_name('dropboxes', query['dropbox_name'])['id']
634
- query.delete('dropbox_name')
635
- end
636
- query['workspace_id'] ||= aoc_api.context[:workspace_id] unless aoc_api.context[:workspace_id].eql?(:undefined)
637
- # get list of packages in inbox
638
- package_info = aoc_api.read('packages', query)[:data]
682
+ case ids_to_download
683
+ when ExtendedValue::ALL, ExtendedValue::INIT
684
+ query = query_read_delete(default: PACKAGE_RECEIVED_BASE_QUERY)
685
+ Aspera.assert_type(query, Hash){'query'}
686
+ resolve_dropbox_name_default_ws_id(query)
639
687
  # remove from list the ones already downloaded
640
- ids_to_download = package_info.map{|e|e['id']}
688
+ all_ids = api_read_all('packages', query)[:data].map{|e|e['id']}
689
+ if ids_to_download.eql?(ExtendedValue::INIT)
690
+ Aspera.assert(skip_ids_persistency){'Only with option once_only'}
691
+ skip_ids_persistency.data.clear.concat(all_ids)
692
+ skip_ids_persistency.save
693
+ return Main.result_status("Initialized skip for #{skip_ids_persistency.data.count} package(s)")
694
+ end
641
695
  # array here
642
- ids_to_download.reject!{|id|skip_ids_data.include?(id)}
696
+ ids_to_download = all_ids.reject{|id|skip_ids_data.include?(id)}
697
+ else
698
+ ids_to_download = [ids_to_download] unless ids_to_download.is_a?(Array)
643
699
  end # ExtendedValue::ALL
644
700
  # list here
645
- ids_to_download = [ids_to_download] unless ids_to_download.is_a?(Array)
646
701
  result_transfer = []
647
702
  formatter.display_status("found #{ids_to_download.length} package(s).")
648
703
  ids_to_download.each do |package_id|
649
704
  package_info = aoc_api.read("packages/#{package_id}")[:data]
650
- formatter.display_status("downloading package: #{package_info['name']}")
705
+ formatter.display_status("downloading package: [#{package_info['id']}] #{package_info['name']}")
651
706
  package_node_api = aoc_api.node_api_from(
652
707
  node_id: package_info['node_id'],
653
708
  workspace_id: aoc_api.context[:workspace_id],
@@ -656,7 +711,7 @@ module Aspera
656
711
  statuses = transfer.start(
657
712
  package_node_api.transfer_spec_gen4(
658
713
  package_info['contents_file_id'],
659
- Fasp::TransferSpec::DIRECTION_RECEIVE,
714
+ Transfer::Spec::DIRECTION_RECEIVE,
660
715
  {'paths'=> [{'source' => '.'}]}),
661
716
  rest_token: package_node_api)
662
717
  result_transfer.push({'package' => package_id, Main::STATUS_FIELD => statuses})
@@ -673,36 +728,25 @@ module Aspera
673
728
  return { type: :single_object, data: package_info }
674
729
  when :list
675
730
  display_fields = %w[id name bytes_transferred]
676
- query = query_read_delete(default: PACKAGE_QUERY_DEFAULT)
677
- raise 'option query must be Hash' unless query.is_a?(Hash)
678
- if query.key?('dropbox_name')
679
- # convenience: specify name instead of id
680
- raise 'not both dropbox_name and dropbox_id' if query.key?('dropbox_id')
681
- query['dropbox_id'] = aoc_api.lookup_by_name('dropboxes', query['dropbox_name'])['id']
682
- query.delete('dropbox_name')
683
- end
684
- if aoc_api.context[:workspace_id].eql?(:undefined)
685
- display_fields.push('workspace_id')
686
- else
687
- query['workspace_id'] ||= aoc_api.context[:workspace_id]
688
- end
689
- packages = aoc_api.read('packages', query)[:data]
690
- return {type: :object_list, data: packages, fields: display_fields}
731
+ display_fields.push('workspace_id') if aoc_api.context[:workspace_id].eql?(:undefined)
732
+ return result_list('packages', fields: display_fields, base_query: PACKAGE_RECEIVED_BASE_QUERY) do |query|
733
+ resolve_dropbox_name_default_ws_id(query)
734
+ end
691
735
  when :delete
692
736
  return do_bulk_operation(command: package_command, descr: 'identifier', values: identifier) do |id|
693
- raise 'expecting String identifier' unless id.is_a?(String) || id.is_a?(Integer)
737
+ Aspera.assert_values(id.class, [String, Integer]){'identifier'}
694
738
  aoc_api.delete("packages/#{id}")[:data]
695
739
  end
696
740
  when *Node::NODE4_READ_ACTIONS
697
741
  package_id = instance_identifier
698
742
  package_info = aoc_api.read("packages/#{package_id}")[:data]
699
- return execute_nodegen4_command(package_command, package_info['node_id'], file_id: package_info['file_id'], scope: Aspera::Node::SCOPE_USER)
743
+ return execute_nodegen4_command(package_command, package_info['node_id'], file_id: package_info['file_id'], scope: Api::Node::SCOPE_USER)
700
744
  end
701
745
  when :files
702
746
  command_repo = options.get_next_command([:short_link].concat(NODE4_EXT_COMMANDS))
703
747
  case command_repo
704
748
  when *NODE4_EXT_COMMANDS
705
- return execute_nodegen4_command(command_repo, aoc_api.context[:home_node_id], file_id: aoc_api.context[:home_file_id], scope: Aspera::Node::SCOPE_USER)
749
+ 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)
706
750
  when :short_link
707
751
  link_type = options.get_next_argument('link type', expected: %i[public private])
708
752
  short_link_command = options.get_next_command(%i[create delete list])
@@ -720,7 +764,7 @@ module Aspera
720
764
  purpose = case link_type
721
765
  when :public then 'token_auth_redirection'
722
766
  when :private then 'shared_folder_auth_link'
723
- else raise 'internal error'
767
+ else Aspera.error_unreachable_line
724
768
  end
725
769
  case short_link_command
726
770
  when :delete
@@ -754,8 +798,7 @@ module Aspera
754
798
  # embed: 'updated_by_user',
755
799
  sort: '-created_at'
756
800
  }
757
- result = aoc_api.read('short_links', list_params)[:data]
758
- return {type: :object_list, data: result, fields: Formatter.all_but('data')}
801
+ return result_list('short_links', fields: Formatter.all_but('data'), base_query: list_params)
759
802
  when :create
760
803
  creation_params = {
761
804
  purpose: purpose,
@@ -781,7 +824,7 @@ module Aspera
781
824
  if link_type.eql?(:public)
782
825
  # TODO: merge with node permissions ?
783
826
  # TODO: access level as arg
784
- access_levels = Aspera::Node::ACCESS_LEVELS # ['delete','list','mkdir','preview','read','rename','write']
827
+ access_levels = Api::Node::ACCESS_LEVELS # ['delete','list','mkdir','preview','read','rename','write']
785
828
  folder_name = File.basename(folder_dest)
786
829
  perm_data = {
787
830
  'file_id' => shared_apfid[:file_id],
@@ -810,9 +853,7 @@ module Aspera
810
853
  when :automation
811
854
  Log.log.warn('BETA: work under progress')
812
855
  # automation api is not in the same place
813
- automation_rest_params = aoc_api.params.clone
814
- automation_rest_params[:base_url].gsub!('/api/', '/automation/')
815
- automation_api = Rest.new(automation_rest_params)
856
+ automation_api = Rest.new(**aoc_api.params.merge(base_url: aoc_api.base_url.gsub('/api/', '/automation/')))
816
857
  command_automation = options.get_next_command(%i[workflows instances])
817
858
  case command_automation
818
859
  when :instances
@@ -846,21 +887,16 @@ module Aspera
846
887
  url = value_create_modify(command: command, type: String)
847
888
  uri = URI.parse(url)
848
889
  server = WebServerSimple.new(uri)
849
- server.mount(uri.path, Faspex4GWServlet, aoc_api, aoc_api.context(:none)[:workspace_id])
850
- trap('INT') { server.shutdown }
851
- formatter.display_status("Faspex 4 gateway listening on #{url}")
852
- Log.log.info("Listening on #{url}")
853
- # this is blocking until server exits
890
+ server.mount(uri.path, Faspex4GWServlet, aoc_api, aoc_api.context(:files)[:workspace_id])
854
891
  server.start
855
892
  return Main.result_status('Gateway terminated')
856
- else
857
- raise "internal error: #{command}"
893
+ else Aspera.error_unreachable_line
858
894
  end # action
859
- raise 'internal error: command shall return'
895
+ Aspera.error_unreachable_line
860
896
  end
861
897
 
862
898
  private :execute_admin_action
863
- end # AoC
864
- end # Plugins
865
- end # Cli
866
- end # Aspera
899
+ end
900
+ end
901
+ end
902
+ end