aspera-cli 4.18.0 → 4.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,11 +5,12 @@ require 'aspera/cli/basic_auth_plugin'
5
5
  require 'aspera/cli/plugins/node'
6
6
  require 'aspera/cli/plugins/config'
7
7
  require 'aspera/cli/extended_value'
8
+ require 'aspera/cli/special_values'
8
9
  require 'aspera/cli/transfer_agent'
9
10
  require 'aspera/transfer/uri'
10
11
  require 'aspera/transfer/spec'
11
12
  require 'aspera/persistency_action_once'
12
- require 'aspera/open_application'
13
+ require 'aspera/environment'
13
14
  require 'aspera/nagios'
14
15
  require 'aspera/id_generator'
15
16
  require 'aspera/log'
@@ -353,9 +354,9 @@ module Aspera
353
354
  delivery_id = instance_identifier
354
355
  raise 'empty id' if delivery_id.empty?
355
356
  recipient = options.get_option(:recipient)
356
- if delivery_id.eql?(ExtendedValue::ALL)
357
+ if delivery_id.eql?(SpecialValues::ALL)
357
358
  pkg_id_uri = mailbox_filtered_entries.map{|i|{id: i[PACKAGE_MATCH_FIELD], uri: self.class.get_fasp_uri_from_entry(i, raise_no_link: false)}}
358
- elsif delivery_id.eql?(ExtendedValue::INIT)
359
+ elsif delivery_id.eql?(SpecialValues::INIT)
359
360
  Aspera.assert(skip_ids_persistency){'Only with option once_only'}
360
361
  skip_ids_persistency.data.clear.concat(mailbox_filtered_entries.map{|i|{id: i[PACKAGE_MATCH_FIELD]}})
361
362
  skip_ids_persistency.save
@@ -390,7 +391,7 @@ module Aspera
390
391
  headers: {'Accept' => 'application/xml'},
391
392
  query: {passcode: link_data[:query]['passcode']})
392
393
  if !package_creation_data[:http].body.start_with?('<?xml ')
393
- OpenApplication.instance.uri(link_url)
394
+ Environment.instance.open_uri(link_url)
394
395
  raise Cli::Error, 'Unexpected response: package not found ?'
395
396
  end
396
397
  package_entry = XmlSimple.xml_in(package_creation_data[:http].body, {'ForceArray' => false})
@@ -4,6 +4,7 @@
4
4
 
5
5
  require 'aspera/cli/basic_auth_plugin'
6
6
  require 'aspera/cli/extended_value'
7
+ require 'aspera/cli/special_values'
7
8
  require 'aspera/persistency_action_once'
8
9
  require 'aspera/id_generator'
9
10
  require 'aspera/nagios'
@@ -81,7 +82,7 @@ module Aspera
81
82
  if options.get_option(:client_id).nil? || options.get_option(:client_secret).nil?
82
83
  formatter.display_status('Ask the ascli client id and secret to your Administrator.'.red)
83
84
  formatter.display_status("Admin should login to: #{instance_url}")
84
- OpenApplication.instance.uri(instance_url)
85
+ Environment.instance.open_uri(instance_url)
85
86
  formatter.display_status('Navigate to: 𓃑 → Admin → Configurations → API clients')
86
87
  formatter.display_status('Create an API client with:')
87
88
  formatter.display_status('- name: ascli')
@@ -118,7 +119,7 @@ module Aspera
118
119
  options.declare(:auth, 'OAuth type of authentication', values: STD_AUTH_TYPES, default: :jwt)
119
120
  options.declare(:private_key, 'OAuth JWT RSA private key PEM value (prefix file path with @file:)')
120
121
  options.declare(:passphrase, 'OAuth JWT RSA private key passphrase')
121
- options.declare(:box, "Package inbox, either shared inbox name or one of: #{API_LIST_MAILBOX_TYPES.join(', ')} or #{ExtendedValue::ALL}", default: 'inbox')
122
+ options.declare(:box, "Package inbox, either shared inbox name or one of: #{API_LIST_MAILBOX_TYPES.join(', ')} or #{SpecialValues::ALL}", default: 'inbox')
122
123
  options.declare(:shared_folder, 'Send package with files from shared folder')
