aspera-cli 4.0.0.pre1

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 (88) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +3592 -0
  3. data/bin/ascli +7 -0
  4. data/bin/asession +89 -0
  5. data/docs/Makefile +59 -0
  6. data/docs/README.erb.md +3012 -0
  7. data/docs/README.md +13 -0
  8. data/docs/diagrams.txt +49 -0
  9. data/docs/secrets.make +38 -0
  10. data/docs/test_env.conf +117 -0
  11. data/docs/transfer_spec.html +99 -0
  12. data/examples/aoc.rb +17 -0
  13. data/examples/proxy.pac +60 -0
  14. data/examples/transfer.rb +115 -0
  15. data/lib/aspera/api_detector.rb +60 -0
  16. data/lib/aspera/ascmd.rb +151 -0
  17. data/lib/aspera/ats_api.rb +43 -0
  18. data/lib/aspera/cli/basic_auth_plugin.rb +38 -0
  19. data/lib/aspera/cli/extended_value.rb +88 -0
  20. data/lib/aspera/cli/formater.rb +238 -0
  21. data/lib/aspera/cli/listener/line_dump.rb +17 -0
  22. data/lib/aspera/cli/listener/logger.rb +20 -0
  23. data/lib/aspera/cli/listener/progress.rb +52 -0
  24. data/lib/aspera/cli/listener/progress_multi.rb +91 -0
  25. data/lib/aspera/cli/main.rb +304 -0
  26. data/lib/aspera/cli/manager.rb +440 -0
  27. data/lib/aspera/cli/plugin.rb +90 -0
  28. data/lib/aspera/cli/plugins/alee.rb +24 -0
  29. data/lib/aspera/cli/plugins/ats.rb +231 -0
  30. data/lib/aspera/cli/plugins/bss.rb +71 -0
  31. data/lib/aspera/cli/plugins/config.rb +806 -0
  32. data/lib/aspera/cli/plugins/console.rb +62 -0
  33. data/lib/aspera/cli/plugins/cos.rb +106 -0
  34. data/lib/aspera/cli/plugins/faspex.rb +377 -0
  35. data/lib/aspera/cli/plugins/faspex5.rb +93 -0
  36. data/lib/aspera/cli/plugins/node.rb +438 -0
  37. data/lib/aspera/cli/plugins/oncloud.rb +937 -0
  38. data/lib/aspera/cli/plugins/orchestrator.rb +169 -0
  39. data/lib/aspera/cli/plugins/preview.rb +464 -0
  40. data/lib/aspera/cli/plugins/server.rb +216 -0
  41. data/lib/aspera/cli/plugins/shares.rb +63 -0
  42. data/lib/aspera/cli/plugins/shares2.rb +114 -0
  43. data/lib/aspera/cli/plugins/sync.rb +65 -0
  44. data/lib/aspera/cli/plugins/xnode.rb +115 -0
  45. data/lib/aspera/cli/transfer_agent.rb +251 -0
  46. data/lib/aspera/cli/version.rb +5 -0
  47. data/lib/aspera/colors.rb +39 -0
  48. data/lib/aspera/command_line_builder.rb +137 -0
  49. data/lib/aspera/fasp/aoc.rb +24 -0
  50. data/lib/aspera/fasp/connect.rb +99 -0
  51. data/lib/aspera/fasp/error.rb +21 -0
  52. data/lib/aspera/fasp/error_info.rb +60 -0
  53. data/lib/aspera/fasp/http_gw.rb +81 -0
  54. data/lib/aspera/fasp/installation.rb +240 -0
  55. data/lib/aspera/fasp/listener.rb +11 -0
  56. data/lib/aspera/fasp/local.rb +377 -0
  57. data/lib/aspera/fasp/manager.rb +69 -0
  58. data/lib/aspera/fasp/node.rb +88 -0
  59. data/lib/aspera/fasp/parameters.rb +235 -0
  60. data/lib/aspera/fasp/resume_policy.rb +76 -0
  61. data/lib/aspera/fasp/uri.rb +51 -0
  62. data/lib/aspera/faspex_gw.rb +196 -0
  63. data/lib/aspera/hash_ext.rb +28 -0
  64. data/lib/aspera/log.rb +80 -0
  65. data/lib/aspera/nagios.rb +71 -0
  66. data/lib/aspera/node.rb +14 -0
  67. data/lib/aspera/oauth.rb +319 -0
  68. data/lib/aspera/on_cloud.rb +421 -0
  69. data/lib/aspera/open_application.rb +72 -0
  70. data/lib/aspera/persistency_action_once.rb +42 -0
  71. data/lib/aspera/persistency_folder.rb +91 -0
  72. data/lib/aspera/preview/file_types.rb +300 -0
  73. data/lib/aspera/preview/generator.rb +258 -0
  74. data/lib/aspera/preview/image_error.png +0 -0
  75. data/lib/aspera/preview/options.rb +35 -0
  76. data/lib/aspera/preview/utils.rb +131 -0
  77. data/lib/aspera/preview/video_error.png +0 -0
  78. data/lib/aspera/proxy_auto_config.erb.js +287 -0
  79. data/lib/aspera/proxy_auto_config.rb +34 -0
  80. data/lib/aspera/rest.rb +296 -0
  81. data/lib/aspera/rest_call_error.rb +13 -0
  82. data/lib/aspera/rest_error_analyzer.rb +98 -0
  83. data/lib/aspera/rest_errors_aspera.rb +58 -0
  84. data/lib/aspera/ssh.rb +53 -0
  85. data/lib/aspera/sync.rb +82 -0
  86. data/lib/aspera/temp_file_manager.rb +37 -0
  87. data/lib/aspera/uri_reader.rb +25 -0
  88. metadata +288 -0
