Class: Aspera::Cli::Plugins::Config

Inherits:
Base
  • Object
show all
Includes:
AscpActions, GemChecker, Aspera::Cli::PresetActions, SyncActions, TransferActions, VaultManager
Defined in:
lib/aspera/cli/plugins/config.rb

Overview

Manage the CLI config file

Constant Summary

Constants included from Aspera::Cli::PresetActions

Aspera::Cli::PresetActions::CONF_OVERVIEW_KEYS, Aspera::Cli::PresetActions::GLOBAL_DEFAULT_KEYWORD

Constants included from AscpActions

AscpActions::TRANSFERD_APP_NAME

Constants included from GemChecker

GemChecker::GEM_CHECK_DATE_FMT

Constants included from SyncActions

SyncActions::ADMIN_COMMANDS, SyncActions::PATH_AND_INFO_ARGS, SyncActions::STATE_STR

Constants inherited from Base

Base::FILTER_ARGS

Instance Attribute Summary collapse

Attributes inherited from Base

#context, #help_path

Class Method Summary collapse

Instance Method Summary collapse

Methods included from TransferActions

#action_transfer_cleanup, #action_transfer_status

Methods included from Aspera::Cli::PresetActions

#action_preset_ask, #action_preset_delete, #action_preset_get, #action_preset_initialize, #action_preset_lookup, #action_preset_overview, #action_preset_secure, #action_preset_set, #action_preset_show, #action_preset_unset, #action_preset_update

Methods included from AscpActions

#action_agents_list, #action_agents_parameters, #action_agents_show, #action_ascp_errors, #action_ascp_info, #action_ascp_schema, #install_transfer_sdk, #set_sdk_dir

Methods included from GemChecker

#check_gem_version, #periodic_check_newer_gem_version

Methods included from VaultManager

#action_vault_delete, #action_vault_import, #vault, #vault_required, #vault_value

Methods included from SyncActions

#action_sync_admin_file_info, #async_info_from_args, #db_from_args, included, register_sync_admin_commands, #run_sync_transfer

Methods inherited from Base

#action_for, #add_manual_header, application_name, #bulk_result, command, command_registry, commands_under, #config, crud_commands, declare_options, define_action_method, #dispatch_child, #dispatch_from_registry, #dispatch_leaf, #dispatch_mount, #entity_create, #entity_delete, #entity_list, #entity_modify, entity_noun, #entity_res_path, #entity_show, #execute_action, #execute_leaf, file_matcher, #formatter, #generate_help, #http_config, #invoke_action, operation_description, option_sources, option_specs, #options, #persistency, #presets, #progress_bar, #query_read_delete, register_option_spec, #resolve_argument, #resolve_arguments, #transfer, use_options, used_option_sources, #validate_registry

Methods included from OptionDeclarator

#declare_options, #option, #option_specs, #register_option_spec

Constructor Details

#initialize(**_) ⇒ Config

Returns a new instance of Config.



103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/aspera/cli/plugins/config.rb', line 103

def initialize(**_)
  super
  @vault_instance = nil
  @sdk_default_location = false
  # Declare wizard options (Wizard#initialize calls options.declare internally)
  @wizard = Wizard.new(self, context.main_folder)
  # HTTP options: declare metadata (class method), then bind to the instance
  Http.declare_options(options)
  context.http_config.bind_options(options)
  # Values set through `on_set` callbacks are used below (sdk_folder)
  options.parse_options!
  set_sdk_dir
end

Instance Attribute Details

#gem_url ⇒ Object (readonly)

Returns the value of attribute gem_url.



440
441
442
# File 'lib/aspera/cli/plugins/config.rb', line 440

def gem_url
  @gem_url
end

Class Method Details

.deep_clone(val) ⇒ Object

Deep clone hash so that it does not get modified in case of display and secret hide



65
66
67
# File 'lib/aspera/cli/plugins/config.rb', line 65

def deep_clone(val)
  return Marshal.load(Marshal.dump(val))
end

.default_app_main_folder(app_name:) ⇒ String

Returns Product config folder (~/.aspera/).

Returns:

  • (String) —

    Product config folder (~/.aspera/)