123
124
  options.declare(:group_type, 'Type of shared box', values: %i[shared_inboxes workgroups], default: :shared_inboxes)
124
125
  options.parse_options!
@@ -304,12 +305,12 @@ module Aspera
304
305
 
305
306
  # list all packages with optional filter
306
307
  def list_packages_with_filter(query: {})
307
- filter = options.get_next_argument('filter', mandatory: false, type: Proc, default: ->(_x){true})
308
+ filter = options.get_next_argument('filter', mandatory: false, validation: Proc, default: ->(_x){true})
308
309
  # translate box name to API prefix (with ending slash)
309
310
  box = options.get_option(:box)
310
311
  real_path =
311
312
  case box
312
- when ExtendedValue::ALL then 'packages' # only admin can list all packages globally
313
+ when SpecialValues::ALL then 'packages' # only admin can list all packages globally
313
314
  when *API_LIST_MAILBOX_TYPES then "#{box}/packages"
314
315
  else
315
316
  group_type = options.get_option(:group_type)
@@ -338,12 +339,12 @@ module Aspera
338
339
  end
339
340
  packages = []
340
341
  case package_ids
341
- when ExtendedValue::INIT
342
+ when SpecialValues::INIT
342
343
  Aspera.assert(skip_ids_persistency){'Only with option once_only'}
343
344
  skip_ids_persistency.data.clear.concat(list_packages_with_filter.map{|p|p['id']})
344
345
  skip_ids_persistency.save
345
346
  return Main.result_status("Initialized skip for #{skip_ids_persistency.data.count} package(s)")
346
- when ExtendedValue::ALL
347
+ when SpecialValues::ALL
347
348
  # TODO: if packages have same name, they will overwrite ?
348
349
  packages = list_packages_with_filter(query: {'status' => 'completed'})
349
350
  Log.log.trace1{Log.dump(:package_ids, packages.map{|p|p['id']})}
@@ -562,10 +563,12 @@ module Aspera
562
563
  res_command = options.get_next_command(available_commands)
563
564
  case res_command
564
565
  when *Plugin::ALL_OPS
565
- return entity_command(res_command, adm_api, res_path, item_list_key: list_key, display_fields: display_fields, id_as_arg: id_as_arg, delete_style: delete_style) do |field, value|
566
- lookup_entity_by_field(
567
- type: res_type, value: value, field: field, real_path: res_path, item_list_key: list_key, query: res_id_query)['id']
568
- end
566
+ return entity_command(
567
+ res_command, adm_api, res_path, item_list_key: list_key, display_fields: display_fields, id_as_arg: id_as_arg,
568
+ delete_style: delete_style) do |field, value|
569
+ lookup_entity_by_field(
570
+ type: res_type, value: value, field: field, real_path: res_path, item_list_key: list_key, query: res_id_query)['id']
571
+ end
569
572
  when :shared_folders
570
573
  node_id = instance_identifier do |field, value|
571
574
  lookup_entity_by_field(type: res_type, field: field, value: value)['id']
@@ -626,7 +629,7 @@ module Aspera
626
629
  user
627
630
  end
628
631
  end
629
- access = options.get_next_argument('level', mandatory: false, expected: %i[submit_only standard shared_inbox_admin], default: :standard)
632
+ access = options.get_next_argument('level', mandatory: false, accept_list: %i[submit_only standard shared_inbox_admin], default: :standard)
630
633
  # TODO: unshift to command line parameters instead of using deprecated option "value"
631
634
  options.set_option(:value, {user: users.map{|u|{id: u, access: access}}})
632
635
  end
@@ -644,7 +647,7 @@ module Aspera
644
647
  command = options.get_next_command(%i[configuration smtp resource events clean_deleted].concat(ADMIN_RESOURCES).freeze)
645
648
  case command
646
649
  when :resource
647
- # resource is will be deprecated
650
+ # resource will be deprecated
648
651
  Log.log.warn('resource command is deprecated (4.18), directly use the specific command instead')
649
652
  return execute_resource(options.get_next_command(ADMIN_RESOURCES))
650
653
  when *ADMIN_RESOURCES
@@ -722,7 +725,7 @@ module Aspera
722
725
  when :show
