aspera-cli 4.2.1 → 4.5.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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1580 -946
  3. data/bin/ascli +1 -1
  4. data/bin/asession +3 -5
  5. data/docs/Makefile +8 -11
  6. data/docs/README.erb.md +1521 -829
  7. data/docs/doc_tools.rb +58 -0
  8. data/docs/test_env.conf +3 -1
  9. data/examples/faspex4.rb +28 -19
  10. data/examples/transfer.rb +2 -2
  11. data/lib/aspera/aoc.rb +157 -134
  12. data/lib/aspera/cli/listener/progress_multi.rb +5 -5
  13. data/lib/aspera/cli/main.rb +106 -48
  14. data/lib/aspera/cli/manager.rb +19 -20
  15. data/lib/aspera/cli/plugin.rb +22 -7
  16. data/lib/aspera/cli/plugins/aoc.rb +260 -208
  17. data/lib/aspera/cli/plugins/ats.rb +11 -10
  18. data/lib/aspera/cli/plugins/bss.rb +2 -2
  19. data/lib/aspera/cli/plugins/config.rb +360 -189
  20. data/lib/aspera/cli/plugins/faspex.rb +119 -56
  21. data/lib/aspera/cli/plugins/faspex5.rb +32 -17
  22. data/lib/aspera/cli/plugins/node.rb +72 -31
  23. data/lib/aspera/cli/plugins/orchestrator.rb +5 -3
  24. data/lib/aspera/cli/plugins/preview.rb +94 -68
  25. data/lib/aspera/cli/plugins/server.rb +16 -5
  26. data/lib/aspera/cli/plugins/shares.rb +17 -0
  27. data/lib/aspera/cli/transfer_agent.rb +64 -82
  28. data/lib/aspera/cli/version.rb +1 -1
  29. data/lib/aspera/command_line_builder.rb +48 -31
  30. data/lib/aspera/cos_node.rb +4 -3
  31. data/lib/aspera/environment.rb +4 -4
  32. data/lib/aspera/fasp/{manager.rb → agent_base.rb} +7 -6
  33. data/lib/aspera/fasp/{connect.rb → agent_connect.rb} +46 -39
  34. data/lib/aspera/fasp/{local.rb → agent_direct.rb} +42 -38
  35. data/lib/aspera/fasp/{http_gw.rb → agent_httpgw.rb} +50 -29
  36. data/lib/aspera/fasp/{node.rb → agent_node.rb} +43 -4
  37. data/lib/aspera/fasp/agent_trsdk.rb +106 -0
  38. data/lib/aspera/fasp/default.rb +17 -0
  39. data/lib/aspera/fasp/installation.rb +64 -48
  40. data/lib/aspera/fasp/parameters.rb +78 -91
  41. data/lib/aspera/fasp/parameters.yaml +531 -0
  42. data/lib/aspera/fasp/uri.rb +1 -1
  43. data/lib/aspera/faspex_gw.rb +12 -11
  44. data/lib/aspera/id_generator.rb +22 -0
  45. data/lib/aspera/keychain/encrypted_hash.rb +120 -0
  46. data/lib/aspera/keychain/macos_security.rb +94 -0
  47. data/lib/aspera/log.rb +45 -32
  48. data/lib/aspera/node.rb +9 -4
  49. data/lib/aspera/oauth.rb +116 -100
  50. data/lib/aspera/persistency_action_once.rb +11 -7
  51. data/lib/aspera/persistency_folder.rb +6 -26
  52. data/lib/aspera/rest.rb +66 -50
  53. data/lib/aspera/sync.rb +40 -35
  54. data/lib/aspera/timer_limiter.rb +22 -0
  55. metadata +86 -29
  56. data/docs/transfer_spec.html +0 -99
  57. data/lib/aspera/api_detector.rb +0 -60
  58. data/lib/aspera/fasp/aoc.rb +0 -24
  59. data/lib/aspera/secrets.rb +0 -20
@@ -2,19 +2,32 @@ require 'aspera/cli/plugins/node'
2
2
  require 'aspera/cli/plugins/ats'
3
3
  require 'aspera/cli/basic_auth_plugin'
4
4
  require 'aspera/cli/transfer_agent'
5
+ require 'aspera/fasp/agent_node'
5
6
  require 'aspera/aoc'
6
7
  require 'aspera/node'
7
8
  require 'aspera/persistency_action_once'
9
+ require 'aspera/id_generator'
8
10
  require 'securerandom'
9
- require 'resolv'
10
11
  require 'date'
11
12
 
12
13
  module Aspera
13
14
  module Cli
14
15
  module Plugins
15
16
  class Aoc < BasicAuthPlugin
17
+ class << self
18
+ def detect(base_url)
19
+ api=Rest.new({base_url: base_url})
20
+ result=api.call({operation: 'GET',subpath: '',headers: {'Accept'=>'text/html'}})
21
+ if result[:http].body.include?('content="AoC"')
22
+ return {product: :aoc,version: 'unknown'}
23
+ end
24
+ return nil
25
+ end
26
+ end
16
27
  # special value for package id
17
28
  VAL_ALL='ALL'
29
+ ID_AK_ADMIN='ASPERA_ACCESS_KEY_ADMIN'
30
+
18
31
  def initialize(env)
19
32
  super(env)
20
33
  @default_workspace_id=nil
@@ -24,7 +37,6 @@ module Aspera
24
37
  @home_node_file=nil
25
38
  @api_aoc=nil
26
39
  @url_token_data=nil
27
- @user_info=nil
28
40
  @api_aoc=nil
29
41
  self.options.add_opt_list(:auth,Oauth.auth_types,'OAuth type of authentication')
30
42
  self.options.add_opt_list(:operation,[:push,:pull],'client operation for transfers')
@@ -39,7 +51,6 @@ module Aspera
39
51
  self.options.add_opt_simple(:new_user_option,'new user creation option')
40
52
  self.options.add_opt_simple(:from_folder,'share to share source folder')
41
53
  self.options.add_opt_simple(:scope,'OAuth scope for AoC API calls')
42
- self.options.add_opt_simple(:notify,'notify users that file was received')
43
54
  self.options.add_opt_boolean(:bulk,'bulk operation')
44
55
  self.options.add_opt_boolean(:default_ports,'use standard FASP ports or get from node api')
45
56
  self.options.set_option(:bulk,:no)
@@ -58,44 +69,41 @@ module Aspera
58
69
  if @api_aoc.nil?
59
70
  @api_aoc=AoC.new(aoc_params(AoC::API_V1))
60
71
  # add keychain for access key secrets
61
- @api_aoc.key_chain=@agents[:secret]
72
+ @api_aoc.key_chain=@agents[:config]
62
73
  end
63
74
  return @api_aoc
64
75
  end
65
76
 
66
- # cached user information
67
- def c_user_info
68
- if @user_info.nil?
69
- # get our user's default information
70
- # self?embed[]=default_workspace&embed[]=organization
71
- @user_info=@api_aoc.read('self')[:data] rescue {
72
- 'name' => 'unknown',
73
- 'email' => 'unknown',
74
- }
75
- end
76
- return @user_info
77
- end
78
-
79
77
  # starts transfer using transfer agent
80
78
  def transfer_start(app,direction,node_file,ts_add)
81
79
  ts_add.deep_merge!(AoC.analytics_ts(app,direction,@workspace_id,@workspace_name))