77
78
79
80
81
# File 'lib/aspera/cli/plugins/config.rb', line 77

def default_app_main_folder(app_name:)
  Aspera.assert_type(app_name, String)
  Aspera.assert(!app_name.empty?, 'app_name must not be empty')
  return File.join(module_family_folder, app_name)
end

.gem_plugins_folder ⇒ Object

Folder containing plugins in the gem's main folder



53
54
55
# File 'lib/aspera/cli/plugins/config.rb', line 53

def gem_plugins_folder
  File.dirname(File.expand_path(__FILE__))
end

.gem_src_root ⇒ String

Go up as many times as englobing modules (not counting class, as it is a file)

Returns:

  • (String) —

    main folder where code is, i.e. .../lib



59
60
61
62
# File 'lib/aspera/cli/plugins/config.rb', line 59

def gem_src_root
  # Module.nesting[2] is Cli::Plugins
  File.expand_path(Module.nesting[2].to_s.gsub('::', '/').gsub(%r{[^/]+}, '..'), gem_plugins_folder)
end

.module_family_folder ⇒ String

Returns product family folder (~/.aspera).

Returns:

  • (String) —

    product family folder (~/.aspera)



70
71
72
73
74
# File 'lib/aspera/cli/plugins/config.rb', line 70

def module_family_folder
  user_home_folder = Dir.home
  Aspera.assert(Dir.exist?(user_home_folder), type: Cli::Error) { "Home folder does not exist: #{user_home_folder}. Check your user environment." }
  return File.join(user_home_folder, ASPERA_HOME_FOLDER_NAME)
end

Instance Method Details

#action_commands(plugin_name: nil, command_path: nil) ⇒ Object



566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
# File 'lib/aspera/cli/plugins/config.rb', line 566

def action_commands(plugin_name: nil, command_path: nil, **)
  plugin_names = Plugins::Factory.instance.plugin_list
  unless plugin_name.nil?
    Aspera.assert_values(plugin_name.to_sym, plugin_names, type: Cli::BadArgument)
    plugin_names = [plugin_name.to_sym]
  end
  prefix = Array(command_path).map(&:to_sym)
  Aspera.assert(prefix.empty? || plugin_names.length.eql?(1), type: Cli::BadArgument) { 'command path requires a plugin name' }
  expand_mounts = options.get_option(:expand_mounts)
  commands = plugin_names.flat_map do |name|
    reg = Plugins::Factory.instance.plugin_class(name).command_registry
    Aspera.assert(prefix.empty? || reg[prefix], type: Cli::BadArgument) { "no such command: #{name} #{prefix.join(' ')}" }
    paths = prefix.empty? || reg.children_of(prefix).any? ? reg.leaf_paths(prefix, expand_mounts: expand_mounts) : [prefix]
    paths.map do |path|
      mount = expand_mounts ? nil : reg.mount_at(path)
      description = reg[path]&.description.to_s
      if mount
        target = mount.plugin.name.split('::').last.downcase
        description = "#{description} (see: #{command_syntax(target, mount.registry, mount.at)})"
      end
      {
        syntax:      command_syntax(name, reg, path) + (mount ? ' <command...>' : ''),
        description: description
      }
    end
  end
  Result::ObjectList.new(commands, fields: %w[syntax description])
end

#action_completion_words(words: nil) ⇒ Object



625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
# File 'lib/aspera/cli/plugins/config.rb', line 625

def action_completion_words(words: nil, **)
  if words.nil? || words.empty?
    # Level 0: propose plugin names
    Plugins::Factory.instance.plugin_list.each { |p| puts p }
  else
    plugin_sym = words.first.to_sym
    plugin_class = begin
      Plugins::Factory.instance.plugin_class(plugin_sym)
    rescue StandardError
      Process.exit(0)
    end
    # Navigate into the registry using remaining words as path
    path = words[1..].map(&:to_sym)
    plugin_class.command_registry.children_of(path).each_key { |k| puts k }
  end
  Process.exit(0)
end

#action_documentation(location: :github, section: nil) ⇒ Object

DSL handlers - one method per leaf command



465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
# File 'lib/aspera/cli/plugins/config.rb', line 465

