aspera-cli 4.5.0 → 4.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +1 -0
  3. data/README.md +1894 -1574
  4. data/bin/ascli +21 -1
  5. data/bin/asession +38 -34
  6. data/docs/test_env.conf +14 -3
  7. data/examples/aoc.rb +17 -15
  8. data/examples/dascli +26 -0
  9. data/examples/faspex4.rb +42 -35
  10. data/examples/proxy.pac +1 -1
  11. data/examples/transfer.rb +38 -37
  12. data/lib/aspera/aoc.rb +245 -205
  13. data/lib/aspera/ascmd.rb +111 -90
  14. data/lib/aspera/ats_api.rb +16 -14
  15. data/lib/aspera/cli/basic_auth_plugin.rb +19 -18
  16. data/lib/aspera/cli/extended_value.rb +50 -39
  17. data/lib/aspera/cli/formater.rb +161 -135
  18. data/lib/aspera/cli/info.rb +18 -0
  19. data/lib/aspera/cli/listener/line_dump.rb +4 -2
  20. data/lib/aspera/cli/listener/logger.rb +3 -1
  21. data/lib/aspera/cli/listener/progress.rb +20 -21
  22. data/lib/aspera/cli/listener/progress_multi.rb +29 -31
  23. data/lib/aspera/cli/main.rb +194 -183
  24. data/lib/aspera/cli/manager.rb +213 -206
  25. data/lib/aspera/cli/plugin.rb +71 -49
  26. data/lib/aspera/cli/plugins/alee.rb +8 -7
  27. data/lib/aspera/cli/plugins/aoc.rb +675 -558
  28. data/lib/aspera/cli/plugins/ats.rb +116 -109
  29. data/lib/aspera/cli/plugins/bss.rb +35 -34
  30. data/lib/aspera/cli/plugins/config.rb +722 -542
  31. data/lib/aspera/cli/plugins/console.rb +28 -22
  32. data/lib/aspera/cli/plugins/cos.rb +28 -37
  33. data/lib/aspera/cli/plugins/faspex.rb +281 -227
  34. data/lib/aspera/cli/plugins/faspex5.rb +129 -84
  35. data/lib/aspera/cli/plugins/node.rb +426 -232
  36. data/lib/aspera/cli/plugins/orchestrator.rb +106 -98
  37. data/lib/aspera/cli/plugins/preview.rb +196 -191
  38. data/lib/aspera/cli/plugins/server.rb +131 -126
  39. data/lib/aspera/cli/plugins/shares.rb +49 -36
  40. data/lib/aspera/cli/plugins/sync.rb +27 -28
  41. data/lib/aspera/cli/transfer_agent.rb +84 -79
  42. data/lib/aspera/cli/version.rb +3 -1
  43. data/lib/aspera/colors.rb +37 -28
  44. data/lib/aspera/command_line_builder.rb +84 -63
  45. data/lib/aspera/cos_node.rb +68 -34
  46. data/lib/aspera/data_repository.rb +4 -2
  47. data/lib/aspera/environment.rb +61 -46
  48. data/lib/aspera/fasp/agent_base.rb +36 -31
  49. data/lib/aspera/fasp/agent_connect.rb +44 -37
  50. data/lib/aspera/fasp/agent_direct.rb +101 -104
  51. data/lib/aspera/fasp/agent_httpgw.rb +91 -90
  52. data/lib/aspera/fasp/agent_node.rb +36 -33
  53. data/lib/aspera/fasp/agent_trsdk.rb +28 -31
  54. data/lib/aspera/fasp/error.rb +3 -1
  55. data/lib/aspera/fasp/error_info.rb +81 -54
  56. data/lib/aspera/fasp/installation.rb +171 -151
  57. data/lib/aspera/fasp/listener.rb +2 -0
  58. data/lib/aspera/fasp/parameters.rb +105 -111
  59. data/lib/aspera/fasp/parameters.yaml +305 -249
  60. data/lib/aspera/fasp/resume_policy.rb +20 -20
  61. data/lib/aspera/fasp/transfer_spec.rb +27 -0
  62. data/lib/aspera/fasp/uri.rb +31 -29
  63. data/lib/aspera/faspex_gw.rb +95 -118
  64. data/lib/aspera/hash_ext.rb +12 -13
  65. data/lib/aspera/id_generator.rb +11 -9
  66. data/lib/aspera/keychain/encrypted_hash.rb +73 -57
  67. data/lib/aspera/keychain/macos_security.rb +27 -29
  68. data/lib/aspera/log.rb +40 -39
  69. data/lib/aspera/nagios.rb +24 -22
  70. data/lib/aspera/node.rb +38 -30
  71. data/lib/aspera/oauth.rb +217 -248
  72. data/lib/aspera/open_application.rb +9 -7
  73. data/lib/aspera/persistency_action_once.rb +15 -14
  74. data/lib/aspera/persistency_folder.rb +15 -18
  75. data/lib/aspera/preview/file_types.rb +266 -270
  76. data/lib/aspera/preview/generator.rb +94 -92
  77. data/lib/aspera/preview/image_error.png +0 -0
  78. data/lib/aspera/preview/options.rb +20 -17
  79. data/lib/aspera/preview/utils.rb +99 -102
  80. data/lib/aspera/preview/video_error.png +0 -0
  81. data/lib/aspera/{proxy_auto_config.erb.js → proxy_auto_config.js} +23 -31
  82. data/lib/aspera/proxy_auto_config.rb +114 -21
  83. data/lib/aspera/rest.rb +144 -142
  84. data/lib/aspera/rest_call_error.rb +3 -2
  85. data/lib/aspera/rest_error_analyzer.rb +31 -31
  86. data/lib/aspera/rest_errors_aspera.rb +18 -16
  87. data/lib/aspera/secret_hider.rb +68 -0
  88. data/lib/aspera/ssh.rb +20 -16
  89. data/lib/aspera/sync.rb +57 -54
  90. data/lib/aspera/temp_file_manager.rb +20 -14
  91. data/lib/aspera/timer_limiter.rb +10 -8
  92. data/lib/aspera/uri_reader.rb +14 -15
  93. data/lib/aspera/web_auth.rb +85 -80
  94. data.tar.gz.sig +0 -0
  95. metadata +169 -40
  96. metadata.gz.sig +2 -0
  97. data/bin/dascli +0 -13
  98. data/docs/Makefile +0 -63
  99. data/docs/README.erb.md +0 -4221
  100. data/docs/README.md +0 -13
  101. data/docs/diagrams.txt +0 -49
  102. data/docs/doc_tools.rb +0 -58
  103. data/lib/aspera/cli/plugins/shares2.rb +0 -114
  104. data/lib/aspera/fasp/default.rb +0 -17
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'aspera/cli/plugins/node'
2
4
  require 'aspera/cli/plugins/ats'
3
5
  require 'aspera/cli/basic_auth_plugin'
4
6
  require 'aspera/cli/transfer_agent'
5
7
  require 'aspera/fasp/agent_node'
8
+ require 'aspera/fasp/transfer_spec'
6
9
  require 'aspera/aoc'
7
10
  require 'aspera/node'
8
11
  require 'aspera/persistency_action_once'
@@ -16,322 +19,321 @@ module Aspera
16
19
  class Aoc < BasicAuthPlugin
17
20
  class << self
18
21
  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'}
22
+ api = Rest.new({base_url: base_url})
23
+ # either in standard domain, or product name in page
24
+ if URI.parse(base_url).host.end_with?(Aspera::AoC::PROD_DOMAIN) ||
25
+ api.call({operation: 'GET', redirect_max: 1, headers: {'Accept' => 'text/html'}})[:http].body.include?(Aspera::AoC::PRODUCT_NAME)
26
+ return {product: :aoc,version: 'SaaS' }
23
27
  end
24
28
  return nil
25
29
  end
26
30
  end
27
31
  # special value for package id
28
- VAL_ALL='ALL'
29
- ID_AK_ADMIN='ASPERA_ACCESS_KEY_ADMIN'
32
+ VAL_ALL = 'ALL'
33
+ ID_AK_ADMIN = 'ASPERA_ACCESS_KEY_ADMIN'
34
+ KNOWN_AOC_RES=%i[
35
+ self organization user group group_membership client contact dropbox node operation package saml_configuration
36
+ workspace workspace_membership dropbox_membership short_link application client_registration_token client_access_key
37
+ kms_profile].freeze
30
38
 
31
39
  def initialize(env)
32
40
  super(env)
33
- @default_workspace_id=nil
34
- @workspace_name=nil
35
- @workspace_id=nil
36
- @persist_ids=nil
37
- @home_node_file=nil
38
- @api_aoc=nil
39
- @url_token_data=nil
40
- @api_aoc=nil
41
- self.options.add_opt_list(:auth,Oauth.auth_types,'OAuth type of authentication')
42
- self.options.add_opt_list(:operation,[:push,:pull],'client operation for transfers')
43
- self.options.add_opt_simple(:client_id,'OAuth API client identifier in application')
44
- self.options.add_opt_simple(:client_secret,'OAuth API client passcode')
45
- self.options.add_opt_simple(:redirect_uri,'OAuth API client redirect URI')
46
- self.options.add_opt_simple(:private_key,'OAuth JWT RSA private key PEM value (prefix file path with @val:@file:)')
47
- self.options.add_opt_simple(:workspace,'name of workspace')
48
- self.options.add_opt_simple(:name,'resource name')
49
- self.options.add_opt_simple(:path,'file or folder path')
50
- self.options.add_opt_simple(:link,'public link to shared resource')
51
- self.options.add_opt_simple(:new_user_option,'new user creation option')
52
- self.options.add_opt_simple(:from_folder,'share to share source folder')
53
- self.options.add_opt_simple(:scope,'OAuth scope for AoC API calls')
54
- self.options.add_opt_boolean(:bulk,'bulk operation')
55
- self.options.add_opt_boolean(:default_ports,'use standard FASP ports or get from node api')
56
- self.options.set_option(:bulk,:no)
57
- self.options.set_option(:default_ports,:yes)
58
- self.options.set_option(:new_user_option,{'package_contact'=>true})
59
- self.options.set_option(:operation,:push)
60
- self.options.set_option(:auth,:jwt)
61
- self.options.set_option(:scope,AoC::SCOPE_FILES_USER)
62
- self.options.set_option(:private_key,'@file:'+env[:private_key_path]) if env[:private_key_path].is_a?(String)
63
- self.options.parse_options!
64
- AoC.set_use_default_ports(self.options.get_option(:default_ports))
41
+ @workspace_info = nil
42
+ @persist_ids = nil
43
+ @home_node_file = nil
44
+ @api_aoc = nil
45
+ @url_token_data = nil
46
+ @api_aoc = nil
47
+ options.add_opt_list(:auth,Oauth::STD_AUTH_TYPES,'OAuth type of authentication')
48
+ options.add_opt_list(:operation, %i[push pull],'client operation for transfers')
49
+ options.add_opt_simple(:client_id,'OAuth API client identifier in application')
50
+ options.add_opt_simple(:client_secret,'OAuth API client passcode')
51
+ options.add_opt_simple(:redirect_uri,'OAuth API client redirect URI')
52
+ options.add_opt_simple(:private_key,'OAuth JWT RSA private key PEM value (prefix file path with @file:)')
53
+ options.add_opt_simple(:passphrase,'RSA private key passphrase')
54
+ options.add_opt_simple(:workspace,'name of workspace')
55
+ options.add_opt_simple(:name,'resource name')
56
+ options.add_opt_simple(:path,'file or folder path')
57
+ options.add_opt_simple(:link,'public link to shared resource')
58
+ options.add_opt_simple(:new_user_option,'new user creation option')
59
+ options.add_opt_simple(:from_folder,'share to share source folder')
60
+ options.add_opt_simple(:scope,'OAuth scope for AoC API calls')
61
+ options.add_opt_boolean(:bulk,'bulk operation')
62
+ options.add_opt_boolean(:default_ports,'use standard FASP ports or get from node api')
63
+ options.add_opt_boolean(:validate_metadata,'validate shared inbox metadata')
64
+ options.set_option(:bulk,:no)
65
+ options.set_option(:default_ports,:yes)
66
+ options.set_option(:validate_metadata,:yes)
67
+ options.set_option(:new_user_option,{'package_contact' => true})
68
+ options.set_option(:operation,:push)
69
+ options.set_option(:auth,:jwt)
70
+ options.set_option(:scope,AoC::SCOPE_FILES_USER)
71
+ options.set_option(:private_key,'@file:' + env[:private_key_path]) if env[:private_key_path].is_a?(String)
72
+ options.set_option(:workspace,:default)
73
+ options.parse_options!
74
+ AoC.use_standard_ports = options.get_option(:default_ports)
65
75
  return if env[:man_only]
