aspera-cli 4.14.0 → 4.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +54 -3
  4. data/CONTRIBUTING.md +7 -7
  5. data/README.md +1457 -880
  6. data/bin/ascli +18 -9
  7. data/bin/asession +12 -14
  8. data/examples/proxy.pac +1 -1
  9. data/lib/aspera/aoc.rb +198 -127
  10. data/lib/aspera/ascmd.rb +24 -14
  11. data/lib/aspera/cli/basic_auth_plugin.rb +9 -6
  12. data/lib/aspera/cli/error.rb +17 -0
  13. data/lib/aspera/cli/extended_value.rb +47 -12
  14. data/lib/aspera/cli/formatter.rb +260 -171
  15. data/lib/aspera/cli/hints.rb +80 -0
  16. data/lib/aspera/cli/main.rb +101 -147
  17. data/lib/aspera/cli/manager.rb +160 -124
  18. data/lib/aspera/cli/plugin.rb +70 -59
  19. data/lib/aspera/cli/plugins/alee.rb +0 -1
  20. data/lib/aspera/cli/plugins/aoc.rb +239 -273
  21. data/lib/aspera/cli/plugins/ats.rb +8 -5
  22. data/lib/aspera/cli/plugins/bss.rb +2 -2
  23. data/lib/aspera/cli/plugins/config.rb +516 -375
  24. data/lib/aspera/cli/plugins/console.rb +40 -0
  25. data/lib/aspera/cli/plugins/cos.rb +4 -5
  26. data/lib/aspera/cli/plugins/faspex.rb +99 -84
  27. data/lib/aspera/cli/plugins/faspex5.rb +179 -148
  28. data/lib/aspera/cli/plugins/node.rb +219 -153
  29. data/lib/aspera/cli/plugins/orchestrator.rb +52 -17
  30. data/lib/aspera/cli/plugins/preview.rb +46 -32
  31. data/lib/aspera/cli/plugins/server.rb +57 -17
  32. data/lib/aspera/cli/plugins/shares.rb +34 -12
  33. data/lib/aspera/cli/sync_actions.rb +68 -0
  34. data/lib/aspera/cli/transfer_agent.rb +45 -55
  35. data/lib/aspera/cli/transfer_progress.rb +74 -0
  36. data/lib/aspera/cli/version.rb +1 -1
  37. data/lib/aspera/colors.rb +3 -1
  38. data/lib/aspera/command_line_builder.rb +14 -11
  39. data/lib/aspera/cos_node.rb +3 -2
  40. data/lib/aspera/environment.rb +17 -6
  41. data/lib/aspera/fasp/agent_aspera.rb +126 -0
  42. data/lib/aspera/fasp/agent_base.rb +31 -77
  43. data/lib/aspera/fasp/agent_connect.rb +21 -22
  44. data/lib/aspera/fasp/agent_direct.rb +88 -102
  45. data/lib/aspera/fasp/agent_httpgw.rb +196 -192
  46. data/lib/aspera/fasp/agent_node.rb +41 -34
  47. data/lib/aspera/fasp/agent_trsdk.rb +75 -34
  48. data/lib/aspera/fasp/error_info.rb +2 -2
  49. data/lib/aspera/fasp/faux_file.rb +52 -0
  50. data/lib/aspera/fasp/installation.rb +43 -184
  51. data/lib/aspera/fasp/management.rb +244 -0
  52. data/lib/aspera/fasp/parameters.rb +59 -26
  53. data/lib/aspera/fasp/parameters.yaml +75 -8
  54. data/lib/aspera/fasp/products.rb +162 -0
  55. data/lib/aspera/fasp/transfer_spec.rb +1 -1
  56. data/lib/aspera/fasp/uri.rb +4 -4
  57. data/lib/aspera/faspex_gw.rb +2 -2
  58. data/lib/aspera/faspex_postproc.rb +2 -2
  59. data/lib/aspera/hash_ext.rb +2 -2
  60. data/lib/aspera/json_rpc.rb +49 -0
  61. data/lib/aspera/line_logger.rb +23 -0
  62. data/lib/aspera/log.rb +57 -16
  63. data/lib/aspera/node.rb +97 -14
  64. data/lib/aspera/oauth.rb +36 -18
  65. data/lib/aspera/open_application.rb +4 -4
  66. data/lib/aspera/persistency_folder.rb +2 -2
  67. data/lib/aspera/preview/file_types.rb +4 -2
  68. data/lib/aspera/preview/generator.rb +22 -35
  69. data/lib/aspera/preview/options.rb +2 -0
  70. data/lib/aspera/preview/terminal.rb +24 -13
  71. data/lib/aspera/preview/utils.rb +19 -26
  72. data/lib/aspera/rest.rb +103 -72
  73. data/lib/aspera/rest_call_error.rb +1 -1
  74. data/lib/aspera/rest_error_analyzer.rb +15 -14
  75. data/lib/aspera/rest_errors_aspera.rb +37 -34
  76. data/lib/aspera/secret_hider.rb +14 -16
  77. data/lib/aspera/ssh.rb +4 -1
  78. data/lib/aspera/sync.rb +128 -122
  79. data/lib/aspera/temp_file_manager.rb +10 -3
  80. data/lib/aspera/web_auth.rb +10 -7
  81. data/lib/aspera/web_server_simple.rb +9 -4
  82. data.tar.gz.sig +0 -0
  83. metadata +33 -15
  84. metadata.gz.sig +0 -0
  85. data/lib/aspera/cli/listener/line_dump.rb +0 -19
  86. data/lib/aspera/cli/listener/logger.rb +0 -22
  87. data/lib/aspera/cli/listener/progress.rb +0 -50
  88. data/lib/aspera/cli/listener/progress_multi.rb +0 -84
  89. data/lib/aspera/cli/plugins/sync.rb +0 -44
  90. data/lib/aspera/fasp/listener.rb +0 -13
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # spellchecker: ignore workgroups,mypackages
3
+ # spellchecker: ignore workgroups mypackages passcode
4
4
 
