aspera-cli 4.0.0 → 4.1.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.
data/docs/test_env.conf CHANGED
@@ -19,7 +19,6 @@ default:
19
19
  cli_default:
20
20
  interactive: your value here
21
21
  smtp: your value here
22
- ascp_path: your value here
23
22
  local_user:
24
23
  ssh_keys: your value here
25
24
  smtp_config:
@@ -50,6 +49,7 @@ tst_node_faspex:
50
49
  password: your value here
51
50
  tst_faspex5:
52
51
  url: your value here
52
+ auth: your value here
53
53
  username: your value here
54
54
  password: your value here
55
55
  tst_shares:
@@ -94,6 +94,7 @@ tst_ak_preview:
94
94
  url: your value here
95
95
  username: your value here
96
96
  password: your value here
97
+ mimemagic: your value here
97
98
  tst_node_preview:
98
99
  url: your value here
99
100
  username: your value here
@@ -122,6 +123,7 @@ misc:
122
123
  aoc_publink_recv_from_aocuser: your value here
123
124
  aoc_publink_send_shd_inbox: your value here
124
125
  aoc_publink_send_aoc_user: your value here
126
+ aoc_publink_send_use_pass: your value here
125
127
  aoc_publink_folder: your value here
126
128
  aoc_shbx_ws: your value here
127
129
  aoc_shbx_name: your value here
@@ -138,3 +140,4 @@ misc:
138
140
  email_external: your value here
139
141
  aoc_org: your value here
140
142
  aoc_user: your value here
143
+ http_gw_fqdn_port: your value here
@@ -28,7 +28,7 @@ arg: related ascp argument or env var suffix (PASS for ASPERA_SCP_PASS)
28
28
  </p>
29
29
  <p>
30
30
  UNDER CONSTRUCTION<br/>
31
- <a href="https://developer.ibm.com/api/view/aspera-prod:ibm-aspera:title-IBM_Aspera#id90944">Documentation&rarr;Node API&rarr;/opt/transfers</a><br/>
31
+ <a href="https://developer.ibm.com/apis/catalog/?search=aspera">Aspera API Documentation</a>&rarr;Node API&rarr;/opt/transfers<br/>
32
32
  </p>
33
33
 
34
34
  <table>
data/lib/aspera/aoc.rb CHANGED
@@ -121,7 +121,7 @@ module Aspera
121
121
  raise RuntimeError,'too many redirections'
122
122
  end
123
123
 
124
- # @param :link,:url,:auth,:client_id,:client_secret,:scope,:redirect_uri,:private_key,:username,:subpath
124
+ # @param :link,:url,:auth,:client_id,:client_secret,:scope,:redirect_uri,:private_key,:username,:subpath,:password (for pub link)
125
125
  def initialize(opt)
126
126
  # access key secrets are provided out of band to get node api access
127
127
  # key: access key
@@ -185,6 +185,7 @@ module Aspera
185
185
  aoc_auth_p[:jwt_subject] = opt[:username]
186
186
  aoc_auth_p[:jwt_private_key_obj] = OpenSSL::PKey::RSA.new(private_key_PEM_string)
187
187
  when :url_token
188
+ aoc_auth_p[:password]=opt[:password] unless opt[:password].nil?
188
189
  # nothing more
189
190
  else raise "ERROR: unsupported auth method: #{aoc_auth_p[:grant]}"
190
191
  end
@@ -293,6 +294,7 @@ module Aspera
293
294
  # no scope: requires secret
294
295
  # if secret provided beforehand: use it
295
296
  def get_node_api(node_info,node_scope=nil)