66
76
  end
67
77
 
68
- def get_api
78
+ def aoc_api
69
79
  if @api_aoc.nil?
70
- @api_aoc=AoC.new(aoc_params(AoC::API_V1))
80
+ @api_aoc = AoC.new(aoc_params(AoC::API_V1))
71
81
  # add keychain for access key secrets
72
- @api_aoc.key_chain=@agents[:config]
82
+ @api_aoc.key_chain = @agents[:config]
73
83
  end
74
84
  return @api_aoc
75
85
  end
76
86
 
77
87
  # starts transfer using transfer agent
78
88
  def transfer_start(app,direction,node_file,ts_add)
79
- ts_add.deep_merge!(AoC.analytics_ts(app,direction,@workspace_id,@workspace_name))
80
- ts_add.deep_merge!(@api_aoc.console_ts(app))
81
- return self.transfer.start(*@api_aoc.tr_spec(app,direction,node_file,ts_add))
89
+ ts_add.deep_merge!(AoC.analytics_ts(app,direction,@workspace_info['id'],@workspace_info['name']))
90
+ ts_add.deep_merge!(aoc_api.console_ts(app))
91
+ return transfer.start(*aoc_api.tr_spec(app,direction,node_file,ts_add))
82
92
  end
83
93
 
84
- NODE4_COMMANDS=[ :browse, :find, :mkdir, :rename, :delete, :upload, :download, :transfer, :http_node_download, :v3, :file, :bearer_token_node, :node_info ].freeze
94
+ NODE4_COMMANDS = %i[bearer_token_node node_info browse find mkdir rename delete upload download transfer http_node_download v3 file].freeze
85
95
 
86
96
  def execute_node_gen4_command(command_repo,top_node_file)
87
97
  case command_repo
88
98
  when :bearer_token_node
89
- thepath=self.options.get_next_argument('path')
90
- node_file = @api_aoc.resolve_node_file(top_node_file,thepath)
91
- node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER, use_secret: false)
99
+ thepath = options.get_next_argument('path')
100
+ node_file = aoc_api.resolve_node_file(top_node_file,thepath)
101
+ node_api = aoc_api.get_node_api(node_file[:node_info], use_secret: false)
92
102
  return Main.result_status(node_api.oauth_token)
93
103
  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)
104
+ thepath = options.get_next_argument('path')
105
+ node_file = aoc_api.resolve_node_file(top_node_file,thepath)
106
+ node_api = aoc_api.get_node_api(node_file[:node_info], use_secret: false)
97
107
  return {type: :single_object,data: {
98
- url: node_file[:node_info]['url'],
108
+ url: node_file[:node_info]['url'],
99
109
  username: node_file[:node_info]['access_key'],
100
110
  password: node_api.oauth_token,
101
- root_id: node_file[:file_id]
102
- }}
111
+ root_id: node_file[:file_id]
112
+ }}
103
113
  when :browse
104
- thepath=self.options.get_next_argument('path')
105
- node_file = @api_aoc.resolve_node_file(top_node_file,thepath)
106
- node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
114
+ thepath = options.get_next_argument('path')
115
+ node_file = aoc_api.resolve_node_file(top_node_file,thepath)
116
+ node_api = aoc_api.get_node_api(node_file[:node_info])
107
117
  file_info = node_api.read("files/#{node_file[:file_id]}")[:data]
108
118
  if file_info['type'].eql?('folder')
109
- result=node_api.read("files/#{node_file[:file_id]}/files",self.options.get_option(:value,:optional))
110
- items=result[:data]
119
+ result = node_api.read("files/#{node_file[:file_id]}/files",options.get_option(:value))
120
+ items = result[:data]
111
121
  self.format.display_status("Items: #{result[:data].length}/#{result[:http]['X-Total-Count']}")
112
122
  else
113
- items=[file_info]
123
+ items = [file_info]
114
124
  end
115
- return {type: :object_list,data: items,fields: ['name','type','recursive_size','size','modified_time','access_level']}
125
+ return {type: :object_list,data: items,fields: %w[name type recursive_size size modified_time access_level]}
116
126
  when :find
117
- thepath=self.options.get_next_argument('path')
118
- node_file=@api_aoc.resolve_node_file(top_node_file,thepath)
119
- test_block=Aspera::Node.file_matcher(self.options.get_option(:value,:optional))
120
- return {type: :object_list,data: @api_aoc.find_files(node_file,test_block),fields: ['path']}
127
+ thepath = options.get_next_argument('path')
128
+ node_file = aoc_api.resolve_node_file(top_node_file,thepath)
129
+ test_block = Aspera::Node.file_matcher(options.get_option(:value))
130
+ return {type: :object_list,data: aoc_api.find_files(node_file,test_block),fields: ['path']}
121
131
  when :mkdir
122
- thepath=self.options.get_next_argument('path')
132
+ thepath = options.get_next_argument('path')
123
133
  containing_folder_path = thepath.split(AoC::PATH_SEPARATOR)
124
- new_folder=containing_folder_path.pop
125
- node_file = @api_aoc.resolve_node_file(top_node_file,containing_folder_path.join(AoC::PATH_SEPARATOR))
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]
134
+ new_folder = containing_folder_path.pop
135
+ node_file = aoc_api.resolve_node_file(top_node_file,containing_folder_path.join(AoC::PATH_SEPARATOR))
136
+ node_api = aoc_api.get_node_api(node_file[:node_info])
137
+ result = node_api.create("files/#{node_file[:file_id]}/files",{name: new_folder,type: :folder})[:data]
128
138
  return Main.result_status("created: #{result['name']} (id=#{result['id']})")
129
139
  when :rename
130
- thepath=self.options.get_next_argument('source path')
131
- newname=self.options.get_next_argument('new name')
132
- node_file = @api_aoc.resolve_node_file(top_node_file,thepath)
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]
140
+ thepath = options.get_next_argument('source path')
141
+ newname = options.get_next_argument('new name')
142
+ node_file = aoc_api.resolve_node_file(top_node_file,thepath)
143
+ node_api = aoc_api.get_node_api(node_file[:node_info])
144
+ result = node_api.update("files/#{node_file[:file_id]}",{name: newname})[:data]
135
145
  return Main.result_status("renamed #{thepath} to #{newname}")
136
146
  when :delete
137
- thepath=self.options.get_next_argument('path')
147
+ thepath = options.get_next_argument('path')
138
148
  return do_bulk_operation(thepath,'deleted','path') do |l_path|
139
149
  raise "expecting String (path), got #{l_path.class.name} (#{l_path})" unless l_path.is_a?(String)
140
- node_file = @api_aoc.resolve_node_file(top_node_file,l_path)
141
- node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
142
- result=node_api.delete("files/#{node_file[:file_id]}")[:data]
143
- {'path'=>l_path}
150
+ node_file = aoc_api.resolve_node_file(top_node_file,l_path)
151
+ node_api = aoc_api.get_node_api(node_file[:node_info])
152
+ result = node_api.delete("files/#{node_file[:file_id]}")[:data]
153
+ {'path' => l_path}
144
154
  end
145
155
  when :transfer
146
156
  # client side is agent
147
157
  # server side is protocol server
148
158
  # in same workspace
149
- server_home_node_file=client_home_node_file=top_node_file
159
+ server_home_node_file = client_home_node_file = top_node_file
150
160
  # default is push
151
- case self.options.get_option(:operation,:mandatory)
161
+ case options.get_option(:operation,is_type: :mandatory)
152
162
  when :push
153
- client_tr_oper='send'
154
- client_folder=self.options.get_option(:from_folder,:mandatory)
155
- server_folder=self.transfer.destination_folder(client_tr_oper)
163
+ client_tr_oper = Fasp::TransferSpec::DIRECTION_SEND
164
+ client_folder = options.get_option(:from_folder,is_type: :mandatory)
165
+ server_folder = transfer.destination_folder(client_tr_oper)
156
166
  when :pull
157
- client_tr_oper='receive'
158
- client_folder=self.transfer.destination_folder(client_tr_oper)
159
- server_folder=self.options.get_option(:from_folder,:mandatory)
167
+ client_tr_oper = Fasp::TransferSpec::DIRECTION_RECEIVE
168
+ client_folder = transfer.destination_folder(client_tr_oper)
169
+ server_folder = options.get_option(:from_folder,is_type: :mandatory)
160
170
  end
161
- client_node_file = @api_aoc.resolve_node_file(client_home_node_file,client_folder)
162
- server_node_file = @api_aoc.resolve_node_file(server_home_node_file,server_folder)
171
+ client_node_file = aoc_api.resolve_node_file(client_home_node_file,client_folder)
172
+ server_node_file = aoc_api.resolve_node_file(server_home_node_file,server_folder)
163
173
  # force node as transfer agent
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],
174
+ client_node_api = aoc_api.get_node_api(client_node_file[:node_info], use_secret: false)
175
+ @agents[:transfer].agent_instance = Fasp::AgentNode.new({
176
+ url: client_node_api.params[:base_url],
167
177
  username: client_node_file[:node_info]['access_key'],
168
178
  password: client_node_api.oauth_token,
169
179
  root_id: client_node_file[:file_id]
170
- }))
180
+ })
171
181
  # additional node to node TS info