723
726
  return { type: :single_object, data: @api_v5.read('account/preferences')[:data] }
724
727
  when :modify
725
- @api_v5.update('account/preferences', options.get_next_argument('modified parameters', type: Hash))
728
+ @api_v5.update('account/preferences', options.get_next_argument('modified parameters', validation: Hash))
726
729
  return Main.result_status('modified')
727
730
  end
728
731
  end
@@ -3,6 +3,7 @@
3
3
  # cspell:ignore snid fnid bidi ssync asyncs rund asnodeadmin mkfile mklink asperabrowser asperabrowserurl watchfolders watchfolderd entsrv
4
4
  require 'aspera/cli/basic_auth_plugin'
5
5
  require 'aspera/cli/sync_actions'
6
+ require 'aspera/cli/special_values'
6
7
  require 'aspera/transfer/spec'
7
8
  require 'aspera/nagios'
8
9
  require 'aspera/hash_ext'
@@ -162,7 +163,7 @@ module Aspera
162
163
  # translates paths results into CLI result, and removes prefix
163
164
  def c_result_translate_rem_prefix(response, type, success_msg, path_prefix)
164
165
  errors = []
165
- final_result = { data: [], type: :object_list, fields: [type, 'result']}
166
+ final_result = {type: :object_list, data: [], fields: [type, 'result']}
166
167
  JSON.parse(response[:http].body)['paths'].each do |p|
167
168
  result = success_msg
168
169
  if p.key?('error')
@@ -179,20 +180,11 @@ module Aspera
179
180
  return c_result_remove_prefix_path(final_result, type, path_prefix)
180
181
  end
181
182
 
182
- # get path arguments from command line, and add prefix
183
- def get_next_arg_add_prefix(path_prefix, name, number=:single)
184
- path_or_list = options.get_next_argument(name, expected: number)
185
- return path_or_list if path_prefix.nil?
186
- return File.join(path_prefix, path_or_list) if path_or_list.is_a?(String)
187
- return path_or_list.map {|p| File.join(path_prefix, p)} if path_or_list.is_a?(Array)
188
- raise StandardError, 'expect: nil, String or Array'
189
- end
190
-
191
183
  # directory: node, container: shares
192
184
  FOLDER_TYPE = %w[directory container].freeze
193
185
 
194
186
  def browse_gen3(prefix_path)
195
- folders_to_process = [get_next_arg_add_prefix(prefix_path, 'path')]
187
+ folders_to_process = [get_one_argument_with_prefix(prefix_path, 'path')]
196
188
  query = options.get_option(:query, default: {})
197
189
  # special parameter: max number of entries in result
198
190
  max_items = query.delete('max')
@@ -254,11 +246,12 @@ module Aspera
254
246
  def execute_command_gen3(command, prefix_path)
255
247
  case command
256
248
  when :delete
257
- paths_to_delete = get_next_arg_add_prefix(prefix_path, 'file list', :multiple)
249
+ # TODO: add query for recursive
250
+ paths_to_delete = get_all_arguments_with_prefix(prefix_path, 'file list')
258
251
  resp = @api_node.create('files/delete', { paths: paths_to_delete.map{|i| {'path' => i.start_with?('/') ? i : "/#{i}"} }})
259
252
  return c_result_translate_rem_prefix(resp, 'file', 'deleted', prefix_path)
260
253
  when :search
261
- search_root = get_next_arg_add_prefix(prefix_path, 'search root')
254
+ search_root = get_one_argument_with_prefix(prefix_path, 'search root')
262
255
  parameters = {'path' => search_root}
263
256
  other_options = query_option
264
257
  parameters.merge!(other_options) unless other_options.nil?