5
5
  require 'aspera/cli/basic_auth_plugin'
6
6
  require 'aspera/persistency_action_once'
@@ -8,7 +8,6 @@ require 'aspera/id_generator'
8
8
  require 'aspera/nagios'
9
9
  require 'aspera/environment'
10
10
  require 'securerandom'
11
- require 'ruby-progressbar'
12
11
  require 'tty-spinner'
13
12
 
14
13
  module Aspera
@@ -22,78 +21,130 @@ module Aspera
22
21
  API_LIST_MAILBOX_TYPES = %w[inbox inbox_history inbox_all inbox_all_history outbox outbox_history pending pending_history all].freeze
23
22
  PACKAGE_ALL_INIT = 'INIT'
24
23
  PACKAGE_SEND_FROM_REMOTE_SOURCE = 'remote_source'
25
- ADMIN_RESOURCES = %i[accounts contacts jobs workgroups shared_inboxes nodes oauth_clients registrations saml_configs metadata_profiles
26
- email_notifications].freeze
27
- private_constant(*%i[RECIPIENT_TYPES PACKAGE_TERMINATED API_DETECT API_LIST_MAILBOX_TYPES PACKAGE_SEND_FROM_REMOTE_SOURCE])
24
+ # Faspex API v5: get transfer spec for connect
25
+ TRANSFER_CONNECT = 'connect'
26
+ ADMIN_RESOURCES = %i[
27
+ accounts contacts jobs workgroups shared_inboxes nodes oauth_clients registrations saml_configs
28
+ metadata_profiles email_notifications
29
+ ].freeze
30
+ JOB_RUNNING = %w[queued working].freeze
31
+ STANDARD_PATH = '/aspera/faspex'
32
+ private_constant(*%i[JOB_RUNNING RECIPIENT_TYPES PACKAGE_TERMINATED API_DETECT API_LIST_MAILBOX_TYPES PACKAGE_SEND_FROM_REMOTE_SOURCE])
28
33
  class << self