172
- add_ts={
182
+ add_ts = {
173
183
  'remote_access_key' => server_node_file[:node_info]['access_key'],
174
184
  'destination_root_id' => server_node_file[:file_id],
175
185
  'source_root_id' => client_node_file[:file_id]
176
186
  }
177
187
  return Main.result_transfer(transfer_start(AoC::FILES_APP,client_tr_oper,server_node_file,add_ts))
178
188
  when :upload
179
- node_file = @api_aoc.resolve_node_file(top_node_file,self.transfer.destination_folder('send'))
180
- add_ts={'tags'=>{'aspera'=>{'files'=>{'parentCwd'=>"#{node_file[:node_info]['id']}:#{node_file[:file_id]}"}}}}
181
- return Main.result_transfer(transfer_start(AoC::FILES_APP,'send',node_file,add_ts))
189
+ node_file = aoc_api.resolve_node_file(top_node_file,transfer.destination_folder(Fasp::TransferSpec::DIRECTION_SEND))
190
+ add_ts = {'tags' => {'aspera' => {'files' => {'parentCwd' => "#{node_file[:node_info]['id']}:#{node_file[:file_id]}"}}}}
191
+ return Main.result_transfer(transfer_start(AoC::FILES_APP,Fasp::TransferSpec::DIRECTION_SEND,node_file,add_ts))
182
192
  when :download
183
- source_paths=self.transfer.ts_source_paths
193
+ source_paths = transfer.ts_source_paths
184
194
  # special case for AoC : all files must be in same folder
185
- source_folder=source_paths.shift['source']
195
+ source_folder = source_paths.shift['source']
186
196
  # if a single file: split into folder and path
187
197
  if source_paths.empty?
188
- source_folder=source_folder.split(AoC::PATH_SEPARATOR)
189
- source_paths=[{'source'=>source_folder.pop}]
190
- source_folder=source_folder.join(AoC::PATH_SEPARATOR)
198
+ source_folder = source_folder.split(AoC::PATH_SEPARATOR)
199
+ source_paths = [{'source' => source_folder.pop}]
200
+ source_folder = source_folder.join(AoC::PATH_SEPARATOR)
191
201
  end
192
- node_file = @api_aoc.resolve_node_file(top_node_file,source_folder)
202
+ node_file = aoc_api.resolve_node_file(top_node_file,source_folder)
193
203
  # override paths with just filename
194
- add_ts={'tags'=>{'aspera'=>{'files'=>{'parentCwd'=>"#{node_file[:node_info]['id']}:#{node_file[:file_id]}"}}}}
195
- add_ts.merge!({'paths'=>source_paths})
196
- return Main.result_transfer(transfer_start(AoC::FILES_APP,'receive',node_file,add_ts))
204
+ add_ts = {'tags' => {'aspera' => {'files' => {'parentCwd' => "#{node_file[:node_info]['id']}:#{node_file[:file_id]}"}}}}
205
+ add_ts['paths'] = source_paths
206
+ return Main.result_transfer(transfer_start(AoC::FILES_APP,Fasp::TransferSpec::DIRECTION_RECEIVE,node_file,add_ts))
197
207
  when :http_node_download
198
- source_paths=self.transfer.ts_source_paths
199
- source_folder=source_paths.shift['source']
208
+ source_paths = transfer.ts_source_paths
209
+ source_folder = source_paths.shift['source']
200
210
  if source_paths.empty?
201
- source_folder=source_folder.split(AoC::PATH_SEPARATOR)
202
- source_paths=[{'source'=>source_folder.pop}]
203
- source_folder=source_folder.join(AoC::PATH_SEPARATOR)
211
+ source_folder = source_folder.split(AoC::PATH_SEPARATOR)
212
+ source_paths = [{'source' => source_folder.pop}]
213
+ source_folder = source_folder.join(AoC::PATH_SEPARATOR)
204
214
  end
205
215
  raise CliBadArgument,'one file at a time only in HTTP mode' if source_paths.length > 1
206
216
  file_name = source_paths.first['source']
207
- node_file = @api_aoc.resolve_node_file(top_node_file,File.join(source_folder,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)})
217
+ node_file = aoc_api.resolve_node_file(top_node_file,File.join(source_folder,file_name))
218
+ node_api = aoc_api.get_node_api(node_file[:node_info])
219
+ node_api.call(
220
+ operation: 'GET',
221
+ subpath: "files/#{node_file[:file_id]}/content",
222
+ save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTION_RECEIVE),file_name))
210
223
  return Main.result_status("downloaded: #{file_name}")
211
224
  when :v3
212
225
  # Note: other common actions are unauthorized with user scope
213
- command_legacy=self.options.get_next_command(Node::SIMPLE_ACTIONS)
226
+ command_legacy = options.get_next_command(Node::SIMPLE_ACTIONS)
214
227
  # TODO: shall we support all methods here ? what if there is a link ?
