aspera-cli 4.25.2 → 4.25.4

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.
@@ -94,7 +94,7 @@ module Aspera
94
94
 
95
95
  def initialize(**_)
96
96
  super
97
- options.declare(:box, "Package inbox, either shared inbox name or one of: #{Api::Faspex::API_LIST_MAILBOX_TYPES.join(', ')} or #{SpecialValues::ALL}", default: 'inbox')
97
+ options.declare(:box, "Package inbox, either shared inbox name or one of: #{Api::Faspex::API_LIST_MAILBOX_TYPES.join(', ')} or #{SpecialValues::ALL}", default: 'inbox_all')
98
98
  options.declare(:shared_folder, 'Send package with files from shared folder')
99
99
  options.declare(:group_type, 'Type of shared box', allowed: %i[shared_inboxes workgroups], default: :shared_inboxes)
100
100
  options.parse_options!
@@ -102,7 +102,7 @@ module Aspera
102
102
 
103
103
  def set_api
104
104
  # create an API object with the same options, but with a different subpath
105
- @api_v5 = new_with_options(Api::Faspex)
105
+ @api_v5 = Api::Faspex.new(**Oauth.args_from_options(options))
106
106
  # in case user wants to use HTTPGW tell transfer agent how to get address
107
107
  transfer.httpgw_url_cb = lambda{@api_v5.read('account')['gateway_url']}
108
108
  end
@@ -118,7 +118,7 @@ module Aspera
118
118
  recipient_types = [recipient_types] unless recipient_types.is_a?(Array)
119
119
  end
120
120
  parameters['recipients'].map! do |recipient_data|
121
- # if just a string, make a general lookup and build expected name/type hash
121
+ # If just a string, make a general lookup and build expected name/type hash
122
122
  if recipient_data.is_a?(String)
123
123
  matched = @api_v5.lookup_by_name('contacts', recipient_data, query: Rest.php_style({context: 'packages', type: recipient_types}))