@@ -270,31 +263,30 @@ module Aspera
270
263
  formatter.display_status("params: #{resp[:data]['parameters'].keys.map{|k|"#{k}:#{resp[:data]['parameters'][k]}"}.join(',')}")
271
264
  return c_result_remove_prefix_path(result, 'path', prefix_path)
272
265
  when :space
273
- path_list = get_next_arg_add_prefix(prefix_path, 'folder path or ext.val. list')
274
- path_list = [path_list] unless path_list.is_a?(Array)
266
+ path_list = get_all_arguments_with_prefix(prefix_path, 'folder path or ext.val. list')
275
267
  resp = @api_node.create('space', { 'paths' => path_list.map {|i| { path: i} } })
276
- result = { data: resp[:data]['paths'], type: :object_list}
268
+ result = { type: :object_list, data: resp[:data]['paths']}
277
269
  # return c_result_translate_rem_prefix(resp,'folder','created',prefix_path)
278
270
  return c_result_remove_prefix_path(result, 'path', prefix_path)
279
271
  when :mkdir
280
- path_list = get_next_arg_add_prefix(prefix_path, 'folder path or ext.val. list')
281
- path_list = [path_list] unless path_list.is_a?(Array)
282
- resp = @api_node.create('files/create', { 'paths' => [{ type: :directory, path: path_list }] })
272
+ path_list = get_all_arguments_with_prefix(prefix_path, 'folder path or ext.val. list')
273
+ resp = @api_node.create('files/create', { 'paths' => path_list.map{|i|{ type: :directory, path: i }}})
283
274
  return c_result_translate_rem_prefix(resp, 'folder', 'created', prefix_path)
284
275
  when :mklink
285
- target = get_next_arg_add_prefix(prefix_path, 'target')
286
- path_list = get_next_arg_add_prefix(prefix_path, 'link path')
287
- resp = @api_node.create('files/create', { 'paths' => [{ type: :symbolic_link, path: path_list, target: { path: target} }] })
276
+ target = get_one_argument_with_prefix(prefix_path, 'target')
277
+ one_path = get_one_argument_with_prefix(prefix_path, 'link path')
278
+ resp = @api_node.create('files/create', { 'paths' => [{ type: :symbolic_link, path: one_path, target: { path: target} }] })
288
279
  return c_result_translate_rem_prefix(resp, 'folder', 'created', prefix_path)
289
280
  when :mkfile
290
- path_list = get_next_arg_add_prefix(prefix_path, 'file path')
281
+ one_path = get_one_argument_with_prefix(prefix_path, 'file path')
291
282
  contents64 = Base64.strict_encode64(options.get_next_argument('contents'))
292
- resp = @api_node.create('files/create', { 'paths' => [{ type: :file, path: path_list, contents: contents64 }] })
283
+ resp = @api_node.create('files/create', { 'paths' => [{ type: :file, path: one_path, contents: contents64 }] })
293
284
  return c_result_translate_rem_prefix(resp, 'folder', 'created', prefix_path)
294
285
  when :rename
295
- path_base = get_next_arg_add_prefix(prefix_path, 'path_base')
296
- path_src = get_next_arg_add_prefix(prefix_path, 'path_src')
297
- path_dst = get_next_arg_add_prefix(prefix_path, 'path_dst')
286
+ # TODO: multiple ?
287
+ path_base = get_one_argument_with_prefix(prefix_path, 'path_base')
288
+ path_src = get_one_argument_with_prefix(prefix_path, 'path_src')
289
+ path_dst = get_one_argument_with_prefix(prefix_path, 'path_dst')
298
290
  resp = @api_node.create('files/rename', { 'paths' => [{ 'path' => path_base, 'source' => path_src, 'destination' => path_dst }] })
299
291
  return c_result_translate_rem_prefix(resp, 'entry', 'moved', prefix_path)
300
292
  when :browse
@@ -346,7 +338,7 @@ module Aspera
346
338
  transfer_spec.delete('paths') if command.eql?(:upload)
347
339
  return Main.result_transfer(transfer.start(transfer_spec))
348
340
  when :http_node_download
349
- remote_path = get_next_arg_add_prefix(prefix_path, 'remote path')
341
+ remote_path = get_one_argument_with_prefix(prefix_path, 'remote path')
350
342
  file_name = File.basename(remote_path)
351
343
  @api_node.call(
352
344
  operation: 'GET',
@@ -390,7 +382,7 @@ module Aspera
390
382
  when :set_bearer_key
391
383
  access_key_id = options.get_next_argument('access key id')
392
384
  access_key_id = @api_node.read('access_keys/self')[:data]['id'] if access_key_id.eql?('self')
393
- bearer_key_pem = options.get_next_argument('public or private RSA key PEM value', type: String)
385
+ bearer_key_pem = options.get_next_argument('public or private RSA key PEM value', validation: String)
394
386
  key = OpenSSL::PKey.read(bearer_key_pem)
395
387
  key = key.public_key if key.private?
396
388
  bearer_key_pem = key.to_pem
@@ -581,7 +573,7 @@ module Aspera
581
573
  return {type: :single_object, data: items}
582
574
  when :modify
583
575
  apifid = apifid_from_next_arg(top_file_id)
584
- update_param = options.get_next_argument('update data', type: Hash)
576
+ update_param = options.get_next_argument('update data', validation: Hash)
585
577
  apifid[:api].update("files/#{apifid[:file_id]}", update_param)[:data]
586
578
  return Main.result_status('Done')
587
579
  when :thumbnail
@@ -612,7 +604,7 @@ module Aspera
612
604
  {'id' => one_id}
613
605
  end
614
606
  when :create
615
- create_param = options.get_next_argument('creation data', type: Hash)
607
+ create_param = options.get_next_argument('creation data', validation: Hash)
616
608
  raise 'no file_id' if create_param.key?('file_id')
617
609
  create_param['file_id'] = apifid[:file_id]
618
610
  create_param['access_levels'] = Api::Node::ACCESS_LEVELS unless create_param.key?('access_levels')
@@ -638,7 +630,7 @@ module Aspera
638
630
  async_name = options.get_option(:sync_name)
639
631
  if async_name.nil?
640
632
  async_id = instance_identifier
641
- if async_id.eql?(ExtendedValue::ALL) && %i[show delete].include?(command)
633
+ if async_id.eql?(SpecialValues::ALL) && %i[show delete].include?(command)
642
634
  async_ids = @api_node.read('async/list')[:data]['sync_ids']
643
635
  else
644
636
  Integer(async_id) # must be integer
@@ -661,7 +653,7 @@ module Aspera
661
653
  when :show
662
654
  resp = @api_node.create('async/summary', post_data)[:data]['sync_summaries']
663
655
  return Main.result_empty if resp.empty?
664
- return { type: :object_list, data: resp, fields: %w[snid name local_dir remote_dir] } if async_id.eql?(ExtendedValue::ALL)
656
+ return { type: :object_list, data: resp, fields: %w[snid name local_dir remote_dir] } if async_id.eql?(SpecialValues::ALL)
665
657
  return { type: :single_object, data: resp.first }
666
658
  when :delete
667
659
  resp = @api_node.create('async/delete', post_data)[:data]
@@ -815,7 +807,7 @@ module Aspera
815
807
  resp = @api_node.read(one_res_path)
816
808
  return { type: :other_struct, data: resp[:data] }
817
809
  when :modify
818
- resp = @api_node.update(one_res_path, options.get_next_argument('update value', type: Hash))
810
+ resp = @api_node.update(one_res_path, options.get_next_argument('update value', validation: Hash))
819
811
  return { type: :other_struct, data: resp[:data] }
820
812
  when :bandwidth_average
821
813
  transfers_data = @api_node.read(res_class_path, query_read_delete)[:data]
@@ -876,7 +868,7 @@ module Aspera
876
868
  return { type: :object_list, data: resp[:data]['services'] }
877
869
  when :create
878
870
  # @json:'{"type":"WATCHFOLDERD","run_as":{"user":"user1"}}'
879
- params = options.get_next_argument('Run creation data (structure)')
871
+ params = options.get_next_argument('creation data', validation: Hash)
880
872
  resp = @api_node.create('rund/services', params)
881
873
  return Main.result_status("#{resp[:data]['id']} created")
882
874
  when :delete
@@ -952,13 +944,13 @@ module Aspera
952
944
  }
953
945
  # encode parameters so that it looks good in url
954
946
  encoded_params = Base64.strict_encode64(Zlib::Deflate.deflate(JSON.generate(browse_params))).gsub(/=+$/, '').tr('+/', '-_').reverse
955
- OpenApplication.instance.uri("#{options.get_option(:asperabrowserurl)}?goto=#{encoded_params}")
947
+ Environment.instance.open_uri("#{options.get_option(:asperabrowserurl)}?goto=#{encoded_params}")
956
948
  return Main.result_status('done')
957
949
  when :basic_token
958
950
  return Main.result_status(Rest.basic_token(options.get_option(:username, mandatory: true), options.get_option(:password, mandatory: true)))
959
951
  when :bearer_token
960
- private_key = OpenSSL::PKey::RSA.new(options.get_next_argument('private RSA key PEM value', type: String))
961
- token_info = options.get_next_argument('user and group identification', type: Hash)
952
+ private_key = OpenSSL::PKey::RSA.new(options.get_next_argument('private RSA key PEM value', validation: String))
953
+ token_info = options.get_next_argument('user and group identification', validation: Hash)
962
954
  access_key = options.get_option(:username, mandatory: true)
963
955
  return Main.result_status(Api::Node.bearer_token(payload: token_info, access_key: access_key, private_key: private_key))
964
956
  when :simulator
@@ -974,6 +966,22 @@ module Aspera
974
966
  end
975
967
  raise 'ERROR: shall not reach this line'
976
968
  end
969
+
970
+ private
971
+
972
+ # get remaining path arguments from command line, and add prefix
973
+ def get_all_arguments_with_prefix(path_prefix, name)
974
+ path_args = options.get_next_argument(name, multiple: true)
975
+ return path_args if path_prefix.nil?
976
+ return path_args.map {|p| File.join(path_prefix, p)}
977
+ end
978
+
979
+ # get next path argument from command line, and add prefix
980
+ def get_one_argument_with_prefix(path_prefix, name)
981
+ path_arg = options.get_next_argument(name, validation: String)
982
+ return path_arg if path_prefix.nil?
983
+ return File.join(path_prefix, path_arg)
984
+ end
977
985
  end
978
986
  end
979
987
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'aspera/cli/basic_auth_plugin'
4
+ require 'aspera/cli/special_values'
4
5
  require 'aspera/nagios'
5
6
  require 'aspera/log'
6
7
  require 'aspera/assert'
@@ -150,7 +151,7 @@ module Aspera
150
151
  end
151
152
  case command
152
153
  when :status
153
- wf_id = nil if wf_id.eql?(ExtendedValue::ALL)
154
+ wf_id = nil if wf_id.eql?(SpecialValues::ALL)
154
155
  result = call_ao('workflows_status', id: wf_id)[:data]
155
156
  return {type: :object_list, data: result['workflows']['workflow']}
156
157
  when :list
@@ -176,7 +177,7 @@ module Aspera
176
177
  }