215
- node_api=@api_aoc.get_node_api(top_node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
228
+ node_api = aoc_api.get_node_api(top_node_file[:node_info])
216
229
  return Node.new(@agents.merge(skip_basic_auth_options: true, node_api: node_api)).execute_action(command_legacy)
217
230
  when :file
218
- command_node_file=self.options.get_next_command([:show,:permission,:modify])
219
- file_path=self.options.get_option(:path,:optional)
220
- node_file = if !file_path.nil?
221
- @api_aoc.resolve_node_file(top_node_file,file_path) # TODO: allow follow link ?
222
- else
223
- {node_info: top_node_file[:node_info],file_id: self.instance_identifier()}
224
- end
225
- node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
231
+ command_node_file = options.get_next_command(%i[show permission modify])
232
+ file_path = options.get_option(:path)
233
+ node_file =
234
+ if !file_path.nil?
235
+ aoc_api.resolve_node_file(top_node_file,file_path) # TODO: allow follow link ?
236
+ else
237
+ {node_info: top_node_file[:node_info],file_id: instance_identifier}
238
+ end
239
+ node_api = aoc_api.get_node_api(node_file[:node_info])
226
240
  case command_node_file
227
241
  when :show
228
- items=node_api.read("files/#{node_file[:file_id]}")[:data]
242
+ items = node_api.read("files/#{node_file[:file_id]}")[:data]
229
243
  return {type: :single_object,data: items}
230
244
  when :modify
231
- update_param=self.options.get_next_argument('update data (Hash)')
232
- res=node_api.update("files/#{node_file[:file_id]}",update_param)[:data]
245
+ update_param = options.get_next_argument('update data (Hash)')
246
+ res = node_api.update("files/#{node_file[:file_id]}",update_param)[:data]
233
247
  return {type: :single_object,data: res}
234
248
  when :permission
235
- command_perm=self.options.get_next_command([:list,:create])
249
+ command_perm = options.get_next_command(%i[list create])
236
250
  case command_perm
237
251
  when :list
238
252
  # generic options : TODO: as arg ? option_url_query
239
- list_options||={'include'=>['[]','access_level','permission_count']}
253
+ list_options ||= {'include' => ['[]','access_level','permission_count']}
240
254
  # special value: ALL will show all permissions
241
255
  if !VAL_ALL.eql?(node_file[:file_id])
242
256
  # add which one to get
243
- list_options['file_id']=node_file[:file_id]
244
- list_options['inherited']||=false
257
+ list_options['file_id'] = node_file[:file_id]
258
+ list_options['inherited'] ||= false
245
259
  end
246
- items=node_api.read('permissions',list_options)[:data]
260
+ items = node_api.read('permissions',list_options)[:data]
247
261
  return {type: :object_list,data: items}
248
262
  when :create
249
263
  #create_param=self.options.get_next_argument('creation data (Hash)')
250
264
  set_workspace_info
251
- access_id="#{ID_AK_ADMIN}_WS_#{@workspace_id}"
265
+ access_id = "#{ID_AK_ADMIN}_WS_#{@workspace_info['id']}"
252
266
  node_file[:node_info]
253
- params={
254
- 'file_id' =>node_file[:file_id], # mandatory
255
- 'access_type' =>'user', # mandatory: user or group
256
- 'access_id' =>access_id, # id of user or group
257
- 'access_levels'=>Aspera::Node::ACCESS_LEVELS,
258
- 'tags' =>{'aspera'=>{'files'=>{'workspace'=>{
259
- 'id' =>@workspace_id,
260
- 'workspace_name' =>@workspace_name,
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'],
265
- 'shared_with_name' =>access_id,
266
- 'access_key' =>node_file[:node_info]['access_key'],
267
- 'node' =>node_file[:node_info]['name']}}}}}
268
- item=node_api.create('permissions',params)[:data]
267
+ params = {
268
+ 'file_id' => node_file[:file_id], # mandatory
269
+ 'access_type' => 'user', # mandatory: user or group
270
+ 'access_id' => access_id, # id of user or group
271
+ 'access_levels' => Aspera::Node::ACCESS_LEVELS,
272
+ 'tags' => {'aspera' => {'files' => {'workspace' => {
273
+ 'id' => @workspace_info['id'],
274
+ 'workspace_name' => @workspace_info['name'],
275
+ 'user_name' => aoc_api.user_info['name'],
276
+ 'shared_by_user_id' => aoc_api.user_info['id'],
277
+ 'shared_by_name' => aoc_api.user_info['name'],
278
+ 'shared_by_email' => aoc_api.user_info['email'],
279
+ 'shared_with_name' => access_id,
280
+ 'access_key' => node_file[:node_info]['access_key'],
281
+ 'node' => node_file[:node_info]['name']}}}}}
282
+ #node_api = aoc_api.get_node_api(node_file[:node_info])
283
+ item = node_api.create('permissions',params)[:data]
269
284
  return {type: :single_object,data: item}
270
285
  else raise "internal error:shall not reach here (#{command_perm})"
271
286
  end
272
- raise 'internal error:shall not reach here'
273
287
  else raise "internal error:shall not reach here (#{command_node_file})"
274
288
  end
275
- raise 'internal error:shall not reach here'
276
289
  end # command_repo
277
- raise 'ERR'
290
+ raise 'internal error:shall not reach here'
278
291
  end # execute_node_gen4_command
279
-
292
+ AOC_PARAMS_COPY=%i[link url auth client_id client_secret scope redirect_uri private_key passphrase username password].freeze
280
293
  # build constructor option list for AoC based on options of CLI
281
294
  def aoc_params(subpath)
282
295
  # copy command line options to args
283
- opt=[:link,:url,:auth,:client_id,:client_secret,:scope,:redirect_uri,:private_key,:username,:password].inject({}){|m,i|m[i]=self.options.get_option(i,:optional);m}
284
- opt[:subpath]=subpath
296
+ opt = AOC_PARAMS_COPY.each_with_object({}){|i,m|m[i] = options.get_option(i)}
297
+ opt[:subpath] = subpath
285
298
  return opt
286
299
  end
287
300
 
288
301
  # initialize apis and authentication
289
302
  # set:
290
- # @default_workspace_id
291
- # @workspace_name
292
- # @workspace_id
303
+ # @workspace_info
293
304
  # @persist_ids
294
305
  # returns nil
295
306
  def set_workspace_info
296
- if @api_aoc.params[:auth].has_key?(:url_token)
297
- # TODO: can there be several in list ?
298
- @url_token_data=@api_aoc.read('url_tokens')[:data].first
299
- @default_workspace_id=@url_token_data['data']['workspace_id']
300
- @persist_ids=[] # TODO : @url_token_data['id'] ?
307
+ @url_token_data = aoc_api.url_token_data
308
+ if @url_token_data.nil?
309
+ default_workspace_id = aoc_api.user_info['default_workspace_id']
310
+ @persist_ids = [aoc_api.user_info['id']]
301
311
  else
302
- @default_workspace_id=@api_aoc.user_info['default_workspace_id']
303
- @persist_ids=[@api_aoc.user_info['id']]
312
+ default_workspace_id = @url_token_data['data']['workspace_id']
313
+ @persist_ids = [] # TODO : @url_token_data['id'] ?
304
314
  end
305
315
 
306
- ws_name=self.options.get_option(:workspace,:optional)
307
- if ws_name.nil?
308
- Log.log.debug('using default workspace'.green)
309
- if @default_workspace_id.eql?(nil)
310
- raise CliError,'no default workspace defined for user, please specify workspace'
316
+ ws_name = options.get_option(:workspace)
317
+ ws_id =
318
+ case ws_name
319
+ when :default
320
+ Log.log.debug('Using default workspace'.green)
321
+ raise CliError,'No default workspace defined for user, please specify workspace' if default_workspace_id.nil?
322
+ default_workspace_id
323
+ when String then aoc_api.lookup_entity_by_name('workspaces',ws_name)['id']
324
+ when NilClass then nil
325
+ else raise CliError,'unexpected value type for workspace'
311
326
  end
312
- # get default workspace
313
- @workspace_id=@default_workspace_id
314
- else
315
- # lookup another workspace
316
- wss=@api_aoc.read('workspaces',{'q'=>ws_name})[:data]
317
- wss=wss.select { |i| i['name'].eql?(ws_name) }
318
- case wss.length
319
- when 0
320
- raise CliBadArgument,"no such workspace: #{ws_name}"
321
- when 1
322
- @workspace_id=wss.first['id']
323
- else
324
- raise 'unexpected case'
327
+ @workspace_info =
328
+ begin
329
+ aoc_api.read("workspaces/#{ws_id}")[:data]
330
+ rescue Aspera::RestCallError => e
331
+ Log.log.debug(e.message)
332
+ { 'id' => :undefined, 'name' => :undefined }
325
333
  end
326
- end
327
- @workspace_data=@api_aoc.read("workspaces/#{@workspace_id}")[:data]
328
- Log.log.debug("workspace_id=#{@workspace_id},@workspace_data=#{@workspace_data}".red)
329
-
330
- @workspace_name||=@workspace_data['name']
331
- Log.log.info('current workspace is '+@workspace_name.red)
332
-
334
+ Log.dump(:workspace_data,@workspace_info)
333
335
  # display workspace
334
- self.format.display_status("Current Workspace: #{@workspace_name.red}#{@workspace_id == @default_workspace_id ? ' (default)' : ''}")
336
+ self.format.display_status("Current Workspace: #{@workspace_info['name'].to_s.red}#{@workspace_info['id'] == default_workspace_id ? ' (default)' : ''}")
335
337
  return nil
336
338
  end
337
339
 
@@ -339,87 +341,85 @@ module Aspera
339
341
  def set_home_node_file
340
342
  if !@url_token_data.nil?
341
343
  assert_public_link_types(['view_shared_file'])
342
- home_node_id=@url_token_data['data']['node_id']
343
- home_file_id=@url_token_data['data']['file_id']
344
+ home_node_id = @url_token_data['data']['node_id']
345
+ home_file_id = @url_token_data['data']['file_id']
344
346
  end
345
- home_node_id||=@workspace_data['home_node_id']||@workspace_data['node_id']
346
- home_file_id||=@workspace_data['home_file_id']
347
- raise 'node_id must be defined' if home_node_id.to_s.empty?
348
- @home_node_file={
349
- node_info: @api_aoc.read("nodes/#{home_node_id}")[:data],
350
- file_id: home_file_id
347
+ home_node_id ||= @workspace_info['home_node_id'] || @workspace_info['node_id']
348
+ home_file_id ||= @workspace_info['home_file_id']
349
+ raise 'Cannot get users home node id' if home_node_id.to_s.empty?
350
+ @home_node_file = {
351
+ node_info: aoc_api.read("nodes/#{home_node_id}")[:data],
352
+ file_id: home_file_id
351
353
  }
352
- @api_aoc.check_get_node_file(@home_node_file)
354
+ aoc_api.check_get_node_file(@home_node_file)
353
355
 
354
356
  return nil
355
357
  end
356
358
 
357
- def do_bulk_operation(ids_or_one,success_msg,id_result='id',&do_action)
358
- ids_or_one=[ids_or_one] unless self.options.get_option(:bulk)
359
+ def do_bulk_operation(ids_or_one,success_msg,id_result='id')
360
+ raise 'missing block' unless block_given?
361
+ ids_or_one = [ids_or_one] unless options.get_option(:bulk)
359
362
  raise 'expecting Array' unless ids_or_one.is_a?(Array)
360
- result_list=[]
363
+ result_list = []
361
364
  ids_or_one.each do |id|
362
- one={id_result=>id}
365
+ one = {id_result => id}
363
366
  begin
364
- res=do_action.call(id)
365
- one=res if id.is_a?(Hash) # if block returns a has, let's use this
366
- one['status']=success_msg
367
- rescue => e
368
- one['status']=e.to_s
367
+ res = yield(id)
368
+ one = res if id.is_a?(Hash) # if block returns a has, let's use this
369
+ one['status'] = success_msg
370
+ rescue StandardError => e
371
+ one['status'] = e.to_s
369
372
  end
370
373
  result_list.push(one)
371
374
  end
372
375
  return {type: :object_list,data: result_list,fields: [id_result,'status']}
373
376
  end
374
377
 
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
378
+ # get identifier or name from command line
379
+ # @return identifier
380
+ def get_resource_id_from_args(resource_class_path)
381
+ l_res_id = options.get_option(:id)
382
+ l_res_name = options.get_option(:name)
383
+ raise 'Provide either option id or name, not both' unless l_res_id.nil? || l_res_name.nil?
384
+ # try to find item by name (single partial match or exact match)
385
+ l_res_id = aoc_api.lookup_entity_by_name(resource_class_path,l_res_name)['id'] unless l_res_name.nil?
386
+ # if no name or id option, taken on command line (after command)
387
+ if l_res_id.nil?
388
+ l_res_id = options.get_next_argument('identifier')
389
+ l_res_id = aoc_api.lookup_entity_by_name(resource_class_path,options.get_next_argument('identifier'))['id'] if l_res_id.eql?('name')
392
390
  end
391
+ return l_res_id
392
+ end
393
+
394
+ def get_resource_path_from_args(resource_class_path)
395
+ return "#{resource_class_path}/#{get_resource_id_from_args(resource_class_path)}"
393
396
  end
394
397
 
395
398
  # package creation params can give just email, and full hash is created
396
- def resolve_package_recipients(package_creation,recipient_list_field)
397
- return unless package_creation.has_key?(recipient_list_field)
398
- raise CliBadArgument,"#{recipient_list_field} must be an Array" unless package_creation[recipient_list_field].is_a?(Array)
399
- new_user_option=self.options.get_option(:new_user_option,:mandatory)
399
+ def resolve_package_recipients(package_data,recipient_list_field)
400
+ return unless package_data.has_key?(recipient_list_field)
401
+ raise CliBadArgument,"#{recipient_list_field} must be an Array" unless package_data[recipient_list_field].is_a?(Array)
402
+ new_user_option = options.get_option(:new_user_option,is_type: :mandatory)
400
403
  # list with resolved elements
401
- resolved_list=[]
402
- package_creation[recipient_list_field].each do |short_recipient_info|
404
+ resolved_list = []
405
+ package_data[recipient_list_field].each do |short_recipient_info|
403
406
  case short_recipient_info
404
407
  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'])
408
+ raise "#{recipient_list_field} element shall have fields: id and type" unless short_recipient_info.keys.sort.eql?(%w[id type])
406
409
  when String # need to resolve name to type/id
407
410
  # email: user, else dropbox
408
- entity_type=short_recipient_info.include?('@') ? 'contacts' : 'dropboxes'
411
+ entity_type = short_recipient_info.include?('@') ? 'contacts' : 'dropboxes'
409
412
  begin
410
- full_recipient_info=lookup_single(entity_type,short_recipient_info,{'current_workspace_id'=>@workspace_id})
413
+ full_recipient_info = aoc_api.lookup_entity_by_name(entity_type,short_recipient_info,{'current_workspace_id' => @workspace_info['id']})
411
414
  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}"
417
- end
415
+ raise e unless e.message.start_with?(Aspera::AoC::ENTITY_NOT_FOUND)
416
+ raise "no such shared inbox in workspace #{@workspace_info['name']}" unless entity_type.eql?('contacts')
417
+ full_recipient_info = aoc_api.create('contacts',{'current_workspace_id' => @workspace_info['id'],'email' => short_recipient_info}.merge(new_user_option))[:data]
418
418
  end
419
- if entity_type.eql?('dropboxes')
420
- short_recipient_info={'id'=>full_recipient_info['id'],'type'=>'dropbox'}
419
+ short_recipient_info = if entity_type.eql?('dropboxes')
420
+ {'id' => full_recipient_info['id'],'type' => 'dropbox'}
421
421
  else
422
- short_recipient_info={'id'=>full_recipient_info['source_id'],'type'=>full_recipient_info['source_type']}
422
+ {'id' => full_recipient_info['source_id'],'type' => full_recipient_info['source_type']}
423
423
  end
424
424
  else # unexpected extended value, must be String or Hash
425
425
  raise "#{recipient_list_field} item must be a String (email, shared inbox) or Hash (id,type)"
@@ -428,76 +428,129 @@ module Aspera
428
428
  resolved_list.push(short_recipient_info)
429
429
  end
430
430
  # replace with resolved elements
431
- package_creation[recipient_list_field]=resolved_list
431
+ package_data[recipient_list_field] = resolved_list
432
+ end
433
+
434
+ def normalize_metadata(pkg_data)
435
+ case pkg_data['metadata']
436
+ when Array,NilClass # no action
437
+ when Hash
438
+ api_meta = []
439
+ pkg_data['metadata'].each do |k,v|
440
+ api_meta.push({
441
+ #'input_type' => 'single-dropdown',
442
+ 'name' => k,
443
+ 'values' => v.is_a?(Array) ? v : [v]
444
+ })
445
+ end
446
+ pkg_data['metadata'] = api_meta
447
+ else raise "metadata field if not of expected type: #{pkg_meta.class}"
448
+ end
449
+ return nil
450
+ end
451
+
452
+ # Check metadata: remove when validation is done server side
453
+ def validate_metadata(pkg_data)
454
+ # validate only for shared inboxes
455
+ return unless
456
+ pkg_data['recipients'].is_a?(Array) &&
457
+ pkg_data['recipients'].first.is_a?(Hash) &&
458
+ pkg_data['recipients'].first.has_key?('type') &&
459
+ pkg_data['recipients'].first['type'].eql?('dropbox')
460
+
461
+ shbx_kid = pkg_data['recipients'].first['id']
462
+ meta_schema = aoc_api.read("dropboxes/#{shbx_kid}")[:data]['metadata_schema']
463
+ if meta_schema.nil? || meta_schema.empty?
464
+ Log.log.debug('no metadata in shared inbox')
465
+ return
466
+ end
467
+ pkg_meta = pkg_data['metadata']
468
+ raise "package requires metadata: #{meta_schema}" unless pkg_data.has_key?('metadata')
469
+ raise 'metadata must be an Array' unless pkg_meta.is_a?(Array)
470
+ Log.dump(:metadata,pkg_meta)
471
+ pkg_meta.each do |field|
472
+ raise 'metadata field must be Hash' unless field.is_a?(Hash)
473
+ raise 'metadata field must have name' unless field.has_key?('name')
474
+ raise 'metadata field must have values' unless field.has_key?('values')
475
+ raise 'metadata values must be an Array' unless field['values'].is_a?(Array)
476
+ raise "unknown metadata field: #{field['name']}" if meta_schema.select{|i|i['name'].eql?(field['name'])}.empty?
477
+ end
478
+ meta_schema.each do |field|
479
+ provided=pkg_meta.select{|i|i['name'].eql?(field['name'])}
480
+ raise "only one field with name #{field['name']} allowed" if provided.count > 1
481
+ raise "missing mandatory field: #{field['name']}" if field['required'] && provided.empty?
482
+ end
432
483
  end
433
484
 
434
485
  # private
435
486
  def option_url_query(default)
436
- query=self.options.get_option(:query,:optional)
437
- query=default if query.nil?
487
+ query = options.get_option(:query)
488
+ query = default if query.nil?
438
489
  Log.log.debug("Query=#{query}".bg_red)
439
490
  begin
440
491
  # check it is suitable
441
492
  URI.encode_www_form(query) unless query.nil?
442
- rescue => e
493
+ rescue StandardError => e
443
494
  raise CliBadArgument,"query must be an extended value which can be encoded with URI.encode_www_form. Refer to manual. (#{e.message})"
444
495
  end
445
496
  return query
446
497
  end
447
498
 
448
499
  def assert_public_link_types(expected)
449
- if !expected.include?(@url_token_data['purpose'])
450
- raise CliBadArgument,"public link type is #{@url_token_data['purpose']} but action requires one of #{expected.join(',')}"
451
- end
500
+ raise CliBadArgument,"public link type is #{@url_token_data['purpose']} but action requires one of #{expected.join(',')}" \
501
+ unless expected.include?(@url_token_data['purpose'])
452
502
  end
453
503
 
454
- # Call @api_aoc.read with same parameters, but use paging if necessary to get all results
504
+ # Call aoc_api.read with same parameters.
505
+ # Use paging if necessary to get all results
455
506
  def read_with_paging(resource_class_path,base_query)
456
- raise "Query must be Hash" unless base_query.is_a?(Hash)
507
+ raise 'Query must be Hash' unless base_query.is_a?(Hash)
457
508
  # 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]
509
+ base_query['per_page'] = 1000 unless base_query.has_key?('per_page')
510
+ max_items = base_query[MAX_ITEMS]
460
511
  base_query.delete(MAX_ITEMS)
461
- max_pages=base_query[MAX_PAGES]
512
+ max_pages = base_query[MAX_PAGES]
462
513
  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
514
+ item_list = []
515
+ total_count = nil
516
+ current_page = base_query['page']
517
+ current_page = 1 if current_page.nil?
518
+ page_count = 0
468
519
  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]
