aspera-cli 4.27.0 → 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 +29 -0
- data/CONTRIBUTING.md +1 -7
- data/TODO.md +50 -0
- data/docs/README.md +708 -488
- data/lib/aspera/agent/base.rb +10 -2
- data/lib/aspera/agent/connect.rb +7 -7
- data/lib/aspera/agent/desktop.rb +3 -3
- data/lib/aspera/agent/direct.rb +48 -19
- data/lib/aspera/agent/factory.rb +3 -3
- data/lib/aspera/agent/httpgw.rb +55 -14
- data/lib/aspera/agent/node.rb +2 -2
- data/lib/aspera/agent/transferd.rb +12 -12
- data/lib/aspera/api/aoc.rb +28 -28
- data/lib/aspera/api/cos_node.rb +10 -9
- data/lib/aspera/api/faspex.rb +2 -2
- data/lib/aspera/api/httpgw.rb +20 -20
- data/lib/aspera/api/node.rb +31 -53
- data/lib/aspera/ascmd.rb +13 -13
- data/lib/aspera/ascp/installation.rb +43 -36
- data/lib/aspera/ascp/management.rb +6 -6
- data/lib/aspera/assert.rb +12 -13
- data/lib/aspera/cli/ascp_actions.rb +7 -7
- data/lib/aspera/cli/async_transfer_store.rb +49 -5
- data/lib/aspera/cli/bootstrapper.rb +9 -9
- data/lib/aspera/cli/command_registry.rb +1 -1
- data/lib/aspera/cli/command_spec.rb +6 -3
- data/lib/aspera/cli/context.rb +3 -3
- data/lib/aspera/cli/extended_value.rb +26 -26
- data/lib/aspera/cli/formatter.rb +76 -54
- data/lib/aspera/cli/gem_checker.rb +1 -1
- data/lib/aspera/cli/hints.rb +2 -2
- data/lib/aspera/cli/http.rb +22 -29
- data/lib/aspera/cli/mailer.rb +7 -7
- data/lib/aspera/cli/mcp_tool.rb +31 -4
- data/lib/aspera/cli/option_declarator.rb +77 -0
- data/lib/aspera/cli/options.schema.yaml +273 -3
- data/lib/aspera/cli/parser.rb +539 -280
- data/lib/aspera/cli/plugins/alee.rb +1 -1
- data/lib/aspera/cli/plugins/aoc.rb +297 -313
- data/lib/aspera/cli/plugins/ats.rb +25 -21
- data/lib/aspera/cli/plugins/base.rb +357 -186
- data/lib/aspera/cli/plugins/basic_auth.rb +0 -12
- data/lib/aspera/cli/plugins/config.rb +76 -73
- data/lib/aspera/cli/plugins/console.rb +8 -8
- data/lib/aspera/cli/plugins/cos.rb +4 -3
- data/lib/aspera/cli/plugins/factory.rb +2 -2
- data/lib/aspera/cli/plugins/faspex5.rb +259 -248
- data/lib/aspera/cli/plugins/faspio.rb +7 -2
- data/lib/aspera/cli/plugins/httpgw.rb +1 -1
- data/lib/aspera/cli/plugins/mcp.rb +11 -11
- data/lib/aspera/cli/plugins/node.rb +227 -283
- data/lib/aspera/cli/plugins/orchestrator.rb +23 -23
- data/lib/aspera/cli/plugins/preview.rb +61 -64
- data/lib/aspera/cli/plugins/server.rb +36 -31
- data/lib/aspera/cli/plugins/shares.rb +174 -135
- data/lib/aspera/cli/preset_actions.rb +39 -19
- data/lib/aspera/cli/preset_manager.rb +74 -32
- data/lib/aspera/cli/result.rb +18 -16
- data/lib/aspera/cli/runner.rb +75 -39
- data/lib/aspera/cli/secret_finder.rb +1 -1
- data/lib/aspera/cli/sync_actions.rb +36 -35
- data/lib/aspera/cli/terminal_formatter.rb +2 -2
- data/lib/aspera/cli/transfer_actions.rb +17 -10
- data/lib/aspera/cli/transfer_agent.rb +44 -44
- data/lib/aspera/cli/transfer_progress.rb +7 -7
- data/lib/aspera/cli/vault_manager.rb +34 -12
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +17 -20
- data/lib/aspera/colors.rb +3 -3
- data/lib/aspera/command_line_builder.rb +16 -16
- data/lib/aspera/command_line_converter.rb +1 -1
- data/lib/aspera/coverage.rb +1 -2
- data/lib/aspera/data_repository.rb +1 -1
- data/lib/aspera/dot_container.rb +8 -8
- data/lib/aspera/environment.rb +12 -12
- data/lib/aspera/faspex_gw.rb +4 -4
- data/lib/aspera/faspex_postproc.rb +14 -9
- data/lib/aspera/graphql.rb +4 -2
- data/lib/aspera/hash_ext.rb +2 -2
- data/lib/aspera/json_rpc/client.rb +1 -1
- data/lib/aspera/keychain/base.rb +10 -3
- data/lib/aspera/keychain/encrypted_hash.rb +92 -17
- data/lib/aspera/keychain/factory.rb +17 -5
- 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 +2 -2
- data/lib/aspera/log.rb +13 -13
- data/lib/aspera/markdown.rb +5 -5
- data/lib/aspera/nagios.rb +8 -8
- data/lib/aspera/node_simulator.rb +33 -27
- data/lib/aspera/oauth/base.rb +4 -5
- data/lib/aspera/oauth/boot.rb +3 -3
- data/lib/aspera/oauth/factory.rb +5 -5
- data/lib/aspera/oauth/jwt.rb +5 -7
- data/lib/aspera/oauth/web.rb +4 -4
- data/lib/aspera/persistency_action_once.rb +4 -4
- data/lib/aspera/persistency_folder.rb +8 -8
- data/lib/aspera/preview/file_types.rb +4 -4
- data/lib/aspera/preview/generator.rb +49 -46
- data/lib/aspera/preview/options.rb +3 -2
- data/lib/aspera/preview/terminal.rb +5 -5
- data/lib/aspera/preview/utils.rb +65 -18
- data/lib/aspera/products/connect.rb +2 -2
- data/lib/aspera/products/desktop.rb +1 -1
- data/lib/aspera/products/other.rb +2 -2
- data/lib/aspera/products/transferd.rb +3 -3
- data/lib/aspera/proxy_auto_config.rb +10 -10
- data/lib/aspera/rest.rb +149 -115
- data/lib/aspera/rest_error_analyzer.rb +3 -3
- data/lib/aspera/rest_list.rb +10 -9
- 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_Shares.yaml +4653 -0
- data/lib/aspera/schema/documentation.rb +17 -11
- data/lib/aspera/schema/reader.rb +78 -4
- data/lib/aspera/schema/registry.rb +26 -7
- data/lib/aspera/secret_hider.rb +5 -5
- data/lib/aspera/ssh.rb +64 -31
- data/lib/aspera/ssl.rb +3 -3
- data/lib/aspera/sync/database.rb +2 -2
- data/lib/aspera/sync/operations.rb +13 -13
- data/lib/aspera/temp_file_manager.rb +24 -4
- data/lib/aspera/transfer/faux_file.rb +2 -2
- data/lib/aspera/transfer/parameters.rb +15 -15
- data/lib/aspera/transfer/resumer.rb +9 -9
- data/lib/aspera/transfer/spec.rb +1 -1
- data/lib/aspera/uri_reader.rb +52 -15
- data/lib/aspera/web_auth.rb +6 -5
- data/lib/aspera/web_server_simple.rb +9 -9
- data/lib/aspera/yaml.rb +4 -4
- data.tar.gz.sig +0 -0
- metadata +10 -4
- metadata.gz.sig +0 -0
- data/lib/aspera/cli/plugins/faspex.rb +0 -576
- data/lib/aspera/transfer/uri.rb +0 -56
data/lib/aspera/api/aoc.rb
CHANGED
|
@@ -65,7 +65,7 @@ module Aspera
|
|
|
65
65
|
MAX_AOC_URL_REDIRECT = 10
|
|
66
66
|
CLIENT_ID_PREFIX = 'aspera.'
|
|
67
67
|
# Well-known AoC global client apps
|
|
68
|
-
GLOBAL_CLIENT_APPS = DataRepository::ELEMENTS.select{ |i| i.to_s.start_with?(CLIENT_ID_PREFIX)}.freeze
|
|
68
|
+
GLOBAL_CLIENT_APPS = DataRepository::ELEMENTS.select { |i| i.to_s.start_with?(CLIENT_ID_PREFIX) }.freeze
|
|
69
69
|
# cookie prefix so that console can decode identity
|
|
70
70
|
COOKIE_PREFIX_CONSOLE_AOC = 'aspera.aoc'
|
|
71
71
|
# path in URL of public links (warning if not in list)
|
|
@@ -123,9 +123,9 @@ module Aspera
|
|
|
123
123
|
# split host of URL into organization and domain
|
|
124
124
|
def split_org_domain(uri)
|
|
125
125
|
Aspera.assert_type(uri, URI)
|
|
126
|
-
Aspera.assert(!uri.host.nil?){"No host found in URL. Please check URL format: https://myorg.#{SAAS_DOMAIN_PROD}"}
|
|
126
|
+
Aspera.assert(!uri.host.nil?) { "No host found in URL. Please check URL format: https://myorg.#{SAAS_DOMAIN_PROD}" }
|
|
127
127
|
parts = uri.host.split('.', 2)
|
|
128
|
-
Aspera.assert(parts.length == 2){"expecting a public FQDN for #{PRODUCT_NAME}"}
|
|
128
|
+
Aspera.assert(parts.length == 2) { "expecting a public FQDN for #{PRODUCT_NAME}" }
|
|
129
129
|
parts[0] = nil if parts[0].eql?('api')
|
|
130
130
|
return %i{organization domain}.zip(parts).to_h
|
|
131
131
|
end
|
|
@@ -146,14 +146,14 @@ module Aspera
|
|
|
146
146
|
if (m = final_uri.path.match(%r{/oauth2/([^/]+)/login$}))
|
|
147
147
|
org_domain[:organization] = m[1] if org_domain[:organization].nil?
|
|
148
148
|
else
|
|
149
|
-
Log.log.debug{"path=#{final_uri.path} does not end with /login"}
|
|
149
|
+
Log.log.debug { "path=#{final_uri.path} does not end with /login" }
|
|
150
150
|
end
|
|
151
151
|
Aspera.assert(!final_uri.query.nil?, 'AoC shall redirect to login page with a query', type: Error)
|
|
152
152
|
query = Rest.query_to_h(final_uri.query)
|
|
153
153
|
Log.dump(:query, query, level: :trace1)
|
|
154
154
|
# is that a public link ?
|
|
155
155
|
if query.key?('token')
|
|
156
|
-
Log.log.warn{"Unknown pub link path: #{final_uri.path}"} unless PUBLIC_LINK_PATHS.include?(final_uri.path)
|
|
156
|
+
Log.log.warn { "Unknown pub link path: #{final_uri.path}" } unless PUBLIC_LINK_PATHS.include?(final_uri.path)
|
|
157
157
|
# ok we get it !
|
|
158
158
|
return {
|
|
159
159
|
instance_domain: org_domain[:domain],
|
|
@@ -198,7 +198,7 @@ module Aspera
|
|
|
198
198
|
# @option return [Array<Hash>] :items The list of items
|
|
199
199
|
# @option return [Integer] :total The total number of items
|
|
200
200
|
def call_paging(query: {})
|
|
201
|
-
Aspera.assert_type(query, Hash){'query'}
|
|
201
|
+
Aspera.assert_type(query, Hash) { 'query' }
|
|
202
202
|
Aspera.assert(block_given?, 'block required for call_paging')
|
|
203
203
|
# set default large page if user does not specify own parameters. AoC Caps to 1000 anyway
|
|
204
204
|
query['per_page'] = 1000 unless query.key?('per_page')
|
|
@@ -257,10 +257,10 @@ module Aspera
|
|
|
257
257
|
when 'download' then %w[list preview read]
|
|
258
258
|
when 'upload' then %w[mkdir write]
|
|
259
259
|
when Array
|
|
260
|
-
Aspera.assert_array_all(levels, String){'access_levels'}
|
|
261
|
-
levels.each{ |level| Aspera.assert_values(level, Node::ACCESS_LEVELS){'access_level'}}
|
|
260
|
+
Aspera.assert_array_all(levels, String) { 'access_levels' }
|
|
261
|
+
levels.each { |level| Aspera.assert_values(level, Node::ACCESS_LEVELS) { 'access_level' } }
|
|
262
262
|
levels
|
|
263
|
-
else Aspera.error_unexpected_value(levels){"access_levels must be a list of #{Node::ACCESS_LEVELS.join(', ')} or one of edit, preview, download, upload"}
|
|
263
|
+
else Aspera.error_unexpected_value(levels) { "access_levels must be a list of #{Node::ACCESS_LEVELS.join(', ')} or one of edit, preview, download, upload" }
|
|
264
264
|
end
|
|
265
265
|
end
|
|
266
266
|
end
|
|
@@ -352,7 +352,7 @@ module Aspera
|
|
|
352
352
|
when :none
|
|
353
353
|
auth_params.clear
|
|
354
354
|
auth_params[:type] = :none
|
|
355
|
-
else Aspera.error_unexpected_value(auth_params[:grant_method]){'auth, use one of: web, jwt, boot'}
|
|
355
|
+
else Aspera.error_unexpected_value(auth_params[:grant_method]) { 'auth, use one of: web, jwt, boot' }
|
|
356
356
|
end
|
|
357
357
|
super(**base_args)
|
|
358
358
|
end
|
|
@@ -369,7 +369,7 @@ module Aspera
|
|
|
369
369
|
|
|
370
370
|
# @param expected [Array<String>] Link types
|
|
371
371
|
def assert_public_link_types(expected)
|
|
372
|
-
Aspera.assert_values(public_link['purpose'], expected){'public link type'}
|
|
372
|
+
Aspera.assert_values(public_link['purpose'], expected) { 'public link type' }
|
|
373
373
|
end
|
|
374
374
|
|
|
375
375
|
def additional_persistence_ids
|
|
@@ -396,10 +396,10 @@ module Aspera
|
|
|
396
396
|
read('self')
|
|
397
397
|
rescue Aspera::RestCallError => e
|
|
398
398
|
raise if exception || e.message.include?('invalid_grant')
|
|
399
|
-
Log.log.debug{"Ignoring error: (#{e.class}) #{e}"}
|
|
399
|
+
Log.log.debug { "Ignoring error: (#{e.class}) #{e}" }
|
|
400
400
|
{}
|
|
401
401
|
end
|
|
402
|
-
USER_INFO_FIELDS_MIN.each{ |f| @cache_user_info[f] = nil if @cache_user_info[f].nil?}
|
|
402
|
+
USER_INFO_FIELDS_MIN.each { |f| @cache_user_info[f] = nil if @cache_user_info[f].nil? }
|
|
403
403
|
return @cache_user_info
|
|
404
404
|
end
|
|
405
405
|
|
|
@@ -488,7 +488,7 @@ module Aspera
|
|
|
488
488
|
ak_secret = @secret_finder&.lookup(url: node_info['url'], username: node_info['access_key'])
|
|
489
489
|
# If secret is available, or no scope, use basic auth
|
|
490
490
|
if scope.nil? || ak_secret
|
|
491
|
-
Aspera.assert(ak_secret, type: Error){"Secret not found for access key #{node_info['access_key']}@#{node_info['url']}"}
|
|
491
|
+
Aspera.assert(ak_secret, type: Error) { "Secret not found for access key #{node_info['access_key']}@#{node_info['url']}" }
|
|
492
492
|
node_params[:auth] = {
|
|
493
493
|
type: :basic,
|
|
494
494
|
username: node_info['access_key'],
|
|
@@ -519,21 +519,21 @@ module Aspera
|
|
|
519
519
|
Log.log.debug('no metadata in shared inbox')
|
|
520
520
|
return
|
|
521
521
|
end
|
|
522
|
-
Aspera.assert(pkg_data.key?('metadata')){"package requires metadata: #{meta_schema}"}
|
|
522
|
+
Aspera.assert(pkg_data.key?('metadata')) { "package requires metadata: #{meta_schema}" }
|
|
523
523
|
pkg_meta = pkg_data['metadata']
|
|
524
|
-
Aspera.assert_type(pkg_meta, Array){'metadata'}
|
|
524
|
+
Aspera.assert_type(pkg_meta, Array) { 'metadata' }
|
|
525
525
|
Log.dump(:metadata, pkg_meta)
|
|
526
526
|
pkg_meta.each do |field|
|
|
527
|
-
Aspera.assert_type(field, Hash){'metadata field'}
|
|
527
|
+
Aspera.assert_type(field, Hash) { 'metadata field' }
|
|
528
528
|
Aspera.assert(field.key?('name'), 'metadata field must have name')
|
|
529
529
|
Aspera.assert(field.key?('values'), 'metadata field must have values')
|
|
530
|
-
Aspera.assert_type(field['values'], Array){'metadata field values'}
|
|
531
|
-
Aspera.assert(meta_schema.any?{ |i| i['name'].eql?(field['name'])}){"unknown metadata field: #{field['name']}"}
|
|
530
|
+
Aspera.assert_type(field['values'], Array) { 'metadata field values' }
|
|
531
|
+
Aspera.assert(meta_schema.any? { |i| i['name'].eql?(field['name']) }) { "unknown metadata field: #{field['name']}" }
|
|
532
532
|
end
|
|
533
533
|
meta_schema.each do |field|
|
|
534
|
-
provided = pkg_meta.select{ |i| i['name'].eql?(field['name'])}
|
|
535
|
-
Aspera.assert(provided.count <= 1, type: Error){"only one field with name #{field['name']} allowed"}
|
|
536
|
-
Aspera.assert(!provided.empty?, type: Error){"missing mandatory field: #{field['name']}"} if field['required']
|
|
534
|
+
provided = pkg_meta.select { |i| i['name'].eql?(field['name']) }
|
|
535
|
+
Aspera.assert(provided.count <= 1, type: Error) { "only one field with name #{field['name']} allowed" }
|
|
536
|
+
Aspera.assert(!provided.empty?, type: Error) { "missing mandatory field: #{field['name']}" } if field['required']
|
|
537
537
|
end
|
|
538
538
|
end
|
|
539
539
|
|
|
@@ -546,16 +546,16 @@ module Aspera
|
|
|
546
546
|
# @return [nil] `package_data` is modified in place
|
|
547
547
|
def resolve_package_recipients(package_data, rcpt_lst_field, new_user_option)
|
|
548
548
|
return unless package_data.key?(rcpt_lst_field)
|
|
549
|
-
Aspera.assert_type(package_data[rcpt_lst_field], Array){rcpt_lst_field}
|
|
549
|
+
Aspera.assert_type(package_data[rcpt_lst_field], Array) { rcpt_lst_field }
|
|
550
550
|
new_user_option = {'package_contact' => true} if new_user_option.nil?
|
|
551
|
-
Aspera.assert_type(new_user_option, Hash){'new_user_option'}
|
|
551
|
+
Aspera.assert_type(new_user_option, Hash) { 'new_user_option' }
|
|
552
552
|
ws_id = package_data['workspace_id']
|
|
553
553
|
# list with resolved elements
|
|
554
554
|
resolved_list = []
|
|
555
555
|
package_data[rcpt_lst_field].each do |short_recipient_info|
|
|
556
556
|
case short_recipient_info
|
|
557
557
|
when Hash # native API information, check keys
|
|
558
|
-
Aspera.assert(short_recipient_info.keys.sort.eql?(%w[id type])){"#{rcpt_lst_field} element shall have fields: id and type"}
|
|
558
|
+
Aspera.assert(short_recipient_info.keys.sort.eql?(%w[id type])) { "#{rcpt_lst_field} element shall have fields: id and type" }
|
|
559
559
|
when String # CLI helper: need to resolve provided name to type/id
|
|
560
560
|
# email: user, else dropbox
|
|
561
561
|
entity_type = short_recipient_info.include?('@') ? 'contacts' : 'dropboxes'
|
|
@@ -563,7 +563,7 @@ module Aspera
|
|
|
563
563
|
full_recipient_info = lookup_with_q(entity_type, value: short_recipient_info, query: {'workspace_id' => ws_id})
|
|
564
564
|
rescue EntityNotFound
|
|
565
565
|
# dropboxes cannot be created on the fly
|
|
566
|
-
Aspera.assert_values(entity_type, %w[contacts], type: Error){"No such shared inbox in workspace #{ws_id}"}
|
|
566
|
+
Aspera.assert_values(entity_type, %w[contacts], type: Error) { "No such shared inbox in workspace #{ws_id}" }
|
|
567
567
|
# unknown user: create it as external user
|
|
568
568
|
full_recipient_info = create('contacts', {
|
|
569
569
|
'current_workspace_id' => ws_id,
|
|
@@ -575,7 +575,7 @@ module Aspera
|
|
|
575
575
|
else
|
|
576
576
|
{'id' => full_recipient_info['source_id'], 'type' => full_recipient_info['source_type']}
|
|
577
577
|
end
|
|
578
|
-
else Aspera.error_unexpected_value(short_recipient_info.class.name){"#{rcpt_lst_field} item must be a String (email, shared inbox) or Hash (id,type)"}
|
|
578
|
+
else Aspera.error_unexpected_value(short_recipient_info.class.name) { "#{rcpt_lst_field} item must be a String (email, shared inbox) or Hash (id,type)" }
|
|
579
579
|
end
|
|
580
580
|
# add original or resolved recipient info
|
|
581
581
|
resolved_list.push(short_recipient_info)
|
|
@@ -669,7 +669,7 @@ module Aspera
|
|
|
669
669
|
# Console cookie
|
|
670
670
|
################
|
|
671
671
|
# we are sure that fields are not nil
|
|
672
|
-
cookie_elements = [app_info.app, current_user_info['name'] || 'public link', current_user_info['email'] || 'none'].map{ |e| Base64.strict_encode64(e)}
|
|
672
|
+
cookie_elements = [app_info.app, current_user_info['name'] || 'public link', current_user_info['email'] || 'none'].map { |e| Base64.strict_encode64(e) }
|
|
673
673
|
cookie_elements.unshift(COOKIE_PREFIX_CONSOLE_AOC)
|
|
674
674
|
transfer_spec['cookie'] = cookie_elements.join(':')
|
|
675
675
|
# Application tags
|
data/lib/aspera/api/cos_node.rb
CHANGED
|
@@ -12,19 +12,20 @@ module Aspera
|
|
|
12
12
|
IBM_CLOUD_TOKEN_URL = 'https://iam.cloud.ibm.com/identity'
|
|
13
13
|
TOKEN_FIELD = 'delegated_refresh_token'
|
|
14
14
|
FASP_INFO_KEYS = %w[ATSEndpoint AccessKey].freeze
|
|
15
|
+
SERVICE_CREDS_PARAMS = %w[apikey resource_instance_id endpoints]
|
|
15
16
|
class << self
|
|
16
17
|
def parameters_from_svc_credentials(service_credentials, bucket_region)
|
|
17
18
|
# check necessary contents
|
|
18
|
-
Aspera.assert_type(service_credentials, Hash){'service_credentials'}
|
|
19
|
+
Aspera.assert_type(service_credentials, Hash) { 'service_credentials' }
|
|
19
20
|
Log.dump(:service_credentials, service_credentials)
|
|
20
|
-
|
|
21
|
-
Aspera.assert(service_credentials.key?(field)){"service_credentials must have a field: #{field}"}
|
|
21
|
+
SERVICE_CREDS_PARAMS.each do |field|
|
|
22
|
+
Aspera.assert(service_credentials.key?(field)) { "service_credentials must have a field: #{field}" }
|
|
22
23
|
end
|
|
23
24
|
# read endpoints from service provided in service credentials
|
|
24
25
|
endpoints = Aspera::Rest.new(base_url: service_credentials['endpoints']).read('')
|
|
25
26
|
Log.dump(:endpoints, endpoints)
|
|
26
27
|
endpoint = endpoints.dig('service-endpoints', 'regional', bucket_region, 'public', bucket_region)
|
|
27
|
-
Aspera.assert(!endpoint.nil?){"no such region: #{bucket_region}"}
|
|
28
|
+
Aspera.assert(!endpoint.nil?) { "no such region: #{bucket_region}" }
|
|
28
29
|
return {
|
|
29
30
|
instance_id: service_credentials['resource_instance_id'],
|
|
30
31
|
api_key: service_credentials['apikey'],
|
|
@@ -34,8 +35,8 @@ module Aspera
|
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
def initialize(instance_id:, api_key:, endpoint:, bucket:, auth_url: IBM_CLOUD_TOKEN_URL)
|
|
37
|
-
Aspera.assert_type(instance_id, String){'resource instance id (crn)'}
|
|
38
|
-
Aspera.assert_type(endpoint, String){'endpoint'}
|
|
38
|
+
Aspera.assert_type(instance_id, String) { 'resource instance id (crn)' }
|
|
39
|
+
Aspera.assert_type(endpoint, String) { 'endpoint' }
|
|
39
40
|
endpoint = "https://#{endpoint}" unless endpoint.start_with?('http')
|
|
40
41
|
@auth_url = auth_url
|
|
41
42
|
@api_key = api_key
|
|
@@ -61,10 +62,10 @@ module Aspera
|
|
|
61
62
|
ret: :resp
|
|
62
63
|
).body
|
|
63
64
|
ats_info = XmlSimple.xml_in(xml_result_text, {'ForceArray' => false})
|
|
64
|
-
Log.dump(:ats_info, ats_info)
|
|
65
|
-
Aspera.assert_hash_all(ats_info, String, nil){'ats_info'}
|
|
65
|
+
Log.dump(:ats_info, ats_info.reject { |k, _| k == 'AccessKey' })
|
|
66
|
+
Aspera.assert_hash_all(ats_info, String, nil) { 'ats_info' }
|
|
66
67
|
Aspera.assert((FASP_INFO_KEYS - ats_info.keys).empty?, 'ats_info missing required keys')
|
|
67
|
-
Aspera.assert_hash_all(ats_info['AccessKey'], String, String){'ats_info'}
|
|
68
|
+
Aspera.assert_hash_all(ats_info['AccessKey'], String, String) { 'ats_info' }
|
|
68
69
|
@storage_credentials = {
|
|
69
70
|
'type' => 'token',
|
|
70
71
|
'token' => {TOKEN_FIELD => nil}
|
data/lib/aspera/api/faspex.rb
CHANGED
|
@@ -170,7 +170,7 @@ module Aspera
|
|
|
170
170
|
Log.dump(:final_url, final_url, level: :trace1)
|
|
171
171
|
# Get context from query
|
|
172
172
|
encoded_context = Rest.query_to_h(URI.parse(final_url).query)['context']
|
|
173
|
-
Aspera.assert(!encoded_context.nil?, type: ParameterError){'Bad faspex5 public link, missing context in query'}
|
|
173
|
+
Aspera.assert(!encoded_context.nil?, type: ParameterError) { 'Bad faspex5 public link, missing context in query' }
|
|
174
174
|
# public link information (contains passcode and allowed usage)
|
|
175
175
|
@pub_link_context = JSON.parse(Base64.decode64(encoded_context))
|
|
176
176
|
Log.dump(:pub_link_context, @pub_link_context, level: :trace1)
|
|
@@ -238,7 +238,7 @@ module Aspera
|
|
|
238
238
|
headers: {typ: 'JWT'}
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
|
-
else Aspera.error_unexpected_value(auth, type: ParameterError){'auth'}
|
|
241
|
+
else Aspera.error_unexpected_value(auth, type: ParameterError) { 'auth' }
|
|
242
242
|
end
|
|
243
243
|
)
|
|
244
244
|
end
|
data/lib/aspera/api/httpgw.rb
CHANGED
|
@@ -60,7 +60,7 @@ module Aspera
|
|
|
60
60
|
|
|
61
61
|
# send data on http gw web socket
|
|
62
62
|
def ws_send(ws_type:, data:)
|
|
63
|
-
Log.log.trace1{"#{LOG_WS_SEND}sending: #{ws_type} (#{data&.length || 0} bytes)"}
|
|
63
|
+
Log.log.trace1 { "#{LOG_WS_SEND}sending: #{ws_type} (#{data&.length || 0} bytes)" }
|
|
64
64
|
@shared_info[:count][:sent_general] += 1 if ws_type.eql?(:binary)
|
|
65
65
|
frame_generator = ::WebSocket::Frame::Outgoing::Client.new(data: data, type: ws_type, version: @ws_handshake.version)
|
|
66
66
|
@ws_io.write(frame_generator.to_s)
|
|
@@ -72,12 +72,12 @@ module Aspera
|
|
|
72
72
|
@shared_info[:read_exception].nil? &&
|
|
73
73
|
(((@shared_info[:count][:sent_general] - @shared_info[:count][:received_general]) > 1) ||
|
|
74
74
|
((@shared_info[:count][:received_v2_delimiter] - @shared_info[:count][:sent_v2_delimiter]) > 1))
|
|
75
|
-
Log.log.trace1{"#{LOG_WS_SEND}#{'timeout'.blue}: #{@shared_info[:count]}"} if !@shared_info[:cond_var].wait(@shared_info[:mutex], 2.0)
|
|
75
|
+
Log.log.trace1 { "#{LOG_WS_SEND}#{'timeout'.blue}: #{@shared_info[:count]}" } if !@shared_info[:cond_var].wait(@shared_info[:mutex], 2.0)
|
|
76
76
|
end
|
|
77
77
|
end
|
|
78
78
|
end
|
|
79
79
|
raise @shared_info[:read_exception] unless @shared_info[:read_exception].nil?
|
|
80
|
-
Log.log.trace2{"#{LOG_WS_SEND}counts: #{@shared_info[:count]}"}
|
|
80
|
+
Log.log.trace2 { "#{LOG_WS_SEND}counts: #{@shared_info[:count]}" }
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
# Check header ourself and give precise error message, as websocket will only throw error without details
|
|
@@ -91,7 +91,7 @@ module Aspera
|
|
|
91
91
|
|
|
92
92
|
# message processing for read thread
|
|
93
93
|
def process_received_message(message)
|
|
94
|
-
Log.log.debug{"#{LOG_WS_RECV}message: [#{message}] (#{message.class})"}
|
|
94
|
+
Log.log.debug { "#{LOG_WS_RECV}message: [#{message}] (#{message.class})" }
|
|
95
95
|
if message.eql?(MSG_RECV_DATA_RECEIVED_SIGNAL)
|
|
96
96
|
@shared_info[:mutex].synchronize do
|
|
97
97
|
@shared_info[:count][:received_general] += 1
|
|
@@ -119,21 +119,21 @@ module Aspera
|
|
|
119
119
|
|
|
120
120
|
# main function of read thread
|
|
121
121
|
def process_read_thread
|
|
122
|
-
Log.log.debug{"#{LOG_WS_RECV}read thread started"}
|
|
122
|
+
Log.log.debug { "#{LOG_WS_RECV}read thread started" }
|
|
123
123
|
frame_parser = ::WebSocket::Frame::Incoming::Client.new(version: @ws_handshake.version)
|
|
124
124
|
until @ws_io.eof?
|
|
125
125
|
begin
|
|
126
126
|
# ready byte by byte until frame is ready
|
|
127
127
|
# blocking read
|
|
128
128
|
byte = @ws_io.read(1)
|
|
129
|
-
Log.log.trace2{"#{LOG_WS_RECV}read: #{byte} (#{byte.class}) eof=#{@ws_io.eof?}"}
|
|
129
|
+
Log.log.trace2 { "#{LOG_WS_RECV}read: #{byte} (#{byte.class}) eof=#{@ws_io.eof?}" }
|
|
130
130
|
frame_parser << byte
|
|
131
131
|
frame_ok = frame_parser.next
|
|
132
132
|
next if frame_ok.nil?
|
|
133
133
|
process_received_message(frame_ok.data.to_s)
|
|
134
|
-
Log.log.trace2{"#{LOG_WS_RECV}counts: #{@shared_info[:count]}"}
|
|
134
|
+
Log.log.trace2 { "#{LOG_WS_RECV}counts: #{@shared_info[:count]}" }
|
|
135
135
|
rescue => e
|
|
136
|
-
Log.log.debug{"#{LOG_WS_RECV}Exception: #{e}"}
|
|
136
|
+
Log.log.debug { "#{LOG_WS_RECV}Exception: #{e}" }
|
|
137
137
|
@shared_info[:mutex].synchronize do
|
|
138
138
|
@shared_info[:read_exception] = e
|
|
139
139
|
@shared_info[:cond_var].signal
|
|
@@ -144,7 +144,7 @@ module Aspera
|
|
|
144
144
|
Log.log.debug do
|
|
145
145
|
"#{LOG_WS_RECV}exception: #{@shared_info[:read_exception]},cls=#{@shared_info[:read_exception].class})"
|
|
146
146
|
end unless @shared_info[:read_exception].nil?
|
|
147
|
-
Log.log.debug{"#{LOG_WS_RECV}read thread stopped (ws eof=#{@ws_io.eof?})"}
|
|
147
|
+
Log.log.debug { "#{LOG_WS_RECV}read thread stopped (ws eof=#{@ws_io.eof?})" }
|
|
148
148
|
end
|
|
149
149
|
|
|
150
150
|
def upload(transfer_spec)
|
|
@@ -170,7 +170,7 @@ module Aspera
|
|
|
170
170
|
# Get whole HTTP response header, Check and process
|
|
171
171
|
# no need to check `finished?` or result of `<<` (true), as we give the whole header at once
|
|
172
172
|
@ws_handshake << validated_ws_response_header(@ws_io.readuntil("\r\n\r\n"))
|
|
173
|
-
Log.log.debug{"#{LOG_WS_SEND}handshake success"}
|
|
173
|
+
Log.log.debug { "#{LOG_WS_SEND}handshake success" }
|
|
174
174
|
# data shared between main thread and read thread
|
|
175
175
|
@shared_info = {
|
|
176
176
|
read_exception: nil, # error message if any in callback
|
|
@@ -184,7 +184,7 @@ module Aspera
|
|
|
184
184
|
cond_var: ConditionVariable.new
|
|
185
185
|
}
|
|
186
186
|
# start read thread after handshake
|
|
187
|
-
@ws_read_thread = Thread.new{process_read_thread}
|
|
187
|
+
@ws_read_thread = Thread.new { process_read_thread }
|
|
188
188
|
@notify_cb&.call(:session_start, session_id: session_id)
|
|
189
189
|
@notify_cb&.call(:session_size, session_id: session_id, info: total_bytes_to_transfer)
|
|
190
190
|
sleep(1)
|
|
@@ -224,7 +224,7 @@ module Aspera
|
|
|
224
224
|
# send once, before data, at beginning
|
|
225
225
|
ws_snd_json(MSG_SEND_SLICE_UPLOAD, slice_info) if slice_info[:slice].eql?(0)
|
|
226
226
|
ws_send(ws_type: :binary, data: slice_bin_data)
|
|
227
|
-
Log.log.trace1{"#{LOG_WS_SEND}buffer: file: #{file_index}, slice: #{slice_info[:slice]}/#{last_slice}"}
|
|
227
|
+
Log.log.trace1 { "#{LOG_WS_SEND}buffer: file: #{file_index}, slice: #{slice_info[:slice]}/#{last_slice}" }
|
|
228
228
|
# send once, after data, at end
|
|
229
229
|
ws_snd_json(MSG_SEND_SLICE_UPLOAD, slice_info) if slice_info[:slice].eql?(last_slice)
|
|
230
230
|
end
|
|
@@ -232,7 +232,7 @@ module Aspera
|
|
|
232
232
|
raise @shared_info[:read_exception] unless @shared_info[:read_exception].nil?
|
|
233
233
|
raise e
|
|
234
234
|
rescue Net::ReadTimeout => e
|
|
235
|
-
Log.log.warn{'A timeout condition using HTTPGW may signal a permission problem on destination. Check ascp logs on httpgw.'}
|
|
235
|
+
Log.log.warn { 'A timeout condition using HTTPGW may signal a permission problem on destination. Check ascp logs on httpgw.' }
|
|
236
236
|
raise e
|
|
237
237
|
end
|
|
238
238
|
session_sent_bytes += slice_bin_data.length
|
|
@@ -250,7 +250,7 @@ module Aspera
|
|
|
250
250
|
ws_send(ws_type: :close, data: nil)
|
|
251
251
|
Log.log.debug("Finished upload, waiting for end of #{THR_RECV} thread.")
|
|
252
252
|
@ws_read_thread.join
|
|
253
|
-
Log.log.debug{'Read thread joined'}
|
|
253
|
+
Log.log.debug { 'Read thread joined' }
|
|
254
254
|
ensure
|
|
255
255
|
# session no more used
|
|
256
256
|
@ws_io = nil
|
|
@@ -284,7 +284,7 @@ module Aspera
|
|
|
284
284
|
File.basename(default_file_name)
|
|
285
285
|
end
|
|
286
286
|
file_path = File.join(transfer_spec['destination_root'], file_name)
|
|
287
|
-
call(operation: 'GET', subpath: "download/#{transfer_uuid}",
|
|
287
|
+
call(operation: 'GET', subpath: "download/#{transfer_uuid}", save_to: file_path)
|
|
288
288
|
end
|
|
289
289
|
|
|
290
290
|
def info
|
|
@@ -308,7 +308,7 @@ module Aspera
|
|
|
308
308
|
Log.dump(:gw_url, url)
|
|
309
309
|
# add scheme if missing
|
|
310
310
|
url = "https://#{url}" unless url.match?(%r{^[a-z]{1,6}://})
|
|
311
|
-
Aspera.assert(url.start_with?('https://'), type: Error){'GW URL shall be with scheme https'}
|
|
311
|
+
Aspera.assert(url.start_with?('https://'), type: Error) { 'GW URL shall be with scheme https' }
|
|
312
312
|
# remove trailing slash and version (o=only once) if present
|
|
313
313
|
# TODO: issue warning ?
|
|
314
314
|
url = url.chomp('/').gsub(%r{/#{API_V1}$}o, '')
|
|
@@ -326,8 +326,8 @@ module Aspera
|
|
|
326
326
|
# web socket endpoint: by default use v2 (newer gateways), without base64 encoding
|
|
327
327
|
# is the latest supported? else revert to old api
|
|
328
328
|
if !@upload_version.eql?(API_V1)
|
|
329
|
-
if !@api_info['endpoints'].any?{ |i| i.include?(@upload_version)}
|
|
330
|
-
Log.log.warn{"API version #{@upload_version} not supported, reverting to #{API_V1}"}
|
|
329
|
+
if !@api_info['endpoints'].any? { |i| i.include?(@upload_version) }
|
|
330
|
+
Log.log.warn { "API version #{@upload_version} not supported, reverting to #{API_V1}" }
|
|
331
331
|
@upload_version = API_V1
|
|
332
332
|
end
|
|
333
333
|
end
|
|
@@ -386,7 +386,7 @@ module Aspera
|
|
|
386
386
|
size: file_size
|
|
387
387
|
})
|
|
388
388
|
else
|
|
389
|
-
Log.log.warn{"Ignoring non file/directory: #{entry_path}"}
|
|
389
|
+
Log.log.warn { "Ignoring non file/directory: #{entry_path}" }
|
|
390
390
|
end
|
|
391
391
|
end
|
|
392
392
|
end
|
|
@@ -394,7 +394,7 @@ module Aspera
|
|
|
394
394
|
raise "File not found: #{source_path}"
|
|
395
395
|
end
|
|
396
396
|
end
|
|
397
|
-
transfer_spec['paths'] = files_to_send.map{ |i| {'source' => i[:name]}}
|
|
397
|
+
transfer_spec['paths'] = files_to_send.map { |i| {'source' => i[:name]} }
|
|
398
398
|
files_to_send.push(total_bytes_to_transfer)
|
|
399
399
|
return files_to_send
|
|
400
400
|
end
|