177
178
  call_params = {format: :json}
178
179
  # get external parameters if any
179
- options.get_next_argument('external_parameters', mandatory: false, type: Hash, default: {}).each do |name, value|
180
+ options.get_next_argument('external_parameters', mandatory: false, validation: Hash, default: {}).each do |name, value|
180
181
  call_params["external_parameters[#{name}]"] = value
181
182
  end
182
183
  # synchronous call ?
@@ -489,7 +489,7 @@ module Aspera
489
489
  return Main.result_status('Tools validated')
490
490
  when :test, :show
491
491
  source = options.get_next_argument('source file')
492
- format = options.get_next_argument('format', expected: Aspera::Preview::Generator::PREVIEW_FORMATS, default: :png)
492
+ format = options.get_next_argument('format', accept_list: Aspera::Preview::Generator::PREVIEW_FORMATS, default: :png)
493
493
  generated_file_path = preview_filename(format, options.get_option(:base))
494
494
  g = Aspera::Preview::Generator.new(source, generated_file_path, @gen_options, @tmp_folder, nil)
495
495
  g.generate
@@ -232,7 +232,7 @@ module Aspera
232
232
  when *TRANSFER_COMMANDS
233
233
  return execute_transfer(command, server_transfer_spec)
234
234
  when *AsCmd::OPERATIONS
