aspera-cli 4.14.0 → 4.15.0

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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +54 -3
  4. data/CONTRIBUTING.md +7 -7
  5. data/README.md +1457 -880
  6. data/bin/ascli +18 -9
  7. data/bin/asession +12 -14
  8. data/examples/proxy.pac +1 -1
  9. data/lib/aspera/aoc.rb +198 -127
  10. data/lib/aspera/ascmd.rb +24 -14
  11. data/lib/aspera/cli/basic_auth_plugin.rb +9 -6
  12. data/lib/aspera/cli/error.rb +17 -0
  13. data/lib/aspera/cli/extended_value.rb +47 -12
  14. data/lib/aspera/cli/formatter.rb +260 -171
  15. data/lib/aspera/cli/hints.rb +80 -0
  16. data/lib/aspera/cli/main.rb +101 -147
  17. data/lib/aspera/cli/manager.rb +160 -124
  18. data/lib/aspera/cli/plugin.rb +70 -59
  19. data/lib/aspera/cli/plugins/alee.rb +0 -1
  20. data/lib/aspera/cli/plugins/aoc.rb +239 -273
  21. data/lib/aspera/cli/plugins/ats.rb +8 -5
  22. data/lib/aspera/cli/plugins/bss.rb +2 -2
  23. data/lib/aspera/cli/plugins/config.rb +516 -375
  24. data/lib/aspera/cli/plugins/console.rb +40 -0
  25. data/lib/aspera/cli/plugins/cos.rb +4 -5
  26. data/lib/aspera/cli/plugins/faspex.rb +99 -84
  27. data/lib/aspera/cli/plugins/faspex5.rb +179 -148
  28. data/lib/aspera/cli/plugins/node.rb +219 -153
  29. data/lib/aspera/cli/plugins/orchestrator.rb +52 -17
  30. data/lib/aspera/cli/plugins/preview.rb +46 -32
  31. data/lib/aspera/cli/plugins/server.rb +57 -17
  32. data/lib/aspera/cli/plugins/shares.rb +34 -12
  33. data/lib/aspera/cli/sync_actions.rb +68 -0
  34. data/lib/aspera/cli/transfer_agent.rb +45 -55
  35. data/lib/aspera/cli/transfer_progress.rb +74 -0
  36. data/lib/aspera/cli/version.rb +1 -1
  37. data/lib/aspera/colors.rb +3 -1
  38. data/lib/aspera/command_line_builder.rb +14 -11
  39. data/lib/aspera/cos_node.rb +3 -2
  40. data/lib/aspera/environment.rb +17 -6
  41. data/lib/aspera/fasp/agent_aspera.rb +126 -0
  42. data/lib/aspera/fasp/agent_base.rb +31 -77
  43. data/lib/aspera/fasp/agent_connect.rb +21 -22
  44. data/lib/aspera/fasp/agent_direct.rb +88 -102
  45. data/lib/aspera/fasp/agent_httpgw.rb +196 -192
  46. data/lib/aspera/fasp/agent_node.rb +41 -34
  47. data/lib/aspera/fasp/agent_trsdk.rb +75 -34
  48. data/lib/aspera/fasp/error_info.rb +2 -2
  49. data/lib/aspera/fasp/faux_file.rb +52 -0
  50. data/lib/aspera/fasp/installation.rb +43 -184
  51. data/lib/aspera/fasp/management.rb +244 -0
  52. data/lib/aspera/fasp/parameters.rb +59 -26
  53. data/lib/aspera/fasp/parameters.yaml +75 -8
  54. data/lib/aspera/fasp/products.rb +162 -0
  55. data/lib/aspera/fasp/transfer_spec.rb +1 -1
  56. data/lib/aspera/fasp/uri.rb +4 -4
  57. data/lib/aspera/faspex_gw.rb +2 -2
  58. data/lib/aspera/faspex_postproc.rb +2 -2
  59. data/lib/aspera/hash_ext.rb +2 -2
  60. data/lib/aspera/json_rpc.rb +49 -0
  61. data/lib/aspera/line_logger.rb +23 -0
  62. data/lib/aspera/log.rb +57 -16
  63. data/lib/aspera/node.rb +97 -14
  64. data/lib/aspera/oauth.rb +36 -18
  65. data/lib/aspera/open_application.rb +4 -4
  66. data/lib/aspera/persistency_folder.rb +2 -2
  67. data/lib/aspera/preview/file_types.rb +4 -2
  68. data/lib/aspera/preview/generator.rb +22 -35
  69. data/lib/aspera/preview/options.rb +2 -0
  70. data/lib/aspera/preview/terminal.rb +24 -13
  71. data/lib/aspera/preview/utils.rb +19 -26
  72. data/lib/aspera/rest.rb +103 -72
  73. data/lib/aspera/rest_call_error.rb +1 -1
  74. data/lib/aspera/rest_error_analyzer.rb +15 -14
  75. data/lib/aspera/rest_errors_aspera.rb +37 -34
  76. data/lib/aspera/secret_hider.rb +14 -16
  77. data/lib/aspera/ssh.rb +4 -1
  78. data/lib/aspera/sync.rb +128 -122
  79. data/lib/aspera/temp_file_manager.rb +10 -3
  80. data/lib/aspera/web_auth.rb +10 -7
  81. data/lib/aspera/web_server_simple.rb +9 -4
  82. data.tar.gz.sig +0 -0
  83. metadata +33 -15
  84. metadata.gz.sig +0 -0
  85. data/lib/aspera/cli/listener/line_dump.rb +0 -19
  86. data/lib/aspera/cli/listener/logger.rb +0 -22
  87. data/lib/aspera/cli/listener/progress.rb +0 -50
  88. data/lib/aspera/cli/listener/progress_multi.rb +0 -84
  89. data/lib/aspera/cli/plugins/sync.rb +0 -44
  90. data/lib/aspera/fasp/listener.rb +0 -13
