aspera-cli 4.13.0 → 4.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +81 -7
  4. data/CONTRIBUTING.md +22 -6
  5. data/README.md +2038 -1080
  6. data/bin/ascli +18 -9
  7. data/bin/asession +12 -14
  8. data/examples/dascli +1 -1
  9. data/examples/proxy.pac +1 -1
  10. data/examples/rubyc +24 -0
  11. data/lib/aspera/aoc.rb +219 -159
  12. data/lib/aspera/ascmd.rb +25 -14
  13. data/lib/aspera/cli/basic_auth_plugin.rb +12 -9
  14. data/lib/aspera/cli/error.rb +17 -0
  15. data/lib/aspera/cli/extended_value.rb +47 -12
  16. data/lib/aspera/cli/formatter.rb +260 -179
  17. data/lib/aspera/cli/hints.rb +80 -0
  18. data/lib/aspera/cli/main.rb +104 -156
  19. data/lib/aspera/cli/manager.rb +259 -209
  20. data/lib/aspera/cli/plugin.rb +123 -63
  21. data/lib/aspera/cli/plugins/alee.rb +2 -3
  22. data/lib/aspera/cli/plugins/aoc.rb +341 -261
  23. data/lib/aspera/cli/plugins/ats.rb +22 -21
  24. data/lib/aspera/cli/plugins/bss.rb +5 -5
  25. data/lib/aspera/cli/plugins/config.rb +578 -627
  26. data/lib/aspera/cli/plugins/console.rb +44 -6
  27. data/lib/aspera/cli/plugins/cos.rb +15 -17
  28. data/lib/aspera/cli/plugins/faspex.rb +114 -100
  29. data/lib/aspera/cli/plugins/faspex5.rb +411 -264
  30. data/lib/aspera/cli/plugins/node.rb +354 -259
  31. data/lib/aspera/cli/plugins/orchestrator.rb +61 -29
  32. data/lib/aspera/cli/plugins/preview.rb +82 -90
  33. data/lib/aspera/cli/plugins/server.rb +79 -32
  34. data/lib/aspera/cli/plugins/shares.rb +55 -42
  35. data/lib/aspera/cli/sync_actions.rb +68 -0
  36. data/lib/aspera/cli/transfer_agent.rb +66 -73
  37. data/lib/aspera/cli/transfer_progress.rb +74 -0
  38. data/lib/aspera/cli/version.rb +1 -1
  39. data/lib/aspera/colors.rb +12 -8
  40. data/lib/aspera/command_line_builder.rb +14 -11
  41. data/lib/aspera/cos_node.rb +3 -2
  42. data/lib/aspera/data/6 +0 -0
  43. data/lib/aspera/environment.rb +24 -9
  44. data/lib/aspera/fasp/agent_aspera.rb +126 -0
  45. data/lib/aspera/fasp/agent_base.rb +31 -77
  46. data/lib/aspera/fasp/agent_connect.rb +25 -21
  47. data/lib/aspera/fasp/agent_direct.rb +89 -103
  48. data/lib/aspera/fasp/agent_httpgw.rb +231 -149
  49. data/lib/aspera/fasp/agent_node.rb +41 -34
  50. data/lib/aspera/fasp/agent_trsdk.rb +75 -32
  51. data/lib/aspera/fasp/error_info.rb +4 -2
  52. data/lib/aspera/fasp/faux_file.rb +52 -0
  53. data/lib/aspera/fasp/installation.rb +53 -195
  54. data/lib/aspera/fasp/management.rb +244 -0
  55. data/lib/aspera/fasp/parameters.rb +71 -37
  56. data/lib/aspera/fasp/parameters.yaml +76 -8
  57. data/lib/aspera/fasp/products.rb +162 -0
  58. data/lib/aspera/fasp/resume_policy.rb +3 -3
  59. data/lib/aspera/fasp/transfer_spec.rb +7 -6
  60. data/lib/aspera/fasp/uri.rb +26 -24
  61. data/lib/aspera/faspex_gw.rb +2 -2
  62. data/lib/aspera/faspex_postproc.rb +2 -2
  63. data/lib/aspera/hash_ext.rb +14 -4
  64. data/lib/aspera/json_rpc.rb +49 -0
  65. data/lib/aspera/keychain/macos_security.rb +13 -13
  66. data/lib/aspera/line_logger.rb +23 -0
  67. data/lib/aspera/log.rb +58 -16
  68. data/lib/aspera/node.rb +157 -92
  69. data/lib/aspera/oauth.rb +37 -19
  70. data/lib/aspera/open_application.rb +4 -4
  71. data/lib/aspera/persistency_action_once.rb +1 -1
  72. data/lib/aspera/persistency_folder.rb +2 -2
  73. data/lib/aspera/preview/file_types.rb +4 -2
  74. data/lib/aspera/preview/generator.rb +22 -35
  75. data/lib/aspera/preview/options.rb +2 -0
  76. data/lib/aspera/preview/terminal.rb +73 -16
  77. data/lib/aspera/preview/utils.rb +21 -28
  78. data/lib/aspera/proxy_auto_config.js +2 -2
  79. data/lib/aspera/rest.rb +136 -68
  80. data/lib/aspera/rest_call_error.rb +1 -1
  81. data/lib/aspera/rest_error_analyzer.rb +15 -14
  82. data/lib/aspera/rest_errors_aspera.rb +37 -34
  83. data/lib/aspera/secret_hider.rb +18 -15
  84. data/lib/aspera/ssh.rb +5 -2
  85. data/lib/aspera/sync.rb +127 -119
  86. data/lib/aspera/temp_file_manager.rb +10 -3
  87. data/lib/aspera/web_auth.rb +10 -7
  88. data/lib/aspera/web_server_simple.rb +9 -4
  89. data.tar.gz.sig +0 -0
  90. metadata +34 -17
  91. metadata.gz.sig +0 -0
  92. data/docs/test_env.conf +0 -186
  93. data/lib/aspera/cli/listener/line_dump.rb +0 -19
  94. data/lib/aspera/cli/listener/logger.rb +0 -22
  95. data/lib/aspera/cli/listener/progress.rb +0 -50
  96. data/lib/aspera/cli/listener/progress_multi.rb +0 -84
  97. data/lib/aspera/cli/plugins/sync.rb +0 -44
  98. data/lib/aspera/data/7 +0 -0
  99. data/lib/aspera/fasp/listener.rb +0 -13
@@ -17,15 +17,130 @@ module Aspera
17
17
  module Cli
18
18
  module Plugins
19
19
  class Aoc < Aspera::Cli::BasicAuthPlugin
20
+ AOC_PATH_API_CLIENTS = 'admin/api-clients'
21
+ # default redirect for AoC web auth
22
+ DEFAULT_REDIRECT = 'http://localhost:12345'
23
+ private_constant :AOC_PATH_API_CLIENTS, :DEFAULT_REDIRECT
20
24
  class << self
25
+ def application_name
26
+ 'Aspera on Cloud'
27
+ end
28
+
21
29
  def detect(base_url)