82
- ts_add.deep_merge!(AoC.console_ts(app,c_user_info['name'],c_user_info['email']))
80
+ ts_add.deep_merge!(@api_aoc.console_ts(app))
83
81
  return self.transfer.start(*@api_aoc.tr_spec(app,direction,node_file,ts_add))
84
82
  end
85
83
 
86
- NODE4_COMMANDS=[ :browse, :find, :mkdir, :rename, :delete, :upload, :download, :transfer, :http_node_download, :v3, :file, :bearer_token_node ]
84
+ NODE4_COMMANDS=[ :browse, :find, :mkdir, :rename, :delete, :upload, :download, :transfer, :http_node_download, :v3, :file, :bearer_token_node, :node_info ].freeze
87
85
 
88
86
  def execute_node_gen4_command(command_repo,top_node_file)
89
87
  case command_repo
90
88
  when :bearer_token_node
91
89
  thepath=self.options.get_next_argument('path')
92
90
  node_file = @api_aoc.resolve_node_file(top_node_file,thepath)
93
- node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
91
+ node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER, use_secret: false)
94
92
  return Main.result_status(node_api.oauth_token)
93
+ when :node_info
94
+ thepath=self.options.get_next_argument('path')
95
+ node_file = @api_aoc.resolve_node_file(top_node_file,thepath)
96
+ node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER, use_secret: false)
97
+ return {type: :single_object,data: {
98
+ url: node_file[:node_info]['url'],
99
+ username: node_file[:node_info]['access_key'],
100
+ password: node_api.oauth_token,
101
+ root_id: node_file[:file_id]
102
+ }}
95
103
  when :browse
96
104
  thepath=self.options.get_next_argument('path')
97
105
  node_file = @api_aoc.resolve_node_file(top_node_file,thepath)
98
- node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
106
+ node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
99
107
  file_info = node_api.read("files/#{node_file[:file_id]}")[:data]
100
108
  if file_info['type'].eql?('folder')
101
109
  result=node_api.read("files/#{node_file[:file_id]}/files",self.options.get_option(:value,:optional))
@@ -104,33 +112,33 @@ module Aspera
104
112
  else
105
113
  items=[file_info]
106
114
  end
107
- return {:type=>:object_list,:data=>items,:fields=>['name','type','recursive_size','size','modified_time','access_level']}
115
+ return {type: :object_list,data: items,fields: ['name','type','recursive_size','size','modified_time','access_level']}
108
116
  when :find
109
117
  thepath=self.options.get_next_argument('path')
110
118
  node_file=@api_aoc.resolve_node_file(top_node_file,thepath)
111
119
  test_block=Aspera::Node.file_matcher(self.options.get_option(:value,:optional))
112
- return {:type=>:object_list,:data=>@api_aoc.find_files(node_file,test_block),:fields=>['path']}
120
+ return {type: :object_list,data: @api_aoc.find_files(node_file,test_block),fields: ['path']}
113
121
  when :mkdir
114
122
  thepath=self.options.get_next_argument('path')
115
123
  containing_folder_path = thepath.split(AoC::PATH_SEPARATOR)
116
124
  new_folder=containing_folder_path.pop
117
125
  node_file = @api_aoc.resolve_node_file(top_node_file,containing_folder_path.join(AoC::PATH_SEPARATOR))
118
- node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
119
- result=node_api.create("files/#{node_file[:file_id]}/files",{:name=>new_folder,:type=>:folder})[:data]
126
+ node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
127
+ result=node_api.create("files/#{node_file[:file_id]}/files",{name: new_folder,type: :folder})[:data]
120
128
  return Main.result_status("created: #{result['name']} (id=#{result['id']})")
121
129
  when :rename
122
130
  thepath=self.options.get_next_argument('source path')
123
131
  newname=self.options.get_next_argument('new name')
124
132
  node_file = @api_aoc.resolve_node_file(top_node_file,thepath)
125
- node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
126
- result=node_api.update("files/#{node_file[:file_id]}",{:name=>newname})[:data]
133
+ node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
134
+ result=node_api.update("files/#{node_file[:file_id]}",{name: newname})[:data]
127
135
  return Main.result_status("renamed #{thepath} to #{newname}")
128
136
  when :delete
129
137
  thepath=self.options.get_next_argument('path')
130
138
  return do_bulk_operation(thepath,'deleted','path') do |l_path|
131
139
  raise "expecting String (path), got #{l_path.class.name} (#{l_path})" unless l_path.is_a?(String)
132
140
  node_file = @api_aoc.resolve_node_file(top_node_file,l_path)
133
- node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
141
+ node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
134
142
  result=node_api.delete("files/#{node_file[:file_id]}")[:data]
135
143
  {'path'=>l_path}
136
144
  end
@@ -153,7 +161,13 @@ module Aspera
153
161
  client_node_file = @api_aoc.resolve_node_file(client_home_node_file,client_folder)
154
162
  server_node_file = @api_aoc.resolve_node_file(server_home_node_file,server_folder)
155
163
  # force node as transfer agent
156
- @agents[:transfer].set_agent_instance(Fasp::Node.new(@api_aoc.get_node_api(client_node_file[:node_info],AoC::SCOPE_NODE_USER)))
164
+ client_node_api=@api_aoc.get_node_api(client_node_file[:node_info],scope: AoC::SCOPE_NODE_USER, use_secret: false)
165
+ @agents[:transfer].set_agent_instance(Fasp::AgentNode.new({
166
+ url: client_node_api.params[:base_url],
167
+ username: client_node_file[:node_info]['access_key'],
168
+ password: client_node_api.oauth_token,
169
+ root_id: client_node_file[:file_id]
170
+ }))
157
171
  # additional node to node TS info
