aspera-cli 4.27.3 → 4.27.5
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 +118 -0
- data/CONTRIBUTING.md +5 -2
- data/bin/ascli +1 -0
- data/docs/README.md +224 -42
- data/lib/aspera/agent/base.rb +7 -2
- data/lib/aspera/agent/connect.rb +0 -4
- data/lib/aspera/agent/desktop.rb +0 -4
- data/lib/aspera/agent/direct.rb +49 -21
- data/lib/aspera/agent/node.rb +6 -5
- data/lib/aspera/agent/transferd.rb +2 -2
- data/lib/aspera/api/faspex.rb +2 -0
- data/lib/aspera/api/httpgw.rb +1 -2
- data/lib/aspera/api/node.rb +3 -3
- data/lib/aspera/ascp/installation.rb +1 -1
- data/lib/aspera/cli/async_transfer_store.rb +10 -9
- data/lib/aspera/cli/bootstrapper.rb +3 -1
- data/lib/aspera/cli/command_registry.rb +69 -7
- data/lib/aspera/cli/command_spec.rb +1 -1
- data/lib/aspera/cli/extended_value.rb +4 -3
- data/lib/aspera/cli/formatter.rb +10 -8
- data/lib/aspera/cli/http.rb +8 -20
- data/lib/aspera/cli/option_types.rb +3 -1
- data/lib/aspera/cli/option_value.rb +16 -19
- data/lib/aspera/cli/options.schema.yaml +86 -10
- data/lib/aspera/cli/parser.rb +30 -18
- data/lib/aspera/cli/plugins/aoc.rb +133 -156
- data/lib/aspera/cli/plugins/ats.rb +1 -7
- data/lib/aspera/cli/plugins/base.rb +40 -34
- data/lib/aspera/cli/plugins/config.rb +24 -11
- data/lib/aspera/cli/plugins/console.rb +1 -1
- data/lib/aspera/cli/plugins/faspex5.rb +30 -11
- data/lib/aspera/cli/plugins/faspio.rb +1 -1
- data/lib/aspera/cli/plugins/node.rb +26 -19
- data/lib/aspera/cli/plugins/orchestrator.rb +73 -44
- data/lib/aspera/cli/plugins/preview.rb +21 -19
- data/lib/aspera/cli/plugins/server.rb +3 -4
- data/lib/aspera/cli/plugins/shares.rb +21 -24
- data/lib/aspera/cli/preset_actions.rb +28 -18
- data/lib/aspera/cli/preset_manager.rb +34 -19
- data/lib/aspera/cli/prompt.rb +2 -1
- data/lib/aspera/cli/result.rb +33 -22
- data/lib/aspera/cli/runner.rb +1 -5
- data/lib/aspera/cli/sync_actions.rb +17 -16
- data/lib/aspera/cli/transfer_actions.rb +14 -3
- data/lib/aspera/cli/transfer_agent.rb +6 -4
- data/lib/aspera/cli/transfer_progress.rb +290 -55
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +1 -1
- data/lib/aspera/coverage.rb +0 -1
- data/lib/aspera/environment.rb +29 -5
- data/lib/aspera/keychain/encrypted_hash.rb +1 -1
- data/lib/aspera/keychain/factory.rb +2 -1
- data/lib/aspera/log.rb +25 -2
- data/lib/aspera/node_emulator.rb +759 -0
- data/lib/aspera/oauth/base.rb +2 -1
- data/lib/aspera/oauth/factory.rb +6 -3
- data/lib/aspera/oauth/json_credentials.rb +34 -0
- data/lib/aspera/oauth/jwt.rb +3 -4
- data/lib/aspera/oauth.rb +1 -0
- data/lib/aspera/persistency_folder.rb +1 -3
- data/lib/aspera/preview/generator.rb +4 -1
- data/lib/aspera/preview/options.schema.yaml +119 -0
- data/lib/aspera/rest/aspera_errors.rb +12 -0
- data/lib/aspera/rest/client.rb +28 -19
- data/lib/aspera/rest/list.rb +14 -8
- data/lib/aspera/rest/parameters.rb +2 -2
- data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +0 -20
- data/lib/aspera/schema/IBM Aspera Orchestrator API-v1.yaml +1784 -0
- data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +1230 -137
- data/lib/aspera/schema/IBM_Aspera_Shares.yaml +14 -13
- data/lib/aspera/schema/reader.rb +12 -18
- data/lib/aspera/schema/registry.rb +6 -1
- data.tar.gz.sig +0 -0
- metadata +5 -3
- metadata.gz.sig +0 -0
- data/lib/aspera/node_simulator.rb +0 -345
- data/lib/aspera/preview/options.rb +0 -45
|
@@ -24,7 +24,7 @@ using Rainbow
|
|
|
24
24
|
module Aspera
|
|
25
25
|
module Cli
|
|
26
26
|
module Plugins
|
|
27
|
-
class Aoc < Oauth
|
|
27
|
+
class Aoc < Oauth
|
|
28
28
|
# default redirect for AoC web auth
|
|
29
29
|
REDIRECT_LOCALHOST = 'http://localhost:12345'
|
|
30
30
|
# admin objects that can be manipulated
|
|
@@ -140,44 +140,46 @@ module Aspera
|
|
|
140
140
|
}
|
|
141
141
|
end
|
|
142
142
|
|
|
143
|
-
#
|
|
144
|
-
#
|
|
145
|
-
# @
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
143
|
+
# Optional `Hash` argument of short link create and modify, merged into the request body
|
|
144
|
+
# @param endpoint [String] endpoint of the request body schema, e.g. `short_links.post`
|
|
145
|
+
# @return [Hash] argument declaration
|
|
146
|
+
def short_link_argument(endpoint)
|
|
147
|
+
{
|
|
148
|
+
name: :short_link, type: Hash, mandatory: false, default: {},
|
|
149
|
+
description: 'Merged into the request body, and: `password` (link password), `access_levels` (shared folder only)',
|
|
150
|
+
schema: Schema::Registry.req_body(Schema::Registry::AOC, endpoint)
|
|
151
|
+
}
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# DSL class method: declare the 5 short link leaf commands under the given node.
|
|
155
|
+
# The node itself is declared by the caller, with its arguments and setup.
|
|
156
|
+
# @param parent [Symbol, Array<Symbol>] path relative to current scope, e.g. :short_link
|
|
157
|
+
def short_link_commands(parent)
|
|
158
|
+
commands_under(parent) do
|
|
159
|
+
command(
|
|
160
|
+
:create, description: operation_description(:create, 'short link'),
|
|
161
|
+
arguments: [short_link_argument('short_links.post')],
|
|
162
|
+
action: ->(short_link: {}, **ctx) { short_link_create(short_link, **ctx) }
|
|
152
163
|
)
|
|
153
|
-
|
|
154
|
-
:modify, description:
|
|
155
|
-
arguments: [{name: :short_link_id, type: :identifier}, {
|
|
164
|
+
command(
|
|
165
|
+
:modify, description: operation_description(:modify, 'short link'),
|
|
166
|
+
arguments: [{name: :short_link_id, type: :identifier}, short_link_argument('short_links/{id}.put')],
|
|
167
|
+
action: ->(short_link: {}, **ctx) { short_link_modify(short_link, **short_link_fetch_list(**ctx), **ctx) }
|
|
156
168
|
)
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
:
|
|
160
|
-
arguments: [{name: :short_link_id, type: :identifier}]
|
|
169
|
+
command(
|
|
170
|
+
:list, description: operation_description(:list, 'short link'), query_schema: SHORT_LINK_QUERY_SCHEMA,
|
|
171
|
+
action: ->(**ctx) { short_link_list(**short_link_fetch_list(**ctx)) }
|
|
161
172
|
)
|
|
162
|
-
|
|
163
|
-
:
|
|
164
|
-
arguments: [{name: :short_link_id, type: :identifier}]
|
|
173
|
+
command(
|
|
174
|
+
:show, description: operation_description(:show, 'short link'),
|
|
175
|
+
arguments: [{name: :short_link_id, type: :identifier}],
|
|
176
|
+
action: ->(**ctx) { short_link_show(**short_link_fetch_list(**ctx), **ctx) }
|
|
177
|
+
)
|
|
178
|
+
command(
|
|
179
|
+
:delete, description: operation_description(:delete, 'short link'),
|
|
180
|
+
arguments: [{name: :short_link_id, type: :identifier}],
|
|
181
|
+
action: ->(**ctx) { short_link_delete(**short_link_fetch_list(**ctx), **ctx) }
|
|
165
182
|
)
|
|
166
|
-
end
|
|
167
|
-
base.define_action_method(parent_path + [:create]) do |short_link: {}, **ctx|
|
|
168
|
-
sl_exec_create(short_link, **ctx)
|
|
169
|
-
end
|
|
170
|
-
base.define_action_method(parent_path + [:list]) do |**ctx|
|
|
171
|
-
sl_exec_list(**sl_fetch_list(**ctx))
|
|
172
|
-
end
|
|
173
|
-
base.define_action_method(parent_path + [:show]) do |**ctx|
|
|
174
|
-
sl_exec_show(**sl_fetch_list(**ctx))
|
|
175
|
-
end
|
|
176
|
-
base.define_action_method(parent_path + [:delete]) do |**ctx|
|
|
177
|
-
sl_exec_delete(**sl_fetch_list(**ctx), **ctx)
|
|
178
|
-
end
|
|
179
|
-
base.define_action_method(parent_path + [:modify]) do |short_link: {}, **ctx|
|
|
180
|
-
sl_exec_modify(short_link, **sl_fetch_list(**ctx), **ctx)
|
|
181
183
|
end
|
|
182
184
|
end
|
|
183
185
|
end
|
|
@@ -474,13 +476,10 @@ module Aspera
|
|
|
474
476
|
)))
|
|
475
477
|
end
|
|
476
478
|
|
|
477
|
-
# Execute an action on admin resources
|
|
478
|
-
# @param resource_type [Symbol] One of ADMIN_OBJECTS
|
|
479
479
|
# Per-resource configuration for admin CRUD resources.
|
|
480
480
|
# Keys: path, list_fields, id_result, require_ws_id, create_schema, extra_ops, singleton, op_setup, op_mount, op_descriptions
|
|
481
|
-
#
|
|
482
|
-
#
|
|
483
|
-
# For extra_ops that are instance ops, specify explicitly (or rely on the auto-generated one).
|
|
481
|
+
# Ops other than `create` and `list` read the identifier `<res>_id` (argument of the op node).
|
|
482
|
+
# op_setup: Hash of op => setup method name, for ops that need context derived from the identifier.
|
|
484
483
|
# op_mount: Hash of op => mount: of that op's node.
|
|
485
484
|
# op_descriptions: Hash of op => description, for ops other than standard ones.
|
|
486
485
|
ADMIN_OBJECT_CONFIG = {
|
|
@@ -515,7 +514,6 @@ module Aspera
|
|
|
515
514
|
user: {
|
|
516
515
|
list_fields: %w[id name email],
|
|
517
516
|
extra_ops: %i[preferences notifications],
|
|
518
|
-
op_setup: {preferences: :setup_admin_user_instance, notifications: :setup_admin_user_instance},
|
|
519
517
|
op_descriptions: {preferences: 'Manage user preferences', notifications: 'Manage user notification preferences'}
|
|
520
518
|
},
|
|
521
519
|
workspace: {
|
|
@@ -544,37 +542,24 @@ module Aspera
|
|
|
544
542
|
}))
|
|
545
543
|
end
|
|
546
544
|
|
|
547
|
-
#
|
|
548
|
-
|
|
549
|
-
#
|
|
550
|
-
|
|
551
|
-
def short_link_purposes(shared_data, link_type)
|
|
552
|
-
if shared_data.keys.sort == %i[dropbox_id name]
|
|
553
|
-
token_purpose = 'send_package_to_dropbox'
|
|
554
|
-
short_link_purpose = link_type.eql?(:public) ? 'send_package_to_dropbox' : 'shared_folder_auth_link'
|
|
555
|
-
elsif shared_data.keys.sort == %i[file_id node_id]
|
|
556
|
-
token_purpose = 'view_shared_file'
|
|
557
|
-
short_link_purpose = link_type.eql?(:public) ? 'token_auth_redirection' : 'shared_folder_auth_link'
|
|
558
|
-
else
|
|
559
|
-
Aspera.error_unexpected_value(shared_data.keys)
|
|
560
|
-
end
|
|
561
|
-
[token_purpose, short_link_purpose]
|
|
562
|
-
end
|
|
545
|
+
# Purpose of a short link, by link type (the purpose of its URL token depends on the shared resource)
|
|
546
|
+
SHORT_LINK_PURPOSES = {public: 'token_auth_redirection', private: 'shared_folder_auth_link'}.freeze
|
|
547
|
+
# Query parameters of short link list
|
|
548
|
+
SHORT_LINK_QUERY_SCHEMA = Schema::Registry.query_params(Schema::Registry::AOC, 'short_links')
|
|
563
549
|
|
|
564
550
|
# Build the list_params hash used by delete/list/show/modify short link operations.
|
|
565
551
|
# @return [Hash]
|
|
566
|
-
def short_link_list_params(shared_data:, link_type:, token_purpose:, short_link_purpose:, **)
|
|
552
|
+
def short_link_list_params(shared_data:, link_type:, token_purpose:, short_link_purpose:, **ctx)
|
|
567
553
|
query = if link_type.eql?(:private)
|
|
568
554
|
shared_data
|
|
569
555
|
else
|
|
570
556
|
{url_token_data: {data: shared_data, purpose: token_purpose}}
|
|
571
557
|
end
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
}
|
|
558
|
+
# String keys: the user's `--query` overrides them
|
|
559
|
+
list_params = {'json_query' => query.to_json, 'purpose' => short_link_purpose, 'sort' => '-created_at'}
|
|
560
|
+
# `edit_access` requires `file_id` and `node_id`: shared folder links only
|
|
561
|
+
list_params['edit_access'] = true if ctx.key?(:shared_apifid)
|
|
562
|
+
list_params
|
|
578
563
|
end
|
|
579
564
|
|
|
580
565
|
# @return [PersistencyActionOnce, nil] persistency object if option `once_only` is used.
|
|
@@ -926,22 +911,20 @@ module Aspera
|
|
|
926
911
|
arguments: [{name: :dropbox_id, type: :identifier, lookup: :lookup_aoc_dropbox_id}],
|
|
927
912
|
action: ->(dropbox_id:, **) { Result::SingleObject.new(aoc_api.read("dropboxes/#{dropbox_id}")) }
|
|
928
913
|
command :short_link, description: 'Manage shared inbox short links',
|
|
929
|
-
arguments: [{name: :link_type, allowed: %i[public
|
|
914
|
+
arguments: [{name: :link_type, allowed: %i[public]}, {name: :dropbox_id, type: :identifier, lookup: :lookup_aoc_dropbox_id}],
|
|
930
915
|
setup: :setup_packages_short_link
|
|
916
|
+
short_link_commands :short_link
|
|
931
917
|
end
|
|
932
|
-
# packages > shared_inboxes > short_link sub-commands
|
|
933
|
-
register_short_link_commands(self, %i[packages shared_inboxes short_link])
|
|
934
918
|
|
|
935
919
|
# files sub-commands: Gen4 commands are mounted, plus AoC-specific commands
|
|
936
920
|
commands_under :files do
|
|
937
921
|
command :short_link, description: 'Manage file short link',
|
|
938
922
|
arguments: [{name: :folder}, {name: :link_type, allowed: %i[public private]}],
|
|
939
923
|
setup: :setup_files_short_link
|
|
924
|
+
short_link_commands :short_link
|
|
940
925
|
command :transfer, description: 'Transfer files (node-to-node)', arguments: TRANSFER_ARGS,
|
|
941
926
|
action: ->(direction:, source_folder:, **) { nodegen4_transfer(aoc_api.home[:node_id], file_id: aoc_api.home[:file_id], scope: Api::Node::Scope::USER, direction: direction, source_folder: source_folder) }
|
|
942
927
|
end
|
|
943
|
-
# files > short_link sub-commands
|
|
944
|
-
register_short_link_commands(self, %i[files short_link])
|
|
945
928
|
|
|
946
929
|
# automation sub-commands
|
|
947
930
|
# Automation API: a workflow has ordered steps (step_order), a step has ordered actions (action_order)
|
|
@@ -1156,8 +1139,8 @@ module Aspera
|
|
|
1156
1139
|
end
|
|
1157
1140
|
|
|
1158
1141
|
# setup: files > short_link
|
|
1159
|
-
# Resolves the target folder,
|
|
1160
|
-
# @return [Hash] ctx keys:
|
|
1142
|
+
# Resolves the target folder, sets purposes.
|
|
1143
|
+
# @return [Hash] ctx keys: shared_data, token_purpose, short_link_purpose, shared_apifid
|
|
1161
1144
|
def setup_files_short_link(folder:, link_type:, **)
|
|
1162
1145
|
home_node_api = aoc_api.node_api_from(
|
|
1163
1146
|
node_id: aoc_api.home[:node_id],
|
|
@@ -1168,77 +1151,75 @@ module Aspera
|
|
|
1168
1151
|
node_id: shared_apifid.node_api.app_info.node_info['id'],
|
|
1169
1152
|
file_id: shared_apifid.file_id
|
|
1170
1153
|
}
|
|
1171
|
-
token_purpose, short_link_purpose = short_link_purposes(shared_data, link_type)
|
|
1172
|
-
perm_block = lambda do |op, id, access_levels|
|
|
1173
|
-
case op
|
|
1174
|
-
when :create
|
|
1175
|
-
perm_data = {
|
|
1176
|
-
'file_id' => shared_apifid.file_id,
|
|
1177
|
-
'access_id' => id,
|
|
1178
|
-
'access_type' => 'user',
|
|
1179
|
-
'access_levels' => Api::AoC.expand_access_levels(access_levels),
|
|
1180
|
-
'tags' => {
|
|
1181
|
-
'url_token' => true,
|
|
1182
|
-
'folder_name' => File.basename(folder),
|
|
1183
|
-
'created_by_name' => aoc_api.current_user_info['name'],
|
|
1184
|
-
'created_by_email' => aoc_api.current_user_info['email'],
|
|
1185
|
-
'access_key' => shared_apifid.node_api.app_info.node_info['access_key'],
|
|
1186
|
-
'node' => shared_apifid.node_api.app_info.node_info['name'],
|
|
1187
|
-
**workspace_id_hash(string: true, name: true)
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
created_data = shared_apifid.node_api.create('permissions', perm_data)
|
|
1191
|
-
aoc_api.permissions_send_event(event_data: created_data, app_info: shared_apifid.node_api.app_info)
|
|
1192
|
-
when :update
|
|
1193
|
-
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) }
|
|
1194
|
-
Aspera.assert(!found.nil?, type: Error) { "Short link not found: #{id}" }
|
|
1195
|
-
shared_apifid.node_api.update("permissions/#{found['id']}", {access_levels: Api::AoC.expand_access_levels(access_levels)})
|
|
1196
|
-
when :delete
|
|
1197
|
-
found = shared_apifid.node_api.read('permissions', {file_id: shared_apifid.file_id, inherited: false, access_type: 'user', access_id: id}).first
|
|
1198
|
-
Aspera.assert(!found.nil?, type: Error) { "Short link not found: #{id}" }
|
|
1199
|
-
shared_apifid.node_api.delete("permissions/#{found['id']}")
|
|
1200
|
-
else Aspera.error_unexpected_value(op)
|
|
1201
|
-
end
|
|
1202
|
-
end
|
|
1203
1154
|
{
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
sl_perm_block: perm_block
|
|
1155
|
+
shared_data: shared_data,
|
|
1156
|
+
token_purpose: 'view_shared_file',
|
|
1157
|
+
short_link_purpose: SHORT_LINK_PURPOSES[link_type],
|
|
1158
|
+
shared_apifid: shared_apifid
|
|
1209
1159
|
}
|
|
1210
1160
|
end
|
|
1211
1161
|
|
|
1162
|
+
# Node permission of a public short link on the shared folder (files > short_link only)
|
|
1163
|
+
# @param op [Symbol] :create, :update or :delete
|
|
1164
|
+
# @param id [String] Short link resource id: access_id of the permission
|
|
1165
|
+
# @param access_levels [String, Array, nil] Access levels for :create and :update
|
|
1166
|
+
def short_link_permission(op, id, access_levels, shared_apifid:, folder:, **)
|
|
1167
|
+
case op
|
|
1168
|
+
when :create
|
|
1169
|
+
perm_data = {
|
|
1170
|
+
'file_id' => shared_apifid.file_id,
|
|
1171
|
+
'access_id' => id,
|
|
1172
|
+
'access_type' => 'user',
|
|
1173
|
+
'access_levels' => Api::AoC.expand_access_levels(access_levels),
|
|
1174
|
+
'tags' => {
|
|
1175
|
+
'url_token' => true,
|
|
1176
|
+
'folder_name' => File.basename(folder),
|
|
1177
|
+
'created_by_name' => aoc_api.current_user_info['name'],
|
|
1178
|
+
'created_by_email' => aoc_api.current_user_info['email'],
|
|
1179
|
+
'access_key' => shared_apifid.node_api.app_info.node_info['access_key'],
|
|
1180
|
+
'node' => shared_apifid.node_api.app_info.node_info['name'],
|
|
1181
|
+
**workspace_id_hash(string: true, name: true)
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
created_data = shared_apifid.node_api.create('permissions', perm_data)
|
|
1185
|
+
aoc_api.permissions_send_event(event_data: created_data, app_info: shared_apifid.node_api.app_info)
|
|
1186
|
+
when :update
|
|
1187
|
+
found = shared_apifid.node_api.read('permissions', {file_id: shared_apifid.file_id, inherited: false, access_type: 'user', access_id: id}).find { |i| i['access_id'].eql?(id) }
|
|
1188
|
+
Aspera.assert(!found.nil?, type: Error) { "Short link not found: #{id}" }
|
|
1189
|
+
shared_apifid.node_api.update("permissions/#{found['id']}", {access_levels: Api::AoC.expand_access_levels(access_levels)})
|
|
1190
|
+
when :delete
|
|
1191
|
+
found = shared_apifid.node_api.read('permissions', {file_id: shared_apifid.file_id, inherited: false, access_type: 'user', access_id: id}).first
|
|
1192
|
+
Aspera.assert(!found.nil?, type: Error) { "Short link not found: #{id}" }
|
|
1193
|
+
shared_apifid.node_api.delete("permissions/#{found['id']}")
|
|
1194
|
+
else Aspera.error_unexpected_value(op)
|
|
1195
|
+
end
|
|
1196
|
+
end
|
|
1197
|
+
|
|
1212
1198
|
# setup: packages > shared_inboxes > short_link
|
|
1213
|
-
# link_type: and dropbox_id: resolved via arguments: on the node,
|
|
1214
|
-
# @return [Hash] ctx keys:
|
|
1199
|
+
# link_type: and dropbox_id: resolved via arguments: on the node, sets purposes.
|
|
1200
|
+
# @return [Hash] ctx keys: shared_data, token_purpose, short_link_purpose
|
|
1215
1201
|
def setup_packages_short_link(link_type:, dropbox_id:, **)
|
|
1216
|
-
shared_data = {dropbox_id: dropbox_id, name: ''}
|
|
1217
|
-
token_purpose, short_link_purpose = short_link_purposes(shared_data, link_type)
|
|
1218
1202
|
{
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
sl_short_link_purpose: short_link_purpose,
|
|
1223
|
-
sl_perm_block: nil
|
|
1203
|
+
shared_data: {dropbox_id: dropbox_id, name: ''},
|
|
1204
|
+
token_purpose: 'send_package_to_dropbox',
|
|
1205
|
+
short_link_purpose: SHORT_LINK_PURPOSES[link_type]
|
|
1224
1206
|
}
|
|
1225
1207
|
end
|
|
1226
1208
|
|
|
1227
1209
|
# Shared implementation for short_link > create
|
|
1228
|
-
def
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
case sl_link_type
|
|
1210
|
+
def short_link_create(custom_data = {}, shared_data:, link_type:, token_purpose:, short_link_purpose:, **ctx)
|
|
1211
|
+
ws_shared_data = workspace_id_hash(shared_data.dup)
|
|
1212
|
+
create_payload = {purpose: short_link_purpose, user_selected_name: nil}
|
|
1213
|
+
case link_type
|
|
1233
1214
|
when :private
|
|
1234
|
-
create_payload[:data] =
|
|
1215
|
+
create_payload[:data] = ws_shared_data
|
|
1235
1216
|
when :public
|
|
1236
1217
|
create_payload[:expires_at] = nil
|
|
1237
1218
|
create_payload[:password_enabled] = false
|
|
1238
|
-
|
|
1219
|
+
ws_shared_data[:name] = ''
|
|
1239
1220
|
create_payload[:data] = {
|
|
1240
1221
|
aoc: true,
|
|
1241
|
-
url_token_data: {data:
|
|
1222
|
+
url_token_data: {data: ws_shared_data, purpose: token_purpose}
|
|
1242
1223
|
}
|
|
1243
1224
|
end
|
|
1244
1225
|
custom_data = custom_data.dup
|
|
@@ -1249,66 +1230,65 @@ module Aspera
|
|
|
1249
1230
|
end
|
|
1250
1231
|
create_payload.deep_merge!(custom_data)
|
|
1251
1232
|
result_create_short_link = aoc_api.create('short_links', create_payload)
|
|
1252
|
-
|
|
1233
|
+
short_link_permission(:create, result_create_short_link['resource_id'], access_levels, **ctx) if link_type.eql?(:public) && ctx.key?(:shared_apifid)
|
|
1253
1234
|
Result::SingleObject.new(result_create_short_link)
|
|
1254
1235
|
end
|
|
1255
1236
|
|
|
1256
1237
|
# Shared implementation for short_link > delete|list|show|modify: fetch the short_list
|
|
1257
|
-
def
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
list_params = short_link_list_params(
|
|
1261
|
-
shared_data: shared_data, link_type: sl_link_type,
|
|
1262
|
-
token_purpose: sl_token_purpose, short_link_purpose: sl_short_link_purpose
|
|
1263
|
-
)
|
|
1238
|
+
def short_link_fetch_list(shared_data:, **ctx)
|
|
1239
|
+
ws_shared_data = workspace_id_hash(shared_data.dup)
|
|
1240
|
+
list_params = short_link_list_params(shared_data: ws_shared_data, **ctx)
|
|
1264
1241
|
{
|
|
1265
|
-
|
|
1266
|
-
|
|
1242
|
+
short_list: aoc_api.read_with_paging('short_links', list_params.merge(query_read_delete(default: {}, schema: SHORT_LINK_QUERY_SCHEMA)).compact),
|
|
1243
|
+
ws_shared_data: ws_shared_data
|
|
1267
1244
|
}
|
|
1268
1245
|
end
|
|
1269
1246
|
|
|
1270
1247
|
# Shared implementation for short_link > delete
|
|
1271
|
-
def
|
|
1248
|
+
def short_link_delete(ws_shared_data:, short_list:, link_type:, short_link_id: nil, **ctx)
|
|
1272
1249
|
one_id = short_link_id
|
|
1273
|
-
if
|
|
1274
|
-
found =
|
|
1250
|
+
if link_type.eql?(:public)
|
|
1251
|
+
found = short_list[:items].find { |item| item['id'].eql?(one_id) }
|
|
1275
1252
|
raise BadIdentifier.new('Short link', one_id) if found.nil?
|
|
1276
|
-
|
|
1253
|
+
short_link_permission(:delete, found['resource_id'], nil, **ctx) if ctx.key?(:shared_apifid)
|
|
1277
1254
|
end
|
|
1278
|
-
|
|
1255
|
+
# `edit_access` requires `file_id` and `node_id`: shared folder links only
|
|
1256
|
+
aoc_api.delete("short_links/#{one_id}", ({edit_access: true, json_query: ws_shared_data.to_json} if ctx.key?(:shared_apifid)))
|
|
1279
1257
|
Result::Status.new('deleted')
|
|
1280
1258
|
end
|
|
1281
1259
|
|
|
1282
1260
|
# Shared implementation for short_link > list
|
|
1283
|
-
def
|
|
1284
|
-
Result::ObjectList.new(
|
|
1261
|
+
def short_link_list(short_list:, **)
|
|
1262
|
+
Result::ObjectList.new(short_list[:items], fields: Formatter.all_but('data'), total: short_list[:total])
|
|
1285
1263
|
end
|
|
1286
1264
|
|
|
1287
1265
|
# Shared implementation for short_link > show
|
|
1288
|
-
def
|
|
1266
|
+
def short_link_show(short_list:, short_link_id: nil, **)
|
|
1289
1267
|
one_id = short_link_id
|
|
1290
|
-
found =
|
|
1268
|
+
found = short_list[:items].find { |item| item['id'].eql?(one_id) }
|
|
1291
1269
|
raise BadIdentifier.new('Short link', one_id) if found.nil?
|
|
1292
1270
|
Result::SingleObject.new(found, fields: Formatter.all_but('data'))
|
|
1293
1271
|
end
|
|
1294
1272
|
|
|
1295
1273
|
# Shared implementation for short_link > modify
|
|
1296
|
-
def
|
|
1297
|
-
Aspera.assert_values(
|
|
1274
|
+
def short_link_modify(custom_data = {}, shared_data:, short_list:, link_type:, short_link_id: nil, **ctx)
|
|
1275
|
+
Aspera.assert_values(link_type, [:public], type: Cli::BadArgument) { 'link_type' }
|
|
1298
1276
|
one_id = short_link_id
|
|
1299
|
-
|
|
1300
|
-
|
|
1277
|
+
# Identifies the shared resource: `node_id` and `file_id`, or `dropbox_id`
|
|
1278
|
+
token_data = shared_data.except(:name)
|
|
1279
|
+
# `edit_access` requires `file_id` and `node_id`: shared folder links only
|
|
1280
|
+
modify_payload = ctx.key?(:shared_apifid) ? {edit_access: true, json_query: token_data} : {}
|
|
1301
1281
|
custom_data = custom_data.dup
|
|
1302
1282
|
if (pass = custom_data.delete('password'))
|
|
1303
1283
|
modify_payload[:password_enabled] = true
|
|
1304
|
-
modify_payload[:data] = {url_token_data: {password: pass, data:
|
|
1284
|
+
modify_payload[:data] = {url_token_data: {password: pass, data: token_data}}
|
|
1305
1285
|
else
|
|
1306
1286
|
modify_payload[:password_enabled] = false
|
|
1307
1287
|
end
|
|
1308
|
-
if custom_data.delete('access_levels')
|
|
1309
|
-
found =
|
|
1288
|
+
if (access_levels = custom_data.delete('access_levels'))
|
|
1289
|
+
found = short_list[:items].find { |item| item['id'].eql?(one_id) }
|
|
1310
1290
|
raise BadIdentifier.new('Short link', one_id) if found.nil?
|
|
1311
|
-
|
|
1291
|
+
short_link_permission(:update, found['resource_id'], access_levels, **ctx) if ctx.key?(:shared_apifid)
|
|
1312
1292
|
end
|
|
1313
1293
|
modify_payload.deep_merge!(custom_data)
|
|
1314
1294
|
aoc_api.update("short_links/#{one_id}", modify_payload)
|
|
@@ -1544,9 +1524,6 @@ module Aspera
|
|
|
1544
1524
|
Ats.new(context: context, api: ats_api)
|
|
1545
1525
|
end
|
|
1546
1526
|
|
|
1547
|
-
# admin > node > do | bearer_token — setup reuses the generic instance setup
|
|
1548
|
-
# (setup_admin_node_instance is auto-generated above, providing res_id:)
|
|
1549
|
-
|
|
1550
1527
|
# admin > node > do - mount target: Gen4 commands on the node, admin scope
|
|
1551
1528
|
def admin_node_do_plugin(node_id:, **)
|
|
1552
1529
|
nodegen4_plugin(node_id, scope: Api::Node::Scope::ADMIN)
|
|
@@ -1617,7 +1594,7 @@ module Aspera
|
|
|
1617
1594
|
end
|
|
1618
1595
|
|
|
1619
1596
|
# admin > user > preferences|notifications > show|modify
|
|
1620
|
-
# (
|
|
1597
|
+
# (user_id: argument of the preferences|notifications node)
|
|
1621
1598
|
%i[preferences notifications].each do |pref|
|
|
1622
1599
|
pref_path = pref.eql?(:preferences) ? 'user_interaction_preferences' : 'notification_preferences'
|
|
1623
1600
|
define_action_method([:admin, :user, pref, :show]) do |user_id:, **|
|
|
@@ -229,12 +229,6 @@ module Aspera
|
|
|
229
229
|
[Node.new(context: context, api: api_node), {do_root_file_id: ak_data['root_file_id']}]
|
|
230
230
|
end
|
|
231
231
|
|
|
232
|
-
def action_api_key_instances
|
|
233
|
-
instances = ats_api_v2_auth_ibm.read('instances')
|
|
234
|
-
Log.log.warn { "more instances remaining: #{instances['remaining']}" } unless instances['remaining'].to_i.eql?(0)
|
|
235
|
-
Result::ValueList.new(instances['data'], name: 'instance')
|
|
236
|
-
end
|
|
237
|
-
|
|
238
232
|
def action_access_key_cluster(access_key_id:, **)
|
|
239
233
|
ats_url = ats_api.base_url
|
|
240
234
|
api_ak_auth = Rest::Client.new(
|
|
@@ -255,7 +249,7 @@ module Aspera
|
|
|
255
249
|
def build_ats_ibm_api_with_instance
|
|
256
250
|
instance = options.get_option(:instance)
|
|
257
251
|
if instance.nil?
|
|
258
|
-
instance = ats_api_v2_auth_ibm.read('instances')['data']
|
|
252
|
+
instance = ats_api_v2_auth_ibm.read('instances')['data']&.first
|
|
259
253
|
formatter.display_status("using first instance: #{instance}")
|
|
260
254
|
end
|
|
261
255
|
ats_api_v2_auth_ibm({'X-ATS-Service-Instance-Id' => instance})
|
|
@@ -6,6 +6,7 @@ require 'aspera/assert'
|
|
|
6
6
|
require 'aspera/cli/result'
|
|
7
7
|
require 'aspera/cli/command_registry'
|
|
8
8
|
require 'aspera/cli/option_declarator'
|
|
9
|
+
require 'aspera/cli/special_values'
|
|
9
10
|
require 'aspera/schema/registry'
|
|
10
11
|
|
|
11
12
|
module Aspera
|
|
@@ -26,16 +27,18 @@ module Aspera
|
|
|
26
27
|
class << self
|
|
27
28
|
include OptionDeclarator
|
|
28
29
|
|
|
29
|
-
# Option sources added with `use_options
|
|
30
|
-
# @return [
|
|
30
|
+
# Option sources added with `use_options`, with their `on_set` target.
|
|
31
|
+
# @return [Hash{Class, Module => Proc, nil}]
|
|
31
32
|
def used_option_sources
|
|
32
|
-
@used_option_sources ||=
|
|
33
|
+
@used_option_sources ||= {}
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
# Include options from another plugin or OptionDeclarator module.
|
|
36
37
|
# @param source [Class, Module]
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
# @param target [Proc, nil] Executed on the plugin instance, returns the object for Symbol and Proc `on_set` callbacks
|
|
39
|
+
# of `source` (e.g. `-> { http_config }`); nil: the plugin instance
|
|
40
|
+
def use_options(source, target: nil)
|
|
41
|
+
used_option_sources[source] = target unless used_option_sources.key?(source)
|
|
39
42
|
end
|
|
40
43
|
|
|
41
44
|
# Per-class DSL registry (not inherited: each subclass gets its own instance).
|
|
@@ -218,15 +221,15 @@ module Aspera
|
|
|
218
221
|
|
|
219
222
|
# Classes and modules whose options apply to this plugin:
|
|
220
223
|
# this class, its plugin ancestors and sources added via `use_options`.
|
|
221
|
-
# @return [
|
|
224
|
+
# @return [Hash{Class, Module => Proc, nil}] key responds to `option_specs`, value: `on_set` target (see `use_options`)
|
|
222
225
|
def option_sources
|
|
223
|
-
sources =
|
|
226
|
+
sources = {}
|
|
224
227
|
ancestors.each do |klass|
|
|
225
228
|
next unless klass.is_a?(Class) && klass <= Base
|
|
226
|
-
sources
|
|
227
|
-
sources.
|
|
229
|
+
sources[klass] = nil if klass.instance_variable_defined?(:@command_registry) && !sources.key?(klass)
|
|
230
|
+
klass.used_option_sources.each { |src, src_target| sources[src] = src_target unless sources.key?(src) }
|
|
228
231
|
end
|
|
229
|
-
sources
|
|
232
|
+
sources
|
|
230
233
|
end
|
|
231
234
|
|
|
232
235
|
# Declare all options of `option_sources` onto a Parser instance.
|
|
@@ -235,9 +238,10 @@ module Aspera
|
|
|
235
238
|
# @param target [Base, nil] plugin instance for Symbol and Proc `on_set` callbacks; nil: such callbacks are not bound
|
|
236
239
|
# @param parse [Boolean] whether to call parse_options! after declaring
|
|
237
240
|
def declare_options(options, target: nil, parse: false)
|
|
238
|
-
option_sources.each do |src|
|
|
241
|
+
option_sources.each do |src, src_target|
|
|
242
|
+
src_target = src_target.nil? ? target : target&.instance_exec(&src_target)
|
|
239
243
|
src.option_specs.each_value do |spec|
|
|
240
|
-
spec.declare_on(options, target:
|
|
244
|
+
spec.declare_on(options, target: src_target) unless options.option_declared?(spec.name)
|
|
241
245
|
end
|
|
242
246
|
end
|
|
243
247
|
options.parse_options! if parse
|
|
@@ -312,11 +316,6 @@ module Aspera
|
|
|
312
316
|
# @return [Aspera::Cli::TransferProgress, nil]
|
|
313
317
|
def progress_bar; @context.progress_bar; end
|
|
314
318
|
|
|
315
|
-
def add_manual_header(_has_options = true)
|
|
316
|
-
# No-op: the group is set at the start of initialize.
|
|
317
|
-
# Kept for compatibility with Config, which calls add_manual_header(false) from Runner.
|
|
318
|
-
end
|
|
319
|
-
|
|
320
319
|
# Entry point for all DSL-based plugins.
|
|
321
320
|
def execute_action
|
|
322
321
|
@help_path = nil
|
|
@@ -390,7 +389,8 @@ module Aspera
|
|
|
390
389
|
# (mounted children are only walked here for --help, see below).
|
|
391
390
|
# With --help, conditions are not evaluated: they may need the API, which is not built for help.
|
|
392
391
|
available = children.reject { |id, c| c.condition && !@context.help_requested && registry.local?(current_path + [id]) && !send(c.condition) }
|
|
393
|
-
|
|
392
|
+
# Aliases of available commands only: an alias must not bypass the condition of its command
|
|
393
|
+
aliases = available.values.each_with_object({}) do |c, h|
|
|
394
394
|
Array(c.aliases).each { |a| h[a] = c.id } if c.aliases
|
|
395
395
|
end
|
|
396
396
|
|
|
@@ -485,14 +485,19 @@ module Aspera
|
|
|
485
485
|
end
|
|
486
486
|
|
|
487
487
|
# Resolve positional arguments from the CLI argument stream, in order.
|
|
488
|
-
# Arguments already present in `ctx` are not read
|
|
488
|
+
# Arguments already present in `ctx` (e.g. injected by a setup method) are not read from the command line.
|
|
489
|
+
# If the next CLI argument is that same value, or `ALL`, it is skipped, so that it is not mistaken for a later argument
|
|
490
|
+
# (e.g. `faspex5 packages receive ALL` with a public link to a package).
|
|
489
491
|
# For type: :identifier, the percent-selector lookup receives the ctx accumulated so far.
|
|
490
492
|
# @param arg_specs [Array<ArgumentSpec>]
|
|
491
493
|
# @param ctx [Hash] accumulated context
|
|
492
494
|
# @return [Hash] ctx merged with the resolved arguments
|
|
493
495
|
def resolve_arguments(arg_specs, ctx)
|
|
494
496
|
arg_specs.each do |arg_spec|
|
|
495
|
-
|
|
497
|
+
if ctx.key?(arg_spec.name)
|
|
498
|
+
options.get_next_argument(arg_spec.name.to_s, mandatory: false) if options.next_argument_in?([ctx[arg_spec.name].to_s, SpecialValues::ALL])
|
|
499
|
+
next
|
|
500
|
+
end
|
|
496
501
|
lookup_cb = arg_spec.lookup if arg_spec.type.eql?(:identifier)
|
|
497
502
|
current = ctx
|
|
498
503
|
block =
|
|
@@ -508,6 +513,7 @@ module Aspera
|
|
|
508
513
|
|
|
509
514
|
# Resolve a single positional argument from the CLI argument stream.
|
|
510
515
|
# When arg_spec.bulk is true, always returns an Array (normalized to [value] when non-bulk).
|
|
516
|
+
# Only such arguments are read as a list with `--bulk=yes`: others (e.g. the id of a parent node) are single values.
|
|
511
517
|
# For type: :identifier, an optional block provides the percent-selector lookup.
|
|
512
518
|
# @param arg_spec [ArgumentSpec]
|
|
513
519
|
# @yieldparam field [String] field name from a percent-selector (%field:value)
|
|
@@ -518,7 +524,7 @@ module Aspera
|
|
|
518
524
|
if arg_spec.bulk
|
|
519
525
|
is_bulk = options.get_option(:bulk)
|
|
520
526
|
if arg_spec.type.eql?(:identifier)
|
|
521
|
-
val = options.instance_identifier(description: arg_spec.name.to_s, &block)
|
|
527
|
+
val = options.instance_identifier(description: arg_spec.name.to_s, multiple: is_bulk, &block)
|
|
522
528
|
else
|
|
523
529
|
val = options.get_next_argument(
|
|
524
530
|
arg_spec.name.to_s,
|
|
@@ -539,19 +545,19 @@ module Aspera
|
|
|
539
545
|
options.instance_identifier(description: arg_spec.name.to_s, &block)
|
|
540
546
|
else
|
|
541
547
|
# Class or Array<Class> -> pass as validation type
|
|
542
|
-
# When interactive: true,
|
|
543
|
-
#
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
548
|
+
# When interactive: true, prompt for this argument only when no CLI argument is provided
|
|
549
|
+
# (mandatory is forced to true and default ignored: a non-nil default would short-circuit the prompt).
|
|
550
|
+
options.with_interactive(enabled: arg_spec.interactive) do
|
|
551
|
+
options.get_next_argument(
|
|
552
|
+
arg_spec.name.to_s,
|
|
553
|
+
mandatory: arg_spec.interactive ? true : arg_spec.mandatory,
|
|
554
|
+
multiple: arg_spec.multiple || false,
|
|
555
|
+
validation: arg_spec.type,
|
|
556
|
+
accept_list: arg_spec.allowed,
|
|
557
|
+
default: arg_spec.interactive ? nil : arg_spec.default,
|
|
558
|
+
schema: arg_spec.schema
|
|
559
|
+
)
|
|
560
|
+
end
|
|
555
561
|
end
|
|
556
562
|
end
|
|
557
563
|
end
|