297
+ # X-Aspera-AccessKey required for bearer token only
296
298
  node_rest_params={
297
299
  :base_url => node_info['url'],
298
300
  :headers => {'X-Aspera-AccessKey'=>node_info['access_key']},
@@ -312,7 +314,7 @@ module Aspera
312
314
  node_rest_params[:auth]=self.params[:auth].clone
313
315
  node_rest_params[:auth][:scope]=self.class.node_scope(node_info['access_key'],node_scope)
314
316
  end
315
- return Rest.new(node_rest_params)
317
+ return Node.new(node_rest_params)
316
318
  end
317
319
 
318
320
  # check that parameter has necessary types
@@ -328,102 +330,82 @@ module Aspera
328
330
  return node_info,file_id
329
331
  end
330
332
 
331
- # returns node api and folder_id from soft link
332
- def read_asplnk(current_file_info)
333
- new_node_api=get_node_api(self.read("nodes/#{current_file_info['target_node_id']}")[:data],SCOPE_NODE_USER)
334
- return {:node_api=>new_node_api,:folder_id=>current_file_info['target_id']}
333
+ # add entry to list if test block is success
334
+ def process_find_files(entry,path)
335
+ begin
336
+ # add to result if match filter
337
+ @find_state[:found].push(entry.merge({'path'=>path})) if @find_state[:test_block].call(entry)
338
+ # process link
339
+ if entry[:type].eql?('link')
340
+ sub_node_info=self.read("nodes/#{entry['target_node_id']}")[:data]
341
+ sub_opt={method: process_find_files, top_file_id: entry['target_id'], top_file_path: path}
342
+ get_node_api(sub_node_info,SCOPE_NODE_USER).crawl(self,sub_opt)
343
+ end
344
+ rescue => e
345
+ Log.log.error("#{path}: #{e.message}")
346
+ end
347
+ # process all folders
348
+ return true
335
349
  end
336
350
 
337
- # @returns list of file paths that match given regex
338
351
  def find_files( top_node_file, test_block )
339
352
  top_node_info,top_file_id=check_get_node_file(top_node_file)
340
353
  Log.log.debug("find_files: node_info=#{top_node_info}, fileid=#{top_file_id}")
341
- result=[]
342
- top_node_api=get_node_api(top_node_info,SCOPE_NODE_USER)
343
- # initialize loop elements : list of folders to scan
344
- # Note: top file id is necessarily a folder
345
- items_to_explore=[{:node_api=>top_node_api,:folder_id=>top_file_id,:path=>''}]
346
-
347
- while !items_to_explore.empty? do
348
- current_item = items_to_explore.shift
349
- Log.log.debug("searching #{current_item[:path]}".bg_green)
350
- # get folder content
351
- begin
352
- folder_contents = current_item[:node_api].read("files/#{current_item[:folder_id]}/files")[:data]
353
- rescue => e
354
- Log.log.warn("#{current_item[:path]}: #{e.message}")
355
- folder_contents=[]
354
+ @find_state={found: [], test_block: test_block}
355
+ get_node_api(top_node_info,SCOPE_NODE_USER).crawl(self,{method: :process_find_files, top_file_id: top_file_id})
356
+ result=@find_state[:found]
357
+ @find_state=nil
358
+ return result
359
+ end
360
+
361
+ def process_resolve_node_file(entry,path)
362
+ # stop digging here if not in right path
363
+ return false unless entry['name'].eql?(@resolve_state[:path].first)
364
+ # ok it matches, so we remove the match
365
+ @resolve_state[:path].shift
366
+ case entry['type']
367
+ when 'file'
368
+ # file must be terminal
369
+ raise "#{entry['name']} is a file, expecting folder to find: #{@resolve_state[:path]}" unless @resolve_state[:path].empty?
370
+ @resolve_state[:result][:file_id]=entry['id']
371
+ when 'link'
372
+ @resolve_state[:result][:node_info]=self.read("nodes/#{entry['target_node_id']}")[:data]
373
+ if @resolve_state[:path].empty?
374
+ @resolve_state[:result][:file_id]=entry['target_id']
375
+ else
376
+ get_node_api(@resolve_state[:result][:node_info],SCOPE_NODE_USER).crawl(self,{method: :process_resolve_node_file, top_file_id: entry['target_id']})
356
377
  end
357
- # TODO: check if this is a folder or file ?
358
- Log.dump(:folder_contents,folder_contents)
359
- folder_contents.each do |current_file_info|
360
- item_path=File.join(current_item[:path],current_file_info['name'])
361
- Log.log.debug("looking #{item_path}".bg_green)
362
- begin
363
- # does item match ?
364
- result.push(current_file_info.merge({'path'=>item_path})) if test_block.call(current_file_info)
365
- # does it need further processing ?
366
- case current_file_info['type']
367
- when 'file'
368
- Log.log.debug("testing : #{current_file_info['name']}")
369
- when 'folder'
370
- items_to_explore.push({:node_api=>current_item[:node_api],:folder_id=>current_file_info['id'],:path=>item_path})
371
- when 'link' # .*.asp-lnk
372
- items_to_explore.push(read_asplnk(current_file_info).merge({:path=>item_path}))
373
- else
374
- Log.log.error("unknown folder item type: #{current_file_info['type']}")
375
- end
376
- rescue => e
377
- Log.log.error("#{item_path}: #{e.message}")
378
- end
378
+ when 'folder'
379
+ if @resolve_state[:path].empty?
380
+ # found: store
381
+ @resolve_state[:result][:file_id]=entry['id']
382
+ return false
379
383
  end
384
+ else
385
+ Log.log.warn("unknown element type: #{entry['type']}")
380
386
  end
381
- return result
387
+ # continue to dig folder
388
+ return true
382
389
  end
383
390
 
384
- # @return node information (returned by API) and file id, from a "/" based path
391
+ # @return Array(node_info,file_id) for the given path
392
+ # @param top_node_file Array [root node,file id]
393
+ # @param element_path_string String path of element
385
394
  # supports links to secondary nodes
386
- # input: Array(root node,file id), String path
387
- # output: Array(node_info,file_id) for the given path
388
- def resolve_node_file( top_node_file, element_path_string='' )
389
- Log.log.debug("resolve_node_file: top_node_file=#{top_node_file}, path=#{element_path_string}")
390
- # initialize loop invariants
391
- current_node_info,current_file_id=check_get_node_file(top_node_file)
392
- items_to_explore=element_path_string.split(PATH_SEPARATOR).select{|i| !i.empty?}
393
-
394
- while !items_to_explore.empty? do
395
- current_item = items_to_explore.shift
396
- Log.log.debug "searching #{current_item}".bg_green
397
- # get API if changed
398
- current_node_api=get_node_api(current_node_info,SCOPE_NODE_USER) if current_node_api.nil?
399
- # get folder content
400
- folder_contents = current_node_api.read("files/#{current_file_id}/files")
401
- Log.dump(:folder_contents,folder_contents)
402
- matching_folders = folder_contents[:data].select { |i| i['name'].eql?(current_item)}
403
- #Log.log.debug "matching_folders: #{matching_folders}"
404
- raise "no such folder: #{current_item} in #{folder_contents[:data].map { |i| i['name']}}" if matching_folders.empty?
405
- current_file_info = matching_folders.first
406
- # process type of file
407
- case current_file_info['type']
408
- when 'file'
409
- current_file_id=current_file_info['id']
410
- # a file shall be terminal
411
- if !items_to_explore.empty? then
412
- raise "#{current_item} is a file, expecting folder to find: #{items_to_explore}"
413
- end
414
- when 'link'
415
- current_node_info=self.read("nodes/#{current_file_info['target_node_id']}")[:data]
416
- current_file_id=current_file_info['target_id']
417
- # need to switch node
418
- current_node_api=nil
419
- when 'folder'
420
- current_file_id=current_file_info['id']
421
- else
422
- Log.log.warn("unknown element type: #{current_file_info['type']}")
423
- end
395
+ def resolve_node_file( top_node_file, element_path_string )
396
+ top_node_info,top_file_id=check_get_node_file(top_node_file)
397
+ path_elements=element_path_string.split(PATH_SEPARATOR).select{|i| !i.empty?}
398
+ result={node_info: top_node_info, file_id: nil}
399
+ if path_elements.empty?
400
+ result[:file_id]=top_file_id
401
+ else
402
+ @resolve_state={path: path_elements, result: result}
403
+ get_node_api(top_node_info,SCOPE_NODE_USER).crawl(self,{method: :process_resolve_node_file, top_file_id: top_file_id})
404
+ not_found=@resolve_state[:path]
405
+ @resolve_state=nil
406
+ raise "entry not found: #{not_found}" if result[:file_id].nil?
424
407
  end
425
- Log.log.info("resolve_node_file(#{element_path_string}): file_id=#{current_file_id},node_info=#{current_node_info}")
426
- return {node_info: current_node_info, file_id: current_file_id}
408
+ return result
427
409
  end
428
410
 
429
411
  end # AoC
@@ -151,6 +151,8 @@ module Aspera
151
151
  else
152
152
  if user_asked_fields_list_str.start_with?('+')
153
153
  result_default_fields(results,table_rows_hash_val).push(*user_asked_fields_list_str.gsub(/^\+/,'').split(','))
154
+ elsif user_asked_fields_list_str.start_with?('-')
155
+ result_default_fields(results,table_rows_hash_val).select{|i| ! user_asked_fields_list_str.gsub(/^\-/,'').split(',').include?(i)}
154
156
  else
155
157
  user_asked_fields_list_str.split(',')
156
158
  end
@@ -53,13 +53,20 @@ module Aspera
53
53
  @help_url='http://www.rubydoc.info/gems/'+GEM_NAME
54
54
  @gem_url='https://rubygems.org/gems/'+GEM_NAME
55
55
  # give command line arguments to option manager (no parsing)
56
- @plugin_env[:options]=@opt_mgr=Manager.new(self.program_name,argv,app_banner())
56
+ app_main_folder=ENV[conf_dir_env_var]
57
+ # if env var undefined or empty
58
+ if app_main_folder.nil? or app_main_folder.empty?
59
+ user_home_folder=Dir.home
60
+ raise CliError,"Home folder does not exist: #{user_home_folder}. Check your user environment or use #{conf_dir_env_var}." unless Dir.exist?(user_home_folder)
61
+ app_main_folder=File.join(user_home_folder,Plugins::Config::ASPERA_HOME_FOLDER_NAME,PROGRAM_NAME)
62
+ end
63
+ @plugin_env[:options]=@opt_mgr=Manager.new(PROGRAM_NAME,argv,app_banner())
57
64
  @plugin_env[:formater]=Formater.new(@plugin_env[:options])
58
- Rest.user_agent=self.program_name
65
+ Rest.user_agent=PROGRAM_NAME
59
66
  # must override help methods before parser called (in other constructors)
60
67
  init_global_options()
61
68
  # the Config plugin adds the @preset parser
62
- @plugin_env[:config]=Plugins::Config.new(@plugin_env,self.program_name,@help_url,Aspera::Cli::VERSION)
69
+ @plugin_env[:config]=Plugins::Config.new(@plugin_env,PROGRAM_NAME,@help_url,Aspera::Cli::VERSION,app_main_folder)
63
70
  # the TransferAgent plugin may use the @preset parser
64
71
  @plugin_env[:transfer]=TransferAgent.new(@plugin_env)
65
72
  Log.log.debug('created plugin env'.red)
@@ -73,21 +80,21 @@ module Aspera
73
80
  end
74
81
 
75
82
  def app_banner
76
- banner = "NAME\n\t#{self.program_name} -- a command line tool for Aspera Applications (v#{Aspera::Cli::VERSION})\n\n"
83
+ banner = "NAME\n\t#{PROGRAM_NAME} -- a command line tool for Aspera Applications (v#{Aspera::Cli::VERSION})\n\n"
77
84
  banner << "SYNOPSIS\n"
78
- banner << "\t#{self.program_name} COMMANDS [OPTIONS] [ARGS]\n"
79
- banner << "\n"
80
- banner << "DESCRIPTION\n"
85
+ banner << "\t#{PROGRAM_NAME} COMMANDS [OPTIONS] [ARGS]\n"
86
+ banner << "\nDESCRIPTION\n"
81
87
  banner << "\tUse Aspera application to perform operations on command line.\n"
82
88
  banner << "\tDocumentation and examples: #{@gem_url}\n"
83
- banner << "\texecute: #{self.program_name} conf doc\n"
89
+ banner << "\texecute: #{PROGRAM_NAME} conf doc\n"
84
90
  banner << "\tor visit: #{@help_url}\n"
85
- banner << "\n"
86
- banner << "COMMANDS\n"
87
- banner << "\tTo list first level commands, execute: #{self.program_name}\n"
91
+ banner << "\nENVIRONMENT VARIABLES\n"
92
+ banner << "\t#{conf_dir_env_var} config folder, default: $HOME/#{Plugins::Config::ASPERA_HOME_FOLDER_NAME}/#{PROGRAM_NAME}\n"
93
+ banner << "\t#any option can be set as an environment variable, refer to the manual\n"
94
+ banner << "\nCOMMANDS\n"
95
+ banner << "\tTo list first level commands, execute: #{PROGRAM_NAME}\n"
88
96
  banner << "\tNote that commands can be written shortened (provided it is unique).\n"
89
- banner << "\n"
90
- banner << "OPTIONS\n"
97
+ banner << "\nOPTIONS\n"
91
98
  banner << "\tOptions begin with a '-' (minus), and value is provided on command line.\n"
92
99
  banner << "\tSpecial values are supported beginning with special prefix, like: #{ExtendedValue.instance.modifiers.map{|m|"@#{m}:"}.join(' ')}.\n"
93
100
  banner << "\tDates format is 'DD-MM-YY HH:MM:SS', or 'now' or '-<num>h'\n\n"
@@ -167,7 +174,7 @@ module Aspera
167
174
  # override main option parser with a brand new, to avoid having global options
168
175
  plugin_env=@plugin_env.clone
169
176
  plugin_env[:man_only]=true
170
- plugin_env[:options]=Manager.new(self.program_name,[],'')
177
+ plugin_env[:options]=Manager.new(PROGRAM_NAME,[],'')
171
178
  get_plugin_instance_with_options(plugin_name_sym,plugin_env)
172
179
  # display generated help for plugin options
173
180
  @plugin_env[:formater].display_message(:error,plugin_env[:options].parser.to_s)
@@ -178,10 +185,14 @@ module Aspera
178
185
 
179
186
  protected
180
187
 
188
+ def conf_dir_env_var
189
+ return "#{PROGRAM_NAME}_home".upcase
190
+ end
191
+
181
192
  # early debug for parser
182
193
  # Note: does not accept shortcuts
183
194
  def early_debug_setup(argv)
184
- Log.instance.program_name=self.program_name
195
+ Log.instance.program_name=PROGRAM_NAME
185
196
  argv.each do |arg|
186
197
  case arg
187
198
  when '--'
@@ -205,10 +216,6 @@ module Aspera
205
216
  return Main.result_nothing
206
217
  end
207
218
 
208
- def options;@opt_mgr;end
209
-
210
- def program_name;PROGRAM_NAME;end
211
-
212
219
  # this is the main function called by initial script just after constructor
213
220
  def process_command_line
214
221
  Log.log.debug('process_command_line')
@@ -232,6 +239,7 @@ module Aspera
232
239
  raise CliError,"Another instance is already running (lock port=#{lock_port})."
233
240
  end
234
241
  end
242
+ @plugin_env[:config].periodic_check_newer_gem_version
235
243
  if @option_show_config and @opt_mgr.command_or_arg_empty?
236
244
  command_sym=Plugins::Config::CONF_PLUGIN_SYM
237
245
  else
@@ -67,7 +67,10 @@ module Aspera
67
67
  when :delete
68
68
  rest_api.delete(one_res_path)
69
69
  return Main.result_status("deleted")
70
+ else
71
+ raise "unknown action: #{command}"
70
72
  end
73
+ raise "internal error should not reach here"
71
74
  end
72
75
 
73
76
  # implement generic rest operations on given resource path
@@ -77,13 +80,15 @@ module Aspera
77
80
  return entity_command(command,rest_api,res_class_path,display_fields,id_symb,id_default,subkey)
78
81
  end
79
82
 
80
- def options;@agents[:options];end
83
+ def options; return @agents[:options];end
81
84
 
82
- def transfer;@agents[:transfer];end
85
+ def transfer; return @agents[:transfer];end
83
86
 
84
- def config;return @agents[:config];end
87
+ def config; return @agents[:config];end
85
88
 
86
- def format;return @agents[:formater];end
89
+ def format; return @agents[:formater];end
90
+
91
+ def persistency; return @agents[:persistency];end
87
92
 
88
93
  end # Plugin
89
94
  end # Cli
@@ -12,7 +12,7 @@ module Aspera
12
12
  command=self.options.get_next_command(ACTIONS)
13
13
  case command
14
14
  when :entitlement
15
- entitlement_id = self.options.get_option(:username,:mandatory),
15
+ entitlement_id = self.options.get_option(:username,:mandatory)
16
16
  customer_id = self.options.get_option(:password,:mandatory)
17
17
  api_metering=AoC.metering_api(entitlement_id,customer_id)
18
18
  return {:type=>:single_object, :data=>api_metering.read('entitlement')[:data]}
@@ -3,6 +3,7 @@ require 'aspera/cli/plugins/ats'
3
3
  require 'aspera/cli/basic_auth_plugin'
4
4
  require 'aspera/cli/transfer_agent'
5
5
  require 'aspera/aoc'
6
+ require 'aspera/node'
6
7
  require 'aspera/persistency_action_once'
7
8
  require 'securerandom'
8
9
  require 'resolv'
@@ -12,8 +13,8 @@ module Aspera
12
13
  module Cli
13
14
  module Plugins
14
15
  class Aoc < BasicAuthPlugin
16
+ # special value for package id
15
17
  VAL_ALL='ALL'
16
- private_constant :VAL_ALL
17
18
  attr_reader :api_aoc
18
19
  def initialize(env)
19
20
  super(env)
@@ -25,23 +26,22 @@ module Aspera
25
26
  @api_aoc=nil
26
27
  @url_token_data=nil
27
28
  @user_info=nil
28
- @ats=Ats.new(@agents)
29
- self.options.add_opt_list(:auth,Oauth.auth_types,"type of Oauth authentication")
30
- self.options.add_opt_list(:operation,[:push,:pull],"client operation for transfers")
31
- self.options.add_opt_simple(:client_id,"API client identifier in application")
32
- self.options.add_opt_simple(:client_secret,"API client passcode")
33
- self.options.add_opt_simple(:redirect_uri,"API client redirect URI")
34
- self.options.add_opt_simple(:private_key,"RSA private key PEM value for JWT (prefix file path with @val:@file:)")
35
- self.options.add_opt_simple(:workspace,"name of workspace")
36
- self.options.add_opt_simple(:eid,"identifier") # used ?
37
- self.options.add_opt_simple(:name,"resource name")
38
- self.options.add_opt_simple(:link,"public link to shared resource")
39
- self.options.add_opt_simple(:new_user_option,"new user creation option")
40
- self.options.add_opt_simple(:from_folder,"share to share source folder")
41
- self.options.add_opt_simple(:scope,"scope for AoC API calls")
42
- self.options.add_opt_simple(:notify,"notify users that file was received")
43
- self.options.add_opt_boolean(:bulk,"bulk operation")
44
- self.options.add_opt_boolean(:default_ports,"use standard FASP ports or get from node api")
29
+ self.options.add_opt_list(:auth,Oauth.auth_types,'type of Oauth authentication')
30
+ self.options.add_opt_list(:operation,[:push,:pull],'client operation for transfers')
31
+ self.options.add_opt_simple(:client_id,'API client identifier in application')
32
+ self.options.add_opt_simple(:client_secret,'API client passcode')
33
+ self.options.add_opt_simple(:redirect_uri,'API client redirect URI')
34
+ self.options.add_opt_simple(:private_key,'RSA private key PEM value for JWT (prefix file path with @val:@file:)')
35
+ self.options.add_opt_simple(:workspace,'name of workspace')
36
+ self.options.add_opt_simple(:name,'resource name')
37
+ self.options.add_opt_simple(:path,'file or folder path')
38
+ self.options.add_opt_simple(:link,'public link to shared resource')
39
+ self.options.add_opt_simple(:new_user_option,'new user creation option')
40
+ self.options.add_opt_simple(:from_folder,'share to share source folder')
41
+ self.options.add_opt_simple(:scope,'scope for AoC API calls')
42
+ self.options.add_opt_simple(:notify,'notify users that file was received')
43
+ self.options.add_opt_boolean(:bulk,'bulk operation')
44
+ self.options.add_opt_boolean(:default_ports,'use standard FASP ports or get from node api')
45
45
  self.options.set_option(:bulk,:no)
46
46
  self.options.set_option(:default_ports,:yes)
47
47
  self.options.set_option(:new_user_option,{'package_contact'=>true})
@@ -52,7 +52,9 @@ module Aspera
52
52
  self.options.parse_options!
53
53
  AoC.set_use_default_ports(self.options.get_option(:default_ports))
54
54
  return if env[:man_only]
55
- update_aoc_api
55
+ @api_aoc=AoC.new(aoc_params('api/v1'))
56
+ # add access key secrets
57
+ @api_aoc.add_secrets(self.config.get_secrets)
56
58
  end
57
59
 
58
60
  # call this to populate single AK secret in AoC API object, from options
@@ -65,7 +67,8 @@ module Aspera
65
67
  raise CliBadArgument,"Please provide option secret or entry in option secrets for: #{ak}" unless @api_aoc.has_secret(ak) or !mandatory
66
68
  end
67
69
 
68
- def user_info
70
+ # cached user information
71
+ def c_user_info
69
72
  if @user_info.nil?
70
73
  # get our user's default information
71
74
  # self?embed[]=default_workspace&embed[]=organization
@@ -80,16 +83,11 @@ module Aspera
80
83
  # starts transfer using transfer agent
81
84
  def transfer_start(app,direction,node_file,ts_add)
82
85
  ts_add.deep_merge!(AoC.analytics_ts(app,direction,@workspace_id,@workspace_name))
83
- ts_add.deep_merge!(AoC.console_ts(app,user_info['name'],user_info['email']))
86
+ ts_add.deep_merge!(AoC.console_ts(app,c_user_info['name'],c_user_info['email']))
84
87
  return self.transfer.start(*@api_aoc.tr_spec(app,direction,node_file,ts_add))
85
88
  end
86
89
 
87
- NODE4_COMMANDS=[ :browse, :find, :mkdir, :rename, :delete, :upload, :download, :transfer, :http_node_download, :v3, :file, :bearer_token_node, :permissions ]
88
-
89
- def node_gen4_execute_action(top_node_file)
90
- command_repo=self.options.get_next_command(NODE4_COMMANDS)
91
- return execute_node_gen4_command(command_repo,top_node_file)
92
- end
90
+ NODE4_COMMANDS=[ :browse, :find, :mkdir, :rename, :delete, :upload, :download, :transfer, :http_node_download, :v3, :file, :bearer_token_node ]
93
91
 
94
92
  def execute_node_gen4_command(command_repo,top_node_file)
95
93
  case command_repo
@@ -213,69 +211,79 @@ module Aspera
213
211
  node_api=@api_aoc.get_node_api(top_node_file[:node_info],AoC::SCOPE_NODE_USER)
214
212
  return Node.new(@agents.merge(skip_basic_auth_options: true, node_api: node_api)).execute_action(command_legacy)
215
213
  when :file
216
- fileid=self.options.get_next_argument('file id')
217
- node_file = @api_aoc.resolve_node_file(top_node_file)
218
- node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
219
- items=node_api.read("files/#{fileid}")[:data]
220
- return {:type=>:single_object,:data=>items}
221
- when :permissions
222
- fileid=self.options.get_next_argument('file id')
223
- node_file = @api_aoc.resolve_node_file(top_node_file)
214
+ file_path=self.options.get_option(:path,:optional)
215
+ node_file = if !file_path.nil?
216
+ @api_aoc.resolve_node_file(top_node_file,file_path) # TODO: allow follow link ?
217
+ else
218
+ {node_info: top_node_file[:node_info],file_id: self.options.get_option(:id,:mandatory)}
219
+ end
224
220
  node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
225
- command_perms=self.options.get_next_command([:show,:create])
226
- case command_perms
221
+ command_node_file=self.options.get_next_command([:show,:permission,:modify])
222
+ case command_node_file
227
223
  when :show
228
- items=node_api.read('permissions',{'include'=>['[]','access_level','permission_count'],'file_id'=>fileid,'inherited'=>false})[:data]
229
- return {:type=>:object_list,:data=>items}
230
- when :create
231
- #value=self.options.get_next_argument('creation value')
232
- set_workspace_info
233
- access_id="ASPERA_ACCESS_KEY_ADMIN_WS_#{@workspace_id}"
234
- node_file[:node_info]
235
- params={
236
- "file_id"=>fileid,
237
- "access_type"=>"user",
238
- "access_id"=>access_id,
239
- "access_levels"=>["list","read","write","delete","mkdir","rename","preview"],
240
- "tags"=>{
241
- "aspera"=>{
242
- "files"=>{
243
- "workspace"=>{
244
- "id"=>@workspace_id,
245
- "workspace_name"=>@workspace_name,
246
- "user_name"=>user_info['name'],
247
- "shared_by_user_id"=>user_info['id'],
248
- "shared_by_name"=>user_info['name'],
249
- "shared_by_email"=>user_info['email'],
250
- "shared_with_name"=>access_id,
251
- "access_key"=>node_file[:node_info]['access_key'],
252
- "node"=>node_file[:node_info]['name']}}}}}
253
- item=node_api.create('permissions',params)[:data]
254
- return {:type=>:single_object,:data=>item}
255
- else raise "error"
224
+ items=node_api.read("files/#{node_file[:file_id]}")[:data]
225
+ return {:type=>:single_object,:data=>items}
226
+ when :modify
227
+ update_param=self.options.get_next_argument("update data (Hash)")
228
+ res=node_api.update("files/#{node_file[:file_id]}",update_param)[:data]
229
+ return {:type=>:single_object,:data=>res}
230
+ when :permission
231
+ command_perm=self.options.get_next_command([:list,:create])
232
+ case command_perm
233
+ when :list
234
+ # generic options : TODO: as arg ?
235
+ list_options||={'include'=>['[]','access_level','permission_count']}
236
+ # special value: ALL will show all permissions
237
+ if !VAL_ALL.eql?(node_file[:file_id])
238
+ # add which one to get
239
+ list_options['file_id']=node_file[:file_id]
240
+ list_options['inherited']||=false
241
+ end
242
+ #option_url_query
243
+ items=node_api.read('permissions',list_options)[:data]
244
+ return {:type=>:object_list,:data=>items}
245
+ when :create
246
+ #create_param=self.options.get_next_argument("creation data (Hash)")
247
+ set_workspace_info
248
+ access_id="ASPERA_ACCESS_KEY_ADMIN_WS_#{@workspace_id}"
249
+ node_file[:node_info]
250
+ params={
251
+ 'file_id' =>node_file[:file_id], # mandatory
252
+ 'access_type' =>'user', # mandatory: user or group
253
+ 'access_id' =>access_id, # id of user or group
254
+ 'access_levels'=>Aspera::Node::ACCESS_LEVELS,
255
+ 'tags' =>{'aspera'=>{'files'=>{'workspace'=>{
256
+ 'id' =>@workspace_id,
257
+ 'workspace_name' =>@workspace_name,
258
+ 'user_name' =>c_user_info['name'],
259
+ 'shared_by_user_id'=>c_user_info['id'],
260
+ 'shared_by_name' =>c_user_info['name'],
261
+ 'shared_by_email' =>c_user_info['email'],
262
+ 'shared_with_name' =>access_id,
263
+ 'access_key' =>node_file[:node_info]['access_key'],
264
+ 'node' =>node_file[:node_info]['name']}}}}}
265
+ item=node_api.create('permissions',params)[:data]
266
+ return {:type=>:single_object,:data=>item}
267
+ else raise "internal error:shall not reach here (#{command_perm})"
268
+ end
269
+ raise "internal error:shall not reach here"
270
+ else raise "internal error:shall not reach here (#{command_node_file})"
256
271
  end
272
+ raise "internal error:shall not reach here"
273
+ when :permissions
274
+
257
275
  end # command_repo
258
- throw "ERR"
276
+ raise "ERR"
259
277
  end # execute_node_gen4_command
260
278
 
261
279
  # build constructor option list for AoC based on options of CLI
262
280
  def aoc_params(subpath)
263
281
  # copy command line options to args
264
- opt=[:link,:url,:auth,:client_id,:client_secret,:scope,:redirect_uri,:private_key,:username].inject({}){|m,i|m[i]=self.options.get_option(i,:optional);m}
282
+ opt=[:link,:url,:auth,:client_id,:client_secret,:scope,:redirect_uri,:private_key,:username,:password].inject({}){|m,i|m[i]=self.options.get_option(i,:optional);m}
265
283
  opt[:subpath]=subpath
266
284
  return opt
267
285
  end
268
286
 
269
- # Create a new AoC API REST object and set @api_aoc.
270
- # Parameters based on command line options
271
- # @return nil
272
- def update_aoc_api
273
- @api_aoc=AoC.new(aoc_params('api/v1'))
274
- # add access key secrets
275
- @api_aoc.add_secrets(self.config.get_secrets)
276
- return nil
277
- end
278
-
279
287
  # initialize apis and authentication
280
288
  # set:
281
289
  # @default_workspace_id
@@ -290,8 +298,8 @@ module Aspera
290
298
  @default_workspace_id=@url_token_data['data']['workspace_id']
291
299
  @persist_ids=[] # TODO : @url_token_data['id'] ?
292
300
  else
293
- @default_workspace_id=user_info['default_workspace_id']
294
- @persist_ids=[user_info['id']]
301
+ @default_workspace_id=c_user_info['default_workspace_id']
302
+ @persist_ids=[c_user_info['id']]
295
303
  end
296
304
 
297
305
  ws_name=self.options.get_option(:workspace,:optional)
@@ -401,7 +409,8 @@ module Aspera
401
409
  package_creation[recipient_list_field]=resolved_list
402
410
  end
403
411
 
404
- def url_query(default)
412
+ # private
413
+ def option_url_query(default)
405
414
  query=self.options.get_option(:query,:optional)||default
406
415
  Log.log.debug("Query=#{query}".bg_red)
407
416
  begin
@@ -420,8 +429,7 @@ module Aspera
420
429
  end
421
430
 
422
431
  def execute_admin_action
423
- self.options.set_option(:scope,AoC::SCOPE_FILES_ADMIN)
424
- update_aoc_api
432
+ @api_aoc.oauth.params[:scope]=AoC::SCOPE_FILES_ADMIN
425
433
  command_admin=self.options.get_next_command([ :ats, :resource, :usage_reports, :analytics, :subscription, :auth_providers ])
426
434
  case command_admin
427
435
  when :auth_providers
@@ -490,20 +498,7 @@ module Aspera
490
498
  :base_url => @api_aoc.params[:base_url]+'/admin/ats/pub/v1',
491
499
  :auth => {:scope => AoC::SCOPE_FILES_ADMIN_USER}
492
500
  }))