158
172
  add_ts={
159
173
  'remote_access_key' => server_node_file[:node_info]['access_key'],
@@ -191,37 +205,37 @@ module Aspera
191
205
  raise CliBadArgument,'one file at a time only in HTTP mode' if source_paths.length > 1
192
206
  file_name = source_paths.first['source']
193
207
  node_file = @api_aoc.resolve_node_file(top_node_file,File.join(source_folder,file_name))
194
- node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
195
- node_api.call({:operation=>'GET',:subpath=>"files/#{node_file[:file_id]}/content",:save_to_file=>File.join(self.transfer.destination_folder('receive'),file_name)})
208
+ node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
209
+ node_api.call({operation: 'GET',subpath: "files/#{node_file[:file_id]}/content",save_to_file: File.join(self.transfer.destination_folder('receive'),file_name)})
196
210
  return Main.result_status("downloaded: #{file_name}")
197
211
  when :v3
198
- # Note: other "common" actions are unauthorized with user scope
212
+ # Note: other common actions are unauthorized with user scope
199
213
  command_legacy=self.options.get_next_command(Node::SIMPLE_ACTIONS)
200
214
  # TODO: shall we support all methods here ? what if there is a link ?
201
- node_api=@api_aoc.get_node_api(top_node_file[:node_info],AoC::SCOPE_NODE_USER)
215
+ node_api=@api_aoc.get_node_api(top_node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
202
216
  return Node.new(@agents.merge(skip_basic_auth_options: true, node_api: node_api)).execute_action(command_legacy)
203
217
  when :file
218
+ command_node_file=self.options.get_next_command([:show,:permission,:modify])
204
219
  file_path=self.options.get_option(:path,:optional)
205
220
  node_file = if !file_path.nil?
206
221
  @api_aoc.resolve_node_file(top_node_file,file_path) # TODO: allow follow link ?
207
222
  else
208
- {node_info: top_node_file[:node_info],file_id: self.options.get_option(:id,:mandatory)}
223
+ {node_info: top_node_file[:node_info],file_id: self.instance_identifier()}
209
224
  end
210
- node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
211
- command_node_file=self.options.get_next_command([:show,:permission,:modify])
225
+ node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
212
226
  case command_node_file
213
227
  when :show
214
228
  items=node_api.read("files/#{node_file[:file_id]}")[:data]
215
- return {:type=>:single_object,:data=>items}
229
+ return {type: :single_object,data: items}
216
230
  when :modify
217
- update_param=self.options.get_next_argument("update data (Hash)")
231
+ update_param=self.options.get_next_argument('update data (Hash)')
218
232
  res=node_api.update("files/#{node_file[:file_id]}",update_param)[:data]
219
- return {:type=>:single_object,:data=>res}
233
+ return {type: :single_object,data: res}
220
234
  when :permission
221
235
  command_perm=self.options.get_next_command([:list,:create])
222
236
  case command_perm
223
237
  when :list
224
- # generic options : TODO: as arg ?
238
+ # generic options : TODO: as arg ? option_url_query
225
239
  list_options||={'include'=>['[]','access_level','permission_count']}
226
240
  # special value: ALL will show all permissions
227
241
  if !VAL_ALL.eql?(node_file[:file_id])
@@ -229,13 +243,12 @@ module Aspera
229
243
  list_options['file_id']=node_file[:file_id]
230
244
  list_options['inherited']||=false
231
245
  end
232
- #option_url_query
233
246
  items=node_api.read('permissions',list_options)[:data]
234
- return {:type=>:object_list,:data=>items}
247
+ return {type: :object_list,data: items}
235
248
  when :create
236
- #create_param=self.options.get_next_argument("creation data (Hash)")
249
+ #create_param=self.options.get_next_argument('creation data (Hash)')
237
250
  set_workspace_info
238
- access_id="ASPERA_ACCESS_KEY_ADMIN_WS_#{@workspace_id}"
251
+ access_id="#{ID_AK_ADMIN}_WS_#{@workspace_id}"
239
252
  node_file[:node_info]
240
253
  params={
241
254
  'file_id' =>node_file[:file_id], # mandatory
@@ -245,25 +258,23 @@ module Aspera
245
258
  'tags' =>{'aspera'=>{'files'=>{'workspace'=>{
246
259
  'id' =>@workspace_id,
247
260
  'workspace_name' =>@workspace_name,
248
- 'user_name' =>c_user_info['name'],
249
- 'shared_by_user_id'=>c_user_info['id'],
250
- 'shared_by_name' =>c_user_info['name'],
251
- 'shared_by_email' =>c_user_info['email'],
261
+ 'user_name' =>@api_aoc.user_info['name'],
262
+ 'shared_by_user_id'=>@api_aoc.user_info['id'],
263
+ 'shared_by_name' =>@api_aoc.user_info['name'],
264
+ 'shared_by_email' =>@api_aoc.user_info['email'],
252
265
  'shared_with_name' =>access_id,
253
266
  'access_key' =>node_file[:node_info]['access_key'],
254
267
  'node' =>node_file[:node_info]['name']}}}}}
255
268
  item=node_api.create('permissions',params)[:data]
256
- return {:type=>:single_object,:data=>item}
269
+ return {type: :single_object,data: item}
257
270
  else raise "internal error:shall not reach here (#{command_perm})"
258
271
  end
259
- raise "internal error:shall not reach here"
272
+ raise 'internal error:shall not reach here'
260
273
  else raise "internal error:shall not reach here (#{command_node_file})"
261
274
  end
262
- raise "internal error:shall not reach here"
263
- when :permissions
264
-
275
+ raise 'internal error:shall not reach here'
265
276
  end # command_repo
266
- raise "ERR"
277
+ raise 'ERR'
267
278
  end # execute_node_gen4_command
268
279
 
269
280
  # build constructor option list for AoC based on options of CLI
@@ -288,21 +299,21 @@ module Aspera
288
299
  @default_workspace_id=@url_token_data['data']['workspace_id']
289
300
  @persist_ids=[] # TODO : @url_token_data['id'] ?
290
301
  else
291
- @default_workspace_id=c_user_info['default_workspace_id']
292
- @persist_ids=[c_user_info['id']]
302
+ @default_workspace_id=@api_aoc.user_info['default_workspace_id']
303
+ @persist_ids=[@api_aoc.user_info['id']]
293
304
  end
294
305
 
295
306
  ws_name=self.options.get_option(:workspace,:optional)
296
307
  if ws_name.nil?
297
- Log.log.debug("using default workspace".green)
308
+ Log.log.debug('using default workspace'.green)
298
309
  if @default_workspace_id.eql?(nil)
299
- raise CliError,"no default workspace defined for user, please specify workspace"
310
+ raise CliError,'no default workspace defined for user, please specify workspace'
300
311
  end
301
312
  # get default workspace
302
313
  @workspace_id=@default_workspace_id
303
314
  else
304
315
  # lookup another workspace
305
- wss=@api_aoc.read("workspaces",{'q'=>ws_name})[:data]
316
+ wss=@api_aoc.read('workspaces',{'q'=>ws_name})[:data]
306
317
  wss=wss.select { |i| i['name'].eql?(ws_name) }
307
318
  case wss.length
308
319
  when 0
@@ -310,14 +321,14 @@ module Aspera
310
321
  when 1
311
322
  @workspace_id=wss.first['id']
312
323
  else
313
- raise "unexpected case"
324
+ raise 'unexpected case'
314
325
  end
315
326
  end
316
327
  @workspace_data=@api_aoc.read("workspaces/#{@workspace_id}")[:data]
317
328
  Log.log.debug("workspace_id=#{@workspace_id},@workspace_data=#{@workspace_data}".red)
318
329
 
319
330
  @workspace_name||=@workspace_data['name']
320
- Log.log.info("current workspace is "+@workspace_name.red)
331
+ Log.log.info('current workspace is '+@workspace_name.red)
321
332
 
322
333
  # display workspace
323
334
  self.format.display_status("Current Workspace: #{@workspace_name.red}#{@workspace_id == @default_workspace_id ? ' (default)' : ''}")
@@ -333,7 +344,7 @@ module Aspera
333
344
  end
334
345
  home_node_id||=@workspace_data['home_node_id']||@workspace_data['node_id']
335
346
  home_file_id||=@workspace_data['home_file_id']
336
- raise "node_id must be defined" if home_node_id.to_s.empty?
347
+ raise 'node_id must be defined' if home_node_id.to_s.empty?
337
348
  @home_node_file={
338
349
  node_info: @api_aoc.read("nodes/#{home_node_id}")[:data],
339
350
  file_id: home_file_id
@@ -345,7 +356,7 @@ module Aspera
345
356
 
346
357
  def do_bulk_operation(ids_or_one,success_msg,id_result='id',&do_action)
347
358
  ids_or_one=[ids_or_one] unless self.options.get_option(:bulk)
348
- raise "expecting Array" unless ids_or_one.is_a?(Array)
359
+ raise 'expecting Array' unless ids_or_one.is_a?(Array)
349
360
  result_list=[]
350
361
  ids_or_one.each do |id|
351
362
  one={id_result=>id}
@@ -358,7 +369,27 @@ module Aspera
358
369
  end
359
370
  result_list.push(one)
360
371
  end
361
- return {:type=>:object_list,:data=>result_list,:fields=>[id_result,'status']}
372
+ return {type: :object_list,data: result_list,fields: [id_result,'status']}
373
+ end
374
+
375
+ NOT_FOUND="not found"
376
+
377
+ def lookup_single(entity_type,entity_name,options={})
378
+ # returns entities whose name contains value (case insensitive)
379
+ matching_items=@api_aoc.read(entity_type,options.merge({'q'=>entity_name}))[:data]
380
+ case matching_items.length
381
+ when 1; return matching_items.first
382
+ when 0; raise RuntimeError,NOT_FOUND
383
+ else
384
+ # multiple case insensitive partial matches, try case insensitive full match
385
+ # (anyway AoC does not allow creation of 2 entities with same case insensitive name)
386
+ icase_matches=matching_items.select{|i|i['name'].casecmp?(entity_name)}
387
+ case icase_matches.length
388
+ when 1; return icase_matches.first
389
+ when 0; raise CliBadArgument,"#{entity_type}: multiple case insensitive partial match for: \"#{entity_name}\": #{matching_items.map{|i|i['name']}} but no case insensitive full match. Please be more specific or give exact name."
390
+ else raise "Two entities cannot have the same case insensitive name: #{icase_matches.map{|i|i['name']}}"
391
+ end
392
+ end
362
393
  end
363
394
 
364
395
  # package creation params can give just email, and full hash is created
@@ -366,42 +397,44 @@ module Aspera
366
397
  return unless package_creation.has_key?(recipient_list_field)
367
398
  raise CliBadArgument,"#{recipient_list_field} must be an Array" unless package_creation[recipient_list_field].is_a?(Array)
368
399
  new_user_option=self.options.get_option(:new_user_option,:mandatory)
400
+ # list with resolved elements
369
401
  resolved_list=[]
370
- package_creation[recipient_list_field].each do |recipient_email_or_info|
371
- case recipient_email_or_info
372
- when Hash
373
- raise 'recipient element hash shall have field id and type' unless recipient_email_or_info.has_key?('id') and recipient_email_or_info.has_key?('type')
374
- # already provided all information ?
375
- resolved_list.push(recipient_email_or_info)
376
- when String
377
- if recipient_email_or_info.include?('@')
378
- # or need to resolve email
379
- item_lookup=@api_aoc.read('contacts',{'current_workspace_id'=>@workspace_id,'q'=>recipient_email_or_info})[:data]
380
- case item_lookup.length
381
- when 1; recipient_user_id=item_lookup.first
382
- when 0; recipient_user_id=@api_aoc.create('contacts',{'current_workspace_id'=>@workspace_id,'email'=>recipient_email_or_info}.merge(new_user_option))[:data]
383
- else raise CliBadArgument,"multiple match for: #{recipient_email_or_info}"
402
+ package_creation[recipient_list_field].each do |short_recipient_info|
403
+ case short_recipient_info
404
+ when Hash # native api information, check keys
405
+ raise "#{recipient_list_field} element shall have fields: id and type" unless short_recipient_info.keys.sort.eql?(['id','type'])
406
+ when String # need to resolve name to type/id
407
+ # email: user, else dropbox
408
+ entity_type=short_recipient_info.include?('@') ? 'contacts' : 'dropboxes'
409
+ begin
410
+ full_recipient_info=lookup_single(entity_type,short_recipient_info,{'current_workspace_id'=>@workspace_id})
411
+ rescue RuntimeError => e
412
+ raise e unless e.message.eql?(NOT_FOUND)
413
+ if entity_type.eql?('contacts')
414
+ full_recipient_info=@api_aoc.create('contacts',{'current_workspace_id'=>@workspace_id,'email'=>short_recipient_info}.merge(new_user_option))[:data]
415
+ else
416
+ raise "no such shared inbox in workspace #{@workspace_name}"
384
417
  end
385
- resolved_list.push({'id'=>recipient_user_id['source_id'],'type'=>recipient_user_id['source_type']})
418
+ end
419
+ if entity_type.eql?('dropboxes')
420
+ short_recipient_info={'id'=>full_recipient_info['id'],'type'=>'dropbox'}
386
421
  else
387
- item_lookup=@api_aoc.read('dropboxes',{'current_workspace_id'=>@workspace_id,'q'=>recipient_email_or_info})[:data]
388
- case item_lookup.length
389
- when 1; recipient_user_id=item_lookup.first
390
- when 0; raise "no such shared inbox in workspace #{@workspace_name}"
391
- else raise CliBadArgument,"multiple match for: #{recipient_email_or_info}"
392
- end
393
- resolved_list.push({'id'=>recipient_user_id['id'],'type'=>'dropbox'})
422
+ short_recipient_info={'id'=>full_recipient_info['source_id'],'type'=>full_recipient_info['source_type']}
394
423
  end
395
- else
396
- raise "recipient item must be a String (email, shared inboc) or hash (id,type)"
397
- end
424
+ else # unexpected extended value, must be String or Hash
425
+ raise "#{recipient_list_field} item must be a String (email, shared inbox) or Hash (id,type)"
426
+ end # type of recipient info
427
+ # add original or resolved recipient info
428
+ resolved_list.push(short_recipient_info)
398
429
  end
430
+ # replace with resolved elements
399
431
  package_creation[recipient_list_field]=resolved_list
400
432
  end
401
433
 
402
434
  # private
403
435
  def option_url_query(default)
404
- query=self.options.get_option(:query,:optional)||default
436
+ query=self.options.get_option(:query,:optional)
437
+ query=default if query.nil?
405
438
  Log.log.debug("Query=#{query}".bg_red)
406
439
  begin
407
440
  # check it is suitable
@@ -418,6 +451,37 @@ module Aspera
418
451
  end
419
452
  end
420
453
 
454
+ # Call @api_aoc.read with same parameters, but use paging if necessary to get all results
455
+ def read_with_paging(resource_class_path,base_query)
456
+ raise "Query must be Hash" unless base_query.is_a?(Hash)
457
+ # set default large page if user does not specify own parameters. AoC Caps to 1000 anyway
458
+ base_query['per_page']=1000 unless base_query.has_key?('per_page')
459
+ max_items=base_query[MAX_ITEMS]
460
+ base_query.delete(MAX_ITEMS)
461
+ max_pages=base_query[MAX_PAGES]
462
+ base_query.delete(MAX_PAGES)
463
+ item_list=[]
464
+ total_count=nil
465
+ current_page=base_query['page']
466
+ current_page=1 if current_page.nil?
467
+ page_count=0
468
+ loop do
469
+ query=base_query.clone
470
+ query['page']=current_page
471
+ result=@api_aoc.read(resource_class_path,query)
472
+ total_count=result[:http]['X-Total-Count']
473
+ page_count+=1
474
+ current_page+=1
475
+ add_items=result[:data]
476
+ break if add_items.empty?
477
+ # append new items to full list
478
+ item_list += add_items
479
+ break if !max_pages.nil? and page_count > max_pages
480
+ break if !max_items.nil? and item_list.count > max_items
481
+ end
482
+ return item_list,total_count
483
+ end
484
+
421
485
  def execute_admin_action
422
486
  @api_aoc.oauth.params[:scope]=AoC::SCOPE_FILES_ADMIN
423
487
  command_admin=self.options.get_next_command([ :ats, :resource, :usage_reports, :analytics, :subscription, :auth_providers ])
@@ -427,7 +491,7 @@ module Aspera
427
491
  case command_auth_prov
428
492
  when :list
429
493
  providers=@api_aoc.read('admin/auth_providers')[:data]
430
- return {:type=>:object_list,:data=>providers}
494
+ return {type: :object_list,data: providers}
431
495
  when :update
432
496
  end
433
497
  when :subscription
@@ -482,43 +546,43 @@ module Aspera
482
546
  }
483
547
  "
484
548
  result=bss_api.create('graphql',{'variables'=>{'organization_id'=>org['id']},'query'=>graphql_query})[:data]['data']
485
- return {:type=>:single_object,:data=>result['aoc']['bssSubscription']}
549
+ return {type: :single_object,data: result['aoc']['bssSubscription']}
486
550
  when :ats
487
551
  ats_api = Rest.new(@api_aoc.params.deep_merge({
488
- :base_url => @api_aoc.params[:base_url]+'/admin/ats/pub/v1',
489
- :auth => {:scope => AoC::SCOPE_FILES_ADMIN_USER}
552
+ base_url: @api_aoc.params[:base_url]+'/admin/ats/pub/v1',
553
+ auth: {scope: AoC::SCOPE_FILES_ADMIN_USER}
490
554
  }))
491
555
  return Ats.new(@agents).execute_action_gen(ats_api)
492
556
  when :analytics
493
557
  analytics_api = Rest.new(@api_aoc.params.deep_merge({
494
- :base_url => @api_aoc.params[:base_url].gsub('/api/v1','')+'/analytics/v2',
495
- :auth => {:scope => AoC::SCOPE_FILES_ADMIN_USER}
558
+ base_url: @api_aoc.params[:base_url].gsub('/api/v1','')+'/analytics/v2',
559
+ auth: {scope: AoC::SCOPE_FILES_ADMIN_USER}
496
560
  }))
497
561
  command_analytics=self.options.get_next_command([ :application_events, :transfers ])
498
562
  case command_analytics
499
563
  when :application_events
500
564
  event_type=command_analytics.to_s
501
- events=analytics_api.read("organizations/#{c_user_info['organization_id']}/#{event_type}")[:data][event_type]
502
- return {:type=>:object_list,:data=>events}
565
+ events=analytics_api.read("organizations/#{@api_aoc.user_info['organization_id']}/#{event_type}")[:data][event_type]
566
+ return {type: :object_list,data: events}
503
567
  when :transfers
504
568
  event_type=command_analytics.to_s
505
569
  filter_resource=self.options.get_option(:name,:optional) || 'organizations'
506
570
  filter_id=self.options.get_option(:id,:optional) || case filter_resource
507
- when 'organizations'; c_user_info['organization_id']
508
- when 'users'; c_user_info['id']
509
- when 'nodes'; c_user_info['id']
510
- else raise "organizations or users for option --name"
571
+ when 'organizations'; @api_aoc.user_info['organization_id']
572
+ when 'users'; @api_aoc.user_info['id']
573
+ when 'nodes'; @api_aoc.user_info['id']
574
+ else raise 'organizations or users for option --name'
511
575
  end
512
576
  #
513
577
  filter=self.options.get_option(:query,:optional) || {}
514
- raise "query must be Hash" unless filter.is_a?(Hash)
578
+ raise 'query must be Hash' unless filter.is_a?(Hash)
515
579
  filter['limit']||=100
516
580
  if self.options.get_option(:once_only,:mandatory)
517
581
  saved_date=[]
518
582
  startdate_persistency=PersistencyActionOnce.new(
519
583
  manager: @agents[:persistency],
520
584
  data: saved_date,
521
- ids: ['aoc_ana_date',self.options.get_option(:url,:mandatory),@workspace_name].push(filter_resource,filter_id))
585
+ ids: IdGenerator.from_list(['aoc_ana_date',self.options.get_option(:url,:mandatory),@workspace_name].push(filter_resource,filter_id)))
522
586
  start_datetime=saved_date.first
523
587
  stop_datetime=Time.now.utc.strftime('%FT%T.%LZ')
524
588
  #Log.log().error("start: #{start_datetime}")
@@ -527,38 +591,26 @@ module Aspera
527
591
  filter['start_time'] = start_datetime unless start_datetime.nil?
528
592
  filter['stop_time'] = stop_datetime
529
593
  end
530
- notification=self.options.get_option(:notify,:optional)
531
594
  events=analytics_api.read("#{filter_resource}/#{filter_id}/#{event_type}",option_url_query(filter))[:data][event_type]
532
595
  startdate_persistency.save unless startdate_persistency.nil?
533
- if !notification.nil?
534
- require 'erb'
535
- events.each do |transfer|
536
- email_to_send={}
537
- notification.each do |k,v|
538
- email_to_send[k.to_sym]=ERB.new(v).result(binding)
539
- end
540
- Log.log().error("send email: #{email_to_send}")
541
- self.config.send_email(email_to_send)
596
+ if !self.options.get_option(:notif_to,:optional).nil?
597
+ events.each do |tr_event|
598
+ self.config.send_email_template({ev: tr_event})
542
599
  end
543
600
  end
544
- return {:type=>:object_list,:data=>events}
601
+ return {type: :object_list,data: events}
545
602
  end
546
603
  when :resource
547
- resource_type=self.options.get_next_argument('resource',[:self,:organization,:user,:group,:client,:contact,:dropbox,:node,:operation,:package,:saml_configuration, :workspace, :dropbox_membership,:short_link,:workspace_membership,:apps_new,:client_registration_token,:client_access_key,:kms_profile])
548
- # get path on API
604
+ resource_type=self.options.get_next_argument('resource',[:self,:organization,:user,:group,:client,:contact,:dropbox,:node,:operation,:package,:saml_configuration, :workspace, :dropbox_membership,:short_link,:workspace_membership,:application,:client_registration_token,:client_access_key,:kms_profile])
605
+ # get path on API, resource type is singular, but api is plural
549
606
  resource_class_path=case resource_type
550
- when :self,:organization
551
- "#{resource_type}"
552
- when :apps_new
553
- "admin/#{resource_type}"
554
- when :dropbox
555
- resource_type.to_s+'es'
556
- when :client_registration_token,:client_access_key
557
- "admin/#{resource_type}s"
558
- when :kms_profile
559
- "integrations/#{resource_type}s"
560
- else
561
- resource_type.to_s+'s'
607
+ # special cases: singleton, in admin, with x
608
+ when :self,:organization; resource_type
609
+ when :client_registration_token,:client_access_key; "admin/#{resource_type}s"
610
+ when :application; 'admin/apps_new'
611
+ when :dropbox; resource_type.to_s+'es'
612
+ when :kms_profile; "integrations/#{resource_type}s"
613
+ else resource_type.to_s+'s'
562
614
  end
563
615
  # build list of supported operations
564
616
  singleton_object=[:self,:organization].include?(resource_type)
@@ -573,19 +625,14 @@ module Aspera
573
625
  if !singleton_object and !global_operations.include?(command)
574
626
  res_id=self.options.get_option(:id)
575
627
  res_name=self.options.get_option(:name)
576
- if res_id.nil? and res_name.nil? and resource_type.eql?(:node)
577
- set_workspace_info
578
- set_home_node_file
579
- res_id=@home_node_file[:node_info]['id']
580
- end
581
- if !res_name.nil?
582
- Log.log.warn("name overrides id") unless res_id.nil?
583
- matching=@api_aoc.read(resource_class_path,{:q=>res_name})[:data]
584
- raise CliError,"no resource match name" if matching.empty?
585
- raise CliError,"several resources match name (#{matching.join(',')})" unless matching.length.eql?(1)
586
- res_id=matching.first['id']
628
+ raise "Provide id or name, not both" unless res_id.nil? or res_name.nil?
629
+ # try to find item by name (single partial match or exact match)
630
+ res_id=lookup_single(resource_class_path,res_name)['id'] if !res_name.nil?
631
+ # if no name or id option, taken on command line (after command)
632
+ if res_id.nil?
633
+ res_id=self.options.get_next_argument('identifier')
634
+ res_id=lookup_single(resource_class_path,self.options.get_next_argument('identifier'))['id'] if res_id.eql?('name')
587
635
  end
588
- raise CliBadArgument,"provide either id or name" if res_id.nil?
589
636
  resource_instance_path="#{resource_class_path}/#{res_id}"
590
637
  end
591
638
  resource_instance_path=resource_class_path if singleton_object
@@ -595,30 +642,33 @@ module Aspera
595
642
  id_result='token' if resource_class_path.eql?('admin/client_registration_tokens')
596
643
  # TODO: report inconsistency: creation url is !=, and does not return id.
597
644
  resource_class_path='admin/client_registration/token' if resource_class_path.eql?('admin/client_registration_tokens')
598
- list_or_one=self.options.get_next_argument("creation data (Hash)")
645
+ list_or_one=self.options.get_next_argument('creation data (Hash)')
599
646
  return do_bulk_operation(list_or_one,'created',id_result)do|params|
600
- raise "expecting Hash" unless params.is_a?(Hash)
647
+ raise 'expecting Hash' unless params.is_a?(Hash)
601
648
  @api_aoc.create(resource_class_path,params)[:data]
602
649
  end
603
650
  when :list
604
- default_fields=['id','name']
605
- list_query=nil
651
+ default_fields=['id']
652
+ default_query={}
606
653
  case resource_type
607
- when :node; default_fields.push('host','access_key')
654
+ when :application; default_query={organization_apps: true};default_fields.push('app_type','app_name','available','direct_authorizations_allowed','workspace_authorizations_allowed')
655
+ when :client,:client_access_key,:dropbox,:group,:package,:saml_configuration,:workspace; default_fields.push('name')
656
+ when :client_registration_token; default_fields.push('value','data.client_subject_scopes','created_at')
657
+ when :contact; default_fields=['email','name','source_id','source_type']
658
+ when :node; default_fields.push('name','host','access_key')
608
659
  when :operation; default_fields=nil
609
- when :contact; default_fields=["email","name","source_id","source_type"]
610
- when :apps_new; list_query={:organization_apps=>true};default_fields=['app_type','available']
611
- when :client_registration_token; default_fields=['id','value','data.client_subject_scopes','created_at']
660
+ when :short_link; default_fields.push('short_url','data.url_token_data.purpose')
661
+ when :user; default_fields.push('name','email')
612
662
  end
613
- result=@api_aoc.read(resource_class_path,option_url_query(list_query))
614
- count_msg="Items: #{result[:data].length}/#{result[:http]['X-Total-Count']}"
615
- count_msg=count_msg.bg_red unless result[:data].length.eql?(result[:http]['X-Total-Count'].to_i)
663
+ item_list,total_count=read_with_paging(resource_class_path,option_url_query(default_query))
664
+ count_msg="Items: #{item_list.length}/#{total_count}"
665
+ count_msg=count_msg.bg_red unless item_list.length.eql?(total_count.to_i)
616
666
  self.format.display_status(count_msg)
617
- return {:type=>:object_list,:data=>result[:data],:fields=>default_fields}
667
+ return {type: :object_list,data: item_list,fields: default_fields}
618
668
  when :show
619
669
  object=@api_aoc.read(resource_instance_path)[:data]
620
670
  fields=object.keys.select{|k|!k.eql?('certificate')}
621
- return { :type=>:single_object, :data =>object, :fields=>fields }
671
+ return { type: :single_object, data: object, fields: fields }
622
672
  when :modify
623
673
  changes=self.options.get_next_argument('modified parameters (hash)')
624
674
  @api_aoc.update(resource_instance_path,changes)
@@ -632,20 +682,20 @@ module Aspera
632
682
  # special : reads private and generate public
633
683
  the_private_key=self.options.get_next_argument('private_key')
634
684
  the_public_key=OpenSSL::PKey::RSA.new(the_private_key).public_key.to_s
635
- @api_aoc.update(resource_instance_path,{:jwt_grant_enabled=>true, :public_key=>the_public_key})
685
+ @api_aoc.update(resource_instance_path,{jwt_grant_enabled: true, public_key: the_public_key})
636
686
  return Main.result_success
637
687
  when :v3,:v4
638
688
  res_data=@api_aoc.read(resource_instance_path)[:data]
639
689
  api_node=@api_aoc.get_node_api(res_data)
640
690
  return Node.new(@agents.merge(skip_basic_auth_options: true, node_api: api_node)).execute_action if command.eql?(:v3)
641
- ak_data=api_node.call({:operation=>'GET',:subpath=>"access_keys/#{res_data['access_key']}",:headers=>{'Accept'=>'application/json'}})[:data]
691
+ ak_data=api_node.call({operation: 'GET',subpath: "access_keys/#{res_data['access_key']}",headers: {'Accept'=>'application/json'}})[:data]
642
692
  command_repo=self.options.get_next_command(NODE4_COMMANDS)
643
693
  return execute_node_gen4_command(command_repo,{node_info: res_data, file_id: ak_data['root_file_id']})
644
694
  when :shared_folders
645
695
  read_params = case resource_type
646
- when :workspace;{'access_id'=>"ASPERA_ACCESS_KEY_ADMIN_WS_#{res_id}",'access_type'=>'user'}
647
- when :node;{'include'=>['[]','access_level','permission_count'],'created_by_id'=>"ASPERA_ACCESS_KEY_ADMIN"}
648
- else raise "error"
696
+ when :workspace;{'access_id'=>"#{ID_AK_ADMIN}_WS_#{res_id}",'access_type'=>'user'}
697
+ when :node;{'include'=>['[]','access_level','permission_count'],'created_by_id'=>ID_AK_ADMIN}
698
+ else raise 'error'
649
699
  end
650
700
  res_data=@api_aoc.read("#{resource_class_path}/#{res_id}/permissions",read_params)[:data]
651
701
  fields=case resource_type
@@ -653,18 +703,18 @@ module Aspera
653
703
  when :workspace;['id','node_id','file_id','node_name','file.path','tags.aspera.files.workspace.share_as']
654
704
  else raise "unexpected resource type #{resource_type}"
655
705
  end
656
- return { :type=>:object_list, :data =>res_data , :fields=>fields}
706
+ return { type: :object_list, data: res_data , fields: fields}
657
707
  when :shared_create
658
- # TODO
708
+ # TODO: finish implementation
659
709
  folder_path=self.options.get_next_argument('folder path in node')
660
710
  user_create_data=self.options.get_next_argument('creation data (Hash)')
661
711
  res_data=@api_aoc.read(resource_instance_path)[:data]
662
712
  node_file = @api_aoc.resolve_node_file({node_info: res_data, file_id: ak_data['root_file_id']},folder_path)
663
713
 
664
- #node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
714
+ #node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
665
715
  #file_info = node_api.read("files/#{node_file[:file_id]}")[:data]
666
716
 
667
- access_id="ASPERA_ACCESS_KEY_ADMIN_WS_#{@workspace_id}"
717
+ access_id="#{ID_AK_ADMIN}_WS_#{@workspace_id}"
668
718
  create_data={
669
719
  'file_id' =>node_file[:file_id],
670
720
  'access_type' =>'user',
@@ -674,10 +724,10 @@ module Aspera
674
724
  'id' =>@workspace_id,
675
725
  'workspace_name' =>@workspace_name,
676
726
  'share_as' =>File.basename(folder_path),
677
- 'user_name' =>c_user_info['name'],
678
- 'shared_by_user_id'=>c_user_info['id'],
679
- 'shared_by_name' =>c_user_info['name'],
680
- 'shared_by_email' =>c_user_info['email'],
727
+ 'user_name' =>@api_aoc.user_info['name'],
728
+ 'shared_by_user_id'=>@api_aoc.user_info['id'],
729
+ 'shared_by_name' =>@api_aoc.user_info['name'],
730
+ 'shared_by_email' =>@api_aoc.user_info['email'],
681
731
  'shared_with_name' =>access_id,
682
732
  'access_key' =>node_file[:node_info]['access_key'],
683
733
  'node' =>node_file[:node_info]['name']}
@@ -685,15 +735,15 @@ module Aspera
685
735
  create_data.deep_merge!(user_create_data)
686
736
  Log.dump(:data,create_data)
687
737
  raise :ERROR
688
- else raise "unknown command"
738
+ else raise 'unknown command'
689
739
  end
690
740
  when :usage_reports
691
- return {:type=>:object_list,:data=>@api_aoc.read('usage_reports',{:workspace_id=>@workspace_id})[:data]}
741
+ return {type: :object_list,data: @api_aoc.read('usage_reports',{workspace_id: @workspace_id})[:data]}
692
742
  end
693
743
  end
694
744
 
695
745
  # must be public
696
- ACTIONS=[ :reminder, :bearer_token, :organization, :tier_restrictions, :user, :workspace, :packages, :files, :gateway, :admin, :automation, :servers]
746
+ ACTIONS=[ :reminder, :bearer_token, :organization, :tier_restrictions, :user, :workspace, :packages, :files, :gateway, :admin, :automation, :servers].freeze
697
747
 
698
748
  def execute_action
699
749
  command=self.options.get_next_command(ACTIONS)
@@ -705,38 +755,38 @@ module Aspera
705
755
  Rest.new(base_url: "#{AoC.api_base_url}/#{AoC::API_V1}").create('organization_reminders',{email: user_email})[:data]
706
756
  return Main.result_status("List of organizations user is member of, has been sent by e-mail to #{user_email}")
707
757
  when :bearer_token
708
- return {:type=>:text,:data=>@api_aoc.oauth_token}
758
+ return {type: :text,data: @api_aoc.oauth_token}
709
759
  when :organization
710
- return { :type=>:single_object, :data =>@api_aoc.read('organization')[:data] }
760
+ return { type: :single_object, data: @api_aoc.read('organization')[:data] }
711
761
  when :tier_restrictions
712
- return { :type=>:single_object, :data =>@api_aoc.read('tier_restrictions')[:data] }
762
+ return { type: :single_object, data: @api_aoc.read('tier_restrictions')[:data] }
713
763
  when :user
714
764
  command=self.options.get_next_command([ :workspaces,:info,:shared_inboxes ])
715
765
  case command
716
766
  when :workspaces
717
- return {:type=>:object_list,:data=>@api_aoc.read('workspaces')[:data],:fields=>['id','name']}
767
+ return {type: :object_list,data: @api_aoc.read('workspaces')[:data],fields: ['id','name']}
718
768
  # when :settings
719
- # return {:type=>:object_list,:data=>@api_aoc.read("client_settings/")[:data]}
769
+ # return {type: :object_list,data: @api_aoc.read('client_settings/')[:data]}
720
770
  when :shared_inboxes
721
771
  query=option_url_query(nil)
722
772
  if query.nil?
723
773
  set_workspace_info
724
774
  query={'embed[]'=>'dropbox','workspace_id'=>@workspace_id,'aggregate_permissions_by_dropbox'=>true,'sort'=>'dropbox_name'}
725
775
  end
726
- return {:type=>:object_list,:data=>@api_aoc.read('dropbox_memberships',query)[:data],:fields=>['dropbox_id','dropbox.name']}
776
+ return {type: :object_list,data: @api_aoc.read('dropbox_memberships',query)[:data],fields: ['dropbox_id','dropbox.name']}
727
777
  when :info
728
778
  command=self.options.get_next_command([ :show,:modify ])
729
779
  case command
730
780
  when :show
731
- return { :type=>:single_object, :data =>c_user_info }
781
+ return { type: :single_object, data: @api_aoc.user_info }
732
782
  when :modify
733
- @api_aoc.update("users/#{c_user_info['id']}",self.options.get_next_argument('modified parameters (hash)'))
783
+ @api_aoc.update("users/#{@api_aoc.user_info['id']}",self.options.get_next_argument('modified parameters (hash)'))
734
784
  return Main.result_status('modified')
735
785
  end
736
786
  end
737
787
  when :workspace # show current workspace parameters
738
788
  set_workspace_info
739
- return { :type=>:single_object, :data =>@workspace_data }
789
+ return { type: :single_object, data: @workspace_data }
740
790
  when :packages
741
791
  set_workspace_info if @url_token_data.nil?
742
792
  command_pkg=self.options.get_next_command([ :send, :recv, :list, :show, :delete ])
@@ -761,7 +811,7 @@ module Aspera
761
811
  resolve_package_recipients(package_creation,'recipients')
762
812
  resolve_package_recipients(package_creation,'bcc_recipients')
763
813
 
764
- # create a new package with one file
814
+ # create a new package container
765
815
  package_info=@api_aoc.create('packages',package_creation)[:data]
766
816
 
767
817
  # get node information for the node on which package must be created
@@ -772,24 +822,24 @@ module Aspera
772
822
 
773
823
  # execute transfer
774
824
  node_file = {node_info: node_info, file_id: package_info['contents_file_id']}
775
- # raise esception if at least one error
825
+ # raise exception if at least one error
776
826
  Main.result_transfer(transfer_start(AoC::PACKAGES_APP,'send',node_file,AoC.package_tags(package_info,'upload')))
777
827
  # return all info on package
778
- return { :type=>:single_object, :data =>package_info}
828
+ return { type: :single_object, data: package_info}
779
829
  when :recv
780
830
  if !@url_token_data.nil?
781
831
  assert_public_link_types(['view_received_package'])
782
832
  self.options.set_option(:id,@url_token_data['data']['package_id'])
783
833
  end
784
834
  # scalar here
785
- ids_to_download=self.options.get_option(:id,:mandatory)
835
+ ids_to_download=self.instance_identifier()
786
836
  skip_ids_data=[]
787
837
  skip_ids_persistency=nil
788
838
  if self.options.get_option(:once_only,:mandatory)
789
839
  skip_ids_persistency=PersistencyActionOnce.new(
790
840
  manager: @agents[:persistency],
791
841
  data: skip_ids_data,
792
- ids: ['aoc_recv',self.options.get_option(:url,:mandatory),@workspace_id].push(*@persist_ids))
842
+ id: IdGenerator.from_list(['aoc_recv',self.options.get_option(:url,:mandatory),@workspace_id].push(*@persist_ids)))
793
843
  end
