aspera-cli 4.26.0 → 4.26.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 +17 -3
- data/lib/aspera/api/aoc.rb +2 -1
- data/lib/aspera/api/node.rb +2 -2
- data/lib/aspera/ascp/installation.rb +7 -4
- data/lib/aspera/assert.rb +17 -13
- data/lib/aspera/cli/extended_value.rb +6 -2
- data/lib/aspera/cli/formatter.rb +65 -60
- data/lib/aspera/cli/main.rb +69 -10
- data/lib/aspera/cli/manager.rb +130 -76
- data/lib/aspera/cli/options.schema.yaml +82 -0
- data/lib/aspera/cli/plugins/aoc.rb +36 -11
- data/lib/aspera/cli/plugins/base.rb +46 -37
- data/lib/aspera/cli/plugins/config.rb +9 -9
- data/lib/aspera/cli/plugins/faspex.rb +1 -1
- data/lib/aspera/cli/plugins/faspex5.rb +4 -5
- data/lib/aspera/cli/plugins/node.rb +1 -1
- data/lib/aspera/cli/sync_actions.rb +1 -1
- data/lib/aspera/cli/transfer_agent.rb +17 -15
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/command_line_builder.rb +22 -18
- data/lib/aspera/environment.rb +3 -3
- data/lib/aspera/formatter_interface.rb +14 -0
- data/lib/aspera/hash_ext.rb +6 -0
- data/lib/aspera/log.rb +4 -3
- data/lib/aspera/markdown.rb +4 -1
- data/lib/aspera/oauth/factory.rb +1 -1
- data/lib/aspera/proxy_auto_config.rb +3 -0
- data/lib/aspera/rest.rb +1 -1
- data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +62801 -0
- data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +8898 -0
- data/lib/aspera/schema/documentation.rb +107 -0
- data/lib/aspera/schema/reader.rb +75 -0
- data/lib/aspera/schema/registry.rb +63 -0
- data/lib/aspera/sync/conf.schema.yaml +0 -26
- data/lib/aspera/sync/operations.rb +9 -5
- data/lib/aspera/transfer/faux_file.rb +1 -1
- data/lib/aspera/transfer/resumer.rb +1 -1
- data/lib/aspera/transfer/spec.rb +3 -3
- data/lib/aspera/transfer/spec.schema.yaml +1 -1
- data/lib/aspera/uri_reader.rb +1 -1
- data/lib/aspera/yaml.rb +4 -2
- data.tar.gz.sig +0 -0
- metadata +9 -3
- metadata.gz.sig +0 -0
- data/lib/aspera/transfer/spec_doc.rb +0 -76
|
@@ -304,7 +304,7 @@ module Aspera
|
|
|
304
304
|
# @param fields fields to display
|
|
305
305
|
# @param base_query a query applied always
|
|
306
306
|
# @param default_query default query unless overridden by user
|
|
307
|
-
# @
|
|
307
|
+
# @yieldparam query [Hash] The user's or default query for modification
|
|
308
308
|
def result_list(resource_class_path, fields: nil, base_query: {}, default_query: {})
|
|
309
309
|
Aspera.assert_type(base_query, Hash)
|
|
310
310
|
Aspera.assert_type(default_query, Hash)
|
|
@@ -402,34 +402,50 @@ module Aspera
|
|
|
402
402
|
# Execute an action on admin resources
|
|
403
403
|
# @param resource_type [Symbol] One of ADMIN_OBJECTS
|
|
404
404
|
def execute_resource_action(resource_type)
|
|
405
|
+
# Set to `true` is resource creation requires a workspace id
|
|
405
406
|
require_workspace_id = false
|
|
407
|
+
# Default fields to display
|
|
406
408
|
list_default_fields = %w[id name]
|
|
409
|
+
# Default query for `list` action
|
|
407
410
|
list_default_query = {}
|
|
411
|
+
# In result of create, what is the field that contains the id of the created resource
|
|
412
|
+
id_result = 'id'
|
|
408
413
|
supported_operations = Operations::ALL
|
|
414
|
+
# API path
|
|
409
415
|
resource_class_path = "#{resource_type}s"
|
|
416
|
+
# path in openapi where post is located to get creation schema
|
|
417
|
+
create_schema_path = resource_class_path
|
|
410
418
|
case resource_type
|
|
411
419
|
when :client
|
|
412
420
|
supported_operations += %i[set_pub_key]
|
|
421
|
+
# schema_create_modify = Schema::Registry.req_body(Schema::Registry::AOC, "#{resource_class_path}.post")
|
|
413
422
|
when :client_access_key
|
|
414
423
|
resource_class_path = "admin/#{resource_type}s"
|
|
415
424
|
when :client_registration_token
|
|
416
425
|
resource_class_path = "admin/#{resource_type}s"
|
|
417
|
-
list_default_fields = %w[id value data.client_subject_scopes created_at]
|
|
426
|
+
list_default_fields = %w[id value data.client_subject_scopes data.name created_at]
|
|
427
|
+
id_result = 'token'
|
|
418
428
|
when :contact
|
|
419
429
|
list_default_fields = %w[source_type source_id name email]
|
|
420
430
|
# list_default_query = {'include_only_user_personal_contacts' => true} if @scope == Api::AoC::Scope::USER
|
|
421
431
|
when :dropbox
|
|
422
432
|
require_workspace_id = true
|
|
423
433
|
resource_class_path = "#{resource_type}es"
|
|
434
|
+
create_schema_path = resource_class_path
|
|
435
|
+
when :group, :saml_configuration
|
|
436
|
+
create_schema_path = nil
|
|
424
437
|
when :group_membership
|
|
425
438
|
list_default_fields = %w[id group_id member_type member_id]
|
|
439
|
+
create_schema_path = nil
|
|
426
440
|
when :kms_profile
|
|
427
441
|
resource_class_path = "integrations/#{resource_type}s"
|
|
442
|
+
create_schema_path = nil
|
|
428
443
|
when :node
|
|
429
444
|
list_default_fields = %w[id name host access_key]
|
|
430
445
|
supported_operations += %i[do bearer_token]
|
|
431
446
|
when :operation
|
|
432
|
-
list_default_fields =
|
|
447
|
+
list_default_fields = %w[id type status created_at updated_at workspace_id user_id workspace_membership_id group_membership_id]
|
|
448
|
+
supported_operations = %i[list show modify]
|
|
433
449
|
when :organization, :self
|
|
434
450
|
supported_operations = Operations::SINGLETON
|
|
435
451
|
resource_instance_path = resource_class_path = resource_type
|
|
@@ -443,6 +459,8 @@ module Aspera
|
|
|
443
459
|
when :workspace_membership
|
|
444
460
|
list_default_fields = %w[id workspace_id member_type member_id]
|
|
445
461
|
end
|
|
462
|
+
# Default location of creation payload in schema
|
|
463
|
+
schema_create_modify = Schema::Registry.req_body(Schema::Registry::AOC, "#{create_schema_path}.post") if create_schema_path && supported_operations.include?(:create)
|
|
446
464
|
command = options.get_next_command(supported_operations)
|
|
447
465
|
# Require identifier for non global commands
|
|
448
466
|
if (supported_operations != Operations::SINGLETON) && !Operations::GLOBAL.include?(command)
|
|
@@ -451,12 +469,10 @@ module Aspera
|
|
|
451
469
|
end
|
|
452
470
|
case command
|
|
453
471
|
when :create
|
|
454
|
-
id_result = 'id'
|
|
455
|
-
id_result = 'token' if resource_class_path.eql?('admin/client_registration_tokens')
|
|
456
472
|
# TODO: report inconsistency: creation url is !=, and does not return id.
|
|
457
473
|
resource_class_path = 'admin/client_registration/token' if resource_class_path.eql?('admin/client_registration_tokens')
|
|
458
474
|
workspace_id = aoc_api.workspace_info[:id] if require_workspace_id
|
|
459
|
-
return do_bulk_operation(command: command, descr: 'creation data', id_result: id_result) do |params|
|
|
475
|
+
return do_bulk_operation(command: command, descr: 'creation data', id_result: id_result, schema: schema_create_modify) do |params|
|
|
460
476
|
params['workspace_id'] = workspace_id if require_workspace_id && workspace_id && !params.key?('workspace_id')
|
|
461
477
|
aoc_api.create(resource_class_path, params)
|
|
462
478
|
end
|
|
@@ -466,7 +482,7 @@ module Aspera
|
|
|
466
482
|
object = aoc_api.read(resource_instance_path, query_read_delete)
|
|
467
483
|
return Main.result_single_object(object, fields: Formatter.all_but('certificate'))
|
|
468
484
|
when :modify
|
|
469
|
-
changes = options.get_next_argument('properties', validation: Hash)
|
|
485
|
+
changes = options.get_next_argument('properties', validation: Hash, schema: schema_create_modify)
|
|
470
486
|
return do_bulk_operation(command: command, values: res_id) do |one_id|
|
|
471
487
|
aoc_api.update("#{resource_class_path}/#{one_id}", changes)
|
|
472
488
|
{'id' => one_id}
|
|
@@ -503,12 +519,18 @@ module Aspera
|
|
|
503
519
|
query = options.get_option(:query) || Api::AoC.workspace_access(res_id).merge({'admin' => true})
|
|
504
520
|
shared_folders = aoc_api.read_with_paging("#{resource_instance_path}/permissions", query)[:items]
|
|
505
521
|
# inside a workspace
|
|
506
|
-
command_shared = options.get_next_command(%i[list member])
|
|
522
|
+
command_shared = options.get_next_command(%i[list node member])
|
|
507
523
|
case command_shared
|
|
508
524
|
when :list
|
|
509
525
|
return Main.result_object_list(shared_folders, fields: %w[id node_name node_id file_id file.path tags.aspera.files.workspace.share_as])
|
|
526
|
+
when :node
|
|
527
|
+
shared_folder_id = options.instance_identifier(description: 'Shared folder ID')
|
|
528
|
+
shared_folder = shared_folders.find{ |i| i['id'].eql?(shared_folder_id)}
|
|
529
|
+
Aspera.assert(shared_folder)
|
|
530
|
+
command_repo = options.get_next_command(FILES_COMMANDS)
|
|
531
|
+
return execute_nodegen4_command(command_repo, shared_folder['node_id'], file_id: shared_folder['file_id'], scope: Api::Node::Scope::ADMIN)
|
|
510
532
|
when :member
|
|
511
|
-
shared_folder_id = options.instance_identifier
|
|
533
|
+
shared_folder_id = options.instance_identifier(description: 'Shared folder ID')
|
|
512
534
|
shared_folder = shared_folders.find{ |i| i['id'].eql?(shared_folder_id)}
|
|
513
535
|
Aspera.assert(shared_folder)
|
|
514
536
|
command_shared_member = options.get_next_command(%i[list])
|
|
@@ -825,7 +847,10 @@ module Aspera
|
|
|
825
847
|
# - a resource id, e.g. `scQ7uXPbvQ`
|
|
826
848
|
# - a short URL path, e.g. `dxyRpT9`
|
|
827
849
|
# @param shared_data [Hash] Information for shared data: dropbox_id+name or file_id+node_id
|
|
828
|
-
# @param
|
|
850
|
+
# @param shared_data [Hash] Shared data for the short link
|
|
851
|
+
# @yieldparam operation [Symbol] Operation type (:create, :update, :delete)
|
|
852
|
+
# @yieldparam resource_id [String] Resource ID for permission management
|
|
853
|
+
# @yieldparam access_levels [Object] Access levels for permissions
|
|
829
854
|
def short_link_command(**shared_data, &perm_block)
|
|
830
855
|
link_type = options.get_next_argument('link access (public or private)', accept_list: %i[public private])
|
|
831
856
|
if shared_data.keys.sort == %i[dropbox_id name]
|
|
@@ -1043,7 +1068,7 @@ module Aspera
|
|
|
1043
1068
|
return short_link_command(dropbox_id: get_resource_id_from_args('dropboxes'), name: '')
|
|
1044
1069
|
end
|
|
1045
1070
|
when :send
|
|
1046
|
-
package_data = value_create_modify(command: package_command)
|
|
1071
|
+
package_data = value_create_modify(command: package_command, schema: Schema::Registry.req_body(Schema::Registry::AOC, 'packages.post'))
|
|
1047
1072
|
new_user_option = options.get_option(:new_user_option)
|
|
1048
1073
|
option_validate = options.get_option(:validate_metadata)
|
|
1049
1074
|
# Works for both normal user auth and link auth.
|
|
@@ -37,15 +37,15 @@ module Aspera
|
|
|
37
37
|
# Global objects
|
|
38
38
|
attr_reader :context
|
|
39
39
|
|
|
40
|
-
# @return [Manager]
|
|
40
|
+
# @return [Aspera::Cli::Manager]
|
|
41
41
|
def options; @context.options; end
|
|
42
|
-
# @return [TransferAgent]
|
|
42
|
+
# @return [Aspera::Cli::TransferAgent]
|
|
43
43
|
def transfer; @context.transfer; end
|
|
44
|
-
# @return [Config]
|
|
44
|
+
# @return [Aspera::Cli::Plugins::Config]
|
|
45
45
|
def config; @context.config; end
|
|
46
|
-
# @return [Formatter]
|
|
46
|
+
# @return [Aspera::Cli::Formatter]
|
|
47
47
|
def formatter; @context.formatter; end
|
|
48
|
-
# @return [PersistencyFolder]
|
|
48
|
+
# @return [Aspera::PersistencyFolder]
|
|
49
49
|
def persistency; @context.persistency; end
|
|
50
50
|
|
|
51
51
|
def add_manual_header(has_options = true)
|
|
@@ -57,20 +57,23 @@ module Aspera
|
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
# For create and delete operations: execute one action or multiple if bulk is yes
|
|
60
|
-
# @param command
|
|
61
|
-
# @param descr
|
|
62
|
-
# @param values
|
|
63
|
-
# @param id_result [String] Key in result
|
|
64
|
-
# @param fields
|
|
65
|
-
# @param
|
|
66
|
-
|
|
60
|
+
# @param command [Symbol] Operation: :create, :delete, ...
|
|
61
|
+
# @param descr [String, nil] Description of the value
|
|
62
|
+
# @param values [Class, Array, Symbol] Type, or list of values, or :identifier, result is given to the block in loop
|
|
63
|
+
# @param id_result [String] Key in result Hash to use as identifier
|
|
64
|
+
# @param fields [Symbol, Array] Fields to display
|
|
65
|
+
# @param schema [Hash, nil] JSON schema for validation
|
|
66
|
+
# @yieldparam param [Object] The parameter value to process
|
|
67
|
+
# @yieldreturn [Hash, nil] Result hash for the operation (optional)
|
|
68
|
+
# @return [Hash] Result suitable for CLI output
|
|
69
|
+
def do_bulk_operation(command:, descr: nil, values: Hash, id_result: 'id', fields: :default, schema: nil, &block)
|
|
67
70
|
Aspera.assert(block_given?){'missing block'}
|
|
68
71
|
is_bulk = options.get_option(:bulk)
|
|
69
72
|
case values
|
|
70
73
|
when :identifier
|
|
71
74
|
values = options.instance_identifier(description: descr)
|
|
72
75
|
when Class
|
|
73
|
-
values = value_create_modify(command: command, description: descr, type: values, bulk: is_bulk)
|
|
76
|
+
values = value_create_modify(command: command, description: descr, type: values, bulk: is_bulk, schema: schema)
|
|
74
77
|
end
|
|
75
78
|
# If not bulk, there is a single value
|
|
76
79
|
params = is_bulk ? values : [values]
|
|
@@ -105,15 +108,17 @@ module Aspera
|
|
|
105
108
|
end
|
|
106
109
|
|
|
107
110
|
# Operations: Create, Delete, Show, List, Modify
|
|
108
|
-
# @param api
|
|
109
|
-
# @param entity
|
|
110
|
-
# @param command
|
|
111
|
-
# @param display_fields [Array]
|
|
112
|
-
# @param items_key
|
|
113
|
-
# @param delete_style
|
|
114
|
-
# @param id_as_arg
|
|
115
|
-
# @param is_singleton
|
|
116
|
-
# @param
|
|
111
|
+
# @param api [Aspera::Rest] API to use
|
|
112
|
+
# @param entity [String] Sub path in URL to resource relative to base url
|
|
113
|
+
# @param command [Symbol, nil] Command to execute: :create, :show, :list, :modify, :delete
|
|
114
|
+
# @param display_fields [Array, nil] Fields to display by default
|
|
115
|
+
# @param items_key [String, nil] Result is in a sub key of the JSON
|
|
116
|
+
# @param delete_style [String, nil] If set, the delete operation by array in payload
|
|
117
|
+
# @param id_as_arg [Boolean, String] If set, the id is provided as url argument ?<id_as_arg>=<id>
|
|
118
|
+
# @param is_singleton [Boolean] If `true`, entity is the full path to the resource
|
|
119
|
+
# @param list_query [Hash, nil] Query parameters for list operation
|
|
120
|
+
# @yieldparam value [String] Value to search for identifier
|
|
121
|
+
# @yieldreturn [String] The identifier
|
|
117
122
|
# @return [Hash] Result suitable for CLI result
|
|
118
123
|
def entity_execute(
|
|
119
124
|
api:,
|
|
@@ -189,6 +194,8 @@ module Aspera
|
|
|
189
194
|
end
|
|
190
195
|
|
|
191
196
|
# Query parameters in URL suitable for REST: list/`GET` and delete/`DELETE`
|
|
197
|
+
# @param default [Hash, nil] Default query parameters
|
|
198
|
+
# @return [Hash, nil] Query parameters
|
|
192
199
|
def query_read_delete(default: nil)
|
|
193
200
|
# Dup default, as it could be frozen
|
|
194
201
|
query = options.get_option(:query) || default.dup
|
|
@@ -202,27 +209,29 @@ module Aspera
|
|
|
202
209
|
return query
|
|
203
210
|
end
|
|
204
211
|
|
|
205
|
-
# Retrieves an extended value from command line
|
|
206
|
-
#
|
|
207
|
-
# @param
|
|
208
|
-
# @param
|
|
209
|
-
# @param
|
|
210
|
-
|
|
212
|
+
# Retrieves an extended value from command line.
|
|
213
|
+
# Used for creation or modification of entities.
|
|
214
|
+
# @param command [Symbol] Command name for error message
|
|
215
|
+
# @param description [String, nil] Description of the value
|
|
216
|
+
# @param type [Class, nil] Expected type of value
|
|
217
|
+
# @param bulk [Boolean] If `true`, value must be an Array of `type`
|
|
218
|
+
# @param default [Object, nil] Default value if not provided
|
|
219
|
+
# @param schema [Hash, nil] JSON schema for validation
|
|
220
|
+
# @return [Hash, Array<Hash>] The value(s) to create object(s)
|
|
221
|
+
def value_create_modify(command:, description: nil, type: Hash, bulk: false, default: nil, schema: nil)
|
|
211
222
|
value = options.get_next_argument(
|
|
212
|
-
"parameters for #{command}#{" (#{description})" unless description.nil?}",
|
|
213
|
-
|
|
223
|
+
"parameters for #{command}#{" (#{description})" unless description.nil?}",
|
|
224
|
+
mandatory: default.nil?,
|
|
225
|
+
validation: bulk ? Array : type,
|
|
226
|
+
schema: schema
|
|
214
227
|
)
|
|
215
228
|
value = default if value.nil?
|
|
216
229
|
unless type.nil?
|
|
217
|
-
type
|
|
218
|
-
Aspera.assert_array_all(type, Class){'check types'}
|
|
230
|
+
Aspera.assert_type(type, Class){'type'}
|
|
219
231
|
if bulk
|
|
220
|
-
Aspera.
|
|
221
|
-
value.each do |v|
|
|
222
|
-
Aspera.assert_values(v.class, type, type: Cli::BadArgument)
|
|
223
|
-
end
|
|
232
|
+
Aspera.assert_array_all(value, type, type: Cli::BadArgument){'type'}
|
|
224
233
|
else
|
|
225
|
-
Aspera.
|
|
234
|
+
Aspera.assert_type(value, type, type: Cli::BadArgument){'type'}
|
|
226
235
|
end
|
|
227
236
|
end
|
|
228
237
|
return value
|
|
@@ -16,7 +16,7 @@ require 'aspera/sync/operations'
|
|
|
16
16
|
require 'aspera/products/transferd'
|
|
17
17
|
require 'aspera/transfer/parameters'
|
|
18
18
|
require 'aspera/transfer/spec'
|
|
19
|
-
require 'aspera/
|
|
19
|
+
require 'aspera/schema/documentation'
|
|
20
20
|
require 'aspera/keychain/macos_security'
|
|
21
21
|
require 'aspera/proxy_auto_config'
|
|
22
22
|
require 'aspera/environment'
|
|
@@ -436,7 +436,7 @@ module Aspera
|
|
|
436
436
|
end
|
|
437
437
|
|
|
438
438
|
def set_preset_key(preset, param_name, param_value)
|
|
439
|
-
Aspera.
|
|
439
|
+
Aspera.assert_type(param_name, String, Symbol){'parameter'}
|
|
440
440
|
param_name = param_name.to_s
|
|
441
441
|
selected_preset = @config_presets[preset]
|
|
442
442
|
if selected_preset.nil?
|
|
@@ -598,10 +598,10 @@ module Aspera
|
|
|
598
598
|
when :install
|
|
599
599
|
return install_transfer_sdk
|
|
600
600
|
when :spec
|
|
601
|
-
|
|
602
|
-
return Main.result_object_list(
|
|
601
|
+
builder = Schema::Documentation.new(TerminalFormatter, Transfer::Spec::SCHEMA, include_option: true, agent_columns: true).build
|
|
602
|
+
return Main.result_object_list(builder.rows, fields: builder.columns)
|
|
603
603
|
when :schema
|
|
604
|
-
schema = Transfer::Spec::SCHEMA.merge({'$comment'=>'DO NOT EDIT, this file was generated from the YAML.'})
|
|
604
|
+
schema = Transfer::Spec::SCHEMA.current.merge({'$comment'=>'DO NOT EDIT, this file was generated from the YAML.'})
|
|
605
605
|
agent = options.get_next_argument('transfer agent name', mandatory: false)
|
|
606
606
|
schema['properties'] = schema['properties'].select{ |_k, v| CommandLineBuilder.supported_by_agent(agent, v)} unless agent.nil?
|
|
607
607
|
schema['properties'] = schema['properties'].sort.to_h
|
|
@@ -834,8 +834,8 @@ module Aspera
|
|
|
834
834
|
plugin_class = Plugins::Factory.instance.plugin_class(name)
|
|
835
835
|
result.push({
|
|
836
836
|
plugin: name,
|
|
837
|
-
detect:
|
|
838
|
-
wizard:
|
|
837
|
+
detect: TerminalFormatter.tick(plugin_class.respond_to?(:detect)),
|
|
838
|
+
wizard: TerminalFormatter.tick(plugin_class.method_defined?(:wizard)),
|
|
839
839
|
path: Plugins::Factory.instance.plugin_source(name)
|
|
840
840
|
})
|
|
841
841
|
end
|
|
@@ -878,8 +878,8 @@ module Aspera
|
|
|
878
878
|
when :sync
|
|
879
879
|
case options.get_next_command(%i[spec admin translate])
|
|
880
880
|
when :spec
|
|
881
|
-
|
|
882
|
-
return Main.result_object_list(
|
|
881
|
+
builder = Schema::Documentation.new(TerminalFormatter, Sync::Operations::CONF_SCHEMA, include_option: true).build
|
|
882
|
+
return Main.result_object_list(builder.rows, fields: builder.columns)
|
|
883
883
|
when :admin
|
|
884
884
|
return execute_sync_admin
|
|
885
885
|
when :translate
|
|
@@ -165,7 +165,7 @@ module Aspera
|
|
|
165
165
|
def mailbox_filtered_entries(stop_at_id: nil)
|
|
166
166
|
recipient_names = [options.get_option(:recipient) || options.get_option(:username, mandatory: true)]
|
|
167
167
|
# some workgroup messages have no star in recipient name
|
|
168
|
-
recipient_names.push(recipient_names.first
|
|
168
|
+
recipient_names.push(recipient_names.first.delete_prefix('*')) if recipient_names.first.start_with?('*')
|
|
169
169
|
# mailbox is in ATOM_MAILBOXES
|
|
170
170
|
mailbox = options.get_option(:box, mandatory: true)
|
|
171
171
|
# parameters
|
|
@@ -234,8 +234,8 @@ module Aspera
|
|
|
234
234
|
result_transfer = []
|
|
235
235
|
param_file_list = {}
|
|
236
236
|
begin
|
|
237
|
-
param_file_list['paths'] = transfer.
|
|
238
|
-
rescue Cli::
|
|
237
|
+
param_file_list['paths'] = transfer.ts_source_paths
|
|
238
|
+
rescue Cli::MissingArgument
|
|
239
239
|
# paths is optional
|
|
240
240
|
end
|
|
241
241
|
box = options.get_option(:box)
|
|
@@ -270,7 +270,7 @@ module Aspera
|
|
|
270
270
|
end
|
|
271
271
|
|
|
272
272
|
def package_send
|
|
273
|
-
parameters = value_create_modify(command: :send)
|
|
273
|
+
parameters = value_create_modify(command: :send, schema: Schema::Registry.req_body(Schema::Registry::FASPEX, 'packages.post'))
|
|
274
274
|
# autofill recipient for public url
|
|
275
275
|
if @api_v5.pub_link_context&.key?('recipient_type') && !parameters.key?('recipients')
|
|
276
276
|
parameters['recipients'] = [{
|
|
@@ -494,8 +494,7 @@ module Aspera
|
|
|
494
494
|
when :invite_external_collaborator
|
|
495
495
|
# :shared_inboxes, :workgroups
|
|
496
496
|
shared_inbox_id = options.instance_identifier{ |field, value| @api_v5.lookup_entity_by_field(entity: res_sym.to_s, field: field, value: value, query: res_id_query)['id']}
|
|
497
|
-
creation_payload = value_create_modify(command: res_command
|
|
498
|
-
creation_payload = {'email_address' => creation_payload} if creation_payload.is_a?(String)
|
|
497
|
+
creation_payload = value_create_modify(command: res_command)
|
|
499
498
|
result = @api_v5.create("#{res_sym}/#{shared_inbox_id}/external_collaborator", creation_payload)
|
|
500
499
|
formatter.display_status(result['message'])
|
|
501
500
|
result = @api_v5.lookup_entity_by_field(
|
|
@@ -41,7 +41,7 @@ module Aspera
|
|
|
41
41
|
|
|
42
42
|
def remove_in_object_list!(obj_list)
|
|
43
43
|
obj_list.each do |item|
|
|
44
|
-
item['path'] = item['path']
|
|
44
|
+
item['path'] = item['path'].delete_prefix(@root) if item['path'].start_with?(@root)
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
end
|
|
@@ -27,7 +27,7 @@ module Aspera
|
|
|
27
27
|
# @return [Hash] sync info
|
|
28
28
|
def async_info_from_args(direction: nil)
|
|
29
29
|
path = options.get_next_argument('path')
|
|
30
|
-
sync_info = options.get_next_argument('sync info', mandatory: false, validation: Hash, default: {})
|
|
30
|
+
sync_info = options.get_next_argument('sync info', mandatory: false, validation: Hash, default: {}, schema: Schema::Registry::SYNC_CONF)
|
|
31
31
|
# is the positional path a remote path ?
|
|
32
32
|
path_is_remote = direction.eql?(:pull)
|
|
33
33
|
if sync_info.key?('sessions') || sync_info.key?('instance')
|
|
@@ -5,6 +5,7 @@ require 'aspera/transfer/spec'
|
|
|
5
5
|
require 'aspera/cli/info'
|
|
6
6
|
require 'aspera/log'
|
|
7
7
|
require 'aspera/assert'
|
|
8
|
+
require 'aspera/schema/registry'
|
|
8
9
|
|
|
9
10
|
module Aspera
|
|
10
11
|
module Cli
|
|
@@ -12,9 +13,9 @@ module Aspera
|
|
|
12
13
|
# one of the supported transfer agents.
|
|
13
14
|
# Provide CLI options to select one of the transfer agents (FASP/ascp client)
|
|
14
15
|
class TransferAgent
|
|
15
|
-
#
|
|
16
|
+
# `@args` special value for --sources : read file list from arguments
|
|
16
17
|
FILE_LIST_FROM_ARGS = '@args'
|
|
17
|
-
#
|
|
18
|
+
# `@ts` special value for --sources : read file list from transfer spec (--ts)
|
|
18
19
|
FILE_LIST_FROM_TRANSFER_SPEC = '@ts'
|
|
19
20
|
FILE_LIST_OPTIONS = [FILE_LIST_FROM_ARGS, FILE_LIST_FROM_TRANSFER_SPEC, 'Array'].freeze
|
|
20
21
|
DEFAULT_TRANSFER_NOTIFY_TEMPLATE = <<~END_OF_TEMPLATE
|
|
@@ -63,12 +64,12 @@ module Aspera
|
|
|
63
64
|
@transfer_paths = nil
|
|
64
65
|
# HTTPGW URL provided by webapp
|
|
65
66
|
@httpgw_url_lambda = nil
|
|
66
|
-
@opt_mgr.declare(:ts, 'Override transfer spec values', allowed: Hash, handler: {o: self, m: :user_transfer_spec})
|
|
67
|
+
@opt_mgr.declare(:ts, 'Override transfer spec values', allowed: Hash, handler: {o: self, m: :user_transfer_spec}, schema: Schema::Registry::TRANSFER_SPEC)
|
|
67
68
|
@opt_mgr.declare(:to_folder, 'Destination folder for transferred files')
|
|
68
69
|
@opt_mgr.declare(:sources, "How list of transferred files is provided (#{FILE_LIST_OPTIONS.join(',')})", default: FILE_LIST_FROM_ARGS)
|
|
69
70
|
@opt_mgr.declare(:src_type, 'Type of file list', allowed: %i[list pair], default: :list)
|
|
70
71
|
@opt_mgr.declare(:transfer, 'Type of transfer agent', allowed: Agent::Factory::ALL.keys, default: :direct)
|
|
71
|
-
@opt_mgr.declare(:transfer_info, 'Parameters for transfer agent', allowed: Hash, handler: {o: self, m: :transfer_info})
|
|
72
|
+
@opt_mgr.declare(:transfer_info, 'Parameters for transfer agent', allowed: Hash, handler: {o: self, m: :transfer_info}, schema: Schema::Registry::TRANSFER_INFO)
|
|
72
73
|
@opt_mgr.parse_options!
|
|
73
74
|
@notification_cb = nil
|
|
74
75
|
if !@opt_mgr.get_option(:notify_to).nil?
|
|
@@ -154,24 +155,25 @@ module Aspera
|
|
|
154
155
|
end
|
|
155
156
|
|
|
156
157
|
# Transform the list of paths to a list of hash with source/dest
|
|
157
|
-
# @param file_list [Array]
|
|
158
|
+
# @param file_list [Array<Hash>]
|
|
158
159
|
def list_to_paths(file_list)
|
|
159
160
|
source_type = @opt_mgr.get_option(:src_type, mandatory: true)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
161
|
+
@transfer_paths =
|
|
162
|
+
case source_type
|
|
163
|
+
when :list
|
|
164
|
+
# when providing a list, just specify source
|
|
165
|
+
file_list.map{ |i| {'source' => i}}
|
|
166
|
+
when :pair
|
|
167
|
+
Aspera.assert(file_list.length.even?, type: Cli::BadArgument){"When using pair, provide an even number of paths: #{file_list.length}"}
|
|
168
|
+
file_list.each_slice(2).map{ |s, d| {'source' => s, 'destination' => d}}
|
|
169
|
+
else Aspera.error_unexpected_value(source_type)
|
|
170
|
+
end
|
|
169
171
|
end
|
|
170
172
|
|
|
171
173
|
# This is how the list of files to be transferred is specified
|
|
172
174
|
# get paths suitable for transfer spec from command line
|
|
173
175
|
# computation is done only once, cache is kept in @transfer_paths
|
|
174
|
-
# @param default [Array
|
|
176
|
+
# @param default [nil, Array<String>] If set, used as default file for --sources=@args
|
|
175
177
|
# @return [Array, nil] of Hash {source: (mandatory), destination: (optional)}
|
|
176
178
|
def ts_source_paths(default: nil)
|
|
177
179
|
# return cache if set
|
data/lib/aspera/cli/version.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'aspera/log'
|
|
4
4
|
require 'aspera/assert'
|
|
5
|
+
require 'aspera/schema/registry'
|
|
5
6
|
require 'yaml'
|
|
6
7
|
module Aspera
|
|
7
8
|
# Helper class to build command line from a parameter list (key-value hash)
|
|
@@ -35,9 +36,9 @@ module Aspera
|
|
|
35
36
|
|
|
36
37
|
class << self
|
|
37
38
|
# Called by provider of definition before constructor of this class so that schema has all mandatory fields
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
validate_schema(
|
|
39
|
+
# @return [Aspera::Schema::Reader]
|
|
40
|
+
def read_schema(name_sym, ascp: false)
|
|
41
|
+
validate_schema(Schema::Registry.instance.reader(name_sym), ascp: ascp)
|
|
41
42
|
end
|
|
42
43
|
|
|
43
44
|
# @param agent [Symbol] Transfer agent name
|
|
@@ -49,29 +50,32 @@ module Aspera
|
|
|
49
50
|
|
|
50
51
|
private
|
|
51
52
|
|
|
53
|
+
DIRECT_PROPERTIES = %w[x-cli-option x-cli-envvar x-cli-special].freeze
|
|
54
|
+
|
|
52
55
|
# Fill default values for some fields in the schema
|
|
53
|
-
# @param schema [
|
|
54
|
-
# @param ascp
|
|
56
|
+
# @param schema [Schema::Reader] The JSON schema
|
|
57
|
+
# @param ascp [Boolean] `true` if `ascp`
|
|
58
|
+
# @return [Schema::Reader] The JSON schema
|
|
55
59
|
def validate_schema(schema, ascp: false)
|
|
56
|
-
|
|
57
|
-
schema
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
Aspera.assert_type(schema, Schema::Reader){'schema'}
|
|
61
|
+
Aspera.assert(schema.current.key?('properties')){"Schema must have 'properties': #{schema}"}
|
|
62
|
+
schema.each_property do |property_schema, name, _full_name|
|
|
63
|
+
node = property_schema.current
|
|
64
|
+
unsupported_keys = node.keys - PROPERTY_KEYS
|
|
60
65
|
Aspera.assert(unsupported_keys.empty?){"Unsupported definition keys: #{unsupported_keys}"}
|
|
61
|
-
Aspera.assert(
|
|
62
|
-
Aspera.assert(
|
|
63
|
-
|
|
64
|
-
Aspera.assert(
|
|
65
|
-
|
|
66
|
-
validate_schema(info, ascp: ascp) if info['type'].eql?('object') && info['properties']
|
|
67
|
-
validate_schema(info['items'], ascp: ascp) if info['type'].eql?('array') && info['items'] && info['items']['properties']
|
|
66
|
+
Aspera.assert(node.key?('type') || node.key?('enum')){"Missing type for #{name} in #{schema.current.dig('description').current}"}
|
|
67
|
+
Aspera.assert(node['type'].eql?('boolean')){"switch must be bool: #{name}"} if node['x-cli-switch'] && !node['x-cli-special']
|
|
68
|
+
node['x-cli-option'] = "--#{name.to_s.tr('_', '-')}" if node['x-cli-option'].eql?(true) || (node['x-cli-switch'].eql?(true) && !node.key?('x-cli-option'))
|
|
69
|
+
Aspera.assert(DIRECT_PROPERTIES.any?{ |i| node.key?(i)}, type: :warn){name} if ascp && supported_by_agent(:direct, node)
|
|
70
|
+
node.freeze
|
|
68
71
|
end
|
|
69
72
|
schema
|
|
70
73
|
end
|
|
71
74
|
end
|
|
72
75
|
|
|
73
|
-
# @param [Hash] object with parameters
|
|
74
|
-
# @param [
|
|
76
|
+
# @param object [Hash] object with parameters
|
|
77
|
+
# @param schema [Schema::Reader] JSON schema
|
|
78
|
+
# @param convert [Object] function to convert value
|
|
75
79
|
def initialize(object, schema, convert)
|
|
76
80
|
@object = object # keep reference so that it can be modified by caller before calling `process_params`
|
|
77
81
|
@schema = schema
|
data/lib/aspera/environment.rb
CHANGED
|
@@ -151,7 +151,7 @@ module Aspera
|
|
|
151
151
|
# @param path [String] the file path
|
|
152
152
|
# @param force [Boolean] if true, overwrite the file
|
|
153
153
|
# @param mode [Integer] the file mode (permissions)
|
|
154
|
-
# @
|
|
154
|
+
# @yieldreturn [String] The content to write to the file
|
|
155
155
|
def write_file_restricted(path, force: false, mode: nil)
|
|
156
156
|
Aspera.assert(block_given?, type: Aspera::InternalError)
|
|
157
157
|
if force || !File.exist?(path)
|
|
@@ -280,7 +280,7 @@ module Aspera
|
|
|
280
280
|
when Environment::OS_MACOS then return self.class.secure_execute('open', uri.to_s)
|
|
281
281
|
when Environment::OS_WINDOWS then return self.class.secure_execute('start', 'explorer', %Q{"#{uri}"})
|
|
282
282
|
when Environment::OS_LINUX then return self.class.secure_execute('xdg-open', uri.to_s)
|
|
283
|
-
else
|
|
283
|
+
else Aspera.error_unexpected_value(os){'no graphical open method'}
|
|
284
284
|
end
|
|
285
285
|
end
|
|
286
286
|
|
|
@@ -334,7 +334,7 @@ module Aspera
|
|
|
334
334
|
filename = filename.chop while filename.end_with?(' ', '.')
|
|
335
335
|
if @file_illegal_characters&.size.to_i >= 2
|
|
336
336
|
# replace all illegal characters with safe_char
|
|
337
|
-
filename = filename.tr(@file_illegal_characters[1
|
|
337
|
+
filename = filename.tr(@file_illegal_characters[1..], safe_char)
|
|
338
338
|
end
|
|
339
339
|
# ensure only one safe_char is used at a time
|
|
340
340
|
return filename.gsub(/#{Regexp.escape(safe_char)}+/, safe_char).chomp(safe_char)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'aspera/assert'
|
|
4
|
+
|
|
5
|
+
module Aspera
|
|
6
|
+
# Common interface for documentation formatters
|
|
7
|
+
# Used by Schema::Documentation to format tables
|
|
8
|
+
module FormatterInterface
|
|
9
|
+
Aspera.require_method!(:tick)
|
|
10
|
+
Aspera.require_method!(:special_format)
|
|
11
|
+
Aspera.require_method!(:check_row)
|
|
12
|
+
Aspera.require_method!(:markdown_text)
|
|
13
|
+
end
|
|
14
|
+
end
|
data/lib/aspera/hash_ext.rb
CHANGED
|
@@ -9,6 +9,12 @@ class ::Hash
|
|
|
9
9
|
merge!(second){ |_key, v1, v2| v1.is_a?(Hash) && v2.is_a?(Hash) ? v1.deep_merge!(v2) : v2}
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
+
# Recursively iterate through hash and execute block on leaf values
|
|
13
|
+
# @param memory [Object, nil] Optional memory object passed to block
|
|
14
|
+
# @yieldparam hash [Hash] The current hash
|
|
15
|
+
# @yieldparam key [Object] The current key
|
|
16
|
+
# @yieldparam value [Object] The current value (non-Hash)
|
|
17
|
+
# @yieldparam memory [Object, nil] The memory object
|
|
12
18
|
def deep_do(memory = nil, &block)
|
|
13
19
|
each do |key, value|
|
|
14
20
|
if value.is_a?(Hash)
|
data/lib/aspera/log.rb
CHANGED
|
@@ -77,7 +77,7 @@ module Aspera
|
|
|
77
77
|
# @param name [String, Symbol] Name of object dumped
|
|
78
78
|
# @param object [Hash, nil] Data to dump
|
|
79
79
|
# @param level [Symbol] Debug level
|
|
80
|
-
# @
|
|
80
|
+
# @yieldreturn [Object] Computed object to dump (alternative to object parameter)
|
|
81
81
|
def dump(name, object = nil, level: :debug, &block)
|
|
82
82
|
return unless instance.logger.send(:"#{level}?")
|
|
83
83
|
Aspera.assert(object.nil? || block.nil?){'Use either object, or block, not both'}
|
|
@@ -93,12 +93,13 @@ module Aspera
|
|
|
93
93
|
JSON.pretty_generate(object) rescue PP.pp(object, +'')
|
|
94
94
|
when :ruby
|
|
95
95
|
PP.pp(object, +'')
|
|
96
|
-
else error_unexpected_value(instance.dump_format){'dump format'}
|
|
96
|
+
else Aspera.error_unexpected_value(instance.dump_format){'dump format'}
|
|
97
97
|
end
|
|
98
98
|
"#{name.to_s.green}(#{instance.dump_format})#{object.class}=\n#{dump_text}"
|
|
99
99
|
end
|
|
100
100
|
|
|
101
101
|
# Capture the output of $stderr and log it at debug level
|
|
102
|
+
# @yieldreturn [void] Code block whose stderr output will be captured
|
|
102
103
|
def capture_stderr
|
|
103
104
|
real_stderr = $stderr
|
|
104
105
|
$stderr = StringIO.new
|
|
@@ -192,7 +193,7 @@ module Aspera
|
|
|
192
193
|
end
|
|
193
194
|
# Use `local2` facility, like other Aspera components
|
|
194
195
|
@logger = Syslog::Logger.new(@program_name, Syslog::LOG_LOCAL2)
|
|
195
|
-
else error_unexpected_value(new_log_type){"log type (#{LOG_TYPES.join(', ')})"}
|
|
196
|
+
else Aspera.error_unexpected_value(new_log_type){"log type (#{LOG_TYPES.join(', ')})"}
|
|
196
197
|
end
|
|
197
198
|
@logger.level = current_severity_integer
|
|
198
199
|
@logger_type = new_log_type
|