29
- def detect(base_url)
30
- api = Rest.new(base_url: base_url, redirect_max: 1)
31
- result = api.read(API_DETECT)
32
- if result[:http].code.start_with?('2') && result[:http].body.strip.empty?
33
- suffix_length = -2 - API_DETECT.length
34
+ def application_name
35
+ 'Faspex'
36
+ end
37
+
38
+ def detect(address_or_url)
39
+ address_or_url = "https://#{address_or_url}" unless address_or_url.match?(%r{^[a-z]{1,6}://})
40
+ urls = [address_or_url]
41
+ urls.push("#{address_or_url}#{STANDARD_PATH}") unless address_or_url.end_with?(STANDARD_PATH)
42
+
43
+ urls.each do |base_url|
44
+ next unless base_url.start_with?('https://')
45
+ api = Rest.new(base_url: base_url, redirect_max: 1)
46
+ result = api.read(API_DETECT)
47
+ next unless result[:http].code.start_with?('2') && result[:http].body.strip.empty?
48
+ url_length = -2 - API_DETECT.length
49
+ # take redirect if any
34
50
  return {
35
51
  version: result[:http]['x-ibm-aspera'] || '5',
36
- url: result[:http].uri.to_s[0..suffix_length],
37
- name: 'Faspex 5'
52
+ url: result[:http].uri.to_s[0..url_length]
38
53
  }
54
+ rescue StandardError => e
55
+ Log.log.debug{"detect error: #{e}"}
39
56
  end
40
57
  return nil
41
58
  end
42
- end
43
59
 
44
- # Faspex API v5: get transfer spec for connect
45
- TRANSFER_CONNECT = 'connect'
60
+ def wizard(object:, private_key_path:, pub_key_pem:)
61
+ options = object.options
62
+ formatter = object.formatter
63
+ instance_url = options.get_option(:url, mandatory: true)
64
+ wiz_username = options.get_option(:username, mandatory: true)
65
+ raise "Username shall be an email in Faspex: #{wiz_username}" if !(wiz_username =~ /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i)
66
+ if options.get_option(:client_id).nil? || options.get_option(:client_secret).nil?
67
+ formatter.display_status('Ask the ascli client id and secret to your Administrator.'.red)
68
+ formatter.display_status("Admin should login to: #{instance_url}")
69
+ OpenApplication.instance.uri(instance_url)
70
+ formatter.display_status('Navigate to: 𓃑 → Admin → Configurations → API clients')
71
+ formatter.display_status('Create an API client with:')
72
+ formatter.display_status('- name: ascli')
73
+ formatter.display_status('- JWT: enabled')
74
+ formatter.display_status("Then, logged in as #{wiz_username.red} go to your profile:")
75
+ formatter.display_status('👤 → Account Settings → Preferences -> Public Key in PEM:')
76
+ formatter.display_status(pub_key_pem)
77
+ formatter.display_status('Once set, fill in the parameters:')
78
+ end
79
+ return {
80
+ preset_value: {
81
+ url: instance_url,
82
+ username: wiz_username,
83
+ auth: :jwt.to_s,
84
+ private_key: "@file:#{private_key_path}",
85
+ client_id: options.get_option(:client_id, mandatory: true),
86
+ client_secret: options.get_option(:client_secret, mandatory: true)
87
+ },
88
+ test_args: 'user profile show'
89
+ }
90
+ end
91
+
92
+ def public_link?(url)
93
+ url.include?('/public/')
94
+ end
95
+ end
46
96
 
47
97
  def initialize(env)
48
98
  super(env)
49
99
  options.declare(:client_id, 'OAuth client identifier')
50
100
  options.declare(:client_secret, 'OAuth client secret')
51
101
  options.declare(:redirect_uri, 'OAuth redirect URI for web authentication')
52
- options.declare(:auth, 'OAuth type of authentication', values: %i[boot link].concat(Oauth::STD_AUTH_TYPES), default: :jwt)
102
+ options.declare(:auth, 'OAuth type of authentication', values: %i[boot].concat(Oauth::STD_AUTH_TYPES), default: :jwt)
53
103
  options.declare(:private_key, 'OAuth JWT RSA private key PEM value (prefix file path with @file:)')
54
104
  options.declare(:passphrase, 'OAuth JWT RSA private key passphrase')
55
- options.declare(:link, 'Public link authorization (specific operations)')
56
- options.declare(:box, "Package inbox, either shared inbox name or one of #{API_LIST_MAILBOX_TYPES} or #{VAL_ALL}", default: 'inbox')
105
+ options.declare(:box, "Package inbox, either shared inbox name or one of #{API_LIST_MAILBOX_TYPES} or #{ExtendedValue::ALL}", default: 'inbox')
57
106
  options.declare(:shared_folder, 'Send package with files from shared folder')
58
- options.declare(:group_type, 'Shared inbox or workgroup', values: %i[shared_inboxes workgroups], default: :shared_inboxes)
107
+ options.declare(:group_type, 'Type of shared box', values: %i[shared_inboxes workgroups], default: :shared_inboxes)
59
108
  options.parse_options!
60
109
  end
61
110
 
111
+ def api_url
112
+ return "#{@faspex5_api_base_url}/api/v5"
113
+ end
114
+
115
+ def auth_api_url
116
+ return "#{@faspex5_api_base_url}/auth"
117
+ end
118
+
62
119
  def set_api
63
- public_link = options.get_option(:link)
64
- unless public_link.nil?
65
- @faspex5_api_base_url = public_link.gsub(%r{/public/.*}, '').gsub(/\?.*/, '')
66
- options.set_option(:auth, :link)
67
- end
68
- @faspex5_api_base_url ||= options.get_option(:url, mandatory: true).gsub(%r{/+$}, '')
69
- @faspex5_api_auth_url = "#{@faspex5_api_base_url}/auth"
70
- faspex5_api_v5_url = "#{@faspex5_api_base_url}/api/v5"
71
- case options.get_option(:auth, mandatory: true)
72
- when :link
73
- uri = URI.parse(public_link)
74
- args = URI.decode_www_form(uri.query).each_with_object({}){|v, h|h[v.first] = v.last; }
75
- Log.dump(:args, args)
76
- context = args['context']
77
- raise 'missing context' if context.nil?
78
- @pub_link_context = JSON.parse(Base64.decode64(context))
79
- Log.dump(:@pub_link_context, @pub_link_context)
120
+ # get endpoint, remove unnecessary trailing slashes
121
+ @faspex5_api_base_url = options.get_option(:url, mandatory: true).gsub(%r{/+$}, '')
122
+ auth_type = self.class.public_link?(@faspex5_api_base_url) ? :public_link : options.get_option(:auth, mandatory: true)
123
+ case auth_type
124
+ when :public_link
125
+ encoded_context = Rest.decode_query(URI.parse(@faspex5_api_base_url).query)['context']
126
+ raise 'Bad faspex5 public link, missing context in query' if encoded_context.nil?
127
+ @pub_link_context = JSON.parse(Base64.decode64(encoded_context))
128
+ Log.log.trace1{Log.dump(:@pub_link_context, @pub_link_context)}
129
+ # ok, we have the additional parameters, get the base url
130
+ @faspex5_api_base_url = @faspex5_api_base_url.gsub(%r{/public/.*}, '').gsub(/\?.*/, '')
80
131
  @api_v5 = Rest.new({
81
- base_url: faspex5_api_v5_url,
132
+ base_url: api_url,
82
133
  headers: {'Passcode' => @pub_link_context['passcode']}
83
134
  })
84
135
  when :boot
85
136
  # the password here is the token copied directly from browser in developer mode
86
137
  @api_v5 = Rest.new({
87
- base_url: faspex5_api_v5_url,
138
+ base_url: api_url,
88
139
  headers: {'Authorization' => options.get_option(:password, mandatory: true)}
89
140
  })
90
141
  when :web
91
142
  # opens a browser and ask user to auth using web
92
143
  @api_v5 = Rest.new({
93
- base_url: faspex5_api_v5_url,
144
+ base_url: api_url,
94
145
  auth: {
95
146
  type: :oauth2,
96
- base_url: @faspex5_api_auth_url,
147
+ base_url: auth_api_url,
97
148
  grant_method: :web,
98
149
  client_id: options.get_option(:client_id, mandatory: true),
99
150
  web: {redirect_uri: options.get_option(:redirect_uri, mandatory: true)}
@@ -101,10 +152,10 @@ module Aspera
101
152
  when :jwt
102
153
  app_client_id = options.get_option(:client_id, mandatory: true)
103
154
  @api_v5 = Rest.new({
104
- base_url: faspex5_api_v5_url,
155
+ base_url: api_url,
105
156
  auth: {
106
157
  type: :oauth2,
107
- base_url: @faspex5_api_auth_url,
158
+ base_url: auth_api_url,
108
159
  grant_method: :jwt,
109
160
  client_id: app_client_id,
110
161
  jwt: {
@@ -147,32 +198,45 @@ module Aspera
147
198
 
148
199
  # wait for package status to be in provided list
149
200
  def wait_package_status(id, status_list: PACKAGE_TERMINATED)
150
- spinner = nil
151
- progress = nil
152
- while true
201
+ total_sent = false
202
+ loop do
153
203
  status = @api_v5.read("packages/#{id}/upload_details")[:data]
204
+ status['id'] = id
154
205
  # user asked to not follow
155
- break if status_list.nil? || status_list.include?(status['upload_status'])
206
+ return status if status_list.nil?
156
207
  if status['upload_status'].eql?('submitted')
157
- if spinner.nil?
158
- spinner = TTY::Spinner.new('[:spinner] :title', format: :classic)
159
- spinner.start
160
- end
161
- spinner.update(title: status['upload_status'])
162
- spinner.spin
163
- elsif progress.nil?
164
- progress = ProgressBar.create(
165
- format: '%a %B %p%% %r Mbps %e',
166
- rate_scale: lambda{|rate|rate / Environment::BYTES_PER_MEBIBIT},
167
- title: 'progress',
168
- total: status['bytes_total'].to_i)
208
+ config.progress_bar&.event(session_id: nil, type: :pre_start, info: status['upload_status'])
209
+ elsif !total_sent
210
+ config.progress_bar&.event(session_id: id, type: :session_start)
211
+ config.progress_bar&.event(session_id: id, type: :session_size, info: status['bytes_total'].to_i)
212
+ total_sent = true
169
213
  else
170
- progress.progress = status['bytes_written'].to_i
214
+ config.progress_bar&.event(session_id: id, type: :transfer, info: status['bytes_written'].to_i)
215
+ end
216
+ if status_list.include?(status['upload_status'])
217
+ # if status['upload_status'].eql?('completed')
218
+ config.progress_bar&.event(session_id: id, type: :end)
219
+ return status
220
+ # end
221
+ end
222
+ sleep(1.0)
223
+ end
224
+ end
225
+
226
+ def wait_for_job(job_id)
227
+ spinner = nil
228
+ loop do
229
+ status = @api_v5.read("jobs/#{job_id}", {type: :formatted})[:data]
230
+ return status unless JOB_RUNNING.include?(status['status'])
231
+ if spinner.nil?
232
+ spinner = TTY::Spinner.new('[:spinner] :title', format: :classic)
233
+ spinner.start
171
234
  end
235
+ spinner.update(title: status['status'])
236
+ spinner.spin
172
237
  sleep(0.5)
173
238
  end
174
- status['id'] = id
175
- return status
239
+ raise 'internal error'
176
240
  end
177
241
 
178
242
  # get a (full or partial) list of all entities of a given type
@@ -228,7 +292,7 @@ module Aspera
228
292
  box = options.get_option(:box)
229
293
  api_path =
230
294
  case box
231
- when VAL_ALL then '' # only admin can list all packages globally
295
+ when ExtendedValue::ALL then '' # only admin can list all packages globally
232
296
  when *API_LIST_MAILBOX_TYPES then box
233
297
  else
234
298
  group_type = options.get_option(:group_type)
@@ -240,7 +304,7 @@ module Aspera
240
304
  path: api_path).select(&filter)
241
305
  end
242
306
 
243
- def package_receive
307
+ def package_receive(package_ids)
244
308
  # prepare persistency if needed
245
309
  skip_ids_persistency = nil
246
310
  if options.get_option(:once_only, mandatory: true)
@@ -253,50 +317,43 @@ module Aspera
253
317
  options.get_option(:url, mandatory: true),
254
318
  options.get_option(:username, mandatory: true)]))
255
319
  end
256
- package_ids =
257
- if @pub_link_context&.key?('package_id')
258
- @pub_link_context['package_id']
259
- else
260
- # one or several packages
261
- instance_identifier
262
- end
263
320
  case package_ids
264
321
  when PACKAGE_ALL_INIT
265
322
  raise 'Only with option once_only' unless skip_ids_persistency
266
323
  skip_ids_persistency.data.clear.concat(list_packages_with_filter.map{|p|p['id']})
267
324
  skip_ids_persistency.save
268
325
  return Main.result_status("Initialized skip for #{skip_ids_persistency.data.count} package(s)")
269
- when VAL_ALL
326
+ when ExtendedValue::ALL
270
327
  # TODO: if packages have same name, they will overwrite ?
271
328
  package_ids = list_packages_with_filter.map{|p|p['id']}
272
- Log.dump(:package_ids, package_ids)
273
- Log.dump(:package_ids, skip_ids_persistency.data)
329
+ Log.log.debug{Log.dump(:package_ids, package_ids)}
330
+ Log.log.debug{Log.dump(:skip_ids, skip_ids_persistency.data)}
274
331
  package_ids.reject!{|i|skip_ids_persistency.data.include?(i)} if skip_ids_persistency
275
- Log.dump(:package_ids, package_ids)
332
+ Log.log.debug{Log.dump(:package_ids, package_ids)}
276
333
  end
277
334
  # a single id was provided
278
335
  # TODO: check package_ids is a list of strings
279
336
  package_ids = [package_ids] if package_ids.is_a?(String)
280
337
  result_transfer = []
338
+ param_file_list = {}
339
+ begin
340
+ param_file_list['paths'] = transfer.source_list.map{|source|{'path'=>source}}
341
+ rescue Cli::BadArgument
342
+ # paths is optional
343
+ end
344
+ download_params = {
345
+ type: 'received',
346
+ transfer_type: TRANSFER_CONNECT
347
+ }
348
+ box = options.get_option(:box)
349
+ case box
350
+ when /outbox/ then download_params[:type] = 'sent'
351
+ when *API_LIST_MAILBOX_TYPES then nil # nothing to do
352
+ else # shared inbox / workgroup
353
+ download_params[:recipient_workgroup_id] = lookup_entity_by_field(type: options.get_option(:group_type), value: box)['id']
354
+ end
281
355
  package_ids.each do |pkg_id|
282
356
  formatter.display_status("Receiving package #{pkg_id}")
283
- param_file_list = {}
284
- begin
285
- param_file_list['paths'] = transfer.source_list.map{|source|{'path'=>source}}
286
- rescue Aspera::Cli::CliBadArgument
287
- # paths is optional
288
- end
289
- download_params = {
290
- type: 'received',
291
- transfer_type: TRANSFER_CONNECT
292
- }
293
- box = options.get_option(:box)
294
- case box
295
- when /outbox/ then download_params[:type] = 'sent'
296
- when *API_LIST_MAILBOX_TYPES then nil # nothing to do
297
- else # shared inbox / workgroup
298
- download_params[:recipient_workgroup_id] = lookup_entity_by_field(type: options.get_option(:group_type), value: box)['id']
299
- end
300
357
  # TODO: allow from sent as well ?
301
358
  transfer_spec = @api_v5.call(
302
359
  operation: 'POST',
@@ -319,21 +376,15 @@ module Aspera
319
376
  end
320
377
 
321
378
  def package_action
322
- command = options.get_next_command(%i[list show browse status delete send receive])
379
+ command = options.get_next_command(%i[show browse status delete receive send list])
380
+ package_id =
381
+ if %i[receive show browse status delete].include?(command)
382
+ @pub_link_context&.key?('package_id') ? @pub_link_context['package_id'] : instance_identifier
383
+ end
323
384
  case command
324
- when :list
325
- return {
326
- type: :object_list,
327
- data: list_packages_with_filter,
328
- fields: %w[id title release_date total_bytes total_files created_time state]
329
- }
330
385
  when :show
331
- id = @pub_link_context['package_id'] if @pub_link_context&.key?('package_id')
332
- id ||= instance_identifier
333
- return {type: :single_object, data: @api_v5.read("packages/#{id}")[:data]}
386
+ return {type: :single_object, data: @api_v5.read("packages/#{package_id}")[:data]}
334
387
  when :browse
335
- id = @pub_link_context['package_id'] if @pub_link_context&.key?('package_id')
336
- id ||= instance_identifier
337
388
  path = options.get_next_argument('path', expected: :single, mandatory: false) || '/'
338
389
  # TODO: support multi-page listing ?
339
390
  params = {
@@ -343,29 +394,31 @@ module Aspera
343
394
  }
344
395
  result = @api_v5.call({
345
396
  operation: 'POST',
346
- subpath: "packages/#{id}/files/received",
397
+ subpath: "packages/#{package_id}/files/received",
347
398
  headers: {'Accept' => 'application/json'},
348
399
  url_params: params,
349
400
  json_params: {'path' => path, 'filters' => {'basenames'=>[]}}})[:data]
350
401
  formatter.display_item_count(result['item_count'], result['total_count'])
351
402
  return {type: :object_list, data: result['items']}
352
403
  when :status
353
- status = wait_package_status(instance_identifier, status_list: nil)
404
+ status = wait_package_status(package_id, status_list: nil)
354
405
  return {type: :single_object, data: status}
355
406
  when :delete
356
- ids = instance_identifier
407
+ ids = package_id
357
408
  ids = [ids] unless ids.is_a?(Array)
358
409
  raise 'Package identifier must be a single id or an Array' unless ids.is_a?(Array) && ids.all?(String)
359
410
  # API returns 204, empty on success
360
411
  @api_v5.call({operation: 'DELETE', subpath: 'packages', headers: {'Accept' => 'application/json'}, json_params: {ids: ids}})
361
412
  return Main.result_status('Package(s) deleted')
413
+ when :receive
414
+ return package_receive(package_id)
362
415
  when :send
363
- parameters = value_create_modify(command: command, type: Hash)
416
+ parameters = value_create_modify(command: command)
364
417
  normalize_recipients(parameters)
365
418
  package = @api_v5.create('packages', parameters)[:data]
366
419
  shared_folder = options.get_option(:shared_folder)
367
420
  if shared_folder.nil?
368
- # TODO: option to send from remote source or httpgw
421
+ # send from local files
369
422
  transfer_spec = @api_v5.call(
370
423
  operation: 'POST',
371
424
  subpath: "packages/#{package['id']}/transfer_spec/upload",
@@ -377,6 +430,7 @@ module Aspera
377
430
  transfer_spec.delete('authentication')
378
431
  return Main.result_transfer(transfer.start(transfer_spec))
379
432
  else
433
+ # send from remote shared folder
380
434
  if (m = shared_folder.match(REGEX_LOOKUP_ID_BY_FIELD))
381
435
  shared_folder = lookup_entity_by_field(type: 'shared_folders', value: m[2])['id']
382
436
  end
@@ -390,8 +444,12 @@ module Aspera
390
444
  end
391
445
  return {type: :single_object, data: result}
392
446
  end
393
- when :receive
394
- return package_receive
447
+ when :list
448
+ return {
449
+ type: :object_list,
450
+ data: list_packages_with_filter,
451
+ fields: %w[id title release_date total_bytes total_files created_time state]
452
+ }
395
453
  end # case package
396
454
  end
397
455
 
@@ -474,10 +532,10 @@ module Aspera
474
532
  list_key = false
475
533
  id_as_arg = 'type'
476
534
  when :accounts
477
- display_fields = [:all_but, 'user_profile_data_attributes']
535
+ display_fields = Formatter.all_but('user_profile_data_attributes')
478
536
  when :oauth_clients
479
- display_fields = [:all_but, 'public_key']
480
- adm_api = Rest.new(@api_v5.params.merge({base_url: @faspex5_api_auth_url}))
537
+ display_fields = Formatter.all_but('public_key')
538
+ adm_api = Rest.new(@api_v5.params.merge({base_url: auth_api_url}))
481
539
  when :shared_inboxes, :workgroups
482
540
  available_commands.push(:members, :saml_groups, :invite_external_collaborator)
483
541
  end
@@ -533,20 +591,23 @@ module Aspera
533
591
  when :show
534
592
  return { type: :single_object, data: @api_v5.read(smtp_path)[:data] }
535
593
  when :create
536
- return { type: :single_object, data: @api_v5.create(smtp_path, value_create_modify(command: smtp_cmd, type: Hash))[:data] }
594
+ return { type: :single_object, data: @api_v5.create(smtp_path, value_create_modify(command: smtp_cmd))[:data] }
537
595
  when :modify
538
- return { type: :single_object, data: @api_v5.modify(smtp_path, value_create_modify(command: smtp_cmd, type: Hash))[:data] }
596
+ return { type: :single_object, data: @api_v5.update(smtp_path, value_create_modify(command: smtp_cmd))[:data] }
539
597
  when :delete
540
598
  return { type: :single_object, data: @api_v5.delete(smtp_path)[:data] }
541
599
  when :test
542
600
  test_data = options.get_next_argument('Email or test data, see API')
543
601
  test_data = {test_email_recipient: test_data} if test_data.is_a?(String)
544
- return { type: :single_object, data: @api_v5.create(File.join(smtp_path, 'test'), test_data)[:data] }
602
+ creation = @api_v5.create(File.join(smtp_path, 'test'), test_data)[:data]
603
+ result = wait_for_job(creation['job_id'])
604
+ result['serialized_args'] = JSON.parse(result['serialized_args']) rescue result['serialized_args']
605
+ return { type: :single_object, data: result }
545
606
  end
546
607
  end
547
608
  when :gateway
548
609
  require 'aspera/faspex_gw'
549
- url = value_create_modify(type: String)
610
+ url = value_create_modify(command: command, type: String)
550
611
  uri = URI.parse(url)
551
612
  server = WebServerSimple.new(uri)
552
613
  server.mount(uri.path, Faspex4GWServlet, @api_v5, nil)
@@ -559,7 +620,7 @@ module Aspera
559
620
  return Main.result_status('Gateway terminated')
560
621
  when :postprocessing
561
622
  require 'aspera/faspex_postproc' # cspell:disable-line
562
- parameters = value_create_modify(type: Hash)
623
+ parameters = value_create_modify(command: command)
563
624
  parameters = parameters.symbolize_keys
564
625
  raise 'Missing key: url' unless parameters.key?(:url)
565
626
  uri = URI.parse(parameters[:url])
@@ -576,36 +637,6 @@ module Aspera
576
637
  return Main.result_status('Gateway terminated')
577
638
  end # case command
578
639
  end # action
579
-
580
- def wizard(params)
581
- if params[:prepare]
582
- # if not defined by user, generate unique name
583
- params[:preset_name] ||= [params[:plugin_sym]].concat(URI.parse(params[:instance_url]).host.gsub(/[^a-z0-9.]/, '').split('.')).join('_')
584
- params[:need_private_key] = true
585
- return
586
- end
587
- formatter.display_status('Ask the ascli client id and secret to your Administrator, or ask them to go to:'.red)
588
- OpenApplication.instance.uri(params[:instance_url])
589
- formatter.display_status('Then: 𓃑 → Admin → Configurations → API clients')
590
- formatter.display_status('Create an API client with:')
591
- formatter.display_status('- name: ascli')
592
- formatter.display_status('- JWT: enabled')
593
- formatter.display_status('Then, logged in as user go to your profile:')
594
- formatter.display_status('👤 → Account Settings → Preferences -> Public Key in PEM:')
595
- formatter.display_status(params[:pub_key_pem])
596
- formatter.display_status('Once set, fill in the parameters:')
597
- return {
598
- preset_value: {
599
- url: params[:instance_url],
600
- username: options.get_option(:username, mandatory: true),
601
- auth: :jwt.to_s,
602
- private_key: '@file:' + params[:private_key_path],
603
- client_id: options.get_option(:client_id, mandatory: true),
604
- client_secret: options.get_option(:client_secret, mandatory: true)
605
- },
606
- test_args: "#{params[:plugin_sym]} user profile show"
607
- }
608
- end
609
640
  end # Faspex5
610
641
  end # Plugins
611
642
  end # Cli