794
844
  if ids_to_download.eql?(VAL_ALL)
795
845
  # get list of packages in inbox
@@ -810,24 +860,26 @@ module Aspera
810
860
  add_ts={'paths'=>[{'source'=>'.'}]}
811
861
  node_file = {node_info: node_info, file_id: package_info['contents_file_id']}
812
862
  statuses=transfer_start(AoC::PACKAGES_APP,'receive',node_file,AoC.package_tags(package_info,'download').merge(add_ts))
813
- result_transfer.push({'package'=>package_id,'status'=>statuses.map{|i|i.to_s}.join(',')})
863
+ result_transfer.push({'package'=>package_id,Main::STATUS_FIELD=>statuses})
814
864
  # update skip list only if all transfer sessions completed
815
865
  if TransferAgent.session_status(statuses).eql?(:success)
816
866
  skip_ids_data.push(package_id)
817
867
  skip_ids_persistency.save unless skip_ids_persistency.nil?
818
868
  end
819
869
  end
820
- return {:type=>:object_list,:data=>result_transfer}
870
+ return Main.result_transfer_multiple(result_transfer)
821
871
  when :show
822
872
  package_id=self.options.get_next_argument('package ID')
823
873
  package_info=@api_aoc.read("packages/#{package_id}")[:data]