520
+ query = base_query.clone
521
+ query['page'] = current_page
522
+ result = aoc_api.read(resource_class_path,query)
523
+ total_count = result[:http]['X-Total-Count']
524
+ page_count += 1
525
+ current_page += 1
526
+ add_items = result[:data]
476
527
  break if add_items.empty?
477
528
  # append new items to full list
478
529
  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
530
+ break if !max_pages.nil? && page_count > max_pages
531
+ break if !max_items.nil? && item_list.count > max_items
481
532
  end
482
533
  return item_list,total_count
483
534
  end
484
535
 
485
536
  def execute_admin_action
486
- @api_aoc.oauth.params[:scope]=AoC::SCOPE_FILES_ADMIN
487
- command_admin=self.options.get_next_command([ :ats, :resource, :usage_reports, :analytics, :subscription, :auth_providers ])
537
+ # upgrade scope to admin
538
+ aoc_api.oauth.gparams[:scope] = AoC::SCOPE_FILES_ADMIN
539
+ command_admin = options.get_next_command(%i[ats resource usage_reports analytics subscription auth_providers])
488
540
  case command_admin
489
541
  when :auth_providers
490
- command_auth_prov=self.options.get_next_command([ :list, :update ])
542
+ command_auth_prov = options.get_next_command(%i[list update])
491
543
  case command_auth_prov
492
544
  when :list
493
- providers=@api_aoc.read('admin/auth_providers')[:data]
545
+ providers = aoc_api.read('admin/auth_providers')[:data]
494
546
  return {type: :object_list,data: providers}
495
547
  when :update
548
+ raise 'not implemented'
496
549
  end
497
550
  when :subscription