124
124
  recipient_data = {
@@ -164,18 +164,18 @@ module Aspera
164
164
  loop do
165
165
  result = @api_v5.read("jobs/#{job_id}", {type: :formatted})
166
166
  break unless Api::Faspex::JOB_RUNNING.include?(result['status'])
167
- formatter.long_operation_running(result['status'])
167
+ RestParameters.instance.spinner_cb.call(result['status'])
168
168
  sleep(0.5)
169
169
  end
170
- formatter.long_operation_terminated
170
+ RestParameters.instance.spinner_cb.call(action: :success)
171
171
  return result
172
172
  end
173
173
 
174
174
  # list all packages with optional filter
175
175
  def list_packages_with_filter(query: {})
176
176
  filter = options.get_next_argument('filter', mandatory: false, validation: Proc, default: ->(_x){true})
177
- # translate box name to API prefix (with ending slash)
178
177
  box = options.get_option(:box)
178
+ # Translate box name to API prefix (with ending slash)
179
179
  entity =
180
180
  case box
181
181
  when SpecialValues::ALL then 'packages' # only admin can list all packages globally
@@ -192,6 +192,16 @@ module Aspera
192
192
  return list.select(&filter), total
193
193
  end
194
194
 
195
+ # Build query to get package recipients based on package info in case of shared inbox or workgroup recipient
196
+ # @param package_id [String] the package id to get info from
197
+ def recipient_query(package_id)
198
+ package_info = @api_v5.read("packages/#{package_id}")
199
+ base_query = {}
200
+ base_query['recipient_workgroup_id'] = package_info['recipients'].first['id'] if WORKGROUP_TYPES.include?(package_info['recipients'].first['recipient_type'])
201
+ base_query['recipient_user_id'] = package_info['recipients'].first['id'] if package_info['recipients'].first['recipient_type'].eql?('user')
202
+ base_query
203
+ end
204
+
195
205
  def package_receive(package_ids)
196
206
  # prepare persistency if needed
197
207
  skip_ids_persistency = nil
@@ -236,17 +246,12 @@ module Aspera
236
246
  rescue Cli::BadArgument
237
247
  # paths is optional
238
248
  end
249
+ box = options.get_option(:box)
239
250
  download_params = {
240
- type: 'received',
251
+ type: Api::Faspex.box_type(box),
241
252
  transfer_type: Api::Faspex::TRANSFER_CONNECT
242
253
  }
243
- box = options.get_option(:box)
244
- case box
245
- when /outbox/ then download_params[:type] = 'sent'
246
- when *Api::Faspex::API_LIST_MAILBOX_TYPES then nil # nothing to do
247
- else # shared inbox / workgroup
248
- download_params[:recipient_workgroup_id] = lookup_entity_by_field(api: @api_v5, entity: options.get_option(:group_type), value: box)['id']
249
- end
254
+ # download_params[:recipient_workgroup_id] = lookup_entity_by_field(api: @api_v5, entity: options.get_option(:group_type), value: box)['id'] if !Api::Faspex::API_LIST_MAILBOX_TYPES.include?(box) && box != SpecialValues::ALL
250
255
  packages.each do |package|
251
256
  pkg_id = package['id']
252
257
  formatter.display_status("Receiving package #{pkg_id}")
@@ -254,10 +259,10 @@ module Aspera
254
259
  transfer_spec = @api_v5.call(
255
260
  operation: 'POST',
256
261
  subpath: "packages/#{pkg_id}/transfer_spec/download",
257
- query: download_params,
258
- content_type: Rest::MIME_JSON,
262
+ query: download_params.merge(recipient_query(pkg_id)),
263
+ content_type: Mime::JSON,
259
264
  body: param_file_list,
260
- headers: {'Accept' => Rest::MIME_JSON}
265
+ headers: {'Accept' => Mime::JSON}
261
266
  )
262
267
  # delete flag for Connect Client
263
268
  transfer_spec.delete('authentication')
@@ -272,11 +277,61 @@ module Aspera
272
277
  return Main.result_transfer_multiple(result_transfer)
273
278
  end
274
279
 
280
+ def package_send
281
+ parameters = value_create_modify(command: :send)
282
+ # autofill recipient for public url
283
+ if @api_v5.pub_link_context&.key?('recipient_type') && !parameters.key?('recipients')
284
+ parameters['recipients'] = [{
285
+ name: @api_v5.pub_link_context['name'],
286
+ recipient_type: @api_v5.pub_link_context['recipient_type']
287
+ }]
288
+ end
289
+ normalize_recipients(parameters)
290
+ # User specified content prot in tspec, but faspex requires in package creation
291
+ # `transfer_spec/upload` will set `content_protection`
292
+ if transfer.user_transfer_spec['content_protection'] && !parameters.key?('ear_enabled')
293
+ transfer.user_transfer_spec.delete('content_protection')
294
+ parameters['ear_enabled'] = true
295
+ end
296
+ package = @api_v5.create('packages', parameters)
297
+ shared_folder = options.get_option(:shared_folder)
298
+ if shared_folder.nil?
299
+ # send from local files
300
+ transfer_spec = @api_v5.create(
301
+ "packages/#{package['id']}/transfer_spec/upload",
302
+ {paths: transfer.source_list},
303
+ query: {transfer_type: Api::Faspex::TRANSFER_CONNECT}
304
+ )
305
+ # well, we asked a TS for connect, but we actually want a generic one
306
+ transfer_spec.delete('authentication')
307
+ return Main.result_transfer(transfer.start(transfer_spec))
308
+ else
309
+ # send from remote shared folder
310
+ if (m = Base.percent_selector(shared_folder))
311
+ shared_folder = lookup_entity_by_field(
312
+ api: @api_v5,
313
+ entity: 'shared_folders',
314
+ field: m[:field],
315
+ value: m[:value]
316
+ )['id']
317
+ end
318
+ transfer_request = {shared_folder_id: shared_folder, paths: transfer.source_list}
319
+ # start remote transfer and get first status
320
+ result = @api_v5.create("packages/#{package['id']}/remote_transfer", transfer_request)
321
+ result['id'] = package['id']
322
+ unless result['status'].eql?('completed')
323
+ formatter.display_status("Package #{package['id']}")
324
+ result = wait_package_status(package['id'])
325
+ end
326
+ return Main.result_single_object(result)
327
+ end
328
+ end
329
+
275
330
  # Browse a folder
276
331
  # @param browse_endpoint [String] the endpoint to browse
277
- def browse_folder(browse_endpoint)
332
+ def browse_folder(browse_endpoint, base_query = {})
278
333
  folders_to_process = [options.get_next_argument('folder path', default: '/')]
279
- query = query_read_delete(default: {})
334
+ query = base_query.merge(query_read_delete(default: {}))
280
335
  filters = query.delete('filters'){{}}
281
336
  Aspera.assert_type(filters, Hash)
282
337
  filters['basenames'] ||= []
@@ -300,9 +355,9 @@ module Aspera
300
355
  operation: 'POST',
301
356
  subpath: browse_endpoint,
302
357
  query: query,
303
- content_type: Rest::MIME_JSON,
358
+ content_type: Mime::JSON,
304
359
  body: {'path' => path, 'filters' => filters},
305
- headers: {'Accept' => Rest::MIME_JSON},
360
+ headers: {'Accept' => Mime::JSON},
306
361
  ret: :both
307
362
  )