824
- return { :type=>:single_object, :data =>package_info }
874
+ return { type: :single_object, data: package_info }
825
875
  when :list
826
- # list all packages ('page'=>1,'per_page'=>10,)'sort'=>'-sent_at',
827
- packages=@api_aoc.read('packages',{'archived'=>false,'exclude_dropbox_packages'=>true,'has_content'=>true,'received'=>true,'workspace_id'=>@workspace_id})[:data]
828
- return {:type=>:object_list,:data=>packages,:fields=>['id','name','bytes_transferred']}
876
+ query=option_url_query({'archived'=>false,'exclude_dropbox_packages'=>true,'has_content'=>true,'received'=>true})
877
+ raise 'option must be Hash' unless query.is_a?(Hash)
878
+ query['workspace_id']||=@workspace_id
879
+ packages=@api_aoc.read('packages',query)[:data]
880
+ return {type: :object_list,data: packages,fields: ['id','name','bytes_transferred']}
829
881
  when :delete
830
- list_or_one=self.options.get_option(:id,:mandatory)
882
+ list_or_one=self.instance_identifier()
831
883
  return do_bulk_operation(list_or_one,'deleted')do|id|
832
884
  raise 'expecting String identifier' unless id.is_a?(String) or id.is_a?(Integer)
833
885
  @api_aoc.delete("packages/#{id}")[:data]