@@ -16,67 +16,77 @@ module Aspera
16
16
  MAX_ITEMS = 'max'
17
17
  # special query parameter: max number of pages for list command
18
18
  MAX_PAGES = 'pmax'
19
- # used when all resources are selected
20
- VAL_ALL = 'ALL'
21
19
  # special identifier format: look for this name to find where supported
22
20
  REGEX_LOOKUP_ID_BY_FIELD = /^%([^:]+):(.*)$/.freeze
23
21
 
24
- # global for inherited classes
25
- @@options_created = false # rubocop:disable Style/ClassVars
22
+ class << self
23
+ def declare_generic_options(options)
24
+ options.declare(:query, 'Additional filter for for some commands (list/delete)', types: Hash)
25
+ options.declare(
26
+ :value, 'Value for create, update, list filter', types: Hash,
27
+ deprecation: '(4.14) Use positional value for create/modify or option: query for list/delete')
28
+ options.declare(:property, 'Name of property to set (modify operation)')
29
+ options.declare(:id, 'Resource identifier', deprecation: "(4.14) Use positional identifier after verb (#{INSTANCE_OPS.join(',')})")
30
+ options.declare(:bulk, 'Bulk operation (only some)', values: :bool, default: :no)
31
+ options.declare(:bfail, 'Bulk operation error handling', values: :bool, default: :yes)
32
+ end
33
+ end
26
34
 
27
35
  def initialize(env)
28
- raise 'must be Hash' unless env.is_a?(Hash)
29
- # env.each_key {|k| raise "wrong agent key #{k}" unless AGENTS.include?(k)}
36
+ raise 'env must be Hash' unless env.is_a?(Hash)
30
37
  @agents = env
31
38
  # check presence in descendant of mandatory method and constant
32
39
  raise StandardError, "Missing method 'execute_action' in #{self.class}" unless respond_to?(:execute_action)
33
40
  raise StandardError, 'ACTIONS shall be redefined by subclass' unless self.class.constants.include?(:ACTIONS)
41
+ # manual header for all plugins
34
42
  options.parser.separator('')