235
- command_arguments = options.get_next_argument('ascmd command arguments', expected: :multiple, mandatory: false)
235
+ command_arguments = options.get_next_argument('ascmd command arguments', multiple: true, mandatory: false)
236
236
  ascmd = AsCmd.new(ascmd_executor)
237
237
  begin
238
238
  result = ascmd.execute_single(command, command_arguments)
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aspera
4
+ module Cli
5
+ # base class for plugins modules
6
+ module SpecialValues
7
+ # special values
8
+ INIT = 'INIT'
9
+ ALL = 'ALL'
10
+ DEF = 'DEF'
11
+ end
12
+ end
13
+ end
@@ -29,9 +29,9 @@ module Aspera
29
29
  SIMPLE_ARGUMENTS_SYNC.each do |arg, check|
30
30
  value = options.get_next_argument(
31
31
  arg,
32
- type: check.is_a?(Class) ? check : nil,
33
- expected: check.is_a?(Class) ? :single : check,
34
- mandatory: false)
32
+ mandatory: false,
33
+ validation: check.is_a?(Class) ? check : nil,
34
+ accept_list: check.is_a?(Class) ? nil : check)
35
35
  break if value.nil?
36
36
  simple_session_args[arg] = value.to_s
37
37
  end
@@ -57,7 +57,7 @@ module Aspera
57
57
  command2 = options.get_next_command([:status])
