aspera-cli 4.13.0 → 4.15.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 (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