aspera-cli 4.26.2 → 4.27.1
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +310 -21
- data/CONTRIBUTING.md +99 -14
- data/TODO.md +50 -0
- data/bin/ascli +3 -3
- data/docs/README.md +10977 -0
- data/docs/test-mcp-with-ai.md +202 -0
- data/lib/aspera/agent/base.rb +29 -7
- data/lib/aspera/agent/connect.rb +58 -21
- data/lib/aspera/agent/desktop.rb +53 -18
- data/lib/aspera/agent/direct.rb +71 -40
- data/lib/aspera/agent/factory.rb +3 -3
- data/lib/aspera/agent/httpgw.rb +55 -14
- data/lib/aspera/agent/node.rb +34 -2
- data/lib/aspera/agent/transferd.rb +48 -12
- data/lib/aspera/api/alee.rb +1 -1
- data/lib/aspera/api/aoc.rb +42 -44
- data/lib/aspera/api/cos_node.rb +10 -9
- data/lib/aspera/api/faspex.rb +16 -15
- data/lib/aspera/api/httpgw.rb +23 -23
- data/lib/aspera/api/node.rb +51 -70
- data/lib/aspera/api/queries/bss_subscription_account.graphql +18 -0
- data/lib/aspera/api/queries/bss_subscription_usage.graphql +18 -0
- data/lib/aspera/ascmd.rb +32 -30
- data/lib/aspera/ascp/installation.rb +75 -69
- data/lib/aspera/ascp/management.rb +7 -8
- data/lib/aspera/assert.rb +12 -13
- data/lib/aspera/cli/ascp_actions.rb +155 -0
- data/lib/aspera/cli/async_transfer_store.rb +125 -0
- data/lib/aspera/cli/bootstrapper.rb +197 -0
- data/lib/aspera/cli/command_registry.rb +145 -0
- data/lib/aspera/cli/command_spec.rb +141 -0
- data/lib/aspera/cli/context.rb +19 -6
- data/lib/aspera/cli/error.rb +16 -0
- data/lib/aspera/cli/extended_value.rb +27 -26
- data/lib/aspera/cli/formatter.rb +95 -59
- data/lib/aspera/cli/gem_checker.rb +65 -0
- data/lib/aspera/cli/hints.rb +3 -3
- data/lib/aspera/cli/http.rb +74 -30
- data/lib/aspera/cli/info.rb +2 -0
- data/lib/aspera/cli/mailer.rb +97 -0
- data/lib/aspera/cli/mcp_tool.rb +225 -0
- data/lib/aspera/cli/option_declarator.rb +77 -0
- data/lib/aspera/cli/options.schema.yaml +634 -7
- data/lib/aspera/cli/parser.rb +1217 -0
- data/lib/aspera/cli/plugins/alee.rb +20 -22
- data/lib/aspera/cli/plugins/aoc.rb +1178 -881
- data/lib/aspera/cli/plugins/ats.rb +205 -162
- data/lib/aspera/cli/plugins/base.rb +703 -175
- data/lib/aspera/cli/plugins/basic_auth.rb +7 -9
- data/lib/aspera/cli/plugins/config.rb +434 -757
- data/lib/aspera/cli/plugins/console.rb +107 -65
- data/lib/aspera/cli/plugins/cos.rb +46 -33
- data/lib/aspera/cli/plugins/factory.rb +6 -6
- data/lib/aspera/cli/plugins/faspex5.rb +627 -388
- data/lib/aspera/cli/plugins/faspio.rb +54 -51
- data/lib/aspera/cli/plugins/httpgw.rb +18 -25
- data/lib/aspera/cli/plugins/mcp.rb +279 -0
- data/lib/aspera/cli/plugins/node.rb +988 -840
- data/lib/aspera/cli/plugins/oauth.rb +7 -10
- data/lib/aspera/cli/plugins/orchestrator.rb +112 -135
- data/lib/aspera/cli/plugins/preview.rb +195 -157
- data/lib/aspera/cli/plugins/server.rb +141 -91
- data/lib/aspera/cli/plugins/shares.rb +343 -110
- data/lib/aspera/cli/preset_actions.rb +159 -0
- data/lib/aspera/cli/preset_manager.rb +82 -39
- data/lib/aspera/cli/result.rb +78 -26
- data/lib/aspera/cli/runner.rb +323 -153
- data/lib/aspera/cli/secret_finder.rb +40 -0
- data/lib/aspera/cli/special_values.rb +1 -0
- data/lib/aspera/cli/sync_actions.rb +89 -64
- data/lib/aspera/cli/terminal_formatter.rb +3 -3
- data/lib/aspera/cli/transfer_actions.rb +90 -0
- data/lib/aspera/cli/transfer_agent.rb +129 -64
- data/lib/aspera/cli/transfer_progress.rb +9 -9
- data/lib/aspera/cli/vault_manager.rb +79 -0
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +28 -30
- data/lib/aspera/colors.rb +3 -3
- data/lib/aspera/command_line_builder.rb +34 -34
- data/lib/aspera/command_line_converter.rb +1 -1
- data/lib/aspera/coverage.rb +1 -2
- data/lib/aspera/data_repository.rb +2 -1
- data/lib/aspera/dot_container.rb +12 -12
- data/lib/aspera/environment.rb +30 -23
- data/lib/aspera/exec_spec.rb +13 -0
- data/lib/aspera/faspex_gw.rb +5 -5
- data/lib/aspera/faspex_postproc.rb +16 -10
- data/lib/aspera/graphql.rb +37 -0
- data/lib/aspera/hash_ext.rb +8 -2
- data/lib/aspera/json_rpc/client.rb +62 -0
- data/lib/aspera/json_rpc/version.rb +7 -0
- data/lib/aspera/keychain/base.rb +10 -3
- data/lib/aspera/keychain/encrypted_hash.rb +92 -17
- data/lib/aspera/keychain/factory.rb +20 -8
- data/lib/aspera/keychain/hashicorp_vault.rb +1 -1
- data/lib/aspera/keychain/macos_security.rb +23 -25
- data/lib/aspera/keychain/one_password_api.rb +86 -0
- data/lib/aspera/keychain/one_password_base.rb +34 -0
- data/lib/aspera/keychain/one_password_cli.rb +98 -0
- data/lib/aspera/link_header.rb +82 -0
- data/lib/aspera/log.rb +29 -15
- data/lib/aspera/markdown.rb +90 -7
- data/lib/aspera/nagios.rb +8 -8
- data/lib/aspera/node_simulator.rb +35 -27
- data/lib/aspera/oauth/base.rb +9 -10
- data/lib/aspera/oauth/boot.rb +5 -5
- data/lib/aspera/oauth/factory.rb +12 -10
- data/lib/aspera/oauth/jwt.rb +9 -11
- data/lib/aspera/oauth/web.rb +6 -6
- data/lib/aspera/persistency_action_once.rb +12 -12
- data/lib/aspera/persistency_folder.rb +18 -11
- data/lib/aspera/preview/file_types.rb +5 -5
- data/lib/aspera/preview/generator.rb +52 -49
- data/lib/aspera/preview/options.rb +3 -2
- data/lib/aspera/preview/terminal.rb +5 -5
- data/lib/aspera/preview/utils.rb +66 -19
- data/lib/aspera/products/connect.rb +2 -2
- data/lib/aspera/products/desktop.rb +1 -1
- data/lib/aspera/products/other.rb +3 -3
- data/lib/aspera/products/transferd.rb +4 -4
- data/lib/aspera/proxy_auto_config.rb +15 -14
- data/lib/aspera/rest.rb +159 -151
- data/lib/aspera/rest_error_analyzer.rb +6 -6
- data/lib/aspera/rest_errors_aspera.rb +0 -10
- data/lib/aspera/rest_list.rb +11 -10
- data/lib/aspera/schema/IBM Aspera Node API-4.4.6.yaml +6232 -0
- data/lib/aspera/schema/IBM Aspera faspio Gateway API-1.0.0.yaml +249 -0
- data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +1853 -510
- data/lib/aspera/schema/IBM_Aspera_Shares.yaml +4653 -0
- data/lib/aspera/schema/documentation.rb +65 -28
- data/lib/aspera/schema/reader.rb +136 -13
- data/lib/aspera/schema/registry.rb +60 -8
- data/lib/aspera/secret_hider.rb +12 -7
- data/lib/aspera/ssh.rb +64 -31
- data/lib/aspera/ssl.rb +5 -5
- data/lib/aspera/sync/conf.schema.yaml +2 -2
- data/lib/aspera/sync/database.rb +2 -2
- data/lib/aspera/sync/operations.rb +22 -25
- data/lib/aspera/temp_file_manager.rb +25 -5
- data/lib/aspera/timer_limiter.rb +1 -1
- data/lib/aspera/transfer/faux_file.rb +24 -11
- data/lib/aspera/transfer/parameters.rb +36 -34
- data/lib/aspera/transfer/result.rb +74 -0
- data/lib/aspera/transfer/resumer.rb +10 -10
- data/lib/aspera/transfer/spec.rb +18 -1
- data/lib/aspera/transfer/spec.schema.yaml +12 -3
- data/lib/aspera/uri_reader.rb +52 -15
- data/lib/aspera/web_auth.rb +8 -7
- data/lib/aspera/web_server_simple.rb +15 -12
- data/lib/aspera/yaml.rb +5 -4
- data.tar.gz.sig +0 -0
- metadata +34 -6
- metadata.gz.sig +0 -0
- data/lib/aspera/cli/manager.rb +0 -766
- data/lib/aspera/cli/plugins/faspex.rb +0 -539
- data/lib/aspera/json_rpc.rb +0 -52
- data/lib/aspera/transfer/uri.rb +0 -56
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
# spellchecker: ignore workgroups mypackages passcode
|
|
4
4
|
|
|
5
|
+
require 'aspera/schema/registry'
|
|
5
6
|
require 'aspera/cli/plugins/oauth'
|
|
6
7
|
require 'aspera/cli/extended_value'
|
|
8
|
+
require 'aspera/transfer/result'
|
|
7
9
|
require 'aspera/cli/special_values'
|
|
8
10
|
require 'aspera/cli/wizard'
|
|
9
11
|
require 'aspera/api/faspex'
|
|
@@ -18,11 +20,11 @@ module Aspera
|
|
|
18
20
|
module Cli
|
|
19
21
|
module Plugins
|
|
20
22
|
class Faspex5 < Oauth
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
end
|
|
23
|
+
application_name 'Faspex v5'
|
|
24
|
+
# Commands that may carry a query_schema annotation
|
|
25
|
+
QUERY_SCHEMA_COMMANDS = %i[list delete].freeze
|
|
25
26
|
|
|
27
|
+
class << self
|
|
26
28
|
# @return [Hash,NilClass]
|
|
27
29
|
def detect(address_or_url)
|
|
28
30
|
# add scheme if missing
|
|
@@ -45,7 +47,7 @@ module Aspera
|
|
|
45
47
|
}
|
|
46
48
|
rescue StandardError => e
|
|
47
49
|
error = e
|
|
48
|
-
Log.log.debug{"detect error: #{e}"}
|
|
50
|
+
Log.log.debug { "detect error: #{e}" }
|
|
49
51
|
end
|
|
50
52
|
raise error if error
|
|
51
53
|
return
|
|
@@ -79,24 +81,23 @@ module Aspera
|
|
|
79
81
|
)
|
|
80
82
|
return {
|
|
81
83
|
preset_value: {
|
|
82
|
-
url:
|
|
83
|
-
username:
|
|
84
|
-
auth:
|
|
85
|
-
private_key:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
client_secret: client_secret
|
|
89
|
-
}
|
|
84
|
+
url: app_url,
|
|
85
|
+
username: wiz_username,
|
|
86
|
+
auth: :jwt.to_s,
|
|
87
|
+
private_key: "@file:#{private_key_path}",
|
|
88
|
+
client_id: client_id,
|
|
89
|
+
client_secret: client_secret
|
|
90
90
|
},
|
|
91
91
|
test_args: 'user profile show'
|
|
92
92
|
}
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
+
option :box, description: "Package inbox, either shared inbox name or one of: #{Api::Faspex::API_LIST_MAILBOX_TYPES.join(', ')} or #{SpecialValues::ALL}", default: 'inbox_all'
|
|
96
|
+
option :shared_folder, description: 'Send package with files from shared folder'
|
|
97
|
+
option :group_type, description: 'Type of shared box', allowed: %i[shared_inboxes workgroups], default: :shared_inboxes
|
|
98
|
+
|
|
95
99
|
def initialize(**_)
|
|
96
100
|
super
|
|
97
|
-
options.declare(:box, "Package inbox, either shared inbox name or one of: #{Api::Faspex::API_LIST_MAILBOX_TYPES.join(', ')} or #{SpecialValues::ALL}", default: 'inbox_all')
|
|
98
|
-
options.declare(:shared_folder, 'Send package with files from shared folder')
|
|
99
|
-
options.declare(:group_type, 'Type of shared box', allowed: %i[shared_inboxes workgroups], default: :shared_inboxes)
|
|
100
101
|
options.parse_options!
|
|
101
102
|
# [Aspera::Api::Faspex]
|
|
102
103
|
@api_v5 = nil
|
|
@@ -106,7 +107,7 @@ module Aspera
|
|
|
106
107
|
def normalize_recipients(parameters, type)
|
|
107
108
|
type = type.to_s
|
|
108
109
|
return unless parameters.key?(type)
|
|
109
|
-
Aspera.assert_type(parameters[type], Array){type}
|
|
110
|
+
Aspera.assert_type(parameters[type], Array) { type }
|
|
110
111
|
recipient_types = Api::Faspex::RECIPIENT_TYPES
|
|
111
112
|
if parameters.key?('recipient_types')
|
|
112
113
|
recipient_types = parameters['recipient_types']
|
|
@@ -136,24 +137,24 @@ module Aspera
|
|
|
136
137
|
# user asked to not follow
|
|
137
138
|
return status if status_list.nil?
|
|
138
139
|
if status['upload_status'].eql?('submitted')
|
|
139
|
-
|
|
140
|
+
progress_bar&.event(:sessions_init, session_id: nil, info: status['upload_status'])
|
|
140
141
|
elsif !total_sent
|
|
141
|
-
|
|
142
|
-
|
|
142
|
+
progress_bar&.event(:session_start, session_id: id)
|
|
143
|
+
progress_bar&.event(:session_size, session_id: id, info: status['bytes_total'].to_i)
|
|
143
144
|
total_sent = true
|
|
144
145
|
else
|
|
145
|
-
|
|
146
|
+
progress_bar&.event(:transfer, session_id: id, info: status['bytes_written'].to_i)
|
|
146
147
|
end
|
|
147
148
|
if status_list.include?(status['upload_status'])
|
|
148
|
-
|
|
149
|
-
|
|
149
|
+
progress_bar&.event(:session_end, session_id: id)
|
|
150
|
+
progress_bar&.event(:end)
|
|
150
151
|
return status
|
|
151
152
|
end
|
|
152
153
|
sleep(1.0)
|
|
153
154
|
end
|
|
154
155
|
end
|
|
155
156
|
|
|
156
|
-
# @param [String] job identifier
|
|
157
|
+
# @param job_id [String] job identifier
|
|
157
158
|
# @return [Hash] result of API call for job status
|
|
158
159
|
def wait_for_job(job_id)
|
|
159
160
|
result = nil
|
|
@@ -167,9 +168,15 @@ module Aspera
|
|
|
167
168
|
return result
|
|
168
169
|
end
|
|
169
170
|
|
|
170
|
-
#
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
# List all packages with optional filter.
|
|
172
|
+
# The special `max` key is extracted *before* the API call and returned separately,
|
|
173
|
+
# so callers that apply a post-API filter (e.g. once_only) can enforce the limit
|
|
174
|
+
# after filtering rather than before.
|
|
175
|
+
# @param filter [Proc, nil] optional filter lambda applied to each package entry
|
|
176
|
+
# @param query [Hash] additional query parameters forwarded to the API
|
|
177
|
+
# @return [Array(Array, Integer|nil, Integer|nil)] [filtered list, max (or nil), total count]
|
|
178
|
+
def list_packages_with_filter(filter: nil, query: {})
|
|
179
|
+
filter ||= ->(_x) { true }
|
|
173
180
|
box = options.get_option(:box)
|
|
174
181
|
# Translate box name to API prefix (with ending slash)
|
|
175
182
|
entity =
|
|
@@ -180,8 +187,13 @@ module Aspera
|
|
|
180
187
|
group_type = options.get_option(:group_type)
|
|
181
188
|
"#{group_type}/#{@api_v5.lookup_entity_by_field(entity: group_type, value: box)['id']}/packages"
|
|
182
189
|
end
|
|
183
|
-
|
|
184
|
-
|
|
190
|
+
# Merge default query with user-provided query: user values take precedence, but defaults are preserved
|
|
191
|
+
user_query = query_read_delete(schema: Schema::Registry.query_params(Schema::Registry::FASPEX, 'packages'))
|
|
192
|
+
merged_query = user_query.nil? ? query.dup : query.merge(user_query)
|
|
193
|
+
# Extract `max` before the API call so callers can apply it after post-API filtering
|
|
194
|
+
max_items = merged_query.delete(RestList::MAX_ITEMS)&.to_i
|
|
195
|
+
list, total = @api_v5.list_entities_limit_offset_total_count(entity: entity, query: merged_query)
|
|
196
|
+
return list.select(&filter), max_items, total
|
|
185
197
|
end
|
|
186
198
|
|
|
187
199
|
# Build query to get package recipients based on package info in case of shared inbox or workgroup recipient
|
|
@@ -214,22 +226,24 @@ module Aspera
|
|
|
214
226
|
case package_ids
|
|
215
227
|
when SpecialValues::INIT
|
|
216
228
|
Aspera.assert(skip_ids_persistency, 'Only with option once_only')
|
|
217
|
-
skip_ids_persistency.data.clear.concat(list_packages_with_filter.first.map{ |p| p['id']})
|
|
229
|
+
skip_ids_persistency.data.clear.concat(list_packages_with_filter.first.map { |p| p['id'] }) # no filter: all packages, max ignored
|
|
218
230
|
skip_ids_persistency.save
|
|
219
231
|
return Result::Status.new("Initialized skip for #{skip_ids_persistency.data.count} package(s)")
|
|
220
232
|
when SpecialValues::ALL
|
|
221
233
|
# TODO: if packages have same name, they will overwrite ?
|
|
222
|
-
packages = list_packages_with_filter(query: {'status' => 'completed'})
|
|
223
|
-
Log.dump(:package_ids, level: :trace1){packages.map{ |p| p['id']}}
|
|
234
|
+
packages, max_items = list_packages_with_filter(query: {'status' => 'completed'}) # no filter: all completed packages
|
|
235
|
+
Log.dump(:package_ids, level: :trace1) { packages.map { |p| p['id'] } }
|
|
224
236
|
Log.dump(:skip_ids, skip_ids_persistency.data, level: :trace1)
|
|
225
|
-
packages.reject!{ |p| skip_ids_persistency.data.include?(p['id'])} if skip_ids_persistency
|
|
226
|
-
|
|
237
|
+
packages.reject! { |p| skip_ids_persistency.data.include?(p['id']) } if skip_ids_persistency
|
|
238
|
+
# Apply `max` after once_only filtering so we get the N first not-yet-downloaded packages
|
|
239
|
+
packages = packages[0, max_items] if max_items
|
|
240
|
+
Log.dump(:package_ids, level: :trace1) { packages.map { |p| p['id'] } }
|
|
227
241
|
else
|
|
228
242
|
# a single id was provided, or a list of ids
|
|
229
243
|
package_ids = [package_ids] unless package_ids.is_a?(Array)
|
|
230
|
-
Aspera.assert_array_all(package_ids, String){'Package id(s)'}
|
|
244
|
+
Aspera.assert_array_all(package_ids, String) { 'Package id(s)' }
|
|
231
245
|
# packages = package_ids.map{|pkg_id|@api_v5.read("packages/#{pkg_id}")}
|
|
232
|
-
packages = package_ids.map{ |pkg_id| {'id'=>pkg_id}}
|
|
246
|
+
packages = package_ids.map { |pkg_id| {'id'=>pkg_id} }
|
|
233
247
|
end
|
|
234
248
|
result_transfer = []
|
|
235
249
|
param_file_list = {}
|
|
@@ -261,7 +275,7 @@ module Aspera
|
|
|
261
275
|
statuses = transfer.start(transfer_spec)
|
|
262
276
|
result_transfer.push({'package' => pkg_id, Runner::STATUS_FIELD => statuses})
|
|
263
277
|
# skip only if all sessions completed
|
|
264
|
-
if
|
|
278
|
+
if statuses.is_a?(Transfer::Result::Success) && skip_ids_persistency
|
|
265
279
|
skip_ids_persistency.data.push(pkg_id)
|
|
266
280
|
skip_ids_persistency.save
|
|
267
281
|
end
|
|
@@ -269,8 +283,7 @@ module Aspera
|
|
|
269
283
|
return Runner.result_transfer_multiple(result_transfer)
|
|
270
284
|
end
|
|
271
285
|
|
|
272
|
-
def package_send
|
|
273
|
-
parameters = value_create_modify(command: :send, schema: Schema::Registry.req_body(Schema::Registry::FASPEX, 'packages.post'))
|
|
286
|
+
def package_send(parameters)
|
|
274
287
|
# autofill recipient for public url
|
|
275
288
|
if @api_v5.pub_link_context&.key?('recipient_type') && !parameters.key?('recipients')
|
|
276
289
|
parameters['recipients'] = [{
|
|
@@ -278,7 +291,7 @@ module Aspera
|
|
|
278
291
|
recipient_type: @api_v5.pub_link_context['recipient_type']
|
|
279
292
|
}]
|
|
280
293
|
end
|
|
281
|
-
PACKAGE_RECIPIENT_TYPES.each{ |type| normalize_recipients(parameters, type)}
|
|
294
|
+
PACKAGE_RECIPIENT_TYPES.each { |type| normalize_recipients(parameters, type) }
|
|
282
295
|
# User specified content prot in tspec, but faspex requires in package creation
|
|
283
296
|
# `transfer_spec/upload` will set `content_protection`
|
|
284
297
|
if transfer.user_transfer_spec['content_protection'] && !parameters.key?('ear_enabled')
|
|
@@ -299,7 +312,7 @@ module Aspera
|
|
|
299
312
|
return Runner.result_transfer(transfer.start(transfer_spec))
|
|
300
313
|
else
|
|
301
314
|
# send from remote shared folder
|
|
302
|
-
if (m =
|
|
315
|
+
if (m = Parser.percent_selector(shared_folder))
|
|
303
316
|
shared_folder = @api_v5.lookup_entity_by_field(
|
|
304
317
|
entity: 'shared_folders',
|
|
305
318
|
field: m[:field],
|
|
@@ -320,16 +333,17 @@ module Aspera
|
|
|
320
333
|
|
|
321
334
|
# Browse a folder
|
|
322
335
|
# @param browse_endpoint [String] the endpoint to browse
|
|
323
|
-
|
|
324
|
-
|
|
336
|
+
# @param folder_path [String] starting path (default: '/')
|
|
337
|
+
def browse_folder(browse_endpoint, base_query = {}, folder_path: '/')
|
|
338
|
+
folders_to_process = [folder_path]
|
|
325
339
|
query = base_query.merge(query_read_delete(default: {}))
|
|
326
|
-
filters = query.delete('filters'){{}}
|
|
340
|
+
filters = query.delete('filters') { {} }
|
|
327
341
|
Aspera.assert_type(filters, Hash)
|
|
328
342
|
filters['basenames'] ||= []
|
|
329
|
-
Aspera.assert_type(filters, Hash){'filters'}
|
|
343
|
+
Aspera.assert_type(filters, Hash) { 'filters' }
|
|
330
344
|
max_items = query.delete(RestList::MAX_ITEMS)
|
|
331
345
|
recursive = query.delete('recursive')
|
|
332
|
-
use_paging = query.delete('paging'){true}
|
|
346
|
+
use_paging = query.delete('paging') { true }
|
|
333
347
|
if use_paging
|
|
334
348
|
browse_endpoint = "#{browse_endpoint}/page"
|
|
335
349
|
query['per_page'] ||= 500
|
|
@@ -356,7 +370,7 @@ module Aspera
|
|
|
356
370
|
all_items = all_items.slice(0, max_items) if all_items.count > max_items
|
|
357
371
|
break
|
|
358
372
|
end
|
|
359
|
-
folders_to_process.concat(data['items'].select{ |i| i['type'].eql?('directory')}.map{ |i| i['path']}) if recursive
|
|
373
|
+
folders_to_process.concat(data['items'].select { |i| i['type'].eql?('directory') }.map { |i| i['path'] }) if recursive
|
|
360
374
|
if use_paging
|
|
361
375
|
iteration_token = http[Api::Faspex::HEADER_X_NEXT_ITER_TOKEN]
|
|
362
376
|
break if iteration_token.nil? || iteration_token.empty?
|
|
@@ -374,374 +388,599 @@ module Aspera
|
|
|
374
388
|
return Result::ObjectList.new(all_items, total: total_count)
|
|
375
389
|
end
|
|
376
390
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
391
|
+
# Per-resource configuration for admin CRUD sub-trees.
|
|
392
|
+
# Keys mirror entity_list/show/create/modify/delete kwargs; extra_commands lists additional leaf commands.
|
|
393
|
+
# @return [Hash{Symbol => Hash}]
|
|
394
|
+
RESOURCE_CONFIG = {
|
|
395
|
+
accounts: {
|
|
396
|
+
display_fields: -> { Formatter.all_but('user_profile_data_attributes') },
|
|
397
|
+
extra_commands: [:reset_password],
|
|
398
|
+
instance_arg_commands: {reset_password: {arguments: [{name: :contact_id, type: :identifier, lookup: ->(field, value, **) { res_lookup_id(:accounts, field, value) }}]}},
|
|
399
|
+
query_component: Schema::Registry::FASPEX,
|
|
400
|
+
body_component: Schema::Registry::FASPEX
|
|
401
|
+
},
|
|
402
|
+
alternate_addresses: {entity: 'configuration/alternate_addresses', query_component: Schema::Registry::FASPEX, body_component: Schema::Registry::FASPEX},
|
|
403
|
+
contacts: {query_component: Schema::Registry::FASPEX},
|
|
404
|
+
distribution_lists: {entity: 'account/distribution_lists', delete_style: 'ids', query_component: Schema::Registry::FASPEX, body_component: Schema::Registry::FASPEX},
|
|
405
|
+
email_notifications: {id_as_arg: 'type', query_component: Schema::Registry::FASPEX},
|
|
406
|
+
file_processing: {
|
|
407
|
+
commands: %i[next modify],
|
|
408
|
+
body_component: Schema::Registry::FASPEX,
|
|
409
|
+
is_singleton: true
|
|
410
|
+
},
|
|
411
|
+
jobs: {display_fields: %w[id job_name job_type status], query_component: Schema::Registry::FASPEX},
|
|
412
|
+
metadata_profiles: {entity: 'configuration/metadata_profiles', items_key: 'profiles', query_component: Schema::Registry::FASPEX, body_component: Schema::Registry::FASPEX},
|
|
413
|
+
nodes: {
|
|
414
|
+
extra_commands: %i[browse],
|
|
415
|
+
instance_arg_commands: {
|
|
416
|
+
browse: {
|
|
417
|
+
arguments: [{name: :node_id, type: :identifier, lookup: :lookup_node_id},
|
|
418
|
+
{name: :folder_path, type: String, mandatory: false, default: '/'}]
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
query_component: Schema::Registry::FASPEX,
|
|
422
|
+
body_component: Schema::Registry::FASPEX
|
|
423
|
+
},
|
|
424
|
+
oauth_clients: {
|
|
425
|
+
display_fields: -> { Formatter.all_but('public_key') },
|
|
426
|
+
api: -> { Api::Faspex.new(root: Api::Faspex::PATH_AUTH, **Oauth.kwargs_from_options(options)) },
|
|
427
|
+
list_query: {'expand': true, 'no_api_path': true, 'client_types[]': 'public'},
|
|
428
|
+
query_component: Schema::Registry::FASPEX,
|
|
429
|
+
body_component: Schema::Registry::FASPEX
|
|
430
|
+
},
|
|
431
|
+
registrations: {query_component: Schema::Registry::FASPEX},
|
|
432
|
+
saml_configs: {query_component: Schema::Registry::FASPEX, body_component: Schema::Registry::FASPEX},
|
|
433
|
+
shared_inboxes: {res_id_query: {'all': true}, query_component: Schema::Registry::FASPEX, body_component: Schema::Registry::FASPEX},
|
|
434
|
+
webhooks: {query_component: Schema::Registry::FASPEX, body_component: Schema::Registry::FASPEX},
|
|
435
|
+
workgroups: {res_id_query: {'all': true}, query_component: Schema::Registry::FASPEX, body_component: Schema::Registry::FASPEX}
|
|
436
|
+
}.freeze
|
|
437
|
+
private_constant :RESOURCE_CONFIG
|
|
438
|
+
|
|
439
|
+
# Resolve a RESOURCE_CONFIG value that may be a Proc (evaluated in instance context).
|
|
440
|
+
def resource_config_value(cfg, key)
|
|
441
|
+
v = cfg[key]
|
|
442
|
+
v.is_a?(Proc) ? instance_exec(&v) : v
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
# Build args hash from RESOURCE_CONFIG for use with entity_list/show/create/modify/delete.
|
|
446
|
+
def res_exec_args(res_sym)
|
|
447
|
+
cfg = RESOURCE_CONFIG.fetch(res_sym, {})
|
|
448
|
+
{
|
|
449
|
+
api: resource_config_value(cfg, :api) || @api_v5,
|
|
450
|
+
entity: resource_config_value(cfg, :entity) || res_sym.to_s,
|
|
451
|
+
items_key: resource_config_value(cfg, :items_key),
|
|
452
|
+
delete_style: resource_config_value(cfg, :delete_style),
|
|
453
|
+
id_as_arg: resource_config_value(cfg, :id_as_arg) || false,
|
|
454
|
+
display_fields: resource_config_value(cfg, :display_fields),
|
|
455
|
+
list_query: resource_config_value(cfg, :list_query),
|
|
456
|
+
is_singleton: resource_config_value(cfg, :is_singleton) || false,
|
|
457
|
+
query_component: resource_config_value(cfg, :query_component),
|
|
458
|
+
body_component: resource_config_value(cfg, :body_component)
|
|
459
|
+
}.compact
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
# admin > nodes — lookup node id by field/value
|
|
463
|
+
def lookup_node_id(field, value, **)
|
|
464
|
+
@api_v5.lookup_entity_by_field(entity: 'nodes', field: field, value: value)['id']
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
# Lookup id for a RESOURCE_CONFIG resource by field/value.
|
|
468
|
+
def res_lookup_id(res_sym, field, value)
|
|
469
|
+
cfg = RESOURCE_CONFIG.fetch(res_sym, {})
|
|
470
|
+
entity = resource_config_value(cfg, :entity) || res_sym.to_s
|
|
471
|
+
items_key = resource_config_value(cfg, :items_key)
|
|
472
|
+
res_id_query = resource_config_value(cfg, :res_id_query) || :default
|
|
473
|
+
@api_v5.lookup_entity_by_field(entity: entity, value: value, field: field, items_key: items_key, query: res_id_query)['id']
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
# Dynamically define lookup methods for admin resources (used by crud_commands)
|
|
477
|
+
Api::Faspex::ADMIN_RESOURCES.each do |res|
|
|
478
|
+
define_method(:"lookup_admin_#{res}_id") do |field, value, **|
|
|
479
|
+
res_lookup_id(res, field, value)
|
|
422
480
|
end
|
|
423
481
|
end
|
|
424
482
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
483
|
+
# --- DSL ---
|
|
484
|
+
|
|
485
|
+
# Commands that need @api_v5 carry setup: :setup_api_v5.
|
|
486
|
+
# :health and :postprocessing work without authentication, so they have no setup.
|
|
487
|
+
command :health, description: 'Check Faspex 5 health'
|
|
488
|
+
command :version, description: 'Show Faspex 5 version', setup: :setup_api_v5, action: -> { Result::SingleObject.new(@api_v5.read('version')) }
|
|
489
|
+
command :bearer_token, description: 'Show OAuth bearer token', setup: :setup_api_v5, action: -> { Result::Text.new(@api_v5.oauth.authorization) }
|
|
490
|
+
command :packages, description: 'Manage packages', setup: :setup_api_v5
|
|
491
|
+
commands_under :packages do
|
|
492
|
+
command :list, description: 'List packages',
|
|
493
|
+
arguments: [{name: :filter, mandatory: false, default: nil, type: Proc}]
|
|
494
|
+
command :send, description: 'Send a package', transfer_paths: :send,
|
|
495
|
+
arguments: [{name: :data, type: Hash, schema: Schema::Registry.req_body(Schema::Registry::FASPEX, 'packages.post')}],
|
|
496
|
+
action: ->(data:, **) { package_send(data) }
|
|
497
|
+
command :show, description: 'Show a package', setup: :setup_package_id,
|
|
498
|
+
action: ->(package_id:, **) { Result::SingleObject.new(@api_v5.read("packages/#{package_id}")) }
|
|
499
|
+
command :browse, description: 'Browse package files', setup: :setup_package_id,
|
|
500
|
+
arguments: [{name: :folder_path, type: String, mandatory: false, default: '/'}],
|
|
501
|
+
action: ->(folder_path:, package_id:, **) { browse_folder("packages/#{package_id}/files/#{Api::Faspex.box_type(options.get_option(:box))}", recipient_query(package_id), folder_path: folder_path) }
|
|
502
|
+
command :status, description: 'Wait for package status', setup: :setup_package_id,
|
|
503
|
+
arguments: [{name: :status_list, type: Array, mandatory: false, default: nil}],
|
|
504
|
+
action: ->(status_list:, package_id:, **) { Result::SingleObject.new(wait_package_status(package_id, status_list: status_list)) }
|
|
505
|
+
command :delete, description: 'Delete packages', setup: :setup_package_id
|
|
506
|
+
command :receive, description: 'Receive a package', setup: :setup_package_id, transfer_paths: :receive,
|
|
507
|
+
action: ->(package_id:, **) { package_receive(package_id) }
|
|
508
|
+
command(:file_processing, description: 'Show file processing status', setup: :setup_package_id, action: lambda do |package_id:, **|
|
|
509
|
+
result, count = @api_v5.list_entities_limit_offset_total_count(entity: "packages/#{package_id}/file_statuses", items_key: 'files')
|
|
510
|
+
Result::ObjectList.new(result, total: count)
|
|
511
|
+
end)
|
|
512
|
+
end
|
|
513
|
+
command :admin, description: 'Administer Faspex 5', setup: :setup_api_v5
|
|
514
|
+
command :user, description: 'Manage current user', setup: :setup_api_v5
|
|
515
|
+
command :shared_folders, description: 'Browse shared folders', setup: :setup_api_v5
|
|
516
|
+
command :gateway, description: 'Start Faspex 4 gateway emulation', setup: :setup_api_v5,
|
|
517
|
+
arguments: [{name: :parameters, type: Hash, mandatory: false, default: {}}]
|
|
518
|
+
command :postprocessing, description: 'Start Faspex 4 post-processing server',
|
|
519
|
+
arguments: [{name: :parameters, type: Hash, mandatory: false, default: {}}]
|
|
520
|
+
command :invitations, description: 'Manage invitations', setup: :setup_api_v5
|
|
521
|
+
|
|
522
|
+
commands_under :invitations do
|
|
523
|
+
command :create, description: 'Create an invitation',
|
|
524
|
+
arguments: [{name: :input_data, type: Hash, bulk: true}]
|
|
525
|
+
command :resend, description: 'Resend an invitation',
|
|
526
|
+
arguments: [{name: :invitation_id, type: :identifier}]
|
|
527
|
+
crud_commands entity: 'invitations',
|
|
528
|
+
api: :@api_v5,
|
|
529
|
+
operations: Operations::ALL - %i[create],
|
|
530
|
+
items_key: 'invitations',
|
|
531
|
+
display_fields: %w[id public recipient_type recipient_name email_address]
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
commands_under :user do
|
|
535
|
+
command :account, description: 'Show account information', action: -> { Result::SingleObject.new(@api_v5.read('account', query_read_delete)) }
|
|
536
|
+
command :profile, description: 'Manage user profile'
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
commands_under %i[user profile] do
|
|
540
|
+
command :show, description: 'Show user profile', action: -> { Result::SingleObject.new(@api_v5.read('account/preferences')) }
|
|
541
|
+
command(
|
|
542
|
+
:modify,
|
|
543
|
+
description: 'Modify user profile',
|
|
544
|
+
arguments: [{name: :properties, type: Hash}],
|
|
545
|
+
action: lambda do |properties:, **|
|
|
546
|
+
@api_v5.update('account/preferences', properties)
|
|
547
|
+
Result::Status.new('modified')
|
|
479
548
|
end
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
549
|
+
)
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
commands_under :shared_folders do
|
|
553
|
+
command :list, description: 'List shared folders', action: -> { Result::ObjectList.new(@api_v5.read('shared_folders')['shared_folders']) }
|
|
554
|
+
command :browse, description: 'Browse a shared folder',
|
|
555
|
+
arguments: [{name: :shared_folder_id, type: :identifier, lookup: :lookup_shared_folder_id},
|
|
556
|
+
{name: :folder_path, type: String, mandatory: false, default: '/'}]
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
# admin sub-tree: fixed commands + all ADMIN_RESOURCES with their sub-commands
|
|
560
|
+
commands_under :admin do
|
|
561
|
+
command :configuration, description: 'Manage Faspex 5 configuration'
|
|
562
|
+
command :smtp, description: 'Manage SMTP configuration'
|
|
563
|
+
command :events, description: 'List events'
|
|
564
|
+
command :clean_deleted, description: 'Clean deleted packages',
|
|
565
|
+
arguments: [{name: :input_data, type: Hash, mandatory: false, default: {}}]
|
|
566
|
+
Api::Faspex::ADMIN_RESOURCES.each do |res|
|
|
567
|
+
cfg = RESOURCE_CONFIG.fetch(res, {})
|
|
568
|
+
extra = cfg[:extra_commands] || []
|
|
569
|
+
ia_cmds = cfg[:instance_arg_commands] || {}
|
|
570
|
+
is_singleton = cfg[:is_singleton] || false
|
|
571
|
+
entity_path = cfg[:entity] || res.to_s
|
|
572
|
+
crud_ops = ((cfg[:commands] || Operations::ALL) - %i[list]) & Operations::ALL
|
|
573
|
+
|
|
574
|
+
command res, description: "Manage #{res.to_s.tr('_', ' ')}"
|
|
575
|
+
commands_under(res) do
|
|
576
|
+
# List is handled with Faspex 5 pagination (item_list_with_total)
|
|
577
|
+
unless is_singleton
|
|
578
|
+
list_kwargs = {description: 'List'}
|
|
579
|
+
list_kwargs[:query_schema] = Schema::Registry.query_params(cfg[:query_component], entity_path) if cfg[:query_component]
|
|
580
|
+
command :list, **list_kwargs
|
|
494
581
|
end
|
|
495
|
-
user_path = "#{shared_folder_entity}/#{sh_id}/custom_access_users"
|
|
496
|
-
return entity_execute(api: @api_v5, entity: user_path, items_key: 'users') do |field, value|
|
|
497
|
-
@api_v5.lookup_entity_by_field(entity: user_path, items_key: 'users', field: field, value: value)['id']
|
|
498
|
-
end
|
|
499
582
|
|
|
583
|
+
# Standard CRUD operations handled via crud_commands
|
|
584
|
+
crud_commands(
|
|
585
|
+
api: -> { res_exec_args(res)[:api] },
|
|
586
|
+
entity: entity_path,
|
|
587
|
+
operations: crud_ops,
|
|
588
|
+
lookup: :"lookup_admin_#{res}_id",
|
|
589
|
+
is_singleton: is_singleton,
|
|
590
|
+
body_component: cfg[:body_component],
|
|
591
|
+
display_fields: cfg[:display_fields],
|
|
592
|
+
delete_style: cfg[:delete_style],
|
|
593
|
+
id_as_arg: cfg[:id_as_arg] || false
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
# Extra commands (e.g. browse, reset_password, next)
|
|
597
|
+
extra.each do |c|
|
|
598
|
+
ia = ia_cmds[c] || {}
|
|
599
|
+
command c, description: c.to_s.tr('_', ' ').capitalize, **ia
|
|
600
|
+
end
|
|
500
601
|
end
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
602
|
+
end
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
commands_under %i[admin nodes] do
|
|
606
|
+
command :shared_folders, description: 'Manage shared folders',
|
|
607
|
+
arguments: [{name: :node_id, type: :identifier, lookup: :lookup_node_id}],
|
|
608
|
+
setup: :setup_admin_nodes_shared_folders
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
# admin > nodes > shared_folders sub-tree
|
|
612
|
+
# entity: :sf_entity is resolved at runtime from ctx (injected by setup_admin_nodes_shared_folders).
|
|
613
|
+
commands_under %i[admin nodes shared_folders] do
|
|
614
|
+
crud_commands entity: :sf_entity,
|
|
615
|
+
api: :@api_v5,
|
|
616
|
+
name: 'shared folder',
|
|
617
|
+
lookup: :lookup_sf_id,
|
|
618
|
+
items_key: 'shared_folders'
|
|
619
|
+
command :user, description: 'Custom access users',
|
|
620
|
+
arguments: [{name: :sf_id, type: :identifier, lookup: :lookup_sf_id}],
|
|
621
|
+
setup: :setup_admin_nodes_shared_folders_user
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
# admin > nodes > shared_folders > user sub-tree (custom access users)
|
|
625
|
+
# entity: :user_path is resolved at runtime from ctx (injected by setup_admin_nodes_shared_folders_user).
|
|
626
|
+
commands_under %i[admin nodes shared_folders user] do
|
|
627
|
+
crud_commands entity: :user_path, api: :@api_v5, name: 'custom access user', lookup: :lookup_sf_user_id, items_key: 'users'
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
# `members` and `saml_groups`
|
|
631
|
+
CRUD_NO_SHOW = %i[create list modify delete].freeze
|
|
632
|
+
CRUD_NO_LIST = %i[create modify delete show].freeze
|
|
633
|
+
|
|
634
|
+
RES_SINGULAR = {shared_inboxes: :shared_inbox, workgroups: :workgroup}.freeze
|
|
635
|
+
private_constant :RES_SINGULAR
|
|
636
|
+
|
|
637
|
+
# admin > shared_inboxes|workgroups > members|saml_groups|invite_external_collaborator:
|
|
638
|
+
# res_id consumed via arguments:(:identifier) + lookup:, builds res_instance_path for all children
|
|
639
|
+
%i[shared_inboxes workgroups].each do |res|
|
|
640
|
+
lookup_res_id = ->(field, value, **) { @api_v5.lookup_entity_by_field(entity: res.to_s, field: field, value: value, query: {'all': true})['id'] }
|
|
641
|
+
commands_under [:admin, res] do
|
|
642
|
+
command :members, description: 'Members',
|
|
643
|
+
arguments: [{name: :"#{RES_SINGULAR[res]}_id", type: :identifier, lookup: lookup_res_id}],
|
|
644
|
+
setup: :"setup_admin_#{res}_instance"
|
|
645
|
+
command :saml_groups, description: 'Saml groups',
|
|
646
|
+
arguments: [{name: :"#{RES_SINGULAR[res]}_id", type: :identifier, lookup: lookup_res_id}],
|
|
647
|
+
setup: :"setup_admin_#{res}_instance"
|
|
648
|
+
command :invite_external_collaborator, description: 'Invite external collaborator',
|
|
649
|
+
arguments: [{name: :"#{RES_SINGULAR[res]}_id", type: :identifier, lookup: lookup_res_id},
|
|
650
|
+
{name: :input_data, type: Hash}]
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
commands_under [:admin, res, :members] do
|
|
654
|
+
CRUD_NO_SHOW.each do |c|
|
|
655
|
+
args =
|
|
656
|
+
if c.eql?(:create)
|
|
657
|
+
{arguments: [{name: :users, bulk: true}, {name: :access, mandatory: false, default: :standard}]}
|
|
658
|
+
elsif Operations::INSTANCE.include?(c)
|
|
659
|
+
{arguments: [{name: :member_id, type: :identifier, lookup: :"lookup_#{res}_members_id"}]}
|
|
539
660
|
else
|
|
540
|
-
|
|
541
|
-
user
|
|
661
|
+
{}
|
|
542
662
|
end
|
|
543
|
-
|
|
544
|
-
access = options.get_next_argument('level', mandatory: false, accept_list: SHARED_INBOX_MEMBER_LEVELS, default: :standard)
|
|
545
|
-
options.unshift_next_argument({user: users.map{ |u| {id: u, access: access}}})
|
|
663
|
+
command c, description: c.to_s.capitalize, **args
|
|
546
664
|
end
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
case command
|
|
580
|
-
when *Api::Faspex::ADMIN_RESOURCES
|
|
581
|
-
return execute_resource(command)
|
|
582
|
-
when :clean_deleted
|
|
583
|
-
delete_data = value_create_modify(command: command, default: {})
|
|
584
|
-
delete_data = @api_v5.read('configuration').slice('days_before_deleting_package_records') if delete_data.empty?
|
|
585
|
-
res = @api_v5.create('internal/packages/clean_deleted', delete_data)
|
|
586
|
-
return Result::SingleObject.new(res)
|
|
587
|
-
when :events
|
|
588
|
-
event_type = options.get_next_command(%i[application webhook])
|
|
589
|
-
case event_type
|
|
590
|
-
when :application
|
|
591
|
-
list, total = @api_v5.list_entities_limit_offset_total_count(entity: 'application_events', query: query_read_delete)
|
|
592
|
-
return Result::ObjectList.new(list, total: total, fields: %w[event_type created_at application user.name])
|
|
593
|
-
when :webhook
|
|
594
|
-
list, total = @api_v5.list_entities_limit_offset_total_count(
|
|
595
|
-
entity: 'all_webhooks_events',
|
|
596
|
-
query: query_read_delete,
|
|
597
|
-
items_key: 'events'
|
|
598
|
-
)
|
|
599
|
-
return Result::ObjectList.new(list, total: total)
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
commands_under [:admin, res, :saml_groups] do
|
|
668
|
+
crud_commands entity: :saml_groups_path,
|
|
669
|
+
api: :@api_v5,
|
|
670
|
+
name: 'SAML group',
|
|
671
|
+
operations: CRUD_NO_SHOW,
|
|
672
|
+
lookup: :"lookup_#{res}_saml_groups_id",
|
|
673
|
+
items_key: 'groups'
|
|
674
|
+
end
|
|
675
|
+
end
|
|
676
|
+
|
|
677
|
+
commands_under %i[admin configuration] do
|
|
678
|
+
command :show, description: 'Show configuration', action: -> { Result::SingleObject.new(@api_v5.read('configuration')) }
|
|
679
|
+
command :modify, description: 'Modify configuration',
|
|
680
|
+
arguments: [{name: :input_data, type: Hash}],
|
|
681
|
+
action: ->(input_data:, **) { Result::SingleObject.new(@api_v5.update('configuration', input_data)) }
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
commands_under %i[admin smtp] do
|
|
685
|
+
command :show, description: 'Show SMTP configuration', action: -> { Result::SingleObject.new(@api_v5.read('configuration/smtp')) }
|
|
686
|
+
command :create, description: 'Create SMTP configuration',
|
|
687
|
+
arguments: [{name: :input_data, type: Hash}],
|
|
688
|
+
action: ->(input_data:, **) { Result::SingleObject.new(@api_v5.create('configuration/smtp', input_data)) }
|
|
689
|
+
command :modify, description: 'Modify SMTP configuration',
|
|
690
|
+
arguments: [{name: :input_data, type: Hash}],
|
|
691
|
+
action: ->(input_data:, **) { Result::SingleObject.new(@api_v5.update('configuration/smtp', input_data)) }
|
|
692
|
+
command(
|
|
693
|
+
:delete, description: 'Delete SMTP configuration',
|
|
694
|
+
action: lambda do
|
|
695
|
+
@api_v5.delete('configuration/smtp')
|
|
696
|
+
Result::Status.new('SMTP configuration deleted')
|
|
600
697
|
end
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
698
|
+
)
|
|
699
|
+
command :test, description: 'Test SMTP configuration',
|
|
700
|
+
arguments: [{name: :test_data, type: nil}]
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
commands_under %i[admin events] do
|
|
704
|
+
command(
|
|
705
|
+
:application, description: 'List application events',
|
|
706
|
+
action: lambda do
|
|
707
|
+
list, total = @api_v5.list_entities_limit_offset_total_count(entity: 'application_events', query: query_read_delete(schema: Schema::Registry.query_params(Schema::Registry::FASPEX, 'application_events')))
|
|
708
|
+
Result::ObjectList.new(list, total: total, fields: %w[event_type created_at application user.name])
|
|
609
709
|
end
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
return Result::SingleObject.new(@api_v5.read(smtp_path))
|
|
617
|
-
when :create
|
|
618
|
-
return Result::SingleObject.new(@api_v5.create(smtp_path, value_create_modify(command: smtp_cmd)))
|
|
619
|
-
when :modify
|
|
620
|
-
return Result::SingleObject.new(@api_v5.update(smtp_path, value_create_modify(command: smtp_cmd)))
|
|
621
|
-
when :delete
|
|
622
|
-
@api_v5.delete(smtp_path)
|
|
623
|
-
return Result::Status.new('SMTP configuration deleted')
|
|
624
|
-
when :test
|
|
625
|
-
test_data = options.get_next_argument('Email or test data, see API')
|
|
626
|
-
test_data = {test_email_recipient: test_data} if test_data.is_a?(String)
|
|
627
|
-
creation = @api_v5.create(File.join(smtp_path, 'test'), test_data)
|
|
628
|
-
result = wait_for_job(creation['job_id'])
|
|
629
|
-
result['serialized_args'] = JSON.parse(result['serialized_args']) rescue result['serialized_args']
|
|
630
|
-
return Result::SingleObject.new(result)
|
|
710
|
+
)
|
|
711
|
+
command(
|
|
712
|
+
:webhook, description: 'List webhook events',
|
|
713
|
+
action: lambda do
|
|
714
|
+
list, total = @api_v5.list_entities_limit_offset_total_count(entity: 'all_webhooks_events', query: query_read_delete(schema: Schema::Registry.query_params(Schema::Registry::FASPEX, 'all_webhooks_events')), items_key: 'events')
|
|
715
|
+
Result::ObjectList.new(list, total: total)
|
|
631
716
|
end
|
|
717
|
+
)
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
# admin > clean_deleted handler (leaf, no sub-commands)
|
|
721
|
+
define_action_method(%i[admin clean_deleted]) do |input_data: {}, **|
|
|
722
|
+
input_data = @api_v5.read('configuration').slice('days_before_deleting_package_records') if input_data.empty?
|
|
723
|
+
Result::SingleObject.new(@api_v5.create('internal/packages/clean_deleted', input_data))
|
|
724
|
+
end
|
|
725
|
+
|
|
726
|
+
# admin > <resource> > list
|
|
727
|
+
Api::Faspex::ADMIN_RESOURCES.each do |res|
|
|
728
|
+
define_action_method([:admin, res, :list]) do
|
|
729
|
+
args = res_exec_args(res)
|
|
730
|
+
# Special case: email_notifications list returns a fixed value list
|
|
731
|
+
next Result::ValueList.new(Api::Faspex::EMAIL_NOTIF_LIST, name: 'email_id') if res.eql?(:email_notifications)
|
|
732
|
+
data, total = args[:api].list_entities_limit_offset_total_count(
|
|
733
|
+
entity: args[:entity], items_key: args[:items_key],
|
|
734
|
+
query: query_read_delete(
|
|
735
|
+
default: args[:list_query],
|
|
736
|
+
schema: args[:query_component] ? Schema::Registry.query_params(args[:query_component], args[:entity]) : nil
|
|
737
|
+
)
|
|
738
|
+
)
|
|
739
|
+
Result::ObjectList.new(data, total: total, fields: args[:display_fields])
|
|
632
740
|
end
|
|
633
741
|
end
|
|
634
742
|
|
|
635
|
-
|
|
743
|
+
def action_admin_smtp_test(test_data:, **)
|
|
744
|
+
test_data = {test_email_recipient: test_data} if test_data.is_a?(String)
|
|
745
|
+
creation = @api_v5.create('configuration/smtp/test', test_data)
|
|
746
|
+
result = wait_for_job(creation['job_id'])
|
|
747
|
+
begin
|
|
748
|
+
result['serialized_args'] = JSON.parse(result['serialized_args'])
|
|
749
|
+
rescue JSON::ParserError
|
|
750
|
+
# keep as string if not valid JSON
|
|
751
|
+
end
|
|
752
|
+
Result::SingleObject.new(result)
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
# admin > accounts > reset_password
|
|
756
|
+
def action_admin_accounts_reset_password(contact_id:, **)
|
|
757
|
+
@api_v5.create("accounts/#{contact_id}/reset_password", {})
|
|
758
|
+
Result::Status.new('password reset, user shall check email')
|
|
759
|
+
end
|
|
636
760
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
761
|
+
# admin > file_processing > next
|
|
762
|
+
def action_admin_file_processing_next
|
|
763
|
+
args = res_exec_args(:file_processing)
|
|
764
|
+
result, count = @api_v5.list_entities_limit_offset_total_count(entity: args[:entity], operation: 'POST', items_key: 'files')
|
|
765
|
+
Result::ObjectList.new(result, total: count)
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
# admin > nodes > browse
|
|
769
|
+
def action_admin_nodes_browse(folder_path:, node_id:, **)
|
|
770
|
+
browse_folder("nodes/#{node_id}/browse", {}, folder_path: folder_path)
|
|
771
|
+
end
|
|
772
|
+
|
|
773
|
+
# admin > nodes > shared_folders — node_id: already in ctx via arguments: on the :shared_folders command
|
|
774
|
+
def setup_admin_nodes_shared_folders(node_id:, **)
|
|
775
|
+
{sf_entity: "nodes/#{node_id}/shared_folders"}
|
|
776
|
+
end
|
|
777
|
+
|
|
778
|
+
# Lookup shared folder id by field/value within a node's shared_folders entity.
|
|
779
|
+
# sf_entity is in ctx from setup_admin_nodes_shared_folders (Phase A of parent).
|
|
780
|
+
def lookup_sf_id(field, value, sf_entity:, **)
|
|
781
|
+
@api_v5.lookup_entity_by_field(entity: sf_entity, items_key: 'shared_folders', field: field, value: value)['id']
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
# Lookup custom access user id by field/value within a shared folder's users entity.
|
|
785
|
+
# user_path is in ctx from setup_admin_nodes_shared_folders_user (Phase A of parent).
|
|
786
|
+
def lookup_sf_user_id(field, value, user_path:, **)
|
|
787
|
+
@api_v5.lookup_entity_by_field(entity: user_path, items_key: 'users', field: field, value: value)['id']
|
|
788
|
+
end
|
|
789
|
+
|
|
790
|
+
# admin > nodes > shared_folders > user — sf_id: already in ctx via arguments: on the :user command
|
|
791
|
+
def setup_admin_nodes_shared_folders_user(sf_entity:, sf_id:, **)
|
|
792
|
+
{user_path: "#{sf_entity}/#{sf_id}/custom_access_users"}
|
|
793
|
+
end
|
|
794
|
+
|
|
795
|
+
# admin > shared_inboxes|workgroups — res_id: already in ctx via arguments: on the :members|:saml_groups command
|
|
796
|
+
%i[shared_inboxes workgroups].each do |res|
|
|
797
|
+
define_method(:"setup_admin_#{res}_instance") do |**kwargs|
|
|
798
|
+
res_id = kwargs[:"#{RES_SINGULAR[res]}_id"]
|
|
799
|
+
{
|
|
800
|
+
res_instance_path: "#{res}/#{res_id}",
|
|
801
|
+
saml_groups_path: "#{res}/#{res_id}/saml_groups"
|
|
802
|
+
}
|
|
644
803
|
end
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
end
|
|
656
|
-
nagios.add_ok('version', http['X-IBM-Aspera']) if http['X-IBM-Aspera']
|
|
657
|
-
rescue StandardError => e
|
|
658
|
-
nagios.add_critical('core', e.to_s)
|
|
659
|
-
end
|
|
660
|
-
Result::ObjectList.new(nagios.status_list)
|
|
661
|
-
when :user
|
|
662
|
-
case options.get_next_command(%i[account profile])
|
|
663
|
-
when :account
|
|
664
|
-
return Result::SingleObject.new(@api_v5.read('account', query_read_delete))
|
|
665
|
-
when :profile
|
|
666
|
-
case options.get_next_command(%i[show modify])
|
|
667
|
-
when :show
|
|
668
|
-
return Result::SingleObject.new(@api_v5.read('account/preferences'))
|
|
669
|
-
when :modify
|
|
670
|
-
@api_v5.update('account/preferences', options.get_next_argument('modified parameters', validation: Hash))
|
|
671
|
-
return Result::Status.new('modified')
|
|
672
|
-
end
|
|
673
|
-
end
|
|
674
|
-
when :bearer_token
|
|
675
|
-
return Result::Text.new(@api_v5.oauth.authorization)
|
|
676
|
-
when :packages
|
|
677
|
-
return package_action
|
|
678
|
-
when :shared_folders
|
|
679
|
-
all_shared_folders = @api_v5.read('shared_folders')['shared_folders']
|
|
680
|
-
case options.get_next_command(%i[list browse])
|
|
681
|
-
when :list
|
|
682
|
-
return Result::ObjectList.new(all_shared_folders)
|
|
683
|
-
when :browse
|
|
684
|
-
shared_folder_id = options.instance_identifier do |field, value|
|
|
685
|
-
matches = all_shared_folders.select{ |i| i[field].eql?(value)}
|
|
686
|
-
raise "no match for #{field} = #{value}" if matches.empty?
|
|
687
|
-
raise "multiple matches for #{field} = #{value}" if matches.length > 1
|
|
688
|
-
matches.first['id']
|
|
689
|
-
end
|
|
690
|
-
node = all_shared_folders.find{ |i| i['id'].eql?(shared_folder_id)}
|
|
691
|
-
raise "No such shared folder id #{shared_folder_id}" if node.nil?
|
|
692
|
-
return browse_folder("nodes/#{node['node_id']}/shared_folders/#{shared_folder_id}/browse")
|
|
693
|
-
end
|
|
694
|
-
when :admin
|
|
695
|
-
return execute_admin
|
|
696
|
-
when :invitations
|
|
697
|
-
invitation_endpoint = 'invitations'
|
|
698
|
-
invitation_command = options.get_next_command(%i[resend].concat(Operations::ALL))
|
|
699
|
-
case invitation_command
|
|
700
|
-
when :create
|
|
701
|
-
return do_bulk_operation(command: invitation_command, descr: 'data') do |params|
|
|
702
|
-
invitation_endpoint = params.key?('recipient_name') ? 'public_invitations' : 'invitations'
|
|
703
|
-
@api_v5.create(invitation_endpoint, params)
|
|
704
|
-
end
|
|
705
|
-
when :resend
|
|
706
|
-
@api_v5.create("#{invitation_endpoint}/#{options.instance_identifier}/resend", nil)
|
|
707
|
-
return Result::Status.new('Invitation resent')
|
|
804
|
+
end
|
|
805
|
+
|
|
806
|
+
# Resolve user ids for shared_inbox/workgroup members/create, handling percent selectors.
|
|
807
|
+
# @param users [Array] raw user ids or percent-selector strings
|
|
808
|
+
# @return [Array<String>] resolved user ids
|
|
809
|
+
def resolve_member_user_ids(users)
|
|
810
|
+
users = [users] unless users.is_a?(Array)
|
|
811
|
+
users.map do |user|
|
|
812
|
+
if (m = Parser.percent_selector(user))
|
|
813
|
+
@api_v5.lookup_entity_by_field(entity: 'accounts', field: m[:field], value: m[:value], query: Rest.php_style({type: ACCOUNT_TYPES}))['id']
|
|
708
814
|
else
|
|
709
|
-
|
|
710
|
-
api: @api_v5,
|
|
711
|
-
entity: invitation_endpoint,
|
|
712
|
-
command: invitation_command,
|
|
713
|
-
items_key: invitation_endpoint,
|
|
714
|
-
display_fields: %w[id public recipient_type recipient_name email_address]
|
|
715
|
-
) do |field, value|
|
|
716
|
-
@api_v5.lookup_entity_by_field(entity: invitation_endpoint, field: field, value: value, query: {})['id']
|
|
717
|
-
end
|
|
815
|
+
user
|
|
718
816
|
end
|
|
719
|
-
when :gateway
|
|
720
|
-
require 'aspera/faspex_gw'
|
|
721
|
-
parameters = value_create_modify(command: command, default: {}).symbolize_keys
|
|
722
|
-
uri = URI.parse(parameters.delete(:url){WebServerSimple::DEFAULT_URL})
|
|
723
|
-
server = WebServerSimple.new(uri, **parameters.slice(*WebServerSimple::PARAMS))
|
|
724
|
-
Aspera.assert(parameters.except(*WebServerSimple::PARAMS).empty?){"unexpected parameters: #{parameters.except(*WebServerSimple::PARAMS).keys}"}
|
|
725
|
-
server.mount(uri.path, Faspex4GWServlet, @api_v5, nil)
|
|
726
|
-
server.start
|
|
727
|
-
return Result::Status.new('Gateway terminated')
|
|
728
|
-
when :postprocessing
|
|
729
|
-
require 'aspera/faspex_postproc' # cspell:disable-line
|
|
730
|
-
parameters = value_create_modify(command: command, default: {}).symbolize_keys
|
|
731
|
-
uri = URI.parse(parameters.delete(:url){WebServerSimple::DEFAULT_URL})
|
|
732
|
-
parameters[:root] = uri.path
|
|
733
|
-
server = WebServerSimple.new(uri, **parameters.slice(*WebServerSimple::PARAMS))
|
|
734
|
-
server.mount(uri.path, Faspex4PostProcServlet, parameters.except(*WebServerSimple::PARAMS))
|
|
735
|
-
server.start
|
|
736
|
-
return Result::Status.new('Gateway terminated')
|
|
737
817
|
end
|
|
738
818
|
end
|
|
819
|
+
|
|
820
|
+
# Lookup methods for members/saml_groups identity resolution (used by arguments: lookup:)
|
|
821
|
+
# res_instance_path is in ctx from setup_admin_{res}_instance (Phase A of parent).
|
|
822
|
+
%i[shared_inboxes workgroups].each do |res|
|
|
823
|
+
define_method(:"lookup_#{res}_members_id") do |field, value, res_instance_path:, **|
|
|
824
|
+
res_path = "#{res_instance_path}/members"
|
|
825
|
+
@api_v5.lookup_entity_by_field(entity: res_path, field: field, value: value, query: Rest.php_style({type: %w[user]}))['user_id']
|
|
826
|
+
end
|
|
827
|
+
define_method(:"lookup_#{res}_saml_groups_id") do |field, value, res_instance_path:, **|
|
|
828
|
+
res_path = "#{res_instance_path}/saml_groups"
|
|
829
|
+
@api_v5.lookup_entity_by_field(entity: res_path, field: field, value: value, query: Rest.php_style({type: %w[user]}))['user_id']
|
|
830
|
+
end
|
|
831
|
+
end
|
|
832
|
+
|
|
833
|
+
# admin > shared_inboxes|workgroups > members > create/list/modify/delete
|
|
834
|
+
# (saml_groups CRUD is handled by crud_commands)
|
|
835
|
+
%i[shared_inboxes workgroups].each do |res|
|
|
836
|
+
define_action_method([:admin, res, :members, :list]) do |res_instance_path:, **|
|
|
837
|
+
entity_list(api: @api_v5, entity: "#{res_instance_path}/members", items_key: 'members')
|
|
838
|
+
end
|
|
839
|
+
|
|
840
|
+
define_action_method([:admin, res, :members, :modify]) do |res_instance_path:, **kwargs|
|
|
841
|
+
entity_modify(api: @api_v5, entity: "#{res_instance_path}/members", id: kwargs[:member_id])
|
|
842
|
+
end
|
|
843
|
+
|
|
844
|
+
define_action_method([:admin, res, :members, :delete]) do |res_instance_path:, **kwargs|
|
|
845
|
+
entity_delete(api: @api_v5, entity: "#{res_instance_path}/members", id: kwargs[:member_id])
|
|
846
|
+
end
|
|
847
|
+
|
|
848
|
+
define_action_method([:admin, res, :members, :create]) do |users:, access:, res_instance_path:, **|
|
|
849
|
+
res_path = "#{res_instance_path}/members"
|
|
850
|
+
resolved = resolve_member_user_ids(users)
|
|
851
|
+
input_data = [{user: resolved.map { |u| {id: u, access: access} }}]
|
|
852
|
+
entity_create(api: @api_v5, entity: res_path, input_data: input_data)
|
|
853
|
+
end
|
|
854
|
+
end
|
|
855
|
+
|
|
856
|
+
# admin > shared_inboxes|workgroups > invite_external_collaborator
|
|
857
|
+
%i[shared_inboxes workgroups].each do |res|
|
|
858
|
+
define_action_method([:admin, res, :invite_external_collaborator]) do |input_data:, **kwargs|
|
|
859
|
+
res_instance_path = "#{res}/#{kwargs[:"#{RES_SINGULAR[res]}_id"]}"
|
|
860
|
+
result = @api_v5.create("#{res_instance_path}/external_collaborator", input_data)
|
|
861
|
+
formatter.display_status(result['message'])
|
|
862
|
+
Result::SingleObject.new(@api_v5.lookup_entity_by_field(entity: "#{res_instance_path}/members", items_key: 'members', value: input_data['email_address'], query: {}))
|
|
863
|
+
end
|
|
864
|
+
end
|
|
865
|
+
|
|
866
|
+
# --- root setup ---
|
|
867
|
+
|
|
868
|
+
# Build @api_v5 for all commands that need it (all except :health and :postprocessing).
|
|
869
|
+
# @return [Hash] empty ctx (state stored in @api_v5)
|
|
870
|
+
def setup_api_v5(**)
|
|
871
|
+
return {} if @api_v5
|
|
872
|
+
@api_v5 = Api::Faspex.new(**Oauth.kwargs_from_options(options))
|
|
873
|
+
# in case user wants to use HTTPGW tell transfer agent how to get address
|
|
874
|
+
transfer.httpgw_url_cb = lambda { @api_v5.read('account')['gateway_url'] }
|
|
875
|
+
{}
|
|
876
|
+
end
|
|
877
|
+
|
|
878
|
+
# Setup for package sub-commands that need an id: resolves package_id from pub_link or argument.
|
|
879
|
+
# @return [Hash] ctx key: package_id
|
|
880
|
+
def setup_package_id(**)
|
|
881
|
+
package_id = @api_v5.pub_link_context&.key?('package_id') ? @api_v5.pub_link_context['package_id'] : options.instance_identifier
|
|
882
|
+
{package_id: package_id}
|
|
883
|
+
end
|
|
884
|
+
|
|
885
|
+
def action_packages_list(filter: nil, **)
|
|
886
|
+
list, max_items, total = list_packages_with_filter(filter: filter)
|
|
887
|
+
list = list[0, max_items] if max_items
|
|
888
|
+
fields = %w[id title status sender.name recipients.0.name release_date total_bytes total_files]
|
|
889
|
+
fields.delete('recipients.0.name') if %w[inbox inbox_history].include?(options.get_option(:box))
|
|
890
|
+
fields.delete('sender.name') if %w[outbox outbox_history].include?(options.get_option(:box))
|
|
891
|
+
Result::ObjectList.new(list, total: total, fields: fields)
|
|
892
|
+
end
|
|
893
|
+
|
|
894
|
+
def action_packages_delete(package_id:, **)
|
|
895
|
+
ids = package_id.is_a?(Array) ? package_id : [package_id]
|
|
896
|
+
Aspera.assert_array_all(ids, String) { 'Package id(s)' }
|
|
897
|
+
# API returns 204, empty on success
|
|
898
|
+
@api_v5.call(
|
|
899
|
+
operation: 'DELETE',
|
|
900
|
+
subpath: 'packages',
|
|
901
|
+
content_type: Mime::JSON,
|
|
902
|
+
body: {ids: ids},
|
|
903
|
+
headers: {'Accept' => Mime::JSON}
|
|
904
|
+
)
|
|
905
|
+
Result::Status.new('Package(s) deleted')
|
|
906
|
+
end
|
|
907
|
+
|
|
908
|
+
# --- handlers ---
|
|
909
|
+
|
|
910
|
+
def action_health
|
|
911
|
+
nagios = Nagios.new
|
|
912
|
+
begin
|
|
913
|
+
data, http = Rest.new(base_url: options.get_option(:url, mandatory: true))
|
|
914
|
+
.read('health', ret: :both)
|
|
915
|
+
data.each do |k, v|
|
|
916
|
+
nagios.add_ok(k, v.to_s)
|
|
917
|
+
end
|
|
918
|
+
nagios.add_ok('version', http['X-IBM-Aspera']) if http['X-IBM-Aspera']
|
|
919
|
+
rescue StandardError => e
|
|
920
|
+
nagios.add_critical('core', e.to_s)
|
|
921
|
+
end
|
|
922
|
+
Result::ObjectList.new(nagios.status_list)
|
|
923
|
+
end
|
|
924
|
+
|
|
925
|
+
# Lookup a shared folder id by field/value.
|
|
926
|
+
# Called via lookup: :lookup_shared_folder_id on the shared_folders > browse command.
|
|
927
|
+
def lookup_shared_folder_id(field, value, **)
|
|
928
|
+
all = @api_v5.read('shared_folders')['shared_folders']
|
|
929
|
+
matches = all.select { |i| i[field].eql?(value) }
|
|
930
|
+
Aspera.assert(!matches.empty?) { "no match for #{field} = #{value}" }
|
|
931
|
+
Aspera.assert(matches.length == 1) { "multiple matches for #{field} = #{value}" }
|
|
932
|
+
matches.first['id']
|
|
933
|
+
end
|
|
934
|
+
|
|
935
|
+
def action_shared_folders_browse(folder_path:, shared_folder_id:, **)
|
|
936
|
+
all_shared_folders = @api_v5.read('shared_folders')['shared_folders']
|
|
937
|
+
node = all_shared_folders.find { |i| i['id'].eql?(shared_folder_id) }
|
|
938
|
+
Aspera.assert(!node.nil?) { "No such shared folder id #{shared_folder_id}" }
|
|
939
|
+
browse_folder("nodes/#{node['node_id']}/shared_folders/#{shared_folder_id}/browse", {}, folder_path: folder_path)
|
|
940
|
+
end
|
|
941
|
+
|
|
942
|
+
# invitations sub-handlers
|
|
943
|
+
|
|
944
|
+
def action_invitations_resend(invitation_id:, **)
|
|
945
|
+
@api_v5.create("invitations/#{invitation_id}/resend", nil)
|
|
946
|
+
Result::Status.new('Invitation resent')
|
|
947
|
+
end
|
|
948
|
+
|
|
949
|
+
def action_invitations_create(input_data:, **)
|
|
950
|
+
bulk_result(input_data, command: :create) do |params|
|
|
951
|
+
endpoint = params.key?('recipient_name') ? 'public_invitations' : 'invitations'
|
|
952
|
+
@api_v5.create(endpoint, params)
|
|
953
|
+
end
|
|
954
|
+
end
|
|
955
|
+
|
|
956
|
+
def action_gateway(parameters: {}, **)
|
|
957
|
+
require 'aspera/faspex_gw'
|
|
958
|
+
parameters = parameters.symbolize_keys
|
|
959
|
+
uri = URI.parse(parameters.delete(:url) { WebServerSimple::DEFAULT_URL })
|
|
960
|
+
server = WebServerSimple.new(uri, **parameters.slice(*WebServerSimple::PARAMS))
|
|
961
|
+
Aspera.assert(parameters.except(*WebServerSimple::PARAMS).empty?) { "unexpected parameters: #{parameters.except(*WebServerSimple::PARAMS).keys}" }
|
|
962
|
+
server.mount(uri.path, Faspex4GWServlet, @api_v5, nil)
|
|
963
|
+
server.start
|
|
964
|
+
Result::Status.new('Gateway terminated')
|
|
965
|
+
end
|
|
966
|
+
|
|
967
|
+
def action_postprocessing(parameters: {}, **)
|
|
968
|
+
require 'aspera/faspex_postproc' # cspell:disable-line
|
|
969
|
+
parameters = parameters.symbolize_keys
|
|
970
|
+
uri = URI.parse(parameters.delete(:url) { WebServerSimple::DEFAULT_URL })
|
|
971
|
+
parameters[:root] = uri.path
|
|
972
|
+
server = WebServerSimple.new(uri, **parameters.slice(*WebServerSimple::PARAMS))
|
|
973
|
+
server.mount(uri.path, Faspex4PostProcServlet, parameters.except(*WebServerSimple::PARAMS))
|
|
974
|
+
server.start
|
|
975
|
+
Result::Status.new('Gateway terminated')
|
|
976
|
+
end
|
|
739
977
|
SHARED_INBOX_MEMBER_LEVELS = %i[submit_only standard shared_inbox_admin].freeze
|
|
740
978
|
ACCOUNT_TYPES = %w{local_user saml_user self_registered_user external_user}.freeze
|
|
741
979
|
WORKGROUP_TYPES = %w{workgroup shared_inbox}.freeze
|
|
742
980
|
CONTACT_TYPES = (WORKGROUP_TYPES + %w{distribution_list user external_user}).freeze
|
|
743
981
|
PACKAGE_RECIPIENT_TYPES = %i{recipients private_recipients notified_on_upload notified_on_download notified_on_receipt}
|
|
744
|
-
private_constant :SHARED_INBOX_MEMBER_LEVELS, :ACCOUNT_TYPES, :CONTACT_TYPES, :PACKAGE_RECIPIENT_TYPES
|
|
982
|
+
private_constant :SHARED_INBOX_MEMBER_LEVELS, :ACCOUNT_TYPES, :CONTACT_TYPES, :PACKAGE_RECIPIENT_TYPES,
|
|
983
|
+
:CRUD_NO_SHOW, :CRUD_NO_LIST
|
|
745
984
|
end
|
|
746
985
|
end
|
|
747
986
|
end
|