aspera-cli 4.18.0 → 4.18.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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'}