aspera-cli 4.26.1 → 4.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +300 -22
- data/CONTRIBUTING.md +100 -12
- data/bin/ascli +5 -5
- data/docs/README.md +10757 -0
- data/docs/test-mcp-with-ai.md +202 -0
- data/lib/aspera/agent/base.rb +20 -6
- data/lib/aspera/agent/connect.rb +53 -16
- data/lib/aspera/agent/desktop.rb +52 -17
- data/lib/aspera/agent/direct.rb +27 -25
- data/lib/aspera/agent/factory.rb +4 -0
- data/lib/aspera/agent/httpgw.rb +2 -2
- data/lib/aspera/agent/node.rb +34 -2
- data/lib/aspera/agent/transferd.rb +39 -3
- data/lib/aspera/api/alee.rb +1 -1
- data/lib/aspera/api/aoc.rb +56 -51
- data/lib/aspera/api/cos_node.rb +2 -2
- data/lib/aspera/api/faspex.rb +22 -20
- data/lib/aspera/api/httpgw.rb +4 -4
- data/lib/aspera/api/node.rb +33 -30
- 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 +25 -23
- data/lib/aspera/ascp/installation.rb +43 -40
- data/lib/aspera/ascp/management.rb +2 -3
- data/lib/aspera/cli/ascp_actions.rb +155 -0
- data/lib/aspera/cli/async_transfer_store.rb +81 -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 +138 -0
- data/lib/aspera/cli/context.rb +71 -0
- data/lib/aspera/cli/error.rb +16 -0
- data/lib/aspera/cli/extended_value.rb +5 -3
- data/lib/aspera/cli/formatter.rb +122 -209
- data/lib/aspera/cli/gem_checker.rb +65 -0
- data/lib/aspera/cli/hints.rb +2 -2
- data/lib/aspera/cli/http.rb +218 -0
- data/lib/aspera/cli/info.rb +2 -0
- data/lib/aspera/cli/mailer.rb +97 -0
- data/lib/aspera/cli/mcp_tool.rb +198 -0
- data/lib/aspera/cli/options.schema.yaml +364 -7
- data/lib/aspera/cli/{manager.rb → parser.rb} +337 -144
- data/lib/aspera/cli/plugins/alee.rb +20 -22
- data/lib/aspera/cli/plugins/aoc.rb +1173 -849
- data/lib/aspera/cli/plugins/ats.rb +200 -161
- data/lib/aspera/cli/plugins/base.rb +470 -107
- data/lib/aspera/cli/plugins/basic_auth.rb +14 -4
- data/lib/aspera/cli/plugins/config.rb +434 -1032
- data/lib/aspera/cli/plugins/console.rb +106 -64
- data/lib/aspera/cli/plugins/cos.rb +44 -32
- data/lib/aspera/cli/plugins/factory.rb +7 -4
- data/lib/aspera/cli/plugins/faspex.rb +296 -259
- data/lib/aspera/cli/plugins/faspex5.rb +592 -344
- data/lib/aspera/cli/plugins/faspio.rb +49 -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 +1001 -797
- data/lib/aspera/cli/plugins/oauth.rb +7 -10
- data/lib/aspera/cli/plugins/orchestrator.rb +111 -139
- data/lib/aspera/cli/plugins/preview.rb +183 -142
- data/lib/aspera/cli/plugins/server.rb +125 -80
- data/lib/aspera/cli/plugins/shares.rb +301 -107
- data/lib/aspera/cli/preset_actions.rb +139 -0
- data/lib/aspera/cli/preset_manager.rb +236 -0
- data/lib/aspera/cli/result.rb +360 -0
- data/lib/aspera/cli/runner.rb +473 -0
- 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 +84 -56
- data/lib/aspera/cli/terminal_formatter.rb +65 -0
- data/lib/aspera/cli/transfer_actions.rb +83 -0
- data/lib/aspera/cli/transfer_agent.rb +116 -51
- data/lib/aspera/cli/transfer_progress.rb +9 -9
- data/lib/aspera/cli/vault_manager.rb +57 -0
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +26 -26
- data/lib/aspera/command_line_builder.rb +23 -23
- data/lib/aspera/data_repository.rb +6 -1
- data/lib/aspera/dot_container.rb +5 -5
- data/lib/aspera/environment.rb +39 -13
- data/lib/aspera/exec_spec.rb +13 -0
- data/lib/aspera/faspex_gw.rb +2 -2
- data/lib/aspera/faspex_postproc.rb +4 -3
- data/lib/aspera/graphql.rb +35 -0
- data/lib/aspera/hash_ext.rb +6 -0
- data/lib/aspera/json_rpc/client.rb +62 -0
- data/lib/aspera/json_rpc/version.rb +7 -0
- data/lib/aspera/keychain/base.rb +1 -1
- data/lib/aspera/keychain/encrypted_hash.rb +2 -2
- data/lib/aspera/keychain/factory.rb +4 -4
- data/lib/aspera/keychain/macos_security.rb +4 -4
- data/lib/aspera/link_header.rb +82 -0
- data/lib/aspera/log.rb +23 -5
- data/lib/aspera/markdown.rb +85 -2
- data/lib/aspera/nagios.rb +2 -2
- data/lib/aspera/node_simulator.rb +3 -1
- data/lib/aspera/oauth/base.rb +10 -10
- data/lib/aspera/oauth/boot.rb +43 -0
- data/lib/aspera/oauth/factory.rb +41 -15
- data/lib/aspera/oauth/jwt.rb +4 -4
- data/lib/aspera/oauth/web.rb +4 -4
- data/lib/aspera/oauth.rb +1 -0
- data/lib/aspera/persistency_action_once.rb +10 -10
- data/lib/aspera/persistency_folder.rb +10 -3
- data/lib/aspera/preview/file_types.rb +7 -3
- data/lib/aspera/preview/generator.rb +3 -3
- data/lib/aspera/preview/utils.rb +1 -1
- data/lib/aspera/products/connect.rb +4 -1
- data/lib/aspera/products/other.rb +1 -1
- data/lib/aspera/products/transferd.rb +3 -3
- data/lib/aspera/proxy_auto_config.rb +10 -9
- data/lib/aspera/rest.rb +18 -40
- data/lib/aspera/rest_error_analyzer.rb +7 -3
- data/lib/aspera/rest_errors_aspera.rb +0 -10
- data/lib/aspera/rest_list.rb +13 -6
- data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +14 -4
- data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +2730 -648
- data/lib/aspera/schema/async_tables.yaml +361 -0
- data/lib/aspera/schema/documentation.rb +57 -26
- data/lib/aspera/schema/reader.rb +60 -11
- data/lib/aspera/schema/registry.rb +50 -10
- data/lib/aspera/secret_hider.rb +11 -2
- data/lib/aspera/ssl.rb +4 -4
- data/lib/aspera/sync/conf.schema.yaml +2 -2
- data/lib/aspera/sync/database.rb +14 -10
- data/lib/aspera/sync/operations.rb +15 -18
- data/lib/aspera/temp_file_manager.rb +5 -1
- data/lib/aspera/timer_limiter.rb +1 -1
- data/lib/aspera/transfer/faux_file.rb +24 -11
- data/lib/aspera/transfer/parameters.rb +27 -25
- data/lib/aspera/transfer/result.rb +74 -0
- data/lib/aspera/transfer/resumer.rb +7 -7
- data/lib/aspera/transfer/spec.rb +18 -1
- data/lib/aspera/transfer/spec.schema.yaml +26 -8
- data/lib/aspera/transfer/uri.rb +1 -1
- data/lib/aspera/uri_reader.rb +2 -2
- data/lib/aspera/web_auth.rb +2 -2
- data/lib/aspera/web_server_simple.rb +8 -5
- data/lib/aspera/yaml.rb +2 -1
- data.tar.gz.sig +0 -0
- metadata +45 -16
- metadata.gz.sig +0 -0
- data/lib/aspera/cli/main.rb +0 -480
- data/lib/aspera/json_rpc.rb +0 -51
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'aspera/schema/registry'
|
|
3
4
|
require 'aspera/cli/plugins/oauth'
|
|
4
5
|
require 'aspera/cli/plugins/node'
|
|
5
6
|
require 'aspera/cli/plugins/ats'
|
|
@@ -7,12 +8,14 @@ require 'aspera/cli/transfer_agent'
|
|
|
7
8
|
require 'aspera/cli/special_values'
|
|
8
9
|
require 'aspera/cli/wizard'
|
|
9
10
|
require 'aspera/agent/node'
|
|
11
|
+
require 'aspera/transfer/result'
|
|
10
12
|
require 'aspera/transfer/spec'
|
|
11
13
|
require 'aspera/api/aoc'
|
|
12
14
|
require 'aspera/api/node'
|
|
13
15
|
require 'aspera/persistency_action_once'
|
|
14
16
|
require 'aspera/id_generator'
|
|
15
17
|
require 'aspera/assert'
|
|
18
|
+
require 'aspera/graphql'
|
|
16
19
|
require 'securerandom'
|
|
17
20
|
require 'date'
|
|
18
21
|
|
|
@@ -27,12 +30,14 @@ module Aspera
|
|
|
27
30
|
client
|
|
28
31
|
client_access_key
|
|
29
32
|
client_registration_token
|
|
33
|
+
configuration_policy
|
|
30
34
|
contact
|
|
31
35
|
dropbox
|
|
32
36
|
dropbox_membership
|
|
33
37
|
group
|
|
34
38
|
group_membership
|
|
35
39
|
kms_profile
|
|
40
|
+
network_policy
|
|
36
41
|
node
|
|
37
42
|
operation
|
|
38
43
|
organization
|
|
@@ -54,11 +59,9 @@ module Aspera
|
|
|
54
59
|
PACKAGE_LIST_DEFAULT_FIELDS = %w[id name created_at files_completed bytes_transferred].freeze
|
|
55
60
|
|
|
56
61
|
private_constant :REDIRECT_LOCALHOST, :ADMIN_OBJECTS, :PACKAGE_RECEIVED_BASE_QUERY, :PACKAGE_LIST_DEFAULT_FIELDS
|
|
57
|
-
|
|
58
|
-
def application_name
|
|
59
|
-
'Aspera on Cloud'
|
|
60
|
-
end
|
|
62
|
+
application_name 'Aspera on Cloud'
|
|
61
63
|
|
|
64
|
+
class << self
|
|
62
65
|
# @return [Hash,NilClass]
|
|
63
66
|
def detect(base_url)
|
|
64
67
|
# no protocol ?
|
|
@@ -97,10 +100,10 @@ module Aspera
|
|
|
97
100
|
# or a sequential number if extension == :seq
|
|
98
101
|
# @param package_info [Hash] Package information
|
|
99
102
|
# @param destination_folder [String] Base folder
|
|
100
|
-
# @param fld
|
|
103
|
+
# @param fld [Array] List of fields of package
|
|
101
104
|
def unique_folder(package_info, destination_folder, fld: nil, seq: false, opt: false)
|
|
102
105
|
Aspera.assert_array_all(fld, String, type: BadArgument){'fld'}
|
|
103
|
-
Aspera.
|
|
106
|
+
Aspera.assert_values(fld.length, [1, 2]){'fld length'}
|
|
104
107
|
folder = Environment.instance.sanitized_filename(package_info[fld[0]])
|
|
105
108
|
if seq
|
|
106
109
|
folder = next_available_folder(folder, always: !opt)
|
|
@@ -108,7 +111,6 @@ module Aspera
|
|
|
108
111
|
# NOTE: it might already exist
|
|
109
112
|
folder = "#{folder}.#{Environment.instance.sanitized_filename(fld[1])}"
|
|
110
113
|
end
|
|
111
|
-
puts("sub= #{folder}")
|
|
112
114
|
File.join(destination_folder, folder)
|
|
113
115
|
end
|
|
114
116
|
end
|
|
@@ -131,7 +133,7 @@ module Aspera
|
|
|
131
133
|
test_args: 'organization'
|
|
132
134
|
}
|
|
133
135
|
end
|
|
134
|
-
options.declare(:use_generic_client, 'Wizard: AoC: use global or org specific jwt client id', allowed: Allowed::TYPES_BOOLEAN, default: Api::AoC.saas_url?(app_url))
|
|
136
|
+
options.declare(:use_generic_client, description: 'Wizard: AoC: use global or org specific jwt client id', allowed: Allowed::TYPES_BOOLEAN, default: Api::AoC.saas_url?(app_url))
|
|
135
137
|
options.parse_options!
|
|
136
138
|
# make username mandatory for jwt, this triggers interactive input
|
|
137
139
|
wiz_username = options.get_option(:username, mandatory: true)
|
|
@@ -178,7 +180,7 @@ module Aspera
|
|
|
178
180
|
end
|
|
179
181
|
myself = aoc_api.read('self')
|
|
180
182
|
if auto_set_pub_key
|
|
181
|
-
Aspera.assert(myself['public_key'].empty?,
|
|
183
|
+
Aspera.assert(myself['public_key'].empty?, 'Public key is already set in profile (use --override=yes)', type: Error) unless option_override
|
|
182
184
|
formatter.display_status('Updating profile with the public key.')
|
|
183
185
|
aoc_api.update("users/#{myself['id']}", {'public_key' => pub_key_pem})
|
|
184
186
|
end
|
|
@@ -199,16 +201,17 @@ module Aspera
|
|
|
199
201
|
}
|
|
200
202
|
end
|
|
201
203
|
|
|
204
|
+
option :workspace, description: 'Name of workspace', allowed: [String, NilClass], default: Api::AoC::DEFAULT_WORKSPACE
|
|
205
|
+
option :new_user_option, description: 'New user creation option for unknown package recipients', allowed: [Hash, NilClass]
|
|
206
|
+
option :validate_metadata, description: 'Validate shared inbox metadata', allowed: Allowed::TYPES_BOOLEAN, default: true
|
|
207
|
+
option :package_folder, schema: Schema::Registry::PACKAGE_FOLDER_OPTIONS
|
|
208
|
+
|
|
202
209
|
def initialize(**_)
|
|
203
210
|
super
|
|
204
211
|
@cache_workspace_info = nil
|
|
205
212
|
@cache_home_node_file = nil
|
|
206
213
|
@cache_api_aoc = nil
|
|
207
214
|
@scope = Api::AoC::Scope::USER
|
|
208
|
-
options.declare(:workspace, 'Name of workspace', allowed: [String, NilClass], default: Api::AoC::DEFAULT_WORKSPACE)
|
|
209
|
-
options.declare(:new_user_option, 'New user creation option for unknown package recipients', allowed: Hash)
|
|
210
|
-
options.declare(:validate_metadata, 'Validate shared inbox metadata', allowed: Allowed::TYPES_BOOLEAN, default: true)
|
|
211
|
-
options.declare(:package_folder, 'Handling of reception of packages in folders', allowed: Hash, default: {})
|
|
212
215
|
options.parse_options!
|
|
213
216
|
# add node plugin options (for manual)
|
|
214
217
|
Node.declare_options(options)
|
|
@@ -231,13 +234,13 @@ module Aspera
|
|
|
231
234
|
api = Api::AoC.new(
|
|
232
235
|
scope: @scope,
|
|
233
236
|
subpath: base_path,
|
|
234
|
-
secret_finder:
|
|
237
|
+
secret_finder: context.secret_finder,
|
|
235
238
|
**Oauth.kwargs_from_options(options)
|
|
236
239
|
)
|
|
237
240
|
# User set a workspace ?
|
|
238
241
|
# @type [String, nil]
|
|
239
242
|
workspace = options.get_option(:workspace)
|
|
240
|
-
if !workspace.nil? && (m =
|
|
243
|
+
if !workspace.nil? && (m = Parser.percent_selector(workspace))
|
|
241
244
|
case m[:field]
|
|
242
245
|
when 'name' then api.ws_ids[:name] = m[:value]
|
|
243
246
|
when 'id' then api.ws_ids[:id] = m[:value]
|
|
@@ -285,11 +288,11 @@ module Aspera
|
|
|
285
288
|
end
|
|
286
289
|
|
|
287
290
|
# Get resource identifier from command line, either directly specifying the `id` or from `name` (percent selector).
|
|
288
|
-
# @param resource_class_path url path for resource
|
|
289
|
-
# @return identifier
|
|
291
|
+
# @param resource_class_path [String] url path for resource
|
|
292
|
+
# @return [String] identifier
|
|
290
293
|
def get_resource_id_from_args(resource_class_path)
|
|
291
294
|
return options.instance_identifier do |field, value|
|
|
292
|
-
Aspera.
|
|
295
|
+
Aspera.assert_values(field, ['name'], type: BadArgument){'selector field'}
|
|
293
296
|
aoc_api.lookup_with_q(resource_class_path, value: value)['id']
|
|
294
297
|
end
|
|
295
298
|
end
|
|
@@ -300,26 +303,28 @@ module Aspera
|
|
|
300
303
|
end
|
|
301
304
|
|
|
302
305
|
# List all entities, given additional, default and user's queries
|
|
303
|
-
# @param resource_class_path path to query on API
|
|
304
|
-
# @param fields fields to display
|
|
305
|
-
# @param base_query
|
|
306
|
-
# @param default_query
|
|
306
|
+
# @param resource_class_path [String] path to query on API
|
|
307
|
+
# @param fields [Array, nil] fields to display
|
|
308
|
+
# @param base_query [Hash] a query applied always
|
|
309
|
+
# @param default_query [Hash] default query unless overridden by user
|
|
310
|
+
# @param query_component [String, nil] registry component key; when set, --query=help shows filter schema
|
|
307
311
|
# @yieldparam query [Hash] The user's or default query for modification
|
|
308
|
-
def result_list(resource_class_path, fields: nil, base_query: {}, default_query: {})
|
|
312
|
+
def result_list(resource_class_path, fields: nil, base_query: {}, default_query: {}, query_component: nil)
|
|
309
313
|
Aspera.assert_type(base_query, Hash)
|
|
310
314
|
Aspera.assert_type(default_query, Hash)
|
|
311
|
-
|
|
315
|
+
qs_path = query_component ? Schema::Registry.query_params(query_component, resource_class_path) : nil
|
|
316
|
+
query = query_read_delete(default: default_query, schema: qs_path)
|
|
312
317
|
# caller may add specific modifications or checks to query
|
|
313
318
|
yield(query) if block_given?
|
|
314
319
|
result = aoc_api.read_with_paging(resource_class_path, base_query.merge(query).compact)
|
|
315
|
-
return
|
|
320
|
+
return Result::ObjectList.new(result[:items], fields: fields, total: result[:total])
|
|
316
321
|
end
|
|
317
322
|
|
|
318
323
|
# Translates `dropbox_name` to `dropbox_id` and fills current workspace_id
|
|
319
324
|
def resolve_dropbox_name_default_ws_id(query)
|
|
320
325
|
if query.key?('dropbox_name')
|
|
321
326
|
# convenience: specify name instead of id
|
|
322
|
-
|
|
327
|
+
Aspera.assert(!query.key?('dropbox_id'), type: BadArgument){'Use field dropbox_name or dropbox_id, not both'}
|
|
323
328
|
# TODO : craft a query that looks for dropbox only in current workspace
|
|
324
329
|
query['dropbox_id'] = aoc_api.lookup_with_q('dropboxes', value: query.delete('dropbox_name'))['id']
|
|
325
330
|
end
|
|
@@ -329,7 +334,6 @@ module Aspera
|
|
|
329
334
|
end
|
|
330
335
|
|
|
331
336
|
# List all packages according to `query` option.
|
|
332
|
-
# @param <none>
|
|
333
337
|
# @return [Hash] {items,total} with all packages according to combination of user's query and default query
|
|
334
338
|
def list_all_packages_with_query
|
|
335
339
|
query = query_read_delete(default: {})
|
|
@@ -342,11 +346,13 @@ module Aspera
|
|
|
342
346
|
FILES_COMMANDS = (Node::COMMANDS_GEN4 + %i[transfer]).freeze
|
|
343
347
|
|
|
344
348
|
# Execute a node gen4 command starting at given node and file IDs
|
|
345
|
-
# @param command_repo
|
|
346
|
-
# @param node_id
|
|
347
|
-
# @param file_id
|
|
348
|
-
# @param scope
|
|
349
|
-
|
|
349
|
+
# @param command_repo [Symbol] Command to execute
|
|
350
|
+
# @param node_id [String] Node identifier
|
|
351
|
+
# @param file_id [String] Root file id for the operation. If nil, uses AK root file id.
|
|
352
|
+
# @param scope [String] node scope (Node::SCOPE_<USER|ADMIN>), or nil (requires secret)
|
|
353
|
+
# @param transfer_direction [Symbol] :push or :pull (only for command_repo == :transfer)
|
|
354
|
+
# @param transfer_source [String] source folder (only for command_repo == :transfer)
|
|
355
|
+
def execute_nodegen4_command(command_repo, node_id, file_id: nil, scope: nil, transfer_direction: nil, transfer_source: nil)
|
|
350
356
|
top_node_api = aoc_api.node_api_from(
|
|
351
357
|
node_id: node_id,
|
|
352
358
|
scope: scope,
|
|
@@ -356,13 +362,16 @@ module Aspera
|
|
|
356
362
|
node_plugin = Node.new(context: context, api: top_node_api)
|
|
357
363
|
case command_repo
|
|
358
364
|
when *Node::COMMANDS_GEN4
|
|
359
|
-
|
|
365
|
+
# For permission: the handler consumes the path first then re-dispatches to sub-commands.
|
|
366
|
+
# Calling dispatch_from_registry with skip_setup would bypass path consumption and fail.
|
|
367
|
+
return node_plugin.send(:"action_access_keys_do_#{command_repo}", do_root_file_id: file_id) if command_repo.eql?(:permission)
|
|
368
|
+
return node_plugin.dispatch_from_registry([:access_keys, :do, command_repo], {do_root_file_id: file_id}, skip_setup: true)
|
|
360
369
|
when :transfer
|
|
361
370
|
# client side is agent
|
|
362
371
|
# server side is transfer server
|
|
363
372
|
# in same workspace
|
|
364
|
-
push_pull =
|
|
365
|
-
source_folder =
|
|
373
|
+
push_pull = transfer_direction
|
|
374
|
+
source_folder = transfer_source
|
|
366
375
|
case push_pull
|
|
367
376
|
when :push
|
|
368
377
|
client_direction = Transfer::Spec::DIRECTION_SEND
|
|
@@ -389,7 +398,7 @@ module Aspera
|
|
|
389
398
|
'destination_root_id' => server_apifid.file_id,
|
|
390
399
|
'source_root_id' => client_apifid.file_id
|
|
391
400
|
}
|
|
392
|
-
return
|
|
401
|
+
return Runner.result_transfer(transfer.start(server_apifid.node_api.transfer_spec_gen4(
|
|
393
402
|
server_apifid.file_id,
|
|
394
403
|
client_direction,
|
|
395
404
|
add_ts
|
|
@@ -401,578 +410,154 @@ module Aspera
|
|
|
401
410
|
|
|
402
411
|
# Execute an action on admin resources
|
|
403
412
|
# @param resource_type [Symbol] One of ADMIN_OBJECTS
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
when :kms_profile
|
|
441
|
-
resource_class_path = "integrations/#{resource_type}s"
|
|
442
|
-
create_schema_path = nil
|
|
443
|
-
when :node
|
|
444
|
-
list_default_fields = %w[id name host access_key]
|
|
445
|
-
supported_operations += %i[do bearer_token]
|
|
446
|
-
when :operation
|
|
447
|
-
list_default_fields = %w[id type status created_at updated_at workspace_id user_id workspace_membership_id group_membership_id]
|
|
448
|
-
supported_operations = %i[list show modify]
|
|
449
|
-
when :organization, :self
|
|
450
|
-
supported_operations = Operations::SINGLETON
|
|
451
|
-
resource_instance_path = resource_class_path = resource_type
|
|
452
|
-
when :short_link
|
|
453
|
-
list_default_fields = %w[id short_url data.url_token_data.purpose password_enabled password_protected updated_by_user_id updated_at]
|
|
454
|
-
when :user
|
|
455
|
-
list_default_fields = %w[id name email]
|
|
456
|
-
supported_operations += %i[preferences notifications]
|
|
457
|
-
when :workspace
|
|
458
|
-
supported_operations += %i[shared_folder dropbox]
|
|
459
|
-
when :workspace_membership
|
|
460
|
-
list_default_fields = %w[id workspace_id member_type member_id]
|
|
413
|
+
# Per-resource configuration for admin CRUD resources.
|
|
414
|
+
# Keys: path, list_fields, id_result, require_ws_id, create_schema, extra_ops, singleton, op_setup
|
|
415
|
+
# op_setup: Hash of op => setup method name, used for ops that require consuming an instance identifier.
|
|
416
|
+
# For Operations::INSTANCE ops (show/modify/delete), use the auto-generated :setup_admin_<res>_instance.
|
|
417
|
+
# For extra_ops that are instance ops, specify explicitly (or rely on the auto-generated one).
|
|
418
|
+
ADMIN_OBJECT_CONFIG = {
|
|
419
|
+
client: {extra_ops: %i[set_pub_key], extra_op_args: {set_pub_key: [{name: :private_key_pem, type: String}]}},
|
|
420
|
+
client_access_key: {path: 'admin/client_access_keys'},
|
|
421
|
+
client_registration_token: {path: 'admin/client_registration_tokens', list_fields: %w[id value data.client_subject_scopes data.name created_at], id_result: 'token'},
|
|
422
|
+
configuration_policy: {list_fields: nil},
|
|
423
|
+
contact: {list_fields: %w[source_type source_id name email]},
|
|
424
|
+
dropbox: {path: 'dropboxes', require_ws_id: true},
|
|
425
|
+
dropbox_membership: {},
|
|
426
|
+
group: {create_schema: false},
|
|
427
|
+
group_membership: {list_fields: %w[id group_id member_type member_id], create_schema: false},
|
|
428
|
+
kms_profile: {path: 'integrations/kms_profiles', create_schema: false},
|
|
429
|
+
network_policy: {list_fields: nil},
|
|
430
|
+
node: {list_fields: %w[id name host access_key], extra_ops: %i[do bearer_token], extra_op_args: {bearer_token: [{name: :scope, mandatory: false, default: nil}]}},
|
|
431
|
+
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]},
|
|
432
|
+
organization: {singleton: true},
|
|
433
|
+
package: {},
|
|
434
|
+
saml_configuration: {create_schema: false},
|
|
435
|
+
self: {singleton: true},
|
|
436
|
+
short_link: {list_fields: %w[id short_url data.url_token_data.purpose password_enabled password_protected updated_by_user_id updated_at]},
|
|
437
|
+
user: {list_fields: %w[id name email], extra_ops: %i[preferences notifications], op_setup: {preferences: :setup_admin_user_instance, notifications: :setup_admin_user_instance}},
|
|
438
|
+
workspace: {extra_ops: %i[shared_folder dropbox], op_setup: {shared_folder: :setup_admin_workspace_shared_folder, dropbox: :setup_admin_workspace_dropbox}},
|
|
439
|
+
workspace_membership: {list_fields: %w[id workspace_id member_type member_id]}
|
|
440
|
+
}.freeze
|
|
441
|
+
private_constant :ADMIN_OBJECT_CONFIG
|
|
442
|
+
|
|
443
|
+
class << self
|
|
444
|
+
# @return [String] AoC REST path for an admin resource type
|
|
445
|
+
def aoc_res_path(res)
|
|
446
|
+
cfg = ADMIN_OBJECT_CONFIG.fetch(res, {})
|
|
447
|
+
return cfg[:path] if cfg[:path]
|
|
448
|
+
"#{res}s".gsub(/ys$/, 'ies')
|
|
461
449
|
end
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
450
|
+
|
|
451
|
+
# @return [Hash] {path:, ops:, id_result:, require_ws_id:, list_fields:, schema:, query_component:}
|
|
452
|
+
def aoc_res_cfg(res)
|
|
453
|
+
cfg = ADMIN_OBJECT_CONFIG.fetch(res, {})
|
|
454
|
+
path = aoc_res_path(res)
|
|
455
|
+
ops = cfg[:ops] || (Base::Operations::ALL + (cfg[:extra_ops] || []))
|
|
456
|
+
schema = cfg[:create_schema] == false ? nil : Schema::Registry.req_body(Schema::Registry::AOC, "#{path}.post")
|
|
457
|
+
{
|
|
458
|
+
path: path,
|
|
459
|
+
ops: ops,
|
|
460
|
+
id_result: cfg[:id_result] || 'id',
|
|
461
|
+
require_ws_id: cfg[:require_ws_id] || false,
|
|
462
|
+
list_fields: cfg.key?(:list_fields) ? cfg[:list_fields] : %w[id name],
|
|
463
|
+
schema: schema,
|
|
464
|
+
query_component: Schema::Registry::AOC
|
|
465
|
+
}
|
|
469
466
|
end
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
467
|
+
|
|
468
|
+
# DSL helper: register the 5 short_link leaf commands under the given parent path
|
|
469
|
+
# and define the corresponding action methods on `base`.
|
|
470
|
+
# @param base [Class] the plugin class
|
|
471
|
+
# @param parent_path [Array<Symbol>] full path ending with :short_link
|
|
472
|
+
def register_short_link_commands(base, parent_path)
|
|
473
|
+
base.commands_under(parent_path) do
|
|
474
|
+
base.command(
|
|
475
|
+
:create, description: 'Create',
|
|
476
|
+
arguments: [{name: :custom_data, type: Hash, mandatory: false, default: {}}]
|
|
477
|
+
)
|
|
478
|
+
base.command(
|
|
479
|
+
:modify, description: 'Modify',
|
|
480
|
+
arguments: [{name: :short_link_id, type: :identifier}, {name: :custom_data, type: Hash, mandatory: false, default: {}}]
|
|
481
|
+
)
|
|
482
|
+
base.command(:list, description: 'List short links')
|
|
483
|
+
base.command(
|
|
484
|
+
:show, description: 'Show a short link',
|
|
485
|
+
arguments: [{name: :short_link_id, type: :identifier}]
|
|
486
|
+
)
|
|
487
|
+
base.command(
|
|
488
|
+
:delete, description: 'Delete a short link',
|
|
489
|
+
arguments: [{name: :short_link_id, type: :identifier}]
|
|
490
|
+
)
|
|
478
491
|
end
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
when :show
|
|
482
|
-
object = aoc_api.read(resource_instance_path, query_read_delete)
|
|
483
|
-
return Main.result_single_object(object, fields: Formatter.all_but('certificate'))
|
|
484
|
-
when :modify
|
|
485
|
-
changes = options.get_next_argument('properties', validation: Hash, schema: schema_create_modify)
|
|
486
|
-
return do_bulk_operation(command: command, values: res_id) do |one_id|
|
|
487
|
-
aoc_api.update("#{resource_class_path}/#{one_id}", changes)
|
|
488
|
-
{'id' => one_id}
|
|
492
|
+
base.define_action_method(parent_path + [:create]) do |custom_data: {}, **ctx|
|
|
493
|
+
sl_exec_create(custom_data, **ctx)
|
|
489
494
|
end
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
aoc_api.delete("#{resource_class_path}/#{one_id}")
|
|
493
|
-
{'id' => one_id}
|
|
495
|
+
base.define_action_method(parent_path + [:list]) do |**ctx|
|
|
496
|
+
sl_exec_list(**sl_fetch_list(**ctx))
|
|
494
497
|
end
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
the_private_key = options.get_next_argument('private_key PEM value', validation: String)
|
|
498
|
-
the_public_key = OpenSSL::PKey::RSA.new(the_private_key).public_key.to_s
|
|
499
|
-
aoc_api.update(resource_instance_path, {jwt_grant_enabled: true, public_key: the_public_key})
|
|
500
|
-
return Main.result_success
|
|
501
|
-
when :do
|
|
502
|
-
command_repo = options.get_next_command(FILES_COMMANDS)
|
|
503
|
-
return execute_nodegen4_command(command_repo, res_id, scope: Api::Node::Scope::ADMIN)
|
|
504
|
-
when :bearer_token
|
|
505
|
-
node_api = aoc_api.node_api_from(
|
|
506
|
-
node_id: res_id,
|
|
507
|
-
scope: options.get_next_argument('scope', default: Api::Node::Scope::ADMIN)
|
|
508
|
-
)
|
|
509
|
-
return Main.result_text(node_api.oauth.authorization)
|
|
510
|
-
when :dropbox
|
|
511
|
-
command_shared = options.get_next_command(%i[list])
|
|
512
|
-
case command_shared
|
|
513
|
-
when :list
|
|
514
|
-
query = options.get_option(:query) || {}
|
|
515
|
-
res_data = aoc_api.read('dropboxes', query.merge({'workspace_id'=>res_id}))
|
|
516
|
-
return Main.result_object_list(res_data, fields: %w[id name description])
|
|
498
|
+
base.define_action_method(parent_path + [:show]) do |**ctx|
|
|
499
|
+
sl_exec_show(**sl_fetch_list(**ctx))
|
|
517
500
|
end
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
shared_folders = aoc_api.read_with_paging("#{resource_instance_path}/permissions", query)[:items]
|
|
521
|
-
# inside a workspace
|
|
522
|
-
command_shared = options.get_next_command(%i[list node member])
|
|
523
|
-
case command_shared
|
|
524
|
-
when :list
|
|
525
|
-
return Main.result_object_list(shared_folders, fields: %w[id node_name node_id file_id file.path tags.aspera.files.workspace.share_as])
|
|
526
|
-
when :node
|
|
527
|
-
shared_folder_id = options.instance_identifier(description: 'Shared folder ID')
|
|
528
|
-
shared_folder = shared_folders.find{ |i| i['id'].eql?(shared_folder_id)}
|
|
529
|
-
Aspera.assert(shared_folder)
|
|
530
|
-
command_repo = options.get_next_command(FILES_COMMANDS)
|
|
531
|
-
return execute_nodegen4_command(command_repo, shared_folder['node_id'], file_id: shared_folder['file_id'], scope: Api::Node::Scope::ADMIN)
|
|
532
|
-
when :member
|
|
533
|
-
shared_folder_id = options.instance_identifier(description: 'Shared folder ID')
|
|
534
|
-
shared_folder = shared_folders.find{ |i| i['id'].eql?(shared_folder_id)}
|
|
535
|
-
Aspera.assert(shared_folder)
|
|
536
|
-
command_shared_member = options.get_next_command(%i[list])
|
|
537
|
-
case command_shared_member
|
|
538
|
-
when :list
|
|
539
|
-
node_api = aoc_api.node_api_from(
|
|
540
|
-
node_id: shared_folder['node_id'],
|
|
541
|
-
workspace_id: res_id,
|
|
542
|
-
workspace_name: nil,
|
|
543
|
-
scope: Api::Node::Scope::USER
|
|
544
|
-
)
|
|
545
|
-
result = node_api.read(
|
|
546
|
-
'permissions',
|
|
547
|
-
{'file_id' => shared_folder['file_id'], 'tag' => "aspera.files.workspace.id=#{res_id}"}
|
|
548
|
-
)
|
|
549
|
-
result.each do |item|
|
|
550
|
-
item['member'] = begin
|
|
551
|
-
if Api::AoC.workspace_access?(item)
|
|
552
|
-
{'name'=>'[Internal permission]'}
|
|
553
|
-
else
|
|
554
|
-
aoc_api.read("admin/#{item['access_type']}s/#{item['access_id']}") rescue {'name': 'not found'}
|
|
555
|
-
end
|
|
556
|
-
rescue => e
|
|
557
|
-
{'name'=>e.to_s}
|
|
558
|
-
end
|
|
559
|
-
end
|
|
560
|
-
# TODO : read users and group name and add, if query "include_members"
|
|
561
|
-
return Main.result_object_list(result, fields: %w[access_type access_id access_level last_updated_at member.name member.email member.system_group_type member.system_group])
|
|
562
|
-
end
|
|
501
|
+
base.define_action_method(parent_path + [:delete]) do |**ctx|
|
|
502
|
+
sl_exec_delete(**sl_fetch_list(**ctx), **ctx)
|
|
563
503
|
end
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
case options.get_next_command(%i[show modify])
|
|
567
|
-
when :show
|
|
568
|
-
return Main.result_single_object(aoc_api.read(user_preferences_res))
|
|
569
|
-
when :modify
|
|
570
|
-
aoc_api.update(user_preferences_res, options.get_next_argument('properties', validation: Hash))
|
|
571
|
-
return Main.result_status('modified')
|
|
504
|
+
base.define_action_method(parent_path + [:modify]) do |custom_data: {}, **ctx|
|
|
505
|
+
sl_exec_modify(custom_data, **sl_fetch_list(**ctx), **ctx)
|
|
572
506
|
end
|
|
573
|
-
else Aspera.error_unexpected_value(command)
|
|
574
507
|
end
|
|
575
508
|
end
|
|
576
509
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
return Main.result_object_list(apps_info)
|
|
584
|
-
when :settings
|
|
585
|
-
app_type = options.get_next_command(all_app_types)
|
|
586
|
-
cmd_path = "/apps/#{app_type}/settings"
|
|
587
|
-
command_app_settings = options.get_next_command(Operations::SINGLETON)
|
|
588
|
-
case command_app_settings
|
|
589
|
-
when :show
|
|
590
|
-
return Main.result_single_object(aoc_api.read(cmd_path))
|
|
591
|
-
when :modify
|
|
592
|
-
aoc_api.update(cmd_path, options.get_next_argument('properties', validation: Hash))
|
|
593
|
-
return Main.result_status('modified')
|
|
594
|
-
end
|
|
595
|
-
when :instance
|
|
596
|
-
list_default_query = {workspace_id: aoc_api.workspace_info[:id]}
|
|
597
|
-
list_default_fields = %w[id app_type available workspace_id]
|
|
598
|
-
command_app_instances = options.get_next_command(%i[list] + Operations::SINGLETON)
|
|
599
|
-
resource_path = 'admin/apps_new'
|
|
600
|
-
if Operations::SINGLETON.include?(command_app_instances)
|
|
601
|
-
app_type = options.get_next_command(all_app_types)
|
|
602
|
-
resource_path = "#{resource_path}/#{app_type}/#{options.instance_identifier(description: "#{app_type} identifier")}"
|
|
603
|
-
end
|
|
604
|
-
case command_app_instances
|
|
605
|
-
when :list
|
|
606
|
-
return result_list(resource_path, fields: list_default_fields, default_query: list_default_query)
|
|
607
|
-
when :show
|
|
608
|
-
return Main.result_single_object(aoc_api.read(resource_path, query_read_delete))
|
|
609
|
-
when :modify
|
|
610
|
-
aoc_api.update(resource_path, options.get_next_argument('properties', validation: Hash))
|
|
611
|
-
return Main.result_status('modified')
|
|
612
|
-
end
|
|
613
|
-
when :membership
|
|
614
|
-
resource_path = 'apps/app_memberships'
|
|
615
|
-
command_app_member = options.get_next_command(%i[create list show delete])
|
|
616
|
-
resource_path = "#{resource_path}/#{options.instance_identifier(description: 'membership id')}" unless Operations::GLOBAL.include?(command_app_member)
|
|
617
|
-
case command_app_member
|
|
618
|
-
when :list
|
|
619
|
-
return result_list(resource_path)
|
|
620
|
-
when :delete
|
|
621
|
-
aoc_api.delete("#{resource_path}}")
|
|
622
|
-
return Main.result_status('deleted')
|
|
623
|
-
when :show
|
|
624
|
-
return Main.result_single_object(aoc_api.read(resource_path, query_read_delete))
|
|
625
|
-
when :create
|
|
626
|
-
aoc_api.update(resource_path, options.get_next_argument('membership properties', validation: Hash))
|
|
627
|
-
return Main.result_status('modified')
|
|
628
|
-
end
|
|
629
|
-
end
|
|
630
|
-
end
|
|
510
|
+
# Instance delegators so instance methods can call aoc_res_path/aoc_res_cfg without self.class.
|
|
511
|
+
def aoc_res_path(res) = self.class.aoc_res_path(res)
|
|
512
|
+
def aoc_res_cfg(res) = self.class.aoc_res_cfg(res)
|
|
513
|
+
|
|
514
|
+
# Known fixed set of AoC application types (verified against API: activity, automation, files, packages)
|
|
515
|
+
APP_TYPES = %i[activity automation files packages].freeze
|
|
631
516
|
|
|
632
517
|
ADMIN_ACTIONS = %i[bearer_token application ats usage_reports analytics subscription auth_providers].concat(ADMIN_OBJECTS).freeze
|
|
633
518
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
return Main.result_text(aoc_api.oauth.authorization)
|
|
641
|
-
when *ADMIN_OBJECTS
|
|
642
|
-
return execute_resource_action(command_admin)
|
|
643
|
-
when :application
|
|
644
|
-
return execute_application_action
|
|
645
|
-
when :auth_providers
|
|
646
|
-
command_auth_prov = options.get_next_command(%i[list update])
|
|
647
|
-
case command_auth_prov
|
|
648
|
-
when :list
|
|
649
|
-
return result_list('admin/auth_providers')
|
|
650
|
-
when :update
|
|
651
|
-
Aspera.error_not_implemented
|
|
652
|
-
end
|
|
653
|
-
when :subscription
|
|
654
|
-
org = aoc_api.read('organization')
|
|
655
|
-
bss_graphql = api_from_options('bss/platform/graphql')
|
|
656
|
-
command_subscription = options.get_next_command(%i[account usage])
|
|
657
|
-
case command_subscription
|
|
658
|
-
when :account
|
|
659
|
-
# cspell:disable
|
|
660
|
-
graphql_query = <<-GRAPHQL
|
|
661
|
-
query ($organization_id: ID!) {
|
|
662
|
-
aoc (organization_id: $organization_id) {
|
|
663
|
-
bssSubscription {
|
|
664
|
-
aocVersion
|
|
665
|
-
endDate
|
|
666
|
-
startDate
|
|
667
|
-
termMonths
|
|
668
|
-
plan
|
|
669
|
-
trial
|
|
670
|
-
termType
|
|
671
|
-
aocOrganizations {
|
|
672
|
-
id
|
|
673
|
-
}
|
|
674
|
-
additionalStorageVolumeGb
|
|
675
|
-
additionalEgressVolumeGb
|
|
676
|
-
term {
|
|
677
|
-
startDate
|
|
678
|
-
endDate
|
|
679
|
-
transferVolumeGb
|
|
680
|
-
egressVolumeGb
|
|
681
|
-
storageVolumeGb
|
|
682
|
-
transferVolumeOffsetGb
|
|
683
|
-
}
|
|
684
|
-
paygoRate {
|
|
685
|
-
transferRate
|
|
686
|
-
storageRate
|
|
687
|
-
currency
|
|
688
|
-
}
|
|
689
|
-
aocPlanData {
|
|
690
|
-
tier
|
|
691
|
-
trial
|
|
692
|
-
workspaces { max }
|
|
693
|
-
users {
|
|
694
|
-
planAmount
|
|
695
|
-
max
|
|
696
|
-
}
|
|
697
|
-
samlIntegration
|
|
698
|
-
activity
|
|
699
|
-
sharedInboxes
|
|
700
|
-
uniqueUrls
|
|
701
|
-
support
|
|
702
|
-
watermarking
|
|
703
|
-
byok
|
|
704
|
-
automation { planAmount, max }
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
GRAPHQL
|
|
710
|
-
# cspell:enable
|
|
711
|
-
result = bss_graphql.create(nil, {query: graphql_query, variables: {organization_id: org['id']}})['data']
|
|
712
|
-
return Main.result_single_object(result['aoc']['bssSubscription'])
|
|
713
|
-
when :usage
|
|
714
|
-
# cspell:disable
|
|
715
|
-
graphql_query = <<-GRAPHQL
|
|
716
|
-
query ($organization_id: ID!, $startDate: Date!, $endDate: Date!, $aggregate: TransferUsageAggregateOption!) {
|
|
717
|
-
aoc (organization_id: $organization_id) {
|
|
718
|
-
bssSubscription {
|
|
719
|
-
aocOrganizations { id }
|
|
720
|
-
additionalStorageVolumeGb
|
|
721
|
-
additionalEgressVolumeGb
|
|
722
|
-
aocPlanData {
|
|
723
|
-
tier
|
|
724
|
-
trial
|
|
725
|
-
}
|
|
726
|
-
term {
|
|
727
|
-
transferVolumeGb
|
|
728
|
-
egressVolumeGb
|
|
729
|
-
storageVolumeGb
|
|
730
|
-
startDate
|
|
731
|
-
endDate
|
|
732
|
-
transferVolumeOffsetGb
|
|
733
|
-
}
|
|
734
|
-
termMonths
|
|
735
|
-
transferUsages (startDate: $startDate, endDate: $endDate, aggregate: $aggregate) { mbTotal }
|
|
736
|
-
egressUsages (startDate: $startDate, endDate: $endDate, aggregate: $aggregate) { usageMb }
|
|
737
|
-
}
|
|
738
|
-
subscriptionEntitlements {
|
|
739
|
-
id
|
|
740
|
-
transferUsages (startDate: $startDate, endDate: $endDate, aggregate: $aggregate) { mbTotal }
|
|
741
|
-
egressUsages (startDate: $startDate, endDate: $endDate, aggregate: $aggregate) { usageMb }
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
GRAPHQL
|
|
746
|
-
aggregate = options.get_next_argument('aggregation', accept_list: %i[ALL MONTHLY], default: :ALL)
|
|
747
|
-
today = Date.today
|
|
748
|
-
start_date = options.get_next_argument('start date', mandatory: false, default: today.prev_year.strftime('%Y-%m-%d'))
|
|
749
|
-
end_date = options.get_next_argument('end date', mandatory: false, default: today.strftime('%Y-%m-%d'))
|
|
750
|
-
# cspell:enable
|
|
751
|
-
result = bss_graphql.create(
|
|
752
|
-
nil,
|
|
753
|
-
{
|
|
754
|
-
query: graphql_query,
|
|
755
|
-
variables: {
|
|
756
|
-
organization_id: org['id'],
|
|
757
|
-
aggregate: aggregate,
|
|
758
|
-
startDate: start_date,
|
|
759
|
-
endDate: end_date
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
)['data']
|
|
763
|
-
return Main.result_single_object(result['aoc'])
|
|
764
|
-
end
|
|
765
|
-
when :ats
|
|
766
|
-
ats_api = Rest.new(**aoc_api.params.deep_merge({
|
|
767
|
-
base_url: "#{aoc_api.base_url}/admin/ats/pub/v1",
|
|
768
|
-
auth: {params: {scope: Api::AoC::Scope::ADMIN_USER}}
|
|
769
|
-
}))
|
|
770
|
-
return Ats.new(context: context, api: ats_api).execute_action
|
|
771
|
-
when :analytics
|
|
772
|
-
analytics_api = Rest.new(**aoc_api.params.deep_merge({
|
|
773
|
-
base_url: "#{aoc_api.base_url.gsub('/api/v1', '')}/analytics/v2",
|
|
774
|
-
auth: {params: {scope: Api::AoC::Scope::ADMIN_USER}}
|
|
775
|
-
}))
|
|
776
|
-
command_analytics = options.get_next_command(%i[application_events transfers files])
|
|
777
|
-
case command_analytics
|
|
778
|
-
when :application_events
|
|
779
|
-
event_type = command_analytics.to_s
|
|
780
|
-
events = analytics_api.read("organizations/#{aoc_api.current_user_info['organization_id']}/#{event_type}")[event_type]
|
|
781
|
-
return Main.result_object_list(events)
|
|
782
|
-
when :transfers
|
|
783
|
-
event_type = command_analytics.to_s
|
|
784
|
-
event_resource_type = options.get_next_argument('resource', accept_list: %i[organizations users nodes])
|
|
785
|
-
event_resource_id = options.get_next_argument("#{event_resource_type} identifier", mandatory: false) ||
|
|
786
|
-
case event_resource_type
|
|
787
|
-
when :organizations then aoc_api.current_user_info['organization_id']
|
|
788
|
-
when :users then aoc_api.current_user_info['id']
|
|
789
|
-
when :nodes then aoc_api.current_user_info['read_only_home_node_id']
|
|
790
|
-
else Aspera.error_unreachable_line
|
|
791
|
-
end
|
|
792
|
-
filter = query_read_delete(default: {})
|
|
793
|
-
filter['limit'] ||= 100
|
|
794
|
-
if options.get_option(:once_only, mandatory: true)
|
|
795
|
-
saved_date = []
|
|
796
|
-
start_date_persistency = PersistencyActionOnce.new(
|
|
797
|
-
manager: persistency,
|
|
798
|
-
data: saved_date,
|
|
799
|
-
id: IdGenerator.from_list(
|
|
800
|
-
'aoc_ana_date',
|
|
801
|
-
options.get_option(:url, mandatory: true),
|
|
802
|
-
aoc_api.workspace_info[:name],
|
|
803
|
-
event_resource_type.to_s,
|
|
804
|
-
event_resource_id
|
|
805
|
-
)
|
|
806
|
-
)
|
|
807
|
-
start_date_time = saved_date.first
|
|
808
|
-
stop_date_time = Time.now.utc.strftime('%FT%T.%LZ')
|
|
809
|
-
saved_date[0] = stop_date_time
|
|
810
|
-
filter['start_time'] = start_date_time unless start_date_time.nil?
|
|
811
|
-
filter['stop_time'] = stop_date_time
|
|
812
|
-
end
|
|
813
|
-
events = analytics_api.read("#{event_resource_type}/#{event_resource_id}/#{event_type}", filter)[event_type]
|
|
814
|
-
start_date_persistency&.save
|
|
815
|
-
if !options.get_option(:notify_to).nil?
|
|
816
|
-
events.each do |tr_event|
|
|
817
|
-
config.send_email_template(values: {ev: tr_event})
|
|
818
|
-
end
|
|
819
|
-
end
|
|
820
|
-
return Main.result_object_list(events)
|
|
821
|
-
when :files
|
|
822
|
-
event_type = command_analytics.to_s
|
|
823
|
-
event_resource_type = options.get_next_argument('resource', accept_list: %i[organizations users nodes])
|
|
824
|
-
event_resource_id = options.instance_identifier(description: "#{event_resource_type} identifier")
|
|
825
|
-
event_resource_id =
|
|
826
|
-
case event_resource_type
|
|
827
|
-
when :organizations then aoc_api.current_user_info['organization_id']
|
|
828
|
-
when :users then aoc_api.current_user_info['id']
|
|
829
|
-
when :nodes then aoc_api.current_user_info['read_only_home_node_id']
|
|
830
|
-
else Aspera.error_unreachable_line
|
|
831
|
-
end if event_resource_id.empty?
|
|
832
|
-
event_uuid = options.instance_identifier(description: 'event uuid')
|
|
833
|
-
filter = query_read_delete(default: {})
|
|
834
|
-
filter['limit'] ||= 100
|
|
835
|
-
events = analytics_api.read("#{event_resource_type}/#{event_resource_id}/transfers/#{event_uuid}/#{event_type}", filter)[event_type]
|
|
836
|
-
return Main.result_object_list(events)
|
|
837
|
-
end
|
|
838
|
-
when :usage_reports
|
|
839
|
-
return result_list('usage_reports', base_query: workspace_id_hash)
|
|
840
|
-
end
|
|
519
|
+
# Build analytics REST API (shared by action_admin_analytics_*)
|
|
520
|
+
def build_analytics_api
|
|
521
|
+
Rest.new(**aoc_api.params.deep_merge({
|
|
522
|
+
base_url: "#{aoc_api.base_url.gsub('/api/v1', '')}/analytics/v2",
|
|
523
|
+
auth: {params: {scope: Api::AoC::Scope::ADMIN_USER}}
|
|
524
|
+
}))
|
|
841
525
|
end
|
|
842
526
|
|
|
843
|
-
#
|
|
844
|
-
#
|
|
845
|
-
#
|
|
846
|
-
#
|
|
847
|
-
|
|
848
|
-
# - a short URL path, e.g. `dxyRpT9`
|
|
849
|
-
# @param shared_data [Hash] Information for shared data: dropbox_id+name or file_id+node_id
|
|
850
|
-
# @param shared_data [Hash] Shared data for the short link
|
|
851
|
-
# @yieldparam operation [Symbol] Operation type (:create, :update, :delete)
|
|
852
|
-
# @yieldparam resource_id [String] Resource ID for permission management
|
|
853
|
-
# @yieldparam access_levels [Object] Access levels for permissions
|
|
854
|
-
def short_link_command(**shared_data, &perm_block)
|
|
855
|
-
link_type = options.get_next_argument('link access (public or private)', accept_list: %i[public private])
|
|
527
|
+
# Compute short-link purposes from shared_data keys and link_type.
|
|
528
|
+
# @param shared_data [Hash] :dropbox_id+:name or :file_id+:node_id
|
|
529
|
+
# @param link_type [Symbol] :public or :private
|
|
530
|
+
# @return [Array(String,String)] [token_purpose, short_link_purpose]
|
|
531
|
+
def short_link_purposes(shared_data, link_type)
|
|
856
532
|
if shared_data.keys.sort == %i[dropbox_id name]
|
|
857
|
-
# Packages app, Shared inbox
|
|
858
533
|
token_purpose = 'send_package_to_dropbox'
|
|
859
534
|
short_link_purpose = link_type.eql?(:public) ? 'send_package_to_dropbox' : 'shared_folder_auth_link'
|
|
860
535
|
elsif shared_data.keys.sort == %i[file_id node_id]
|
|
861
|
-
# Files app, Shared folder
|
|
862
536
|
token_purpose = 'view_shared_file'
|
|
863
537
|
short_link_purpose = link_type.eql?(:public) ? 'token_auth_redirection' : 'shared_folder_auth_link'
|
|
864
538
|
else
|
|
865
539
|
Aspera.error_unexpected_value(shared_data.keys)
|
|
866
540
|
end
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
when :private
|
|
878
|
-
create_payload[:data] = shared_data
|
|
879
|
-
when :public
|
|
880
|
-
create_payload[:expires_at] = nil
|
|
881
|
-
create_payload[:password_enabled] = false
|
|
882
|
-
shared_data[:name] = ''
|
|
883
|
-
create_payload[:data] = {
|
|
884
|
-
aoc: true,
|
|
885
|
-
url_token_data: {
|
|
886
|
-
data: shared_data,
|
|
887
|
-
purpose: token_purpose
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
end
|
|
891
|
-
custom_data = value_create_modify(command: command, default: {})
|
|
892
|
-
access_levels = custom_data.delete('access_levels')
|
|
893
|
-
if (pass = custom_data.delete('password'))
|
|
894
|
-
create_payload[:data][:url_token_data][:password] = pass
|
|
895
|
-
create_payload[:password_enabled] = true
|
|
896
|
-
end
|
|
897
|
-
create_payload.deep_merge!(custom_data)
|
|
898
|
-
result_create_short_link = aoc_api.create('short_links', create_payload)
|
|
899
|
-
# Creation: perm_block: permission on node
|
|
900
|
-
yield(:create, result_create_short_link['resource_id'], access_levels) if block_given? && link_type.eql?(:public)
|
|
901
|
-
return Main.result_single_object(result_create_short_link)
|
|
902
|
-
when :delete, :list, :show, :modify
|
|
903
|
-
workspace_id_hash(shared_data)
|
|
904
|
-
query = if link_type.eql?(:private)
|
|
905
|
-
shared_data
|
|
906
|
-
else
|
|
907
|
-
{
|
|
908
|
-
url_token_data: {
|
|
909
|
-
data: shared_data,
|
|
910
|
-
purpose: token_purpose
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
end
|
|
914
|
-
list_params = {
|
|
915
|
-
json_query: query.to_json,
|
|
916
|
-
purpose: short_link_purpose,
|
|
917
|
-
edit_access: true,
|
|
918
|
-
# embed: 'updated_by_user',
|
|
919
|
-
sort: '-created_at'
|
|
920
|
-
}
|
|
921
|
-
short_list = aoc_api.read_with_paging('short_links', list_params.merge(query_read_delete(default: {})).compact)
|
|
922
|
-
case command
|
|
923
|
-
when :delete
|
|
924
|
-
one_id = options.instance_identifier(description: 'short link id')
|
|
925
|
-
if link_type.eql?(:public)
|
|
926
|
-
found = short_list[:items].find{ |item| item['id'].eql?(one_id)}
|
|
927
|
-
raise BadIdentifier.new('Short link', one_id) if found.nil?
|
|
928
|
-
yield(:delete, found['resource_id'], nil)
|
|
929
|
-
end
|
|
930
|
-
aoc_api.delete("short_links/#{one_id}", {
|
|
931
|
-
edit_access: true,
|
|
932
|
-
json_query: shared_data.to_json
|
|
933
|
-
})
|
|
934
|
-
return Main.result_status('deleted')
|
|
935
|
-
when :list
|
|
936
|
-
return Main.result_object_list(short_list[:items], fields: Formatter.all_but('data'), total: short_list[:total])
|
|
937
|
-
when :show
|
|
938
|
-
one_id = options.instance_identifier(description: 'short link id')
|
|
939
|
-
found = short_list[:items].find{ |item| item['id'].eql?(one_id)}
|
|
940
|
-
raise BadIdentifier.new('Short link', one_id) if found.nil?
|
|
941
|
-
return Main.result_single_object(found, fields: Formatter.all_but('data'))
|
|
942
|
-
when :modify
|
|
943
|
-
one_id = options.instance_identifier(description: 'short link id')
|
|
944
|
-
node_file = shared_data.slice(:node_id, :file_id)
|
|
945
|
-
modify_payload = {
|
|
946
|
-
edit_access: true,
|
|
947
|
-
json_query: node_file
|
|
948
|
-
}
|
|
949
|
-
custom_data = value_create_modify(command: command)
|
|
950
|
-
if (pass = custom_data.delete('password'))
|
|
951
|
-
modify_payload[:password_enabled] = true
|
|
952
|
-
modify_payload[:data] = {
|
|
953
|
-
url_token_data: {
|
|
954
|
-
password: pass,
|
|
955
|
-
data: node_file
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
else
|
|
959
|
-
modify_payload[:password_enabled] = false
|
|
960
|
-
end
|
|
961
|
-
if custom_data.delete('access_levels')
|
|
962
|
-
# Modification: perm_block: permission on node
|
|
963
|
-
found = short_list[:items].find{ |item| item['id'].eql?(one_id)}
|
|
964
|
-
raise BadIdentifier.new('Short link', one_id) if found.nil?
|
|
965
|
-
yield(:update, found['resource_id'], access_levels)
|
|
966
|
-
end
|
|
967
|
-
modify_payload.deep_merge!(custom_data)
|
|
968
|
-
aoc_api.update("short_links/#{one_id}", modify_payload)
|
|
969
|
-
return Main.result_status('modified')
|
|
970
|
-
end
|
|
971
|
-
else Aspera.error_unexpected_value(command)
|
|
541
|
+
[token_purpose, short_link_purpose]
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
# Build the list_params hash used by delete/list/show/modify short link operations.
|
|
545
|
+
# @return [Hash]
|
|
546
|
+
def short_link_list_params(shared_data:, link_type:, token_purpose:, short_link_purpose:, **)
|
|
547
|
+
query = if link_type.eql?(:private)
|
|
548
|
+
shared_data
|
|
549
|
+
else
|
|
550
|
+
{url_token_data: {data: shared_data, purpose: token_purpose}}
|
|
972
551
|
end
|
|
552
|
+
{
|
|
553
|
+
json_query: query.to_json,
|
|
554
|
+
purpose: short_link_purpose,
|
|
555
|
+
edit_access: true,
|
|
556
|
+
sort: '-created_at'
|
|
557
|
+
}
|
|
973
558
|
end
|
|
974
559
|
|
|
975
|
-
# @return persistency object if option `once_only` is used.
|
|
560
|
+
# @return [PersistencyActionOnce, nil] persistency object if option `once_only` is used.
|
|
976
561
|
def package_persistency
|
|
977
562
|
return unless options.get_option(:once_only, mandatory: true)
|
|
978
563
|
# TODO: add query info to id
|
|
@@ -994,288 +579,1027 @@ module Aspera
|
|
|
994
579
|
all_packages.reject!{ |pkg| skip_package[pkg['id']]}
|
|
995
580
|
end
|
|
996
581
|
|
|
997
|
-
#
|
|
998
|
-
ACTIONS = %i[reminder servers bearer_token organization tier_restrictions user packages files admin automation gateway].freeze
|
|
582
|
+
# --- DSL command declarations ---
|
|
999
583
|
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
end
|
|
584
|
+
# Root-level commands
|
|
585
|
+
command :reminder, description: 'Send reminder email with list of orgs'
|
|
586
|
+
command(
|
|
587
|
+
:servers, description: 'List AoC servers (no auth)',
|
|
588
|
+
action: lambda do
|
|
589
|
+
no_auth_api = Api::AoC.new(url: options.get_option(:url), auth: :none)
|
|
590
|
+
Result::ObjectList.new(no_auth_api.read('servers'))
|
|
1008
591
|
end
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
when :list
|
|
1033
|
-
return result_list('workspaces', fields: %w[id name])
|
|
1034
|
-
when :current
|
|
1035
|
-
return Main.result_single_object(aoc_api.workspace_info)
|
|
1036
|
-
end
|
|
1037
|
-
when :profile
|
|
1038
|
-
case options.get_next_command(%i[show modify])
|
|
1039
|
-
when :show
|
|
1040
|
-
return Main.result_single_object(aoc_api.current_user_info(exception: true))
|
|
1041
|
-
when :modify
|
|
1042
|
-
aoc_api.update("users/#{aoc_api.current_user_info(exception: true)['id']}", options.get_next_argument('properties', validation: Hash))
|
|
1043
|
-
return Main.result_status('modified')
|
|
1044
|
-
end
|
|
1045
|
-
when :preferences, :notifications
|
|
1046
|
-
user_preferences_res = "users/#{aoc_api.current_user_info(exception: true)['id']}/#{user_cmd.eql?(:preferences) ? 'user_interaction_preferences' : 'notification_preferences'}"
|
|
1047
|
-
case options.get_next_command(%i[show modify])
|
|
1048
|
-
when :show
|
|
1049
|
-
return Main.result_single_object(aoc_api.read(user_preferences_res))
|
|
1050
|
-
when :modify
|
|
1051
|
-
aoc_api.update(user_preferences_res, options.get_next_argument('properties', validation: Hash))
|
|
1052
|
-
return Main.result_status('modified')
|
|
1053
|
-
end
|
|
592
|
+
)
|
|
593
|
+
command :bearer_token, description: 'Display bearer token',
|
|
594
|
+
action: ->{Result::Text.new(aoc_api.oauth.authorization)}
|
|
595
|
+
command :organization, description: 'Show organization info',
|
|
596
|
+
action: ->{Result::SingleObject.new(aoc_api.read('organization'))}
|
|
597
|
+
command :tier_restrictions, description: 'Show tier restrictions',
|
|
598
|
+
action: ->{Result::SingleObject.new(aoc_api.read('tier_restrictions'))}
|
|
599
|
+
command :user, description: 'User commands'
|
|
600
|
+
command :packages, description: 'Package commands', setup: :setup_workspace_display
|
|
601
|
+
command :files, description: 'Files commands (workspace-aware)', setup: :setup_workspace_display
|
|
602
|
+
command :admin, description: 'Administration commands', setup: :setup_admin_scope
|
|
603
|
+
commands_under(:admin) do
|
|
604
|
+
command :bearer_token, description: 'Show admin bearer token',
|
|
605
|
+
action: ->{Result::Text.new(aoc_api.oauth.authorization)}
|
|
606
|
+
command :application, description: 'Manage applications'
|
|
607
|
+
command(
|
|
608
|
+
:ats, description: 'Manage ATS (Aspera Transfer Service)',
|
|
609
|
+
action: lambda do
|
|
610
|
+
ats_api = Rest.new(**aoc_api.params.deep_merge({
|
|
611
|
+
base_url: "#{aoc_api.base_url}/admin/ats/pub/v1",
|
|
612
|
+
auth: {params: {scope: Api::AoC::Scope::ADMIN_USER}}
|
|
613
|
+
}))
|
|
614
|
+
Ats.new(context: context, api: ats_api).execute_action
|
|
1054
615
|
end
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
skip_ids_persistency = package_persistency
|
|
1099
|
-
case ids_to_download
|
|
1100
|
-
when SpecialValues::INIT
|
|
1101
|
-
all_packages = list_all_packages_with_query[:items]
|
|
1102
|
-
Aspera.assert(skip_ids_persistency){'INIT requires option once_only'}
|
|
1103
|
-
skip_ids_persistency.data.clear.concat(all_packages.map{ |e| e['id']})
|
|
1104
|
-
skip_ids_persistency.save
|
|
1105
|
-
return Main.result_status("Initialized skip for #{skip_ids_persistency.data.count} package(s)")
|
|
1106
|
-
when SpecialValues::ALL
|
|
1107
|
-
all_packages = list_all_packages_with_query[:items]
|
|
1108
|
-
# remove from list the ones already downloaded
|
|
1109
|
-
reject_packages_from_persistency(all_packages, skip_ids_persistency)
|
|
1110
|
-
ids_to_download = all_packages.map{ |e| e['id']}
|
|
1111
|
-
formatter.display_status("Found #{ids_to_download.length} package(s).")
|
|
1112
|
-
else
|
|
1113
|
-
# single id to array
|
|
1114
|
-
ids_to_download = [ids_to_download] unless ids_to_download.is_a?(Array)
|
|
1115
|
-
end
|
|
1116
|
-
# download all files, or specified list only
|
|
1117
|
-
ts_paths = transfer.ts_source_paths(default: ['.'])
|
|
1118
|
-
per_package_def = options.get_option(:package_folder).symbolize_keys
|
|
1119
|
-
save_metadata = per_package_def.delete(:inf)
|
|
1120
|
-
# get value outside of loop
|
|
1121
|
-
destination_folder = transfer.destination_folder(Transfer::Spec::DIRECTION_RECEIVE)
|
|
1122
|
-
result_transfer = []
|
|
1123
|
-
ids_to_download.each do |package_id|
|
|
1124
|
-
package_info = aoc_api.read("packages/#{package_id}")
|
|
1125
|
-
package_node_api = aoc_api.node_api_from(
|
|
1126
|
-
node_id: package_info['node_id'],
|
|
1127
|
-
package_info: package_info,
|
|
1128
|
-
**workspace_id_hash(name: true)
|
|
1129
|
-
)
|
|
1130
|
-
transfer_spec = package_node_api.transfer_spec_gen4(
|
|
1131
|
-
package_info['contents_file_id'],
|
|
1132
|
-
Transfer::Spec::DIRECTION_RECEIVE,
|
|
1133
|
-
{'paths'=> ts_paths}
|
|
1134
|
-
)
|
|
1135
|
-
transfer.user_transfer_spec['destination_root'] = self.class.unique_folder(package_info, destination_folder, **per_package_def) unless per_package_def.empty?
|
|
1136
|
-
dest_folder = transfer.user_transfer_spec['destination_root'] || destination_folder
|
|
1137
|
-
formatter.display_status(%Q{Downloading package: [#{package_info['id']}] "#{package_info['name']}" to [#{dest_folder}]})
|
|
1138
|
-
statuses = transfer.start(
|
|
1139
|
-
transfer_spec,
|
|
1140
|
-
rest_token: package_node_api
|
|
1141
|
-
)
|
|
1142
|
-
File.write(File.join(dest_folder, "#{package_id}.info.json"), package_info.to_json) if save_metadata
|
|
1143
|
-
result_transfer.push({'package' => package_id, Main::STATUS_FIELD => statuses})
|
|
1144
|
-
# update skip list only if all transfer sessions completed
|
|
1145
|
-
if skip_ids_persistency && TransferAgent.session_status(statuses).eql?(:success)
|
|
1146
|
-
skip_ids_persistency.data.push(package_id)
|
|
1147
|
-
skip_ids_persistency.save
|
|
616
|
+
)
|
|
617
|
+
command :usage_reports, description: 'List usage reports',
|
|
618
|
+
action: ->{result_list('usage_reports', base_query: workspace_id_hash)}
|
|
619
|
+
command :auth_providers, description: 'Manage auth providers'
|
|
620
|
+
command :subscription, description: 'Show subscription info'
|
|
621
|
+
command :analytics, description: 'Query analytics'
|
|
622
|
+
ADMIN_OBJECTS.each do |res|
|
|
623
|
+
cfg = ADMIN_OBJECT_CONFIG.fetch(res, {})
|
|
624
|
+
op_setup = cfg[:op_setup] || {}
|
|
625
|
+
extra_op_args = cfg[:extra_op_args] || {}
|
|
626
|
+
is_singleton = cfg[:singleton]
|
|
627
|
+
id_arg_spec = is_singleton ? [] : [{name: :res_id, type: :identifier, lookup: :"lookup_aoc_#{res}_id"}]
|
|
628
|
+
ops = if cfg[:ops]
|
|
629
|
+
cfg[:ops]
|
|
630
|
+
elsif is_singleton
|
|
631
|
+
%i[show]
|
|
632
|
+
else
|
|
633
|
+
Operations::ALL + (cfg[:extra_ops] || [])
|
|
634
|
+
end
|
|
635
|
+
command(res, description: "Manage #{res.to_s.tr('_', ' ')}")
|
|
636
|
+
commands_under([:admin, res]) do
|
|
637
|
+
ops.each do |op|
|
|
638
|
+
extra_setup = op_setup[op]
|
|
639
|
+
base_attrs = extra_setup ? {setup: extra_setup} : {}
|
|
640
|
+
extra_arg_list =
|
|
641
|
+
if !is_singleton && op.eql?(:create)
|
|
642
|
+
c = aoc_res_cfg(res)
|
|
643
|
+
[{name: :data, type: Hash, bulk: true, schema: c[:schema]}]
|
|
644
|
+
elsif !is_singleton && op.eql?(:modify)
|
|
645
|
+
c = aoc_res_cfg(res)
|
|
646
|
+
[{name: :data, type: Hash, schema: c[:schema]}]
|
|
647
|
+
elsif extra_op_args.key?(op)
|
|
648
|
+
extra_op_args[op]
|
|
649
|
+
else
|
|
650
|
+
[]
|
|
651
|
+
end
|
|
652
|
+
# For non-global ops, prepend the identifier spec
|
|
653
|
+
arg_list = (Operations::GLOBAL.include?(op) ? [] : id_arg_spec) + extra_arg_list
|
|
654
|
+
merged = arg_list.empty? ? base_attrs : base_attrs.merge(arguments: arg_list)
|
|
655
|
+
# Attach query_schema (full path) to :list CommandSpec so --help shows the tip
|
|
656
|
+
if op.eql?(:list)
|
|
657
|
+
c = aoc_res_cfg(res)
|
|
658
|
+
merged = merged.merge(query_schema: Schema::Registry.query_params(c[:query_component], c[:path]))
|
|
1148
659
|
end
|
|
660
|
+
command(op, description: op.to_s.tr('_', ' ').capitalize, **merged)
|
|
1149
661
|
end
|
|
1150
|
-
return Main.result_transfer_multiple(result_transfer)
|
|
1151
|
-
when :show
|
|
1152
|
-
package_id = options.instance_identifier
|
|
1153
|
-
package_info = aoc_api.read("packages/#{package_id}")
|
|
1154
|
-
return Main.result_single_object(package_info)
|
|
1155
|
-
when :list
|
|
1156
|
-
result = list_all_packages_with_query
|
|
1157
|
-
skip_ids_persistency = package_persistency
|
|
1158
|
-
reject_packages_from_persistency(result[:items], skip_ids_persistency)
|
|
1159
|
-
display_fields = PACKAGE_LIST_DEFAULT_FIELDS
|
|
1160
|
-
display_fields += ['workspace_id'] if aoc_api.workspace_info[:id].nil?
|
|
1161
|
-
return Main.result_object_list(result[:items], fields: display_fields, total: result[:total])
|
|
1162
|
-
when :delete
|
|
1163
|
-
return do_bulk_operation(command: package_command, values: options.instance_identifier) do |package_id|
|
|
1164
|
-
Aspera.assert_type(package_id, String, Integer){'identifier'}
|
|
1165
|
-
aoc_api.delete("packages/#{package_id}")
|
|
1166
|
-
end
|
|
1167
|
-
when :modify
|
|
1168
|
-
package_id = options.instance_identifier
|
|
1169
|
-
package_data = value_create_modify(command: package_command)
|
|
1170
|
-
aoc_api.update("packages/#{package_id}", package_data)
|
|
1171
|
-
return Main.result_status('modified')
|
|
1172
|
-
when *Node::NODE4_READ_ACTIONS
|
|
1173
|
-
package_id = options.instance_identifier
|
|
1174
|
-
package_info = aoc_api.read("packages/#{package_id}")
|
|
1175
|
-
return execute_nodegen4_command(package_command, package_info['node_id'], file_id: package_info['contents_file_id'], scope: Api::Node::Scope::USER)
|
|
1176
662
|
end
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
663
|
+
end
|
|
664
|
+
end
|
|
665
|
+
# admin > workspace > shared_folder sub-tree
|
|
666
|
+
commands_under(%i[admin workspace shared_folder]) do
|
|
667
|
+
command :list, description: 'List shared folders'
|
|
668
|
+
command :node, description: 'Execute node command on shared folder',
|
|
669
|
+
arguments: [{name: :sf_id, type: :identifier}],
|
|
670
|
+
setup: :setup_admin_workspace_shared_folder_node
|
|
671
|
+
command :member, description: 'Show folder members',
|
|
672
|
+
arguments: [{name: :sf_id, type: :identifier}],
|
|
673
|
+
setup: :setup_admin_workspace_shared_folder_member
|
|
674
|
+
end
|
|
675
|
+
# admin > workspace > shared_folder > node sub-tree (FILES_COMMANDS)
|
|
676
|
+
commands_under(%i[admin workspace shared_folder node]) do
|
|
677
|
+
FILES_COMMANDS.each{ |c| command(c, description: c.to_s.tr('_', ' ').capitalize)}
|
|
678
|
+
end
|
|
679
|
+
commands_under(%i[admin workspace shared_folder member]) do
|
|
680
|
+
command :list, description: 'List members of a shared folder'
|
|
681
|
+
end
|
|
682
|
+
# admin > workspace > dropbox sub-tree
|
|
683
|
+
commands_under(%i[admin workspace dropbox]) do
|
|
684
|
+
command :list, description: 'List dropboxes in workspace'
|
|
685
|
+
end
|
|
686
|
+
# admin > node > do sub-tree (FILES_COMMANDS)
|
|
687
|
+
commands_under(%i[admin node do]) do
|
|
688
|
+
FILES_COMMANDS.each{ |c| command(c, description: c.to_s.tr('_', ' ').capitalize)}
|
|
689
|
+
end
|
|
690
|
+
# admin > user > preferences|notifications sub-trees
|
|
691
|
+
%i[preferences notifications].each do |pref|
|
|
692
|
+
commands_under([:admin, :user, pref]) do
|
|
693
|
+
command :show, description: "Show #{pref}"
|
|
694
|
+
command :modify, description: "Modify #{pref}",
|
|
695
|
+
arguments: [{name: :properties, type: Hash}]
|
|
696
|
+
end
|
|
697
|
+
end
|
|
698
|
+
commands_under(%i[admin auth_providers]) do
|
|
699
|
+
command :list, description: 'List auth providers',
|
|
700
|
+
action: ->{result_list('admin/auth_providers')}
|
|
701
|
+
command :update, description: 'Update auth provider', action: ->{Aspera.error_not_implemented}
|
|
702
|
+
end
|
|
703
|
+
commands_under(%i[admin subscription]) do
|
|
704
|
+
command :account, description: 'Show subscription account'
|
|
705
|
+
command :usage, description: 'Show subscription usage',
|
|
706
|
+
arguments: [
|
|
707
|
+
{name: :aggregate, mandatory: false, default: :ALL, allowed: %i[ALL MONTHLY]},
|
|
708
|
+
{name: :start_date, mandatory: false, default: nil},
|
|
709
|
+
{name: :end_date, mandatory: false, default: nil}
|
|
710
|
+
]
|
|
711
|
+
end
|
|
712
|
+
commands_under(%i[admin analytics]) do
|
|
713
|
+
command :application_events, description: 'List application events'
|
|
714
|
+
command :transfers, description: 'List transfer events',
|
|
715
|
+
arguments: [
|
|
716
|
+
{name: :event_resource_type, mandatory: true, allowed: %i[organizations users nodes]},
|
|
717
|
+
{name: :event_resource_id, mandatory: false, default: nil}
|
|
718
|
+
]
|
|
719
|
+
command :files, description: 'List file events',
|
|
720
|
+
arguments: [
|
|
721
|
+
{name: :event_resource_type, mandatory: true, allowed: %i[organizations users nodes]},
|
|
722
|
+
{name: :event_resource_id, mandatory: true},
|
|
723
|
+
{name: :event_uuid, mandatory: true}
|
|
724
|
+
]
|
|
725
|
+
end
|
|
726
|
+
# application sub-commands
|
|
727
|
+
commands_under(%i[admin application]) do
|
|
728
|
+
command :types, description: 'List application types',
|
|
729
|
+
action: ->{Result::ObjectList.new(aoc_api.read('admin/apps'))}
|
|
730
|
+
command :settings, description: 'Manage per-app-type settings'
|
|
731
|
+
command :instance, description: 'Manage app instances'
|
|
732
|
+
command :membership, description: 'Manage app memberships'
|
|
733
|
+
end
|
|
734
|
+
APP_SETTINGS_PATH = %i[admin application settings].freeze
|
|
735
|
+
APP_INSTANCE_PATH = %i[admin application instance].freeze
|
|
736
|
+
private_constant :APP_SETTINGS_PATH, :APP_INSTANCE_PATH
|
|
737
|
+
commands_under(APP_SETTINGS_PATH) do
|
|
738
|
+
APP_TYPES.each do |app_type|
|
|
739
|
+
command(app_type, description: "Settings for #{app_type} app")
|
|
740
|
+
commands_under(APP_SETTINGS_PATH + [app_type]) do
|
|
741
|
+
command :show, description: "Show #{app_type} settings",
|
|
742
|
+
action: ->{Result::SingleObject.new(aoc_api.read("/apps/#{app_type}/settings"))}
|
|
743
|
+
command(
|
|
744
|
+
:modify, description: "Modify #{app_type} settings",
|
|
745
|
+
arguments: [{name: :properties, type: Hash}],
|
|
746
|
+
action: lambda do |properties:, **|
|
|
747
|
+
aoc_api.update("/apps/#{app_type}/settings", properties)
|
|
748
|
+
Result::Status.new('modified')
|
|
1224
749
|
end
|
|
1225
|
-
|
|
750
|
+
)
|
|
751
|
+
end
|
|
752
|
+
end
|
|
753
|
+
end
|
|
754
|
+
commands_under(APP_INSTANCE_PATH) do
|
|
755
|
+
command(:list, description: 'List app instances', action: lambda do
|
|
756
|
+
result_list(
|
|
757
|
+
'admin/apps_new',
|
|
758
|
+
fields: %w[id app_type available workspace_id],
|
|
759
|
+
default_query: {workspace_id: aoc_api.workspace_info[:id]}
|
|
760
|
+
)
|
|
761
|
+
end)
|
|
762
|
+
APP_TYPES.each do |app_type|
|
|
763
|
+
command(app_type, description: "Show or modify a #{app_type} instance")
|
|
764
|
+
commands_under(APP_INSTANCE_PATH + [app_type]) do
|
|
765
|
+
command :show, description: "Show a #{app_type} instance",
|
|
766
|
+
arguments: [{name: :res_id, type: :identifier}]
|
|
767
|
+
command :modify, description: "Modify a #{app_type} instance",
|
|
768
|
+
arguments: [{name: :res_id, type: :identifier}, {name: :properties, type: Hash}]
|
|
769
|
+
end
|
|
770
|
+
end
|
|
771
|
+
end
|
|
772
|
+
commands_under(%i[admin application membership]) do
|
|
773
|
+
command :list, description: 'List app memberships',
|
|
774
|
+
action: ->{result_list('apps/app_memberships')}
|
|
775
|
+
command :show, description: 'Show an app membership',
|
|
776
|
+
arguments: [{name: :res_id, type: :identifier}]
|
|
777
|
+
command :delete, description: 'Delete an app membership',
|
|
778
|
+
arguments: [{name: :res_id, type: :identifier}]
|
|
779
|
+
command :create, description: 'Create an app membership',
|
|
780
|
+
arguments: [{name: :membership, type: Hash}]
|
|
781
|
+
end
|
|
782
|
+
command :automation, description: 'Automation commands (BETA)', setup: :setup_automation_api
|
|
783
|
+
command :gateway, description: 'Start AoC Faspex4 gateway',
|
|
784
|
+
arguments: [{name: :parameters, type: Hash, mandatory: false, default: {}}]
|
|
785
|
+
|
|
786
|
+
# user sub-commands
|
|
787
|
+
commands_under(:user) do
|
|
788
|
+
command :workspaces, description: 'Workspace commands'
|
|
789
|
+
command :profile, description: 'User profile commands'
|
|
790
|
+
command :preferences, description: 'User interaction preferences'
|
|
791
|
+
command :notifications, description: 'Notification preferences'
|
|
792
|
+
command :contacts, description: 'Manage contacts'
|
|
793
|
+
entity_command :settings, api: :aoc_api, entity: 'client_settings', description: 'Manage client settings'
|
|
794
|
+
end
|
|
795
|
+
# user > contacts sub-commands (same CRUD as admin > contact)
|
|
796
|
+
commands_under(%i[user contacts]) do
|
|
797
|
+
Operations::ALL.each{ |op| command(op, description: op.to_s.capitalize)}
|
|
798
|
+
end
|
|
799
|
+
|
|
800
|
+
commands_under(%i[user workspaces]) do
|
|
801
|
+
command :list, description: 'List workspaces',
|
|
802
|
+
action: ->{result_list('workspaces', fields: %w[id name])}
|
|
803
|
+
command :current, description: 'Show current workspace',
|
|
804
|
+
action: ->{Result::SingleObject.new(aoc_api.workspace_info)}
|
|
805
|
+
end
|
|
806
|
+
|
|
807
|
+
commands_under(%i[user profile]) do
|
|
808
|
+
command :show, description: 'Show user profile',
|
|
809
|
+
action: ->{Result::SingleObject.new(aoc_api.current_user_info(exception: true))}
|
|
810
|
+
command(
|
|
811
|
+
:modify, description: 'Modify user profile',
|
|
812
|
+
arguments: [{name: :properties, type: Hash}],
|
|
813
|
+
action: lambda do |properties:, **|
|
|
814
|
+
aoc_api.update("users/#{aoc_api.current_user_info(exception: true)['id']}", properties)
|
|
815
|
+
Result::Status.new('modified')
|
|
816
|
+
end
|
|
817
|
+
)
|
|
818
|
+
end
|
|
819
|
+
|
|
820
|
+
commands_under(%i[user preferences]) do
|
|
821
|
+
command(
|
|
822
|
+
:show, description: 'Show user preferences',
|
|
823
|
+
action: lambda do
|
|
824
|
+
user_id = aoc_api.current_user_info(exception: true)['id']
|
|
825
|
+
Result::SingleObject.new(aoc_api.read("users/#{user_id}/user_interaction_preferences"))
|
|
826
|
+
end
|
|
827
|
+
)
|
|
828
|
+
command(
|
|
829
|
+
:modify, description: 'Modify user preferences',
|
|
830
|
+
arguments: [{name: :properties, type: Hash}],
|
|
831
|
+
action: lambda do |properties:, **|
|
|
832
|
+
user_id = aoc_api.current_user_info(exception: true)['id']
|
|
833
|
+
aoc_api.update("users/#{user_id}/user_interaction_preferences", properties)
|
|
834
|
+
Result::Status.new('modified')
|
|
1226
835
|
end
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
836
|
+
)
|
|
837
|
+
end
|
|
838
|
+
|
|
839
|
+
commands_under(%i[user notifications]) do
|
|
840
|
+
command(
|
|
841
|
+
:show, description: 'Show notification preferences',
|
|
842
|
+
action: lambda do
|
|
843
|
+
user_id = aoc_api.current_user_info(exception: true)['id']
|
|
844
|
+
Result::SingleObject.new(aoc_api.read("users/#{user_id}/notification_preferences"))
|
|
845
|
+
end
|
|
846
|
+
)
|
|
847
|
+
command(
|
|
848
|
+
:modify, description: 'Modify notification preferences',
|
|
849
|
+
arguments: [{name: :properties, type: Hash}],
|
|
850
|
+
action: lambda do |properties:, **|
|
|
851
|
+
user_id = aoc_api.current_user_info(exception: true)['id']
|
|
852
|
+
aoc_api.update("users/#{user_id}/notification_preferences", properties)
|
|
853
|
+
Result::Status.new('modified')
|
|
854
|
+
end
|
|
855
|
+
)
|
|
856
|
+
end
|
|
857
|
+
|
|
858
|
+
# packages sub-commands — instance commands consume package_id
|
|
859
|
+
commands_under(:packages) do
|
|
860
|
+
command :shared_inboxes, description: 'Shared inbox commands'
|
|
861
|
+
command :send, description: 'Send a package', transfer_paths: :send,
|
|
862
|
+
arguments: [{name: :data, type: Hash, schema: Schema::Registry.req_body(Schema::Registry::AOC, 'packages.post')}]
|
|
863
|
+
command :receive, description: 'Receive package(s)', aliases: [:recv], transfer_paths: :receive,
|
|
864
|
+
arguments: [{name: :package_id, type: :identifier}]
|
|
865
|
+
command :list, description: 'List packages'
|
|
866
|
+
command :show, description: 'Show a package',
|
|
867
|
+
arguments: [{name: :package_id, type: :identifier}]
|
|
868
|
+
command :delete, description: 'Delete package(s)',
|
|
869
|
+
arguments: [{name: :package_id, type: :identifier}]
|
|
870
|
+
command :modify, description: 'Modify a package',
|
|
871
|
+
arguments: [{name: :package_id, type: :identifier}, {name: :data, type: Hash}]
|
|
872
|
+
# Node Gen4 read-only actions on packages
|
|
873
|
+
command :bearer_token_node, description: 'Show bearer token for package node',
|
|
874
|
+
arguments: [{name: :package_id, type: :identifier}]
|
|
875
|
+
command :node_info, description: 'Show node info for package',
|
|
876
|
+
arguments: [{name: :package_id, type: :identifier}]
|
|
877
|
+
command :browse, description: 'Browse package contents',
|
|
878
|
+
arguments: [{name: :package_id, type: :identifier}]
|
|
879
|
+
command :find, description: 'Find files in package',
|
|
880
|
+
arguments: [{name: :package_id, type: :identifier}]
|
|
881
|
+
end
|
|
882
|
+
|
|
883
|
+
commands_under(%i[packages shared_inboxes]) do
|
|
884
|
+
command :list, description: 'List shared inboxes',
|
|
885
|
+
action: (lambda do
|
|
886
|
+
result_list(
|
|
887
|
+
'dropbox_memberships',
|
|
888
|
+
fields: %w[dropbox_id dropbox.name],
|
|
889
|
+
default_query: workspace_id_hash({'embed[]' => 'dropbox', 'aggregate_permissions_by_dropbox' => true, 'sort' => 'dropbox_name'}, string: true)
|
|
890
|
+
)
|
|
891
|
+
end)
|
|
892
|
+
command :show, description: 'Show a shared inbox',
|
|
893
|
+
arguments: [{name: :dropbox_id, type: :identifier, lookup: :lookup_aoc_dropbox_id}],
|
|
894
|
+
action: ->(dropbox_id:, **){Result::SingleObject.new(aoc_api.read("dropboxes/#{dropbox_id}"))}
|
|
895
|
+
command :short_link, description: 'Manage shared inbox short links',
|
|
896
|
+
arguments: [{name: :link_type, allowed: %i[public private]}],
|
|
897
|
+
setup: :setup_packages_short_link
|
|
898
|
+
end
|
|
899
|
+
# packages > shared_inboxes > short_link sub-commands
|
|
900
|
+
register_short_link_commands(self, %i[packages shared_inboxes short_link])
|
|
901
|
+
|
|
902
|
+
# files sub-commands: all FILES_COMMANDS + :short_link
|
|
903
|
+
# Declared dynamically after FILES_COMMANDS is available (class body evaluated after constants)
|
|
904
|
+
commands_under(:files) do
|
|
905
|
+
command :short_link, description: 'Manage file short link',
|
|
906
|
+
arguments: [{name: :folder_dest, type: String}, {name: :link_type, allowed: %i[public private]}],
|
|
907
|
+
setup: :setup_files_short_link
|
|
908
|
+
command :transfer, description: 'Transfer files (node-to-node)',
|
|
909
|
+
arguments: [{name: :direction, allowed: %i[push pull]}, {name: :source_folder, type: String}]
|
|
910
|
+
command :mkdir, description: 'Create folder'
|
|
911
|
+
command :mklink, description: 'Create symbolic link'
|
|
912
|
+
command :mkfile, description: 'Create file'
|
|
913
|
+
command :rename, description: 'Rename entry'
|
|
914
|
+
command :delete, description: 'Delete entry'
|
|
915
|
+
command :upload, description: 'Upload files', transfer_paths: :send
|
|
916
|
+
command :download, description: 'Download files', transfer_paths: :receive
|
|
917
|
+
command :sync, description: 'Synchronize folders'
|
|
918
|
+
command :cat, description: 'Show file contents'
|
|
919
|
+
command :show, description: 'Show file info'
|
|
920
|
+
command :modify, description: 'Modify file'
|
|
921
|
+
command :permission, description: 'Manage permissions'
|
|
922
|
+
command :thumbnail, description: 'Show file thumbnail'
|
|
923
|
+
command :v3, description: 'Legacy v3 commands on files'
|
|
924
|
+
command :bearer_token_node, description: 'Show bearer token for file node'
|
|
925
|
+
command :node_info, description: 'Show node info for file'
|
|
926
|
+
command :browse, description: 'Browse files'
|
|
927
|
+
command :find, description: 'Find files'
|
|
928
|
+
end
|
|
929
|
+
# files > short_link sub-commands
|
|
930
|
+
register_short_link_commands(self, %i[files short_link])
|
|
931
|
+
|
|
932
|
+
# automation sub-commands
|
|
933
|
+
commands_under(:automation) do
|
|
934
|
+
entity_command :instances, description: 'Manage workflow instances', api: :aoc_api, entity: 'workflow_instances'
|
|
935
|
+
command :workflows, description: 'Manage workflows'
|
|
936
|
+
end
|
|
937
|
+
|
|
938
|
+
commands_under(%i[automation workflows]) do
|
|
939
|
+
command :create, description: 'Create a workflow'
|
|
940
|
+
command :list, description: 'List workflows'
|
|
941
|
+
command :show, description: 'Show a workflow'
|
|
942
|
+
command :modify, description: 'Modify a workflow'
|
|
943
|
+
command :delete, description: 'Delete a workflow'
|
|
944
|
+
command :launch, description: 'Launch a workflow',
|
|
945
|
+
arguments: [{name: :wf_id, type: :identifier}],
|
|
946
|
+
action: ->(wf_id:, **){Result::SingleObject.new(@automation_api.create("workflows/#{wf_id}/launch", {}))}
|
|
947
|
+
command :action, description: 'Add action to workflow (TODO)'
|
|
948
|
+
end
|
|
949
|
+
|
|
950
|
+
commands_under(%i[automation workflows action]) do
|
|
951
|
+
command :list, description: 'List actions (TODO)',
|
|
952
|
+
arguments: [{name: :wf_id, type: :identifier}]
|
|
953
|
+
command :create, description: 'Create an action (TODO)',
|
|
954
|
+
arguments: [{name: :wf_id, type: :identifier}]
|
|
955
|
+
command :show, description: 'Show an action (TODO)',
|
|
956
|
+
arguments: [{name: :wf_id, type: :identifier}]
|
|
957
|
+
end
|
|
958
|
+
|
|
959
|
+
# --- setup methods ---
|
|
960
|
+
|
|
961
|
+
# Display workspace info before dispatching files/packages sub-commands.
|
|
962
|
+
# Returns {} so it does not inject anything into ctx.
|
|
963
|
+
def setup_workspace_display(**)
|
|
964
|
+
formatter.display_status("Workspace: #{aoc_api.workspace_info[:name].to_s.red}#{' (default)' if aoc_api.default_workspace?}")
|
|
965
|
+
if !aoc_api.private_link.nil?
|
|
966
|
+
folder_name = aoc_api.node_api_from(node_id: aoc_api.home[:node_id]).read("files/#{aoc_api.home[:file_id]}")['name']
|
|
967
|
+
formatter.display_status("Private Folder: #{folder_name}")
|
|
968
|
+
end
|
|
969
|
+
{}
|
|
970
|
+
end
|
|
971
|
+
|
|
972
|
+
# Build automation API and store in @automation_api ivar.
|
|
973
|
+
def setup_automation_api(**)
|
|
974
|
+
change_api_scope(Api::AoC::Scope::ADMIN_USER)
|
|
975
|
+
Log.log.warn('BETA: work under progress')
|
|
976
|
+
@automation_api = Rest.new(**aoc_api.params, base_url: aoc_api.base_url.gsub('/api/', '/automation/'))
|
|
977
|
+
{}
|
|
978
|
+
end
|
|
979
|
+
|
|
980
|
+
# --- handler methods ---
|
|
981
|
+
|
|
982
|
+
def action_reminder
|
|
983
|
+
user_email = options.get_option(:username, mandatory: true)
|
|
984
|
+
no_auth_api = Api::AoC.new(url: options.get_option(:url), auth: :none)
|
|
985
|
+
no_auth_api.create('organization_reminders', {email: user_email})
|
|
986
|
+
return Result::Status.new("List of organizations user is member of, has been sent by e-mail to #{user_email}")
|
|
987
|
+
end
|
|
988
|
+
|
|
989
|
+
# packages > send
|
|
990
|
+
def action_packages_send(data:, **)
|
|
991
|
+
package_data = data
|
|
992
|
+
new_user_option = options.get_option(:new_user_option)
|
|
993
|
+
option_validate = options.get_option(:validate_metadata)
|
|
994
|
+
workspace_id_hash(package_data, string: true) unless package_data.key?('workspace_id')
|
|
995
|
+
if !aoc_api.public_link.nil?
|
|
996
|
+
aoc_api.assert_public_link_types(%w[send_package_to_user send_package_to_dropbox])
|
|
997
|
+
box_type = aoc_api.public_link['purpose'].split('_').last
|
|
998
|
+
package_data['recipients'] = [{'id' => aoc_api.public_link['data']["#{box_type}_id"], 'type' => box_type}]
|
|
999
|
+
package_data['workspace_id'] = aoc_api.public_link['data']['workspace_id']
|
|
1000
|
+
end
|
|
1001
|
+
package_data['encryption_at_rest'] = true if transfer.user_transfer_spec['content_protection'].eql?('encrypt')
|
|
1002
|
+
created_package = aoc_api.create_package_simple(package_data, option_validate, new_user_option)
|
|
1003
|
+
Runner.result_transfer(transfer.start(created_package[:spec], rest_token: created_package[:node]))
|
|
1004
|
+
return Result::SingleObject.new(created_package[:info])
|
|
1005
|
+
end
|
|
1006
|
+
|
|
1007
|
+
# packages > receive — package_id: from arguments:(:identifier) (or overridden by public_link)
|
|
1008
|
+
def action_packages_receive(package_id:, **)
|
|
1009
|
+
ids_to_download = if aoc_api.public_link.nil?
|
|
1010
|
+
package_id
|
|
1011
|
+
else
|
|
1012
|
+
aoc_api.assert_public_link_types(['view_received_package'])
|
|
1013
|
+
aoc_api.public_link['data']['package_id']
|
|
1014
|
+
end
|
|
1015
|
+
skip_ids_persistency = package_persistency
|
|
1016
|
+
case ids_to_download
|
|
1017
|
+
when SpecialValues::INIT
|
|
1018
|
+
all_packages = list_all_packages_with_query[:items]
|
|
1019
|
+
Aspera.assert(skip_ids_persistency, 'INIT requires option once_only')
|
|
1020
|
+
skip_ids_persistency.data.clear.concat(all_packages.map{ |e| e['id']})
|
|
1021
|
+
skip_ids_persistency.save
|
|
1022
|
+
return Result::Status.new("Initialized skip for #{skip_ids_persistency.data.count} package(s)")
|
|
1023
|
+
when SpecialValues::ALL
|
|
1024
|
+
all_packages = list_all_packages_with_query[:items]
|
|
1025
|
+
reject_packages_from_persistency(all_packages, skip_ids_persistency)
|
|
1026
|
+
ids_to_download = all_packages.map{ |e| e['id']}
|
|
1027
|
+
formatter.display_status("Found #{ids_to_download.length} package(s).")
|
|
1028
|
+
else
|
|
1029
|
+
ids_to_download = [ids_to_download] unless ids_to_download.is_a?(Array)
|
|
1030
|
+
end
|
|
1031
|
+
ts_paths = transfer.ts_source_paths(default: ['.'])
|
|
1032
|
+
per_package_def = options.get_option(:package_folder).symbolize_keys
|
|
1033
|
+
save_metadata = per_package_def.delete(:inf)
|
|
1034
|
+
destination_folder = transfer.destination_folder(Transfer::Spec::DIRECTION_RECEIVE)
|
|
1035
|
+
result_transfer = []
|
|
1036
|
+
ids_to_download.each do |package_id|
|
|
1037
|
+
package_info = aoc_api.read("packages/#{package_id}")
|
|
1038
|
+
package_node_api = aoc_api.node_api_from(
|
|
1039
|
+
node_id: package_info['node_id'],
|
|
1040
|
+
package_info: package_info,
|
|
1041
|
+
**workspace_id_hash(name: true)
|
|
1042
|
+
)
|
|
1043
|
+
transfer_spec = package_node_api.transfer_spec_gen4(
|
|
1044
|
+
package_info['contents_file_id'],
|
|
1045
|
+
Transfer::Spec::DIRECTION_RECEIVE,
|
|
1046
|
+
{'paths'=> ts_paths}
|
|
1047
|
+
)
|
|
1048
|
+
transfer.user_transfer_spec['destination_root'] = self.class.unique_folder(package_info, destination_folder, **per_package_def) unless per_package_def.empty?
|
|
1049
|
+
dest_folder = transfer.user_transfer_spec['destination_root'] || destination_folder
|
|
1050
|
+
formatter.display_status(%Q{Downloading package: [#{package_info['id']}] "#{package_info['name']}" to [#{dest_folder}]})
|
|
1051
|
+
statuses = transfer.start(transfer_spec, rest_token: package_node_api)
|
|
1052
|
+
File.write(File.join(dest_folder, "#{package_id}.info.json"), package_info.to_json) if save_metadata
|
|
1053
|
+
result_transfer.push({'package' => package_id, Runner::STATUS_FIELD => statuses})
|
|
1054
|
+
if skip_ids_persistency && statuses.is_a?(Transfer::Result::Success)
|
|
1055
|
+
skip_ids_persistency.data.push(package_id)
|
|
1056
|
+
skip_ids_persistency.save
|
|
1057
|
+
end
|
|
1058
|
+
end
|
|
1059
|
+
return Runner.result_transfer_multiple(result_transfer)
|
|
1060
|
+
end
|
|
1061
|
+
|
|
1062
|
+
# packages > list
|
|
1063
|
+
def action_packages_list
|
|
1064
|
+
result = list_all_packages_with_query
|
|
1065
|
+
skip_ids_persistency = package_persistency
|
|
1066
|
+
reject_packages_from_persistency(result[:items], skip_ids_persistency)
|
|
1067
|
+
display_fields = PACKAGE_LIST_DEFAULT_FIELDS
|
|
1068
|
+
display_fields += ['workspace_id'] if aoc_api.workspace_info[:id].nil?
|
|
1069
|
+
Result::ObjectList.new(result[:items], fields: display_fields, total: result[:total])
|
|
1070
|
+
end
|
|
1071
|
+
|
|
1072
|
+
# packages > show
|
|
1073
|
+
def action_packages_show(package_id:, **)
|
|
1074
|
+
Result::SingleObject.new(aoc_api.read("packages/#{package_id}"))
|
|
1075
|
+
end
|
|
1076
|
+
|
|
1077
|
+
# packages > delete
|
|
1078
|
+
def action_packages_delete(package_id:, **)
|
|
1079
|
+
is_bulk = options.get_option(:bulk)
|
|
1080
|
+
items = package_id.is_a?(Array) ? package_id : [package_id]
|
|
1081
|
+
Result.bulk(items, is_bulk: is_bulk, command: :delete, bfail: options.get_option(:bfail)) do |one_id|
|
|
1082
|
+
Aspera.assert_type(one_id, String, Integer){'identifier'}
|
|
1083
|
+
aoc_api.delete("packages/#{one_id}")
|
|
1084
|
+
end
|
|
1085
|
+
end
|
|
1086
|
+
|
|
1087
|
+
# packages > modify
|
|
1088
|
+
def action_packages_modify(data:, package_id:, **)
|
|
1089
|
+
aoc_api.update("packages/#{package_id}", data)
|
|
1090
|
+
Result::Status.new('modified')
|
|
1091
|
+
end
|
|
1092
|
+
|
|
1093
|
+
# packages > bearer_token_node / node_info / browse / find
|
|
1094
|
+
# (NODE4_READ_ACTIONS dispatched by full path: action_packages_bearer_token_node, etc.)
|
|
1095
|
+
Node::NODE4_READ_ACTIONS.each do |action|
|
|
1096
|
+
define_action_method([:packages, action]) do |package_id:, **|
|
|
1097
|
+
package_info = aoc_api.read("packages/#{package_id}")
|
|
1098
|
+
execute_nodegen4_command(action, package_info['node_id'], file_id: package_info['contents_file_id'], scope: Api::Node::Scope::USER)
|
|
1099
|
+
end
|
|
1100
|
+
end
|
|
1101
|
+
|
|
1102
|
+
# setup: files > short_link
|
|
1103
|
+
# Resolves the target folder, consumes link_type argument, computes purposes.
|
|
1104
|
+
# @return [Hash] ctx keys: sl_shared_data, sl_link_type, sl_token_purpose, sl_short_link_purpose, sl_perm_block, sl_shared_apifid, sl_folder_dest
|
|
1105
|
+
def setup_files_short_link(folder_dest:, link_type:, **)
|
|
1106
|
+
home_node_api = aoc_api.node_api_from(
|
|
1107
|
+
node_id: aoc_api.home[:node_id],
|
|
1108
|
+
**workspace_id_hash(name: true)
|
|
1109
|
+
)
|
|
1110
|
+
shared_apifid = home_node_api.resolve_api_fid(aoc_api.home[:file_id], folder_dest)
|
|
1111
|
+
shared_data = {
|
|
1112
|
+
node_id: shared_apifid.node_api.app_info.node_info['id'],
|
|
1113
|
+
file_id: shared_apifid.file_id
|
|
1114
|
+
}
|
|
1115
|
+
token_purpose, short_link_purpose = short_link_purposes(shared_data, link_type)
|
|
1116
|
+
perm_block = lambda do |op, id, access_levels|
|
|
1117
|
+
case op
|
|
1118
|
+
when :create
|
|
1119
|
+
perm_data = {
|
|
1120
|
+
'file_id' => shared_apifid.file_id,
|
|
1121
|
+
'access_id' => id,
|
|
1122
|
+
'access_type' => 'user',
|
|
1123
|
+
'access_levels' => Api::AoC.expand_access_levels(access_levels),
|
|
1124
|
+
'tags' => {
|
|
1125
|
+
'url_token' => true,
|
|
1126
|
+
'folder_name' => File.basename(folder_dest),
|
|
1127
|
+
'created_by_name' => aoc_api.current_user_info['name'],
|
|
1128
|
+
'created_by_email' => aoc_api.current_user_info['email'],
|
|
1129
|
+
'access_key' => shared_apifid.node_api.app_info.node_info['access_key'],
|
|
1130
|
+
'node' => shared_apifid.node_api.app_info.node_info['name'],
|
|
1131
|
+
**workspace_id_hash(string: true, name: true)
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
created_data = shared_apifid.node_api.create('permissions', perm_data)
|
|
1135
|
+
aoc_api.permissions_send_event(event_data: created_data, app_info: shared_apifid.node_api.app_info)
|
|
1136
|
+
when :update
|
|
1137
|
+
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)}
|
|
1138
|
+
Aspera.assert(!found.nil?, type: Error){"Short link not found: #{id}"}
|
|
1139
|
+
shared_apifid.node_api.update("permissions/#{found['id']}", {access_levels: Api::AoC.expand_access_levels(access_levels)})
|
|
1140
|
+
when :delete
|
|
1141
|
+
found = shared_apifid.node_api.read('permissions', {file_id: shared_apifid.file_id, inherited: false, access_type: 'user', access_id: id}).first
|
|
1142
|
+
Aspera.assert(!found.nil?, type: Error){"Short link not found: #{id}"}
|
|
1143
|
+
shared_apifid.node_api.delete("permissions/#{found['id']}")
|
|
1144
|
+
else Aspera.error_unexpected_value(op)
|
|
1145
|
+
end
|
|
1146
|
+
end
|
|
1147
|
+
{
|
|
1148
|
+
sl_shared_data: shared_data,
|
|
1149
|
+
sl_link_type: link_type,
|
|
1150
|
+
sl_token_purpose: token_purpose,
|
|
1151
|
+
sl_short_link_purpose: short_link_purpose,
|
|
1152
|
+
sl_perm_block: perm_block
|
|
1153
|
+
}
|
|
1154
|
+
end
|
|
1155
|
+
|
|
1156
|
+
# setup: packages > shared_inboxes > short_link
|
|
1157
|
+
# Reads dropbox_id, consumes link_type argument, computes purposes.
|
|
1158
|
+
# @return [Hash] ctx keys: sl_shared_data, sl_link_type, sl_token_purpose, sl_short_link_purpose
|
|
1159
|
+
def setup_packages_short_link(link_type:, **)
|
|
1160
|
+
dropbox_id = get_resource_id_from_args('dropboxes')
|
|
1161
|
+
shared_data = {dropbox_id: dropbox_id, name: ''}
|
|
1162
|
+
token_purpose, short_link_purpose = short_link_purposes(shared_data, link_type)
|
|
1163
|
+
{
|
|
1164
|
+
sl_shared_data: shared_data,
|
|
1165
|
+
sl_link_type: link_type,
|
|
1166
|
+
sl_token_purpose: token_purpose,
|
|
1167
|
+
sl_short_link_purpose: short_link_purpose,
|
|
1168
|
+
sl_perm_block: nil
|
|
1169
|
+
}
|
|
1170
|
+
end
|
|
1171
|
+
|
|
1172
|
+
# Shared implementation for short_link > create
|
|
1173
|
+
def sl_exec_create(custom_data = {}, sl_shared_data:, sl_link_type:, sl_token_purpose:, sl_short_link_purpose:, sl_perm_block:, **)
|
|
1174
|
+
shared_data = sl_shared_data.dup
|
|
1175
|
+
workspace_id_hash(shared_data)
|
|
1176
|
+
create_payload = {purpose: sl_short_link_purpose, user_selected_name: nil}
|
|
1177
|
+
case sl_link_type
|
|
1178
|
+
when :private
|
|
1179
|
+
create_payload[:data] = shared_data
|
|
1180
|
+
when :public
|
|
1181
|
+
create_payload[:expires_at] = nil
|
|
1182
|
+
create_payload[:password_enabled] = false
|
|
1183
|
+
shared_data[:name] = ''
|
|
1184
|
+
create_payload[:data] = {
|
|
1185
|
+
aoc: true,
|
|
1186
|
+
url_token_data: {data: shared_data, purpose: sl_token_purpose}
|
|
1187
|
+
}
|
|
1188
|
+
end
|
|
1189
|
+
custom_data = {}
|
|
1190
|
+
access_levels = custom_data.delete('access_levels')
|
|
1191
|
+
if (pass = custom_data.delete('password'))
|
|
1192
|
+
create_payload[:data][:url_token_data][:password] = pass
|
|
1193
|
+
create_payload[:password_enabled] = true
|
|
1194
|
+
end
|
|
1195
|
+
create_payload.deep_merge!(custom_data)
|
|
1196
|
+
result_create_short_link = aoc_api.create('short_links', create_payload)
|
|
1197
|
+
sl_perm_block&.call(:create, result_create_short_link['resource_id'], access_levels) if sl_link_type.eql?(:public)
|
|
1198
|
+
Result::SingleObject.new(result_create_short_link)
|
|
1199
|
+
end
|
|
1200
|
+
|
|
1201
|
+
# Shared implementation for short_link > delete|list|show|modify: fetch the short_list
|
|
1202
|
+
def sl_fetch_list(sl_shared_data:, sl_link_type:, sl_token_purpose:, sl_short_link_purpose:, **)
|
|
1203
|
+
shared_data = sl_shared_data.dup
|
|
1204
|
+
workspace_id_hash(shared_data)
|
|
1205
|
+
list_params = short_link_list_params(
|
|
1206
|
+
shared_data: shared_data, link_type: sl_link_type,
|
|
1207
|
+
token_purpose: sl_token_purpose, short_link_purpose: sl_short_link_purpose
|
|
1208
|
+
)
|
|
1209
|
+
{
|
|
1210
|
+
sl_short_list: aoc_api.read_with_paging('short_links', list_params.merge(query_read_delete(default: {})).compact),
|
|
1211
|
+
sl_shared_data_ws: shared_data
|
|
1212
|
+
}
|
|
1213
|
+
end
|
|
1214
|
+
|
|
1215
|
+
# Shared implementation for short_link > delete
|
|
1216
|
+
def sl_exec_delete(sl_shared_data_ws:, sl_short_list:, sl_link_type:, sl_perm_block:, short_link_id: nil, **)
|
|
1217
|
+
one_id = short_link_id
|
|
1218
|
+
if sl_link_type.eql?(:public)
|
|
1219
|
+
found = sl_short_list[:items].find{ |item| item['id'].eql?(one_id)}
|
|
1220
|
+
raise BadIdentifier.new('Short link', one_id) if found.nil?
|
|
1221
|
+
sl_perm_block&.call(:delete, found['resource_id'], nil)
|
|
1222
|
+
end
|
|
1223
|
+
aoc_api.delete("short_links/#{one_id}", {edit_access: true, json_query: sl_shared_data_ws.to_json})
|
|
1224
|
+
Result::Status.new('deleted')
|
|
1225
|
+
end
|
|
1226
|
+
|
|
1227
|
+
# Shared implementation for short_link > list
|
|
1228
|
+
def sl_exec_list(sl_short_list:, **)
|
|
1229
|
+
Result::ObjectList.new(sl_short_list[:items], fields: Formatter.all_but('data'), total: sl_short_list[:total])
|
|
1230
|
+
end
|
|
1231
|
+
|
|
1232
|
+
# Shared implementation for short_link > show
|
|
1233
|
+
def sl_exec_show(sl_short_list:, short_link_id: nil, **)
|
|
1234
|
+
one_id = short_link_id
|
|
1235
|
+
found = sl_short_list[:items].find{ |item| item['id'].eql?(one_id)}
|
|
1236
|
+
raise BadIdentifier.new('Short link', one_id) if found.nil?
|
|
1237
|
+
Result::SingleObject.new(found, fields: Formatter.all_but('data'))
|
|
1238
|
+
end
|
|
1239
|
+
|
|
1240
|
+
# Shared implementation for short_link > modify
|
|
1241
|
+
def sl_exec_modify(custom_data = {}, sl_shared_data:, sl_short_list:, sl_link_type:, sl_perm_block:, short_link_id: nil, **)
|
|
1242
|
+
Aspera.assert_values(sl_link_type, [:public], type: Cli::BadArgument){'link_type'}
|
|
1243
|
+
one_id = short_link_id
|
|
1244
|
+
node_file = sl_shared_data.slice(:node_id, :file_id)
|
|
1245
|
+
modify_payload = {edit_access: true, json_query: node_file}
|
|
1246
|
+
custom_data = {}
|
|
1247
|
+
if (pass = custom_data.delete('password'))
|
|
1248
|
+
modify_payload[:password_enabled] = true
|
|
1249
|
+
modify_payload[:data] = {url_token_data: {password: pass, data: node_file}}
|
|
1250
|
+
else
|
|
1251
|
+
modify_payload[:password_enabled] = false
|
|
1252
|
+
end
|
|
1253
|
+
if custom_data.delete('access_levels')
|
|
1254
|
+
found = sl_short_list[:items].find{ |item| item['id'].eql?(one_id)}
|
|
1255
|
+
raise BadIdentifier.new('Short link', one_id) if found.nil?
|
|
1256
|
+
sl_perm_block&.call(:update, found['resource_id'], nil)
|
|
1257
|
+
end
|
|
1258
|
+
modify_payload.deep_merge!(custom_data)
|
|
1259
|
+
aoc_api.update("short_links/#{one_id}", modify_payload)
|
|
1260
|
+
Result::Status.new('modified')
|
|
1261
|
+
end
|
|
1262
|
+
|
|
1263
|
+
# files > transfer
|
|
1264
|
+
def action_files_transfer(direction:, source_folder:, **)
|
|
1265
|
+
execute_nodegen4_command(
|
|
1266
|
+
:transfer, aoc_api.home[:node_id],
|
|
1267
|
+
file_id: aoc_api.home[:file_id],
|
|
1268
|
+
scope: Api::Node::Scope::USER,
|
|
1269
|
+
transfer_direction: direction,
|
|
1270
|
+
transfer_source: source_folder
|
|
1271
|
+
)
|
|
1272
|
+
end
|
|
1273
|
+
|
|
1274
|
+
# files > FILES_COMMANDS (all Gen4 node commands except :transfer, handled above)
|
|
1275
|
+
FILES_COMMANDS.reject{ |a| a.eql?(:transfer)}.each do |action|
|
|
1276
|
+
define_action_method([:files, action]) do
|
|
1277
|
+
execute_nodegen4_command(action, aoc_api.home[:node_id], file_id: aoc_api.home[:file_id], scope: Api::Node::Scope::USER)
|
|
1278
|
+
end
|
|
1279
|
+
end
|
|
1280
|
+
|
|
1281
|
+
# admin > application > instance > <type> > show|modify
|
|
1282
|
+
APP_TYPES.each do |app_type|
|
|
1283
|
+
define_action_method([:admin, :application, :instance, app_type, :show]) do |res_id:, **|
|
|
1284
|
+
Result::SingleObject.new(aoc_api.read("admin/apps_new/#{app_type}/#{res_id}", query_read_delete))
|
|
1285
|
+
end
|
|
1286
|
+
|
|
1287
|
+
define_action_method([:admin, :application, :instance, app_type, :modify]) do |properties:, res_id:, **|
|
|
1288
|
+
aoc_api.update("admin/apps_new/#{app_type}/#{res_id}", properties)
|
|
1289
|
+
Result::Status.new('modified')
|
|
1290
|
+
end
|
|
1291
|
+
end
|
|
1292
|
+
|
|
1293
|
+
def action_admin_application_membership_create(membership:, **)
|
|
1294
|
+
data = membership.dup
|
|
1295
|
+
app_type = data.delete('app_type')
|
|
1296
|
+
Aspera.assert_type(app_type, String){'app_type'}
|
|
1297
|
+
Aspera.assert_values(app_type.to_sym, APP_TYPES){'app_type'}
|
|
1298
|
+
Result::SingleObject.new(aoc_api.create("apps/#{app_type}/app_memberships", data))
|
|
1299
|
+
end
|
|
1300
|
+
|
|
1301
|
+
# admin > application > membership > show|delete
|
|
1302
|
+
def action_admin_application_membership_show(res_id:, **)
|
|
1303
|
+
Result::SingleObject.new(aoc_api.read("apps/app_memberships/#{res_id}", query_read_delete))
|
|
1304
|
+
end
|
|
1305
|
+
|
|
1306
|
+
def action_admin_application_membership_delete(res_id:, **)
|
|
1307
|
+
aoc_api.delete("apps/app_memberships/#{res_id}")
|
|
1308
|
+
Result::Status.new('deleted')
|
|
1309
|
+
end
|
|
1310
|
+
|
|
1311
|
+
# admin - setup: change API scope to admin once
|
|
1312
|
+
def setup_admin_scope(**)
|
|
1313
|
+
change_api_scope(Api::AoC::Scope::ADMIN)
|
|
1314
|
+
{}
|
|
1315
|
+
end
|
|
1316
|
+
|
|
1317
|
+
# admin > subscription > account
|
|
1318
|
+
def action_admin_subscription_account
|
|
1319
|
+
org = aoc_api.read('organization')
|
|
1320
|
+
result = GraphQL.execute(api_from_options('bss/platform/graphql'), 'bss_subscription_account', {organization_id: org['id']})
|
|
1321
|
+
Result::SingleObject.new(result['aoc']['bssSubscription'])
|
|
1322
|
+
end
|
|
1323
|
+
|
|
1324
|
+
# admin > subscription > usage
|
|
1325
|
+
def action_admin_subscription_usage(aggregate:, start_date:, end_date:, **)
|
|
1326
|
+
today = Date.today
|
|
1327
|
+
aggregate = :ALL if aggregate.nil?
|
|
1328
|
+
start_date = today.prev_year.strftime('%Y-%m-%d') if start_date.nil?
|
|
1329
|
+
end_date = today.strftime('%Y-%m-%d') if end_date.nil?
|
|
1330
|
+
org = aoc_api.read('organization')
|
|
1331
|
+
result = GraphQL.execute(
|
|
1332
|
+
api_from_options('bss/platform/graphql'), 'bss_subscription_usage',
|
|
1333
|
+
{organization_id: org['id'], aggregate: aggregate, startDate: start_date, endDate: end_date}
|
|
1334
|
+
)
|
|
1335
|
+
Result::SingleObject.new(result['aoc'])
|
|
1336
|
+
end
|
|
1337
|
+
|
|
1338
|
+
# admin > analytics > application_events
|
|
1339
|
+
def action_admin_analytics_application_events
|
|
1340
|
+
analytics_api = build_analytics_api
|
|
1341
|
+
events = analytics_api.read("organizations/#{aoc_api.current_user_info['organization_id']}/application_events")['application_events']
|
|
1342
|
+
Result::ObjectList.new(events)
|
|
1343
|
+
end
|
|
1344
|
+
|
|
1345
|
+
# admin > analytics > transfers
|
|
1346
|
+
def action_admin_analytics_transfers(event_resource_type:, event_resource_id:, **)
|
|
1347
|
+
analytics_api = build_analytics_api
|
|
1348
|
+
event_resource_id ||=
|
|
1349
|
+
case event_resource_type
|
|
1350
|
+
when :organizations then aoc_api.current_user_info['organization_id']
|
|
1351
|
+
when :users then aoc_api.current_user_info['id']
|
|
1352
|
+
when :nodes then aoc_api.current_user_info['read_only_home_node_id']
|
|
1353
|
+
else Aspera.error_unreachable_line
|
|
1354
|
+
end
|
|
1355
|
+
filter = query_read_delete(default: {})
|
|
1356
|
+
filter['limit'] ||= 100
|
|
1357
|
+
if options.get_option(:once_only, mandatory: true)
|
|
1358
|
+
saved_date = []
|
|
1359
|
+
start_date_persistency = PersistencyActionOnce.new(
|
|
1360
|
+
manager: persistency,
|
|
1361
|
+
data: saved_date,
|
|
1362
|
+
id: IdGenerator.from_list('aoc_ana_date', options.get_option(:url, mandatory: true), aoc_api.workspace_info[:name], event_resource_type.to_s, event_resource_id)
|
|
1363
|
+
)
|
|
1364
|
+
start_date_time = saved_date.first
|
|
1365
|
+
stop_date_time = Time.now.utc.strftime('%FT%T.%LZ')
|
|
1366
|
+
saved_date[0] = stop_date_time
|
|
1367
|
+
filter['start_time'] = start_date_time unless start_date_time.nil?
|
|
1368
|
+
filter['stop_time'] = stop_date_time
|
|
1369
|
+
end
|
|
1370
|
+
events = analytics_api.read("#{event_resource_type}/#{event_resource_id}/transfers", filter)['transfers']
|
|
1371
|
+
start_date_persistency&.save
|
|
1372
|
+
events.each{ |tr_event| context.mailer.send_email_template(values: {ev: tr_event})} if !options.get_option(:notify_to).nil?
|
|
1373
|
+
Result::ObjectList.new(events)
|
|
1374
|
+
end
|
|
1375
|
+
|
|
1376
|
+
# admin > analytics > files
|
|
1377
|
+
def action_admin_analytics_files(event_resource_type:, event_resource_id:, event_uuid:, **)
|
|
1378
|
+
analytics_api = build_analytics_api
|
|
1379
|
+
event_resource_id =
|
|
1380
|
+
case event_resource_type
|
|
1381
|
+
when :organizations then aoc_api.current_user_info['organization_id']
|
|
1382
|
+
when :users then aoc_api.current_user_info['id']
|
|
1383
|
+
when :nodes then aoc_api.current_user_info['read_only_home_node_id']
|
|
1384
|
+
else Aspera.error_unreachable_line
|
|
1385
|
+
end if event_resource_id.to_s.empty?
|
|
1386
|
+
filter = query_read_delete(default: {})
|
|
1387
|
+
filter['limit'] ||= 100
|
|
1388
|
+
events = analytics_api.read("#{event_resource_type}/#{event_resource_id}/transfers/#{event_uuid}/files", filter)['files']
|
|
1389
|
+
Result::ObjectList.new(events)
|
|
1390
|
+
end
|
|
1391
|
+
|
|
1392
|
+
# Lookup methods for arguments:(:identifier) + lookup: on admin resources.
|
|
1393
|
+
# One method per non-singleton resource; each delegates to get_resource_id_from_args.
|
|
1394
|
+
ADMIN_OBJECTS.reject{ |r| ADMIN_OBJECT_CONFIG.dig(r, :singleton)}.each do |res|
|
|
1395
|
+
define_method(:"lookup_aoc_#{res}_id") do |_field, value, **|
|
|
1396
|
+
aoc_api.lookup_with_q(aoc_res_path(res), value: value)['id']
|
|
1397
|
+
end
|
|
1398
|
+
end
|
|
1399
|
+
|
|
1400
|
+
# admin > <res> > list
|
|
1401
|
+
ADMIN_OBJECTS.each do |res|
|
|
1402
|
+
define_action_method([:admin, res, :list]) do
|
|
1403
|
+
c = aoc_res_cfg(res)
|
|
1404
|
+
result_list(c[:path], fields: c[:list_fields], query_component: c[:query_component])
|
|
1405
|
+
end
|
|
1406
|
+
end
|
|
1407
|
+
|
|
1408
|
+
# admin > <res> > show
|
|
1409
|
+
ADMIN_OBJECTS.reject{ |r| ADMIN_OBJECT_CONFIG.dig(r, :singleton)}.each do |res|
|
|
1410
|
+
define_action_method([:admin, res, :show]) do |res_id:, **|
|
|
1411
|
+
c = aoc_res_cfg(res)
|
|
1412
|
+
Result::SingleObject.new(aoc_api.read("#{c[:path]}/#{res_id}", query_read_delete), fields: Formatter.all_but('certificate'))
|
|
1413
|
+
end
|
|
1414
|
+
end
|
|
1415
|
+
|
|
1416
|
+
# admin > organization|self > show (singleton)
|
|
1417
|
+
%i[organization self].each do |res|
|
|
1418
|
+
define_action_method([:admin, res, :show]) do
|
|
1419
|
+
Result::SingleObject.new(aoc_api.read(res.to_s, query_read_delete), fields: Formatter.all_but('certificate'))
|
|
1420
|
+
end
|
|
1421
|
+
end
|
|
1422
|
+
|
|
1423
|
+
# admin > <res> > create
|
|
1424
|
+
ADMIN_OBJECTS.reject{ |r| ADMIN_OBJECT_CONFIG.dig(r, :singleton)}.each do |res|
|
|
1425
|
+
define_action_method([:admin, res, :create]) do |data:, **|
|
|
1426
|
+
c = aoc_res_cfg(res)
|
|
1427
|
+
path = c[:path]
|
|
1428
|
+
# Special case: client_registration_token has a different creation URL
|
|
1429
|
+
path = 'admin/client_registration/token' if path.eql?('admin/client_registration_tokens')
|
|
1430
|
+
workspace_id = aoc_api.workspace_info[:id] if c[:require_ws_id]
|
|
1431
|
+
is_bulk = options.get_option(:bulk)
|
|
1432
|
+
Result.bulk(data, is_bulk: is_bulk, command: :create, id_result: c[:id_result], bfail: options.get_option(:bfail)) do |params|
|
|
1433
|
+
params['workspace_id'] = workspace_id if c[:require_ws_id] && workspace_id && !params.key?('workspace_id')
|
|
1434
|
+
aoc_api.create(path, params)
|
|
1435
|
+
end
|
|
1436
|
+
end
|
|
1437
|
+
end
|
|
1438
|
+
|
|
1439
|
+
# admin > <res> > modify
|
|
1440
|
+
ADMIN_OBJECTS.reject{ |r| ADMIN_OBJECT_CONFIG.dig(r, :singleton) || ADMIN_OBJECT_CONFIG.dig(r, :ops)&.then{ |o| !o.include?(:modify)}}.each do |res|
|
|
1441
|
+
define_action_method([:admin, res, :modify]) do |data:, res_id:, **|
|
|
1442
|
+
c = aoc_res_cfg(res)
|
|
1443
|
+
aoc_api.update("#{c[:path]}/#{res_id}", data)
|
|
1444
|
+
Result::Status.new('modified')
|
|
1445
|
+
end
|
|
1446
|
+
end
|
|
1447
|
+
|
|
1448
|
+
# admin > <res> > delete
|
|
1449
|
+
ADMIN_OBJECTS.reject do |r|
|
|
1450
|
+
cfg = ADMIN_OBJECT_CONFIG.fetch(r, {})
|
|
1451
|
+
cfg[:singleton] || (cfg[:ops] && !cfg[:ops].include?(:delete))
|
|
1452
|
+
end.each do |res|
|
|
1453
|
+
define_action_method([:admin, res, :delete]) do |res_id:, **|
|
|
1454
|
+
c = aoc_res_cfg(res)
|
|
1455
|
+
is_bulk = options.get_option(:bulk)
|
|
1456
|
+
items = res_id.is_a?(Array) ? res_id : [res_id]
|
|
1457
|
+
Result.bulk(items, is_bulk: is_bulk, command: :delete, bfail: options.get_option(:bfail)) do |one_id|
|
|
1458
|
+
aoc_api.delete("#{c[:path]}/#{one_id}")
|
|
1459
|
+
{'id' => one_id}
|
|
1460
|
+
end
|
|
1461
|
+
end
|
|
1462
|
+
end
|
|
1463
|
+
|
|
1464
|
+
# user > contacts > list|show|create|modify|delete (same API path as admin > contact)
|
|
1465
|
+
Operations::ALL.each do |op|
|
|
1466
|
+
define_action_method([:user, :contacts, op]) do |**ctx|
|
|
1467
|
+
send(CommandSpec.action_method([:admin, :contact, op]), **ctx)
|
|
1468
|
+
end
|
|
1469
|
+
end
|
|
1470
|
+
|
|
1471
|
+
# admin > client > set_pub_key
|
|
1472
|
+
def action_admin_client_set_pub_key(private_key_pem:, res_id:, **)
|
|
1473
|
+
c = aoc_res_cfg(:client)
|
|
1474
|
+
the_public_key = OpenSSL::PKey::RSA.new(private_key_pem).public_key.to_s
|
|
1475
|
+
aoc_api.update("#{c[:path]}/#{res_id}", {jwt_grant_enabled: true, public_key: the_public_key})
|
|
1476
|
+
Result::Success.new
|
|
1477
|
+
end
|
|
1478
|
+
|
|
1479
|
+
# admin > node > do | bearer_token — setup reuses the generic instance setup
|
|
1480
|
+
# (setup_admin_node_instance is auto-generated above, providing res_id:)
|
|
1481
|
+
|
|
1482
|
+
# admin > node > do > <FILES_COMMAND>
|
|
1483
|
+
FILES_COMMANDS.each do |cmd|
|
|
1484
|
+
define_action_method([:admin, :node, :do, cmd]) do |res_id:, **|
|
|
1485
|
+
execute_nodegen4_command(cmd, res_id, scope: Api::Node::Scope::ADMIN)
|
|
1486
|
+
end
|
|
1487
|
+
end
|
|
1488
|
+
|
|
1489
|
+
# admin > node > bearer_token
|
|
1490
|
+
def action_admin_node_bearer_token(scope:, res_id:, **)
|
|
1491
|
+
scope ||= Api::Node::Scope::ADMIN
|
|
1492
|
+
node_api = aoc_api.node_api_from(node_id: res_id, scope: scope)
|
|
1493
|
+
Result::Text.new(node_api.oauth.authorization)
|
|
1494
|
+
end
|
|
1495
|
+
|
|
1496
|
+
# admin > workspace > dropbox — res_id: already in ctx via arguments:(:identifier)
|
|
1497
|
+
def setup_admin_workspace_dropbox(res_id:, **)
|
|
1498
|
+
{ws_res_id: res_id}
|
|
1499
|
+
end
|
|
1500
|
+
|
|
1501
|
+
# admin > workspace > dropbox > list
|
|
1502
|
+
def action_admin_workspace_dropbox_list(ws_res_id:, **)
|
|
1503
|
+
query = options.get_option(:query) || {}
|
|
1504
|
+
Result::ObjectList.new(aoc_api.read('dropboxes', query.merge({'workspace_id' => ws_res_id})), fields: %w[id name description])
|
|
1505
|
+
end
|
|
1506
|
+
|
|
1507
|
+
# admin > workspace > shared_folder — res_id: already in ctx via arguments:(:identifier)
|
|
1508
|
+
def setup_admin_workspace_shared_folder(res_id:, **)
|
|
1509
|
+
resource_instance_path = "#{aoc_res_path(:workspace)}/#{res_id}"
|
|
1510
|
+
query = options.get_option(:query) || Api::AoC.workspace_access(res_id).merge({'admin' => true})
|
|
1511
|
+
shared_folders = aoc_api.read_with_paging("#{resource_instance_path}/permissions", query)[:items]
|
|
1512
|
+
{ws_res_id: res_id, shared_folders: shared_folders}
|
|
1513
|
+
end
|
|
1514
|
+
|
|
1515
|
+
# admin > workspace > shared_folder > list
|
|
1516
|
+
def action_admin_workspace_shared_folder_list(shared_folders:, **)
|
|
1517
|
+
Result::ObjectList.new(shared_folders, fields: %w[id node_name node_id file_id file.path tags.aspera.files.workspace.share_as])
|
|
1518
|
+
end
|
|
1519
|
+
|
|
1520
|
+
# admin > workspace > shared_folder > node|member — sf_id: already in ctx via arguments:(:identifier)
|
|
1521
|
+
def resolve_sf_item(shared_folders:, sf_id:, **)
|
|
1522
|
+
sf_item = shared_folders.find{ |i| i['id'].eql?(sf_id)}
|
|
1523
|
+
Aspera.assert(sf_item, 'shared folder not found')
|
|
1524
|
+
{sf_item: sf_item}
|
|
1525
|
+
end
|
|
1526
|
+
|
|
1527
|
+
alias_method :setup_admin_workspace_shared_folder_node, :resolve_sf_item
|
|
1528
|
+
alias_method :setup_admin_workspace_shared_folder_member, :resolve_sf_item
|
|
1529
|
+
|
|
1530
|
+
# admin > workspace > shared_folder > node > <FILES_COMMAND>
|
|
1531
|
+
FILES_COMMANDS.each do |cmd|
|
|
1532
|
+
define_action_method([:admin, :workspace, :shared_folder, :node, cmd]) do |sf_item:, **|
|
|
1533
|
+
execute_nodegen4_command(cmd, sf_item['node_id'], file_id: sf_item['file_id'], scope: Api::Node::Scope::ADMIN)
|
|
1534
|
+
end
|
|
1535
|
+
end
|
|
1536
|
+
|
|
1537
|
+
# admin > workspace > shared_folder > member > list
|
|
1538
|
+
def action_admin_workspace_shared_folder_member_list(ws_res_id:, sf_item:, **)
|
|
1539
|
+
node_api = aoc_api.node_api_from(
|
|
1540
|
+
node_id: sf_item['node_id'],
|
|
1541
|
+
workspace_id: ws_res_id,
|
|
1542
|
+
workspace_name: nil,
|
|
1543
|
+
scope: Api::Node::Scope::USER
|
|
1544
|
+
)
|
|
1545
|
+
result = node_api.read('permissions', {'file_id' => sf_item['file_id'], 'tag' => "aspera.files.workspace.id=#{ws_res_id}"})
|
|
1546
|
+
result.each do |item|
|
|
1547
|
+
item['member'] = begin
|
|
1548
|
+
if Api::AoC.workspace_access?(item)
|
|
1549
|
+
{'name' => '[Internal permission]'}
|
|
1550
|
+
else
|
|
1551
|
+
aoc_api.read("admin/#{item['access_type']}s/#{item['access_id']}") rescue {'name' => 'not found'}
|
|
1260
1552
|
end
|
|
1553
|
+
rescue => e
|
|
1554
|
+
{'name' => e.to_s}
|
|
1261
1555
|
end
|
|
1262
|
-
when :admin
|
|
1263
|
-
return execute_admin_action
|
|
1264
|
-
when :gateway
|
|
1265
|
-
require 'aspera/faspex_gw'
|
|
1266
|
-
parameters = value_create_modify(command: command, default: {}).symbolize_keys
|
|
1267
|
-
uri = URI.parse(parameters.delete(:url){WebServerSimple::DEFAULT_URL})
|
|
1268
|
-
server = WebServerSimple.new(uri, **parameters.slice(*WebServerSimple::PARAMS))
|
|
1269
|
-
Aspera.assert(parameters.except(*WebServerSimple::PARAMS).empty?)
|
|
1270
|
-
server.mount(uri.path, Faspex4GWServlet, aoc_api, aoc_api.workspace_info[:id])
|
|
1271
|
-
server.start
|
|
1272
|
-
return Main.result_status('Gateway terminated')
|
|
1273
|
-
else Aspera.error_unreachable_line
|
|
1274
1556
|
end
|
|
1275
|
-
|
|
1557
|
+
# TODO : read users and group name and add, if query "include_members"
|
|
1558
|
+
Result::ObjectList.new(result, fields: %w[access_type access_id access_level last_updated_at member.name member.email member.system_group_type member.system_group])
|
|
1276
1559
|
end
|
|
1277
1560
|
|
|
1278
|
-
|
|
1561
|
+
# admin > user > preferences|notifications > show|modify
|
|
1562
|
+
# (setup_admin_user_instance is auto-generated, providing res_id:)
|
|
1563
|
+
%i[preferences notifications].each do |pref|
|
|
1564
|
+
pref_path = pref.eql?(:preferences) ? 'user_interaction_preferences' : 'notification_preferences'
|
|
1565
|
+
define_action_method([:admin, :user, pref, :show]) do |res_id:, **|
|
|
1566
|
+
Result::SingleObject.new(aoc_api.read("#{aoc_res_path(:user)}/#{res_id}/#{pref_path}"))
|
|
1567
|
+
end
|
|
1568
|
+
define_action_method([:admin, :user, pref, :modify]) do |properties:, res_id:, **|
|
|
1569
|
+
aoc_api.update("#{aoc_res_path(:user)}/#{res_id}/#{pref_path}", properties)
|
|
1570
|
+
Result::Status.new('modified')
|
|
1571
|
+
end
|
|
1572
|
+
end
|
|
1573
|
+
|
|
1574
|
+
# automation > workflows > CRUD operations
|
|
1575
|
+
Operations::ALL.each do |op|
|
|
1576
|
+
define_action_method([:automation, :workflows, op]) do
|
|
1577
|
+
entity_execute(api: @automation_api, entity: 'workflows', command: op)
|
|
1578
|
+
end
|
|
1579
|
+
end
|
|
1580
|
+
|
|
1581
|
+
# automation > workflows > action > * (TODO: not fully implemented)
|
|
1582
|
+
%i[list create show].each do |cmd|
|
|
1583
|
+
define_action_method([:automation, :workflows, :action, cmd]) do |wf_id:, **|
|
|
1584
|
+
Log.log.warn{"Not implemented: #{cmd}"}
|
|
1585
|
+
step = @automation_api.create('steps', {'workflow_id' => wf_id})
|
|
1586
|
+
@automation_api.update("workflows/#{wf_id}", {'step_order' => [step['id']]})
|
|
1587
|
+
action = @automation_api.create('actions', {'step_id' => step['id'], 'type' => 'manual'})
|
|
1588
|
+
@automation_api.update("steps/#{step['id']}", {'action_order' => [action['id']]})
|
|
1589
|
+
Result::SingleObject.new(@automation_api.read("workflows/#{wf_id}"))
|
|
1590
|
+
end
|
|
1591
|
+
end
|
|
1592
|
+
|
|
1593
|
+
def action_gateway(parameters: {}, **)
|
|
1594
|
+
require 'aspera/faspex_gw'
|
|
1595
|
+
parameters = parameters.symbolize_keys
|
|
1596
|
+
uri = URI.parse(parameters.delete(:url){WebServerSimple::DEFAULT_URL})
|
|
1597
|
+
server = WebServerSimple.new(uri, **parameters.slice(*WebServerSimple::PARAMS))
|
|
1598
|
+
Aspera.assert(parameters.except(*WebServerSimple::PARAMS).empty?){"unexpected parameters: #{parameters.except(*WebServerSimple::PARAMS).keys}"}
|
|
1599
|
+
server.mount(uri.path, Faspex4GWServlet, aoc_api, aoc_api.workspace_info[:id])
|
|
1600
|
+
server.start
|
|
1601
|
+
return Result::Status.new('Gateway terminated')
|
|
1602
|
+
end
|
|
1279
1603
|
end
|
|
1280
1604
|
end
|
|
1281
1605
|
end
|