def action_documentation(location: :github, section: nil, **)
  case location
  when :github
    section = "##{section}" unless section.nil?
    Environment.instance.open_uri("#{Info::DOC_URL}#{section}")
    return Result::Nothing.new
  when :toc, :local
    require 'aspera/markdown'
    local_doc = File.join(self.class.gem_src_root, '..', 'docs', 'README.md')
    Aspera.assert(File.exist?(local_doc), type: Cli::Error) { "Local documentation not found: #{local_doc}" }
    content = File.read(local_doc)
    if location == :toc
      entries = Markdown.toc(content)
      return Result::ObjectList.new(entries, fields: %w[level title anchor])
    end
    # :local
    if section
      text = Markdown.extract_section(content, section)
      Aspera.assert(!text.nil?, type: Cli::Error) { "Section not found: #{section}" }
      return Result::Text.new(text)
    end
    if Environment.instance.url_method.eql?(:graphical)
      Environment.instance.open_uri("file://#{local_doc}")
      return Result::Nothing.new
    end
    return Result::Text.new(content)
  end
end

#action_download(file_url:, file_dest: nil) ⇒ Object



494
495
496
497
498
499
500
# File 'lib/aspera/cli/plugins/config.rb', line 494

def action_download(file_url:, file_dest: nil, **)
  file_url = file_url.chomp
  file_dest = File.join(transfer.destination_folder(Transfer::Spec::DIRECTION_RECEIVE), file_url.gsub(%r{.*/}, '')) if file_dest.nil?
  Log.log.info("Downloading: #{file_url}")
  Rest::Client.new(base_url: file_url).call(operation: 'GET', save_to: file_dest)
  Result::Status.new("Saved to: #{file_dest}")
end

#action_initdemo ⇒ Object



542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
# File 'lib/aspera/cli/plugins/config.rb', line 542

def action_initdemo(**)
  cp = presets.config_presets
  if cp.key?(DEMO_PRESET)
    Log.log.warn { "Demo server preset already present: #{DEMO_PRESET}" }
  else
    Log.log.info { "Creating Demo server preset: #{DEMO_PRESET}" }
    cp[DEMO_PRESET] = {
      'url'                                    => "ssh://#{DEMO_SERVER}.asperasoft.com:33001",
      'username'                               => ASPERA,
      'ssAP'.downcase.reverse + 'drow'.reverse => DEMO_SERVER + ASPERA # cspell:disable-line
    }
  end
  cp[PresetManager::Key::DEFAULTS] ||= {}
  if cp[PresetManager::Key::DEFAULTS].key?(SERVER_COMMAND)
    Log.log.warn { "Server default preset already set to: #{cp[PresetManager::Key::DEFAULTS][SERVER_COMMAND]}" }
    Log.log.warn { "Use #{DEMO_PRESET} for demo: -P#{DEMO_PRESET}" } unless
      DEMO_PRESET.eql?(cp[PresetManager::Key::DEFAULTS][SERVER_COMMAND])
  else
    cp[PresetManager::Key::DEFAULTS][SERVER_COMMAND] = DEMO_PRESET
    Log.log.info { "Setting server default preset to : #{DEMO_PRESET}" }
  end
  Result::Status.new('Done')
end

#action_options(plugin_name:) ⇒ Object



609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
# File 'lib/aspera/cli/plugins/config.rb', line 609

def action_options(plugin_name:, **)
  # Instantiate the plugin so that it registers all its options in context.options
  Plugins::Factory.instance.create(plugin_name.to_sym, context: context)
  rows = context.options.declared_options.map do |sym, opt|
    row = {
      'option'      => "--#{sym.to_s.tr('_', '-')}",
      'description' => opt.description.to_s
    }
    row['allowed']     = opt.allowed_info unless opt.allowed_info.nil?
    row['deprecated']  = opt.deprecation.last if opt.deprecation
    row['replacement'] = opt.deprecation.message if opt.deprecation
    row
  end
  Result::ObjectList.new(rows, fields: %w[option description allowed deprecated replacement])
end

#action_plugins_create(name:, folder: nil) ⇒ Object



522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
# File 'lib/aspera/cli/plugins/config.rb', line 522