308
363
  all_items.concat(data['items'])
@@ -312,7 +367,7 @@ module Aspera
312
367
  end
313
368
  folders_to_process.concat(data['items'].select{ |i| i['type'].eql?('directory')}.map{ |i| i['path']}) if recursive
314
369
  if use_paging
315
- iteration_token = http[Api::Faspex::HEADER_ITERATION_TOKEN]
370
+ iteration_token = http[Api::Faspex::HEADER_X_NEXT_ITER_TOKEN]
316
371
  break if iteration_token.nil? || iteration_token.empty?
317
372
  query['iteration_token'] = iteration_token
318
373
  else
@@ -320,12 +375,11 @@ module Aspera
320
375
  break if data['item_count'].eql?(0)
321
376
  query['offset'] += data['item_count']
322
377
  end
323
- formatter.long_operation_running(all_items.count)
378
+ RestParameters.instance.spinner_cb.call(all_items.count)
324
379
  end
325
380
  query.delete('iteration_token')
326
381
  end
327
- formatter.long_operation_terminated
328
-
382
+ RestParameters.instance.spinner_cb.call(action: :success)
329
383
  return Main.result_object_list(all_items, total: total_count)
330
384
  end
331
385
 
@@ -339,12 +393,7 @@ module Aspera
339
393
  when :show
340
394
  return Main.result_single_object(@api_v5.read("packages/#{package_id}"))
341
395
  when :browse
342
- location = case options.get_option(:box)
343
- when 'inbox' then 'received'
344
- when 'outbox' then 'sent'
345
- else raise BadArgument, 'Browse only available for inbox and outbox'
346
- end
347
- return browse_folder("packages/#{package_id}/files/#{location}")
396
+ return browse_folder("packages/#{package_id}/files/#{Api::Faspex.box_type(options.get_option(:box))}", recipient_query(package_id))
348
397
  when :status
349
398
  status_list = options.get_next_argument('list of states, or nothing', mandatory: false, validation: Array)
350
399
  status = wait_package_status(package_id, status_list: status_list)
@@ -357,64 +406,21 @@ module Aspera
357
406
  @api_v5.call(
358
407
  operation: 'DELETE',
359
408
  subpath: 'packages',
360
- content_type: Rest::MIME_JSON,
409
+ content_type: Mime::JSON,
361
410
  body: {ids: ids},
362
- headers: {'Accept' => Rest::MIME_JSON}
411
+ headers: {'Accept' => Mime::JSON}
363
412
  )
364
413
  return Main.result_status('Package(s) deleted')
365
414
  when :receive
366
415
  return package_receive(package_id)
367
416
  when :send