@@ -837,7 +889,7 @@ module Aspera
837
889
  # get workspace related information
838
890
  set_workspace_info
839
891
  set_home_node_file
840
- command_repo=self.options.get_next_command(NODE4_COMMANDS.clone.concat([:short_link]))
892
+ command_repo=self.options.get_next_command([NODE4_COMMANDS,:short_link].flatten)
841
893
  case command_repo
842
894
  when *NODE4_COMMANDS; return execute_node_gen4_command(command_repo,@home_node_file)
843
895
  when :short_link
@@ -883,7 +935,7 @@ module Aspera
883
935
  end
884
936
  result=self.entity_action(@api_aoc,'short_links',nil,:id,'self')
885
937
  if result[:data].is_a?(Hash) and result[:data].has_key?('created_at') and result[:data]['resource_type'].eql?('UrlToken')
886
- node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
938
+ node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
887
939
  # TODO: access level as arg
888
940
  access_levels=Aspera::Node::ACCESS_LEVELS #['delete','list','mkdir','preview','read','rename','write']
889
941
  perm_data={
@@ -896,8 +948,8 @@ module Aspera
896
948
  'workspace_id' =>@workspace_id,
897
949
  'workspace_name' =>@workspace_name,
898
950
  'folder_name' =>'my folder',
899
- 'created_by_name' =>c_user_info['name'],
900
- 'created_by_email'=>c_user_info['email'],
951
+ 'created_by_name' =>@api_aoc.user_info['name'],
952
+ 'created_by_email'=>@api_aoc.user_info['email'],
901
953
  'access_key' =>node_file[:node_info]['access_key'],
902
954
  'node' =>node_file[:node_info]['host']
903
955
  }
