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