35
43
  options.parser.separator("COMMAND: #{self.class.name.split('::').last.downcase}")
36
44
  options.parser.separator("SUBCOMMANDS: #{self.class.const_get(:ACTIONS).map(&:to_s).sort.join(' ')}")
37
45
  options.parser.separator('OPTIONS:')
38
- return if @@options_created
39
- options.declare(:query, 'Additional filter for for some commands (list/delete)', types: Hash)
40
- options.declare(
41
- :value, 'Value for create, update, list filter', types: Hash,
42
- deprecation: 'Use positional value for create/modify or option: query for list/delete')
43
- options.declare(:property, 'Name of property to set (modify operation)')
44
- options.declare(:id, 'Resource identifier', deprecation: "Use identifier after verb (#{INSTANCE_OPS.join(',')})")
45
- options.declare(:bulk, 'Bulk operation (only some)', values: :bool, default: :no)
46
- options.declare(:bfail, 'Bulk operation error handling', values: :bool, default: :yes)
47
- options.parse_options!
48
- @@options_created = true # rubocop:disable Style/ClassVars
49
46
  end
50
47
 
51
48
  # must be called AFTER the instance action, ... folder browse <call instance_identifier>
52
49
  # @param description [String] description of the identifier
50
+ # @param as_option [Symbol] option name to use if identifier is an option
53
51
  # @param block [Proc] block to search for identifier based on attribute value
54
52
  # @return [String] identifier
55
- def instance_identifier(description: 'identifier', &block)
56
- res_id = options.get_option(:id)
57
- res_id = options.get_next_argument(description) if res_id.nil?
53
+ def instance_identifier(description: 'identifier', as_option: nil, &block)
54
+ if as_option.nil?
55
+ res_id = options.get_option(:id)
56
+ res_id = options.get_next_argument(description) if res_id.nil?
57
+ else
58
+ res_id = options.get_option(as_option)
59
+ end
58
60
  # cab be an Array
59
61
  if res_id.is_a?(String) && (m = res_id.match(REGEX_LOOKUP_ID_BY_FIELD))
60
62
  if block
61
63
  res_id = yield(m[1], ExtendedValue.instance.evaluate(m[2]))
62
64
  else
63
- raise CliBadArgument, "Percent syntax for #{description} not supported in this context"
65
+ raise Cli::BadArgument, "Percent syntax for #{description} not supported in this context"
64
66
  end
65
67
  end
66
68
  return res_id
67
69
  end
68
70
 
69
71
  # For create and delete operations: execute one actin or multiple if bulk is yes
70
- # @param single_or_array [Object] single hash, or array of hash for bulk
71
- # @param success_msg deleted or created
72
+ # @param command [Symbol] operation: :create, :delete, ...
73
+ # @param descr [String] description of the value
74
+ # @param values [Object] the value(s), or the type of value to get from user
72
75
  # @param id_result [String] key in result hash to use as identifier
73
76
  # @param fields [Array] fields to display
74
- def do_bulk_operation(single_or_array, success_msg, id_result: 'id', fields: :default)
75
- raise 'programming error: missing block' unless block_given?
76
- params = options.get_option(:bulk) ? single_or_array : [single_or_array]
77
- raise 'expecting Array for bulk operation' unless params.is_a?(Array)
77
+ def do_bulk_operation(command:, descr:, values: Hash, id_result: 'id', fields: :default)
78
+ is_bulk = options.get_option(:bulk)
79
+ case values
80
+ when :identifier
81
+ values = instance_identifier
82
+ when Class
83
+ values = value_create_modify(command: command, type: values, bulk: is_bulk)
84
+ end
85
+ raise 'Internal error: missing block' unless block_given?
86
+ # if not bulk, there is a single value
87
+ params = is_bulk ? values : [values]
78
88
  Log.log.warn('Empty list given for bulk operation') if params.empty?
79
- Log.dump(:bulk_operation, params)
89
+ Log.log.debug{Log.dump(:bulk_operation, params)}
80
90
  result_list = []
81
91
  params.each do |param|