368
- parameters = value_create_modify(command: command)
369
- # autofill recipient for public url
370
- if @api_v5.pub_link_context&.key?('recipient_type') && !parameters.key?('recipients')
371
- parameters['recipients'] = [{
372
- name: @api_v5.pub_link_context['name'],
373
- recipient_type: @api_v5.pub_link_context['recipient_type']
374
- }]
375
- end
376
- normalize_recipients(parameters)
377
- # User specified content prot in tspec, but faspex requires in package creation
378
- # `transfer_spec/upload` will set `content_protection`
379
- if transfer.user_transfer_spec['content_protection'] && !parameters.key?('ear_enabled')
380
- transfer.user_transfer_spec.delete('content_protection')
381
- parameters['ear_enabled'] = true
382
- end
383
- package = @api_v5.create('packages', parameters)
384
- shared_folder = options.get_option(:shared_folder)
385
- if shared_folder.nil?
386
- # send from local files
387
- transfer_spec = @api_v5.create(
388
- "packages/#{package['id']}/transfer_spec/upload",
389
- {paths: transfer.source_list},
390
- query: {transfer_type: Api::Faspex::TRANSFER_CONNECT}
391
- )
392
- # well, we asked a TS for connect, but we actually want a generic one
393
- transfer_spec.delete('authentication')
394
- return Main.result_transfer(transfer.start(transfer_spec))
395
- else
396
- # send from remote shared folder
397
- if (m = Base.percent_selector(shared_folder))
398
- shared_folder = lookup_entity_by_field(
399
- api: @api_v5,
400
- entity: 'shared_folders',
401
- field: m[:field],
402
- value: m[:value]
403
- )['id']
404
- end
405
- transfer_request = {shared_folder_id: shared_folder, paths: transfer.source_list}
406
- # start remote transfer and get first status
407
- result = @api_v5.create("packages/#{package['id']}/remote_transfer", transfer_request)
408
- result['id'] = package['id']
409
- unless result['status'].eql?('completed')
410
- formatter.display_status("Package #{package['id']}")
411
- result = wait_package_status(package['id'])
412
- end
413
- return Main.result_single_object(result)
414
- end
417
+ return package_send
415
418
  when :list
416
419
  list, total = list_packages_with_filter
417
- return Main.result_object_list(list, total: total, fields: %w[id title release_date total_bytes total_files created_time state])
420
+ fields = %w[id title status sender.name recipients.0.name release_date total_bytes total_files]
421
+ fields.delete('recipients.0.name') if %w[inbox inbox_history].include?(options.get_option(:box))
422
+ fields.delete('sender.name') if %w[outbox outbox_history].include?(options.get_option(:box))
423
+ return Main.result_object_list(list, total: total, fields: fields)
418
424
  end
419
425
  end
420
426
 
@@ -450,6 +456,8 @@ module Aspera
450
456
  res_id_query = {'all': true}
451
457
  when :nodes
452
458
  available_commands += %i[shared_folders browse]
459
+ when :jobs
460
+ exec_args[:display_fields] = %w[id job_name job_type status]
453
461
  end
454
462
  res_command = options.get_next_command(available_commands)
455
463
  return Main.result_value_list(Api::Faspex::EMAIL_NOTIF_LIST, name: 'email_id') if res_command.eql?(:list) && res_sym.eql?(:email_notifications)
@@ -687,7 +695,7 @@ module Aspera
687
695
  @api_v5.create(invitation_endpoint, params)
688
696
  end
689
697
  when :resend
690
- @api_v5.create("#{invitation_endpoint}/#{instance_identifier}/resend")
698
+ @api_v5.create("#{invitation_endpoint}/#{instance_identifier}/resend", nil)
691
699
  return Main.result_status('Invitation resent')
692
700
  else