498
- org=@api_aoc.read('organization')[:data]
499
- bss_api=AoC.new(aoc_params('bss/platform'))
500
- graphql_query="
551
+ org = aoc_api.read('organization')[:data]
552
+ bss_api = AoC.new(aoc_params('bss/platform'))
553
+ graphql_query = "
501
554
  query ($organization_id: ID!) {
502
555
  aoc (organization_id: $organization_id) {
503
556
  bssSubscription {
@@ -545,413 +598,478 @@ module Aspera
545
598
  }
546
599
  }
547
600
  "
548
- result=bss_api.create('graphql',{'variables'=>{'organization_id'=>org['id']},'query'=>graphql_query})[:data]['data']
601
+ result = bss_api.create('graphql',{'variables' => {'organization_id' => org['id']},'query' => graphql_query})[:data]['data']
549
602
  return {type: :single_object,data: result['aoc']['bssSubscription']}
550
603
  when :ats
551
- ats_api = Rest.new(@api_aoc.params.deep_merge({
552
- base_url: @api_aoc.params[:base_url]+'/admin/ats/pub/v1',
553
- auth: {scope: AoC::SCOPE_FILES_ADMIN_USER}
604
+ ats_api = Rest.new(aoc_api.params.deep_merge({
605
+ base_url: aoc_api.params[:base_url] + '/admin/ats/pub/v1',
606
+ auth: {scope: AoC::SCOPE_FILES_ADMIN_USER}
554
607
  }))
555
608
  return Ats.new(@agents).execute_action_gen(ats_api)
556
609
  when :analytics
557
- analytics_api = Rest.new(@api_aoc.params.deep_merge({
558
- base_url: @api_aoc.params[:base_url].gsub('/api/v1','')+'/analytics/v2',
559
- auth: {scope: AoC::SCOPE_FILES_ADMIN_USER}
610
+ analytics_api = Rest.new(aoc_api.params.deep_merge({
611
+ base_url: aoc_api.params[:base_url].gsub('/api/v1','') + '/analytics/v2',
612
+ auth: {scope: AoC::SCOPE_FILES_ADMIN_USER}
560
613
  }))
561
- command_analytics=self.options.get_next_command([ :application_events, :transfers ])
614
+ command_analytics = options.get_next_command(%i[application_events transfers])
562
615
  case command_analytics
563
616
  when :application_events
564
- event_type=command_analytics.to_s
565
- events=analytics_api.read("organizations/#{@api_aoc.user_info['organization_id']}/#{event_type}")[:data][event_type]
617
+ event_type = command_analytics.to_s
618
+ events = analytics_api.read("organizations/#{aoc_api.user_info['organization_id']}/#{event_type}")[:data][event_type]
566
619
  return {type: :object_list,data: events}
567
620
  when :transfers
568
- event_type=command_analytics.to_s
569
- filter_resource=self.options.get_option(:name,:optional) || 'organizations'
570
- filter_id=self.options.get_option(:id,:optional) || case filter_resource
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']
621
+ event_type = command_analytics.to_s
622
+ filter_resource = options.get_option(:name) || 'organizations'
623
+ filter_id = options.get_option(:id) ||
624
+ case filter_resource
625
+ when 'organizations' then aoc_api.user_info['organization_id']
626
+ when 'users' then aoc_api.user_info['id']
627
+ when 'nodes' then aoc_api.user_info['id'] # TODO: consistent ? # rubocop:disable Lint/DuplicateBranch
574
628
  else raise 'organizations or users for option --name'
575
629
  end
576
- #
577
- filter=self.options.get_option(:query,:optional) || {}
630
+ filter = options.get_option(:query) || {}
578
631
  raise 'query must be Hash' unless filter.is_a?(Hash)
579
- filter['limit']||=100
580
- if self.options.get_option(:once_only,:mandatory)
581
- saved_date=[]
582
- startdate_persistency=PersistencyActionOnce.new(
583
- manager: @agents[:persistency],
584
- data: saved_date,
585
- ids: IdGenerator.from_list(['aoc_ana_date',self.options.get_option(:url,:mandatory),@workspace_name].push(filter_resource,filter_id)))
586
- start_datetime=saved_date.first
587
- stop_datetime=Time.now.utc.strftime('%FT%T.%LZ')
632
+ filter['limit'] ||= 100
633
+ if options.get_option(:once_only,is_type: :mandatory)
634
+ saved_date = []
635
+ startdate_persistency = PersistencyActionOnce.new(
636
+ manager: @agents[:persistency],
637
+ data: saved_date,
638
+ ids: IdGenerator.from_list(['aoc_ana_date',options.get_option(:url,is_type: :mandatory),@workspace_info['name']].push(filter_resource,filter_id)))
639
+ start_datetime = saved_date.first
640
+ stop_datetime = Time.now.utc.strftime('%FT%T.%LZ')
588
641
  #Log.log().error("start: #{start_datetime}")
589
642
  #Log.log().error("end: #{stop_datetime}")
590
- saved_date[0]=stop_datetime
643
+ saved_date[0] = stop_datetime
591
644
  filter['start_time'] = start_datetime unless start_datetime.nil?
592
645
  filter['stop_time'] = stop_datetime
593
646
  end
594
- events=analytics_api.read("#{filter_resource}/#{filter_id}/#{event_type}",option_url_query(filter))[:data][event_type]
595
- startdate_persistency.save unless startdate_persistency.nil?
596
- if !self.options.get_option(:notif_to,:optional).nil?
647
+ events = analytics_api.read("#{filter_resource}/#{filter_id}/#{event_type}",option_url_query(filter))[:data][event_type]
648
+ startdate_persistency&.save
649
+ if !options.get_option(:notif_to).nil?
597
650
  events.each do |tr_event|
598
- self.config.send_email_template({ev: tr_event})
651
+ config.send_email_template({ev: tr_event})
599
652
  end
600
653
  end
601
654
  return {type: :object_list,data: events}
602
655
  end
603
656
  when :resource
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])
657
+ resource_type = options.get_next_argument('resource',expected: KNOWN_AOC_RES)
605
658
  # get path on API, resource type is singular, but api is plural
606
- resource_class_path=case resource_type
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'
614
- end
659
+ resource_class_path =
660
+ case resource_type
661
+ # special cases: singleton, in admin, with x
662
+ when :self,:organization then resource_type
663
+ when :client_registration_token,:client_access_key then "admin/#{resource_type}s"
664
+ when :application then 'admin/apps_new'
665
+ when :dropbox then resource_type.to_s + 'es'
666
+ when :kms_profile then "integrations/#{resource_type}s"
667
+ else "#{resource_type}s"
668
+ end
615
669
  # build list of supported operations
616
- singleton_object=[:self,:organization].include?(resource_type)
617
- global_operations=[:create,:list]
618
- supported_operations=[:show,:modify]
670
+ singleton_object = %i[self organization].include?(resource_type)
671
+ global_operations = %i[create list]
672
+ supported_operations = %i[show modify]
619
673
  supported_operations.push(:delete,*global_operations) unless singleton_object
620
674
  supported_operations.push(:v4,:v3) if resource_type.eql?(:node)
621
675
  supported_operations.push(:set_pub_key) if resource_type.eql?(:client)
622
- supported_operations.push(:shared_folders,:shared_create) if [:node,:workspace].include?(resource_type)
623
- command=self.options.get_next_command(supported_operations)
676
+ supported_operations.push(:shared_folder) if resource_type.eql?(:workspace)
677
+ command = options.get_next_command(supported_operations)
624
678
  # require identifier for non global commands
625
- if !singleton_object and !global_operations.include?(command)
626
- res_id=self.options.get_option(:id)
627
- res_name=self.options.get_option(:name)
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')
635
- end
636
- resource_instance_path="#{resource_class_path}/#{res_id}"
679
+ if !singleton_object && !global_operations.include?(command)
680
+ res_id = get_resource_id_from_args(resource_class_path)
681
+ resource_instance_path = "#{resource_class_path}/#{res_id}"
637
682
  end
638
- resource_instance_path=resource_class_path if singleton_object
683
+ resource_instance_path = resource_class_path if singleton_object
639
684
  case command
640
685
  when :create
641
- id_result='id'
642
- id_result='token' if resource_class_path.eql?('admin/client_registration_tokens')
686
+ id_result = 'id'
687
+ id_result = 'token' if resource_class_path.eql?('admin/client_registration_tokens')
643
688
  # TODO: report inconsistency: creation url is !=, and does not return id.
644
- resource_class_path='admin/client_registration/token' if resource_class_path.eql?('admin/client_registration_tokens')
645
- list_or_one=self.options.get_next_argument('creation data (Hash)')
646
- return do_bulk_operation(list_or_one,'created',id_result)do|params|
689
+ resource_class_path = 'admin/client_registration/token' if resource_class_path.eql?('admin/client_registration_tokens')
690
+ list_or_one = options.get_next_argument('creation data (Hash)')
691
+ return do_bulk_operation(list_or_one,'created',id_result) do |params|
647
692
  raise 'expecting Hash' unless params.is_a?(Hash)
648
- @api_aoc.create(resource_class_path,params)[:data]
693
+ aoc_api.create(resource_class_path,params)[:data]
649
694
  end
650
695
  when :list
651
- default_fields=['id']
652
- default_query={}
696
+ default_fields = ['id']
697
+ default_query = {}
653
698
  case resource_type
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')
659
- when :operation; default_fields=nil
660
- when :short_link; default_fields.push('short_url','data.url_token_data.purpose')
661
- when :user; default_fields.push('name','email')
699
+ when :application then default_query = {organization_apps: true};
700
+ default_fields.push('app_type','app_name','available','direct_authorizations_allowed','workspace_authorizations_allowed')
701
+ when :client,:client_access_key,:dropbox,:group,:package,:saml_configuration,:workspace then default_fields.push('name')
702
+ when :client_registration_token then default_fields.push('value','data.client_subject_scopes','created_at')
703
+ when :contact then default_fields = %w[email name source_id source_type]
704
+ when :node then default_fields.push('name','host','access_key')
705
+ when :operation then default_fields = nil
706
+ when :short_link then default_fields.push('short_url','data.url_token_data.purpose')
707
+ when :user then default_fields.push('name','email')
708
+ when :group_membership then default_fields.push(*%w[group_id member_type member_id])
709
+ when :workspace_membership then default_fields.push(*%w[workspace_id member_type member_id])
662
710
  end
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)
711
+ item_list,total_count = read_with_paging(resource_class_path,option_url_query(default_query))
712
+ count_msg = "Items: #{item_list.length}/#{total_count}"
713
+ count_msg = count_msg.bg_red unless item_list.length.eql?(total_count.to_i)
666
714
  self.format.display_status(count_msg)
667
715
  return {type: :object_list,data: item_list,fields: default_fields}
668
716
  when :show
669
- object=@api_aoc.read(resource_instance_path)[:data]
670
- fields=object.keys.select{|k|!k.eql?('certificate')}
717
+ object = aoc_api.read(resource_instance_path)[:data]
718
+ fields = object.keys.reject{|k|k.eql?('certificate')}
671
719
  return { type: :single_object, data: object, fields: fields }
672
720
  when :modify
673
- changes=self.options.get_next_argument('modified parameters (hash)')
674
- @api_aoc.update(resource_instance_path,changes)
721
+ changes = options.get_next_argument('modified parameters (hash)')
722
+ aoc_api.update(resource_instance_path,changes)
675
723
  return Main.result_status('modified')
676
724
  when :delete
677
- return do_bulk_operation(res_id,'deleted')do|one_id|
678
- @api_aoc.delete("#{resource_class_path}/#{one_id.to_s}")
679
- {'id'=>one_id}
725
+ return do_bulk_operation(res_id,'deleted') do |one_id|
726
+ aoc_api.delete("#{resource_class_path}/#{one_id}")
727
+ {'id' => one_id}
680
728
  end
681
729
  when :set_pub_key
682
730
  # special : reads private and generate public
683
- the_private_key=self.options.get_next_argument('private_key')
684
- the_public_key=OpenSSL::PKey::RSA.new(the_private_key).public_key.to_s
685
- @api_aoc.update(resource_instance_path,{jwt_grant_enabled: true, public_key: the_public_key})
731
+ the_private_key = options.get_next_argument('private_key')
732
+ the_public_key = OpenSSL::PKey::RSA.new(the_private_key).public_key.to_s
733
+ aoc_api.update(resource_instance_path,{jwt_grant_enabled: true, public_key: the_public_key})
686
734
  return Main.result_success
687
735
  when :v3,:v4
688
- res_data=@api_aoc.read(resource_instance_path)[:data]
689
- api_node=@api_aoc.get_node_api(res_data)
736
+ res_data = aoc_api.read(resource_instance_path)[:data]
737
+ api_node = aoc_api.get_node_api(res_data)
690
738
  return Node.new(@agents.merge(skip_basic_auth_options: true, node_api: api_node)).execute_action if command.eql?(:v3)
691
- ak_data=api_node.call({operation: 'GET',subpath: "access_keys/#{res_data['access_key']}",headers: {'Accept'=>'application/json'}})[:data]
692
- command_repo=self.options.get_next_command(NODE4_COMMANDS)
693
- return execute_node_gen4_command(command_repo,{node_info: res_data, file_id: ak_data['root_file_id']})
694
- when :shared_folders
695
- read_params = case resource_type
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'
699
- end
700
- res_data=@api_aoc.read("#{resource_class_path}/#{res_id}/permissions",read_params)[:data]
701
- fields=case resource_type
702
- when :node;['id','file_id','file.path','access_type']
703
- when :workspace;['id','node_id','file_id','node_name','file.path','tags.aspera.files.workspace.share_as']
704
- else raise "unexpected resource type #{resource_type}"
705
- end
706
- return { type: :object_list, data: res_data , fields: fields}
707
- when :shared_create
708
- # TODO: finish implementation
709
- folder_path=self.options.get_next_argument('folder path in node')
710
- user_create_data=self.options.get_next_argument('creation data (Hash)')
711
- res_data=@api_aoc.read(resource_instance_path)[:data]
712
- node_file = @api_aoc.resolve_node_file({node_info: res_data, file_id: ak_data['root_file_id']},folder_path)
713
-
714
- #node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
715
- #file_info = node_api.read("files/#{node_file[:file_id]}")[:data]
716
-
717
- access_id="#{ID_AK_ADMIN}_WS_#{@workspace_id}"
718
- create_data={
719
- 'file_id' =>node_file[:file_id],
720
- 'access_type' =>'user',
721
- 'access_id' =>access_id,
722
- 'access_levels'=>['list','read','write','delete','mkdir','rename','preview'],
723
- 'tags' =>{'aspera'=>{'files'=>{'workspace'=>{
724
- 'id' =>@workspace_id,
725
- 'workspace_name' =>@workspace_name,
726
- 'share_as' =>File.basename(folder_path),
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'],
731
- 'shared_with_name' =>access_id,
732
- 'access_key' =>node_file[:node_info]['access_key'],
733
- 'node' =>node_file[:node_info]['name']}
739
+ ak_root_file_id = api_node.read("access_keys/#{res_data['access_key']}")[:data]['root_file_id']
740
+ command_repo = options.get_next_command(NODE4_COMMANDS)
741
+ return execute_node_gen4_command(command_repo,{node_info: res_data, file_id: ak_root_file_id})
742
+ when :shared_folder
743
+ Log.log.warn('ATTENTION: under development')
744
+ # inside a workspace
745
+ command_shared = options.get_next_command(%i[list create member delete])
746
+ core_api=Rest.new(aoc_api.params.merge(base_url: aoc_api.params[:base_url].gsub('/api.','/sedemo.')))
747
+ # generic permission created for each shared folder
748
+ access_id = "#{ID_AK_ADMIN}_WS_#{res_id}"
749
+ case command_shared
750
+ when :list
751
+ query=options.get_option(:query)
752
+ query={'admin' => true, 'access_id' => access_id, 'access_type' => 'user'} if query.nil?
753
+ res_data = aoc_api.read("#{resource_instance_path}/permissions",query)[:data]
754
+ return { type: :object_list, data: res_data, fields: %w[id node_id file_id node_name file.path tags.aspera.files.workspace.share_as access_id]}
755
+ when :member
756
+ #https://sedemo.ibmaspera.com/api/v1/node/8669/permissions_and_members/3270?inherited=false&aspera-node-basic=8669&admin=true&page=1&per_page=25
757
+ when :delete
758
+ shared_id=instance_identifier
759
+ all_shared=aoc_api.read("#{resource_instance_path}/permissions",query)[:data].select{|i|i['id'].eql?(shared_id)}
760
+ raise 'no such shared folder id' if all_shared.empty?
761
+ raise 'error' unless all_shared.length.eql?(1)
762
+ shared_info=all_shared.first
763
+ #return { type: :single_object, data: shared_info}
764
+ node_id=shared_info['node_id']
765
+ Log.log.warn('ATTENTION: under dev: user vars: V1 and V2')
766
+ core_api.call(
767
+ operation: 'DELETE',
768
+ subpath: "node/#{node_id}/permissions",
769
+ headers: {
770
+ 'Accept' => 'application/json',
771
+ 'aspera-node-auth' => ENV['V1'],
772
+ 'aspera-node-tokens' => ENV['V2']
773
+ },
774
+ url_params: {
775
+ 'ids' => shared_id,
776
+ 'aspera-node-basic' => node_id,
777
+ 'aspera-node-prefer-basic' => node_id
778
+ }
779
+ )
780
+ return Main.result_success
781
+ when :create
782
+ # workspace information
783
+ ws_info = aoc_api.read(resource_instance_path)[:data]
784
+ shared_create_data = options.get_next_argument('creation data',type: Hash)
785
+ # node is either provided by user, or by default the one of workspace
786
+ node_id = shared_create_data.has_key?('node_id') ? shared_create_data['node_id'] : ws_info['node_id']
787
+ # remove from creation data if present, as it is not a standard argument
788
+ shared_create_data.delete('node_id')
789
+ # get optional link_name
790
+ opt_link_name=shared_create_data['link_name']
791
+ shared_create_data.delete('link_name')
792
+ raise 'missing node information: path' unless shared_create_data.has_key?('path')
793
+ folder_path=shared_create_data['path']
794
+ shared_create_data.delete('path')
795
+ node_file={node_info: aoc_api.read("nodes/#{node_id}")[:data], file_id: 1}
796
+ node_file = aoc_api.resolve_node_file(node_file,folder_path)
797
+ node_api = aoc_api.get_node_api(node_file[:node_info])
798
+ access_id = "#{ID_AK_ADMIN}_WS_#{ws_info['id']}"
799
+ # use can specify: tags.aspera.files.workspace.share_as to File.basename(folder_path)
800
+ default_create_data = {
801
+ 'file_id' => node_file[:file_id],
802
+ 'access_type' => 'user',
803
+ 'access_id' => access_id,
804
+ 'access_levels' => %w[list read write delete mkdir rename preview],
805
+ 'tags' => {'aspera' => {'files' => {'workspace' => {
806
+ 'id' => ws_info['id'],
807
+ 'workspace_name' => ws_info['name'],
808
+ 'user_name' => aoc_api.user_info['name'],
809
+ 'shared_by_user_id' => aoc_api.user_info['id'],
810
+ 'shared_by_name' => aoc_api.user_info['name'],
811
+ 'shared_by_email' => aoc_api.user_info['email'],
812
+ 'shared_with_name' => access_id,
813
+ 'access_key' => node_file[:node_info]['access_key'],
814
+ 'node' => node_file[:node_info]['name']}
734
815
  }}}}
735
- create_data.deep_merge!(user_create_data)
736
- Log.dump(:data,create_data)
737
- raise :ERROR
816
+ shared_create_data = default_create_data.deep_merge(default_create_data) # ?aspera-node-basic=#{node_id}&aspera-node-prefer-basic=#{node_id}
817
+ created_data = node_api.create('permissions',shared_create_data)[:data]
818
+ #created_data=aoc_api.create("node/#{node_id}/permissions",shared_create_data)[:data]
819
+ # TODO: send event
820
+ event_creation={
821
+ 'types' => ['permission.created'],
822
+ 'node_id' => node_file[:node_info]['id'],
823
+ 'workspace_id' => ws_info['id'],
824
+ 'data' => created_data # Response from previous step
825
+ }
826
+ #(optional). The name of the folder to be displayed to the destination user. Use it if its value is different from the "share_as" field.
827
+ event_creation['link_name']=opt_link_name unless opt_link_name.nil?
828
+ aoc_api.create('events',event_creation)
829
+ return { type: :single_object, data: created_data}
830
+ end
738
831
  else raise 'unknown command'
739
832
  end
740
833
  when :usage_reports
741
- return {type: :object_list,data: @api_aoc.read('usage_reports',{workspace_id: @workspace_id})[:data]}
834
+ return {type: :object_list,data: aoc_api.read('usage_reports',{workspace_id: @workspace_info['id']})[:data]}
742
835
  end
743
836
  end
744
837
 
745
838
  # must be public
746
- ACTIONS=[ :reminder, :bearer_token, :organization, :tier_restrictions, :user, :workspace, :packages, :files, :gateway, :admin, :automation, :servers].freeze
839
+ ACTIONS = %i[reminder servers bearer_token organization tier_restrictions user packages files admin automation gateway].freeze
747
840
 
748
841
  def execute_action
749
- command=self.options.get_next_command(ACTIONS)
750
- get_api unless [:reminder,:servers].include?(command)
842
+ command = options.get_next_command(ACTIONS)
751
843
  case command
752
844
  when :reminder
753
845
  # send an email reminder with list of orgs
754
- user_email=options.get_option(:username,:mandatory)
846
+ user_email = options.get_option(:username,is_type: :mandatory)
755
847
  Rest.new(base_url: "#{AoC.api_base_url}/#{AoC::API_V1}").create('organization_reminders',{email: user_email})[:data]
756
848
  return Main.result_status("List of organizations user is member of, has been sent by e-mail to #{user_email}")
849
+ when :servers
850
+ return {type: :object_list,data: Rest.new(base_url: "#{AoC.api_base_url}/#{AoC::API_V1}").read('servers')[:data]}
757
851
  when :bearer_token
758
- return {type: :text,data: @api_aoc.oauth_token}
852
+ return {type: :text,data: aoc_api.oauth_token}
759
853
  when :organization
760
- return { type: :single_object, data: @api_aoc.read('organization')[:data] }
854
+ return { type: :single_object, data: aoc_api.read('organization')[:data] }
761
855
  when :tier_restrictions
762
- return { type: :single_object, data: @api_aoc.read('tier_restrictions')[:data] }
856
+ return { type: :single_object, data: aoc_api.read('tier_restrictions')[:data] }
763
857
  when :user
764
- command=self.options.get_next_command([ :workspaces,:info,:shared_inboxes ])
765
- case command
858
+ case options.get_next_command(%i[workspaces profile])
859
+ # when :settings
860
+ # return {type: :object_list,data: aoc_api.read('client_settings/')[:data]}
766
861
  when :workspaces
767
- return {type: :object_list,data: @api_aoc.read('workspaces')[:data],fields: ['id','name']}
768
- # when :settings
769
- # return {type: :object_list,data: @api_aoc.read('client_settings/')[:data]}
770
- when :shared_inboxes
771
- query=option_url_query(nil)
772
- if query.nil?
862
+ case options.get_next_command(%i[list current])
863
+ when :list
864
+ return {type: :object_list,data: aoc_api.read('workspaces')[:data],fields: %w[id name]}
865
+ when :current
773
866
  set_workspace_info
774
- query={'embed[]'=>'dropbox','workspace_id'=>@workspace_id,'aggregate_permissions_by_dropbox'=>true,'sort'=>'dropbox_name'}
867
+ return { type: :single_object, data: @workspace_info }
775
868
  end
776
- return {type: :object_list,data: @api_aoc.read('dropbox_memberships',query)[:data],fields: ['dropbox_id','dropbox.name']}
777
- when :info
778
- command=self.options.get_next_command([ :show,:modify ])
779
- case command
869
+ when :profile
870
+ case options.get_next_command(%i[show modify])
780
871
  when :show
781
- return { type: :single_object, data: @api_aoc.user_info }
872
+ return { type: :single_object, data: aoc_api.user_info }
782
873
  when :modify
783
- @api_aoc.update("users/#{@api_aoc.user_info['id']}",self.options.get_next_argument('modified parameters (hash)'))
874
+ aoc_api.update("users/#{aoc_api.user_info['id']}",options.get_next_argument('modified parameters (hash)'))
784
875
  return Main.result_status('modified')
785
876
  end
786
877
  end
787
- when :workspace # show current workspace parameters
788
- set_workspace_info
789
- return { type: :single_object, data: @workspace_data }
790
878
  when :packages
791
879
  set_workspace_info if @url_token_data.nil?
792
- command_pkg=self.options.get_next_command([ :send, :recv, :list, :show, :delete ])
793
- case command_pkg
880
+ case options.get_next_command(%i[shared_inboxes send recv list show delete])
881
+ when :shared_inboxes
882
+ case options.get_next_command(%i[list show])
883
+ when :list
884
+ query = option_url_query(nil)
885
+ if query.nil?
886
+ query = {'embed[]' => 'dropbox','aggregate_permissions_by_dropbox' => true,'sort' => 'dropbox_name'}
887
+ query['workspace_id']=@workspace_info['id'] unless @workspace_info['id'].eql?(:undefined)
888
+ end
889
+ return {type: :object_list,data: aoc_api.read('dropbox_memberships',query)[:data],fields: ['dropbox_id','dropbox.name']}
890
+ when :show
891
+ return {type: :single_object,data: aoc_api.read(get_resource_path_from_args('dropboxes'),query)[:data]}
892
+ end
794
893
  when :send
795
- package_creation=self.options.get_option(:value,:mandatory)
796
- raise CliBadArgument,'value must be hash, refer to doc' unless package_creation.is_a?(Hash)
894
+ package_data = options.get_option(:value,is_type: :mandatory)
895
+ raise CliBadArgument,'value must be hash, refer to doc' unless package_data.is_a?(Hash)
797
896
 
798
897
  if !@url_token_data.nil?
799
- assert_public_link_types(['send_package_to_user','send_package_to_dropbox'])
800
- box_type=@url_token_data['purpose'].split('_').last
801
- package_creation['recipients']=[{'id'=>@url_token_data['data']["#{box_type}_id"],'type'=>box_type}]
802
- @workspace_id=@url_token_data['data']['workspace_id']
898
+ assert_public_link_types(%w[send_package_to_user send_package_to_dropbox])
899
+ box_type = @url_token_data['purpose'].split('_').last
900
+ package_data['recipients'] = [{'id' => @url_token_data['data']["#{box_type}_id"],'type' => box_type}]
901
+ # TODO: probably this line is not needed
902
+ @workspace_info['id'] = @url_token_data['data']['workspace_id']
803
903
  end
804
904
 
805
- package_creation['workspace_id']=@workspace_id
905
+ package_data['workspace_id'] = @workspace_info['id']
806
906
 
807
907
  # list of files to include in package, optional
808
- #package_creation['file_names']=self.transfer.ts_source_paths.map{|i|File.basename(i['source'])}
908
+ #package_data['file_names']=self.transfer.ts_source_paths.map{|i|File.basename(i['source'])}
809
909
 
810
910
  # lookup users
811
- resolve_package_recipients(package_creation,'recipients')
812
- resolve_package_recipients(package_creation,'bcc_recipients')
911
+ resolve_package_recipients(package_data,'recipients')
912
+ resolve_package_recipients(package_data,'bcc_recipients')
913
+ normalize_metadata(package_data)
914
+ validate_metadata(package_data) if options.get_option(:validate_metadata)
813
915
 
814
916
  # create a new package container
815
- package_info=@api_aoc.create('packages',package_creation)[:data]
917
+ package_info = aoc_api.create('packages',package_data)[:data]
816
918
 
817
919
  # get node information for the node on which package must be created
818
- node_info=@api_aoc.read("nodes/#{package_info['node_id']}")[:data]
920
+ node_info = aoc_api.read("nodes/#{package_info['node_id']}")[:data]
819
921
 
820
- # tell Aspera what to expect in package: 1 transfer (can also be done after transfer)
821
- @api_aoc.update("packages/#{package_info['id']}",{'sent'=>true,'transfers_expected'=>1})[:data]
922
+ # tell AoC what to expect in package: 1 transfer (can also be done after transfer)
923
+ # TODO: if multisession was used we should probably tell
924
+ # also, currently no "multi-source" , i.e. only from client-side files, unless "node" agent is used
925
+ aoc_api.update("packages/#{package_info['id']}",{'sent' => true,'transfers_expected' => 1})[:data]
822
926
 
823
- # execute transfer
927
+ # get destination: package folder
824
928
  node_file = {node_info: node_info, file_id: package_info['contents_file_id']}
825
- # raise exception if at least one error
826
- Main.result_transfer(transfer_start(AoC::PACKAGES_APP,'send',node_file,AoC.package_tags(package_info,'upload')))
929
+ # execute transfer, raise exception if at least one error
930
+ Main.result_transfer(transfer_start(AoC::PACKAGES_APP,Fasp::TransferSpec::DIRECTION_SEND,node_file,AoC.package_tags(package_info,'upload')))
827
931
  # return all info on package
828
932
  return { type: :single_object, data: package_info}
829
933
  when :recv
830
934
  if !@url_token_data.nil?
831
935
  assert_public_link_types(['view_received_package'])
832
- self.options.set_option(:id,@url_token_data['data']['package_id'])
936
+ options.set_option(:id,@url_token_data['data']['package_id'])
833
937
  end
834
938
  # scalar here
835
- ids_to_download=self.instance_identifier()
836
- skip_ids_data=[]
837
- skip_ids_persistency=nil
838
- if self.options.get_option(:once_only,:mandatory)
839
- skip_ids_persistency=PersistencyActionOnce.new(
840
- manager: @agents[:persistency],
841
- data: skip_ids_data,
842
- id: IdGenerator.from_list(['aoc_recv',self.options.get_option(:url,:mandatory),@workspace_id].push(*@persist_ids)))
939
+ ids_to_download = instance_identifier
940
+ skip_ids_data = []
941
+ skip_ids_persistency = nil
942
+ if options.get_option(:once_only,is_type: :mandatory)
943
+ skip_ids_persistency = PersistencyActionOnce.new(
944
+ manager: @agents[:persistency],
945
+ data: skip_ids_data,
946
+ id: IdGenerator.from_list(['aoc_recv',options.get_option(:url,is_type: :mandatory),@workspace_info['id']].push(*@persist_ids)))
843
947
  end
844
948
  if ids_to_download.eql?(VAL_ALL)
845
949
  # get list of packages in inbox
846
- package_info=@api_aoc.read('packages',{'archived'=>false,'exclude_dropbox_packages'=>true,'has_content'=>true,'received'=>true,'workspace_id'=>@workspace_id})[:data]
950
+ package_info = aoc_api.read('packages',{
951
+ 'archived' => false,
952
+ 'exclude_dropbox_packages' => true,
953
+ 'has_content' => true,
954
+ 'received' => true,
955
+ 'workspace_id' => @workspace_info['id']})[:data]
847
956
  # remove from list the ones already downloaded
848
- ids_to_download=package_info.map{|e|e['id']}
957
+ ids_to_download = package_info.map{|e|e['id']}
849
958
  # array here
850
- ids_to_download.select!{|id|!skip_ids_data.include?(id)}
959
+ ids_to_download.reject!{|id|skip_ids_data.include?(id)}
851
960
  end # ALL
852
961
  # list here
853
962
  ids_to_download = [ids_to_download] unless ids_to_download.is_a?(Array)
854
- result_transfer=[]
963
+ result_transfer = []
855
964
  self.format.display_status("found #{ids_to_download.length} package(s).")
856
965
  ids_to_download.each do |package_id|
857
- package_info=@api_aoc.read("packages/#{package_id}")[:data]
858
- node_info=@api_aoc.read("nodes/#{package_info['node_id']}")[:data]
966
+ package_info = aoc_api.read("packages/#{package_id}")[:data]
967
+ node_info = aoc_api.read("nodes/#{package_info['node_id']}")[:data]
859
968
  self.format.display_status("downloading package: #{package_info['name']}")
860
- add_ts={'paths'=>[{'source'=>'.'}]}
969
+ add_ts = {'paths' => [{'source' => '.'}]}
861
970
  node_file = {node_info: node_info, file_id: package_info['contents_file_id']}
862
- statuses=transfer_start(AoC::PACKAGES_APP,'receive',node_file,AoC.package_tags(package_info,'download').merge(add_ts))
863
- result_transfer.push({'package'=>package_id,Main::STATUS_FIELD=>statuses})
971
+ statuses = transfer_start(AoC::PACKAGES_APP,Fasp::TransferSpec::DIRECTION_RECEIVE,node_file,AoC.package_tags(package_info,'download').merge(add_ts))
972
+ result_transfer.push({'package' => package_id,Main::STATUS_FIELD => statuses})
864
973
  # update skip list only if all transfer sessions completed
865
974
  if TransferAgent.session_status(statuses).eql?(:success)
866
975
  skip_ids_data.push(package_id)
867
- skip_ids_persistency.save unless skip_ids_persistency.nil?
976
+ skip_ids_persistency&.save
868
977
  end
869
978
  end
870
979
  return Main.result_transfer_multiple(result_transfer)
871
980
  when :show
872
- package_id=self.options.get_next_argument('package ID')
873
- package_info=@api_aoc.read("packages/#{package_id}")[:data]
981
+ package_id = options.get_next_argument('package ID')
982
+ package_info = aoc_api.read("packages/#{package_id}")[:data]
874
983
  return { type: :single_object, data: package_info }
875
984
  when :list
876
- query=option_url_query({'archived'=>false,'exclude_dropbox_packages'=>true,'has_content'=>true,'received'=>true})
985
+ display_fields=%w[id name bytes_transferred]
986
+ query = option_url_query({'archived' => false,'exclude_dropbox_packages' => true,'has_content' => true,'received' => true})
987
+ if query.has_key?('dropbox_name')
988
+ # convenience: specify name instead of id
989
+ raise 'not both dropbox_name and dropbox_id' if query.has_key?('dropbox_id')
990
+ query['dropbox_id'] = aoc_api.lookup_entity_by_name('dropboxes',query['dropbox_name'])['id']
991
+ query.delete('dropbox_name')
992
+ end
877
993
  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']}
994
+ if @workspace_info['id'].eql?(:undefined)
995
+ display_fields.push('workspace_id')
996
+ else
997
+ query['workspace_id'] ||= @workspace_info['id']
998
+ end
999
+ packages = aoc_api.read('packages',query)[:data]
1000
+ return {type: :object_list,data: packages,fields: display_fields}
881
1001
  when :delete
882
- list_or_one=self.instance_identifier()
883
- return do_bulk_operation(list_or_one,'deleted')do|id|
884
- raise 'expecting String identifier' unless id.is_a?(String) or id.is_a?(Integer)
885
- @api_aoc.delete("packages/#{id}")[:data]
1002
+ list_or_one = instance_identifier
1003
+ return do_bulk_operation(list_or_one,'deleted') do |id|
1004
+ raise 'expecting String identifier' unless id.is_a?(String) || id.is_a?(Integer)
1005
+ aoc_api.delete("packages/#{id}")[:data]
886
1006
  end
887
1007
  end
888
1008
  when :files
889
1009
  # get workspace related information
890
1010
  set_workspace_info
891
1011
  set_home_node_file
892
- command_repo=self.options.get_next_command([NODE4_COMMANDS,:short_link].flatten)
1012
+ command_repo = options.get_next_command([NODE4_COMMANDS,:short_link].flatten)
893
1013
  case command_repo
894
- when *NODE4_COMMANDS; return execute_node_gen4_command(command_repo,@home_node_file)
1014
+ when *NODE4_COMMANDS then return execute_node_gen4_command(command_repo,@home_node_file)
895
1015
  when :short_link
896
- folder_dest=self.options.get_option(:to_folder,:optional)
897
- value_option=self.options.get_option(:value,:optional)
1016
+ folder_dest = options.get_option(:to_folder)
1017
+ value_option = options.get_option(:value)
898
1018
  case value_option
899
- when 'public'
900
- value_option={'purpose'=>'token_auth_redirection'}
901
- when 'private'
902
- value_option={'purpose'=>'shared_folder_auth_link'}
903
- when NilClass,Hash
1019
+ when 'public' then value_option = {'purpose' => 'token_auth_redirection'}
1020
+ when 'private' then value_option = {'purpose' => 'shared_folder_auth_link'}
1021
+ when NilClass,Hash then nil # keep value
904
1022
  else raise 'value must be either: public, private, Hash or nil'
905
1023
  end
906
- create_params=nil
907
- node_file=nil
1024
+ create_params = nil
1025
+ node_file = nil
908
1026
  if !folder_dest.nil?
909
- node_file = @api_aoc.resolve_node_file(@home_node_file,folder_dest)
910
- create_params={
911
- file_id: node_file[:file_id],
912
- node_id: node_file[:node_info]['id'],
913
- workspace_id: @workspace_id
1027
+ node_file = aoc_api.resolve_node_file(@home_node_file,folder_dest)
1028
+ create_params = {
1029
+ file_id: node_file[:file_id],
1030
+ node_id: node_file[:node_info]['id'],
1031
+ workspace_id: @workspace_info['id']
914
1032
  }
915
1033
  end
916
- if !value_option.nil? and !create_params.nil?
1034
+ if !value_option.nil? && !create_params.nil?
917
1035
  case value_option['purpose']
918
1036
  when 'shared_folder_auth_link'
919
- value_option['data']=create_params
920
- value_option['user_selected_name']=nil
1037
+ value_option['data'] = create_params
1038
+ value_option['user_selected_name'] = nil
921
1039
  when 'token_auth_redirection'
922
- create_params['name']=''
923
- value_option['data']={
924
- aoc: true,
1040
+ create_params['name'] = ''
1041
+ value_option['data'] = {
1042
+ aoc: true,
925
1043
  url_token_data: {
926
- data: create_params,
927
- purpose: 'view_shared_file'
1044
+ data: create_params,
1045
+ purpose: 'view_shared_file'
928
1046
  }
929
1047
  }
930
- value_option['user_selected_name']=nil
1048
+ value_option['user_selected_name'] = nil
931
1049
  else
932
1050
  raise 'purpose must be one of: token_auth_redirection or shared_folder_auth_link'
933
1051
  end
934
- self.options.set_option(:value,value_option)
1052
+ options.set_option(:value,value_option)
935
1053
  end
936
- result=self.entity_action(@api_aoc,'short_links',nil,:id,'self')
937
- if result[:data].is_a?(Hash) and result[:data].has_key?('created_at') and result[:data]['resource_type'].eql?('UrlToken')
938
- node_api=@api_aoc.get_node_api(node_file[:node_info],scope: AoC::SCOPE_NODE_USER)
1054
+ result = entity_action(@api_aoc,'short_links',id_default: 'self')
1055
+ if result[:data].is_a?(Hash) && result[:data].has_key?('created_at') && result[:data]['resource_type'].eql?('UrlToken')
1056
+ node_api = aoc_api.get_node_api(node_file[:node_info])
939
1057
  # TODO: access level as arg
940
- access_levels=Aspera::Node::ACCESS_LEVELS #['delete','list','mkdir','preview','read','rename','write']
941
- perm_data={
942
- 'file_id' =>node_file[:file_id],
943
- 'access_type' =>'user',
944
- 'access_id' =>result[:data]['resource_id'],
945
- 'access_levels'=>access_levels,
946
- 'tags' =>{
947
- 'url_token' =>true,
948
- 'workspace_id' =>@workspace_id,
949
- 'workspace_name' =>@workspace_name,
950
- 'folder_name' =>'my folder',
951
- 'created_by_name' =>@api_aoc.user_info['name'],
952
- 'created_by_email'=>@api_aoc.user_info['email'],
953
- 'access_key' =>node_file[:node_info]['access_key'],
954
- 'node' =>node_file[:node_info]['host']
1058
+ access_levels = Aspera::Node::ACCESS_LEVELS #['delete','list','mkdir','preview','read','rename','write']
1059
+ perm_data = {
1060
+ 'file_id' => node_file[:file_id],
1061
+ 'access_type' => 'user',
1062
+ 'access_id' => result[:data]['resource_id'],
1063
+ 'access_levels' => access_levels,
1064
+ 'tags' => {
1065
+ 'url_token' => true,
1066
+ 'workspace_id' => @workspace_info['id'],
1067
+ 'workspace_name' => @workspace_info['name'],
1068
+ 'folder_name' => 'my folder',
1069
+ 'created_by_name' => aoc_api.user_info['name'],
1070
+ 'created_by_email' => aoc_api.user_info['email'],
1071
+ 'access_key' => node_file[:node_info]['access_key'],
1072
+ 'node' => node_file[:node_info]['host']
955
1073
  }
956
1074
  }
957
1075
  node_api.create("permissions?file_id=#{node_file[:file_id]}",perm_data)
@@ -959,55 +1077,54 @@ module Aspera
959
1077
  end
960
1078
  return result
961
1079
  end # files command
962
- throw 'Error: shall not reach this line'
1080
+ throw('Error: shall not reach this line')
963
1081
  when :automation
964
1082
  Log.log.warn('BETA: work under progress')
965
1083
  # automation api is not in the same place
966
- automation_rest_params=@api_aoc.params.clone
1084
+ automation_rest_params = aoc_api.params.clone
967
1085
  automation_rest_params[:base_url].gsub!('/api/','/automation/')
968
- automation_api=Rest.new(automation_rest_params)
969
- command_automation=self.options.get_next_command([ :workflows, :instances ])
1086
+ automation_api = Rest.new(automation_rest_params)
1087
+ command_automation = options.get_next_command(%i[workflows instances])
970
1088
  case command_automation
971
1089
  when :instances
972
- return self.entity_action(@api_aoc,'workflow_instances',nil,:id,nil)
1090
+ return entity_action(@api_aoc,'workflow_instances')
973
1091
  when :workflows
974
- wF_COMMANDS=Plugin::ALL_OPS.clone.push(:action,:launch)
975
- wf_command=self.options.get_next_command(wF_COMMANDS)
1092
+ wf_command = options.get_next_command([Plugin::ALL_OPS,:action,:launch].flatten)
976
1093
  case wf_command
977
1094
  when *Plugin::ALL_OPS
978
- return self.entity_command(wf_command,automation_api,'workflows',nil,:id)
1095
+ return entity_command(wf_command,automation_api,'workflows',id_default: :id)
979
1096
  when :launch
980
- wf_id=self.instance_identifier()
981
- data=automation_api.create("workflows/#{wf_id}/launch",{})[:data]
1097
+ wf_id = instance_identifier
1098
+ data = automation_api.create("workflows/#{wf_id}/launch",{})[:data]
982
1099
  return {type: :single_object,data: data}
983
1100
  when :action
984
- wf_command=self.options.get_next_command([:list,:create,:show])
985
- wf_id=self.instance_identifier()
986
- step=automation_api.create('steps',{'workflow_id'=>wf_id})[:data]
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']]})
990
- wf=automation_api.read("workflows/#{wf_id}")[:data]
1101
+ #TODO: not complete
1102
+ wf_id = instance_identifier
1103
+ wf_action_cmd = options.get_next_command(%i[list create show])
1104
+ Log.log.warn("Not implemented: #{wf_action_cmd}")
1105
+ step = automation_api.create('steps',{'workflow_id' => wf_id})[:data]
1106
+ automation_api.update("workflows/#{wf_id}",{'step_order' => [step['id']]})
1107
+ action = automation_api.create('actions',{'step_id' => step['id'],'type' => 'manual'})[:data]
1108
+ automation_api.update("steps/#{step['id']}",{'action_order' => [action['id']]})
1109
+ wf = automation_api.read("workflows/#{wf_id}")[:data]
991
1110
  return {type: :single_object,data: wf}
992
1111
  end
993
1112
  end
1113
+ when :admin
1114
+ return execute_admin_action
994
1115
  when :gateway
995
1116
  set_workspace_info
996
1117
  require 'aspera/faspex_gw'
997
- FaspexGW.new(@api_aoc,@workspace_id).start_server
998
- when :admin
999
- return execute_admin_action
1000
- when :servers
1001
- return {type: :object_list,data: Rest.new(base_url: "#{AoC.api_base_url}/#{AoC::API_V1}").read('servers')[:data]}
1118
+ FaspexGW.new(@api_aoc,@workspace_info['id']).start_server
1002
1119
  else
1003
1120
  raise "internal error: #{command}"
1004
1121
  end # action
1005
- raise RuntimeError, 'internal error: command shall return'
1122
+ raise 'internal error: command shall return'
1006
1123
  end
1007
1124
 
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
1125
+ private :aoc_params,:set_workspace_info,:set_home_node_file,:do_bulk_operation,:resolve_package_recipients,:option_url_query,:assert_public_link_types,
1126
+ :execute_admin_action
1009
1127
  private_constant :VAL_ALL,:NODE4_COMMANDS, :ID_AK_ADMIN
1010
-
1011
1128
  end # AoC
1012
1129
  end # Plugins
1013
1130
  end # Cli