aspera-cli 4.14.0 → 4.16.0

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