aspera-cli 4.26.2 → 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 +281 -21
- data/CONTRIBUTING.md +98 -7
- data/bin/ascli +3 -3
- 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 +26 -24
- data/lib/aspera/agent/httpgw.rb +1 -1
- data/lib/aspera/agent/node.rb +32 -0
- data/lib/aspera/agent/transferd.rb +39 -3
- data/lib/aspera/api/alee.rb +1 -1
- data/lib/aspera/api/aoc.rb +14 -16
- data/lib/aspera/api/cos_node.rb +1 -1
- data/lib/aspera/api/faspex.rb +15 -14
- data/lib/aspera/api/httpgw.rb +4 -4
- data/lib/aspera/api/node.rb +26 -23
- 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 +21 -19
- data/lib/aspera/ascp/installation.rb +37 -38
- 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 +16 -3
- data/lib/aspera/cli/error.rb +16 -0
- data/lib/aspera/cli/extended_value.rb +2 -1
- data/lib/aspera/cli/formatter.rb +41 -27
- data/lib/aspera/cli/gem_checker.rb +65 -0
- data/lib/aspera/cli/hints.rb +2 -2
- data/lib/aspera/cli/http.rb +70 -19
- 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} +334 -142
- data/lib/aspera/cli/plugins/alee.rb +20 -22
- data/lib/aspera/cli/plugins/aoc.rb +1166 -853
- data/lib/aspera/cli/plugins/ats.rb +200 -161
- data/lib/aspera/cli/plugins/base.rb +457 -100
- data/lib/aspera/cli/plugins/basic_auth.rb +14 -4
- data/lib/aspera/cli/plugins/config.rb +428 -754
- 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 +4 -4
- data/lib/aspera/cli/plugins/faspex.rb +294 -257
- data/lib/aspera/cli/plugins/faspex5.rb +585 -357
- 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 +998 -794
- data/lib/aspera/cli/plugins/oauth.rb +7 -10
- data/lib/aspera/cli/plugins/orchestrator.rb +111 -134
- data/lib/aspera/cli/plugins/preview.rb +180 -139
- data/lib/aspera/cli/plugins/server.rb +124 -79
- 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 +13 -12
- data/lib/aspera/cli/result.rb +61 -11
- data/lib/aspera/cli/runner.rb +275 -141
- 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 +83 -59
- data/lib/aspera/cli/terminal_formatter.rb +1 -1
- 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 +3 -3
- data/lib/aspera/cli/vault_manager.rb +57 -0
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +21 -20
- data/lib/aspera/command_line_builder.rb +22 -22
- data/lib/aspera/data_repository.rb +2 -1
- data/lib/aspera/dot_container.rb +4 -4
- data/lib/aspera/environment.rb +18 -11
- data/lib/aspera/exec_spec.rb +13 -0
- data/lib/aspera/faspex_gw.rb +1 -1
- 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/encrypted_hash.rb +1 -1
- data/lib/aspera/keychain/factory.rb +3 -3
- data/lib/aspera/keychain/macos_security.rb +3 -3
- data/lib/aspera/link_header.rb +82 -0
- data/lib/aspera/log.rb +17 -3
- data/lib/aspera/markdown.rb +85 -2
- data/lib/aspera/node_simulator.rb +3 -1
- data/lib/aspera/oauth/base.rb +5 -5
- data/lib/aspera/oauth/boot.rb +2 -2
- data/lib/aspera/oauth/factory.rb +7 -5
- data/lib/aspera/oauth/jwt.rb +4 -4
- data/lib/aspera/oauth/web.rb +2 -2
- data/lib/aspera/persistency_action_once.rb +8 -8
- data/lib/aspera/persistency_folder.rb +10 -3
- data/lib/aspera/preview/file_types.rb +3 -3
- data/lib/aspera/preview/generator.rb +3 -3
- data/lib/aspera/preview/utils.rb +1 -1
- data/lib/aspera/products/connect.rb +1 -1
- data/lib/aspera/products/other.rb +1 -1
- data/lib/aspera/products/transferd.rb +1 -1
- data/lib/aspera/proxy_auto_config.rb +7 -6
- data/lib/aspera/rest.rb +10 -36
- data/lib/aspera/rest_error_analyzer.rb +3 -3
- data/lib/aspera/rest_errors_aspera.rb +0 -10
- data/lib/aspera/rest_list.rb +3 -3
- data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +1853 -510
- data/lib/aspera/schema/documentation.rb +57 -26
- data/lib/aspera/schema/reader.rb +59 -10
- data/lib/aspera/schema/registry.rb +39 -6
- data/lib/aspera/secret_hider.rb +7 -2
- data/lib/aspera/ssl.rb +4 -4
- data/lib/aspera/sync/conf.schema.yaml +2 -2
- data/lib/aspera/sync/operations.rb +10 -13
- data/lib/aspera/temp_file_manager.rb +1 -1
- data/lib/aspera/timer_limiter.rb +1 -1
- data/lib/aspera/transfer/faux_file.rb +24 -11
- data/lib/aspera/transfer/parameters.rb +23 -21
- data/lib/aspera/transfer/result.rb +74 -0
- data/lib/aspera/transfer/resumer.rb +6 -6
- data/lib/aspera/transfer/spec.rb +18 -1
- data/lib/aspera/transfer/spec.schema.yaml +12 -3
- 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 +25 -3
- metadata.gz.sig +0 -0
- data/lib/aspera/json_rpc.rb +0 -52
|
@@ -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
|
|
|
@@ -56,11 +59,9 @@ module Aspera
|
|
|
56
59
|
PACKAGE_LIST_DEFAULT_FIELDS = %w[id name created_at files_completed bytes_transferred].freeze
|
|
57
60
|
|
|
58
61
|
private_constant :REDIRECT_LOCALHOST, :ADMIN_OBJECTS, :PACKAGE_RECEIVED_BASE_QUERY, :PACKAGE_LIST_DEFAULT_FIELDS
|
|
59
|
-
|
|
60
|
-
def application_name
|
|
61
|
-
'Aspera on Cloud'
|
|
62
|
-
end
|
|
62
|
+
application_name 'Aspera on Cloud'
|
|
63
63
|
|
|
64
|
+
class << self
|
|
64
65
|
# @return [Hash,NilClass]
|
|
65
66
|
def detect(base_url)
|
|
66
67
|
# no protocol ?
|
|
@@ -99,7 +100,7 @@ module Aspera
|
|
|
99
100
|
# or a sequential number if extension == :seq
|
|
100
101
|
# @param package_info [Hash] Package information
|
|
101
102
|
# @param destination_folder [String] Base folder
|
|
102
|
-
# @param fld
|
|
103
|
+
# @param fld [Array] List of fields of package
|
|
103
104
|
def unique_folder(package_info, destination_folder, fld: nil, seq: false, opt: false)
|
|
104
105
|
Aspera.assert_array_all(fld, String, type: BadArgument){'fld'}
|
|
105
106
|
Aspera.assert_values(fld.length, [1, 2]){'fld length'}
|
|
@@ -110,7 +111,6 @@ module Aspera
|
|
|
110
111
|
# NOTE: it might already exist
|
|
111
112
|
folder = "#{folder}.#{Environment.instance.sanitized_filename(fld[1])}"
|
|
112
113
|
end
|
|
113
|
-
puts("sub= #{folder}")
|
|
114
114
|
File.join(destination_folder, folder)
|
|
115
115
|
end
|
|
116
116
|
end
|
|
@@ -133,7 +133,7 @@ module Aspera
|
|
|
133
133
|
test_args: 'organization'
|
|
134
134
|
}
|
|
135
135
|
end
|
|
136
|
-
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))
|
|
137
137
|
options.parse_options!
|
|
138
138
|
# make username mandatory for jwt, this triggers interactive input
|
|
139
139
|
wiz_username = options.get_option(:username, mandatory: true)
|
|
@@ -201,16 +201,17 @@ module Aspera
|
|
|
201
201
|
}
|
|
202
202
|
end
|
|
203
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
|
+
|
|
204
209
|
def initialize(**_)
|
|
205
210
|
super
|
|
206
211
|
@cache_workspace_info = nil
|
|
207
212
|
@cache_home_node_file = nil
|
|
208
213
|
@cache_api_aoc = nil
|
|
209
214
|
@scope = Api::AoC::Scope::USER
|
|
210
|
-
options.declare(:workspace, 'Name of workspace', allowed: [String, NilClass], default: Api::AoC::DEFAULT_WORKSPACE)
|
|
211
|
-
options.declare(:new_user_option, 'New user creation option for unknown package recipients', allowed: Hash)
|
|
212
|
-
options.declare(:validate_metadata, 'Validate shared inbox metadata', allowed: Allowed::TYPES_BOOLEAN, default: true)
|
|
213
|
-
options.declare(:package_folder, 'Handling of reception of packages in folders', allowed: Hash, default: {})
|
|
214
215
|
options.parse_options!
|
|
215
216
|
# add node plugin options (for manual)
|
|
216
217
|
Node.declare_options(options)
|
|
@@ -233,13 +234,13 @@ module Aspera
|
|
|
233
234
|
api = Api::AoC.new(
|
|
234
235
|
scope: @scope,
|
|
235
236
|
subpath: base_path,
|
|
236
|
-
secret_finder:
|
|
237
|
+
secret_finder: context.secret_finder,
|
|
237
238
|
**Oauth.kwargs_from_options(options)
|
|
238
239
|
)
|
|
239
240
|
# User set a workspace ?
|
|
240
241
|
# @type [String, nil]
|
|
241
242
|
workspace = options.get_option(:workspace)
|
|
242
|
-
if !workspace.nil? && (m =
|
|
243
|
+
if !workspace.nil? && (m = Parser.percent_selector(workspace))
|
|
243
244
|
case m[:field]
|
|
244
245
|
when 'name' then api.ws_ids[:name] = m[:value]
|
|
245
246
|
when 'id' then api.ws_ids[:id] = m[:value]
|
|
@@ -287,8 +288,8 @@ module Aspera
|
|
|
287
288
|
end
|
|
288
289
|
|
|
289
290
|
# Get resource identifier from command line, either directly specifying the `id` or from `name` (percent selector).
|
|
290
|
-
# @param resource_class_path url path for resource
|
|
291
|
-
# @return identifier
|
|
291
|
+
# @param resource_class_path [String] url path for resource
|
|
292
|
+
# @return [String] identifier
|
|
292
293
|
def get_resource_id_from_args(resource_class_path)
|
|
293
294
|
return options.instance_identifier do |field, value|
|
|
294
295
|
Aspera.assert_values(field, ['name'], type: BadArgument){'selector field'}
|
|
@@ -302,15 +303,17 @@ module Aspera
|
|
|
302
303
|
end
|
|
303
304
|
|
|
304
305
|
# List all entities, given additional, default and user's queries
|
|
305
|
-
# @param resource_class_path path to query on API
|
|
306
|
-
# @param fields fields to display
|
|
307
|
-
# @param base_query
|
|
308
|
-
# @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
|
|
309
311
|
# @yieldparam query [Hash] The user's or default query for modification
|
|
310
|
-
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)
|
|
311
313
|
Aspera.assert_type(base_query, Hash)
|
|
312
314
|
Aspera.assert_type(default_query, Hash)
|
|
313
|
-
|
|
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)
|
|
314
317
|
# caller may add specific modifications or checks to query
|
|
315
318
|
yield(query) if block_given?
|
|
316
319
|
result = aoc_api.read_with_paging(resource_class_path, base_query.merge(query).compact)
|
|
@@ -321,7 +324,7 @@ module Aspera
|
|
|
321
324
|
def resolve_dropbox_name_default_ws_id(query)
|
|
322
325
|
if query.key?('dropbox_name')
|
|
323
326
|
# convenience: specify name instead of id
|
|
324
|
-
|
|
327
|
+
Aspera.assert(!query.key?('dropbox_id'), type: BadArgument){'Use field dropbox_name or dropbox_id, not both'}
|
|
325
328
|
# TODO : craft a query that looks for dropbox only in current workspace
|
|
326
329
|
query['dropbox_id'] = aoc_api.lookup_with_q('dropboxes', value: query.delete('dropbox_name'))['id']
|
|
327
330
|
end
|
|
@@ -331,7 +334,6 @@ module Aspera
|
|
|
331
334
|
end
|
|
332
335
|
|
|
333
336
|
# List all packages according to `query` option.
|
|
334
|
-
# @param <none>
|
|
335
337
|
# @return [Hash] {items,total} with all packages according to combination of user's query and default query
|
|
336
338
|
def list_all_packages_with_query
|
|
337
339
|
query = query_read_delete(default: {})
|
|
@@ -344,11 +346,13 @@ module Aspera
|
|
|
344
346
|
FILES_COMMANDS = (Node::COMMANDS_GEN4 + %i[transfer]).freeze
|
|
345
347
|
|
|
346
348
|
# Execute a node gen4 command starting at given node and file IDs
|
|
347
|
-
# @param command_repo
|
|
348
|
-
# @param node_id
|
|
349
|
-
# @param file_id
|
|
350
|
-
# @param scope
|
|
351
|
-
|
|
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)
|
|
352
356
|
top_node_api = aoc_api.node_api_from(
|
|
353
357
|
node_id: node_id,
|
|
354
358
|
scope: scope,
|
|
@@ -358,13 +362,16 @@ module Aspera
|
|
|
358
362
|
node_plugin = Node.new(context: context, api: top_node_api)
|
|
359
363
|
case command_repo
|
|
360
364
|
when *Node::COMMANDS_GEN4
|
|
361
|
-
|
|
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)
|
|
362
369
|
when :transfer
|
|
363
370
|
# client side is agent
|
|
364
371
|
# server side is transfer server
|
|
365
372
|
# in same workspace
|
|
366
|
-
push_pull =
|
|
367
|
-
source_folder =
|
|
373
|
+
push_pull = transfer_direction
|
|
374
|
+
source_folder = transfer_source
|
|
368
375
|
case push_pull
|
|
369
376
|
when :push
|
|
370
377
|
client_direction = Transfer::Spec::DIRECTION_SEND
|
|
@@ -403,585 +410,154 @@ module Aspera
|
|
|
403
410
|
|
|
404
411
|
# Execute an action on admin resources
|
|
405
412
|
# @param resource_type [Symbol] One of ADMIN_OBJECTS
|
|
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
|
-
|
|
441
|
-
|
|
442
|
-
when :group_membership
|
|
443
|
-
list_default_fields = %w[id group_id member_type member_id]
|
|
444
|
-
create_schema_path = nil
|
|
445
|
-
when :kms_profile
|
|
446
|
-
resource_class_path = "integrations/#{resource_type}s"
|
|
447
|
-
create_schema_path = nil
|
|
448
|
-
when :node
|
|
449
|
-
list_default_fields = %w[id name host access_key]
|
|
450
|
-
supported_operations += %i[do bearer_token]
|
|
451
|
-
when :operation
|
|
452
|
-
list_default_fields = %w[id type status created_at updated_at workspace_id user_id workspace_membership_id group_membership_id]
|
|
453
|
-
supported_operations = %i[list show modify]
|
|
454
|
-
when :organization, :self
|
|
455
|
-
supported_operations = Operations::SINGLETON
|
|
456
|
-
resource_instance_path = resource_class_path = resource_type
|
|
457
|
-
when :short_link
|
|
458
|
-
list_default_fields = %w[id short_url data.url_token_data.purpose password_enabled password_protected updated_by_user_id updated_at]
|
|
459
|
-
when :user
|
|
460
|
-
list_default_fields = %w[id name email]
|
|
461
|
-
supported_operations += %i[preferences notifications]
|
|
462
|
-
when :workspace
|
|
463
|
-
supported_operations += %i[shared_folder dropbox]
|
|
464
|
-
when :workspace_membership
|
|
465
|
-
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')
|
|
466
449
|
end
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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
|
+
}
|
|
474
466
|
end
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
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
|
+
)
|
|
483
491
|
end
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
when :show
|
|
487
|
-
object = aoc_api.read(resource_instance_path, query_read_delete)
|
|
488
|
-
return Result::SingleObject.new(object, fields: Formatter.all_but('certificate'))
|
|
489
|
-
when :modify
|
|
490
|
-
changes = options.get_next_argument('properties', validation: Hash, schema: schema_create_modify)
|
|
491
|
-
return do_bulk_operation(command: command, values: res_id) do |one_id|
|
|
492
|
-
aoc_api.update("#{resource_class_path}/#{one_id}", changes)
|
|
493
|
-
{'id' => one_id}
|
|
492
|
+
base.define_action_method(parent_path + [:create]) do |custom_data: {}, **ctx|
|
|
493
|
+
sl_exec_create(custom_data, **ctx)
|
|
494
494
|
end
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
aoc_api.delete("#{resource_class_path}/#{one_id}")
|
|
498
|
-
{'id' => one_id}
|
|
495
|
+
base.define_action_method(parent_path + [:list]) do |**ctx|
|
|
496
|
+
sl_exec_list(**sl_fetch_list(**ctx))
|
|
499
497
|
end
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
the_private_key = options.get_next_argument('private_key PEM value', validation: String)
|
|
503
|
-
the_public_key = OpenSSL::PKey::RSA.new(the_private_key).public_key.to_s
|
|
504
|
-
aoc_api.update(resource_instance_path, {jwt_grant_enabled: true, public_key: the_public_key})
|
|
505
|
-
return Result::Success.new
|
|
506
|
-
when :do
|
|
507
|
-
command_repo = options.get_next_command(FILES_COMMANDS)
|
|
508
|
-
return execute_nodegen4_command(command_repo, res_id, scope: Api::Node::Scope::ADMIN)
|
|
509
|
-
when :bearer_token
|
|
510
|
-
node_api = aoc_api.node_api_from(
|
|
511
|
-
node_id: res_id,
|
|
512
|
-
scope: options.get_next_argument('scope', default: Api::Node::Scope::ADMIN)
|
|
513
|
-
)
|
|
514
|
-
return Result::Text.new(node_api.oauth.authorization)
|
|
515
|
-
when :dropbox
|
|
516
|
-
command_shared = options.get_next_command(%i[list])
|
|
517
|
-
case command_shared
|
|
518
|
-
when :list
|
|
519
|
-
query = options.get_option(:query) || {}
|
|
520
|
-
res_data = aoc_api.read('dropboxes', query.merge({'workspace_id'=>res_id}))
|
|
521
|
-
return Result::ObjectList.new(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))
|
|
522
500
|
end
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
shared_folders = aoc_api.read_with_paging("#{resource_instance_path}/permissions", query)[:items]
|
|
526
|
-
# inside a workspace
|
|
527
|
-
command_shared = options.get_next_command(%i[list node member])
|
|
528
|
-
case command_shared
|
|
529
|
-
when :list
|
|
530
|
-
return Result::ObjectList.new(shared_folders, fields: %w[id node_name node_id file_id file.path tags.aspera.files.workspace.share_as])
|
|
531
|
-
when :node
|
|
532
|
-
shared_folder_id = options.instance_identifier(description: 'Shared folder ID')
|
|
533
|
-
shared_folder = shared_folders.find{ |i| i['id'].eql?(shared_folder_id)}
|
|
534
|
-
Aspera.assert(shared_folder, 'shared folder not found')
|
|
535
|
-
command_repo = options.get_next_command(FILES_COMMANDS)
|
|
536
|
-
return execute_nodegen4_command(command_repo, shared_folder['node_id'], file_id: shared_folder['file_id'], scope: Api::Node::Scope::ADMIN)
|
|
537
|
-
when :member
|
|
538
|
-
shared_folder_id = options.instance_identifier(description: 'Shared folder ID')
|
|
539
|
-
shared_folder = shared_folders.find{ |i| i['id'].eql?(shared_folder_id)}
|
|
540
|
-
Aspera.assert(shared_folder, 'shared folder not found')
|
|
541
|
-
command_shared_member = options.get_next_command(%i[list])
|
|
542
|
-
case command_shared_member
|
|
543
|
-
when :list
|
|
544
|
-
node_api = aoc_api.node_api_from(
|
|
545
|
-
node_id: shared_folder['node_id'],
|
|
546
|
-
workspace_id: res_id,
|
|
547
|
-
workspace_name: nil,
|
|
548
|
-
scope: Api::Node::Scope::USER
|
|
549
|
-
)
|
|
550
|
-
result = node_api.read(
|
|
551
|
-
'permissions',
|
|
552
|
-
{'file_id' => shared_folder['file_id'], 'tag' => "aspera.files.workspace.id=#{res_id}"}
|
|
553
|
-
)
|
|
554
|
-
result.each do |item|
|
|
555
|
-
item['member'] = begin
|
|
556
|
-
if Api::AoC.workspace_access?(item)
|
|
557
|
-
{'name'=>'[Internal permission]'}
|
|
558
|
-
else
|
|
559
|
-
aoc_api.read("admin/#{item['access_type']}s/#{item['access_id']}") rescue {'name': 'not found'}
|
|
560
|
-
end
|
|
561
|
-
rescue => e
|
|
562
|
-
{'name'=>e.to_s}
|
|
563
|
-
end
|
|
564
|
-
end
|
|
565
|
-
# TODO : read users and group name and add, if query "include_members"
|
|
566
|
-
return 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])
|
|
567
|
-
end
|
|
501
|
+
base.define_action_method(parent_path + [:delete]) do |**ctx|
|
|
502
|
+
sl_exec_delete(**sl_fetch_list(**ctx), **ctx)
|
|
568
503
|
end
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
case options.get_next_command(%i[show modify])
|
|
572
|
-
when :show
|
|
573
|
-
return Result::SingleObject.new(aoc_api.read(user_preferences_res))
|
|
574
|
-
when :modify
|
|
575
|
-
aoc_api.update(user_preferences_res, options.get_next_argument('properties', validation: Hash))
|
|
576
|
-
return Result::Status.new('modified')
|
|
504
|
+
base.define_action_method(parent_path + [:modify]) do |custom_data: {}, **ctx|
|
|
505
|
+
sl_exec_modify(custom_data, **sl_fetch_list(**ctx), **ctx)
|
|
577
506
|
end
|
|
578
|
-
else Aspera.error_unexpected_value(command)
|
|
579
507
|
end
|
|
580
508
|
end
|
|
581
509
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
when :types
|
|
589
|
-
return Result::ObjectList.new(apps_info)
|
|
590
|
-
when :settings
|
|
591
|
-
app_type = options.get_next_command(all_app_syms)
|
|
592
|
-
cmd_path = "/apps/#{app_type}/settings"
|
|
593
|
-
command_app_settings = options.get_next_command(Operations::SINGLETON)
|
|
594
|
-
case command_app_settings
|
|
595
|
-
when :show
|
|
596
|
-
return Result::SingleObject.new(aoc_api.read(cmd_path))
|
|
597
|
-
when :modify
|
|
598
|
-
aoc_api.update(cmd_path, options.get_next_argument('properties', validation: Hash))
|
|
599
|
-
return Result::Status.new('modified')
|
|
600
|
-
end
|
|
601
|
-
when :instance
|
|
602
|
-
list_default_query = {workspace_id: aoc_api.workspace_info[:id]}
|
|
603
|
-
list_default_fields = %w[id app_type available workspace_id]
|
|
604
|
-
command_app_instances = options.get_next_command(%i[list] + Operations::SINGLETON)
|
|
605
|
-
resource_path = 'admin/apps_new'
|
|
606
|
-
if Operations::SINGLETON.include?(command_app_instances)
|
|
607
|
-
app_type = options.get_next_command(all_app_syms)
|
|
608
|
-
resource_path = "#{resource_path}/#{app_type}/#{options.instance_identifier(description: "#{app_type} identifier")}"
|
|
609
|
-
end
|
|
610
|
-
case command_app_instances
|
|
611
|
-
when :list
|
|
612
|
-
return result_list(resource_path, fields: list_default_fields, default_query: list_default_query)
|
|
613
|
-
when :show
|
|
614
|
-
return Result::SingleObject.new(aoc_api.read(resource_path, query_read_delete))
|
|
615
|
-
when :modify
|
|
616
|
-
aoc_api.update(resource_path, options.get_next_argument('properties', validation: Hash))
|
|
617
|
-
return Result::Status.new('modified')
|
|
618
|
-
end
|
|
619
|
-
when :membership
|
|
620
|
-
resource_path = 'apps/app_memberships'
|
|
621
|
-
command_app_member = options.get_next_command(%i[create list show delete])
|
|
622
|
-
resource_path = "#{resource_path}/#{options.instance_identifier(description: 'membership id')}" unless Operations::GLOBAL.include?(command_app_member)
|
|
623
|
-
case command_app_member
|
|
624
|
-
when :list
|
|
625
|
-
return result_list(resource_path)
|
|
626
|
-
when :delete
|
|
627
|
-
aoc_api.delete(resource_path)
|
|
628
|
-
return Result::Status.new('deleted')
|
|
629
|
-
when :show
|
|
630
|
-
return Result::SingleObject.new(aoc_api.read(resource_path, query_read_delete))
|
|
631
|
-
when :create
|
|
632
|
-
data = options.get_next_argument('membership properties', validation: Hash)
|
|
633
|
-
app_type = data.delete('app_type')
|
|
634
|
-
Aspera.assert_type(app_type, String){'app_type'}
|
|
635
|
-
Aspera.assert_values(app_type.to_sym, all_app_syms){'app_type'}
|
|
636
|
-
return Result::SingleObject.new(aoc_api.create("apps/#{app_type}/app_memberships", data))
|
|
637
|
-
end
|
|
638
|
-
end
|
|
639
|
-
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
|
|
640
516
|
|
|
641
517
|
ADMIN_ACTIONS = %i[bearer_token application ats usage_reports analytics subscription auth_providers].concat(ADMIN_OBJECTS).freeze
|
|
642
518
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
return Result::Text.new(aoc_api.oauth.authorization)
|
|
650
|
-
when *ADMIN_OBJECTS
|
|
651
|
-
return execute_resource_action(command_admin)
|
|
652
|
-
when :application
|
|
653
|
-
return execute_application_action
|
|
654
|
-
when :auth_providers
|
|
655
|
-
command_auth_prov = options.get_next_command(%i[list update])
|
|
656
|
-
case command_auth_prov
|
|
657
|
-
when :list
|
|
658
|
-
return result_list('admin/auth_providers')
|
|
659
|
-
when :update
|
|
660
|
-
Aspera.error_not_implemented
|
|
661
|
-
end
|
|
662
|
-
when :subscription
|
|
663
|
-
org = aoc_api.read('organization')
|
|
664
|
-
bss_graphql = api_from_options('bss/platform/graphql')
|
|
665
|
-
command_subscription = options.get_next_command(%i[account usage])
|
|
666
|
-
case command_subscription
|
|
667
|
-
when :account
|
|
668
|
-
# cspell:disable
|
|
669
|
-
graphql_query = <<-GRAPHQL
|
|
670
|
-
query ($organization_id: ID!) {
|
|
671
|
-
aoc (organization_id: $organization_id) {
|
|
672
|
-
bssSubscription {
|
|
673
|
-
aocVersion
|
|
674
|
-
endDate
|
|
675
|
-
startDate
|
|
676
|
-
termMonths
|
|
677
|
-
plan
|
|
678
|
-
trial
|
|
679
|
-
termType
|
|
680
|
-
aocOrganizations {
|
|
681
|
-
id
|
|
682
|
-
}
|
|
683
|
-
additionalStorageVolumeGb
|
|
684
|
-
additionalEgressVolumeGb
|
|
685
|
-
term {
|
|
686
|
-
startDate
|
|
687
|
-
endDate
|
|
688
|
-
transferVolumeGb
|
|
689
|
-
egressVolumeGb
|
|
690
|
-
storageVolumeGb
|
|
691
|
-
transferVolumeOffsetGb
|
|
692
|
-
}
|
|
693
|
-
paygoRate {
|
|
694
|
-
transferRate
|
|
695
|
-
storageRate
|
|
696
|
-
currency
|
|
697
|
-
}
|
|
698
|
-
aocPlanData {
|
|
699
|
-
tier
|
|
700
|
-
trial
|
|
701
|
-
workspaces { max }
|
|
702
|
-
users {
|
|
703
|
-
planAmount
|
|
704
|
-
max
|
|
705
|
-
}
|
|
706
|
-
samlIntegration
|
|
707
|
-
activity
|
|
708
|
-
sharedInboxes
|
|
709
|
-
uniqueUrls
|
|
710
|
-
support
|
|
711
|
-
watermarking
|
|
712
|
-
byok
|
|
713
|
-
automation { planAmount, max }
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
GRAPHQL
|
|
719
|
-
# cspell:enable
|
|
720
|
-
result = bss_graphql.create(nil, {query: graphql_query, variables: {organization_id: org['id']}})['data']
|
|
721
|
-
return Result::SingleObject.new(result['aoc']['bssSubscription'])
|
|
722
|
-
when :usage
|
|
723
|
-
# cspell:disable
|
|
724
|
-
graphql_query = <<-GRAPHQL
|
|
725
|
-
query ($organization_id: ID!, $startDate: Date!, $endDate: Date!, $aggregate: TransferUsageAggregateOption!) {
|
|
726
|
-
aoc (organization_id: $organization_id) {
|
|
727
|
-
bssSubscription {
|
|
728
|
-
aocOrganizations { id }
|
|
729
|
-
additionalStorageVolumeGb
|
|
730
|
-
additionalEgressVolumeGb
|
|
731
|
-
aocPlanData {
|
|
732
|
-
tier
|
|
733
|
-
trial
|
|
734
|
-
}
|
|
735
|
-
term {
|
|
736
|
-
transferVolumeGb
|
|
737
|
-
egressVolumeGb
|
|
738
|
-
storageVolumeGb
|
|
739
|
-
startDate
|
|
740
|
-
endDate
|
|
741
|
-
transferVolumeOffsetGb
|
|
742
|
-
}
|
|
743
|
-
termMonths
|
|
744
|
-
transferUsages (startDate: $startDate, endDate: $endDate, aggregate: $aggregate) { mbTotal }
|
|
745
|
-
egressUsages (startDate: $startDate, endDate: $endDate, aggregate: $aggregate) { usageMb }
|
|
746
|
-
}
|
|
747
|
-
subscriptionEntitlements {
|
|
748
|
-
id
|
|
749
|
-
transferUsages (startDate: $startDate, endDate: $endDate, aggregate: $aggregate) { mbTotal }
|
|
750
|
-
egressUsages (startDate: $startDate, endDate: $endDate, aggregate: $aggregate) { usageMb }
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
GRAPHQL
|
|
755
|
-
aggregate = options.get_next_argument('aggregation', accept_list: %i[ALL MONTHLY], default: :ALL)
|
|
756
|
-
today = Date.today
|
|
757
|
-
start_date = options.get_next_argument('start date', mandatory: false, default: today.prev_year.strftime('%Y-%m-%d'))
|
|
758
|
-
end_date = options.get_next_argument('end date', mandatory: false, default: today.strftime('%Y-%m-%d'))
|
|
759
|
-
# cspell:enable
|
|
760
|
-
result = bss_graphql.create(
|
|
761
|
-
nil,
|
|
762
|
-
{
|
|
763
|
-
query: graphql_query,
|
|
764
|
-
variables: {
|
|
765
|
-
organization_id: org['id'],
|
|
766
|
-
aggregate: aggregate,
|
|
767
|
-
startDate: start_date,
|
|
768
|
-
endDate: end_date
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
)['data']
|
|
772
|
-
return Result::SingleObject.new(result['aoc'])
|
|
773
|
-
end
|
|
774
|
-
when :ats
|
|
775
|
-
ats_api = Rest.new(**aoc_api.params.deep_merge({
|
|
776
|
-
base_url: "#{aoc_api.base_url}/admin/ats/pub/v1",
|
|
777
|
-
auth: {params: {scope: Api::AoC::Scope::ADMIN_USER}}
|
|
778
|
-
}))
|
|
779
|
-
return Ats.new(context: context, api: ats_api).execute_action
|
|
780
|
-
when :analytics
|
|
781
|
-
analytics_api = Rest.new(**aoc_api.params.deep_merge({
|
|
782
|
-
base_url: "#{aoc_api.base_url.gsub('/api/v1', '')}/analytics/v2",
|
|
783
|
-
auth: {params: {scope: Api::AoC::Scope::ADMIN_USER}}
|
|
784
|
-
}))
|
|
785
|
-
command_analytics = options.get_next_command(%i[application_events transfers files])
|
|
786
|
-
case command_analytics
|
|
787
|
-
when :application_events
|
|
788
|
-
event_type = command_analytics.to_s
|
|
789
|
-
events = analytics_api.read("organizations/#{aoc_api.current_user_info['organization_id']}/#{event_type}")[event_type]
|
|
790
|
-
return Result::ObjectList.new(events)
|
|
791
|
-
when :transfers
|
|
792
|
-
event_type = command_analytics.to_s
|
|
793
|
-
event_resource_type = options.get_next_argument('resource', accept_list: %i[organizations users nodes])
|
|
794
|
-
event_resource_id = options.get_next_argument("#{event_resource_type} identifier", mandatory: false) ||
|
|
795
|
-
case event_resource_type
|
|
796
|
-
when :organizations then aoc_api.current_user_info['organization_id']
|
|
797
|
-
when :users then aoc_api.current_user_info['id']
|
|
798
|
-
when :nodes then aoc_api.current_user_info['read_only_home_node_id']
|
|
799
|
-
else Aspera.error_unreachable_line
|
|
800
|
-
end
|
|
801
|
-
filter = query_read_delete(default: {})
|
|
802
|
-
filter['limit'] ||= 100
|
|
803
|
-
if options.get_option(:once_only, mandatory: true)
|
|
804
|
-
saved_date = []
|
|
805
|
-
start_date_persistency = PersistencyActionOnce.new(
|
|
806
|
-
manager: persistency,
|
|
807
|
-
data: saved_date,
|
|
808
|
-
id: IdGenerator.from_list(
|
|
809
|
-
'aoc_ana_date',
|
|
810
|
-
options.get_option(:url, mandatory: true),
|
|
811
|
-
aoc_api.workspace_info[:name],
|
|
812
|
-
event_resource_type.to_s,
|
|
813
|
-
event_resource_id
|
|
814
|
-
)
|
|
815
|
-
)
|
|
816
|
-
start_date_time = saved_date.first
|
|
817
|
-
stop_date_time = Time.now.utc.strftime('%FT%T.%LZ')
|
|
818
|
-
saved_date[0] = stop_date_time
|
|
819
|
-
filter['start_time'] = start_date_time unless start_date_time.nil?
|
|
820
|
-
filter['stop_time'] = stop_date_time
|
|
821
|
-
end
|
|
822
|
-
events = analytics_api.read("#{event_resource_type}/#{event_resource_id}/#{event_type}", filter)[event_type]
|
|
823
|
-
start_date_persistency&.save
|
|
824
|
-
if !options.get_option(:notify_to).nil?
|
|
825
|
-
events.each do |tr_event|
|
|
826
|
-
config.send_email_template(values: {ev: tr_event})
|
|
827
|
-
end
|
|
828
|
-
end
|
|
829
|
-
return Result::ObjectList.new(events)
|
|
830
|
-
when :files
|
|
831
|
-
event_type = command_analytics.to_s
|
|
832
|
-
event_resource_type = options.get_next_argument('resource', accept_list: %i[organizations users nodes])
|
|
833
|
-
event_resource_id = options.instance_identifier(description: "#{event_resource_type} identifier")
|
|
834
|
-
event_resource_id =
|
|
835
|
-
case event_resource_type
|
|
836
|
-
when :organizations then aoc_api.current_user_info['organization_id']
|
|
837
|
-
when :users then aoc_api.current_user_info['id']
|
|
838
|
-
when :nodes then aoc_api.current_user_info['read_only_home_node_id']
|
|
839
|
-
else Aspera.error_unreachable_line
|
|
840
|
-
end if event_resource_id.empty?
|
|
841
|
-
event_uuid = options.instance_identifier(description: 'event uuid')
|
|
842
|
-
filter = query_read_delete(default: {})
|
|
843
|
-
filter['limit'] ||= 100
|
|
844
|
-
events = analytics_api.read("#{event_resource_type}/#{event_resource_id}/transfers/#{event_uuid}/#{event_type}", filter)[event_type]
|
|
845
|
-
return Result::ObjectList.new(events)
|
|
846
|
-
end
|
|
847
|
-
when :usage_reports
|
|
848
|
-
return result_list('usage_reports', base_query: workspace_id_hash)
|
|
849
|
-
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
|
+
}))
|
|
850
525
|
end
|
|
851
526
|
|
|
852
|
-
#
|
|
853
|
-
#
|
|
854
|
-
#
|
|
855
|
-
#
|
|
856
|
-
|
|
857
|
-
# - a short URL path, e.g. `dxyRpT9`
|
|
858
|
-
# @param shared_data [Hash] Information for shared data: dropbox_id+name or file_id+node_id
|
|
859
|
-
# @param shared_data [Hash] Shared data for the short link
|
|
860
|
-
# @yieldparam operation [Symbol] Operation type (:create, :update, :delete)
|
|
861
|
-
# @yieldparam resource_id [String] Resource ID for permission management
|
|
862
|
-
# @yieldparam access_levels [Object] Access levels for permissions
|
|
863
|
-
def short_link_command(**shared_data, &perm_block)
|
|
864
|
-
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)
|
|
865
532
|
if shared_data.keys.sort == %i[dropbox_id name]
|
|
866
|
-
# Packages app, Shared inbox
|
|
867
533
|
token_purpose = 'send_package_to_dropbox'
|
|
868
534
|
short_link_purpose = link_type.eql?(:public) ? 'send_package_to_dropbox' : 'shared_folder_auth_link'
|
|
869
535
|
elsif shared_data.keys.sort == %i[file_id node_id]
|
|
870
|
-
# Files app, Shared folder
|
|
871
536
|
token_purpose = 'view_shared_file'
|
|
872
537
|
short_link_purpose = link_type.eql?(:public) ? 'token_auth_redirection' : 'shared_folder_auth_link'
|
|
873
538
|
else
|
|
874
539
|
Aspera.error_unexpected_value(shared_data.keys)
|
|
875
540
|
end
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
when :private
|
|
887
|
-
create_payload[:data] = shared_data
|
|
888
|
-
when :public
|
|
889
|
-
create_payload[:expires_at] = nil
|
|
890
|
-
create_payload[:password_enabled] = false
|
|
891
|
-
shared_data[:name] = ''
|
|
892
|
-
create_payload[:data] = {
|
|
893
|
-
aoc: true,
|
|
894
|
-
url_token_data: {
|
|
895
|
-
data: shared_data,
|
|
896
|
-
purpose: token_purpose
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
end
|
|
900
|
-
custom_data = value_create_modify(command: command, default: {})
|
|
901
|
-
access_levels = custom_data.delete('access_levels')
|
|
902
|
-
if (pass = custom_data.delete('password'))
|
|
903
|
-
create_payload[:data][:url_token_data][:password] = pass
|
|
904
|
-
create_payload[:password_enabled] = true
|
|
905
|
-
end
|
|
906
|
-
create_payload.deep_merge!(custom_data)
|
|
907
|
-
result_create_short_link = aoc_api.create('short_links', create_payload)
|
|
908
|
-
# Creation: perm_block: permission on node
|
|
909
|
-
yield(:create, result_create_short_link['resource_id'], access_levels) if block_given? && link_type.eql?(:public)
|
|
910
|
-
return Result::SingleObject.new(result_create_short_link)
|
|
911
|
-
when :delete, :list, :show, :modify
|
|
912
|
-
workspace_id_hash(shared_data)
|
|
913
|
-
query = if link_type.eql?(:private)
|
|
914
|
-
shared_data
|
|
915
|
-
else
|
|
916
|
-
{
|
|
917
|
-
url_token_data: {
|
|
918
|
-
data: shared_data,
|
|
919
|
-
purpose: token_purpose
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
end
|
|
923
|
-
list_params = {
|
|
924
|
-
json_query: query.to_json,
|
|
925
|
-
purpose: short_link_purpose,
|
|
926
|
-
edit_access: true,
|
|
927
|
-
# embed: 'updated_by_user',
|
|
928
|
-
sort: '-created_at'
|
|
929
|
-
}
|
|
930
|
-
short_list = aoc_api.read_with_paging('short_links', list_params.merge(query_read_delete(default: {})).compact)
|
|
931
|
-
case command
|
|
932
|
-
when :delete
|
|
933
|
-
one_id = options.instance_identifier(description: 'short link id')
|
|
934
|
-
if link_type.eql?(:public)
|
|
935
|
-
found = short_list[:items].find{ |item| item['id'].eql?(one_id)}
|
|
936
|
-
raise BadIdentifier.new('Short link', one_id) if found.nil?
|
|
937
|
-
yield(:delete, found['resource_id'], nil)
|
|
938
|
-
end
|
|
939
|
-
aoc_api.delete("short_links/#{one_id}", {
|
|
940
|
-
edit_access: true,
|
|
941
|
-
json_query: shared_data.to_json
|
|
942
|
-
})
|
|
943
|
-
return Result::Status.new('deleted')
|
|
944
|
-
when :list
|
|
945
|
-
return Result::ObjectList.new(short_list[:items], fields: Formatter.all_but('data'), total: short_list[:total])
|
|
946
|
-
when :show
|
|
947
|
-
one_id = options.instance_identifier(description: 'short link id')
|
|
948
|
-
found = short_list[:items].find{ |item| item['id'].eql?(one_id)}
|
|
949
|
-
raise BadIdentifier.new('Short link', one_id) if found.nil?
|
|
950
|
-
return Result::SingleObject.new(found, fields: Formatter.all_but('data'))
|
|
951
|
-
when :modify
|
|
952
|
-
one_id = options.instance_identifier(description: 'short link id')
|
|
953
|
-
node_file = shared_data.slice(:node_id, :file_id)
|
|
954
|
-
modify_payload = {
|
|
955
|
-
edit_access: true,
|
|
956
|
-
json_query: node_file
|
|
957
|
-
}
|
|
958
|
-
custom_data = value_create_modify(command: command)
|
|
959
|
-
if (pass = custom_data.delete('password'))
|
|
960
|
-
modify_payload[:password_enabled] = true
|
|
961
|
-
modify_payload[:data] = {
|
|
962
|
-
url_token_data: {
|
|
963
|
-
password: pass,
|
|
964
|
-
data: node_file
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
else
|
|
968
|
-
modify_payload[:password_enabled] = false
|
|
969
|
-
end
|
|
970
|
-
if custom_data.delete('access_levels')
|
|
971
|
-
# Modification: perm_block: permission on node
|
|
972
|
-
found = short_list[:items].find{ |item| item['id'].eql?(one_id)}
|
|
973
|
-
raise BadIdentifier.new('Short link', one_id) if found.nil?
|
|
974
|
-
yield(:update, found['resource_id'], access_levels)
|
|
975
|
-
end
|
|
976
|
-
modify_payload.deep_merge!(custom_data)
|
|
977
|
-
aoc_api.update("short_links/#{one_id}", modify_payload)
|
|
978
|
-
return Result::Status.new('modified')
|
|
979
|
-
end
|
|
980
|
-
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}}
|
|
981
551
|
end
|
|
552
|
+
{
|
|
553
|
+
json_query: query.to_json,
|
|
554
|
+
purpose: short_link_purpose,
|
|
555
|
+
edit_access: true,
|
|
556
|
+
sort: '-created_at'
|
|
557
|
+
}
|
|
982
558
|
end
|
|
983
559
|
|
|
984
|
-
# @return persistency object if option `once_only` is used.
|
|
560
|
+
# @return [PersistencyActionOnce, nil] persistency object if option `once_only` is used.
|
|
985
561
|
def package_persistency
|
|
986
562
|
return unless options.get_option(:once_only, mandatory: true)
|
|
987
563
|
# TODO: add query info to id
|
|
@@ -1003,290 +579,1027 @@ module Aspera
|
|
|
1003
579
|
all_packages.reject!{ |pkg| skip_package[pkg['id']]}
|
|
1004
580
|
end
|
|
1005
581
|
|
|
1006
|
-
#
|
|
1007
|
-
ACTIONS = %i[reminder servers bearer_token organization tier_restrictions user packages files admin automation gateway].freeze
|
|
582
|
+
# --- DSL command declarations ---
|
|
1008
583
|
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
folder_name = aoc_api.node_api_from(node_id: aoc_api.home[:node_id]).read("files/#{aoc_api.home[:file_id]}")['name']
|
|
1015
|
-
formatter.display_status("Private Folder: #{folder_name}")
|
|
1016
|
-
end
|
|
1017
|
-
end
|
|
1018
|
-
case command
|
|
1019
|
-
when :reminder
|
|
1020
|
-
# send an email reminder with list of orgs
|
|
1021
|
-
user_email = options.get_option(:username, mandatory: true)
|
|
1022
|
-
no_auth_api = Api::AoC.new(url: options.get_option(:url), auth: :none)
|
|
1023
|
-
no_auth_api.create('organization_reminders', {email: user_email})
|
|
1024
|
-
return Result::Status.new("List of organizations user is member of, has been sent by e-mail to #{user_email}")
|
|
1025
|
-
when :servers
|
|
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
|
|
1026
589
|
no_auth_api = Api::AoC.new(url: options.get_option(:url), auth: :none)
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
when :modify
|
|
1053
|
-
aoc_api.update("users/#{aoc_api.current_user_info(exception: true)['id']}", options.get_next_argument('properties', validation: Hash))
|
|
1054
|
-
return Result::Status.new('modified')
|
|
1055
|
-
end
|
|
1056
|
-
when :preferences, :notifications
|
|
1057
|
-
user_preferences_res = "users/#{aoc_api.current_user_info(exception: true)['id']}/#{user_cmd.eql?(:preferences) ? 'user_interaction_preferences' : 'notification_preferences'}"
|
|
1058
|
-
case options.get_next_command(%i[show modify])
|
|
1059
|
-
when :show
|
|
1060
|
-
return Result::SingleObject.new(aoc_api.read(user_preferences_res))
|
|
1061
|
-
when :modify
|
|
1062
|
-
aoc_api.update(user_preferences_res, options.get_next_argument('properties', validation: Hash))
|
|
1063
|
-
return Result::Status.new('modified')
|
|
1064
|
-
end
|
|
590
|
+
Result::ObjectList.new(no_auth_api.read('servers'))
|
|
591
|
+
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
|
|
1065
615
|
end
|
|
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
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
skip_ids_persistency = package_persistency
|
|
1110
|
-
case ids_to_download
|
|
1111
|
-
when SpecialValues::INIT
|
|
1112
|
-
all_packages = list_all_packages_with_query[:items]
|
|
1113
|
-
Aspera.assert(skip_ids_persistency, 'INIT requires option once_only')
|
|
1114
|
-
skip_ids_persistency.data.clear.concat(all_packages.map{ |e| e['id']})
|
|
1115
|
-
skip_ids_persistency.save
|
|
1116
|
-
return Result::Status.new("Initialized skip for #{skip_ids_persistency.data.count} package(s)")
|
|
1117
|
-
when SpecialValues::ALL
|
|
1118
|
-
all_packages = list_all_packages_with_query[:items]
|
|
1119
|
-
# remove from list the ones already downloaded
|
|
1120
|
-
reject_packages_from_persistency(all_packages, skip_ids_persistency)
|
|
1121
|
-
ids_to_download = all_packages.map{ |e| e['id']}
|
|
1122
|
-
formatter.display_status("Found #{ids_to_download.length} package(s).")
|
|
1123
|
-
else
|
|
1124
|
-
# single id to array
|
|
1125
|
-
ids_to_download = [ids_to_download] unless ids_to_download.is_a?(Array)
|
|
1126
|
-
end
|
|
1127
|
-
# download all files, or specified list only
|
|
1128
|
-
ts_paths = transfer.ts_source_paths(default: ['.'])
|
|
1129
|
-
per_package_def = options.get_option(:package_folder).symbolize_keys
|
|
1130
|
-
save_metadata = per_package_def.delete(:inf)
|
|
1131
|
-
# get value outside of loop
|
|
1132
|
-
destination_folder = transfer.destination_folder(Transfer::Spec::DIRECTION_RECEIVE)
|
|
1133
|
-
result_transfer = []
|
|
1134
|
-
ids_to_download.each do |package_id|
|
|
1135
|
-
package_info = aoc_api.read("packages/#{package_id}")
|
|
1136
|
-
package_node_api = aoc_api.node_api_from(
|
|
1137
|
-
node_id: package_info['node_id'],
|
|
1138
|
-
package_info: package_info,
|
|
1139
|
-
**workspace_id_hash(name: true)
|
|
1140
|
-
)
|
|
1141
|
-
transfer_spec = package_node_api.transfer_spec_gen4(
|
|
1142
|
-
package_info['contents_file_id'],
|
|
1143
|
-
Transfer::Spec::DIRECTION_RECEIVE,
|
|
1144
|
-
{'paths'=> ts_paths}
|
|
1145
|
-
)
|
|
1146
|
-
transfer.user_transfer_spec['destination_root'] = self.class.unique_folder(package_info, destination_folder, **per_package_def) unless per_package_def.empty?
|
|
1147
|
-
dest_folder = transfer.user_transfer_spec['destination_root'] || destination_folder
|
|
1148
|
-
formatter.display_status(%Q{Downloading package: [#{package_info['id']}] "#{package_info['name']}" to [#{dest_folder}]})
|
|
1149
|
-
statuses = transfer.start(
|
|
1150
|
-
transfer_spec,
|
|
1151
|
-
rest_token: package_node_api
|
|
1152
|
-
)
|
|
1153
|
-
File.write(File.join(dest_folder, "#{package_id}.info.json"), package_info.to_json) if save_metadata
|
|
1154
|
-
result_transfer.push({'package' => package_id, Runner::STATUS_FIELD => statuses})
|
|
1155
|
-
# update skip list only if all transfer sessions completed
|
|
1156
|
-
if skip_ids_persistency && TransferAgent.session_status(statuses).eql?(:success)
|
|
1157
|
-
skip_ids_persistency.data.push(package_id)
|
|
1158
|
-
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]))
|
|
1159
659
|
end
|
|
660
|
+
command(op, description: op.to_s.tr('_', ' ').capitalize, **merged)
|
|
1160
661
|
end
|
|
1161
|
-
return Runner.result_transfer_multiple(result_transfer)
|
|
1162
|
-
when :show
|
|
1163
|
-
package_id = options.instance_identifier
|
|
1164
|
-
package_info = aoc_api.read("packages/#{package_id}")
|
|
1165
|
-
return Result::SingleObject.new(package_info)
|
|
1166
|
-
when :list
|
|
1167
|
-
result = list_all_packages_with_query
|
|
1168
|
-
skip_ids_persistency = package_persistency
|
|
1169
|
-
reject_packages_from_persistency(result[:items], skip_ids_persistency)
|
|
1170
|
-
display_fields = PACKAGE_LIST_DEFAULT_FIELDS
|
|
1171
|
-
display_fields += ['workspace_id'] if aoc_api.workspace_info[:id].nil?
|
|
1172
|
-
return Result::ObjectList.new(result[:items], fields: display_fields, total: result[:total])
|
|
1173
|
-
when :delete
|
|
1174
|
-
return do_bulk_operation(command: package_command, values: options.instance_identifier) do |package_id|
|
|
1175
|
-
Aspera.assert_type(package_id, String, Integer){'identifier'}
|
|
1176
|
-
aoc_api.delete("packages/#{package_id}")
|
|
1177
|
-
end
|
|
1178
|
-
when :modify
|
|
1179
|
-
package_id = options.instance_identifier
|
|
1180
|
-
package_data = value_create_modify(command: package_command)
|
|
1181
|
-
aoc_api.update("packages/#{package_id}", package_data)
|
|
1182
|
-
return Result::Status.new('modified')
|
|
1183
|
-
when *Node::NODE4_READ_ACTIONS
|
|
1184
|
-
package_id = options.instance_identifier
|
|
1185
|
-
package_info = aoc_api.read("packages/#{package_id}")
|
|
1186
|
-
return execute_nodegen4_command(package_command, package_info['node_id'], file_id: package_info['contents_file_id'], scope: Api::Node::Scope::USER)
|
|
1187
662
|
end
|
|
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
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
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')
|
|
1235
749
|
end
|
|
1236
|
-
|
|
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')
|
|
835
|
+
end
|
|
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
|
|
1237
1354
|
end
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
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'}
|
|
1271
1552
|
end
|
|
1553
|
+
rescue => e
|
|
1554
|
+
{'name' => e.to_s}
|
|
1272
1555
|
end
|
|
1273
|
-
when :admin
|
|
1274
|
-
return execute_admin_action
|
|
1275
|
-
when :gateway
|
|
1276
|
-
require 'aspera/faspex_gw'
|
|
1277
|
-
parameters = value_create_modify(command: command, default: {}).symbolize_keys
|
|
1278
|
-
uri = URI.parse(parameters.delete(:url){WebServerSimple::DEFAULT_URL})
|
|
1279
|
-
server = WebServerSimple.new(uri, **parameters.slice(*WebServerSimple::PARAMS))
|
|
1280
|
-
Aspera.assert(parameters.except(*WebServerSimple::PARAMS).empty?){"unexpected parameters: #{parameters.except(*WebServerSimple::PARAMS).keys}"}
|
|
1281
|
-
server.mount(uri.path, Faspex4GWServlet, aoc_api, aoc_api.workspace_info[:id])
|
|
1282
|
-
server.start
|
|
1283
|
-
return Result::Status.new('Gateway terminated')
|
|
1284
|
-
else Aspera.error_unreachable_line
|
|
1285
1556
|
end
|
|
1286
|
-
|
|
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])
|
|
1287
1559
|
end
|
|
1288
1560
|
|
|
1289
|
-
|
|
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
|
|
1290
1603
|
end
|
|
1291
1604
|
end
|
|
1292
1605
|
end
|