aspera-cli 4.0.0 → 4.1.0

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