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