@@ -0,0 +1,937 @@
1
+ require 'aspera/cli/plugins/node'
2
+ require 'aspera/cli/plugins/ats'
3
+ require 'aspera/cli/basic_auth_plugin'
4
+ require 'aspera/cli/transfer_agent'
5
+ require 'aspera/on_cloud'
6
+ require 'aspera/persistency_action_once'
7
+ require 'securerandom'
8
+ require 'resolv'
9
+ require 'date'
10
+
11
+ module Aspera
12
+ module Cli
13
+ module Plugins
14
+ class Oncloud < BasicAuthPlugin
15
+ VAL_ALL='ALL'
16
+ private_constant :VAL_ALL
17
+ attr_reader :api_aoc
18
+ def initialize(env)
19
+ super(env)
20
+ @default_workspace_id=nil
21
+ @workspace_name=nil
22
+ @workspace_id=nil
23
+ @persist_ids=nil
24
+ @home_node_file=nil
25
+ @api_aoc=nil
26
+ @url_token_data=nil
27
+ @user_info=nil
28
+ @ats=Ats.new(@agents)
29
+ self.options.add_opt_list(:auth,Oauth.auth_types,"type of Oauth authentication")
30
+ self.options.add_opt_list(:operation,[:push,:pull],"client operation for transfers")
31
+ self.options.add_opt_simple(:client_id,"API client identifier in application")
32
+ self.options.add_opt_simple(:client_secret,"API client passcode")
33
+ self.options.add_opt_simple(:redirect_uri,"API client redirect URI")
34
+ self.options.add_opt_simple(:private_key,"RSA private key PEM value for JWT (prefix file path with @val:@file:)")
35
+ self.options.add_opt_simple(:workspace,"name of workspace")
36
+ self.options.add_opt_simple(:eid,"identifier") # used ?
37
+ self.options.add_opt_simple(:name,"resource name")
38
+ self.options.add_opt_simple(:link,"public link to shared resource")
39
+ self.options.add_opt_simple(:new_user_option,"new user creation option")
40
+ self.options.add_opt_simple(:from_folder,"share to share source folder")
41
+ self.options.add_opt_simple(:scope,"scope for AoC API calls")
42
+ self.options.add_opt_simple(:notify,"notify users that file was received")
43
+ self.options.add_opt_boolean(:bulk,"bulk operation")
44
+ self.options.add_opt_boolean(:default_ports,"use standard FASP ports or get from node api")
45
+ self.options.set_option(:bulk,:no)
46
+ self.options.set_option(:default_ports,:yes)
47
+ self.options.set_option(:new_user_option,{'package_contact'=>true})
48
+ self.options.set_option(:operation,:push)
49
+ self.options.set_option(:auth,:jwt)
50
+ self.options.set_option(:scope,OnCloud::SCOPE_FILES_USER)
51
+ self.options.set_option(:private_key,'@file:'+env[:private_key_path]) if env[:private_key_path].is_a?(String)
52
+ self.options.parse_options!
53
+ OnCloud.set_use_default_ports(self.options.get_option(:default_ports))
54
+ return if env[:man_only]
55
+ update_aoc_api
56
+ end
57
+
58
+ # call this to populate single AK secret in AoC API object, from options
59
+ # make sure secret is available
60
+ def find_ak_secret(ak,mandatory=true)
61
+ # secret hash is already provisioned
62
+ # optionally override with specific secret
63
+ @api_aoc.add_secrets({ak=>self.config.get_secret(ak,mandatory)})
64
+ # check that secret was provided as single value or dictionary
65
+ raise CliBadArgument,"Please provide option secret or entry in option secrets for: #{ak}" unless @api_aoc.has_secret(ak) or !mandatory
66
+ end
67
+
68
+ def user_info
69
+ if @user_info.nil?
70
+ # get our user's default information
71
+ # self?embed[]=default_workspace&embed[]=organization
72
+ @user_info=@api_aoc.read('self')[:data] rescue {
73
+ 'name' => 'unknown',
74
+ 'email' => 'unknown',
75
+ }
76
+ end
77
+ return @user_info
78
+ end
79
+
80
+ # starts transfer using transfer agent
81
+ def transfer_start(app,direction,node_file,ts_add)
82
+ ts_add.deep_merge!(OnCloud.analytics_ts(app,direction,@workspace_id,@workspace_name))
83
+ ts_add.deep_merge!(OnCloud.console_ts(app,user_info['name'],user_info['email']))
84
+ return self.transfer.start(*@api_aoc.tr_spec(app,direction,node_file,ts_add))
85
+ end
86
+
87
+ NODE4_COMMANDS=[ :browse, :find, :mkdir, :rename, :delete, :upload, :download, :transfer, :http_node_download, :v3, :file, :bearer_token_node, :permissions ]
88
+
89
+ def node_gen4_execute_action(top_node_file)
90
+ command_repo=self.options.get_next_command(NODE4_COMMANDS)
91
+ return execute_node_gen4_command(command_repo,top_node_file)
92
+ end
93
+
94
+ def execute_node_gen4_command(command_repo,top_node_file)
95
+ case command_repo
96
+ when :bearer_token_node
97
+ thepath=self.options.get_next_argument('path')
98
+ node_file = @api_aoc.resolve_node_file(top_node_file,thepath)
99
+ node_api=@api_aoc.get_node_api(node_file[:node_info],OnCloud::SCOPE_NODE_USER)
100
+ return Main.result_status(node_api.oauth_token)
101
+ when :browse
102
+ thepath=self.options.get_next_argument('path')
103
+ node_file = @api_aoc.resolve_node_file(top_node_file,thepath)
104
+ node_api=@api_aoc.get_node_api(node_file[:node_info],OnCloud::SCOPE_NODE_USER)
105
+ file_info = node_api.read("files/#{node_file[:file_id]}")[:data]
106
+ if file_info['type'].eql?('folder')
107
+ result=node_api.read("files/#{node_file[:file_id]}/files",self.options.get_option(:value,:optional))
108
+ items=result[:data]
109
+ self.format.display_status("Items: #{result[:data].length}/#{result[:http]['X-Total-Count']}")
110
+ else
111
+ items=[file_info]
112
+ end
113
+ return {:type=>:object_list,:data=>items,:fields=>['name','type','recursive_size','size','modified_time','access_level']}
114
+ when :find
115
+ thepath=self.options.get_next_argument('path')
116
+ exec_prefix='exec:'
117
+ expression=self.options.get_option(:value,:optional)||"#{exec_prefix}true"
118
+ node_file=@api_aoc.resolve_node_file(top_node_file,thepath)
119
+ if expression.start_with?(exec_prefix)
120
+ test_block=eval "lambda{|f|#{expression[exec_prefix.length..-1]}}"
121
+ else
122
+ test_block=lambda{|f|f['name'].match(/#{expression}/)}
123
+ end
124
+ return {:type=>:object_list,:data=>@api_aoc.find_files(node_file,test_block),:fields=>['path']}
125
+ when :mkdir
126
+ thepath=self.options.get_next_argument('path')
127
+ containing_folder_path = thepath.split(OnCloud::PATH_SEPARATOR)
128
+ new_folder=containing_folder_path.pop
129
+ node_file = @api_aoc.resolve_node_file(top_node_file,containing_folder_path.join(OnCloud::PATH_SEPARATOR))
130
+ node_api=@api_aoc.get_node_api(node_file[:node_info],OnCloud::SCOPE_NODE_USER)
131
+ result=node_api.create("files/#{node_file[:file_id]}/files",{:name=>new_folder,:type=>:folder})[:data]
132
+ return Main.result_status("created: #{result['name']} (id=#{result['id']})")
133
+ when :rename
134
+ thepath=self.options.get_next_argument('source path')
135
+ newname=self.options.get_next_argument('new name')
136
+ node_file = @api_aoc.resolve_node_file(top_node_file,thepath)
137
+ node_api=@api_aoc.get_node_api(node_file[:node_info],OnCloud::SCOPE_NODE_USER)
138
+ result=node_api.update("files/#{node_file[:file_id]}",{:name=>newname})[:data]
139
+ return Main.result_status("renamed #{thepath} to #{newname}")
140
+ when :delete
141
+ thepath=self.options.get_next_argument('path')
142
+ return do_bulk_operation(thepath,'deleted','path') do |thepath|
143
+ raise "expecting String (path), got #{thepath.class.name} (#{thepath})" unless thepath.is_a?(String)
144
+ node_file = @api_aoc.resolve_node_file(top_node_file,thepath)
145
+ node_api=@api_aoc.get_node_api(node_file[:node_info],OnCloud::SCOPE_NODE_USER)
146
+ result=node_api.delete("files/#{node_file[:file_id]}")[:data]
147
+ {'path'=>thepath}
148
+ end
149
+ when :transfer
150
+ # client side is agent
151
+ # server side is protocol server
152
+ # in same workspace
153
+ server_home_node_file=client_home_node_file=top_node_file
154
+ # default is push
155
+ case self.options.get_option(:operation,:mandatory)
156
+ when :push
157
+ client_tr_oper='send'
158
+ client_folder=self.options.get_option(:from_folder,:mandatory)
159
+ server_folder=self.transfer.destination_folder(client_tr_oper)
160
+ when :pull
161
+ client_tr_oper='receive'
162
+ client_folder=self.transfer.destination_folder(client_tr_oper)
163
+ server_folder=self.options.get_option(:from_folder,:mandatory)
164
+ end
165
+ client_node_file = @api_aoc.resolve_node_file(client_home_node_file,client_folder)
166
+ server_node_file = @api_aoc.resolve_node_file(server_home_node_file,server_folder)
167
+ # force node as transfer agent
168
+ @agents[:transfer].set_agent_instance(Fasp::Node.new(@api_aoc.get_node_api(client_node_file[:node_info],OnCloud::SCOPE_NODE_USER)))
169
+ # additional node to node TS info
170
+ add_ts={
171
+ 'remote_access_key' => server_node_file[:node_info]['access_key'],
172
+ 'destination_root_id' => server_node_file[:file_id],
173
+ 'source_root_id' => client_node_file[:file_id]
174
+ }
175
+ return Main.result_transfer(transfer_start(OnCloud::FILES_APP,client_tr_oper,server_node_file,add_ts))
176
+ when :upload
177
+ node_file = @api_aoc.resolve_node_file(top_node_file,self.transfer.destination_folder('send'))
178
+ add_ts={'tags'=>{'aspera'=>{'files'=>{'parentCwd'=>"#{node_file[:node_info]['id']}:#{node_file[:file_id]}"}}}}
179
+ return Main.result_transfer(transfer_start(OnCloud::FILES_APP,'send',node_file,add_ts))
180
+ when :download
181
+ source_paths=self.transfer.ts_source_paths
182
+ # special case for AoC : all files must be in same folder
183
+ source_folder=source_paths.shift['source']
184
+ # if a single file: split into folder and path
185
+ if source_paths.empty?
186
+ source_folder=source_folder.split(OnCloud::PATH_SEPARATOR)
187
+ source_paths=[{'source'=>source_folder.pop}]
188
+ source_folder=source_folder.join(OnCloud::PATH_SEPARATOR)
189
+ end
190
+ node_file = @api_aoc.resolve_node_file(top_node_file,source_folder)
191
+ # override paths with just filename
192
+ add_ts={'tags'=>{'aspera'=>{'files'=>{'parentCwd'=>"#{node_file[:node_info]['id']}:#{node_file[:file_id]}"}}}}
193
+ add_ts.merge!({'paths'=>source_paths})
194
+ return Main.result_transfer(transfer_start(OnCloud::FILES_APP,'receive',node_file,add_ts))
195
+ when :http_node_download
196
+ source_paths=self.transfer.ts_source_paths
197
+ source_folder=source_paths.shift['source']
198
+ if source_paths.empty?
199
+ source_folder=source_folder.split(OnCloud::PATH_SEPARATOR)
200
+ source_paths=[{'source'=>source_folder.pop}]
201
+ source_folder=source_folder.join(OnCloud::PATH_SEPARATOR)
202
+ end
203
+ raise CliBadArgument,'one file at a time only in HTTP mode' if source_paths.length > 1
204
+ file_name = source_paths.first['source']
205
+ node_file = @api_aoc.resolve_node_file(top_node_file,File.join(source_folder,file_name))
206
+ node_api=@api_aoc.get_node_api(node_file[:node_info],OnCloud::SCOPE_NODE_USER)
207
+ 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
+ return Main.result_status("downloaded: #{file_name}")
209
+ when :v3
210
+ # Note: other "common" actions are unauthorized with user scope
211
+ command_legacy=self.options.get_next_command(Node::SIMPLE_ACTIONS)
212
+ # TODO: shall we support all methods here ? what if there is a link ?
213
+ node_api=@api_aoc.get_node_api(top_node_file[:node_info],OnCloud::SCOPE_NODE_USER)
214
+ return Node.new(@agents.merge(skip_basic_auth_options: true, node_api: node_api)).execute_action(command_legacy)
215
+ when :file
216
+ fileid=self.options.get_next_argument('file id')
217
+ node_file = @api_aoc.resolve_node_file(top_node_file)
218
+ node_api=@api_aoc.get_node_api(node_file[:node_info],OnCloud::SCOPE_NODE_USER)
219
+ items=node_api.read("files/#{fileid}")[:data]
220
+ return {:type=>:single_object,:data=>items}
221
+ when :permissions
222
+ fileid=self.options.get_next_argument('file id')
223
+ node_file = @api_aoc.resolve_node_file(top_node_file)
224
+ node_api=@api_aoc.get_node_api(node_file[:node_info],OnCloud::SCOPE_NODE_USER)
225
+ command_perms=self.options.get_next_command([:show,:create])
226
+ case command_perms
227
+ when :show
228
+ items=node_api.read('permissions',{'include'=>['[]','access_level','permission_count'],'file_id'=>fileid,'inherited'=>false})[:data]
229
+ return {:type=>:object_list,:data=>items}
230
+ when :create
231
+ #value=self.options.get_next_argument('creation value')
232
+ set_workspace_info
233
+ access_id="ASPERA_ACCESS_KEY_ADMIN_WS_#{@workspace_id}"
234
+ node_file[:node_info]
235
+ params={
236
+ "file_id"=>fileid,
237
+ "access_type"=>"user",
238
+ "access_id"=>access_id,
239
+ "access_levels"=>["list","read","write","delete","mkdir","rename","preview"],
240
+ "tags"=>{
241
+ "aspera"=>{
242
+ "files"=>{
243
+ "workspace"=>{
244
+ "id"=>@workspace_id,
245
+ "workspace_name"=>@workspace_name,
246
+ "user_name"=>user_info['name'],
247
+ "shared_by_user_id"=>user_info['id'],
248
+ "shared_by_name"=>user_info['name'],
249
+ "shared_by_email"=>user_info['email'],
250
+ "shared_with_name"=>access_id,
251
+ "access_key"=>node_file[:node_info]['access_key'],
252
+ "node"=>node_file[:node_info]['name']}}}}}
253
+ item=node_api.create('permissions',params)[:data]
254
+ return {:type=>:single_object,:data=>item}
255
+ else raise "error"
256
+ end
257
+ end # command_repo
258
+ throw "ERR"
259
+ end # execute_node_gen4_command
260
+
261
+ # build constructor option list for OnCloud based on options of CLI
262
+ def oncloud_params(subpath)
263
+ # copy command line options to args
264
+ opt=[:link,:url,:auth,:client_id,:client_secret,:scope,:redirect_uri,:private_key,:username].inject({}){|m,i|m[i]=self.options.get_option(i,:optional);m}
265
+ opt[:subpath]=subpath
266
+ return opt
267
+ end
268
+
269
+ # Create a new AoC API REST object and set @api_aoc.
270
+ # Parameters based on command line options
271
+ # @return nil
272
+ def update_aoc_api
273
+ @api_aoc=OnCloud.new(oncloud_params('api/v1'))
274
+ # add access key secrets
275
+ @api_aoc.add_secrets(self.config.get_secrets)
276
+ return nil
277
+ end
278
+
279
+ # initialize apis and authentication
280
+ # set:
281
+ # @default_workspace_id
282
+ # @workspace_name
283
+ # @workspace_id
284
+ # @persist_ids
285
+ # returns nil
286
+ def set_workspace_info
287
+ if @api_aoc.params[:auth].has_key?(:url_token)
288
+ # TODO: can there be several in list ?
289
+ @url_token_data=@api_aoc.read('url_tokens')[:data].first
290
+ @default_workspace_id=@url_token_data['data']['workspace_id']
291
+ @persist_ids=[] # TODO : @url_token_data['id'] ?
292
+ else
293
+ @default_workspace_id=user_info['default_workspace_id']
294
+ @persist_ids=[user_info['id']]
295
+ end
296
+
297
+ ws_name=self.options.get_option(:workspace,:optional)
298
+ if ws_name.nil?
299
+ Log.log.debug("using default workspace".green)
300
+ if @default_workspace_id.eql?(nil)
301
+ raise CliError,"no default workspace defined for user, please specify workspace"
302
+ end
303
+ # get default workspace
304
+ @workspace_id=@default_workspace_id
305
+ else
306
+ # lookup another workspace
307
+ wss=@api_aoc.read("workspaces",{'q'=>ws_name})[:data]
308
+ wss=wss.select { |i| i['name'].eql?(ws_name) }
309
+ case wss.length
310
+ when 0
311
+ raise CliBadArgument,"no such workspace: #{ws_name}"
312
+ when 1
313
+ @workspace_id=wss.first['id']
314
+ else
315
+ raise "unexpected case"
316
+ end
317
+ end
318
+ @workspace_data=@api_aoc.read("workspaces/#{@workspace_id}")[:data]
319
+ Log.log.debug("workspace_id=#{@workspace_id},@workspace_data=#{@workspace_data}".red)
320
+
321
+ @workspace_name||=@workspace_data['name']
322
+ Log.log.info("current workspace is "+@workspace_name.red)
323
+
324
+ # display workspace
325
+ self.format.display_status("Current Workspace: #{@workspace_name.red}#{@workspace_id == @default_workspace_id ? ' (default)' : ''}")
326
+ return nil
327
+ end
328
+
329
+ # @home_node_file (hash with :node_info and :file_id)
330
+ def set_home_node_file
331
+ if !@url_token_data.nil?
332
+ assert_public_link_types(['view_shared_file'])
333
+ home_node_id=@url_token_data['data']['node_id']
334
+ home_file_id=@url_token_data['data']['file_id']
335
+ end
336
+ home_node_id||=@workspace_data['home_node_id']||@workspace_data['node_id']
337
+ home_file_id||=@workspace_data['home_file_id']
338
+ raise "node_id must be defined" if home_node_id.to_s.empty?
339
+ @home_node_file={
340
+ node_info: @api_aoc.read("nodes/#{home_node_id}")[:data],
341
+ file_id: home_file_id
342
+ }
343
+ @api_aoc.check_get_node_file(@home_node_file)
344
+
345
+ return nil
346
+ end
347
+
348
+ def do_bulk_operation(ids_or_one,success_msg,id_result='id',&do_action)
349
+ ids_or_one=[ids_or_one] unless self.options.get_option(:bulk)
350
+ raise "expecting Array" unless ids_or_one.is_a?(Array)
351
+ result_list=[]
352
+ ids_or_one.each do |id|
353
+ one={id_result=>id}
354
+ begin
355
+ res=do_action.call(id)
356
+ one=res if id.is_a?(Hash) # if block returns a has, let's use this
357
+ one['status']=success_msg
358
+ rescue => e
359
+ one['status']=e.to_s
360
+ end
361
+ result_list.push(one)
362
+ end
363
+ return {:type=>:object_list,:data=>result_list,:fields=>[id_result,'status']}
364
+ end
365
+
366
+ # package creation params can give just email, and full hash is created
367
+ def resolve_package_recipients(package_creation,recipient_list_field)
368
+ return unless package_creation.has_key?(recipient_list_field)
369
+ raise CliBadArgument,"#{recipient_list_field} must be an Array" unless package_creation[recipient_list_field].is_a?(Array)
370
+ new_user_option=self.options.get_option(:new_user_option,:mandatory)
371
+ resolved_list=[]
372
+ package_creation[recipient_list_field].each do |recipient_email_or_info|
373
+ case recipient_email_or_info
374
+ when Hash
375
+ 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')
376
+ # already provided all information ?
377
+ resolved_list.push(recipient_email_or_info)
378
+ when String
379
+ if recipient_email_or_info.include?('@')
380
+ # or need to resolve email
381
+ item_lookup=@api_aoc.read('contacts',{'current_workspace_id'=>@workspace_id,'q'=>recipient_email_or_info})[:data]
382
+ case item_lookup.length
383
+ when 1; recipient_user_id=item_lookup.first
384
+ when 0; recipient_user_id=@api_aoc.create('contacts',{'current_workspace_id'=>@workspace_id,'email'=>recipient_email_or_info}.merge(new_user_option))[:data]
385
+ else raise CliBadArgument,"multiple match for: #{recipient_email_or_info}"
386
+ end
387
+ resolved_list.push({'id'=>recipient_user_id['source_id'],'type'=>recipient_user_id['source_type']})
388
+ else
389
+ item_lookup=@api_aoc.read('dropboxes',{'current_workspace_id'=>@workspace_id,'q'=>recipient_email_or_info})[:data]
390
+ case item_lookup.length
391
+ when 1; recipient_user_id=item_lookup.first
392
+ when 0; raise "no such shared inbox in workspace #{@workspace_name}"
393
+ else raise CliBadArgument,"multiple match for: #{recipient_email_or_info}"
394
+ end
395
+ resolved_list.push({'id'=>recipient_user_id['id'],'type'=>'dropbox'})
396
+ end
397
+ else
398
+ raise "recipient item must be a String (email, shared inboc) or hash (id,type)"
399
+ end
400
+ end
401
+ package_creation[recipient_list_field]=resolved_list
402
+ end
403
+
404
+ def url_query(default)
405
+ query=self.options.get_option(:query,:optional)||default
406
+ Log.log.debug("Query=#{query}".bg_red)
407
+ begin
408
+ # check it is suitable
409
+ URI.encode_www_form(query) unless query.nil?
410
+ rescue => e
411
+ raise CliBadArgument,"query must be an extended value which can be encoded with URI.encode_www_form. Refer to manual. (#{e.message})"
412
+ end
413
+ return query
414
+ end
415
+
416
+ def assert_public_link_types(expected)
417
+ if !expected.include?(@url_token_data['purpose'])
418
+ raise CliBadArgument,"public link type is #{@url_token_data['purpose']} but action requires one of #{expected.join(',')}"
419
+ end
420
+ end
421
+
422
+ def execute_admin_action
423
+ self.options.set_option(:scope,OnCloud::SCOPE_FILES_ADMIN)
424
+ update_aoc_api
425
+ command_admin=self.options.get_next_command([ :ats, :resource, :usage_reports, :analytics, :subscription, :auth_providers ])
426
+ case command_admin
427
+ when :auth_providers
428
+ command_auth_prov=self.options.get_next_command([ :list, :update ])
429
+ case command_auth_prov
430
+ when :list
431
+ providers=@api_aoc.read('admin/auth_providers')[:data]
432
+ return {:type=>:object_list,:data=>providers}
433
+ when :update
434
+ end
435
+ when :subscription
436
+ org=@api_aoc.read('organization')[:data]
437
+ bss_api=OnCloud.new(oncloud_params('bss/platform'))
438
+ graphql_query="
439
+ query ($organization_id: ID!) {
440
+ aoc (organization_id: $organization_id) {
441
+ bssSubscription {
442
+ endDate
443
+ startDate
444
+ termMonths
445
+ plan
446
+ trial
447
+ termType
448
+ instances {
449
+ id
450
+ entitlements {
451
+ maxUsageMb
452
+ }
453
+ }
454
+ additionalStorageVolumeGb
455
+ additionalEgressVolumeGb
456
+ additionalUsers
457
+ term {
458
+ startDate
459
+ endDate
460
+ transferVolumeGb
461
+ egressVolumeGb
462
+ storageVolumeGb
463
+ }
464
+ paygoRate {
465
+ rate
466
+ currency
467
+ }
468
+ aocPlanData {
469
+ tier
470
+ trial
471
+ workspaces { max }
472
+ users {
473
+ planAmount
474
+ max
475
+ }
476
+ samlIntegration
477
+ activity
478
+ sharedInboxes
479
+ uniqueUrls
480
+ support
481
+ }
482
+ }
483
+ }
484
+ }
485
+ "
486
+ result=bss_api.create('graphql',{'variables'=>{'organization_id'=>org['id']},'query'=>graphql_query})[:data]['data']
487
+ return {:type=>:single_object,:data=>result['aoc']['bssSubscription']}
488
+ when :ats
489
+ ats_api = Rest.new(@api_aoc.params.deep_merge({
490
+ :base_url => @api_aoc.params[:base_url]+'/admin/ats/pub/v1',
491
+ :auth => {:scope => OnCloud::SCOPE_FILES_ADMIN_USER}
492
+ }))
493
+ return @ats.execute_action_gen(ats_api)
494
+ # when :search_nodes
495
+ # query=self.options.get_option(:query,:optional) || '*'
496
+ # nodes=@api_aoc.read("search_nodes",{'q'=>query})[:data]
497
+ # # simplify output
498
+ # nodes=nodes.map do |i|
499
+ # item=i['_source']
500
+ # item['score']=i['_score']
501
+ # nodedata=item['access_key_recursive_counts'].first
502
+ # item.delete('access_key_recursive_counts')
503
+ # item['node']=nodedata
504
+ # item
505
+ # end
506
+ # return {:type=>:object_list,:data=>nodes,:fields=>['host_name','node_status.cluster_id','node_status.node_id']}
507
+ when :analytics
508
+ analytics_api = Rest.new(@api_aoc.params.deep_merge({
509
+ :base_url => @api_aoc.params[:base_url].gsub('/api/v1','')+'/analytics/v2',
510
+ :auth => {:scope => OnCloud::SCOPE_FILES_ADMIN_USER}
511
+ }))
512
+ command_analytics=self.options.get_next_command([ :application_events, :transfers ])
513
+ case command_analytics
514
+ when :application_events
515
+ event_type=command_analytics.to_s
516
+ events=analytics_api.read("organizations/#{user_info['organization_id']}/#{event_type}")[:data][event_type]
517
+ return {:type=>:object_list,:data=>events}
518
+ when :transfers
519
+ event_type=command_analytics.to_s
520
+ filter_resource=self.options.get_option(:name,:optional) || 'organizations'
521
+ filter_id=self.options.get_option(:id,:optional) || case filter_resource
522
+ when 'organizations'; user_info['organization_id']
523
+ when 'users'; user_info['id']
524
+ when 'nodes'; user_info['id']
525
+ else raise "organizations or users for option --name"
526
+ end
527
+ #
528
+ filter=self.options.get_option(:query,:optional) || {}
529
+ filter['limit']||=100
530
+ if self.options.get_option(:once_only,:mandatory)
531
+ saved_date=[]
532
+ startdate_persistency=PersistencyActionOnce.new(
533
+ manager: @agents[:persistency],
534
+ data: saved_date,
535
+ ids: ['aoc_ana_date',self.options.get_option(:url,:mandatory),@workspace_name].push(filter_resource,filter_id))
536
+ start_datetime=saved_date.first
537
+ stop_datetime=Time.now.utc.strftime('%FT%T.%LZ')
538
+ #Log.log().error("start: #{start_datetime}")
539
+ #Log.log().error("end: #{stop_datetime}")
540
+ saved_date[0]=stop_datetime
541
+ filter['start_time'] = start_datetime unless start_datetime.nil?
542
+ filter['stop_time'] = stop_datetime
543
+ end
544
+ notification=self.options.get_option(:notify,:optional)
545
+ events=analytics_api.read("#{filter_resource}/#{filter_id}/#{event_type}",url_query(filter))[:data][event_type]
546
+ startdate_persistency.save unless startdate_persistency.nil?
547
+ if !notification.nil?
548
+ require 'erb'
549
+ events.each do |transfer|
550
+ email_to_send={}
551
+ notification.each do |k,v|
552
+ email_to_send[k.to_sym]=ERB.new(v).result(binding)
553
+ end
554
+ Log.log().error("send email: #{email_to_send}")
555
+ self.config.send_email(email_to_send)
556
+ end
557
+ end
558
+ return {:type=>:object_list,:data=>events}
559
+ end
560
+ when :resource
561
+ 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,'admin/apps_new'.to_sym,'admin/client_registration_token'.to_sym,'integrations/kms_profile'.to_sym])
562
+ resource_class_path=resource_type.to_s+case resource_type;when :dropbox;'es';when :self,:organization,'admin/apps_new'.to_sym;'';else; 's';end
563
+ singleton_object=[:self,:organization].include?(resource_type)
564
+ global_operations=[:create,:list]
565
+ supported_operations=[:show,:modify]
566
+ supported_operations.push(:delete,*global_operations) unless singleton_object
567
+ supported_operations.push(:v4,:v3) if resource_type.eql?(:node)
568
+ supported_operations.push(:set_pub_key) if resource_type.eql?(:client)
569
+ supported_operations.push(:shared_folders) if [:node,:workspace].include?(resource_type)
570
+ command=self.options.get_next_command(supported_operations)
571
+
572
+ # require identifier for non global commands
573
+ if !singleton_object and !global_operations.include?(command)
574
+ res_id=self.options.get_option(:id)
575
+ 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']
587
+ end
588
+ raise CliBadArgument,"provide either id or name" if res_id.nil?
589
+ resource_instance_path="#{resource_class_path}/#{res_id}"
590
+ end
591
+ resource_instance_path=resource_class_path if singleton_object
592
+ case command
593
+ when :create
594
+ id_result='id'
595
+ id_result='token' if resource_class_path.eql?('admin/client_registration_tokens')
596
+ # TODO: report inconsistency: creation url is !=, and does not return id.
597
+ 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)")
599
+ return do_bulk_operation(list_or_one,'created',id_result)do|params|
600
+ raise "expecting Hash" unless params.is_a?(Hash)
601
+ @api_aoc.create(resource_class_path,params)[:data]
602
+ end
603
+ when :list
604
+ default_fields=['id','name']
605
+ list_query=nil
606
+ case resource_type
607
+ when :node; default_fields.push('host','access_key')
608
+ when :operation; default_fields=nil
609
+ when :contact; default_fields=["email","name","source_id","source_type"]
610
+ when 'admin/apps_new'.to_sym; list_query={:organization_apps=>true};default_fields=['app_type','available']
611
+ when 'admin/client_registration_token'.to_sym; default_fields=['id','value','data.client_subject_scopes','created_at']
612
+ end
613
+ result=@api_aoc.read(resource_class_path,url_query(list_query))
614
+ self.format.display_status("Items: #{result[:data].length}/#{result[:http]['X-Total-Count']}")
615
+ return {:type=>:object_list,:data=>result[:data],:fields=>default_fields}
616
+ when :show
617
+ object=@api_aoc.read(resource_instance_path)[:data]
618
+ fields=object.keys.select{|k|!k.eql?('certificate')}
619
+ return { :type=>:single_object, :data =>object, :fields=>fields }
620
+ when :modify
621
+ changes=self.options.get_next_argument('modified parameters (hash)')
622
+ @api_aoc.update(resource_instance_path,changes)
623
+ return Main.result_status('modified')
624
+ when :delete
625
+ return do_bulk_operation(res_id,'deleted')do|one_id|
626
+ @api_aoc.delete("#{resource_class_path}/#{one_id.to_s}")
627
+ {'id'=>one_id}
628
+ end
629
+ when :set_pub_key
630
+ # special : reads private and generate public
631
+ the_private_key=self.options.get_next_argument('private_key')
632
+ the_public_key=OpenSSL::PKey::RSA.new(the_private_key).public_key.to_s
633
+ @api_aoc.update(resource_instance_path,{:jwt_grant_enabled=>true, :public_key=>the_public_key})
634
+ return Main.result_success
635
+ when :v3,:v4
636
+ res_data=@api_aoc.read(resource_instance_path)[:data]
637
+ find_ak_secret(res_data['access_key'])
638
+ api_node=@api_aoc.get_node_api(res_data)
639
+ return Node.new(@agents.merge(skip_basic_auth_options: true, node_api: api_node)).execute_action if command.eql?(:v3)
640
+ ak_data=api_node.call({:operation=>'GET',:subpath=>"access_keys/#{res_data['access_key']}",:headers=>{'Accept'=>'application/json'}})[:data]
641
+ return node_gen4_execute_action({node_info: res_data, file_id: ak_data['root_file_id']})
642
+ # when :info
643
+ # object=@api_aoc.read(resource_instance_path)[:data]
644
+ # access_key=object['access_key']
645
+ # match_list=@api_aoc.read('admin/search_nodes',{:q=>"access_key:\"#{access_key}\""})[:data]
646
+ # result=match_list.select{|i|i["_source"]["access_key_recursive_counts"].first["access_key"].eql?(access_key)}
647
+ # return Main.result_status('Private node') if result.empty?
648
+ # raise CliError,"more than one match" unless result.length.eql?(1)
649
+ # result=result.first["_source"]
650
+ # result.merge!(result['access_key_recursive_counts'].first)
651
+ # result.delete('access_key_recursive_counts')
652
+ # result.delete('token')
653
+ # return { :type=>:single_object, :data =>result}
654
+ when :shared_folders
655
+ read_params = case resource_type
656
+ when :workspace;{'access_id'=>"ASPERA_ACCESS_KEY_ADMIN_WS_#{res_id}",'access_type'=>'user'}
657
+ when :node;{'include'=>['[]','access_level','permission_count'],'created_by_id'=>"ASPERA_ACCESS_KEY_ADMIN"}
658
+ else raise "error"
659
+ end
660
+ res_data=@api_aoc.read("#{resource_class_path}/#{res_id}/permissions",read_params)[:data]
661
+ fields=case resource_type
662
+ when :node;['id','file_id','file.path','access_type']
663
+ when :workspace;['id','node_id','file_id','node_name','file.path','tags.aspera.files.workspace.share_as']
664
+ else raise "error"
665
+ end
666
+ return { :type=>:object_list, :data =>res_data , :fields=>fields}
667
+ else raise :ERROR
668
+ end
669
+ when :usage_reports
670
+ return {:type=>:object_list,:data=>@api_aoc.read("usage_reports",{:workspace_id=>@workspace_id})[:data]}
671
+ end
672
+ end
673
+
674
+ ACTIONS=[ :apiinfo, :bearer_token, :organization, :tier_restrictions, :user, :workspace, :packages, :files, :gateway, :admin, :automation, :servers]
675
+
676
+ def execute_action
677
+ command=self.options.get_next_command(ACTIONS)
678
+ case command
679
+ when :apiinfo
680
+ api_info={}
681
+ num=1
682
+ Resolv::DNS.open{|dns|dns.each_address('api.ibmaspera.com'){|a| api_info["api.#{num}"]=a;num+=1}}
683
+ return {:type=>:single_object,:data=>api_info}
684
+ when :bearer_token
685
+ return {:type=>:text,:data=>@api_aoc.oauth_token}
686
+ when :organization
687
+ return { :type=>:single_object, :data =>@api_aoc.read('organization')[:data] }
688
+ when :tier_restrictions
689
+ return { :type=>:single_object, :data =>@api_aoc.read('tier_restrictions')[:data] }
690
+ when :user
691
+ command=self.options.get_next_command([ :workspaces,:info,:shared_inboxes ])
692
+ case command
693
+ when :workspaces
694
+ return {:type=>:object_list,:data=>@api_aoc.read("workspaces")[:data],:fields=>['id','name']}
695
+ # when :settings
696
+ # return {:type=>:object_list,:data=>@api_aoc.read("client_settings/")[:data]}
697
+ when :shared_inboxes
698
+ query=url_query(nil)
699
+ if query.nil?
700
+ set_workspace_info
701
+ query={'embed[]'=>'dropbox','workspace_id'=>@workspace_id,'aggregate_permissions_by_dropbox'=>true,'sort'=>'dropbox_name'}
702
+ end
703
+ return {:type=>:object_list,:data=>@api_aoc.read("dropbox_memberships",query)[:data],:fields=>['dropbox_id','dropbox.name']}
704
+ when :info
705
+ command=self.options.get_next_command([ :show,:modify ])
706
+ case command
707
+ when :show
708
+ return { :type=>:single_object, :data =>user_info }
709
+ when :modify
710
+ @api_aoc.update("users/#{user_info['id']}",self.options.get_next_argument('modified parameters (hash)'))
711
+ return Main.result_status('modified')
712
+ end
713
+ end
714
+ when :workspace # show current workspace parameters
715
+ set_workspace_info
716
+ return { :type=>:single_object, :data =>@workspace_data }
717
+ when :packages
718
+ set_workspace_info if @url_token_data.nil?
719
+ command_pkg=self.options.get_next_command([ :send, :recv, :list, :show, :delete ])
720
+ case command_pkg
721
+ when :send
722
+ package_creation=self.options.get_option(:value,:mandatory)
723
+ raise CliBadArgument,"value must be hash, refer to doc" unless package_creation.is_a?(Hash)
724
+
725
+ if !@url_token_data.nil?
726
+ assert_public_link_types(['send_package_to_user','send_package_to_dropbox'])
727
+ box_type=@url_token_data['purpose'].split('_').last
728
+ package_creation['recipients']=[{'id'=>@url_token_data['data']["#{box_type}_id"],'type'=>box_type}]
729
+ @workspace_id=@url_token_data['data']['workspace_id']
730
+ end
731
+
732
+ package_creation['workspace_id']=@workspace_id
733
+
734
+ # list of files to include in package, optional
735
+ #package_creation['file_names']=self.transfer.ts_source_paths.map{|i|File.basename(i['source'])}
736
+
737
+ # lookup users
738
+ resolve_package_recipients(package_creation,'recipients')
739
+ resolve_package_recipients(package_creation,'bcc_recipients')
740
+
741
+ # create a new package with one file
742
+ package_info=@api_aoc.create('packages',package_creation)[:data]
743
+
744
+ # get node information for the node on which package must be created
745
+ node_info=@api_aoc.read("nodes/#{package_info['node_id']}")[:data]
746
+
747
+ # tell Aspera what to expect in package: 1 transfer (can also be done after transfer)
748
+ @api_aoc.update("packages/#{package_info['id']}",{'sent'=>true,'transfers_expected'=>1})[:data]
749
+
750
+ # execute transfer
751
+ node_file = {node_info: node_info, file_id: package_info['contents_file_id']}
752
+ # raise esception if at least one error
753
+ Main.result_transfer(transfer_start(OnCloud::PACKAGES_APP,'send',node_file,OnCloud.package_tags(package_info,'upload')))
754
+ # return all info on package
755
+ return { :type=>:single_object, :data =>package_info}
756
+ when :recv
757
+ if !@url_token_data.nil?
758
+ assert_public_link_types(['view_received_package'])
759
+ self.options.set_option(:id,@url_token_data['data']['package_id'])
760
+ end
761
+ # scalar here
762
+ ids_to_download=self.options.get_option(:id,:mandatory)
763
+ skip_ids_data=[]
764
+ skip_ids_persistency=nil
765
+ if self.options.get_option(:once_only,:mandatory)
766
+ skip_ids_persistency=PersistencyActionOnce.new(
767
+ manager: @agents[:persistency],
768
+ data: skip_ids_data,
769
+ ids: ['aoc_recv',self.options.get_option(:url,:mandatory),@workspace_id].push(*@persist_ids))
770
+ end
771
+ if ids_to_download.eql?(VAL_ALL)
772
+ # get list of packages in inbox
773
+ package_info=@api_aoc.read('packages',{'archived'=>false,'exclude_dropbox_packages'=>true,'has_content'=>true,'received'=>true,'workspace_id'=>@workspace_id})[:data]
774
+ # remove from list the ones already downloaded
775
+ ids_to_download=package_info.map{|e|e['id']}
776
+ # array here
777
+ ids_to_download.select!{|id|!skip_ids_data.include?(id)}
778
+ end # ALL
779
+ # list here
780
+ ids_to_download = [ids_to_download] unless ids_to_download.is_a?(Array)
781
+ result_transfer=[]
782
+ self.format.display_status("found #{ids_to_download.length} package(s).")
783
+ ids_to_download.each do |package_id|
784
+ package_info=@api_aoc.read("packages/#{package_id}")[:data]
785
+ node_info=@api_aoc.read("nodes/#{package_info['node_id']}")[:data]
786
+ self.format.display_status("downloading package: #{package_info['name']}")
787
+ add_ts={'paths'=>[{'source'=>'.'}]}
788
+ node_file = {node_info: node_info, file_id: package_info['contents_file_id']}
789
+ statuses=transfer_start(OnCloud::PACKAGES_APP,'receive',node_file,OnCloud.package_tags(package_info,'download').merge(add_ts))
790
+ result_transfer.push({'package'=>package_id,'status'=>statuses.map{|i|i.to_s}.join(',')})
791
+ # update skip list only if all transfer sessions completed
792
+ if TransferAgent.session_status(statuses).eql?(:success)
793
+ skip_ids_data.push(package_id)
794
+ skip_ids_persistency.save unless skip_ids_persistency.nil?
795
+ end
796
+ end
797
+ return {:type=>:object_list,:data=>result_transfer}
798
+ when :show
799
+ package_id=self.options.get_next_argument('package ID')
800
+ package_info=@api_aoc.read("packages/#{package_id}")[:data]
801
+ return { :type=>:single_object, :data =>package_info }
802
+ when :list
803
+ # list all packages ('page'=>1,'per_page'=>10,)'sort'=>'-sent_at',
804
+ packages=@api_aoc.read("packages",{'archived'=>false,'exclude_dropbox_packages'=>true,'has_content'=>true,'received'=>true,'workspace_id'=>@workspace_id})[:data]
805
+ return {:type=>:object_list,:data=>packages,:fields=>['id','name','bytes_transferred']}
806
+ when :delete
807
+ list_or_one=self.options.get_option(:id,:mandatory)
808
+ return do_bulk_operation(list_or_one,'deleted')do|id|
809
+ raise "expecting String identifier" unless id.is_a?(String) or id.is_a?(Integer)
810
+ @api_aoc.delete("packages/#{id}")[:data]
811
+ end
812
+ end
813
+ when :files
814
+ # get workspace related information
815
+ set_workspace_info
816
+ set_home_node_file
817
+ find_ak_secret(@home_node_file[:node_info]['access_key'],false)
818
+ command_repo=self.options.get_next_command(NODE4_COMMANDS.clone.concat([:short_link]))
819
+ case command_repo
820
+ when *NODE4_COMMANDS; return execute_node_gen4_command(command_repo,@home_node_file)
821
+ when :short_link
822
+ folder_dest=self.options.get_option(:to_folder,:optional)
823
+ value_option=self.options.get_option(:value,:optional)
824
+ case value_option
825
+ when 'public'
826
+ value_option={'purpose'=>'token_auth_redirection'}
827
+ when 'private'
828
+ value_option={'purpose'=>'shared_folder_auth_link'}
829
+ when NilClass,Hash
830
+ else raise "value must be either: public, private, Hash or nil"
831
+ end
832
+ create_params=nil
833
+ node_file=nil
834
+ if !folder_dest.nil?
835
+ node_file = @api_aoc.resolve_node_file(@home_node_file,folder_dest)
836
+ create_params={
837
+ file_id: node_file[:file_id],
838
+ node_id: node_file[:node_info]['id'],
839
+ workspace_id: @workspace_id
840
+ }
841
+ end
842
+ if !value_option.nil? and !create_params.nil?
843
+ case value_option['purpose']
844
+ when 'shared_folder_auth_link'
845
+ value_option['data']=create_params
846
+ value_option['user_selected_name']=nil
847
+ when 'token_auth_redirection'
848
+ create_params['name']=''
849
+ value_option['data']={
850
+ aoc: true,
851
+ url_token_data: {
852
+ data: create_params,
853
+ purpose: 'view_shared_file'
854
+ }
855
+ }
856
+ value_option['user_selected_name']=nil
857
+ else
858
+ raise "purpose must be one of: token_auth_redirection or shared_folder_auth_link"
859
+ end
860
+ self.options.set_option(:value,value_option)
861
+ end
862
+ result=self.entity_action(@api_aoc,'short_links',nil,:id,'self')
863
+ if result[:data].is_a?(Hash) and result[:data].has_key?('created_at') and result[:data]['resource_type'].eql?('UrlToken')
864
+ node_api=@api_aoc.get_node_api(node_file[:node_info],OnCloud::SCOPE_NODE_USER)
865
+ perm_data={
866
+ "file_id" =>node_file[:file_id],
867
+ "access_type" =>"user",
868
+ "access_id" =>result[:data]['resource_id'],
869
+ "access_levels"=>["delete","list","mkdir","preview","read","rename","write"],
870
+ "tags" =>{
871
+ "url_token" =>true,
872
+ "workspace_id" =>@workspace_id,
873
+ "workspace_name" =>@workspace_name,
874
+ "folder_name" =>"my folder",
875
+ "created_by_name" =>user_info['name'],
876
+ "created_by_email"=>user_info['email'],
877
+ "access_key" =>node_file[:node_info]['access_key'],
878
+ "node" =>node_file[:node_info]['host']
879
+ }
880
+ }
881
+ node_api.create("permissions?file_id=#{node_file[:file_id]}",perm_data)
882
+ end
883
+ return result
884
+ end # files command
885
+ throw "Error: shall not reach this line"
886
+ when :automation
887
+ Log.log.warn("BETA: work under progress")
888
+ # automation api is not in the same place
889
+ automation_rest_params=@api_aoc.params.clone
890
+ automation_rest_params[:base_url].gsub!('/api/','/automation/')
891
+ automation_api=Rest.new(automation_rest_params)
892
+ command_automation=self.options.get_next_command([ :workflows, :instances ])
893
+ case command_automation
894
+ when :instances
895
+ return self.entity_action(@api_aoc,'workflow_instances',nil,:id,nil)
896
+ when :workflows
897
+ wF_COMMANDS=Plugin::ALL_OPS.clone.push(:action,:launch)
898
+ wf_command=self.options.get_next_command(wF_COMMANDS)
899
+ case wf_command
900
+ when *Plugin::ALL_OPS
901
+ return self.entity_command(wf_command,automation_api,'workflows',nil,:id)
902
+ when :launch
903
+ wf_id=self.options.get_option(:id,:mandatory)
904
+ data=automation_api.create("workflows/#{wf_id}/launch",{})[:data]
905
+ return {:type=>:single_object,:data=>data}
906
+ when :action
907
+ wf_command=self.options.get_next_command([:list,:create,:show])
908
+ wf_id=self.options.get_option(:id,:mandatory)
909
+ step=automation_api.create('steps',{'workflow_id'=>wf_id})[:data]
910
+ automation_api.update("workflows/#{wf_id}",{'step_order'=>[step["id"]]})
911
+ action=automation_api.create('actions',{'step_id'=>step["id"],'type'=>'manual'})[:data]
912
+ automation_api.update("steps/#{step["id"]}",{'action_order'=>[action["id"]]})
913
+ wf=automation_api.read("workflows/#{wf_id}")[:data]
914
+ return {:type=>:single_object,:data=>wf}
915
+ end
916
+ end
917
+ when :gateway
918
+ set_workspace_info
919
+ require 'aspera/faspex_gw'
920
+ FaspexGW.new(@api_aoc,@workspace_id).start_server
921
+ when :admin
922
+ return execute_admin_action
923
+ when :servers
924
+ self.format.display_status("Beta feature")
925
+ server_api=Rest.new(base_url: 'https://eudemo.asperademo.com')
926
+ require 'json'
927
+ servers=JSON.parse(server_api.read('servers')[:data])
928
+ return {:type=>:object_list,:data=>servers}
929
+ else
930
+ raise "internal error: #{command}"
931
+ end # action
932
+ raise RuntimeError, "internal error: command shall return"
933
+ end
934
+ end # Aspera
935
+ end # Plugins
936
+ end # Cli
937
+ end # Aspera