82
92
  # init for delete
@@ -86,7 +96,8 @@ module Aspera
86
96
  res = yield(param)
87
97
  # if block returns a hash, let's use this (create)
88
98
  result = res if param.is_a?(Hash)
89
- result['status'] = success_msg
99
+ # create -> created
100
+ result['status'] = "#{command}#{'e' unless command.to_s.end_with?('e')}d".gsub(/yed$/, 'ied')
90
101
  rescue StandardError => e
91
102
  raise e if options.get_option(:bfail)
92
103
  result['status'] = e.to_s
@@ -94,7 +105,7 @@ module Aspera
94
105
  result_list.push(result)
95
106
  end
96
107
  display_fields = [id_result, 'status']
97
- if options.get_option(:bulk)
108
+ if is_bulk
98
109
  return {type: :object_list, data: result_list, fields: display_fields}
99
110
  else
100
111
  display_fields = fields unless fields.eql?(:default)
@@ -106,21 +117,16 @@ module Aspera
106
117
  # @param rest_api [Rest] api to use
107
118
  # @param res_class_path [String] sub path in URL to resource relative to base url
108
119
  # @param display_fields [Array] fields to display by default
109
- # @param id_default [String] default identifier to use for existing entity commands (show, modify)
110
120
  # @param item_list_key [String] result is in a sub key of the json
111
121
  # @param id_as_arg [String] if set, the id is provided as url argument ?<id_as_arg>=<id>
112
122
  # @param is_singleton [Boolean] if true, res_class_path is the full path to the resource
123
+ # @param block [Proc] block to search for identifier based on attribute value
113
124
  # @return result suitable for CLI result
114
- def entity_command(command, rest_api, res_class_path, display_fields: nil, id_default: nil, item_list_key: false, id_as_arg: false, is_singleton: false, &block)
125
+ def entity_command(command, rest_api, res_class_path, display_fields: nil, item_list_key: false, id_as_arg: false, is_singleton: false, &block)
115
126
  if is_singleton
116
127
  one_res_path = res_class_path
117
128
  elsif INSTANCE_OPS.include?(command)
118
- begin
119
- one_res_id = instance_identifier(&block)
120
- rescue StandardError => e
121
- raise e if id_default.nil?
122
- one_res_id = id_default
123
- end
129
+ one_res_id = instance_identifier(&block)
124
130
  one_res_path = "#{res_class_path}/#{one_res_id}"
125
131
  one_res_path = "#{res_class_path}?#{id_as_arg}=#{one_res_id}" if id_as_arg
126
132
  end
@@ -128,13 +134,12 @@ module Aspera
128
134
  case command
129
135
  when :create
130
136
  raise 'cannot create singleton' if is_singleton
131
- return do_bulk_operation(value_create_modify(command: command, type: :bulk_hash), 'created', fields: display_fields) do |params|
132
- raise 'expecting Hash' unless params.is_a?(Hash)
137
+ return do_bulk_operation(command: command, descr: 'data', fields: display_fields) do |params|
133
138
  rest_api.create(res_class_path, params)[:data]
134
139
  end
135
140
  when :delete
136
141
  raise 'cannot delete singleton' if is_singleton
137
- return do_bulk_operation(one_res_id, 'deleted') do |one_id|
142
+ return do_bulk_operation(command: command, descr: 'identifier', values: one_res_id) do |one_id|
138
143
  rest_api.delete("#{res_class_path}/#{one_id}", old_query_read_delete)
139
144
  {'id' => one_id}
140
145
  end
@@ -164,7 +169,7 @@ module Aspera
164
169
  raise "An error occurred: unexpected result type for list: #{data.class}"
165
170
  end
166
171
  when :modify
167
- parameters = value_create_modify(command: command, type: Hash)
172
+ parameters = value_create_modify(command: command)
168
173
  property = options.get_option(:property)
169
174
  parameters = {property => parameters} unless property.nil?
170
175
  rest_api.update(one_res_path, parameters)
@@ -191,12 +196,13 @@ module Aspera
191
196
  # check it is suitable