58
58
  case command2
59
59
  when :status
60
- sync_session_name = options.get_next_argument('name of sync session', mandatory: false, type: String)
60
+ sync_session_name = options.get_next_argument('name of sync session', mandatory: false, validation: String)
61
61
  async_params = options.get_option(:sync_info, mandatory: true)
62
62
  return {type: :single_object, data: Transfer::Sync.admin_status(async_params, sync_session_name)}
63
63
  end
@@ -175,7 +175,7 @@ module Aspera
175
175
  when nil, FILE_LIST_FROM_ARGS
176
176
  Log.log.debug('getting file list as parameters')
177
177
  # get remaining arguments
178
- file_list = @opt_mgr.get_next_argument('source file list', expected: :multiple)
178
+ file_list = @opt_mgr.get_next_argument('source file list', multiple: true)
179
179
  raise Cli::BadArgument, 'specify at least one file on command line or use ' \
180
180
  "--sources=#{FILE_LIST_FROM_TRANSFER_SPEC} to use transfer spec" if !file_list.is_a?(Array) || file_list.empty?
181
181
  when FILE_LIST_FROM_TRANSFER_SPEC
@@ -244,7 +244,7 @@ module Aspera
244
244
  updated_ts(transfer_spec)
245
245
  # if TS from app has content_protection (e.g. F5), that means content is protected: ask password if not provided
246
246
  if transfer_spec['content_protection'].eql?('decrypt') && !transfer_spec.key?('content_protection_password')
247
- transfer_spec['content_protection_password'] = @opt_mgr.prompt_user_input('content protection password', true)
247
+ transfer_spec['content_protection_password'] = @opt_mgr.prompt_user_input('content protection password', sensitive: true)
248
248
  end
249
249
  # create transfer agent
250
250
  agent_instance.start_transfer(transfer_spec, token_regenerator: rest_token)
@@ -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.18.0'
7
+ VERSION = '4.18.1'
8
8
  end
9
9
  end
@@ -4,17 +4,21 @@
4
4
  require 'aspera/log'
5
5
  require 'aspera/assert'
6
6
  require 'rbconfig'
7
+ require 'singleton'
7
8
 
8
9
  # cspell:words MEBI mswin bccwin
9
10
 
10
11
  module Aspera
11
12
  # detect OS, architecture, and specific stuff
12
13
  class Environment
14
+ include Singleton
15
+ USER_INTERFACES = %i[text graphical].freeze
16
+
13
17
  OS_WINDOWS = :windows
14
- OS_X = :osx
18
+ OS_MACOS = :osx
15
19
  OS_LINUX = :linux
16
20
  OS_AIX = :aix
17
- OS_LIST = [OS_WINDOWS, OS_X, OS_LINUX, OS_AIX].freeze
21
+ OS_LIST = [OS_WINDOWS, OS_MACOS, OS_LINUX, OS_AIX].freeze
18
22
  CPU_X86_64 = :x86_64
19
23
  CPU_ARM64 = :arm64
20
24
  CPU_PPC64 = :ppc64
