aspera-cli 4.2.1 → 4.5.0

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