aspera-cli 4.27.2 → 4.27.4
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 +133 -0
- data/CONTRIBUTING.md +5 -2
- data/bin/ascli +3 -1
- data/docs/README.md +1006 -766
- data/lib/aspera/agent/base.rb +7 -2
- data/lib/aspera/agent/connect.rb +6 -8
- data/lib/aspera/agent/desktop.rb +2 -6
- data/lib/aspera/agent/direct.rb +52 -22
- data/lib/aspera/agent/node.rb +9 -8
- data/lib/aspera/agent/transferd.rb +2 -2
- data/lib/aspera/api/alee.rb +1 -1
- data/lib/aspera/api/aoc.rb +14 -12
- data/lib/aspera/api/ats.rb +1 -1
- data/lib/aspera/api/cos_node.rb +2 -2
- data/lib/aspera/api/faspex.rb +11 -7
- data/lib/aspera/api/httpgw.rb +38 -35
- data/lib/aspera/api/node.rb +39 -34
- data/lib/aspera/ascmd.rb +3 -1
- data/lib/aspera/ascp/installation.rb +63 -28
- data/lib/aspera/ascp/management.rb +1 -0
- data/lib/aspera/assert.rb +4 -0
- data/lib/aspera/cli/ascp_actions.rb +20 -41
- data/lib/aspera/cli/async_transfer_store.rb +12 -11
- data/lib/aspera/cli/bootstrapper.rb +14 -16
- data/lib/aspera/cli/command_line.rb +252 -0
- data/lib/aspera/cli/command_registry.rb +215 -37
- data/lib/aspera/cli/command_spec.rb +104 -15
- data/lib/aspera/cli/completion/ascli.bash +12 -0
- data/lib/aspera/cli/completion/ascli.fish +16 -0
- data/lib/aspera/cli/completion/ascli.zsh +19 -0
- data/lib/aspera/cli/context.rb +3 -0
- data/lib/aspera/cli/deprecation.rb +37 -0
- data/lib/aspera/cli/extended_value.rb +6 -3
- data/lib/aspera/cli/formatter.rb +94 -80
- data/lib/aspera/cli/gem_checker.rb +1 -1
- data/lib/aspera/cli/hints.rb +7 -6
- data/lib/aspera/cli/http.rb +22 -34
- data/lib/aspera/cli/info.rb +3 -0
- data/lib/aspera/cli/mcp_tool.rb +47 -83
- data/lib/aspera/cli/option_declarator.rb +33 -42
- data/lib/aspera/cli/option_registry.rb +69 -0
- data/lib/aspera/cli/option_types.rb +105 -0
- data/lib/aspera/cli/option_value.rb +278 -0
- data/lib/aspera/cli/options.schema.yaml +124 -15
- data/lib/aspera/cli/parser.rb +333 -862
- data/lib/aspera/cli/plugins/alee.rb +7 -4
- data/lib/aspera/cli/plugins/aoc.rb +545 -518
- data/lib/aspera/cli/plugins/ats.rb +59 -80
- data/lib/aspera/cli/plugins/base.rb +221 -265
- data/lib/aspera/cli/plugins/basic_auth.rb +2 -10
- data/lib/aspera/cli/plugins/config.rb +263 -184
- data/lib/aspera/cli/plugins/console.rb +103 -39
- data/lib/aspera/cli/plugins/cos.rb +6 -23
- data/lib/aspera/cli/plugins/factory.rb +3 -0
- data/lib/aspera/cli/plugins/faspex5.rb +204 -182
- data/lib/aspera/cli/plugins/faspio.rb +6 -11
- data/lib/aspera/cli/plugins/httpgw.rb +8 -11
- data/lib/aspera/cli/plugins/mcp.rb +20 -55
- data/lib/aspera/cli/plugins/node.rb +300 -327
- data/lib/aspera/cli/plugins/orchestrator.rb +152 -110
- data/lib/aspera/cli/plugins/preview.rb +96 -105
- data/lib/aspera/cli/plugins/server.rb +78 -53
- data/lib/aspera/cli/plugins/shares.rb +80 -131
- data/lib/aspera/cli/preset_actions.rb +44 -27
- data/lib/aspera/cli/preset_manager.rb +44 -19
- data/lib/aspera/cli/prompt.rb +36 -0
- data/lib/aspera/cli/result.rb +42 -36
- data/lib/aspera/cli/runner.rb +32 -59
- data/lib/aspera/cli/special_values.rb +5 -0
- data/lib/aspera/cli/sync_actions.rb +51 -46
- data/lib/aspera/cli/terminal_formatter.rb +9 -3
- data/lib/aspera/cli/transfer_actions.rb +14 -9
- data/lib/aspera/cli/transfer_agent.rb +34 -38
- data/lib/aspera/cli/transfer_progress.rb +290 -55
- data/lib/aspera/cli/vault_manager.rb +0 -17
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +5 -3
- data/lib/aspera/coverage.rb +1 -1
- data/lib/aspera/environment.rb +35 -5
- data/lib/aspera/faspex_gw.rb +2 -1
- data/lib/aspera/faspex_postproc.rb +1 -0
- data/lib/aspera/graphql.rb +5 -5
- data/lib/aspera/json_rpc/client.rb +5 -5
- data/lib/aspera/keychain/encrypted_hash.rb +2 -2
- data/lib/aspera/keychain/factory.rb +2 -1
- data/lib/aspera/keychain/one_password_api.rb +1 -1
- data/lib/aspera/link_header.rb +2 -2
- data/lib/aspera/log.rb +47 -27
- data/lib/aspera/markdown.rb +2 -0
- data/lib/aspera/mime.rb +25 -0
- data/lib/aspera/node_emulator.rb +759 -0
- data/lib/aspera/oauth/base.rb +37 -26
- data/lib/aspera/oauth/factory.rb +7 -3
- data/lib/aspera/oauth/generic.rb +1 -1
- data/lib/aspera/oauth/json_credentials.rb +34 -0
- data/lib/aspera/oauth/jwt.rb +4 -5
- data/lib/aspera/oauth/web.rb +9 -8
- data/lib/aspera/oauth.rb +1 -0
- data/lib/aspera/persistency_folder.rb +1 -3
- data/lib/aspera/preview/file_types.rb +4 -4
- data/lib/aspera/preview/generator.rb +11 -1
- data/lib/aspera/preview/options.schema.yaml +119 -0
- data/lib/aspera/preview/terminal.rb +4 -3
- data/lib/aspera/preview/utils.rb +9 -6
- data/lib/aspera/products/connect.rb +1 -1
- data/lib/aspera/rainbow.rb +7 -0
- data/lib/aspera/rest/aspera_errors.rb +72 -0
- data/lib/aspera/rest/call_error.rb +27 -0
- data/lib/aspera/rest/client.rb +523 -0
- data/lib/aspera/rest/error_analyzer.rb +113 -0
- data/lib/aspera/rest/list.rb +149 -0
- data/lib/aspera/rest/parameters.rb +55 -0
- data/lib/aspera/rest/util.rb +176 -0
- data/lib/aspera/rest.rb +7 -621
- data/lib/aspera/schema/IBM Aspera Console-enhanced.yaml +1125 -0
- data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +0 -20
- data/lib/aspera/schema/IBM Aspera Orchestrator API-v1.yaml +1784 -0
- data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +1230 -137
- data/lib/aspera/schema/IBM Aspera on Cloud Automation API-1.0.5-enhanced.yaml +2395 -0
- data/lib/aspera/schema/IBM_Aspera_Shares.yaml +14 -13
- data/lib/aspera/schema/documentation.rb +13 -3
- data/lib/aspera/schema/reader.rb +12 -18
- data/lib/aspera/schema/registry.rb +23 -1
- data/lib/aspera/schema/validator.rb +92 -0
- data/lib/aspera/secret_hider.rb +36 -25
- data/lib/aspera/string_ext.rb +15 -0
- data/lib/aspera/temp_file_manager.rb +6 -5
- data/lib/aspera/transfer/parameters.rb +2 -0
- data/lib/aspera/transfer/spec.rb +1 -0
- data/lib/aspera/uri_reader.rb +11 -11
- data/lib/aspera/web_auth/index.html +147 -0
- data/lib/aspera/web_auth/server.rb +81 -0
- data.tar.gz.sig +0 -0
- metadata +43 -9
- metadata.gz.sig +0 -0
- data/lib/aspera/colors.rb +0 -79
- data/lib/aspera/node_simulator.rb +0 -344
- data/lib/aspera/preview/options.rb +0 -45
- data/lib/aspera/rest_call_error.rb +0 -25
- data/lib/aspera/rest_error_analyzer.rb +0 -111
- data/lib/aspera/rest_errors_aspera.rb +0 -58
- data/lib/aspera/rest_list.rb +0 -136
- data/lib/aspera/web_auth.rb +0 -211
|
@@ -18,6 +18,8 @@ require 'aspera/assert'
|
|
|
18
18
|
require 'aspera/graphql'
|
|
19
19
|
require 'securerandom'
|
|
20
20
|
require 'date'
|
|
21
|
+
require 'aspera/rainbow'
|
|
22
|
+
using Rainbow
|
|
21
23
|
|
|
22
24
|
module Aspera
|
|
23
25
|
module Cli
|
|
@@ -70,7 +72,7 @@ module Aspera
|
|
|
70
72
|
base_url = "#{base_url}.#{Api::AoC::SAAS_DOMAIN_PROD}" unless base_url.include?('.')
|
|
71
73
|
# AoC is only https
|
|
72
74
|
return unless base_url.start_with?('https://')
|
|
73
|
-
location = Rest.new(base_url: base_url, redirect_max: 0).call(operation: 'GET', subpath: 'auth/ping', exception: false, ret: :resp)['Location']
|
|
75
|
+
location = Rest::Client.new(base_url: base_url, redirect_max: 0).call(operation: 'GET', subpath: 'auth/ping', exception: false, ret: :resp)['Location']
|
|
74
76
|
return if location.nil?
|
|
75
77
|
redirect_uri = URI.parse(location)
|
|
76
78
|
od = Api::AoC.split_org_domain(URI.parse(base_url))
|
|
@@ -138,45 +140,47 @@ module Aspera
|
|
|
138
140
|
}
|
|
139
141
|
end
|
|
140
142
|
|
|
141
|
-
#
|
|
142
|
-
#
|
|
143
|
-
# @
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
143
|
+
# Optional `Hash` argument of short link create and modify, merged into the request body
|
|
144
|
+
# @param endpoint [String] endpoint of the request body schema, e.g. `short_links.post`
|
|
145
|
+
# @return [Hash] argument declaration
|
|
146
|
+
def short_link_argument(endpoint)
|
|
147
|
+
{
|
|
148
|
+
name: :short_link, type: Hash, mandatory: false, default: {},
|
|
149
|
+
description: 'Merged into the request body, and: `password` (link password), `access_levels` (shared folder only)',
|
|
150
|
+
schema: Schema::Registry.req_body(Schema::Registry::AOC, endpoint)
|
|
151
|
+
}
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# DSL class method: declare the 5 short link leaf commands under the given node.
|
|
155
|
+
# The node itself is declared by the caller, with its arguments and setup.
|
|
156
|
+
# @param parent [Symbol, Array<Symbol>] path relative to current scope, e.g. :short_link
|
|
157
|
+
def short_link_commands(parent)
|
|
158
|
+
commands_under(parent) do
|
|
159
|
+
command(
|
|
160
|
+
:create, description: operation_description(:create, 'short link'),
|
|
161
|
+
arguments: [short_link_argument('short_links.post')],
|
|
162
|
+
action: ->(short_link: {}, **ctx) { short_link_create(short_link, **ctx) }
|
|
163
|
+
)
|
|
164
|
+
command(
|
|
165
|
+
:modify, description: operation_description(:modify, 'short link'),
|
|
166
|
+
arguments: [{name: :short_link_id, type: :identifier}, short_link_argument('short_links/{id}.put')],
|
|
167
|
+
action: ->(short_link: {}, **ctx) { short_link_modify(short_link, **short_link_fetch_list(**ctx), **ctx) }
|
|
150
168
|
)
|
|
151
|
-
|
|
152
|
-
:
|
|
153
|
-
|
|
169
|
+
command(
|
|
170
|
+
:list, description: operation_description(:list, 'short link'), query_schema: SHORT_LINK_QUERY_SCHEMA,
|
|
171
|
+
action: ->(**ctx) { short_link_list(**short_link_fetch_list(**ctx)) }
|
|
154
172
|
)
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
:
|
|
158
|
-
|
|
173
|
+
command(
|
|
174
|
+
:show, description: operation_description(:show, 'short link'),
|
|
175
|
+
arguments: [{name: :short_link_id, type: :identifier}],
|
|
176
|
+
action: ->(**ctx) { short_link_show(**short_link_fetch_list(**ctx), **ctx) }
|
|
159
177
|
)
|
|
160
|
-
|
|
161
|
-
:delete, description: '
|
|
162
|
-
arguments: [{name: :short_link_id, type: :identifier}]
|
|
178
|
+
command(
|
|
179
|
+
:delete, description: operation_description(:delete, 'short link'),
|
|
180
|
+
arguments: [{name: :short_link_id, type: :identifier}],
|
|
181
|
+
action: ->(**ctx) { short_link_delete(**short_link_fetch_list(**ctx), **ctx) }
|
|
163
182
|
)
|
|
164
183
|
end
|
|
165
|
-
base.define_action_method(parent_path + [:create]) do |custom_data: {}, **ctx|
|
|
166
|
-
sl_exec_create(custom_data, **ctx)
|
|
167
|
-
end
|
|
168
|
-
base.define_action_method(parent_path + [:list]) do |**ctx|
|
|
169
|
-
sl_exec_list(**sl_fetch_list(**ctx))
|
|
170
|
-
end
|
|
171
|
-
base.define_action_method(parent_path + [:show]) do |**ctx|
|
|
172
|
-
sl_exec_show(**sl_fetch_list(**ctx))
|
|
173
|
-
end
|
|
174
|
-
base.define_action_method(parent_path + [:delete]) do |**ctx|
|
|
175
|
-
sl_exec_delete(**sl_fetch_list(**ctx), **ctx)
|
|
176
|
-
end
|
|
177
|
-
base.define_action_method(parent_path + [:modify]) do |custom_data: {}, **ctx|
|
|
178
|
-
sl_exec_modify(custom_data, **sl_fetch_list(**ctx), **ctx)
|
|
179
|
-
end
|
|
180
184
|
end
|
|
181
185
|
end
|
|
182
186
|
|
|
@@ -187,7 +191,7 @@ module Aspera
|
|
|
187
191
|
pub_link_info = Api::AoC.link_info(app_url)
|
|
188
192
|
# public link case
|
|
189
193
|
if pub_link_info.key?(:token)
|
|
190
|
-
pub_api = Rest.new(base_url: "https://#{URI.parse(pub_link_info[:url]).host}/api/v1")
|
|
194
|
+
pub_api = Rest::Client.new(base_url: "https://#{URI.parse(pub_link_info[:url]).host}/api/v1")
|
|
191
195
|
pub_info = pub_api.read('env/url_token_check', {token: pub_link_info[:token]})
|
|
192
196
|
preset_value = {
|
|
193
197
|
link: app_url
|
|
@@ -198,8 +202,6 @@ module Aspera
|
|
|
198
202
|
test_args: 'organization'
|
|
199
203
|
}
|
|
200
204
|
end
|
|
201
|
-
options.declare(:use_generic_client, description: 'Wizard: AoC: use global or org specific jwt client id', allowed: Type::BOOLEAN, default: Api::AoC.saas_url?(app_url))
|
|
202
|
-
options.parse_options!
|
|
203
205
|
# make username mandatory for jwt, this triggers interactive input
|
|
204
206
|
wiz_username = options.get_option(:username, mandatory: true)
|
|
205
207
|
wizard.check_email(wiz_username)
|
|
@@ -216,7 +218,10 @@ module Aspera
|
|
|
216
218
|
client_id = options.get_option(:client_id)
|
|
217
219
|
client_secret = options.get_option(:client_secret)
|
|
218
220
|
if client_id.nil? || client_secret.nil?
|
|
219
|
-
|
|
221
|
+
use_generic_client = options.get_option(:use_generic_client)
|
|
222
|
+
# Default depends on URL
|
|
223
|
+
use_generic_client = Api::AoC.saas_url?(app_url) if use_generic_client.nil?
|
|
224
|
+
if use_generic_client
|
|
220
225
|
client_id = client_secret = nil
|
|
221
226
|
formatter.display_status('Using global client_id.')
|
|
222
227
|
else
|
|
@@ -270,6 +275,7 @@ module Aspera
|
|
|
270
275
|
option :new_user_option, description: 'New user creation option for unknown package recipients', allowed: [Hash, NilClass]
|
|
271
276
|
option :validate_metadata, description: 'Validate shared inbox metadata', allowed: Type::BOOLEAN, default: true
|
|
272
277
|
option :package_folder, schema: Schema::Registry::PACKAGE_FOLDER_OPTIONS
|
|
278
|
+
option :use_generic_client, description: 'Wizard: AoC: use global or org specific jwt client id (default: global for SaaS URL)', allowed: Type::BOOLEAN
|
|
273
279
|
|
|
274
280
|
use_options Node
|
|
275
281
|
|
|
@@ -279,7 +285,6 @@ module Aspera
|
|
|
279
285
|
@cache_home_node_file = nil
|
|
280
286
|
@cache_api_aoc = nil
|
|
281
287
|
@scope = Api::AoC::Scope::USER
|
|
282
|
-
options.parse_options!
|
|
283
288
|
end
|
|
284
289
|
|
|
285
290
|
# Change API scope for subsequent calls, re-instantiate API object
|
|
@@ -293,7 +298,7 @@ module Aspera
|
|
|
293
298
|
|
|
294
299
|
# Create an API object with the options from CLI, but with a different subpath
|
|
295
300
|
# @param base_path [String] Base path for APIs.
|
|
296
|
-
# @return [Api::AoC] API object for AoC (is Rest)
|
|
301
|
+
# @return [Api::AoC] API object for AoC (is Rest::Client)
|
|
297
302
|
def api_from_options(base_path)
|
|
298
303
|
# Get all existing OAuth kwargs from `options`.
|
|
299
304
|
api = Api::AoC.new(
|
|
@@ -318,7 +323,7 @@ module Aspera
|
|
|
318
323
|
end
|
|
319
324
|
|
|
320
325
|
# AoC Rest object
|
|
321
|
-
# @return [Api::AoC] API object for AoC (is Rest)
|
|
326
|
+
# @return [Api::AoC] API object for AoC (is Rest::Client)
|
|
322
327
|
def aoc_api
|
|
323
328
|
if @cache_api_aoc.nil?
|
|
324
329
|
@cache_api_aoc = api_from_options(Api::AoC::API_V1)
|
|
@@ -337,8 +342,8 @@ module Aspera
|
|
|
337
342
|
# @param name [Boolean] Include name
|
|
338
343
|
# @return [Hash{Symbol, String => String}] the modified hash containing:
|
|
339
344
|
# * `workspace_id` [String] the unique identifier.
|
|
340
|
-
# * `workspace_name` [String] (optional) the name, included if
|
|
341
|
-
# @note The key type (String or Symbol) depends on the
|
|
345
|
+
# * `workspace_name` [String] (optional) the name, included if `name` is true.
|
|
346
|
+
# @note The key type (String or Symbol) depends on the `string` parameter.
|
|
342
347
|
def workspace_id_hash(hash = nil, string: false, name: false)
|
|
343
348
|
info = aoc_api.workspace_info
|
|
344
349
|
hash = {} if hash.nil?
|
|
@@ -352,21 +357,6 @@ module Aspera
|
|
|
352
357
|
return hash
|
|
353
358
|
end
|
|
354
359
|
|
|
355
|
-
# Get resource identifier from command line, either directly specifying the `id` or from `name` (percent selector).
|
|
356
|
-
# @param resource_class_path [String] url path for resource
|
|
357
|
-
# @return [String] identifier
|
|
358
|
-
def get_resource_id_from_args(resource_class_path)
|
|
359
|
-
return options.instance_identifier do |field, value|
|
|
360
|
-
Aspera.assert_values(field, ['name'], type: BadArgument) { 'selector field' }
|
|
361
|
-
aoc_api.lookup_with_q(resource_class_path, value: value)['id']
|
|
362
|
-
end
|
|
363
|
-
end
|
|
364
|
-
|
|
365
|
-
# Get resource path from command line
|
|
366
|
-
def get_resource_path_from_args(resource_class_path)
|
|
367
|
-
return "#{resource_class_path}/#{get_resource_id_from_args(resource_class_path)}"
|
|
368
|
-
end
|
|
369
|
-
|
|
370
360
|
# List all entities, given additional, default and user's queries
|
|
371
361
|
# @param resource_class_path [String] path to query on API
|
|
372
362
|
# @param fields [Array, nil] fields to display
|
|
@@ -409,87 +399,95 @@ module Aspera
|
|
|
409
399
|
PACKAGE_RECEIVED_BASE_QUERY.each { |k, v| query[k] = v unless query.key?(k) }
|
|
410
400
|
resolve_dropbox_name_default_ws_id(query)
|
|
411
401
|
# Extract `max` before paging so callers can apply it after post-API filtering
|
|
412
|
-
max_items = query.delete(
|
|
402
|
+
max_items = query.delete(Rest::List::MAX_ITEMS)&.to_i
|
|
413
403
|
return aoc_api.read_with_paging('packages', query.compact), max_items
|
|
414
404
|
end
|
|
415
405
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
#
|
|
419
|
-
|
|
420
|
-
#
|
|
421
|
-
|
|
422
|
-
#
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
#
|
|
428
|
-
|
|
429
|
-
|
|
406
|
+
# Arguments of the node-to-node `transfer` command (files, admin node do, shared folder node)
|
|
407
|
+
TRANSFER_ARGS = [{name: :direction, allowed: %i[push pull]}, {name: :source_folder}].freeze
|
|
408
|
+
# Mount of the Node plugin Gen4 commands (`node access_keys do <id> ...`), instance: set per mount point
|
|
409
|
+
NODE_GEN4_MOUNT = {plugin: Node, at: %i[access_keys do]}.freeze
|
|
410
|
+
# Package identifier argument, `%name:` selector (lookup method shared with `admin package`)
|
|
411
|
+
PACKAGE_ID_ARG = {name: :package_id, type: :identifier, lookup: :lookup_aoc_package_id}.freeze
|
|
412
|
+
# Resource of analytics events (`admin analytics transfers|files`)
|
|
413
|
+
EVENT_RESOURCE_TYPE_ARG = {name: :event_resource_type, allowed: %i[organizations users nodes]}.freeze
|
|
414
|
+
EVENT_RESOURCE_ID_ARG = {name: :event_resource_id, description: 'Resource identifier, empty for the one of the current user'}.freeze
|
|
415
|
+
private_constant :TRANSFER_ARGS, :NODE_GEN4_MOUNT, :PACKAGE_ID_ARG, :EVENT_RESOURCE_TYPE_ARG, :EVENT_RESOURCE_ID_ARG
|
|
416
|
+
|
|
417
|
+
# Node API on a Gen4 node, and its root file id.
|
|
418
|
+
# @param node_id [String] Node identifier
|
|
419
|
+
# @param file_id [String, nil] Root file id; if nil, the access key root file id is used
|
|
420
|
+
# @param scope [String, nil] node scope (Api::Node::Scope::USER/ADMIN), or nil (requires secret)
|
|
421
|
+
# @return [Array(Api::Node, String)]
|
|
422
|
+
def nodegen4_root(node_id, file_id: nil, scope: nil)
|
|
423
|
+
node_api = aoc_api.node_api_from(
|
|
430
424
|
node_id: node_id,
|
|
431
425
|
scope: scope,
|
|
432
426
|
**workspace_id_hash(name: true)
|
|
433
427
|
)
|
|
434
|
-
file_id =
|
|
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
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
428
|
+
file_id = node_api.read("access_keys/#{node_api.app_info.node_info['access_key']}")['root_file_id'] if file_id.nil?
|
|
429
|
+
[node_api, file_id]
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
# Node plugin for Gen4 commands on a node, with the seed ctx of its `access_keys do` sub-tree.
|
|
433
|
+
# Used as `instance:` of mounts of NODE_GEN4_MOUNT.
|
|
434
|
+
# @return [Array(Node, Hash)]
|
|
435
|
+
def nodegen4_plugin(node_id, file_id: nil, scope: nil)
|
|
436
|
+
node_api, file_id = nodegen4_root(node_id, file_id: file_id, scope: scope)
|
|
437
|
+
[Node.new(context: context, api: node_api), {do_root_file_id: file_id}]
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
# Node-to-node transfer: client side is agent, server side is transfer server, in same workspace.
|
|
441
|
+
# @param direction [Symbol] :push or :pull
|
|
442
|
+
# @param source_folder [String] source folder
|
|
443
|
+
# @return [Result]
|
|
444
|
+
def nodegen4_transfer(node_id, direction:, source_folder:, file_id: nil, scope: nil)
|
|
445
|
+
top_node_api, file_id = nodegen4_root(node_id, file_id: file_id, scope: scope)
|
|
446
|
+
case direction
|
|
447
|
+
when :push
|
|
448
|
+
client_direction = Transfer::Spec::DIRECTION_SEND
|
|
449
|
+
client_folder = source_folder
|
|
450
|
+
server_folder = transfer.destination_folder(client_direction)
|
|
451
|
+
when :pull
|
|
452
|
+
client_direction = Transfer::Spec::DIRECTION_RECEIVE
|
|
453
|
+
client_folder = transfer.destination_folder(client_direction)
|
|
454
|
+
server_folder = source_folder
|
|
455
|
+
else Aspera.error_unexpected_value(direction) { 'direction' }
|
|
456
|
+
end
|
|
457
|
+
client_apifid = top_node_api.resolve_api_fid(file_id, client_folder)
|
|
458
|
+
server_apifid = top_node_api.resolve_api_fid(file_id, server_folder)
|
|
459
|
+
# force node as transfer agent
|
|
460
|
+
transfer.agent_instance = Agent::Node.new(
|
|
461
|
+
url: client_apifid.node_api.base_url,
|
|
462
|
+
username: client_apifid.node_api.app_info.node_info['access_key'],
|
|
463
|
+
password: client_apifid.node_api.oauth.authorization,
|
|
464
|
+
root_id: client_apifid.file_id
|
|
465
|
+
)
|
|
466
|
+
# additional node to node TS info
|
|
467
|
+
add_ts = {
|
|
468
|
+
'remote_access_key' => server_apifid.node_api.app_info.node_info['access_key'],
|
|
469
|
+
'destination_root_id' => server_apifid.file_id,
|
|
470
|
+
'source_root_id' => client_apifid.file_id
|
|
471
|
+
}
|
|
472
|
+
Runner.result_transfer(transfer.start(server_apifid.node_api.transfer_spec_gen4(
|
|
473
|
+
server_apifid.file_id,
|
|
474
|
+
client_direction,
|
|
475
|
+
add_ts
|
|
476
|
+
)))
|
|
482
477
|
end
|
|
483
478
|
|
|
484
|
-
# Execute an action on admin resources
|
|
485
|
-
# @param resource_type [Symbol] One of ADMIN_OBJECTS
|
|
486
479
|
# Per-resource configuration for admin CRUD resources.
|
|
487
|
-
# Keys: path, list_fields, id_result, require_ws_id, create_schema, extra_ops, singleton, op_setup
|
|
488
|
-
#
|
|
489
|
-
#
|
|
490
|
-
#
|
|
480
|
+
# Keys: path, list_fields, id_result, require_ws_id, create_schema, extra_ops, singleton, op_setup, op_mount, op_descriptions
|
|
481
|
+
# Ops other than `create` and `list` read the identifier `<res>_id` (argument of the op node).
|
|
482
|
+
# op_setup: Hash of op => setup method name, for ops that need context derived from the identifier.
|
|
483
|
+
# op_mount: Hash of op => mount: of that op's node.
|
|
484
|
+
# op_descriptions: Hash of op => description, for ops other than standard ones.
|
|
491
485
|
ADMIN_OBJECT_CONFIG = {
|
|
492
|
-
client: {
|
|
486
|
+
client: {
|
|
487
|
+
extra_ops: %i[set_pub_key],
|
|
488
|
+
extra_op_args: {set_pub_key: [{name: :private_key_pem}]},
|
|
489
|
+
op_descriptions: {set_pub_key: 'Set public key of client from a private key'}
|
|
490
|
+
},
|
|
493
491
|
client_access_key: {path: 'admin/client_access_keys'},
|
|
494
492
|
client_registration_token: {path: 'admin/client_registration_tokens', list_fields: %w[id value data.client_subject_scopes data.name created_at], id_result: 'token'},
|
|
495
493
|
configuration_policy: {list_fields: nil},
|
|
@@ -500,15 +498,29 @@ module Aspera
|
|
|
500
498
|
group_membership: {list_fields: %w[id group_id member_type member_id], create_schema: false},
|
|
501
499
|
kms_profile: {path: 'integrations/kms_profiles', create_schema: false},
|
|
502
500
|
network_policy: {list_fields: nil},
|
|
503
|
-
node: {
|
|
501
|
+
node: {
|
|
502
|
+
list_fields: %w[id name host access_key],
|
|
503
|
+
extra_ops: %i[do bearer_token update_status],
|
|
504
|
+
extra_op_args: {bearer_token: [{name: :scope, mandatory: false, default: nil}]},
|
|
505
|
+
op_mount: {do: NODE_GEN4_MOUNT.merge(instance: :admin_node_do_plugin)},
|
|
506
|
+
op_descriptions: {do: 'Execute command on node', bearer_token: 'Generate bearer token for node', update_status: 'Ask AoC to scan node and update its status'}
|
|
507
|
+
},
|
|
504
508
|
operation: {list_fields: %w[id type status created_at updated_at workspace_id user_id workspace_membership_id group_membership_id], ops: %i[list show modify]},
|
|
505
509
|
organization: {singleton: true},
|
|
506
510
|
package: {},
|
|
507
511
|
saml_configuration: {create_schema: false},
|
|
508
|
-
self: {singleton: true},
|
|
512
|
+
self: {singleton: true, op_descriptions: {show: 'Show current user'}},
|
|
509
513
|
short_link: {list_fields: %w[id short_url data.url_token_data.purpose password_enabled password_protected updated_by_user_id updated_at]},
|
|
510
|
-
user: {
|
|
511
|
-
|
|
514
|
+
user: {
|
|
515
|
+
list_fields: %w[id name email],
|
|
516
|
+
extra_ops: %i[preferences notifications],
|
|
517
|
+
op_descriptions: {preferences: 'Manage user preferences', notifications: 'Manage user notification preferences'}
|
|
518
|
+
},
|
|
519
|
+
workspace: {
|
|
520
|
+
extra_ops: %i[shared_folder dropbox],
|
|
521
|
+
op_setup: {shared_folder: :setup_admin_workspace_shared_folder, dropbox: :setup_admin_workspace_dropbox},
|
|
522
|
+
op_descriptions: {shared_folder: 'Manage shared folders of workspace', dropbox: 'Manage shared inboxes of workspace'}
|
|
523
|
+
},
|
|
512
524
|
workspace_membership: {list_fields: %w[id workspace_id member_type member_id]}
|
|
513
525
|
}.freeze
|
|
514
526
|
private_constant :ADMIN_OBJECT_CONFIG
|
|
@@ -524,43 +536,30 @@ module Aspera
|
|
|
524
536
|
|
|
525
537
|
# Build analytics REST API (shared by action_admin_analytics_*)
|
|
526
538
|
def build_analytics_api
|
|
527
|
-
Rest.new(**aoc_api.params.deep_merge({
|
|
539
|
+
Rest::Client.new(**aoc_api.params.deep_merge({
|
|
528
540
|
base_url: "#{aoc_api.base_url.gsub('/api/v1', '')}/analytics/v2",
|
|
529
541
|
auth: {params: {scope: Api::AoC::Scope::ADMIN_USER}}
|
|
530
542
|
}))
|
|
531
543
|
end
|
|
532
544
|
|
|
533
|
-
#
|
|
534
|
-
|
|
535
|
-
#
|
|
536
|
-
|
|
537
|
-
def short_link_purposes(shared_data, link_type)
|
|
538
|
-
if shared_data.keys.sort == %i[dropbox_id name]
|
|
539
|
-
token_purpose = 'send_package_to_dropbox'
|
|
540
|
-
short_link_purpose = link_type.eql?(:public) ? 'send_package_to_dropbox' : 'shared_folder_auth_link'
|
|
541
|
-
elsif shared_data.keys.sort == %i[file_id node_id]
|
|
542
|
-
token_purpose = 'view_shared_file'
|
|
543
|
-
short_link_purpose = link_type.eql?(:public) ? 'token_auth_redirection' : 'shared_folder_auth_link'
|
|
544
|
-
else
|
|
545
|
-
Aspera.error_unexpected_value(shared_data.keys)
|
|
546
|
-
end
|
|
547
|
-
[token_purpose, short_link_purpose]
|
|
548
|
-
end
|
|
545
|
+
# Purpose of a short link, by link type (the purpose of its URL token depends on the shared resource)
|
|
546
|
+
SHORT_LINK_PURPOSES = {public: 'token_auth_redirection', private: 'shared_folder_auth_link'}.freeze
|
|
547
|
+
# Query parameters of short link list
|
|
548
|
+
SHORT_LINK_QUERY_SCHEMA = Schema::Registry.query_params(Schema::Registry::AOC, 'short_links')
|
|
549
549
|
|
|
550
550
|
# Build the list_params hash used by delete/list/show/modify short link operations.
|
|
551
551
|
# @return [Hash]
|
|
552
|
-
def short_link_list_params(shared_data:, link_type:, token_purpose:, short_link_purpose:, **)
|
|
552
|
+
def short_link_list_params(shared_data:, link_type:, token_purpose:, short_link_purpose:, **ctx)
|
|
553
553
|
query = if link_type.eql?(:private)
|
|
554
554
|
shared_data
|
|
555
555
|
else
|
|
556
556
|
{url_token_data: {data: shared_data, purpose: token_purpose}}
|
|
557
557
|
end
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
}
|
|
558
|
+
# String keys: the user's `--query` overrides them
|
|
559
|
+
list_params = {'json_query' => query.to_json, 'purpose' => short_link_purpose, 'sort' => '-created_at'}
|
|
560
|
+
# `edit_access` requires `file_id` and `node_id`: shared folder links only
|
|
561
|
+
list_params['edit_access'] = true if ctx.key?(:shared_apifid)
|
|
562
|
+
list_params
|
|
564
563
|
end
|
|
565
564
|
|
|
566
565
|
# @return [PersistencyActionOnce, nil] persistency object if option `once_only` is used.
|
|
@@ -591,35 +590,39 @@ module Aspera
|
|
|
591
590
|
command :reminder, description: 'Send reminder email with list of orgs'
|
|
592
591
|
command(
|
|
593
592
|
:servers, description: 'List AoC servers (no auth)',
|
|
594
|
-
action: lambda do
|
|
593
|
+
action: lambda do |**|
|
|
595
594
|
no_auth_api = Api::AoC.new(url: options.get_option(:url), auth: :none)
|
|
596
595
|
Result::ObjectList.new(no_auth_api.read('servers'))
|
|
597
596
|
end
|
|
598
597
|
)
|
|
599
|
-
command :bearer_token, description: '
|
|
600
|
-
action: -> { Result::Text.new(aoc_api.oauth.authorization) }
|
|
598
|
+
command :bearer_token, description: 'Show bearer token',
|
|
599
|
+
action: ->(**) { Result::Text.new(aoc_api.oauth.authorization) }
|
|
601
600
|
command :organization, description: 'Show organization info',
|
|
602
|
-
action: -> { Result::SingleObject.new(aoc_api.read('organization')) }
|
|
601
|
+
action: ->(**) { Result::SingleObject.new(aoc_api.read('organization')) }
|
|
603
602
|
command :tier_restrictions, description: 'Show tier restrictions',
|
|
604
|
-
action: -> { Result::SingleObject.new(aoc_api.read('tier_restrictions')) }
|
|
605
|
-
command :user, description: '
|
|
606
|
-
|
|
607
|
-
command :
|
|
608
|
-
|
|
603
|
+
action: ->(**) { Result::SingleObject.new(aoc_api.read('tier_restrictions')) }
|
|
604
|
+
command :user, description: 'Manage current user'
|
|
605
|
+
# Node Gen4 read-only commands on packages: `packages <command> <package_id> ...`
|
|
606
|
+
command :packages, description: 'Manage packages', setup: :setup_workspace_display,
|
|
607
|
+
mount: NODE_GEN4_MOUNT.merge(instance: :package_node_plugin, only: Node::NODE4_READ_ACTIONS, arguments: [PACKAGE_ID_ARG])
|
|
608
|
+
command :files, description: 'Manage files (workspace-aware)', setup: :setup_workspace_display,
|
|
609
|
+
mount: NODE_GEN4_MOUNT.merge(instance: :files_node_plugin)
|
|
610
|
+
command :admin, description: 'Administer AoC', setup: :setup_admin_scope
|
|
609
611
|
commands_under :admin do
|
|
610
612
|
command :bearer_token, description: 'Show admin bearer token',
|
|
611
|
-
action: -> { Result::Text.new(aoc_api.oauth.authorization) }
|
|
613
|
+
action: ->(**) { Result::Text.new(aoc_api.oauth.authorization) }
|
|
612
614
|
command :application, description: 'Manage applications'
|
|
613
615
|
command :ats, description: 'Manage ATS (Aspera Transfer Service)',
|
|
614
|
-
|
|
616
|
+
mount: {plugin: Ats, instance: :build_ats_plugin}
|
|
615
617
|
command :usage_reports, description: 'List usage reports',
|
|
616
|
-
action: -> { result_list('usage_reports', base_query: workspace_id_hash) }
|
|
618
|
+
action: ->(**) { result_list('usage_reports', base_query: workspace_id_hash) }
|
|
617
619
|
command :auth_providers, description: 'Manage auth providers'
|
|
618
620
|
command :subscription, description: 'Show subscription info'
|
|
619
621
|
command :analytics, description: 'Query analytics'
|
|
620
622
|
ADMIN_OBJECTS.each do |res|
|
|
621
623
|
cfg = ADMIN_OBJECT_CONFIG.fetch(res, {})
|
|
622
624
|
op_setup = cfg[:op_setup] || {}
|
|
625
|
+
op_mount = cfg[:op_mount] || {}
|
|
623
626
|
extra_op_args = cfg[:extra_op_args] || {}
|
|
624
627
|
is_singleton = cfg[:singleton]
|
|
625
628
|
id_arg_spec = is_singleton ? [] : [{name: :"#{res}_id", type: :identifier, lookup: :"lookup_aoc_#{res}_id"}]
|
|
@@ -630,18 +633,19 @@ module Aspera
|
|
|
630
633
|
else
|
|
631
634
|
Operations::ALL + (cfg[:extra_ops] || [])
|
|
632
635
|
end
|
|
633
|
-
command res, description: "Manage #{res
|
|
636
|
+
command res, description: "Manage #{entity_noun(res, singular: false)}"
|
|
634
637
|
commands_under res do
|
|
635
638
|
ops.each do |op|
|
|
636
639
|
extra_setup = op_setup[op]
|
|
637
640
|
base_attrs = extra_setup ? {setup: extra_setup} : {}
|
|
641
|
+
base_attrs[:mount] = op_mount[op] if op_mount.key?(op)
|
|
638
642
|
extra_arg_list =
|
|
639
643
|
if !is_singleton && op.eql?(:create)
|
|
640
644
|
c = aoc_res_cfg(res)
|
|
641
|
-
[{name:
|
|
645
|
+
[{name: res, type: Hash, bulk: true, schema: c[:schema]}]
|
|
642
646
|
elsif !is_singleton && op.eql?(:modify)
|
|
643
647
|
c = aoc_res_cfg(res)
|
|
644
|
-
[{name:
|
|
648
|
+
[{name: res, type: Hash, schema: c[:schema]}]
|
|
645
649
|
elsif extra_op_args.key?(op)
|
|
646
650
|
extra_op_args[op]
|
|
647
651
|
else
|
|
@@ -655,62 +659,71 @@ module Aspera
|
|
|
655
659
|
c = aoc_res_cfg(res)
|
|
656
660
|
merged = merged.merge(query_schema: Schema::Registry.query_params(c[:query_component], c[:path]))
|
|
657
661
|
end
|
|
658
|
-
|
|
662
|
+
description = cfg.dig(:op_descriptions, op) ||
|
|
663
|
+
(Operations::ALL.include?(op) ? operation_description(op, entity_noun(res, singular: false)) : op.to_s.tr('_', ' ').capitalize)
|
|
664
|
+
command op, description: description, **merged
|
|
659
665
|
end
|
|
660
666
|
end
|
|
661
667
|
end
|
|
662
668
|
end
|
|
663
669
|
# admin > workspace > shared_folder sub-tree
|
|
664
670
|
commands_under %i[admin workspace shared_folder] do
|
|
665
|
-
command :list, description: 'List shared folders'
|
|
671
|
+
command :list, description: 'List shared folders',
|
|
672
|
+
action: ->(shared_folders:, **) { Result::ObjectList.new(shared_folders, fields: %w[id node_name node_id file_id file.path tags.aspera.files.workspace.share_as]) }
|
|
666
673
|
command :node, description: 'Execute node command on shared folder',
|
|
667
|
-
arguments: [{name: :
|
|
668
|
-
setup: :setup_admin_workspace_shared_folder_node
|
|
674
|
+
arguments: [{name: :shared_folder_id, type: :identifier}],
|
|
675
|
+
setup: :setup_admin_workspace_shared_folder_node,
|
|
676
|
+
mount: NODE_GEN4_MOUNT.merge(instance: :admin_workspace_shared_folder_node_plugin)
|
|
669
677
|
command :member, description: 'Show folder members',
|
|
670
|
-
arguments: [{name: :
|
|
678
|
+
arguments: [{name: :shared_folder_id, type: :identifier}],
|
|
671
679
|
setup: :setup_admin_workspace_shared_folder_member
|
|
672
680
|
end
|
|
673
|
-
# admin > workspace > shared_folder > node
|
|
681
|
+
# admin > workspace > shared_folder > node: Gen4 commands are mounted, plus node-to-node transfer
|
|
674
682
|
commands_under %i[admin workspace shared_folder node] do
|
|
675
|
-
command :transfer,
|
|
676
|
-
|
|
677
|
-
command :sync, description: 'Synchronize folders'
|
|
678
|
-
Node::COMMANDS_GEN4_SPEC.each do |cmd, spec|
|
|
679
|
-
command cmd, **spec
|
|
680
|
-
end
|
|
683
|
+
command :transfer, description: 'Transfer files (node-to-node)', arguments: TRANSFER_ARGS,
|
|
684
|
+
action: ->(sf_item:, direction:, source_folder:, **) { nodegen4_transfer(sf_item['node_id'], file_id: sf_item['file_id'], scope: Api::Node::Scope::ADMIN, direction: direction, source_folder: source_folder) }
|
|
681
685
|
end
|
|
682
686
|
commands_under %i[admin workspace shared_folder member] do
|
|
683
687
|
command :list, description: 'List members of a shared folder'
|
|
684
688
|
end
|
|
685
689
|
# admin > workspace > dropbox sub-tree
|
|
686
690
|
commands_under %i[admin workspace dropbox] do
|
|
687
|
-
command
|
|
691
|
+
command(
|
|
692
|
+
:list, description: 'List dropboxes in workspace',
|
|
693
|
+
action: lambda do |ws_res_id:, **|
|
|
694
|
+
query = query_read_delete(default: {}, schema: Schema::Registry.query_params(Schema::Registry::AOC, 'dropboxes'))
|
|
695
|
+
Result::ObjectList.new(aoc_api.read('dropboxes', query.merge({'workspace_id' => ws_res_id})), fields: %w[id name description])
|
|
696
|
+
end
|
|
697
|
+
)
|
|
688
698
|
end
|
|
689
|
-
# admin > node > do
|
|
699
|
+
# admin > node > do: Gen4 commands are mounted (op_mount), plus node-to-node transfer
|
|
690
700
|
commands_under %i[admin node do] do
|
|
691
|
-
command :transfer,
|
|
692
|
-
|
|
693
|
-
command :sync, description: 'Synchronize folders'
|
|
694
|
-
Node::COMMANDS_GEN4_SPEC.each do |cmd, spec|
|
|
695
|
-
command cmd, **spec
|
|
696
|
-
end
|
|
701
|
+
command :transfer, description: 'Transfer files (node-to-node)', arguments: TRANSFER_ARGS,
|
|
702
|
+
action: ->(node_id:, direction:, source_folder:, **) { nodegen4_transfer(node_id, scope: Api::Node::Scope::ADMIN, direction: direction, source_folder: source_folder) }
|
|
697
703
|
end
|
|
698
704
|
# admin > user > preferences|notifications sub-trees
|
|
699
705
|
%i[preferences notifications].each do |pref|
|
|
700
|
-
commands_under
|
|
701
|
-
command :show, description: "Show #{pref}"
|
|
702
|
-
command :modify, description: "Modify #{pref}",
|
|
703
|
-
arguments: [{name:
|
|
706
|
+
commands_under [:admin, :user, pref] do
|
|
707
|
+
command :show, description: "Show user #{pref}"
|
|
708
|
+
command :modify, description: "Modify user #{pref}",
|
|
709
|
+
arguments: [{name: pref, type: Hash}]
|
|
704
710
|
end
|
|
705
711
|
end
|
|
706
712
|
commands_under %i[admin auth_providers] do
|
|
707
713
|
command :list, description: 'List auth providers',
|
|
708
|
-
action: -> { result_list('admin/auth_providers') }
|
|
709
|
-
command :update, description: 'Update auth provider', action: -> { Aspera.error_not_implemented }
|
|
714
|
+
action: ->(**) { result_list('admin/auth_providers') }
|
|
715
|
+
command :update, description: 'Update auth provider', action: ->(**) { Aspera.error_not_implemented }
|
|
710
716
|
end
|
|
711
717
|
commands_under %i[admin subscription] do
|
|
712
|
-
command
|
|
713
|
-
|
|
718
|
+
command(
|
|
719
|
+
:account, description: 'Show subscription account',
|
|
720
|
+
action: lambda do |**|
|
|
721
|
+
org = aoc_api.read('organization')
|
|
722
|
+
result = GraphQL.execute(api_from_options('bss/platform/graphql'), 'bss_subscription_account', {organization_id: org['id']})
|
|
723
|
+
Result::SingleObject.new(result['aoc']['bssSubscription'])
|
|
724
|
+
end
|
|
725
|
+
)
|
|
726
|
+
command :usage, description: 'Show subscription usage',
|
|
714
727
|
arguments: [
|
|
715
728
|
{name: :aggregate, mandatory: false, default: :ALL, allowed: %i[ALL MONTHLY]},
|
|
716
729
|
{name: :start_date, mandatory: false, default: nil},
|
|
@@ -718,23 +731,29 @@ module Aspera
|
|
|
718
731
|
]
|
|
719
732
|
end
|
|
720
733
|
commands_under %i[admin analytics] do
|
|
721
|
-
command
|
|
734
|
+
command(
|
|
735
|
+
:application_events, description: 'List application events',
|
|
736
|
+
action: lambda do |**|
|
|
737
|
+
events = build_analytics_api.read("organizations/#{aoc_api.current_user_info['organization_id']}/application_events")['application_events']
|
|
738
|
+
Result::ObjectList.new(events)
|
|
739
|
+
end
|
|
740
|
+
)
|
|
722
741
|
command :transfers, description: 'List transfer events',
|
|
723
742
|
arguments: [
|
|
724
|
-
|
|
725
|
-
|
|
743
|
+
EVENT_RESOURCE_TYPE_ARG,
|
|
744
|
+
EVENT_RESOURCE_ID_ARG.merge(mandatory: false, default: nil)
|
|
726
745
|
]
|
|
727
746
|
command :files, description: 'List file events',
|
|
728
747
|
arguments: [
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
{name: :event_uuid,
|
|
748
|
+
EVENT_RESOURCE_TYPE_ARG,
|
|
749
|
+
EVENT_RESOURCE_ID_ARG,
|
|
750
|
+
{name: :event_uuid, type: :identifier}
|
|
732
751
|
]
|
|
733
752
|
end
|
|
734
753
|
# application sub-commands
|
|
735
754
|
commands_under %i[admin application] do
|
|
736
755
|
command :types, description: 'List application types',
|
|
737
|
-
action: -> { Result::ObjectList.new(aoc_api.read('admin/apps')) }
|
|
756
|
+
action: ->(**) { Result::ObjectList.new(aoc_api.read('admin/apps')) }
|
|
738
757
|
command :settings, description: 'Manage per-app-type settings'
|
|
739
758
|
command :instance, description: 'Manage app instances'
|
|
740
759
|
command :membership, description: 'Manage app memberships'
|
|
@@ -744,15 +763,15 @@ module Aspera
|
|
|
744
763
|
private_constant :APP_SETTINGS_PATH, :APP_INSTANCE_PATH
|
|
745
764
|
commands_under APP_SETTINGS_PATH do
|
|
746
765
|
APP_TYPES.each do |app_type|
|
|
747
|
-
command app_type, description: "
|
|
766
|
+
command app_type, description: "Manage settings of #{app_type} app"
|
|
748
767
|
commands_under app_type do
|
|
749
768
|
command :show, description: "Show #{app_type} settings",
|
|
750
|
-
action: -> { Result::SingleObject.new(aoc_api.read("/apps/#{app_type}/settings")) }
|
|
769
|
+
action: ->(**) { Result::SingleObject.new(aoc_api.read("/apps/#{app_type}/settings")) }
|
|
751
770
|
command(
|
|
752
771
|
:modify, description: "Modify #{app_type} settings",
|
|
753
|
-
arguments: [{name: :
|
|
754
|
-
action: lambda do |
|
|
755
|
-
aoc_api.update("/apps/#{app_type}/settings",
|
|
772
|
+
arguments: [{name: :settings, type: Hash}],
|
|
773
|
+
action: lambda do |settings:, **|
|
|
774
|
+
aoc_api.update("/apps/#{app_type}/settings", settings)
|
|
756
775
|
Result::Status.new('modified')
|
|
757
776
|
end
|
|
758
777
|
)
|
|
@@ -760,57 +779,63 @@ module Aspera
|
|
|
760
779
|
end
|
|
761
780
|
end
|
|
762
781
|
commands_under APP_INSTANCE_PATH do
|
|
763
|
-
command
|
|
764
|
-
result_list(
|
|
765
|
-
'admin/apps_new',
|
|
766
|
-
fields: %w[id app_type available workspace_id],
|
|
767
|
-
default_query: {workspace_id: aoc_api.workspace_info[:id]}
|
|
768
|
-
)
|
|
769
|
-
end)
|
|
782
|
+
command :list, description: 'List app instances'
|
|
770
783
|
APP_TYPES.each do |app_type|
|
|
771
|
-
command app_type, description: "
|
|
784
|
+
command app_type, description: "Manage #{app_type} app instance"
|
|
772
785
|
commands_under app_type do
|
|
773
|
-
command :show, description: "Show
|
|
774
|
-
arguments: [{name: :
|
|
775
|
-
command :modify, description: "Modify
|
|
776
|
-
arguments: [{name: :
|
|
786
|
+
command :show, description: "Show #{app_type} instance",
|
|
787
|
+
arguments: [{name: :instance_id, type: :identifier}]
|
|
788
|
+
command :modify, description: "Modify #{app_type} instance",
|
|
789
|
+
arguments: [{name: :instance_id, type: :identifier}, {name: :instance, type: Hash}]
|
|
777
790
|
end
|
|
778
791
|
end
|
|
779
792
|
end
|
|
780
793
|
commands_under %i[admin application membership] do
|
|
781
|
-
command :list, description: 'List app memberships', action: -> { result_list('apps/app_memberships') }
|
|
782
|
-
command :show,
|
|
783
|
-
|
|
794
|
+
command :list, description: 'List app memberships', action: ->(**) { result_list('apps/app_memberships') }
|
|
795
|
+
command :show, description: 'Show an app membership', arguments: [{name: :membership_id, type: :identifier}],
|
|
796
|
+
action: ->(membership_id:, **) { Result::SingleObject.new(aoc_api.read("apps/app_memberships/#{membership_id}", query_read_delete)) }
|
|
797
|
+
command(
|
|
798
|
+
:delete, description: 'Delete an app membership', arguments: [{name: :membership_id, type: :identifier}],
|
|
799
|
+
action: lambda do |membership_id:, **|
|
|
800
|
+
aoc_api.delete("apps/app_memberships/#{membership_id}")
|
|
801
|
+
Result::Status.new('deleted')
|
|
802
|
+
end
|
|
803
|
+
)
|
|
784
804
|
command :create, description: 'Create an app membership', arguments: [{name: :membership, type: Hash}]
|
|
785
805
|
end
|
|
786
|
-
command :automation, description: '
|
|
806
|
+
command :automation, description: 'Manage automation (BETA)', setup: :setup_automation_api
|
|
787
807
|
command :gateway, description: 'Start AoC Faspex4 gateway',
|
|
788
808
|
arguments: [{name: :parameters, type: Hash, mandatory: false, default: {}}]
|
|
789
809
|
|
|
790
810
|
# user sub-commands
|
|
791
811
|
commands_under :user do
|
|
792
|
-
commands_under :workspaces, description: "
|
|
793
|
-
command :list, description: 'List workspaces', action: -> { result_list('workspaces', fields: %w[id name]) }
|
|
794
|
-
command :current, description: 'Show current workspace', action: -> { Result::SingleObject.new(aoc_api.workspace_info) }
|
|
812
|
+
commands_under :workspaces, description: "Manage user's workspaces" do
|
|
813
|
+
command :list, description: 'List workspaces', action: ->(**) { result_list('workspaces', fields: %w[id name]) }
|
|
814
|
+
command :current, description: 'Show current workspace', action: ->(**) { Result::SingleObject.new(aoc_api.workspace_info) }
|
|
795
815
|
end
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
command :show, description: 'Show user profile', action: -> { Result::SingleObject.new(aoc_api.current_user_info(exception: true)) }
|
|
816
|
+
commands_under :profile, description: "Manage user's profile" do
|
|
817
|
+
command :show, description: 'Show user profile', action: ->(**) { Result::SingleObject.new(aoc_api.current_user_info(exception: true)) }
|
|
799
818
|
command(
|
|
800
819
|
:modify, description: 'Modify user profile',
|
|
801
|
-
arguments: [{name: :
|
|
802
|
-
action: lambda do |
|
|
803
|
-
aoc_api.update("users/#{aoc_api.current_user_info(exception: true)['id']}",
|
|
820
|
+
arguments: [{name: :profile, type: Hash}],
|
|
821
|
+
action: lambda do |profile:, **|
|
|
822
|
+
aoc_api.update("users/#{aoc_api.current_user_info(exception: true)['id']}", profile)
|
|
804
823
|
Result::Status.new('modified')
|
|
805
824
|
end
|
|
806
825
|
)
|
|
807
826
|
end
|
|
808
|
-
command :preferences, description: '
|
|
809
|
-
command :notifications, description: '
|
|
827
|
+
command :preferences, description: 'Manage interaction preferences'
|
|
828
|
+
command :notifications, description: 'Manage notification preferences'
|
|
810
829
|
command :contacts, description: 'Manage contacts'
|
|
811
830
|
# user > contacts sub-commands (same CRUD as admin > contact)
|
|
812
831
|
commands_under %i[contacts] do
|
|
813
|
-
|
|
832
|
+
contact_id = {name: :contact_id, type: :identifier, lookup: :lookup_aoc_contact_id}
|
|
833
|
+
contact_schema = aoc_res_cfg(:contact)[:schema]
|
|
834
|
+
command :list, description: operation_description(:list, 'contact')
|
|
835
|
+
command :show, description: operation_description(:show, 'contact'), arguments: [contact_id]
|
|
836
|
+
command :create, description: operation_description(:create, 'contact'), arguments: [{name: :contact, type: Hash, bulk: true, schema: contact_schema}]
|
|
837
|
+
command :modify, description: operation_description(:modify, 'contact'), arguments: [contact_id, {name: :contact, type: Hash, schema: contact_schema}]
|
|
838
|
+
command :delete, description: operation_description(:delete, 'contact'), arguments: [contact_id]
|
|
814
839
|
end
|
|
815
840
|
command :settings, description: 'Manage client settings'
|
|
816
841
|
commands_under %i[settings] do
|
|
@@ -821,17 +846,17 @@ module Aspera
|
|
|
821
846
|
commands_under %i[user preferences] do
|
|
822
847
|
command(
|
|
823
848
|
:show, description: 'Show user preferences',
|
|
824
|
-
action: lambda do
|
|
849
|
+
action: lambda do |**|
|
|
825
850
|
user_id = aoc_api.current_user_info(exception: true)['id']
|
|
826
851
|
Result::SingleObject.new(aoc_api.read("users/#{user_id}/user_interaction_preferences"))
|
|
827
852
|
end
|
|
828
853
|
)
|
|
829
854
|
command(
|
|
830
855
|
:modify, description: 'Modify user preferences',
|
|
831
|
-
arguments: [{name: :
|
|
832
|
-
action: lambda do |
|
|
856
|
+
arguments: [{name: :preferences, type: Hash}],
|
|
857
|
+
action: lambda do |preferences:, **|
|
|
833
858
|
user_id = aoc_api.current_user_info(exception: true)['id']
|
|
834
|
-
aoc_api.update("users/#{user_id}/user_interaction_preferences",
|
|
859
|
+
aoc_api.update("users/#{user_id}/user_interaction_preferences", preferences)
|
|
835
860
|
Result::Status.new('modified')
|
|
836
861
|
end
|
|
837
862
|
)
|
|
@@ -840,100 +865,144 @@ module Aspera
|
|
|
840
865
|
commands_under %i[user notifications] do
|
|
841
866
|
command(
|
|
842
867
|
:show, description: 'Show notification preferences',
|
|
843
|
-
action: lambda do
|
|
868
|
+
action: lambda do |**|
|
|
844
869
|
user_id = aoc_api.current_user_info(exception: true)['id']
|
|
845
870
|
Result::SingleObject.new(aoc_api.read("users/#{user_id}/notification_preferences"))
|
|
846
871
|
end
|
|
847
872
|
)
|
|
848
873
|
command(
|
|
849
874
|
:modify, description: 'Modify notification preferences',
|
|
850
|
-
arguments: [{name: :
|
|
851
|
-
action: lambda do |
|
|
875
|
+
arguments: [{name: :notifications, type: Hash}],
|
|
876
|
+
action: lambda do |notifications:, **|
|
|
852
877
|
user_id = aoc_api.current_user_info(exception: true)['id']
|
|
853
|
-
aoc_api.update("users/#{user_id}/notification_preferences",
|
|
878
|
+
aoc_api.update("users/#{user_id}/notification_preferences", notifications)
|
|
854
879
|
Result::Status.new('modified')
|
|
855
880
|
end
|
|
856
881
|
)
|
|
857
882
|
end
|
|
858
883
|
|
|
859
884
|
# packages sub-commands — instance commands consume package_id
|
|
885
|
+
# Not `crud_commands`: `list` has its own query and `once_only` persistency.
|
|
860
886
|
commands_under :packages do
|
|
861
|
-
command :shared_inboxes, description: '
|
|
887
|
+
command :shared_inboxes, description: 'Manage shared inboxes'
|
|
862
888
|
command :send, description: 'Send a package', transfer_paths: :send,
|
|
863
|
-
arguments: [{name: :
|
|
889
|
+
arguments: [{name: :package, type: Hash, schema: Schema::Registry.req_body(Schema::Registry::AOC, 'packages.post')}]
|
|
864
890
|
command :receive, description: 'Receive packages', aliases: [:recv], transfer_paths: :receive,
|
|
865
|
-
arguments: [
|
|
891
|
+
arguments: [PACKAGE_ID_ARG]
|
|
866
892
|
command :list, description: 'List packages'
|
|
867
893
|
command :show, description: 'Show a package',
|
|
868
|
-
arguments: [
|
|
894
|
+
arguments: [PACKAGE_ID_ARG],
|
|
895
|
+
action: ->(package_id:, **) { Result::SingleObject.new(aoc_api.read("packages/#{package_id}")) }
|
|
869
896
|
command :delete, description: 'Delete packages',
|
|
870
|
-
arguments: [
|
|
871
|
-
command
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
arguments: [{name: :package_id, type: :identifier}]
|
|
880
|
-
command :find, description: 'Find files in package',
|
|
881
|
-
arguments: [{name: :package_id, type: :identifier}]
|
|
897
|
+
arguments: [PACKAGE_ID_ARG.merge(bulk: true)]
|
|
898
|
+
command(
|
|
899
|
+
:modify, description: 'Modify a package',
|
|
900
|
+
arguments: [PACKAGE_ID_ARG, {name: :package, type: Hash}],
|
|
901
|
+
action: lambda do |package:, package_id:, **|
|
|
902
|
+
aoc_api.update("packages/#{package_id}", package)
|
|
903
|
+
Result::Status.new('modified')
|
|
904
|
+
end
|
|
905
|
+
)
|
|
882
906
|
end
|
|
883
907
|
|
|
884
908
|
commands_under %i[packages shared_inboxes] do
|
|
885
|
-
command :list, description: 'List shared inboxes'
|
|
886
|
-
action: (lambda do
|
|
887
|
-
result_list(
|
|
888
|
-
'dropbox_memberships',
|
|
889
|
-
fields: %w[dropbox_id dropbox.name],
|
|
890
|
-
default_query: workspace_id_hash({'embed[]' => 'dropbox', 'aggregate_permissions_by_dropbox' => true, 'sort' => 'dropbox_name'}, string: true)
|
|
891
|
-
)
|
|
892
|
-
end)
|
|
909
|
+
command :list, description: 'List shared inboxes'
|
|
893
910
|
command :show, description: 'Show a shared inbox',
|
|
894
911
|
arguments: [{name: :dropbox_id, type: :identifier, lookup: :lookup_aoc_dropbox_id}],
|
|
895
912
|
action: ->(dropbox_id:, **) { Result::SingleObject.new(aoc_api.read("dropboxes/#{dropbox_id}")) }
|
|
896
913
|
command :short_link, description: 'Manage shared inbox short links',
|
|
897
|
-
arguments: [{name: :link_type, allowed: %i[public
|
|
914
|
+
arguments: [{name: :link_type, allowed: %i[public]}, {name: :dropbox_id, type: :identifier, lookup: :lookup_aoc_dropbox_id}],
|
|
898
915
|
setup: :setup_packages_short_link
|
|
916
|
+
short_link_commands :short_link
|
|
899
917
|
end
|
|
900
|
-
# packages > shared_inboxes > short_link sub-commands
|
|
901
|
-
register_short_link_commands(self, %i[packages shared_inboxes short_link])
|
|
902
918
|
|
|
903
|
-
# files sub-commands:
|
|
919
|
+
# files sub-commands: Gen4 commands are mounted, plus AoC-specific commands
|
|
904
920
|
commands_under :files do
|
|
905
921
|
command :short_link, description: 'Manage file short link',
|
|
906
|
-
arguments: [{name: :
|
|
922
|
+
arguments: [{name: :folder}, {name: :link_type, allowed: %i[public private]}],
|
|
907
923
|
setup: :setup_files_short_link
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
command :sync, description: 'Synchronize folders'
|
|
912
|
-
Node::COMMANDS_GEN4_SPEC.each do |cmd, spec|
|
|
913
|
-
command cmd, **spec
|
|
914
|
-
end
|
|
924
|
+
short_link_commands :short_link
|
|
925
|
+
command :transfer, description: 'Transfer files (node-to-node)', arguments: TRANSFER_ARGS,
|
|
926
|
+
action: ->(direction:, source_folder:, **) { nodegen4_transfer(aoc_api.home[:node_id], file_id: aoc_api.home[:file_id], scope: Api::Node::Scope::USER, direction: direction, source_folder: source_folder) }
|
|
915
927
|
end
|
|
916
|
-
# files > short_link sub-commands
|
|
917
|
-
register_short_link_commands(self, %i[files short_link])
|
|
918
928
|
|
|
919
929
|
# automation sub-commands
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
930
|
+
# Automation API: a workflow has ordered steps (step_order), a step has ordered actions (action_order)
|
|
931
|
+
AUTOMATION_CRUD = {api: :@automation_api, body_component: Schema::Registry::AUTOMATION}.freeze
|
|
932
|
+
private_constant :AUTOMATION_CRUD
|
|
933
|
+
commands_under :automation do
|
|
925
934
|
commands_under :workflows do
|
|
926
|
-
crud_commands
|
|
935
|
+
crud_commands(**AUTOMATION_CRUD, entity: 'workflows', items_key: 'workflows', query_component: Schema::Registry::AUTOMATION)
|
|
927
936
|
command :launch, description: 'Launch a workflow',
|
|
928
|
-
arguments: [{name: :
|
|
929
|
-
action: ->(
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
937
|
+
arguments: [{name: :workflow_id, type: :identifier}],
|
|
938
|
+
action: ->(workflow_id:, **) { Result::SingleObject.new(@automation_api.create("workflows/#{workflow_id}/launch", {})) }
|
|
939
|
+
command :update_state, description: 'Update state of workflow',
|
|
940
|
+
arguments: [{name: :workflow_id, type: :identifier},
|
|
941
|
+
{name: :state, type: Hash, schema: Schema::Registry.req_body(Schema::Registry::AUTOMATION, 'workflows/{id}/update_state.put')}],
|
|
942
|
+
action: ->(workflow_id:, state:, **) { Result::SingleObject.new(@automation_api.update("workflows/#{workflow_id}/update_state", state)) }
|
|
943
|
+
command(
|
|
944
|
+
:cancel_instances, description: 'Cancel all jobs of workflow',
|
|
945
|
+
arguments: [{name: :workflow_id, type: :identifier}],
|
|
946
|
+
action: lambda do |workflow_id:, **|
|
|
947
|
+
@automation_api.update("workflows/#{workflow_id}/cancel_instances", {})
|
|
948
|
+
Result::Status.new('canceled')
|
|
935
949
|
end
|
|
936
|
-
|
|
950
|
+
)
|
|
951
|
+
command(
|
|
952
|
+
:delete_instances, description: 'Delete all jobs of workflow',
|
|
953
|
+
arguments: [{name: :workflow_id, type: :identifier}],
|
|
954
|
+
action: lambda do |workflow_id:, **|
|
|
955
|
+
@automation_api.delete("workflows/#{workflow_id}/delete_instances")
|
|
956
|
+
Result::Status.new('deleted')
|
|
957
|
+
end
|
|
958
|
+
)
|
|
959
|
+
command :action, description: 'Manage actions of workflow',
|
|
960
|
+
arguments: [{name: :workflow_id, type: :identifier}]
|
|
961
|
+
command :permissions, description: 'Manage permissions of workflow',
|
|
962
|
+
arguments: [{name: :workflow_id, type: :identifier}]
|
|
963
|
+
end
|
|
964
|
+
# workflow_id is resolved by the parent command
|
|
965
|
+
commands_under %i[workflows action] do
|
|
966
|
+
command :list, description: 'List actions of all steps of workflow'
|
|
967
|
+
command :create, description: 'Add a step with one action at the end of workflow',
|
|
968
|
+
arguments: [{name: :action, type: Hash, mandatory: false, default: {}, schema: Schema::Registry.req_body(Schema::Registry::AUTOMATION, 'actions.post')}]
|
|
969
|
+
end
|
|
970
|
+
commands_under %i[workflows permissions] do
|
|
971
|
+
# API requires query parameter workflow_id
|
|
972
|
+
command(
|
|
973
|
+
:list, description: 'List permissions of workflow',
|
|
974
|
+
query_schema: Schema::Registry.query_params(Schema::Registry::AUTOMATION, 'workflow_permissions'),
|
|
975
|
+
action: lambda do |workflow_id:, **|
|
|
976
|
+
schema = Schema::Registry.query_params(Schema::Registry::AUTOMATION, 'workflow_permissions')
|
|
977
|
+
query = (query_read_delete(schema: schema) || {}).merge('workflow_id' => workflow_id)
|
|
978
|
+
Result::ObjectList.new(@automation_api.read('workflow_permissions', query))
|
|
979
|
+
end
|
|
980
|
+
)
|
|
981
|
+
command(
|
|
982
|
+
:create, description: 'Create a permission on workflow',
|
|
983
|
+
arguments: [{name: :workflow_permission, type: Hash, bulk: true, schema: Schema::Registry.req_body(Schema::Registry::AUTOMATION, 'workflow_permissions.post')}],
|
|
984
|
+
action: lambda do |workflow_id:, workflow_permission:, **|
|
|
985
|
+
bulk_result(workflow_permission, command: :create) do |params|
|
|
986
|
+
@automation_api.create('workflow_permissions', params.merge('workflow_id' => workflow_id))
|
|
987
|
+
end
|
|
988
|
+
end
|
|
989
|
+
)
|
|
990
|
+
crud_commands(**AUTOMATION_CRUD, entity: 'workflow_permissions', name: 'workflow permission', operations: %i[modify delete])
|
|
991
|
+
end
|
|
992
|
+
commands_under :instances do
|
|
993
|
+
crud_commands(
|
|
994
|
+
**AUTOMATION_CRUD, entity: 'workflow_instances', name: 'workflow instance', operations: %i[list show delete],
|
|
995
|
+
items_key: 'workflow_instances', query_component: Schema::Registry::AUTOMATION
|
|
996
|
+
)
|
|
997
|
+
command :cancel, description: 'Cancel workflow instance',
|
|
998
|
+
arguments: [{name: :workflow_instance_id, type: :identifier}],
|
|
999
|
+
action: ->(workflow_instance_id:, **) { Result::SingleObject.new(@automation_api.update("workflow_instances/#{workflow_instance_id}", {'status' => 'canceled'})) }
|
|
1000
|
+
end
|
|
1001
|
+
commands_under :steps do
|
|
1002
|
+
crud_commands(**AUTOMATION_CRUD, entity: 'steps', operations: %i[create show modify delete])
|
|
1003
|
+
end
|
|
1004
|
+
commands_under :actions do
|
|
1005
|
+
crud_commands(**AUTOMATION_CRUD, entity: 'actions', operations: %i[create show modify delete])
|
|
937
1006
|
end
|
|
938
1007
|
end
|
|
939
1008
|
|
|
@@ -954,13 +1023,13 @@ module Aspera
|
|
|
954
1023
|
def setup_automation_api(**)
|
|
955
1024
|
change_api_scope(Api::AoC::Scope::ADMIN_USER)
|
|
956
1025
|
Log.log.warn('BETA: work under progress')
|
|
957
|
-
@automation_api = Rest.new(**aoc_api.params, base_url: aoc_api.base_url.gsub('/api/', '/automation/'))
|
|
1026
|
+
@automation_api = Rest::Client.new(**aoc_api.params, base_url: aoc_api.base_url.gsub('/api/', '/automation/'))
|
|
958
1027
|
{}
|
|
959
1028
|
end
|
|
960
1029
|
|
|
961
1030
|
# --- handler methods ---
|
|
962
1031
|
|
|
963
|
-
def action_reminder
|
|
1032
|
+
def action_reminder(**)
|
|
964
1033
|
user_email = options.get_option(:username, mandatory: true)
|
|
965
1034
|
no_auth_api = Api::AoC.new(url: options.get_option(:url), auth: :none)
|
|
966
1035
|
no_auth_api.create('organization_reminders', {email: user_email})
|
|
@@ -968,8 +1037,8 @@ module Aspera
|
|
|
968
1037
|
end
|
|
969
1038
|
|
|
970
1039
|
# packages > send
|
|
971
|
-
def action_packages_send(
|
|
972
|
-
package_data =
|
|
1040
|
+
def action_packages_send(package:, **)
|
|
1041
|
+
package_data = package
|
|
973
1042
|
new_user_option = options.get_option(:new_user_option)
|
|
974
1043
|
option_validate = options.get_option(:validate_metadata)
|
|
975
1044
|
workspace_id_hash(package_data, string: true) unless package_data.key?('workspace_id')
|
|
@@ -1042,7 +1111,7 @@ module Aspera
|
|
|
1042
1111
|
end
|
|
1043
1112
|
|
|
1044
1113
|
# packages > list
|
|
1045
|
-
def action_packages_list
|
|
1114
|
+
def action_packages_list(**)
|
|
1046
1115
|
result, max_items = list_all_packages_with_query
|
|
1047
1116
|
skip_ids_persistency = package_persistency
|
|
1048
1117
|
reject_packages_from_persistency(result[:items], skip_ids_persistency)
|
|
@@ -1052,11 +1121,6 @@ module Aspera
|
|
|
1052
1121
|
Result::ObjectList.new(result[:items], fields: display_fields, total: result[:total])
|
|
1053
1122
|
end
|
|
1054
1123
|
|
|
1055
|
-
# packages > show
|
|
1056
|
-
def action_packages_show(package_id:, **)
|
|
1057
|
-
Result::SingleObject.new(aoc_api.read("packages/#{package_id}"))
|
|
1058
|
-
end
|
|
1059
|
-
|
|
1060
1124
|
# packages > delete
|
|
1061
1125
|
def action_packages_delete(package_id:, **)
|
|
1062
1126
|
bulk_result(package_id, command: :delete) do |one_id|
|
|
@@ -1065,109 +1129,100 @@ module Aspera
|
|
|
1065
1129
|
end
|
|
1066
1130
|
end
|
|
1067
1131
|
|
|
1068
|
-
# packages
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
# (NODE4_READ_ACTIONS dispatched by full path: action_packages_bearer_token_node, etc.)
|
|
1076
|
-
Node::NODE4_READ_ACTIONS.each do |action|
|
|
1077
|
-
define_action_method([:packages, action]) do |package_id:, **|
|
|
1078
|
-
package_info = aoc_api.read("packages/#{package_id}")
|
|
1079
|
-
execute_nodegen4_command(action, package_info['node_id'], file_id: package_info['contents_file_id'], scope: Api::Node::Scope::USER)
|
|
1080
|
-
end
|
|
1132
|
+
# Used as `instance:` of the mount on packages: node of the package contents
|
|
1133
|
+
# @return [Array(Node, Hash)]
|
|
1134
|
+
def package_node_plugin(package_id:, **)
|
|
1135
|
+
package_info = aoc_api.read("packages/#{package_id}")
|
|
1136
|
+
# An id that is not a single path segment (e.g. `/`) reads the list of packages
|
|
1137
|
+
Aspera.assert(package_info.is_a?(Hash), type: Cli::BadArgument) { "invalid package id: #{package_id}" }
|
|
1138
|
+
nodegen4_plugin(package_info['node_id'], file_id: package_info['contents_file_id'], scope: Api::Node::Scope::USER)
|
|
1081
1139
|
end
|
|
1082
1140
|
|
|
1083
1141
|
# setup: files > short_link
|
|
1084
|
-
# Resolves the target folder,
|
|
1085
|
-
# @return [Hash] ctx keys:
|
|
1086
|
-
def setup_files_short_link(
|
|
1142
|
+
# Resolves the target folder, sets purposes.
|
|
1143
|
+
# @return [Hash] ctx keys: shared_data, token_purpose, short_link_purpose, shared_apifid
|
|
1144
|
+
def setup_files_short_link(folder:, link_type:, **)
|
|
1087
1145
|
home_node_api = aoc_api.node_api_from(
|
|
1088
1146
|
node_id: aoc_api.home[:node_id],
|
|
1089
1147
|
**workspace_id_hash(name: true)
|
|
1090
1148
|
)
|
|
1091
|
-
shared_apifid = home_node_api.resolve_api_fid(aoc_api.home[:file_id],
|
|
1149
|
+
shared_apifid = home_node_api.resolve_api_fid(aoc_api.home[:file_id], folder)
|
|
1092
1150
|
shared_data = {
|
|
1093
1151
|
node_id: shared_apifid.node_api.app_info.node_info['id'],
|
|
1094
1152
|
file_id: shared_apifid.file_id
|
|
1095
1153
|
}
|
|
1096
|
-
token_purpose, short_link_purpose = short_link_purposes(shared_data, link_type)
|
|
1097
|
-
perm_block = lambda do |op, id, access_levels|
|
|
1098
|
-
case op
|
|
1099
|
-
when :create
|
|
1100
|
-
perm_data = {
|
|
1101
|
-
'file_id' => shared_apifid.file_id,
|
|
1102
|
-
'access_id' => id,
|
|
1103
|
-
'access_type' => 'user',
|
|
1104
|
-
'access_levels' => Api::AoC.expand_access_levels(access_levels),
|
|
1105
|
-
'tags' => {
|
|
1106
|
-
'url_token' => true,
|
|
1107
|
-
'folder_name' => File.basename(folder_dest),
|
|
1108
|
-
'created_by_name' => aoc_api.current_user_info['name'],
|
|
1109
|
-
'created_by_email' => aoc_api.current_user_info['email'],
|
|
1110
|
-
'access_key' => shared_apifid.node_api.app_info.node_info['access_key'],
|
|
1111
|
-
'node' => shared_apifid.node_api.app_info.node_info['name'],
|
|
1112
|
-
**workspace_id_hash(string: true, name: true)
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
created_data = shared_apifid.node_api.create('permissions', perm_data)
|
|
1116
|
-
aoc_api.permissions_send_event(event_data: created_data, app_info: shared_apifid.node_api.app_info)
|
|
1117
|
-
when :update
|
|
1118
|
-
found = shared_apifid.node_api.read('permissions', {file_id: shared_apifid.file_id, inherited: false, access_type: 'user', access_id: id}).find { |i| i['access_id'].eql?(id) }
|
|
1119
|
-
Aspera.assert(!found.nil?, type: Error) { "Short link not found: #{id}" }
|
|
1120
|
-
shared_apifid.node_api.update("permissions/#{found['id']}", {access_levels: Api::AoC.expand_access_levels(access_levels)})
|
|
1121
|
-
when :delete
|
|
1122
|
-
found = shared_apifid.node_api.read('permissions', {file_id: shared_apifid.file_id, inherited: false, access_type: 'user', access_id: id}).first
|
|
1123
|
-
Aspera.assert(!found.nil?, type: Error) { "Short link not found: #{id}" }
|
|
1124
|
-
shared_apifid.node_api.delete("permissions/#{found['id']}")
|
|
1125
|
-
else Aspera.error_unexpected_value(op)
|
|
1126
|
-
end
|
|
1127
|
-
end
|
|
1128
1154
|
{
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
sl_perm_block: perm_block
|
|
1155
|
+
shared_data: shared_data,
|
|
1156
|
+
token_purpose: 'view_shared_file',
|
|
1157
|
+
short_link_purpose: SHORT_LINK_PURPOSES[link_type],
|
|
1158
|
+
shared_apifid: shared_apifid
|
|
1134
1159
|
}
|
|
1135
1160
|
end
|
|
1136
1161
|
|
|
1162
|
+
# Node permission of a public short link on the shared folder (files > short_link only)
|
|
1163
|
+
# @param op [Symbol] :create, :update or :delete
|
|
1164
|
+
# @param id [String] Short link resource id: access_id of the permission
|
|
1165
|
+
# @param access_levels [String, Array, nil] Access levels for :create and :update
|
|
1166
|
+
def short_link_permission(op, id, access_levels, shared_apifid:, folder:, **)
|
|
1167
|
+
case op
|
|
1168
|
+
when :create
|
|
1169
|
+
perm_data = {
|
|
1170
|
+
'file_id' => shared_apifid.file_id,
|
|
1171
|
+
'access_id' => id,
|
|
1172
|
+
'access_type' => 'user',
|
|
1173
|
+
'access_levels' => Api::AoC.expand_access_levels(access_levels),
|
|
1174
|
+
'tags' => {
|
|
1175
|
+
'url_token' => true,
|
|
1176
|
+
'folder_name' => File.basename(folder),
|
|
1177
|
+
'created_by_name' => aoc_api.current_user_info['name'],
|
|
1178
|
+
'created_by_email' => aoc_api.current_user_info['email'],
|
|
1179
|
+
'access_key' => shared_apifid.node_api.app_info.node_info['access_key'],
|
|
1180
|
+
'node' => shared_apifid.node_api.app_info.node_info['name'],
|
|
1181
|
+
**workspace_id_hash(string: true, name: true)
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
created_data = shared_apifid.node_api.create('permissions', perm_data)
|
|
1185
|
+
aoc_api.permissions_send_event(event_data: created_data, app_info: shared_apifid.node_api.app_info)
|
|
1186
|
+
when :update
|
|
1187
|
+
found = shared_apifid.node_api.read('permissions', {file_id: shared_apifid.file_id, inherited: false, access_type: 'user', access_id: id}).find { |i| i['access_id'].eql?(id) }
|
|
1188
|
+
Aspera.assert(!found.nil?, type: Error) { "Short link not found: #{id}" }
|
|
1189
|
+
shared_apifid.node_api.update("permissions/#{found['id']}", {access_levels: Api::AoC.expand_access_levels(access_levels)})
|
|
1190
|
+
when :delete
|
|
1191
|
+
found = shared_apifid.node_api.read('permissions', {file_id: shared_apifid.file_id, inherited: false, access_type: 'user', access_id: id}).first
|
|
1192
|
+
Aspera.assert(!found.nil?, type: Error) { "Short link not found: #{id}" }
|
|
1193
|
+
shared_apifid.node_api.delete("permissions/#{found['id']}")
|
|
1194
|
+
else Aspera.error_unexpected_value(op)
|
|
1195
|
+
end
|
|
1196
|
+
end
|
|
1197
|
+
|
|
1137
1198
|
# setup: packages > shared_inboxes > short_link
|
|
1138
|
-
#
|
|
1139
|
-
# @return [Hash] ctx keys:
|
|
1140
|
-
def setup_packages_short_link(link_type:, **)
|
|
1141
|
-
dropbox_id = get_resource_id_from_args('dropboxes')
|
|
1142
|
-
shared_data = {dropbox_id: dropbox_id, name: ''}
|
|
1143
|
-
token_purpose, short_link_purpose = short_link_purposes(shared_data, link_type)
|
|
1199
|
+
# link_type: and dropbox_id: resolved via arguments: on the node, sets purposes.
|
|
1200
|
+
# @return [Hash] ctx keys: shared_data, token_purpose, short_link_purpose
|
|
1201
|
+
def setup_packages_short_link(link_type:, dropbox_id:, **)
|
|
1144
1202
|
{
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
sl_short_link_purpose: short_link_purpose,
|
|
1149
|
-
sl_perm_block: nil
|
|
1203
|
+
shared_data: {dropbox_id: dropbox_id, name: ''},
|
|
1204
|
+
token_purpose: 'send_package_to_dropbox',
|
|
1205
|
+
short_link_purpose: SHORT_LINK_PURPOSES[link_type]
|
|
1150
1206
|
}
|
|
1151
1207
|
end
|
|
1152
1208
|
|
|
1153
1209
|
# Shared implementation for short_link > create
|
|
1154
|
-
def
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
case sl_link_type
|
|
1210
|
+
def short_link_create(custom_data = {}, shared_data:, link_type:, token_purpose:, short_link_purpose:, **ctx)
|
|
1211
|
+
ws_shared_data = workspace_id_hash(shared_data.dup)
|
|
1212
|
+
create_payload = {purpose: short_link_purpose, user_selected_name: nil}
|
|
1213
|
+
case link_type
|
|
1159
1214
|
when :private
|
|
1160
|
-
create_payload[:data] =
|
|
1215
|
+
create_payload[:data] = ws_shared_data
|
|
1161
1216
|
when :public
|
|
1162
1217
|
create_payload[:expires_at] = nil
|
|
1163
1218
|
create_payload[:password_enabled] = false
|
|
1164
|
-
|
|
1219
|
+
ws_shared_data[:name] = ''
|
|
1165
1220
|
create_payload[:data] = {
|
|
1166
1221
|
aoc: true,
|
|
1167
|
-
url_token_data: {data:
|
|
1222
|
+
url_token_data: {data: ws_shared_data, purpose: token_purpose}
|
|
1168
1223
|
}
|
|
1169
1224
|
end
|
|
1170
|
-
custom_data =
|
|
1225
|
+
custom_data = custom_data.dup
|
|
1171
1226
|
access_levels = custom_data.delete('access_levels')
|
|
1172
1227
|
if (pass = custom_data.delete('password'))
|
|
1173
1228
|
create_payload[:data][:url_token_data][:password] = pass
|
|
@@ -1175,104 +1230,106 @@ module Aspera
|
|
|
1175
1230
|
end
|
|
1176
1231
|
create_payload.deep_merge!(custom_data)
|
|
1177
1232
|
result_create_short_link = aoc_api.create('short_links', create_payload)
|
|
1178
|
-
|
|
1233
|
+
short_link_permission(:create, result_create_short_link['resource_id'], access_levels, **ctx) if link_type.eql?(:public) && ctx.key?(:shared_apifid)
|
|
1179
1234
|
Result::SingleObject.new(result_create_short_link)
|
|
1180
1235
|
end
|
|
1181
1236
|
|
|
1182
1237
|
# Shared implementation for short_link > delete|list|show|modify: fetch the short_list
|
|
1183
|
-
def
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
list_params = short_link_list_params(
|
|
1187
|
-
shared_data: shared_data, link_type: sl_link_type,
|
|
1188
|
-
token_purpose: sl_token_purpose, short_link_purpose: sl_short_link_purpose
|
|
1189
|
-
)
|
|
1238
|
+
def short_link_fetch_list(shared_data:, **ctx)
|
|
1239
|
+
ws_shared_data = workspace_id_hash(shared_data.dup)
|
|
1240
|
+
list_params = short_link_list_params(shared_data: ws_shared_data, **ctx)
|
|
1190
1241
|
{
|
|
1191
|
-
|
|
1192
|
-
|
|
1242
|
+
short_list: aoc_api.read_with_paging('short_links', list_params.merge(query_read_delete(default: {}, schema: SHORT_LINK_QUERY_SCHEMA)).compact),
|
|
1243
|
+
ws_shared_data: ws_shared_data
|
|
1193
1244
|
}
|
|
1194
1245
|
end
|
|
1195
1246
|
|
|
1196
1247
|
# Shared implementation for short_link > delete
|
|
1197
|
-
def
|
|
1248
|
+
def short_link_delete(ws_shared_data:, short_list:, link_type:, short_link_id: nil, **ctx)
|
|
1198
1249
|
one_id = short_link_id
|
|
1199
|
-
if
|
|
1200
|
-
found =
|
|
1250
|
+
if link_type.eql?(:public)
|
|
1251
|
+
found = short_list[:items].find { |item| item['id'].eql?(one_id) }
|
|
1201
1252
|
raise BadIdentifier.new('Short link', one_id) if found.nil?
|
|
1202
|
-
|
|
1253
|
+
short_link_permission(:delete, found['resource_id'], nil, **ctx) if ctx.key?(:shared_apifid)
|
|
1203
1254
|
end
|
|
1204
|
-
|
|
1255
|
+
# `edit_access` requires `file_id` and `node_id`: shared folder links only
|
|
1256
|
+
aoc_api.delete("short_links/#{one_id}", ({edit_access: true, json_query: ws_shared_data.to_json} if ctx.key?(:shared_apifid)))
|
|
1205
1257
|
Result::Status.new('deleted')
|
|
1206
1258
|
end
|
|
1207
1259
|
|
|
1208
1260
|
# Shared implementation for short_link > list
|
|
1209
|
-
def
|
|
1210
|
-
Result::ObjectList.new(
|
|
1261
|
+
def short_link_list(short_list:, **)
|
|
1262
|
+
Result::ObjectList.new(short_list[:items], fields: Formatter.all_but('data'), total: short_list[:total])
|
|
1211
1263
|
end
|
|
1212
1264
|
|
|
1213
1265
|
# Shared implementation for short_link > show
|
|
1214
|
-
def
|
|
1266
|
+
def short_link_show(short_list:, short_link_id: nil, **)
|
|
1215
1267
|
one_id = short_link_id
|
|
1216
|
-
found =
|
|
1268
|
+
found = short_list[:items].find { |item| item['id'].eql?(one_id) }
|
|
1217
1269
|
raise BadIdentifier.new('Short link', one_id) if found.nil?
|
|
1218
1270
|
Result::SingleObject.new(found, fields: Formatter.all_but('data'))
|
|
1219
1271
|
end
|
|
1220
1272
|
|
|
1221
1273
|
# Shared implementation for short_link > modify
|
|
1222
|
-
def
|
|
1223
|
-
Aspera.assert_values(
|
|
1274
|
+
def short_link_modify(custom_data = {}, shared_data:, short_list:, link_type:, short_link_id: nil, **ctx)
|
|
1275
|
+
Aspera.assert_values(link_type, [:public], type: Cli::BadArgument) { 'link_type' }
|
|
1224
1276
|
one_id = short_link_id
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1277
|
+
# Identifies the shared resource: `node_id` and `file_id`, or `dropbox_id`
|
|
1278
|
+
token_data = shared_data.except(:name)
|
|
1279
|
+
# `edit_access` requires `file_id` and `node_id`: shared folder links only
|
|
1280
|
+
modify_payload = ctx.key?(:shared_apifid) ? {edit_access: true, json_query: token_data} : {}
|
|
1281
|
+
custom_data = custom_data.dup
|
|
1228
1282
|
if (pass = custom_data.delete('password'))
|
|
1229
1283
|
modify_payload[:password_enabled] = true
|
|
1230
|
-
modify_payload[:data] = {url_token_data: {password: pass, data:
|
|
1284
|
+
modify_payload[:data] = {url_token_data: {password: pass, data: token_data}}
|
|
1231
1285
|
else
|
|
1232
1286
|
modify_payload[:password_enabled] = false
|
|
1233
1287
|
end
|
|
1234
|
-
if custom_data.delete('access_levels')
|
|
1235
|
-
found =
|
|
1288
|
+
if (access_levels = custom_data.delete('access_levels'))
|
|
1289
|
+
found = short_list[:items].find { |item| item['id'].eql?(one_id) }
|
|
1236
1290
|
raise BadIdentifier.new('Short link', one_id) if found.nil?
|
|
1237
|
-
|
|
1291
|
+
short_link_permission(:update, found['resource_id'], access_levels, **ctx) if ctx.key?(:shared_apifid)
|
|
1238
1292
|
end
|
|
1239
1293
|
modify_payload.deep_merge!(custom_data)
|
|
1240
1294
|
aoc_api.update("short_links/#{one_id}", modify_payload)
|
|
1241
1295
|
Result::Status.new('modified')
|
|
1242
1296
|
end
|
|
1243
1297
|
|
|
1244
|
-
# files
|
|
1245
|
-
def
|
|
1246
|
-
|
|
1247
|
-
:transfer, aoc_api.home[:node_id],
|
|
1248
|
-
file_id: aoc_api.home[:file_id],
|
|
1249
|
-
scope: Api::Node::Scope::USER,
|
|
1250
|
-
transfer_direction: direction,
|
|
1251
|
-
transfer_source: source_folder
|
|
1252
|
-
)
|
|
1253
|
-
end
|
|
1254
|
-
|
|
1255
|
-
# files > FILES_COMMANDS (all Gen4 node commands except :transfer, handled above)
|
|
1256
|
-
FILES_COMMANDS.reject { |a| a.eql?(:transfer) }.each do |action|
|
|
1257
|
-
define_action_method([:files, action]) do |**ctx|
|
|
1258
|
-
execute_nodegen4_command(action, aoc_api.home[:node_id], file_id: aoc_api.home[:file_id], scope: Api::Node::Scope::USER, **ctx)
|
|
1259
|
-
end
|
|
1298
|
+
# files - mount target: Gen4 commands on the user's home folder
|
|
1299
|
+
def files_node_plugin(**)
|
|
1300
|
+
nodegen4_plugin(aoc_api.home[:node_id], file_id: aoc_api.home[:file_id], scope: Api::Node::Scope::USER)
|
|
1260
1301
|
end
|
|
1261
1302
|
|
|
1262
1303
|
# admin > application > instance > <type> > show|modify
|
|
1263
1304
|
APP_TYPES.each do |app_type|
|
|
1264
1305
|
define_action_method([:admin, :application, :instance, app_type, :show]) do |**kwargs|
|
|
1265
|
-
app_id = kwargs[:
|
|
1306
|
+
app_id = kwargs[:instance_id]
|
|
1266
1307
|
Result::SingleObject.new(aoc_api.read("admin/apps_new/#{app_type}/#{app_id}", query_read_delete))
|
|
1267
1308
|
end
|
|
1268
1309
|
|
|
1269
|
-
define_action_method([:admin, :application, :instance, app_type, :modify]) do |
|
|
1270
|
-
app_id = kwargs[:
|
|
1271
|
-
aoc_api.update("admin/apps_new/#{app_type}/#{app_id}",
|
|
1310
|
+
define_action_method([:admin, :application, :instance, app_type, :modify]) do |instance:, **kwargs|
|
|
1311
|
+
app_id = kwargs[:instance_id]
|
|
1312
|
+
aoc_api.update("admin/apps_new/#{app_type}/#{app_id}", instance)
|
|
1272
1313
|
Result::Status.new('modified')
|
|
1273
1314
|
end
|
|
1274
1315
|
end
|
|
1275
1316
|
|
|
1317
|
+
def action_admin_application_instance_list(**)
|
|
1318
|
+
result_list(
|
|
1319
|
+
'admin/apps_new',
|
|
1320
|
+
fields: %w[id app_type available workspace_id],
|
|
1321
|
+
default_query: {workspace_id: aoc_api.workspace_info[:id]}
|
|
1322
|
+
)
|
|
1323
|
+
end
|
|
1324
|
+
|
|
1325
|
+
def action_packages_shared_inboxes_list(**)
|
|
1326
|
+
result_list(
|
|
1327
|
+
'dropbox_memberships',
|
|
1328
|
+
fields: %w[dropbox_id dropbox.name],
|
|
1329
|
+
default_query: workspace_id_hash({'embed[]' => 'dropbox', 'aggregate_permissions_by_dropbox' => true, 'sort' => 'dropbox_name'}, string: true)
|
|
1330
|
+
)
|
|
1331
|
+
end
|
|
1332
|
+
|
|
1276
1333
|
def action_admin_application_membership_create(membership:, **)
|
|
1277
1334
|
data = membership.dup
|
|
1278
1335
|
app_type = data.delete('app_type')
|
|
@@ -1281,29 +1338,12 @@ module Aspera
|
|
|
1281
1338
|
Result::SingleObject.new(aoc_api.create("apps/#{app_type}/app_memberships", data))
|
|
1282
1339
|
end
|
|
1283
1340
|
|
|
1284
|
-
# admin > application > membership > show|delete
|
|
1285
|
-
def action_admin_application_membership_show(membership_id:, **)
|
|
1286
|
-
Result::SingleObject.new(aoc_api.read("apps/app_memberships/#{membership_id}", query_read_delete))
|
|
1287
|
-
end
|
|
1288
|
-
|
|
1289
|
-
def action_admin_application_membership_delete(membership_id:, **)
|
|
1290
|
-
aoc_api.delete("apps/app_memberships/#{membership_id}")
|
|
1291
|
-
Result::Status.new('deleted')
|
|
1292
|
-
end
|
|
1293
|
-
|
|
1294
1341
|
# admin - setup: change API scope to admin once
|
|
1295
1342
|
def setup_admin_scope(**)
|
|
1296
1343
|
change_api_scope(Api::AoC::Scope::ADMIN)
|
|
1297
1344
|
{}
|
|
1298
1345
|
end
|
|
1299
1346
|
|
|
1300
|
-
# admin > subscription > account
|
|
1301
|
-
def action_admin_subscription_account
|
|
1302
|
-
org = aoc_api.read('organization')
|
|
1303
|
-
result = GraphQL.execute(api_from_options('bss/platform/graphql'), 'bss_subscription_account', {organization_id: org['id']})
|
|
1304
|
-
Result::SingleObject.new(result['aoc']['bssSubscription'])
|
|
1305
|
-
end
|
|
1306
|
-
|
|
1307
1347
|
# admin > subscription > usage
|
|
1308
1348
|
def action_admin_subscription_usage(aggregate:, start_date:, end_date:, **)
|
|
1309
1349
|
today = Date.today
|
|
@@ -1318,21 +1358,20 @@ module Aspera
|
|
|
1318
1358
|
Result::SingleObject.new(result['aoc'])
|
|
1319
1359
|
end
|
|
1320
1360
|
|
|
1321
|
-
#
|
|
1322
|
-
def
|
|
1323
|
-
|
|
1324
|
-
|
|
1361
|
+
# @return [String] the given resource id, or the one of the current user if empty
|
|
1362
|
+
def analytics_resource_id(event_resource_type, event_resource_id)
|
|
1363
|
+
return event_resource_id unless event_resource_id.to_s.empty?
|
|
1364
|
+
case event_resource_type
|
|
1365
|
+
when :organizations then aoc_api.current_user_info['organization_id']
|
|
1366
|
+
when :users then aoc_api.current_user_info['id']
|
|
1367
|
+
when :nodes then aoc_api.current_user_info['read_only_home_node_id']
|
|
1368
|
+
else Aspera.error_unreachable_line
|
|
1369
|
+
end
|
|
1325
1370
|
end
|
|
1326
1371
|
|
|
1327
1372
|
# admin > analytics > transfers
|
|
1328
1373
|
def action_admin_analytics_transfers(event_resource_type:, event_resource_id:, **)
|
|
1329
|
-
event_resource_id
|
|
1330
|
-
case event_resource_type
|
|
1331
|
-
when :organizations then aoc_api.current_user_info['organization_id']
|
|
1332
|
-
when :users then aoc_api.current_user_info['id']
|
|
1333
|
-
when :nodes then aoc_api.current_user_info['read_only_home_node_id']
|
|
1334
|
-
else Aspera.error_unreachable_line
|
|
1335
|
-
end
|
|
1374
|
+
event_resource_id = analytics_resource_id(event_resource_type, event_resource_id)
|
|
1336
1375
|
filter = query_read_delete(default: {})
|
|
1337
1376
|
filter['limit'] ||= 100
|
|
1338
1377
|
if options.get_option(:once_only, mandatory: true)
|
|
@@ -1356,13 +1395,7 @@ module Aspera
|
|
|
1356
1395
|
|
|
1357
1396
|
# admin > analytics > files
|
|
1358
1397
|
def action_admin_analytics_files(event_resource_type:, event_resource_id:, event_uuid:, **)
|
|
1359
|
-
event_resource_id =
|
|
1360
|
-
case event_resource_type
|
|
1361
|
-
when :organizations then aoc_api.current_user_info['organization_id']
|
|
1362
|
-
when :users then aoc_api.current_user_info['id']
|
|
1363
|
-
when :nodes then aoc_api.current_user_info['read_only_home_node_id']
|
|
1364
|
-
else Aspera.error_unreachable_line
|
|
1365
|
-
end if event_resource_id.to_s.empty?
|
|
1398
|
+
event_resource_id = analytics_resource_id(event_resource_type, event_resource_id)
|
|
1366
1399
|
filter = query_read_delete(default: {})
|
|
1367
1400
|
filter['limit'] ||= 100
|
|
1368
1401
|
events = build_analytics_api.read("#{event_resource_type}/#{event_resource_id}/transfers/#{event_uuid}/files", filter)['files']
|
|
@@ -1370,7 +1403,7 @@ module Aspera
|
|
|
1370
1403
|
end
|
|
1371
1404
|
|
|
1372
1405
|
# Lookup methods for arguments:(:identifier) + lookup: on admin resources.
|
|
1373
|
-
# One method per non-singleton resource
|
|
1406
|
+
# One method per non-singleton resource: percent selector searches with `q=<value>`.
|
|
1374
1407
|
ADMIN_OBJECTS.reject { |r| ADMIN_OBJECT_CONFIG.dig(r, :singleton) }.each do |res|
|
|
1375
1408
|
define_method(:"lookup_aoc_#{res}_id") do |_field, value, **|
|
|
1376
1409
|
aoc_api.lookup_with_q(aoc_res_path(res), value: value)['id']
|
|
@@ -1379,7 +1412,7 @@ module Aspera
|
|
|
1379
1412
|
|
|
1380
1413
|
# admin > <res> > list
|
|
1381
1414
|
ADMIN_OBJECTS.each do |res|
|
|
1382
|
-
define_action_method([:admin, res, :list]) do
|
|
1415
|
+
define_action_method([:admin, res, :list]) do |**|
|
|
1383
1416
|
c = aoc_res_cfg(res)
|
|
1384
1417
|
result_list(c[:path], fields: c[:list_fields], query_component: c[:query_component])
|
|
1385
1418
|
end
|
|
@@ -1396,14 +1429,15 @@ module Aspera
|
|
|
1396
1429
|
|
|
1397
1430
|
# admin > organization|self > show (singleton)
|
|
1398
1431
|
%i[organization self].each do |res|
|
|
1399
|
-
define_action_method([:admin, res, :show]) do
|
|
1432
|
+
define_action_method([:admin, res, :show]) do |**|
|
|
1400
1433
|
Result::SingleObject.new(aoc_api.read(res.to_s, query_read_delete), fields: Formatter.all_but('certificate'))
|
|
1401
1434
|
end
|
|
1402
1435
|
end
|
|
1403
1436
|
|
|
1404
1437
|
# admin > <res> > create
|
|
1405
1438
|
ADMIN_OBJECTS.reject { |r| ADMIN_OBJECT_CONFIG.dig(r, :singleton) }.each do |res|
|
|
1406
|
-
define_action_method([:admin, res, :create]) do |
|
|
1439
|
+
define_action_method([:admin, res, :create]) do |**kwargs|
|
|
1440
|
+
data = kwargs.fetch(res)
|
|
1407
1441
|
c = aoc_res_cfg(res)
|
|
1408
1442
|
path = c[:path]
|
|
1409
1443
|
# Special case: client_registration_token has a different creation URL
|
|
@@ -1418,7 +1452,8 @@ module Aspera
|
|
|
1418
1452
|
|
|
1419
1453
|
# admin > <res> > modify
|
|
1420
1454
|
ADMIN_OBJECTS.reject { |r| ADMIN_OBJECT_CONFIG.dig(r, :singleton) || ADMIN_OBJECT_CONFIG.dig(r, :ops)&.then { |o| !o.include?(:modify) } }.each do |res|
|
|
1421
|
-
define_action_method([:admin, res, :modify]) do
|
|
1455
|
+
define_action_method([:admin, res, :modify]) do |**kwargs|
|
|
1456
|
+
data = kwargs.fetch(res)
|
|
1422
1457
|
res_id = kwargs[:"#{res}_id"]
|
|
1423
1458
|
c = aoc_res_cfg(res)
|
|
1424
1459
|
aoc_api.update("#{c[:path]}/#{res_id}", data)
|
|
@@ -1448,6 +1483,28 @@ module Aspera
|
|
|
1448
1483
|
end
|
|
1449
1484
|
end
|
|
1450
1485
|
|
|
1486
|
+
# automation > workflows > action > list
|
|
1487
|
+
# A workflow has ordered steps (step_order), a step has ordered actions (action_order)
|
|
1488
|
+
def action_automation_workflows_action_list(workflow_id:, **)
|
|
1489
|
+
workflow = @automation_api.read("workflows/#{workflow_id}")
|
|
1490
|
+
actions = Array(workflow['step_order']).flat_map do |step_id|
|
|
1491
|
+
step = @automation_api.read("steps/#{step_id}")
|
|
1492
|
+
Array(step['action_order']).map { |action_id| @automation_api.read("actions/#{action_id}") }
|
|
1493
|
+
end
|
|
1494
|
+
Result::ObjectList.new(actions)
|
|
1495
|
+
end
|
|
1496
|
+
|
|
1497
|
+
# automation > workflows > action > create
|
|
1498
|
+
# Default action type is `manual`
|
|
1499
|
+
def action_automation_workflows_action_create(workflow_id:, action:, **)
|
|
1500
|
+
workflow = @automation_api.read("workflows/#{workflow_id}")
|
|
1501
|
+
step = @automation_api.create('steps', {'workflow_id' => workflow_id})
|
|
1502
|
+
@automation_api.update("workflows/#{workflow_id}", {'step_order' => Array(workflow['step_order']) + [step['id']]})
|
|
1503
|
+
new_action = @automation_api.create('actions', {'type' => 'manual'}.merge(action).merge('step_id' => step['id']))
|
|
1504
|
+
@automation_api.update("steps/#{step['id']}", {'action_order' => [new_action['id']]})
|
|
1505
|
+
Result::SingleObject.new(new_action)
|
|
1506
|
+
end
|
|
1507
|
+
|
|
1451
1508
|
# admin > client > set_pub_key
|
|
1452
1509
|
def action_admin_client_set_pub_key(private_key_pem:, client_id:, **)
|
|
1453
1510
|
c = aoc_res_cfg(:client)
|
|
@@ -1457,24 +1514,19 @@ module Aspera
|
|
|
1457
1514
|
end
|
|
1458
1515
|
|
|
1459
1516
|
# admin > ats — build and return an Ats plugin instance wired to the AoC ATS API.
|
|
1460
|
-
#
|
|
1517
|
+
# Mount target of `admin ats`.
|
|
1461
1518
|
# @return [Ats] configured Ats plugin instance
|
|
1462
|
-
def build_ats_plugin
|
|
1463
|
-
ats_api = Rest.new(**aoc_api.params.deep_merge({
|
|
1519
|
+
def build_ats_plugin(**)
|
|
1520
|
+
ats_api = Rest::Client.new(**aoc_api.params.deep_merge({
|
|
1464
1521
|
base_url: "#{aoc_api.base_url}/admin/ats/pub/v1",
|
|
1465
1522
|
auth: {params: {scope: Api::AoC::Scope::ADMIN_USER}}
|
|
1466
1523
|
}))
|
|
1467
1524
|
Ats.new(context: context, api: ats_api)
|
|
1468
1525
|
end
|
|
1469
1526
|
|
|
1470
|
-
# admin > node > do
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
# admin > node > do > <FILES_COMMAND>
|
|
1474
|
-
FILES_COMMANDS.each do |cmd|
|
|
1475
|
-
define_action_method([:admin, :node, :do, cmd]) do |node_id:, **ctx|
|
|
1476
|
-
execute_nodegen4_command(cmd, node_id, scope: Api::Node::Scope::ADMIN, **ctx)
|
|
1477
|
-
end
|
|
1527
|
+
# admin > node > do - mount target: Gen4 commands on the node, admin scope
|
|
1528
|
+
def admin_node_do_plugin(node_id:, **)
|
|
1529
|
+
nodegen4_plugin(node_id, scope: Api::Node::Scope::ADMIN)
|
|
1478
1530
|
end
|
|
1479
1531
|
|
|
1480
1532
|
# admin > node > bearer_token
|
|
@@ -1494,28 +1546,17 @@ module Aspera
|
|
|
1494
1546
|
{ws_res_id: workspace_id}
|
|
1495
1547
|
end
|
|
1496
1548
|
|
|
1497
|
-
# admin > workspace > dropbox > list
|
|
1498
|
-
def action_admin_workspace_dropbox_list(ws_res_id:, **)
|
|
1499
|
-
query = options.get_option(:query) || {}
|
|
1500
|
-
Result::ObjectList.new(aoc_api.read('dropboxes', query.merge({'workspace_id' => ws_res_id})), fields: %w[id name description])
|
|
1501
|
-
end
|
|
1502
|
-
|
|
1503
1549
|
# admin > workspace > shared_folder — res_id: already in ctx via arguments:(:identifier)
|
|
1504
1550
|
def setup_admin_workspace_shared_folder(workspace_id:, **)
|
|
1505
1551
|
resource_instance_path = "#{aoc_res_path(:workspace)}/#{workspace_id}"
|
|
1506
|
-
query =
|
|
1552
|
+
query = query_read_delete(default: Api::AoC.workspace_access(workspace_id).merge({'admin' => true}))
|
|
1507
1553
|
shared_folders = aoc_api.read_with_paging("#{resource_instance_path}/permissions", query)[:items]
|
|
1508
1554
|
{ws_res_id: workspace_id, shared_folders: shared_folders}
|
|
1509
1555
|
end
|
|
1510
1556
|
|
|
1511
|
-
# admin > workspace > shared_folder >
|
|
1512
|
-
def
|
|
1513
|
-
|
|
1514
|
-
end
|
|
1515
|
-
|
|
1516
|
-
# admin > workspace > shared_folder > node|member — sf_id: already in ctx via arguments:(:identifier)
|
|
1517
|
-
def resolve_sf_item(shared_folders:, sf_id:, **)
|
|
1518
|
-
sf_item = shared_folders.find { |i| i['id'].eql?(sf_id) }
|
|
1557
|
+
# admin > workspace > shared_folder > node|member — shared_folder_id: already in ctx via arguments:(:identifier)
|
|
1558
|
+
def resolve_sf_item(shared_folders:, shared_folder_id:, **)
|
|
1559
|
+
sf_item = shared_folders.find { |i| i['id'].eql?(shared_folder_id) }
|
|
1519
1560
|
Aspera.assert(sf_item, 'shared folder not found')
|
|
1520
1561
|
{sf_item: sf_item}
|
|
1521
1562
|
end
|
|
@@ -1523,11 +1564,9 @@ module Aspera
|
|
|
1523
1564
|
alias_method :setup_admin_workspace_shared_folder_node, :resolve_sf_item
|
|
1524
1565
|
alias_method :setup_admin_workspace_shared_folder_member, :resolve_sf_item
|
|
1525
1566
|
|
|
1526
|
-
# admin > workspace > shared_folder > node
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
execute_nodegen4_command(cmd, sf_item['node_id'], file_id: sf_item['file_id'], scope: Api::Node::Scope::ADMIN, **ctx)
|
|
1530
|
-
end
|
|
1567
|
+
# admin > workspace > shared_folder > node - mount target: Gen4 commands on the shared folder, admin scope
|
|
1568
|
+
def admin_workspace_shared_folder_node_plugin(sf_item:, **)
|
|
1569
|
+
nodegen4_plugin(sf_item['node_id'], file_id: sf_item['file_id'], scope: Api::Node::Scope::ADMIN)
|
|
1531
1570
|
end
|
|
1532
1571
|
|
|
1533
1572
|
# admin > workspace > shared_folder > member > list
|
|
@@ -1555,30 +1594,18 @@ module Aspera
|
|
|
1555
1594
|
end
|
|
1556
1595
|
|
|
1557
1596
|
# admin > user > preferences|notifications > show|modify
|
|
1558
|
-
# (
|
|
1597
|
+
# (user_id: argument of the preferences|notifications node)
|
|
1559
1598
|
%i[preferences notifications].each do |pref|
|
|
1560
1599
|
pref_path = pref.eql?(:preferences) ? 'user_interaction_preferences' : 'notification_preferences'
|
|
1561
1600
|
define_action_method([:admin, :user, pref, :show]) do |user_id:, **|
|
|
1562
1601
|
Result::SingleObject.new(aoc_api.read("#{aoc_res_path(:user)}/#{user_id}/#{pref_path}"))
|
|
1563
1602
|
end
|
|
1564
|
-
define_action_method([:admin, :user, pref, :modify]) do |
|
|
1565
|
-
aoc_api.update("#{aoc_res_path(:user)}/#{user_id}/#{pref_path}",
|
|
1603
|
+
define_action_method([:admin, :user, pref, :modify]) do |user_id:, **kwargs|
|
|
1604
|
+
aoc_api.update("#{aoc_res_path(:user)}/#{user_id}/#{pref_path}", kwargs.fetch(pref))
|
|
1566
1605
|
Result::Status.new('modified')
|
|
1567
1606
|
end
|
|
1568
1607
|
end
|
|
1569
1608
|
|
|
1570
|
-
# automation > workflows > action > * (TODO: not fully implemented)
|
|
1571
|
-
%i[list create show].each do |cmd|
|
|
1572
|
-
define_action_method([:automation, :workflows, :action, cmd]) do |wf_id:, **|
|
|
1573
|
-
Log.log.warn { "Not implemented: #{cmd}" }
|
|
1574
|
-
step = @automation_api.create('steps', {'workflow_id' => wf_id})
|
|
1575
|
-
@automation_api.update("workflows/#{wf_id}", {'step_order' => [step['id']]})
|
|
1576
|
-
action = @automation_api.create('actions', {'step_id' => step['id'], 'type' => 'manual'})
|
|
1577
|
-
@automation_api.update("steps/#{step['id']}", {'action_order' => [action['id']]})
|
|
1578
|
-
Result::SingleObject.new(@automation_api.read("workflows/#{wf_id}"))
|
|
1579
|
-
end
|
|
1580
|
-
end
|
|
1581
|
-
|
|
1582
1609
|
def action_gateway(parameters: {}, **)
|
|
1583
1610
|
require 'aspera/faspex_gw'
|
|
1584
1611
|
parameters = parameters.symbolize_keys
|