def action_plugins_create(name:, folder: nil, **)
  plugin_name = name.downcase
  destination_folder = folder || File.join(context.main_folder, ASPERA_PLUGINS_FOLDERNAME)
  plugin_file = File.join(destination_folder, "#{plugin_name}.rb")
  content = <<~END_OF_PLUGIN_CODE
    require 'aspera/cli/plugins/base'
    module Aspera
      module Cli
        module Plugins
          class #{plugin_name.snake_to_capital} < Base
            command :example, description: 'example command', action: ->(**) { Result::Status.new('You called plugin #{plugin_name}') }
          end
        end
      end
    end
  END_OF_PLUGIN_CODE
  File.write(plugin_file, content)
  Result::Status.new("Created #{plugin_file}")
end

#action_plugins_list ⇒ Object



509
510
511
512
513
514
515
516
517
518
519
520
# File 'lib/aspera/cli/plugins/config.rb', line 509

def action_plugins_list(**)
  result = Plugins::Factory.instance.plugin_list.map do |name|
    plugin_class = Plugins::Factory.instance.plugin_class(name)
    {
      plugin: name,
      detect: TerminalFormatter.tick(plugin_class.respond_to?(:detect)),
      wizard: TerminalFormatter.tick(plugin_class.method_defined?(:wizard)),
      path:   Plugins::Factory.instance.plugin_source(name)
    }
  end
  Result::ObjectList.new(result, fields: %w[plugin detect wizard path])
end

#action_tokens_show(token_id:) ⇒ Object



502
503
504
505
506
507
# File 'lib/aspera/cli/plugins/config.rb', line 502

def action_tokens_show(token_id:, **)
  require 'aspera/api/node'
  data = OAuth::Factory.instance.get_token_info(token_id)
  Aspera.assert(!data.nil?, type: Cli::Error) { 'Unknown identifier' }
  Result::SingleObject.new(data)
end

#add_plugin_folders(value) ⇒ Object

on_set callback of option plugin_folder

Parameters:

  • value (String, Array<String>) —

    folder(s) where to find plugins



444
445
446
447
448
# File 'lib/aspera/cli/plugins/config.rb', line 444

def add_plugin_folders(value)
  value = [value] unless value.is_a?(Array)
  Aspera.assert_array_all(value, String) { 'plugin folder(s)' }
  value.each { |f| Plugins::Factory.instance.add_lookup_folder(f) }
end

#command_syntax(name, reg, path) ⇒ String

Build syntax by interleaving each path segment with the arguments declared on that node

Parameters:

  • name (String, Symbol) —

    plugin name

  • reg (CommandRegistry) —

    registry of plugin

  • path (Array<Symbol>) —

    command path

Returns:

  • (String) —

    e.g. node access_keys do <access_key_id> ls <path>



600
601
602
603
604
605
606
607
# File 'lib/aspera/cli/plugins/config.rb', line 600

def command_syntax(name, reg, path)
  tokens = [name.to_s]
  path.each_index do |i|
    tokens << path[i].to_s
    reg.arguments_at(path[0, i + 1]).each { |a| tokens << a.syntax }
  end
  tokens.join(' ')
end

#completion_script(shell) ⇒ Object

Display the completion script for the given shell. Raw output, not formatted, so that it can be evaluated by the shell.

Parameters:

  • shell (Symbol) —

    One of COMPLETION_SHELLS



646
647
648
649
# File 'lib/aspera/cli/plugins/config.rb', line 646

def completion_script(shell)
  $stdout.write(File.read(File.join(COMPLETION_FOLDER, "#{Info::CMD_NAME}.#{shell}")))
  Process.exit(0)
end

#load_preset(value) ⇒ Object

on_set callback of option preset

Parameters:

  • value (String, Hash) —

    preset name, or set of option values



452
453
454
455
456
457
458
459
460
461
# File 'lib/aspera/cli/plugins/config.rb', line 452

def load_preset(value)
  case value
  when Hash
    options.add_option_preset(value, 'set')
  when String
    options.add_option_preset(presets.by_name(value), 'set_by_name')
  else
    raise BadArgument, 'Preset definition must be a String for preset name, or Hash for set of values'
  end
end