@@ -907,9 +959,9 @@ module Aspera
907
959
  end
908
960
  return result
909
961
  end # files command
910
- throw "Error: shall not reach this line"
962
+ throw 'Error: shall not reach this line'
911
963
  when :automation
912
- Log.log.warn("BETA: work under progress")
964
+ Log.log.warn('BETA: work under progress')
913
965
  # automation api is not in the same place
914
966
  automation_rest_params=@api_aoc.params.clone
915
967
  automation_rest_params[:base_url].gsub!('/api/','/automation/')
@@ -925,18 +977,18 @@ module Aspera
925
977
  when *Plugin::ALL_OPS
926
978
  return self.entity_command(wf_command,automation_api,'workflows',nil,:id)
927
979
  when :launch
928
- wf_id=self.options.get_option(:id,:mandatory)
980
+ wf_id=self.instance_identifier()
929
981
  data=automation_api.create("workflows/#{wf_id}/launch",{})[:data]
930
- return {:type=>:single_object,:data=>data}
982
+ return {type: :single_object,data: data}
931
983
  when :action
932
984
  wf_command=self.options.get_next_command([:list,:create,:show])
933
- wf_id=self.options.get_option(:id,:mandatory)
985
+ wf_id=self.instance_identifier()
934
986
  step=automation_api.create('steps',{'workflow_id'=>wf_id})[:data]