693
701
  return entity_execute(
@@ -722,7 +730,8 @@ module Aspera
722
730
  end
723
731
  SHARED_INBOX_MEMBER_LEVELS = %i[submit_only standard shared_inbox_admin].freeze
724
732
  ACCOUNT_TYPES = %w{local_user saml_user self_registered_user external_user}.freeze
725
- CONTACT_TYPES = %w{workgroup shared_inbox distribution_list user external_user}.freeze
733
+ WORKGROUP_TYPES = %w{workgroup shared_inbox}.freeze
734
+ CONTACT_TYPES = (WORKGROUP_TYPES + %w{distribution_list user external_user}).freeze
726
735
  private_constant :SHARED_INBOX_MEMBER_LEVELS, :ACCOUNT_TYPES, :CONTACT_TYPES
727
736
  end
728
737
  end
@@ -93,12 +93,9 @@ module Aspera
93
93
  options.declare(:validator, 'Identifier of validator (optional for central)')
94
94
  options.declare(:asperabrowserurl, 'URL for simple aspera web ui', default: 'https://asperabrowser.mybluemix.net')
95
95
  options.declare(
96
- :default_ports, 'Gen4: Use standard FASP ports (true) or get from node API (false)', allowed: Allowed::TYPES_BOOLEAN, default: true,
97
- handler: {o: Api::Node, m: :use_standard_ports}
98
- )
99
- options.declare(
100
- :node_cache, 'Gen4: Set to no to force actual file system read', allowed: Allowed::TYPES_BOOLEAN,
101
- handler: {o: Api::Node, m: :use_node_cache}
96
+ :node_api, 'Gen4: standard_ports: Use standard FASP ports (true) or get from node API (false). cache: Set to false to force actual file system read',
97
+ allowed: Hash,
98
+ handler: {o: Api::Node, m: :api_options}
102
99
  )
103
100
  options.declare(:root_id, 'Gen4: File id of top folder when using access key (override AK root id)')
104
101
  options.declare(:dynamic_key, 'Private key PEM to use for dynamic key auth', handler: {o: Api::Node, m: :use_dynamic_key})
@@ -242,14 +239,14 @@ module Aspera
242
239
  break if all_items.count >= total_count
243
240
  offset += items.count
244
241
  query['skip'] = offset
245
- formatter.long_operation_running(all_items.count)
242
+ RestParameters.instance.spinner_cb.call(all_items.count)
246
243
  end
247
244
  query.delete('skip')
248
245
  end
249
246
  @prefixer&.remove_in_object_list!(all_items)
250
247
  return Main.result_object_list(all_items)
251
248
  ensure
252
- formatter.long_operation_terminated
249
+ RestParameters.instance.spinner_cb.call(action: :success)
253
250
  end
254
251
 
255
252
  # Create async transfer spec request from direction and folders
@@ -388,7 +385,7 @@ module Aspera
388
385
  when :transport
389
386
  return Main.result_single_object(@api_node.transport_params)
390
387
  when :spec
391
- return Main.result_single_object(@api_node.base_spec)
388
+ return Main.result_single_object(@api_node.base_spec, fields: Formatter.all_but(Transfer::Spec::SPECIFIC))
392
389
  end
393
390
  Aspera.error_unreachable_line
394
391
  end
@@ -450,7 +447,7 @@ module Aspera
450
447
  @api_node.call(
451
448
  operation: 'POST',
452
449
  subpath: 'services/soap/Transfer-201210',
453
- content_type: Rest::MIME_TEXT,
450
+ content_type: Mime::TEXT,
454
451
  body: CENTRAL_SOAP_API_TEST,
455
452
  headers: {'Content-Type' => 'text/xml;charset=UTF-8', 'SOAPAction' => 'FASPSessionNET-200911#GetSessionInfo'},
456
453
  ret: :resp
@@ -471,9 +468,7 @@ module Aspera
471
468
  return Main.result_single_object(nd_info)
472
469
  when :license
473
470
  # requires: asnodeadmin -mu <node user> --acl-add=internal --internal
474
- node_license = @api_node.read('license')
475
- Log.log.error('server must have: asnodeadmin -mu <node user> --acl-add=internal --internal') if node_license['failure'].is_a?(String) && node_license['failure'].include?('ACL')
476
- return Main.result_single_object(node_license)
471
+ return Main.result_single_object(@api_node.read('license'))
477
472
  when :api_details
478
473
  return Main.result_single_object({base_url: @api_node.base_url}.merge(@api_node.params))
479
474
  end
@@ -525,7 +520,7 @@ module Aspera
525
520
  return Main.result_text(result[:password])
526
521
  when :browse
527
522
  apifid = apifid_from_next_arg(top_file_id)
528
- file_info = apifid[:api].read("files/#{apifid[:file_id]}", **Api::Node.cache_control)
523
+ file_info = apifid[:api].read("files/#{apifid[:file_id]}", headers: Api::Node.add_cache_control)
529
524
  unless file_info['type'].eql?('folder')
530
525
  # a single file
531
526
  return Main.result_object_list([file_info], fields: GEN4_LS_FIELDS)
@@ -837,7 +832,7 @@ module Aspera
837
832
  @api_node.call(
838
833
  operation: 'POST',
839
834
  subpath: "asyncs/#{asyncs_id}/#{sync_command}",
840
- content_type: Rest::MIME_TEXT,
835
+ content_type: Mime::TEXT,
841
836
  body: '',
842
837
  ret: :resp
843
838
  ).body
@@ -7,9 +7,33 @@ module Aspera
7
7
  module Plugins
8
8
  # base class for applications supporting OAuth 2.0 authentication
9
9
  class Oauth < BasicAuth
10
- # OAuth methods supported
10
+ class << self
11
+ # Get command line options specified by `AUTH_OPTIONS` and `defaults.keys` (value is default).
12
+ # Adds those not nil to the `kwargs`.
13
+ # Instantiate the provided `klass` with those `kwargs`.
14
+ # `defaults` can specify a default value (not `nil`)
15
+ # @param options [Cli::Manager] Object to get command line options.
16
+ # @param kwargs [Hash] Object creation arguments
17
+ # @param defaults [Hash] Additional options, key=symbol, value=default value or nil
18
+ # @return [Object] instance of `klass`
19
+ # @raise [Cli::Error] if a required option is missing
20
+ def args_from_options(options, defaults: nil, **kwargs)
21
+ defaults ||= {}
22
+ (AUTH_OPTIONS + defaults.keys).each_with_object(kwargs) do |i, m|
23
+ v = options.get_option(i)
24
+ m[i] = v unless v.nil?
25
+ m[i] = defaults[i] if m[i].nil? && !defaults[i].nil?
26
+ end
27
+ rescue ::ArgumentError => e
28
+ if (m = e.message.match(/missing keyword: :(.*)$/))
29
+ raise Cli::Error, "Missing option: #{m[1]}"
30
+ end
31
+ raise
32
+ end
33
+ end
34
+ # OAuth methods supported (web, jwt)
11
35
  AUTH_TYPES = %i[web jwt boot].freeze
12
- # Options used for authentication
36
+ # Options used for authentication (url, auth, client_id, etc...)
13
37
  AUTH_OPTIONS = %i[url auth client_id client_secret redirect_uri private_key passphrase username password].freeze
14
38
  def initialize(**_)
15
39
  super
@@ -20,29 +44,6 @@ module Aspera
20
44
  options.declare(:private_key, 'OAuth (JWT) RSA private key PEM value (prefix file path with @file:)')
21
45
  options.declare(:passphrase, 'OAuth (JWT) RSA private key passphrase')
22
46
  end
23
-
24
- # Get command line options specified by `AUTH_OPTIONS` and `option.keys` (value is default).
25
- # Adds those not nil to the `kwargs`.
26
- # Instantiate the provided `klass` with those kwargs.
27
- # `option` can specify a default value (not `nil`)
28
- # @param klass [Class] API object to create
29
- # @param kwargs [Hash] The fixed keyword arguments for creation
30
- # @param option [Hash] Additional options, key=symbol, value:default value or nil
31
- # @return [Object] instance of `klass`
32
- # @raise [Cli::Error] if a required option is missing
33
- def new_with_options(klass, kwargs: {}, option: {})
34
- klass.new(**
35
- (AUTH_OPTIONS + option.keys).each_with_object(kwargs) do |i, m|
36
- v = options.get_option(i)
37
- m[i] = v unless v.nil?
38
- m[i] = option[i] unless !m[i].nil? || option[i].nil?
39
- end)
40
- rescue ::ArgumentError => e
41
- if (m = e.message.match(/missing keyword: :(.*)$/))
42
- raise Cli::Error, "Missing option: #{m[1]}"
43
- end
44
- raise
45
- end
46
47
  end
47
48
  end
48
49
  end
@@ -90,8 +90,8 @@ module Aspera
90
90
  Aspera::Preview::Options::DESCRIPTIONS.each do |opt|
91
91
  values = if opt.key?(:values)
92
92
  opt[:values]
93
- elsif Cli::Manager::BOOLEAN_SIMPLE.include?(opt[:default])
94
- Allowed::TYPES_BOOLEAN
93
+ elsif BoolValue.symbol?(opt[:default])
94
+ BoolValue::TYPES
95
95
  end
96
96
  options.declare(opt[:name], opt[:description].capitalize, allowed: values, handler: {o: @gen_options, m: opt[:name]}, default: opt[:default])
97
97
  end
@@ -109,7 +109,7 @@ module Aspera
109
109
  # /files/id/files is normally cached in Redis, but we can discard the cache
110
110
  # but /files/id is not cached
111
111
  def get_folder_entries(file_id, request_args = nil)
112
- headers = {'Accept' => Rest::MIME_JSON}
112
+ headers = {'Accept' => Mime::JSON}
113
113
  headers['X-Aspera-Cache-Control'] = 'no-cache' if @option_folder_reset_cache.eql?(:header)
114
114
  return @api_node.read("files/#{file_id}/files", request_args, headers: headers)
115
115
  end
@@ -17,20 +17,21 @@ module Aspera
17
17
  # @return [Hash] with version, ping, api
18
18
  def health_check(url)
19
19
  result = {}
20
+ # Get version from main page
20
21
  result[:version] =
21
22
  begin
22
23
  version = nil
23
24
  login_page = Rest
24
25
  .new(base_url: url, redirect_max: 2)
25
26
  .read('', headers: {'Accept'=>'text/html'})
27
+ raise 'not Shares' unless login_page.include?('aspera-Shares')
26
28
  if (m = login_page.match(/\(v([0-9a-f\.]+)\)/))
27
29
  version = m[1]
28
30
  if (m = login_page.match(/Patch level ([0-9]+)/))
29
- version = "#{result[:version]} #{m[0]}"
31
+ version = "#{version} #{m[0]}"
30
32
  end
31
33
  end
32
- raise 'no version' if version.nil?
33
- version
34
+ version.nil? ? 'no version' : version
34
35
  rescue => e
35
36
  e
36
37
  end
@@ -45,10 +46,15 @@ module Aspera
45
46
  end
46
47
  result[:api] =
47
48
  begin
48
- resp = Rest.new(base_url: url, redirect_max: 1).read("#{NODE_API_PATH}/app", exception: false, ret: :resp)
49
+ data, resp = Rest
50
+ .new(base_url: "#{url}/#{NODE_API_PATH}", redirect_max: 1)
51
+ .read('info', exception: false, ret: :both)
49
52
  # shall fail: shares requires auth, but we check error message
50
- raise 'not found' unless resp.code.to_s.eql?('401') && resp.body.eql?('{"error":{"user_message":"API user authentication failed"}}')
51
- 'available'
53
+ if resp.code.to_s.eql?('401') && data&.dig('error', 'user_message')&.include?('authentication failed')
54
+ 'available'
55
+ else
56
+ raise "not found (#{resp.code})"
57
+ end
52
58
  rescue => e
53
59
  e
54
60
  end
@@ -88,7 +94,7 @@ module Aspera
88
94
  SAML_IMPORT_MANDATORY = %w[id name_id].freeze
89
95
  SAML_IMPORT_ALLOWED = %w[email given_name surname].concat(SAML_IMPORT_MANDATORY).freeze
90
96
 
91
- ACTIONS = %i[health files admin].freeze
97
+ ACTIONS = %i[health info files admin].freeze
92
98
  # common to users and groups
93
99
  USR_GRP_SETTINGS = %i[transfer_settings app_authorizations share_permissions].freeze
94
100
 
@@ -111,6 +117,8 @@ module Aspera
111
117
  nagios.add_critical('API', health[:api].to_s)
112
118
  end
113
119
  Main.result_object_list(nagios.status_list)
120
+ when :info
121
+ return Main.result_single_object(basic_auth_api(NODE_API_PATH).read('info', headers: {'Content-Type'=>'application/json'}))
114
122
  when :files
115
123
  api_shares_node = basic_auth_api(NODE_API_PATH)
116
124
  repo_command = options.get_next_command(Node::COMMANDS_SHARES)
@@ -8,6 +8,7 @@ module Aspera
8
8
  INIT = 'INIT'
9
9
  ALL = 'ALL'
10
10
  DEF = 'DEF'
11
+ EOA = 'END'
11
12
  end
12
13
  end
13
14
  end
@@ -4,6 +4,6 @@ module Aspera
4
4
  module Cli
5
5
  # For beta add extension : .beta1
6
6
  # For dev version add extension : .pre
7
- VERSION = '4.25.2'
7
+ VERSION = '4.25.4'
8
8
  end
9
9
  end
@@ -62,7 +62,7 @@ module Aspera
62
62
  detection_info = nil
63
63
  begin
64
64
  Log.log.debug{"detecting #{plugin_name_sym} at #{app_url}"}
65
- formatter.long_operation_running("#{plugin_name_sym}\r")
65
+ RestParameters.instance.spinner_cb.call(plugin_name_sym.to_s)
66
66
  detection_info = plugin_klass.detect(app_url)
67
67
  rescue OpenSSL::SSL::SSLError => e
68
68
  Log.log.warn(e.message)
@@ -78,6 +78,7 @@ module Aspera
78
78
  # If there is a redirect, then the detector can override the url.
79
79
  found_apps.push({product: plugin_name_sym, name: app_name, url: app_url, version: 'unknown'}.merge(detection_info))
80
80
  end
81
+ RestParameters.instance.spinner_cb.call(action: :success)
81
82
  raise "No known application found at #{app_url}" if found_apps.empty?
82
83
  Aspera.assert(found_apps.all?{ |a| a.keys.all?(Symbol)})
83
84
  return found_apps
data/lib/aspera/colors.rb CHANGED
@@ -58,6 +58,13 @@ class String
58
58
  end
59
59
  end
60
60
 
61
+ # Applies the provided list of string decoration (colors).
62
+ # @param colors [Array<Symbol>] List of decorations.
63
+ # @return [String] Enhanced String.
64
+ def apply(*colors)
65
+ colors.reduce(self){ |s, c| s.public_send(c)}
66
+ end
67
+
61
68
  # Transform capitalized to snake case
62
69
  def capital_to_snake
63
70
  return gsub(/([a-z\d])([A-Z])/, '\1_\2')
@@ -52,9 +52,9 @@ module Aspera
52
52
  operation: 'POST',
53
53
  subpath: "packages/#{package['id']}/transfer_spec/upload",
54
54
  query: {transfer_type: Api::Faspex::TRANSFER_CONNECT},
55
- content_type: Rest::MIME_JSON,
55
+ content_type: Mime::JSON,
56
56
  body: {paths: [{'destination'=>'/'}]},
57
- headers: {'Accept' => Rest::MIME_JSON}
57
+ headers: {'Accept' => Mime::JSON}
58
58
  )
59
59
  transfer_spec.delete('authentication')
60
60
  # but we place it in a Faspex package creation response
@@ -82,18 +82,18 @@ module Aspera
82
82
  end
83
83
  Log.log.debug{"faspex_package_create_result=#{faspex_package_create_result}"}
84
84
  response.status = 200
85
- response.content_type = Rest::MIME_JSON
85
+ response.content_type = Mime::JSON
86
86
  response.body = JSON.generate(faspex_package_create_result)
87
87
  rescue => e
88
88
  response.status = 500
89
- response['Content-Type'] = Rest::MIME_JSON
89
+ response['Content-Type'] = Mime::JSON
90
90
  response.body = {error: e.message, stacktrace: e.backtrace}.to_json
91
91
  Log.log.error(e.message)
92
92
  Log.log.debug{e.backtrace.join("\n")}
93
93
  end
94
94
  else
95
95
  response.status = 400
96
- response['Content-Type'] = Rest::MIME_JSON
96
+ response['Content-Type'] = Mime::JSON
97
97
  response.body = {error: 'Unsupported endpoint'}.to_json
98
98
  end
99
99
  end
@@ -32,13 +32,13 @@ module Aspera
32
32
  # Only accept requests on the root
33
33
  if !request.path.start_with?(@parameters[:root])
34
34
  response.status = 400
35
- response['Content-Type'] = Rest::MIME_JSON
35
+ response['Content-Type'] = Mime::JSON
36
36
  response.body = {status: 'error', message: 'Request outside domain'}.to_json
37
37
  return
38
38
  end
39
39
  if request.body.nil?
40
40
  response.status = 400
41
- response['Content-Type'] = Rest::MIME_JSON
41
+ response['Content-Type'] = Mime::JSON
42
42
  response.body = {status: 'error', message: 'Empty request'}.to_json
43
43
  return
44
44
  end
@@ -65,7 +65,7 @@ module Aspera
65
65
  raise "script #{script_path} failed with code #{process_status.exitstatus}" if !process_status.success? && @parameters[:fail_on_error]
66
66
  end
67
67
  response.status = 200
68
- response.content_type = Rest::MIME_JSON
68
+ response.content_type = Mime::JSON
69
69
  response.body = JSON.generate({status: 'success', script: script_path, exit_code: process_status.exitstatus})
70
70
  Log.log.debug{'Script executed successfully'}
71
71
  rescue => e
@@ -76,7 +76,7 @@ module Aspera
76
76
  Log.log.error("Killed process: #{post_proc_pid}")
77
77
  end
78
78
  response.status = 500
79
- response['Content-Type'] = Rest::MIME_JSON
79
+ response['Content-Type'] = Mime::JSON
80
80
  response.body = {status: 'error', script: script_path, message: e.message}.to_json
81
81
  end
82
82
  end