493
- return @ats.execute_action_gen(ats_api)
494
- # when :search_nodes
495
- # query=self.options.get_option(:query,:optional) || '*'
496
- # nodes=@api_aoc.read("search_nodes",{'q'=>query})[:data]
497
- # # simplify output
498
- # nodes=nodes.map do |i|
499
- # item=i['_source']
500
- # item['score']=i['_score']
501
- # nodedata=item['access_key_recursive_counts'].first
502
- # item.delete('access_key_recursive_counts')
503
- # item['node']=nodedata
504
- # item
505
- # end
506
- # return {:type=>:object_list,:data=>nodes,:fields=>['host_name','node_status.cluster_id','node_status.node_id']}
501
+ return Ats.new(@agents).execute_action_gen(ats_api)
507
502
  when :analytics
508
503
  analytics_api = Rest.new(@api_aoc.params.deep_merge({
509
504
  :base_url => @api_aoc.params[:base_url].gsub('/api/v1','')+'/analytics/v2',
@@ -513,19 +508,20 @@ module Aspera
513
508
  case command_analytics
514
509
  when :application_events
515
510
  event_type=command_analytics.to_s
516
- events=analytics_api.read("organizations/#{user_info['organization_id']}/#{event_type}")[:data][event_type]
511
+ events=analytics_api.read("organizations/#{c_user_info['organization_id']}/#{event_type}")[:data][event_type]
517
512
  return {:type=>:object_list,:data=>events}
518
513
  when :transfers
519
514
  event_type=command_analytics.to_s
520
515
  filter_resource=self.options.get_option(:name,:optional) || 'organizations'
521
516
  filter_id=self.options.get_option(:id,:optional) || case filter_resource
522
- when 'organizations'; user_info['organization_id']
523
- when 'users'; user_info['id']
524
- when 'nodes'; user_info['id']
517
+ when 'organizations'; c_user_info['organization_id']
518
+ when 'users'; c_user_info['id']
519
+ when 'nodes'; c_user_info['id']
525
520
  else raise "organizations or users for option --name"
526
521
  end
527
522
  #
528
523
  filter=self.options.get_option(:query,:optional) || {}
524
+ raise "query must be Hash" unless filter.is_a?(Hash)
529
525
  filter['limit']||=100
530
526
  if self.options.get_option(:once_only,:mandatory)
531
527
  saved_date=[]
@@ -542,7 +538,7 @@ module Aspera
542
538
  filter['stop_time'] = stop_datetime
543
539
  end
544
540
  notification=self.options.get_option(:notify,:optional)
545
- events=analytics_api.read("#{filter_resource}/#{filter_id}/#{event_type}",url_query(filter))[:data][event_type]
541
+ events=analytics_api.read("#{filter_resource}/#{filter_id}/#{event_type}",option_url_query(filter))[:data][event_type]
546
542
  startdate_persistency.save unless startdate_persistency.nil?
547
543
  if !notification.nil?
548
544
  require 'erb'
@@ -581,7 +577,7 @@ module Aspera
581
577
  supported_operations.push(:delete,*global_operations) unless singleton_object
582
578
  supported_operations.push(:v4,:v3) if resource_type.eql?(:node)
583
579
  supported_operations.push(:set_pub_key) if resource_type.eql?(:client)
584
- supported_operations.push(:shared_folders) if [:node,:workspace].include?(resource_type)
580
+ supported_operations.push(:shared_folders,:shared_create) if [:node,:workspace].include?(resource_type)
585
581
  command=self.options.get_next_command(supported_operations)
586
582
  # require identifier for non global commands
587
583
  if !singleton_object and !global_operations.include?(command)
@@ -624,8 +620,10 @@ module Aspera
624
620
  when :apps_new; list_query={:organization_apps=>true};default_fields=['app_type','available']
625
621
  when :client_registration_token; default_fields=['id','value','data.client_subject_scopes','created_at']
626
622
  end
627
- result=@api_aoc.read(resource_class_path,url_query(list_query))
628
- self.format.display_status("Items: #{result[:data].length}/#{result[:http]['X-Total-Count']}")
623
+ result=@api_aoc.read(resource_class_path,option_url_query(list_query))
624
+ count_msg="Items: #{result[:data].length}/#{result[:http]['X-Total-Count']}"
625
+ count_msg=count_msg.bg_red unless result[:data].length.eql?(result[:http]['X-Total-Count'].to_i)
626
+ self.format.display_status(count_msg)
629
627
  return {:type=>:object_list,:data=>result[:data],:fields=>default_fields}
630
628
  when :show
631
629
  object=@api_aoc.read(resource_instance_path)[:data]
@@ -652,7 +650,8 @@ module Aspera
652
650
  api_node=@api_aoc.get_node_api(res_data)
653
651
  return Node.new(@agents.merge(skip_basic_auth_options: true, node_api: api_node)).execute_action if command.eql?(:v3)
654
652
  ak_data=api_node.call({:operation=>'GET',:subpath=>"access_keys/#{res_data['access_key']}",:headers=>{'Accept'=>'application/json'}})[:data]
655
- return node_gen4_execute_action({node_info: res_data, file_id: ak_data['root_file_id']})
653
+ command_repo=self.options.get_next_command(NODE4_COMMANDS)
654
+ return execute_node_gen4_command(command_repo,{node_info: res_data, file_id: ak_data['root_file_id']})
656
655
  when :shared_folders
657
656
  read_params = case resource_type
658
657
  when :workspace;{'access_id'=>"ASPERA_ACCESS_KEY_ADMIN_WS_#{res_id}",'access_type'=>'user'}
@@ -663,16 +662,48 @@ module Aspera
663
662
  fields=case resource_type
664
663
  when :node;['id','file_id','file.path','access_type']
665
664
  when :workspace;['id','node_id','file_id','node_name','file.path','tags.aspera.files.workspace.share_as']
666
- else raise "error"
665
+ else raise "unexpected resource type #{resource_type}"
667
666
  end
668
667
  return { :type=>:object_list, :data =>res_data , :fields=>fields}
669
- else raise :ERROR
668
+ when :shared_create
669
+ # TODO
670
+ folder_path=self.options.get_next_argument('folder path in node')
671
+ user_create_data=self.options.get_next_argument('creation data (Hash)')
672
+ res_data=@api_aoc.read(resource_instance_path)[:data]
673
+ node_file = @api_aoc.resolve_node_file({node_info: res_data, file_id: ak_data['root_file_id']},folder_path)
674
+
675
+ #node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
676
+ #file_info = node_api.read("files/#{node_file[:file_id]}")[:data]
677
+
678
+ access_id="ASPERA_ACCESS_KEY_ADMIN_WS_#{@workspace_id}"
679
+ create_data={
680
+ 'file_id' =>node_file[:file_id],
681
+ 'access_type' =>'user',
682
+ 'access_id' =>access_id,
683
+ 'access_levels'=>['list','read','write','delete','mkdir','rename','preview'],
684
+ 'tags' =>{'aspera'=>{'files'=>{'workspace'=>{
685
+ 'id' =>@workspace_id,
686
+ 'workspace_name' =>@workspace_name,
687
+ 'share_as' =>File.basename(folder_path),
688
+ 'user_name' =>c_user_info['name'],
689
+ 'shared_by_user_id'=>c_user_info['id'],
690
+ 'shared_by_name' =>c_user_info['name'],
691
+ 'shared_by_email' =>c_user_info['email'],
692
+ 'shared_with_name' =>access_id,
693
+ 'access_key' =>node_file[:node_info]['access_key'],
694
+ 'node' =>node_file[:node_info]['name']}
695
+ }}}}
696
+ create_data.deep_merge!(user_create_data)
697
+ Log.dump(:data,create_data)
698
+ raise :ERROR
699
+ else raise "unknown command"
670
700
  end
671
701
  when :usage_reports
672
- return {:type=>:object_list,:data=>@api_aoc.read("usage_reports",{:workspace_id=>@workspace_id})[:data]}
702
+ return {:type=>:object_list,:data=>@api_aoc.read('usage_reports',{:workspace_id=>@workspace_id})[:data]}
673
703
  end
674
704
  end
675
705
 
706
+ # must be public
676
707
  ACTIONS=[ :apiinfo, :bearer_token, :organization, :tier_restrictions, :user, :workspace, :packages, :files, :gateway, :admin, :automation, :servers]
677
708
 
678
709
  def execute_action
@@ -693,23 +724,23 @@ module Aspera
693
724
  command=self.options.get_next_command([ :workspaces,:info,:shared_inboxes ])
694
725
  case command
695
726
  when :workspaces
696
- return {:type=>:object_list,:data=>@api_aoc.read("workspaces")[:data],:fields=>['id','name']}
727
+ return {:type=>:object_list,:data=>@api_aoc.read('workspaces')[:data],:fields=>['id','name']}
697
728
  # when :settings
698
729
  # return {:type=>:object_list,:data=>@api_aoc.read("client_settings/")[:data]}
699
730
  when :shared_inboxes
700
- query=url_query(nil)
731
+ query=option_url_query(nil)
701
732
  if query.nil?
702
733
  set_workspace_info
703
734
  query={'embed[]'=>'dropbox','workspace_id'=>@workspace_id,'aggregate_permissions_by_dropbox'=>true,'sort'=>'dropbox_name'}
704
735
  end
705
- return {:type=>:object_list,:data=>@api_aoc.read("dropbox_memberships",query)[:data],:fields=>['dropbox_id','dropbox.name']}
736
+ return {:type=>:object_list,:data=>@api_aoc.read('dropbox_memberships',query)[:data],:fields=>['dropbox_id','dropbox.name']}
706
737
  when :info
707
738
  command=self.options.get_next_command([ :show,:modify ])
708
739
  case command
709
740
  when :show
710
- return { :type=>:single_object, :data =>user_info }
741
+ return { :type=>:single_object, :data =>c_user_info }
711
742
  when :modify
712
- @api_aoc.update("users/#{user_info['id']}",self.options.get_next_argument('modified parameters (hash)'))
743
+ @api_aoc.update("users/#{c_user_info['id']}",self.options.get_next_argument('modified parameters (hash)'))
713
744
  return Main.result_status('modified')
714
745
  end
715
746
  end
@@ -722,7 +753,7 @@ module Aspera
722
753
  case command_pkg
723
754
  when :send
724
755
  package_creation=self.options.get_option(:value,:mandatory)
725
- raise CliBadArgument,"value must be hash, refer to doc" unless package_creation.is_a?(Hash)
756
+ raise CliBadArgument,'value must be hash, refer to doc' unless package_creation.is_a?(Hash)
726
757
 
727
758
  if !@url_token_data.nil?
728
759
  assert_public_link_types(['send_package_to_user','send_package_to_dropbox'])
@@ -803,12 +834,12 @@ module Aspera
803
834
  return { :type=>:single_object, :data =>package_info }
804
835
  when :list
805
836
  # list all packages ('page'=>1,'per_page'=>10,)'sort'=>'-sent_at',
806
- packages=@api_aoc.read("packages",{'archived'=>false,'exclude_dropbox_packages'=>true,'has_content'=>true,'received'=>true,'workspace_id'=>@workspace_id})[:data]
837
+ packages=@api_aoc.read('packages',{'archived'=>false,'exclude_dropbox_packages'=>true,'has_content'=>true,'received'=>true,'workspace_id'=>@workspace_id})[:data]
807
838
  return {:type=>:object_list,:data=>packages,:fields=>['id','name','bytes_transferred']}
808
839
  when :delete
809
840
  list_or_one=self.options.get_option(:id,:mandatory)
810
841
  return do_bulk_operation(list_or_one,'deleted')do|id|
811
- raise "expecting String identifier" unless id.is_a?(String) or id.is_a?(Integer)
842
+ raise 'expecting String identifier' unless id.is_a?(String) or id.is_a?(Integer)
812
843
  @api_aoc.delete("packages/#{id}")[:data]
813
844
  end
814
845
  end
@@ -829,7 +860,7 @@ module Aspera
829
860
  when 'private'
830
861
  value_option={'purpose'=>'shared_folder_auth_link'}
831
862
  when NilClass,Hash
832
- else raise "value must be either: public, private, Hash or nil"
863
+ else raise 'value must be either: public, private, Hash or nil'
833
864
  end
834
865
  create_params=nil
835
866
  node_file=nil
@@ -857,30 +888,33 @@ module Aspera
857
888
  }
858
889
  value_option['user_selected_name']=nil
859
890
  else
860
- raise "purpose must be one of: token_auth_redirection or shared_folder_auth_link"
891
+ raise 'purpose must be one of: token_auth_redirection or shared_folder_auth_link'
861
892
  end
862
893
  self.options.set_option(:value,value_option)
863
894
  end
864
895
  result=self.entity_action(@api_aoc,'short_links',nil,:id,'self')
865
896
  if result[:data].is_a?(Hash) and result[:data].has_key?('created_at') and result[:data]['resource_type'].eql?('UrlToken')
866
897
  node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
898
+ # TODO: access level as arg
899
+ access_levels=Aspera::Node::ACCESS_LEVELS #['delete','list','mkdir','preview','read','rename','write']
867
900
  perm_data={
868
- "file_id" =>node_file[:file_id],
869
- "access_type" =>"user",
870
- "access_id" =>result[:data]['resource_id'],
871
- "access_levels"=>["delete","list","mkdir","preview","read","rename","write"],
872
- "tags" =>{
873
- "url_token" =>true,
874
- "workspace_id" =>@workspace_id,
875
- "workspace_name" =>@workspace_name,
876
- "folder_name" =>"my folder",
877
- "created_by_name" =>user_info['name'],
878
- "created_by_email"=>user_info['email'],
879
- "access_key" =>node_file[:node_info]['access_key'],
880
- "node" =>node_file[:node_info]['host']
901
+ 'file_id' =>node_file[:file_id],
902
+ 'access_type' =>'user',
903
+ 'access_id' =>result[:data]['resource_id'],
904
+ 'access_levels'=>access_levels,
905
+ 'tags' =>{
906
+ 'url_token' =>true,
907
+ 'workspace_id' =>@workspace_id,
908
+ 'workspace_name' =>@workspace_name,
909
+ 'folder_name' =>'my folder',
910
+ 'created_by_name' =>c_user_info['name'],
911
+ 'created_by_email'=>c_user_info['email'],
912
+ 'access_key' =>node_file[:node_info]['access_key'],
913
+ 'node' =>node_file[:node_info]['host']
881
914
  }
882
915
  }
883
916
  node_api.create("permissions?file_id=#{node_file[:file_id]}",perm_data)
917
+ # TODO: event ?
884
918
  end
885
919
  return result
886
920
  end # files command
@@ -923,17 +957,20 @@ module Aspera
923
957
  when :admin
924
958
  return execute_admin_action
925
959
  when :servers
926
- self.format.display_status("Beta feature")
927
- server_api=Rest.new(base_url: 'https://eudemo.asperademo.com')
928
- require 'json'
929
- servers=JSON.parse(server_api.read('servers')[:data])
960
+ self.format.display_status("Beta feature: #{@api_aoc.params[:base_url]}")
961
+ server_api=Rest.new(base_url: @api_aoc.params[:base_url])
962
+ servers=server_api.read('servers')[:data]
930
963
  return {:type=>:object_list,:data=>servers}
931
964
  else
932
965
  raise "internal error: #{command}"
933
966
  end # action
934
967
  raise RuntimeError, "internal error: command shall return"
935
968
  end
936
- end # Aspera
969
+
970
+ private :find_ak_secret,:c_user_info,:aoc_params,:set_workspace_info,:set_home_node_file,:do_bulk_operation,:resolve_package_recipients,:option_url_query,:assert_public_link_types,:execute_admin_action
971
+ private_constant :VAL_ALL,:NODE4_COMMANDS
972
+
973
+ end # AoC
937
974
  end # Plugins
938
975
  end # Cli
939
976
  end # Aspera