935
- automation_api.update("workflows/#{wf_id}",{'step_order'=>[step["id"]]})
936
- action=automation_api.create('actions',{'step_id'=>step["id"],'type'=>'manual'})[:data]
937
- automation_api.update("steps/#{step["id"]}",{'action_order'=>[action["id"]]})
987
+ automation_api.update("workflows/#{wf_id}",{'step_order'=>[step['id']]})
988
+ action=automation_api.create('actions',{'step_id'=>step['id'],'type'=>'manual'})[:data]
989
+ automation_api.update("steps/#{step['id']}",{'action_order'=>[action['id']]})
938
990
  wf=automation_api.read("workflows/#{wf_id}")[:data]
939
- return {:type=>:single_object,:data=>wf}
991
+ return {type: :single_object,data: wf}
940
992
  end
941
993
  end
942
994
  when :gateway
@@ -946,15 +998,15 @@ module Aspera
946
998
  when :admin
947
999
  return execute_admin_action
948
1000
  when :servers
949
- return {:type=>:object_list,:data=>Rest.new(base_url: "#{AoC.api_base_url}/#{AoC::API_V1}").read('servers')[:data]}
1001
+ return {type: :object_list,data: Rest.new(base_url: "#{AoC.api_base_url}/#{AoC::API_V1}").read('servers')[:data]}
950
1002
  else
951
1003
  raise "internal error: #{command}"
952
1004
  end # action
953
- raise RuntimeError, "internal error: command shall return"
1005
+ raise RuntimeError, 'internal error: command shall return'
954
1006
  end
955
1007
 
956
- private :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
957
- private_constant :VAL_ALL,:NODE4_COMMANDS
1008
+ private :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
1009
+ private_constant :VAL_ALL,:NODE4_COMMANDS, :ID_AK_ADMIN
958
1010
 
959
1011
  end # AoC
960
1012
  end # Plugins