192
197
  URI.encode_www_form(query) unless query.nil?
193
198
  rescue StandardError => e
194
- raise CliBadArgument, "Query must be an extended value which can be encoded with URI.encode_www_form. Refer to manual. (#{e.message})"
199
+ raise Cli::BadArgument, "Query must be an extended value which can be encoded with URI.encode_www_form. Refer to manual. (#{e.message})"
195
200
  end
196
201
  return query
197
202
  end
198
203
 
199
204
  # TODO: when deprecation of `value` is completed: remove this method, replace with query_read_delete
205
+ # deprecation: 4.14
200
206
  def old_query_read_delete
201
207
  query = options.get_option(:value) # legacy, deprecated, remove, one day...
202
208
  query = query_read_delete if query.nil?
@@ -204,31 +210,36 @@ module Aspera
204
210
  end
205
211
 
206
212
  # TODO: when deprecation of `value` is completed: remove this method, replace with options.get_option(:query)
207
- def value_or_query(mandatory: false, allowed_types: nil)
208
- value = options.get_option(:value, mandatory: false, allowed_types: allowed_types)
209
- value = options.get_option(:query, mandatory: mandatory, allowed_types: allowed_types) if value.nil?
213
+ # deprecation: 4.14
214
+ def query_option(mandatory: false, default: nil)
215
+ option = :value
216
+ value = options.get_option(option, mandatory: false)
217
+ if value.nil?
218
+ option = :query
219
+ value = options.get_option(option, mandatory: mandatory, default: default)
220
+ end
210
221
  return value
211
222
  end
212
223
 
213
224
  # Retrieves an extended value from command line, used for creation or modification of entities
225
+ # @param command [Symbol] command name for error message
226
+ # @param type [Class] expected type of value, either a Class, an Array of Class, or :bulk_hash
214
227
  # @param default [Object] default value if not provided
215
- # @param command [String] command name for error message
216
- # @param type [Class] expected type of value
217
228
  # TODO: when deprecation of `value` is completed: remove line with :value
218
- def value_create_modify(default: nil, command: 'command', type: nil)
229
+ def value_create_modify(command:, type: Hash, bulk: false, default: nil)
219
230
  value = options.get_option(:value)
231
+ Log.log.warn("option `value` is deprecated. Use positional parameter for #{command}") unless value.nil?
220
232
  value = options.get_next_argument("parameters for #{command}", mandatory: default.nil?) if value.nil?
221
233
  value = default if value.nil?
222
- if type.nil?
223
- # nothing to do
224
- elsif type.is_a?(Class)
225
- raise CliBadArgument, "Value must be a #{type}" unless value.is_a?(type)
226
- elsif type.is_a?(Array)
227
- raise CliBadArgument, "Value must be one of #{type.join(', ')}" unless type.any?{|t| value.is_a?(t)}
228
- elsif type.eql?(:bulk_hash)
229
- raise CliBadArgument, 'Value must be a Hash or Array of Hash' unless value.is_a?(Hash) || (value.is_a?(Array) && value.all?(Hash))
230
- else
231
- raise "Internal error: #{type}"
234
+ unless type.nil?
235
+ type = [type] unless type.is_a?(Array)
236
+ raise "Internal error, check types must be a Class, not #{type.map(&:class).join(',')}" unless type.all?(Class)
237
+ if bulk
238
+ raise Cli::BadArgument, "Value must be an Array of #{type.join(',')}" unless value.is_a?(Array)
239
+ raise Cli::BadArgument, "Value must be a #{type.join(',')}, not #{value.map{|i| i.class.name}.uniq.join(',')}" unless value.all?{|v|type.include?(v.class)}
240
+ else
241
+ raise Cli::BadArgument, "Value must be a #{type.join(',')}, not #{value.class.name}" unless type.include?(value.class)
242
+ end
232
243
  end
233
244
  return value
234
245
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'aspera/rest'
4
3
  require 'aspera/aoc'
5
4
 
6
5
  module Aspera