22
- api = Rest.new({base_url: base_url})
30
+ # no protocol ?
31
+ base_url = "https://#{base_url}" unless base_url.match?(%r{^[a-z]{1,6}://})
32
+ # only org provided ?
33
+ base_url = "#{base_url}.#{Aspera::AoC::PROD_DOMAIN}" unless base_url.include?('.')
34
+ # AoC is only https
35
+ return nil unless base_url.start_with?('https://')
36
+ result = Rest.new({base_url: base_url, redirect_max: 10}).read('')
37
+ # Any AoC is on this domain
38
+ return nil unless result[:http].uri.host.end_with?(Aspera::AoC::PROD_DOMAIN)
39
+ Log.log.debug{'AoC Main page: #{result[:http].body.include?(Aspera::AoC::PRODUCT_NAME)}'}
40
+ base_url = result[:http].uri.to_s if result[:http].uri.path.include?('/public')
23
41
  # 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' }
42
+ return {
43
+ version: 'SaaS',
44
+ url: base_url
45
+ }
46
+ end
47
+
48
+ def private_key_required?(url)
49
+ # pub link do not need private key
50
+ return AoC.link_info(url)[:token].nil?
51
+ end
52
+
53
+ # @param [Hash] env : options, formatter
54
+ # @param [Hash] params : plugin_sym, instance_url
55
+ # @return [Hash] :preset_value, :test_args
56
+ def wizard(object:, private_key_path: nil, pub_key_pem: nil)
57
+ # set vars to look like object
58
+ options = object.options
59
+ formatter = object.formatter
60
+ options.declare(:use_generic_client, 'Wizard: AoC: use global or org specific jwt client id', values: :bool, default: true)
61
+ options.parse_options!
62
+ instance_url = options.get_option(:url, mandatory: true)
63
+ pub_link_info = AoC.link_info(instance_url)
64
+ if !pub_link_info[:token].nil?
65
+ pub_api = Rest.new({base_url: "https://#{URI.parse(pub_link_info[:url]).host}/api/v1"})
66
+ pub_info = pub_api.read('env/url_token_check', {token: pub_link_info[:token]})[:data]
67
+ preset_value = {
68
+ link: instance_url
69
+ }
70
+ preset_value[:password] = options.get_option(:password, mandatory: true) if pub_info['password_protected']
71
+ return {
72
+ preset_value: preset_value,
73
+ test_args: 'organization'
74
+ }
75
+ end
76
+ # make username mandatory for jwt, this triggers interactive input
77
+ wiz_username = options.get_option(:username, mandatory: true)
78
+ raise "Username shall be an email in AoC: #{wiz_username}" if !(wiz_username =~ /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i)
79
+ # Set the pub key and jwt tag in the user's profile automatically
80
+ auto_set_pub_key = false
81
+ auto_set_jwt = false
82
+ # use browser authentication to bootstrap
83
+ use_browser_authentication = false
84
+ if options.get_option(:use_generic_client)
85
+ formatter.display_status('Using global client_id.')
86
+ formatter.display_status('Please Login to your Aspera on Cloud instance.')
87
+ formatter.display_status('Navigate to: 👤 → Account Settings → Profile → Public Key')
88
+ formatter.display_status('Check or update the value to:'.red.blink)
89
+ formatter.display_status(pub_key_pem)
90
+ if !options.get_option(:test_mode)
91
+ formatter.display_status('Once updated or validated, press enter.')
92
+ OpenApplication.instance.uri(instance_url)
93
+ $stdin.gets
94
+ end
95
+ else
96
+ formatter.display_status('Using organization specific client_id.')
97
+ if options.get_option(:client_id).nil? || options.get_option(:client_secret).nil?
98
+ formatter.display_status('Please login to your Aspera on Cloud instance.'.red)
99
+ formatter.display_status('Navigate to: 𓃑 → Admin → Integrations → API Clients')
100
+ formatter.display_status('Check or create in integration:')
101
+ formatter.display_status("- name: #{@info[:name]}")
102
+ formatter.display_status("- redirect uri: #{DEFAULT_REDIRECT}")
103
+ formatter.display_status('- origin: localhost')
104
+ formatter.display_status('Use the generated client id and secret in the following prompts.'.red)
105
+ end
106
+ OpenApplication.instance.uri("#{instance_url}/#{AOC_PATH_API_CLIENTS}")
107
+ options.get_option(:client_id, mandatory: true)
108
+ options.get_option(:client_secret, mandatory: true)
109
+ use_browser_authentication = true
110
+ end
111
+ if use_browser_authentication
112
+ formatter.display_status('We will use web authentication to bootstrap.')
113
+ auto_set_pub_key = true
114
+ auto_set_jwt = true
115
+ aoc_api.oauth.generic_parameters[:grant_method] = :web
116
+ aoc_api.oauth.generic_parameters[:scope] = AoC::SCOPE_FILES_ADMIN
117
+ aoc_api.oauth.specific_parameters[:redirect_uri] = DEFAULT_REDIRECT
118
+ end
119
+ myself = object.aoc_api.read('self')[:data]
120
+ if auto_set_pub_key
121
+ raise Cli::Error, 'Public key is already set in profile (use --override=yes)' unless myself['public_key'].empty? || option_override
122
+ formatter.display_status('Updating profile with the public key.')
123
+ aoc_api.update("users/#{myself['id']}", {'public_key' => pub_key_pem})
124
+ end
125
+ if auto_set_jwt
126
+ formatter.display_status('Enabling JWT for client')
127
+ aoc_api.update("clients/#{options.get_option(:client_id)}", {'jwt_grant_enabled' => true, 'explicit_authorization_required' => false})
128
+ end
129
+ preset_result = {
130
+ url: instance_url,
131
+ username: myself['email'],
132
+ auth: :jwt.to_s,
133
+ private_key: "@file:#{private_key_path}"
134
+ }
135
+ # set only if non nil
136
+ %i[client_id client_secret].each do |s|
137
+ o = options.get_option(s)
138
+ preset_result[s.to_s] = o unless o.nil?
27
139
  end
28
- return nil
140
+ return {
141
+ preset_value: preset_result,
142
+ test_args: 'user profile show'
143
+ }
29
144
  end
30
145
  end
31
146
  # special value for package id
@@ -50,7 +165,6 @@ module Aspera
50
165
  client_registration_token
51
166
  client_access_key
52
167
  kms_profile].freeze
53
- ENTITY_NAME_SPECIFIER = 'name'
54
168
  PACKAGE_QUERY_DEFAULT = {'archived' => false, 'exclude_dropbox_packages' => true, 'has_content' => true, 'received' => true}.freeze
55
169
 
56
170
  def initialize(env)
@@ -58,130 +172,52 @@ module Aspera
58
172
  @cache_workspace_info = nil
59
173
  @cache_home_node_file = nil
60
174
  @cache_api_aoc = nil
61
- options.add_opt_list(:auth, Oauth::STD_AUTH_TYPES, 'OAuth type of authentication')
62
- options.add_opt_list(:operation, %i[push pull], 'client operation for transfers')
63
- options.add_opt_simple(:client_id, 'OAuth API client identifier')
64
- options.add_opt_simple(:client_secret, 'OAuth API client secret')
65
- options.add_opt_simple(:redirect_uri, 'OAuth API client redirect URI')
66
- options.add_opt_simple(:private_key, 'OAuth JWT RSA private key PEM value (prefix file path with @file:)')
67
- options.add_opt_simple(:scope, 'OAuth scope for AoC API calls')
68
- options.add_opt_simple(:passphrase, 'RSA private key passphrase')
69
- options.add_opt_simple(:workspace, 'Name of workspace')
70
- options.add_opt_simple(:name, "Resource name (prefer to use keyword #{ENTITY_NAME_SPECIFIER})")
71
- options.add_opt_simple(:link, 'Public link to shared resource')
72
- options.add_opt_simple(:new_user_option, 'New user creation option for unknown package recipients')
73
- options.add_opt_simple(:from_folder, 'Source folder for Folder-to-Folder transfer')
74
- options.add_opt_boolean(:validate_metadata, 'Validate shared inbox metadata')
75
- options.set_option(:validate_metadata, :yes)
76
- options.set_option(:operation, :push)
77
- options.set_option(:auth, :jwt)
78
- options.set_option(:scope, AoC::SCOPE_FILES_USER)
79
- options.set_option(:private_key, '@file:' + env[:private_key_path]) if env[:private_key_path].is_a?(String)
80
- options.set_option(:workspace, :default)
175
+ options.declare(:auth, 'OAuth type of authentication', values: Oauth::STD_AUTH_TYPES, default: :jwt)
176
+ options.declare(:client_id, 'OAuth API client identifier')
177
+ options.declare(:client_secret, 'OAuth API client secret')
178
+ options.declare(:scope, 'OAuth scope for AoC API calls', default: AoC::SCOPE_FILES_USER)
179
+ options.declare(:redirect_uri, 'OAuth API client redirect URI')
180
+ options.declare(:private_key, 'OAuth JWT RSA private key PEM value (prefix file path with @file:)')
181
+ options.declare(:passphrase, 'RSA private key passphrase')
182
+ options.declare(:workspace, 'Name of workspace', types: [String, NilClass], default: Aspera::AoC::DEFAULT_WORKSPACE)
183
+ options.declare(:new_user_option, 'New user creation option for unknown package recipients')
184
+ options.declare(:validate_metadata, 'Validate shared inbox metadata', values: :bool, default: true)
81
185
  options.parse_options!
82
- # add node plugin options
83
- Node.new(env.merge({man_only: true, skip_basic_auth_options: true}))
84
- end
85
-
86
- # build list of options for AoC API, based on options of CLI
87
- def aoc_params(subpath)
88
- # copy command line options to args
89
- return Aspera::AoC::OPTIONS_NEW.each_with_object({subpath: subpath}){|i, m|m[i] = options.get_option(i)}
186
+ # add node plugin options (for manual)
187
+ Node.declare_options(options)
90
188
  end
91
189
 
92
- def aoc_api
93
- if @cache_api_aoc.nil?
94
- @cache_api_aoc = AoC.new(aoc_params(AoC::API_V1))
95
- # add keychain for access key secrets
96
- @cache_api_aoc.secret_finder = @agents[:config]
97
- end
98
- return @cache_api_aoc
99
- end
190
+ OPTIONS_NEW = %i[url auth client_id client_secret scope redirect_uri private_key passphrase username password workspace].freeze
100
191
 
101
- # @return [Hash] current workspace information,
102
- def current_workspace_info
103
- return @cache_workspace_info unless @cache_workspace_info.nil?
104
- default_workspace_id = if aoc_api.url_token_data.nil?
105
- aoc_api.current_user_info['default_workspace_id']
106
- else
107
- aoc_api.url_token_data['data']['workspace_id']
192
+ def api_from_options(new_base_path)
193
+ create_values = {subpath: new_base_path, secret_finder: @agents[:config]}
194
+ # create an API object with the same options, but with a different subpath
195
+ return Aspera::AoC.new(**OPTIONS_NEW.each_with_object(create_values) { |i, m|m[i] = options.get_option(i) unless options.get_option(i).nil?})
196
+ rescue ArgumentError => e
197
+ if (m = e.message.match(/missing keyword: :(.*)$/))
198
+ raise Cli::Error, "Missing option: #{m[1]}"
108
199
  end
109
-
110
- ws_name = options.get_option(:workspace)
111
- ws_id =
112
- case ws_name
113
- when :default
114
- Log.log.debug('Using default workspace'.green)
115
- raise CliError, 'No default workspace defined for user, please specify workspace' if default_workspace_id.nil?
116
- default_workspace_id
117
- when String then aoc_api.lookup_entity_by_name('workspaces', ws_name)['id']
118
- when NilClass then nil
119
- else raise CliError, 'unexpected value type for workspace'
120
- end
121
- @cache_workspace_info =
122
- begin
123
- aoc_api.read("workspaces/#{ws_id}")[:data]
124
- rescue Aspera::RestCallError => e
125
- Log.log.debug(e.message)
126
- { 'id' => :undefined, 'name' => :undefined }
127
- end
128
- Log.dump(:current_workspace_info, @cache_workspace_info)
129
- # display workspace
130
- default_flag = @cache_workspace_info['id'] == default_workspace_id ? ' (default)' : ''
131
- formatter.display_status("Current Workspace: #{@cache_workspace_info['name'].to_s.red}#{default_flag}")
132
- return @cache_workspace_info
200
+ raise
133
201
  end
134
202
 
135
- # @return [Hash] with :node_id and :file_id
136
- def home_info
137
- return @cache_home_node_file unless @cache_home_node_file.nil?
138
- if !aoc_api.url_token_data.nil?
139
- assert_public_link_types(['view_shared_file'])
140
- home_node_id = aoc_api.url_token_data['data']['node_id']
141
- home_file_id = aoc_api.url_token_data['data']['file_id']
142
- end
143
- home_node_id ||= current_workspace_info['home_node_id'] || current_workspace_info['node_id']
144
- home_file_id ||= current_workspace_info['home_file_id']
145
- if home_node_id.to_s.empty?
146
- # not part of any workspace, but has some folder shared
147
- user_info = aoc_api.current_user_info(exception: true)
148
- home_node_id = user_info['read_only_home_node_id']
149
- home_file_id = user_info['read_only_home_file_id']
150
- end
151
-
152
- raise "Cannot get user's home node id, check your default workspace or specify one" if home_node_id.to_s.empty?
153
- @cache_home_node_file = {
154
- node_id: home_node_id,
155
- file_id: home_file_id
156
- }
157
- return @cache_home_node_file
203
+ def aoc_api
204
+ @cache_api_aoc = api_from_options(AoC::API_V1) if @cache_api_aoc.nil?
205
+ return @cache_api_aoc
158
206
  end
159
207
 
160
208
  # get identifier or name from command line
161
209
  # @return identifier
162
210
  def get_resource_id_from_args(resource_class_path)
163
- l_res_id = options.get_option(:id)
164
- l_res_name = options.get_option(:name)
165
- raise 'Provide either option id or name, not both' unless l_res_id.nil? || l_res_name.nil?
166
- # try to find item by name (single partial match or exact match)
167
- l_res_id = aoc_api.lookup_entity_by_name(resource_class_path, l_res_name)['id'] unless l_res_name.nil?
168
- # if no name or id option, taken on command line (after command)
169
- if l_res_id.nil?
170
- l_res_id = options.get_next_argument('identifier')
171
- l_res_id = aoc_api.lookup_entity_by_name(resource_class_path, options.get_next_argument('identifier'))['id'] if l_res_id.eql?(ENTITY_NAME_SPECIFIER)
211
+ return instance_identifier do |field, value|
212
+ raise Cli::BadArgument, 'only selection by name is supported' unless field.eql?('name')
213
+ aoc_api.lookup_by_name(resource_class_path, value)['id']
172
214
  end
173
- return l_res_id
174
215
  end
175
216
 
176
217
  def get_resource_path_from_args(resource_class_path)
177
218
  return "#{resource_class_path}/#{get_resource_id_from_args(resource_class_path)}"
178
219
  end
179
220
 
180
- def assert_public_link_types(expected)
181
- raise CliBadArgument, "public link type is #{aoc_api.url_token_data['purpose']} but action requires one of #{expected.join(',')}" \
182
- unless expected.include?(aoc_api.url_token_data['purpose'])
183
- end
184
-
185
221
  # Call aoc_api.read with same parameters.
186
222
  # Use paging if necessary to get all results
187
223
  # @return [Hash] {list: , total: }
@@ -189,10 +225,8 @@ module Aspera
189
225
  raise 'Query must be Hash' unless base_query.is_a?(Hash)
190
226
  # set default large page if user does not specify own parameters. AoC Caps to 1000 anyway
191
227
  base_query['per_page'] = 1000 unless base_query.key?('per_page')
192
- max_items = base_query[MAX_ITEMS]
193
- base_query.delete(MAX_ITEMS)
194
- max_pages = base_query[MAX_PAGES]
195
- base_query.delete(MAX_PAGES)
228
+ max_items = base_query.delete(MAX_ITEMS)
229
+ max_pages = base_query.delete(MAX_PAGES)
196
230
  item_list = []
197
231
  total_count = nil
198
232
  current_page = base_query['page']
@@ -209,9 +243,10 @@ module Aspera
209
243
  break if add_items.empty?
210
244
  # append new items to full list
211
245
  item_list += add_items
212
- break if !max_pages.nil? && page_count > max_pages
213
- break if !max_items.nil? && item_list.count > max_items
246
+ break if !max_items.nil? && item_list.count >= max_items
247
+ break if !max_pages.nil? && page_count >= max_pages
214
248
  end
249
+ item_list = item_list[0..max_items - 1] if !max_items.nil? && item_list.count > max_items
215
250
  return {list: item_list, total: total_count}
216
251
  end
217
252
 
@@ -222,33 +257,32 @@ module Aspera
222
257
  # @param scope [String] node scope, or nil (admin)
223
258
  def execute_nodegen4_command(command_repo, node_id, file_id: nil, scope: nil)
224
259
  top_node_api = aoc_api.node_api_from(
225
- node_id: node_id,
226
- workspace_id: current_workspace_info['id'],
227
- workspace_name: current_workspace_info['name'],
228
- scope: scope
260
+ node_id: node_id,
261
+ workspace_id: aoc_api.context[:workspace_id],
262
+ workspace_name: aoc_api.context[:workspace_name],
263
+ scope: scope
229
264
  )
230
265
  file_id = top_node_api.read("access_keys/#{top_node_api.app_info[:node_info]['access_key']}")[:data]['root_file_id'] if file_id.nil?
231
- node_plugin = Node.new(@agents.merge(
232
- skip_basic_auth_options: true,
233
- skip_node_options: true,
234
- node_api: top_node_api))
266
+ node_plugin = Node.new(@agents, api: top_node_api)
235
267
  case command_repo
236
268
  when *Node::COMMANDS_GEN4
237
269
  return node_plugin.execute_command_gen4(command_repo, file_id)
238
270
  when :transfer
239
271
  # client side is agent
240
- # server side is protocol server
272
+ # server side is transfer server
241
273
  # in same workspace
242
- # default is push
243
- case options.get_option(:operation, is_type: :mandatory)
274
+ push_pull = options.get_next_argument('direction', expected: %i[push pull])
275
+ source_folder = options.get_next_argument('folder of source files', type: String)
276
+ case push_pull
244
277
  when :push
245
278
  client_direction = Fasp::TransferSpec::DIRECTION_SEND
246
- client_folder = options.get_option(:from_folder, is_type: :mandatory)
279
+ client_folder = source_folder
247
280
  server_folder = transfer.destination_folder(client_direction)
248
281
  when :pull
249
282
  client_direction = Fasp::TransferSpec::DIRECTION_RECEIVE
250
283
  client_folder = transfer.destination_folder(client_direction)
251
- server_folder = options.get_option(:from_folder, is_type: :mandatory)
284
+ server_folder = source_folder
285
+ else raise 'internal error'
252
286
  end
253
287
  client_apfid = top_node_api.resolve_api_fid(file_id, client_folder)
254
288
  server_apfid = top_node_api.resolve_api_fid(file_id, server_folder)
@@ -290,7 +324,8 @@ module Aspera
290
324
  end
291
325
  when :subscription
292
326
  org = aoc_api.read('organization')[:data]
293
- bss_api = AoC.new(aoc_params('bss/platform'))
327
+ bss_api = api_from_options('bss/platform')
328
+ # cspell:disable
294
329
  graphql_query = "
295
330
  query ($organization_id: ID!) {
296
331
  aoc (organization_id: $organization_id) {
@@ -339,17 +374,18 @@ module Aspera
339
374
  }
340
375
  }
341
376
  "
377
+ # cspell:enable
342
378
  result = bss_api.create('graphql', {'variables' => {'organization_id' => org['id']}, 'query' => graphql_query})[:data]['data']
343
379
  return {type: :single_object, data: result['aoc']['bssSubscription']}
344
380
  when :ats
345
381
  ats_api = Rest.new(aoc_api.params.deep_merge({
346
- base_url: aoc_api.params[:base_url] + '/admin/ats/pub/v1',
382
+ base_url: "#{aoc_api.params[:base_url]}/admin/ats/pub/v1",
347
383
  auth: {scope: AoC::SCOPE_FILES_ADMIN_USER}
348
384
  }))
349
- return Ats.new(@agents.merge(skip_node_options: true)).execute_action_gen(ats_api)
385
+ return Ats.new(@agents).execute_action_gen(ats_api)
350
386
  when :analytics
351
387
  analytics_api = Rest.new(aoc_api.params.deep_merge({
352
- base_url: aoc_api.params[:base_url].gsub('/api/v1', '') + '/analytics/v2',
388
+ base_url: "#{aoc_api.params[:base_url].gsub('/api/v1', '')}/analytics/v2",
353
389
  auth: {scope: AoC::SCOPE_FILES_ADMIN_USER}
354
390
  }))
355
391
  command_analytics = options.get_next_command(%i[application_events transfers])
@@ -360,24 +396,23 @@ module Aspera
360
396
  return {type: :object_list, data: events}
361
397
  when :transfers
362
398
  event_type = command_analytics.to_s
363
- filter_resource = options.get_option(:name) || 'organizations'
364
- filter_id = options.get_option(:id) ||
399
+ filter_resource = options.get_next_argument('resource', expected: %i[organizations users nodes])
400
+ filter_id = options.get_next_argument('identifier', mandatory: false) ||
365
401
  case filter_resource
366
- when 'organizations' then aoc_api.current_user_info['organization_id']
367
- when 'users' then aoc_api.current_user_info['id']
368
- when 'nodes' then aoc_api.current_user_info['id'] # TODO: consistent ? # rubocop:disable Lint/DuplicateBranch
369
- else raise 'organizations or users for option --name'
402
+ when :organizations then aoc_api.current_user_info['organization_id']
403
+ when :users then aoc_api.current_user_info['id']
404
+ when :nodes then aoc_api.current_user_info['id'] # TODO: consistent ? # rubocop:disable Lint/DuplicateBranch
405
+ else raise 'Internal error'
370
406
  end
371
407
  filter = options.get_option(:query) || {}
372
- raise 'query must be Hash' unless filter.is_a?(Hash)
373
408
  filter['limit'] ||= 100
374
- if options.get_option(:once_only, is_type: :mandatory)
409
+ if options.get_option(:once_only, mandatory: true)
375
410
  saved_date = []
376
411
  start_date_persistency = PersistencyActionOnce.new(
377
412
  manager: @agents[:persistency],
378
413
  data: saved_date,
379
- ids: IdGenerator.from_list(['aoc_ana_date', options.get_option(:url, is_type: :mandatory), current_workspace_info['name']].push(
380
- filter_resource,
414
+ ids: IdGenerator.from_list(['aoc_ana_date', options.get_option(:url, mandatory: true), aoc_api.context[:workspace_name]].push(
415
+ filter_resource.to_s,
381
416
  filter_id)))
382
417
  start_date_time = saved_date.first
383
418
  stop_date_time = Time.now.utc.strftime('%FT%T.%LZ')
@@ -387,9 +422,9 @@ module Aspera
387
422
  filter['start_time'] = start_date_time unless start_date_time.nil?
388
423
  filter['stop_time'] = stop_date_time
389
424
  end
390
- events = analytics_api.read("#{filter_resource}/#{filter_id}/#{event_type}", option_url_query(filter))[:data][event_type]
425
+ events = analytics_api.read("#{filter_resource}/#{filter_id}/#{event_type}", query_read_delete(default: filter))[:data][event_type]
391
426
  start_date_persistency&.save
392
- if !options.get_option(:notif_to).nil?
427
+ if !options.get_option(:notify_to).nil?
393
428
  events.each do |tr_event|
394
429
  config.send_email_template(values: {ev: tr_event})
395
430
  end
@@ -405,7 +440,7 @@ module Aspera
405
440
  when :self, :organization then resource_type
406
441
  when :client_registration_token, :client_access_key then "admin/#{resource_type}s"
407
442
  when :application then 'admin/apps_new'
408
- when :dropbox then resource_type.to_s + 'es'
443
+ when :dropbox then "#{resource_type}es"
409
444
  when :kms_profile then "integrations/#{resource_type}s"
410
445
  else "#{resource_type}s"
411
446
  end
@@ -429,9 +464,7 @@ module Aspera
429
464
  id_result = 'token' if resource_class_path.eql?('admin/client_registration_tokens')
430
465
  # TODO: report inconsistency: creation url is !=, and does not return id.
431
466
  resource_class_path = 'admin/client_registration/token' if resource_class_path.eql?('admin/client_registration_tokens')
432
- list_or_one = options.get_next_argument('creation data', type: Hash)
433
- return do_bulk_operation(list_or_one, 'created', id_result: id_result) do |params|
434
- raise 'expecting Hash' unless params.is_a?(Hash)
467
+ return do_bulk_operation(command: command, descr: 'creation data', id_result: id_result) do |params|
435
468
  aoc_api.create(resource_class_path, params)[:data]
436
469
  end
437
470
  when :list
@@ -451,35 +484,39 @@ module Aspera
451
484
  when :group_membership then default_fields.push(*%w[group_id member_type member_id])
452
485
  when :workspace_membership then default_fields.push(*%w[workspace_id member_type member_id])
453
486
  end
454
- items = read_with_paging(resource_class_path, option_url_query(default_query))
487
+ items = read_with_paging(resource_class_path, query_read_delete(default: default_query))
455
488
  formatter.display_item_count(items[:list].length, items[:total])
456
489
  return {type: :object_list, data: items[:list], fields: default_fields}
457
490
  when :show
458
491
  object = aoc_api.read(resource_instance_path)[:data]
492
+ # default: show all, but certificate
459
493
  fields = object.keys.reject{|k|k.eql?('certificate')}
460
494
  return { type: :single_object, data: object, fields: fields }
461
495
  when :modify
462
- changes = options.get_next_argument('modified parameters (hash)')
463
- aoc_api.update(resource_instance_path, changes)
464
- return Main.result_status('modified')
496
+ changes = options.get_next_argument('properties', type: Hash)
497
+ return do_bulk_operation(command: command, descr: 'identifier', values: res_id) do |one_id|
498
+ aoc_api.update("#{resource_class_path}/#{one_id}", changes)
499
+ {'id' => one_id}
500
+ end
465
501
  when :delete
466
- return do_bulk_operation(res_id, 'deleted') do |one_id|
502
+ return do_bulk_operation(command: command, descr: 'identifier', values: res_id) do |one_id|
467
503
  aoc_api.delete("#{resource_class_path}/#{one_id}")
468
504
  {'id' => one_id}
469
505
  end
470
506
  when :set_pub_key
471
507
  # special : reads private and generate public
472
- the_private_key = options.get_next_argument('private_key')
508
+ the_private_key = options.get_next_argument('private_key PEM value', type: String)
473
509
  the_public_key = OpenSSL::PKey::RSA.new(the_private_key).public_key.to_s
474
510
  aoc_api.update(resource_instance_path, {jwt_grant_enabled: true, public_key: the_public_key})
475
511
  return Main.result_success
476
512
  when :do
477
513
  command_repo = options.get_next_command(NODE4_EXT_COMMANDS)
514
+ aoc_api.context(:none)
478
515
  return execute_nodegen4_command(command_repo, res_id)
479
516
  else raise 'unknown command'
480
517
  end
481
518
  when :usage_reports
482
- return {type: :object_list, data: aoc_api.read('usage_reports', {workspace_id: current_workspace_info['id']})[:data]}
519
+ return {type: :object_list, data: aoc_api.read('usage_reports', {workspace_id: aoc_api.context(:none)[:workspace_id]})[:data]}
483
520
  end
484
521
  end
485
522
 
@@ -488,10 +525,19 @@ module Aspera
488
525
 
489
526
  def execute_action
490
527
  command = options.get_next_command(ACTIONS)
528
+ if %i[files packages].include?(command)
529
+ default_flag = ' (default)' if options.get_option(:workspace).eql?(:default)
530
+ app_context = aoc_api.context(command)
531
+ formatter.display_status("Workspace: #{app_context[:workspace_name].to_s.red}#{default_flag}")
532
+ if !aoc_api.private_link.nil?
533
+ folder_name = aoc_api.node_api_from(node_id: app_context[:home_node_id]).read("files/#{app_context[:home_file_id]}")[:data]['name']
534
+ formatter.display_status("Private Folder: #{folder_name}")
535
+ end
536
+ end
491
537
  case command
492
538
  when :reminder
493
539
  # send an email reminder with list of orgs
494
- user_email = options.get_option(:username, is_type: :mandatory)
540
+ user_email = options.get_option(:username, mandatory: true)
495
541
  Rest.new(base_url: "#{AoC.api_base_url}/#{AoC::API_V1}").create('organization_reminders', {email: user_email})[:data]
496
542
  return Main.result_status("List of organizations user is member of, has been sent by e-mail to #{user_email}")
497
543
  when :servers
@@ -511,14 +557,14 @@ module Aspera
511
557
  when :list
512
558
  return {type: :object_list, data: aoc_api.read('workspaces')[:data], fields: %w[id name]}
513
559
  when :current
514
- return { type: :single_object, data: current_workspace_info }
560
+ return { type: :single_object, data: aoc_api.read("workspaces/#{aoc_api.context(:none)[:workspace_id]}")[:data] }
515
561
  end
516
562
  when :profile
517
563
  case options.get_next_command(%i[show modify])
518
564
  when :show
519
565
  return { type: :single_object, data: aoc_api.current_user_info(exception: true) }
520
566
  when :modify
521
- aoc_api.update("users/#{aoc_api.current_user_info(exception: true)['id']}", options.get_next_argument('modified parameters (hash)'))
567
+ aoc_api.update("users/#{aoc_api.current_user_info(exception: true)['id']}", options.get_next_argument('properties', type: Hash))
522
568
  return Main.result_status('modified')
523
569
  end
524
570
  end
@@ -528,29 +574,28 @@ module Aspera
528
574
  when :shared_inboxes
529
575
  case options.get_next_command(%i[list show])
530
576
  when :list
531
- query = option_url_query(nil)
577
+ query = query_read_delete
532
578
  if query.nil?
533
579
  query = {'embed[]' => 'dropbox', 'aggregate_permissions_by_dropbox' => true, 'sort' => 'dropbox_name'}
534
- query['workspace_id'] = current_workspace_info['id'] unless current_workspace_info['id'].eql?(:undefined)
580
+ query['workspace_id'] = aoc_api.context[:workspace_id] unless aoc_api.context[:workspace_id].eql?(:undefined)
535
581
  end
536
582
  return {type: :object_list, data: aoc_api.read('dropbox_memberships', query)[:data], fields: ['dropbox_id', 'dropbox.name']}
537
583
  when :show
538
584
  return {type: :single_object, data: aoc_api.read(get_resource_path_from_args('dropboxes'), query)[:data]}
539
585
  end
540
586
  when :send
541
- package_data = options.get_option(:value, is_type: :mandatory)
542
- raise CliBadArgument, 'value must be hash, refer to doc' unless package_data.is_a?(Hash)
587
+ package_data = value_create_modify(command: package_command)
543
588
  new_user_option = options.get_option(:new_user_option)
544
589
  option_validate = options.get_option(:validate_metadata)
545
590
  # works for both normal usr auth and link auth
546
- package_data['workspace_id'] ||= current_workspace_info['id']
591
+ package_data['workspace_id'] ||= aoc_api.context[:workspace_id]
547
592
 
548
- if !aoc_api.url_token_data.nil?
549
- assert_public_link_types(%w[send_package_to_user send_package_to_dropbox])
550
- box_type = aoc_api.url_token_data['purpose'].split('_').last
551
- package_data['recipients'] = [{'id' => aoc_api.url_token_data['data']["#{box_type}_id"], 'type' => box_type}]
593
+ if !aoc_api.public_link.nil?
594
+ aoc_api.assert_public_link_types(%w[send_package_to_user send_package_to_dropbox])
595
+ box_type = aoc_api.public_link['purpose'].split('_').last
596
+ package_data['recipients'] = [{'id' => aoc_api.public_link['data']["#{box_type}_id"], 'type' => box_type}]
552
597
  # enforce workspace id from link (should be already ok, but in case user wanted to override)
553
- package_data['workspace_id'] = aoc_api.url_token_data['data']['workspace_id']
598
+ package_data['workspace_id'] = aoc_api.public_link['data']['workspace_id']
554
599
  end
555
600
 
556
601
  # transfer may raise an error
@@ -559,38 +604,43 @@ module Aspera
559
604
  # return all info on package (especially package id)
560
605
  return { type: :single_object, data: created_package[:info]}
561
606
  when :recv
562
- if !aoc_api.url_token_data.nil?
563
- assert_public_link_types(['view_received_package'])
564
- options.set_option(:id, aoc_api.url_token_data['data']['package_id'])
607
+ ids_to_download = nil
608
+ if !aoc_api.public_link.nil?
609
+ aoc_api.assert_public_link_types(['view_received_package'])
610
+ # set the package id, it will
611
+ ids_to_download = aoc_api.public_link['data']['package_id']
565
612
  end
566
- # scalar here
567
- ids_to_download = instance_identifier
613
+ # get from command line unless it was a public link
614
+ ids_to_download ||= instance_identifier
568
615
  skip_ids_data = []
569
616
  skip_ids_persistency = nil
570
- if options.get_option(:once_only, is_type: :mandatory)
617
+ if options.get_option(:once_only, mandatory: true)
571
618
  skip_ids_persistency = PersistencyActionOnce.new(
572
619
  manager: @agents[:persistency],
573
620
  data: skip_ids_data,
574
- id: IdGenerator.from_list(['aoc_recv', options.get_option(:url, is_type: :mandatory),
575
- current_workspace_info['id']].concat(aoc_api.additional_persistence_ids)))
621
+ id: IdGenerator.from_list(
622
+ ['aoc_recv',
623
+ options.get_option(:url, mandatory: true),
624
+ aoc_api.context[:workspace_id]
625
+ ].concat(aoc_api.additional_persistence_ids)))
576
626
  end
577
- if VAL_ALL.eql?(ids_to_download)
578
- query = option_url_query(PACKAGE_QUERY_DEFAULT)
627
+ if ExtendedValue::ALL.eql?(ids_to_download)
628
+ query = query_read_delete(default: PACKAGE_QUERY_DEFAULT)
579
629
  raise 'option query must be Hash' unless query.is_a?(Hash)
580
630
  if query.key?('dropbox_name')
581
631
  # convenience: specify name instead of id
582
632
  raise 'not both dropbox_name and dropbox_id' if query.key?('dropbox_id')
583
- query['dropbox_id'] = aoc_api.lookup_entity_by_name('dropboxes', query['dropbox_name'])['id']
633
+ query['dropbox_id'] = aoc_api.lookup_by_name('dropboxes', query['dropbox_name'])['id']
584
634
  query.delete('dropbox_name')
585
635
  end
586
- query['workspace_id'] ||= current_workspace_info['id'] unless current_workspace_info['id'].eql?(:undefined)
636
+ query['workspace_id'] ||= aoc_api.context[:workspace_id] unless aoc_api.context[:workspace_id].eql?(:undefined)
587
637
  # get list of packages in inbox
588
638
  package_info = aoc_api.read('packages', query)[:data]
589
639
  # remove from list the ones already downloaded
590
640
  ids_to_download = package_info.map{|e|e['id']}
591
641
  # array here
592
642
  ids_to_download.reject!{|id|skip_ids_data.include?(id)}
593
- end # VAL_ALL
643
+ end # ExtendedValue::ALL
594
644
  # list here
595
645
  ids_to_download = [ids_to_download] unless ids_to_download.is_a?(Array)
596
646
  result_transfer = []
@@ -600,8 +650,8 @@ module Aspera
600
650
  formatter.display_status("downloading package: #{package_info['name']}")
601
651
  package_node_api = aoc_api.node_api_from(
602
652
  node_id: package_info['node_id'],
603
- workspace_id: current_workspace_info['id'],
604
- workspace_name: current_workspace_info['name'],
653
+ workspace_id: aoc_api.context[:workspace_id],
654
+ workspace_name: aoc_api.context[:workspace_name],
605
655
  package_info: package_info)
606
656
  statuses = transfer.start(
607
657
  package_node_api.transfer_spec_gen4(
@@ -618,112 +668,145 @@ module Aspera
618
668
  end
619
669
  return Main.result_transfer_multiple(result_transfer)
620
670
  when :show
621
- package_id = options.get_next_argument('package ID')
671
+ package_id = instance_identifier
622
672
  package_info = aoc_api.read("packages/#{package_id}")[:data]
623
673
  return { type: :single_object, data: package_info }
624
674
  when :list
625
675
  display_fields = %w[id name bytes_transferred]
626
- query = option_url_query(PACKAGE_QUERY_DEFAULT)
676
+ query = query_read_delete(default: PACKAGE_QUERY_DEFAULT)
627
677
  raise 'option query must be Hash' unless query.is_a?(Hash)
628
678
  if query.key?('dropbox_name')
629
679
  # convenience: specify name instead of id
630
680
  raise 'not both dropbox_name and dropbox_id' if query.key?('dropbox_id')
631
- query['dropbox_id'] = aoc_api.lookup_entity_by_name('dropboxes', query['dropbox_name'])['id']
681
+ query['dropbox_id'] = aoc_api.lookup_by_name('dropboxes', query['dropbox_name'])['id']
632
682
  query.delete('dropbox_name')
633
683
  end
634
- if current_workspace_info['id'].eql?(:undefined)
684
+ if aoc_api.context[:workspace_id].eql?(:undefined)
635
685
  display_fields.push('workspace_id')
636
686
  else
637
- query['workspace_id'] ||= current_workspace_info['id']
687
+ query['workspace_id'] ||= aoc_api.context[:workspace_id]
638
688
  end
639
689
  packages = aoc_api.read('packages', query)[:data]
640
690
  return {type: :object_list, data: packages, fields: display_fields}
641
691
  when :delete
642
- list_or_one = instance_identifier
643
- return do_bulk_operation(list_or_one, 'deleted') do |id|
692
+ return do_bulk_operation(command: package_command, descr: 'identifier', values: identifier) do |id|
644
693
  raise 'expecting String identifier' unless id.is_a?(String) || id.is_a?(Integer)
645
694
  aoc_api.delete("packages/#{id}")[:data]
646
695
  end
647
696
  when *Node::NODE4_READ_ACTIONS
648
- package_id = options.get_next_argument('package ID')
697
+ package_id = instance_identifier
649
698
  package_info = aoc_api.read("packages/#{package_id}")[:data]
650
- return execute_nodegen4_command(package_command, package_info['node_id'], file_id: package_info['file_id'], scope: AoC::SCOPE_NODE_USER)
699
+ return execute_nodegen4_command(package_command, package_info['node_id'], file_id: package_info['file_id'], scope: Aspera::Node::SCOPE_USER)
651
700
  end
652
701
  when :files
653
702
  command_repo = options.get_next_command([:short_link].concat(NODE4_EXT_COMMANDS))
654
703
  case command_repo
655
704
  when *NODE4_EXT_COMMANDS
656
- return execute_nodegen4_command(command_repo, home_info[:node_id], file_id: home_info[:file_id], scope: AoC::SCOPE_NODE_USER)
705
+ return execute_nodegen4_command(command_repo, aoc_api.context[:home_node_id], file_id: aoc_api.context[:home_file_id], scope: Aspera::Node::SCOPE_USER)
657
706
  when :short_link
658
- # TODO: move to permissions ?
659
- folder_dest = options.get_option(:to_folder)
660
- value_option = options.get_option(:value)
661
- case value_option
662
- when 'public' then value_option = {'purpose' => 'token_auth_redirection'}
663
- when 'private' then value_option = {'purpose' => 'shared_folder_auth_link'}
664
- when NilClass, Hash then nil # keep value
665
- else raise 'value must be either: public, private, Hash or nil'
707
+ link_type = options.get_next_argument('link type', expected: %i[public private])
708
+ short_link_command = options.get_next_command(%i[create delete list])
709
+ folder_dest = options.get_next_argument('path', type: String)
710
+ home_node_api = aoc_api.node_api_from(
711
+ node_id: aoc_api.context[:home_node_id],
712
+ workspace_id: aoc_api.context[:workspace_id],
713
+ workspace_name: aoc_api.context[:workspace_name])
714
+ shared_apfid = home_node_api.resolve_api_fid(aoc_api.context[:home_file_id], folder_dest)
715
+ folder_info = {
716
+ node_id: shared_apfid[:api].app_info[:node_info]['id'],
717
+ file_id: shared_apfid[:file_id],
718
+ workspace_id: aoc_api.context[:workspace_id]
719
+ }
720
+ purpose = case link_type
721
+ when :public then 'token_auth_redirection'
722
+ when :private then 'shared_folder_auth_link'
723
+ else raise 'internal error'
666
724
  end
667
- create_params = nil
668
- shared_apfid = nil
669
- if !folder_dest.nil?
670
- home_node_api = aoc_api.node_api_from(
671
- node_id: home_info[:node_id],
672
- workspace_id: current_workspace_info['id'],
673
- workspace_name: current_workspace_info['name'])
674
- shared_apfid = home_node_api.resolve_api_fid(home_info[:file_id], folder_dest)
675
- create_params = {
676
- file_id: shared_apfid[:file_id],
677
- node_id: shared_apfid[:api].app_info[:node_info]['id'],
678
- workspace_id: current_workspace_info['id']
725
+ case short_link_command
726
+ when :delete
727
+ one_id = instance_identifier
728
+ folder_info.delete(:workspace_id)
729
+ delete_params = {
730
+ edit_access: true,
731
+ json_query: folder_info.to_json
679
732
  }
680
- end
681
- if !value_option.nil? && !create_params.nil?
682
- case value_option['purpose']
683
- when 'shared_folder_auth_link'
684
- value_option['data'] = create_params
685
- value_option['user_selected_name'] = nil
686
- when 'token_auth_redirection'
687
- create_params['name'] = ''
688
- value_option['data'] = {
733
+ aoc_api.delete("short_links/#{one_id}", delete_params)
734
+ if link_type.eql?(:public)
735
+ # TODO: get permission id..
736
+ # shared_apfid[:api].delete('permissions', {ids: })[:data]
737
+ end
738
+ return Main.result_status('deleted')
739
+ when :list
740
+ query = if link_type.eql?(:private)
741
+ folder_info
742
+ else
743
+ {
744
+ url_token_data: {
745
+ data: folder_info,
746
+ purpose: 'view_shared_file'
747
+ }
748
+ }
749
+ end
750
+ list_params = {
751
+ json_query: query.to_json,
752
+ purpose: purpose,
753
+ edit_access: true,
754
+ # embed: 'updated_by_user',
755
+ sort: '-created_at'
756
+ }
757
+ result = aoc_api.read('short_links', list_params)[:data]
758
+ return {type: :object_list, data: result, fields: Formatter.all_but('data')}
759
+ when :create
760
+ creation_params = {
761
+ purpose: purpose,
762
+ user_selected_name: nil
763
+ }
764
+ case link_type
765
+ when :private
766
+ creation_params[:data] = folder_info
767
+ when :public
768
+ creation_params[:expires_at] = nil
769
+ creation_params[:password_enabled] = false
770
+ folder_info[:name] = ''
771
+ creation_params[:data] = {
689
772
  aoc: true,
690
773
  url_token_data: {
691
- data: create_params,
774
+ data: folder_info,
692
775
  purpose: 'view_shared_file'
693
776
  }
694
777
  }
695
- value_option['user_selected_name'] = nil
696
- else
697
- raise 'purpose must be one of: token_auth_redirection or shared_folder_auth_link'
698
778
  end
699
- options.set_option(:value, value_option)
700
- end
701
- result = entity_action(aoc_api, 'short_links', id_default: 'self')
702
- if result[:data].is_a?(Hash) && result[:data].key?('created_at') && result[:data]['resource_type'].eql?('UrlToken')
703
- # TODO: access level as arg
704
- access_levels = Aspera::Node::ACCESS_LEVELS # ['delete','list','mkdir','preview','read','rename','write']
705
- perm_data = {
706
- 'file_id' => shared_apfid[:file_id],
707
- 'access_type' => 'user',
708
- 'access_id' => result[:data]['resource_id'],
709
- 'access_levels' => access_levels,
710
- 'tags' => {
711
- 'url_token' => true,
712
- 'workspace_id' => current_workspace_info['id'],
713
- 'workspace_name' => current_workspace_info['name'],
714
- 'folder_name' => 'my folder',
715
- 'created_by_name' => aoc_api.current_user_info['name'],
716
- 'created_by_email' => aoc_api.current_user_info['email'],
717
- 'access_key' => shared_apfid[:api].app_info[:node_info]['access_key'],
718
- 'node' => shared_apfid[:api].app_info[:node_info]['host']
779
+ result_create_short_link = aoc_api.create('short_links', creation_params)[:data]
780
+ # public: Creation: permission on node
781
+ if link_type.eql?(:public)
782
+ # TODO: merge with node permissions ?
783
+ # TODO: access level as arg
784
+ access_levels = Aspera::Node::ACCESS_LEVELS # ['delete','list','mkdir','preview','read','rename','write']
785
+ folder_name = File.basename(folder_dest)
786
+ perm_data = {
787
+ 'file_id' => shared_apfid[:file_id],
788
+ 'access_id' => result_create_short_link['resource_id'],
789
+ 'access_type' => 'user',
790
+ 'access_levels' => access_levels,
791
+ 'tags' => {
792
+ 'url_token' => true,
793
+ 'workspace_id' => aoc_api.context[:workspace_id],
794
+ 'workspace_name' => aoc_api.context[:workspace_name],
795
+ 'folder_name' => folder_name,
796
+ 'created_by_name' => aoc_api.current_user_info['name'],
797
+ 'created_by_email' => aoc_api.current_user_info['email'],
798
+ 'access_key' => shared_apfid[:api].app_info[:node_info]['access_key'],
799
+ 'node' => shared_apfid[:api].app_info[:node_info]['host']
800
+ }
719
801
  }
720
- }
721
- shared_apfid[:api].create("permissions?file_id=#{shared_apfid[:file_id]}", perm_data)
722
- # TODO: event ?
723
- end
724
- return result
802
+ created_data = shared_apfid[:api].create('permissions', perm_data)[:data]
803
+ aoc_api.permissions_send_event(created_data: created_data, app_info: shared_apfid[:api].app_info)
804
+ # TODO: event ?
805
+ end
806
+ return {type: :single_object, data: result_create_short_link}
807
+ end # short_link command
725
808
  end # files command
726
- throw('Error: shall not reach this line')
809
+ raise 'Error: shall not reach this line'
727
810
  when :automation
728
811
  Log.log.warn('BETA: work under progress')
729
812
  # automation api is not in the same place
@@ -738,7 +821,7 @@ module Aspera
738
821
  wf_command = options.get_next_command(%i[action launch].concat(Plugin::ALL_OPS))
739
822
  case wf_command
740
823
  when *Plugin::ALL_OPS
741
- return entity_command(wf_command, automation_api, 'workflows', id_default: :id)
824
+ return entity_command(wf_command, automation_api, 'workflows')
742
825
  when :launch
743
826
  wf_id = instance_identifier
744
827
  data = automation_api.create("workflows/#{wf_id}/launch", {})[:data]
@@ -760,10 +843,10 @@ module Aspera
760
843
  return execute_admin_action
761
844
  when :gateway
762
845
  require 'aspera/faspex_gw'
763
- url = options.get_option(:value, is_type: :mandatory)
846
+ url = value_create_modify(command: command, type: String)
764
847
  uri = URI.parse(url)
765
848
  server = WebServerSimple.new(uri)
766
- server.mount(uri.path, Faspex4GWServlet, aoc_api, current_workspace_info['id'])
849
+ server.mount(uri.path, Faspex4GWServlet, aoc_api, aoc_api.context(:none)[:workspace_id])
767
850
  trap('INT') { server.shutdown }
768
851
  formatter.display_status("Faspex 4 gateway listening on #{url}")
769
852
  Log.log.info("Listening on #{url}")
@@ -776,10 +859,7 @@ module Aspera
776
859
  raise 'internal error: command shall return'
777
860
  end
778
861
 
779
- private :aoc_params,
780
- :home_info,
781
- :assert_public_link_types,
782
- :execute_admin_action
862
+ private :execute_admin_action
783
863
  end # AoC
784
864
  end # Plugins
785
865
  end # Cli