@@ -37,7 +41,7 @@ module Aspera
37
41
  when /mswin/, /msys/, /mingw/, /cygwin/, /bccwin/, /wince/, /emc/
38
42
  return OS_WINDOWS
39
43
  when /darwin/, /mac os/
40
- return OS_X
44
+ return OS_MACOS
41
45
  when /linux/
42
46
  return OS_LINUX
43
47
  when /aix/
@@ -122,10 +126,66 @@ module Aspera
122
126
  end
123
127
 
124
128
  # @return true if we can display Unicode characters
129
+ # https://www.gnu.org/software/libc/manual/html_node/Locale-Categories.html
130
+ # https://pubs.opengroup.org/onlinepubs/7908799/xbd/envvar.html
125
131
  def terminal_supports_unicode?
126
- @terminal_supports_unicode = terminal? && ENV.values_at('LC_ALL', 'LC_CTYPE', 'LANG').compact.first.include?('UTF-8') if @terminal_supports_unicode.nil?
132
+ @terminal_supports_unicode = terminal? && %w(LC_ALL LC_CTYPE LANG).any?{|var|ENV[var]&.include?('UTF-8')} if @terminal_supports_unicode.nil?
127
133
  return @terminal_supports_unicode
128
134
  end
135
+
136
+ def default_gui_mode
137
+ # assume not remotely connected on macos and windows
138
+ return :graphical if [Environment::OS_WINDOWS, Environment::OS_MACOS].include?(Environment.os)
139
+ # unix family
140
+ return :graphical if ENV.key?('DISPLAY') && !ENV['DISPLAY'].empty?
141
+ return :text
142
+ end
143
+
144
+ # command must be non blocking
145
+ def open_uri_graphical(uri)
146
+ case Environment.os
147
+ when Environment::OS_MACOS then return system('open', uri.to_s)
148
+ when Environment::OS_WINDOWS then return system('start', 'explorer', %Q{"#{uri}"})
149
+ when Environment::OS_LINUX then return system('xdg-open', uri.to_s)
150
+ else
151
+ raise "no graphical open method for #{Environment.os}"
152
+ end
153
+ end
154
+
155
+ def open_editor(file_path)
156
+ if ENV.key?('EDITOR')
157
+ system(ENV['EDITOR'], file_path.to_s)
158
+ elsif Environment.os.eql?(Environment::OS_WINDOWS)
159
+ system('notepad.exe', %Q{"#{file_path}"})
160
+ else
161
+ open_uri_graphical(file_path.to_s)
162
+ end
163
+ end
164
+ end
165
+ attr_accessor :url_method
166
+
167
+ def initialize
168
+ @url_method = self.class.default_gui_mode
169
+ end
170
+
171
+ # Allows a user to open a Url
172
+ # if method is "text", then URL is displayed on terminal
173
+ # if method is "graphical", then the URL will be opened with the default browser.
174
+ # this is non blocking
175
+ def open_uri(the_url)
176
+ case @url_method
177
+ when :graphical
178
+ self.class.open_uri_graphical(the_url)
179
+ when :text
180
+ case the_url.to_s
181
+ when /^http/
182
+ puts "USER ACTION: please enter this url in a browser:\n#{the_url.to_s.red}\n"
183
+ else
184
+ puts "USER ACTION: open this:\n#{the_url.to_s.red}\n"
185
+ end
186
+ else
187
+ raise StandardError, "unsupported url open method: #{@url_method}"
188
+ end
129
189
  end
130
190
  end
131
191
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'aspera/oauth/base'
4
- require 'aspera/open_application'
4
+ require 'aspera/environment'
5
5
  require 'aspera/web_auth'
6
6
  require 'aspera/assert'
7
7
  module Aspera
@@ -34,7 +34,7 @@ module Aspera
34
34
  # start a web server to receive request code
35
35
  web_server = WebAuth.new(@redirect_uri)
36
36
  # start browser on login page
37
- OpenApplication.instance.uri(login_page_url)
37
+ Environment.instance.open_uri(login_page_url)
38
38
  # wait for code in request
39
39
  received_params = web_server.received_request
40
40
  Aspera.assert(random_state.eql?(received_params['state'])){'wrong received state'}