aspera-cli 4.11.0 → 4.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/BUGS.md +0 -1
- data/CHANGELOG.md +71 -52
- data/CONTRIBUTING.md +31 -6
- data/README.md +404 -259
- data/bin/asession +2 -2
- data/docs/test_env.conf +1 -0
- data/examples/aoc.rb +2 -2
- data/examples/dascli +11 -11
- data/examples/faspex4.rb +7 -7
- data/examples/node.rb +1 -1
- data/examples/proxy.pac +2 -2
- data/examples/server.rb +3 -3
- data/lib/aspera/aoc.rb +105 -40
- data/lib/aspera/cli/extended_value.rb +4 -4
- data/lib/aspera/cli/{formater.rb → formatter.rb} +7 -7
- data/lib/aspera/cli/listener/progress.rb +1 -1
- data/lib/aspera/cli/listener/progress_multi.rb +2 -2
- data/lib/aspera/cli/main.rb +18 -18
- data/lib/aspera/cli/manager.rb +5 -5
- data/lib/aspera/cli/plugin.rb +23 -20
- data/lib/aspera/cli/plugins/aoc.rb +75 -112
- data/lib/aspera/cli/plugins/ats.rb +6 -6
- data/lib/aspera/cli/plugins/config.rb +84 -83
- data/lib/aspera/cli/plugins/cos.rb +1 -1
- data/lib/aspera/cli/plugins/faspex.rb +38 -38
- data/lib/aspera/cli/plugins/faspex5.rb +187 -43
- data/lib/aspera/cli/plugins/node.rb +30 -37
- data/lib/aspera/cli/plugins/orchestrator.rb +7 -4
- data/lib/aspera/cli/plugins/preview.rb +10 -9
- data/lib/aspera/cli/plugins/server.rb +1 -1
- data/lib/aspera/cli/plugins/shares.rb +67 -43
- data/lib/aspera/cli/transfer_agent.rb +16 -16
- data/lib/aspera/cli/version.rb +2 -1
- data/lib/aspera/command_line_builder.rb +70 -66
- data/lib/aspera/cos_node.rb +9 -9
- data/lib/aspera/fasp/agent_base.rb +3 -1
- data/lib/aspera/fasp/agent_connect.rb +23 -23
- data/lib/aspera/fasp/agent_direct.rb +13 -14
- data/lib/aspera/fasp/agent_httpgw.rb +20 -19
- data/lib/aspera/fasp/agent_node.rb +13 -15
- data/lib/aspera/fasp/agent_trsdk.rb +1 -1
- data/lib/aspera/fasp/installation.rb +5 -5
- data/lib/aspera/fasp/listener.rb +1 -1
- data/lib/aspera/fasp/parameters.rb +49 -41
- data/lib/aspera/fasp/parameters.yaml +311 -212
- data/lib/aspera/fasp/resume_policy.rb +2 -2
- data/lib/aspera/fasp/transfer_spec.rb +0 -13
- data/lib/aspera/faspex_gw.rb +80 -161
- data/lib/aspera/faspex_postproc.rb +77 -0
- data/lib/aspera/log.rb +7 -7
- data/lib/aspera/nagios.rb +6 -6
- data/lib/aspera/node.rb +24 -19
- data/lib/aspera/oauth.rb +50 -47
- data/lib/aspera/proxy_auto_config.js +22 -22
- data/lib/aspera/proxy_auto_config.rb +3 -3
- data/lib/aspera/rest.rb +12 -10
- data/lib/aspera/rest_error_analyzer.rb +5 -5
- data/lib/aspera/secret_hider.rb +4 -3
- data/lib/aspera/ssh.rb +4 -4
- data/lib/aspera/sync.rb +37 -36
- data/lib/aspera/web_auth.rb +7 -59
- data/lib/aspera/web_server_simple.rb +76 -0
- data.tar.gz.sig +0 -0
- metadata +6 -4
- metadata.gz.sig +0 -0
@@ -1,28 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# spellchecker: ignore workgroups,mypackages
|
4
|
+
|
3
5
|
require 'aspera/cli/basic_auth_plugin'
|
4
6
|
require 'aspera/persistency_action_once'
|
5
7
|
require 'aspera/id_generator'
|
6
8
|
require 'aspera/nagios'
|
9
|
+
require 'aspera/environment'
|
7
10
|
require 'securerandom'
|
11
|
+
require 'ruby-progressbar'
|
12
|
+
require 'tty-spinner'
|
8
13
|
|
9
14
|
module Aspera
|
10
15
|
module Cli
|
11
16
|
module Plugins
|
12
17
|
class Faspex5 < Aspera::Cli::BasicAuthPlugin
|
18
|
+
RECIPIENT_TYPES = %w[user workgroup external_user distribution_list shared_inbox].freeze
|
19
|
+
PACKAGE_TERMINATED = %w[completed failed].freeze
|
20
|
+
API_DETECT = 'api/v5/configuration/ping'
|
13
21
|
class << self
|
14
22
|
def detect(base_url)
|
15
23
|
api = Rest.new(base_url: base_url, redirect_max: 1)
|
16
|
-
result = api.read(
|
24
|
+
result = api.read(API_DETECT)
|
17
25
|
if result[:http].code.start_with?('2') && result[:http].body.strip.empty?
|
18
|
-
return {version: '5'}
|
26
|
+
return {version: '5', url: result[:http].uri.to_s[0..-(API_DETECT.length + 2)]}
|
19
27
|
end
|
20
28
|
return nil
|
21
29
|
end
|
22
30
|
end
|
23
31
|
|
24
32
|
TRANSFER_CONNECT = 'connect'
|
25
|
-
private_constant :TRANSFER_CONNECT
|
26
33
|
|
27
34
|
def initialize(env)
|
28
35
|
super(env)
|
@@ -32,42 +39,43 @@ module Aspera
|
|
32
39
|
options.add_opt_list(:auth, [:boot].concat(Oauth::STD_AUTH_TYPES), 'OAuth type of authentication')
|
33
40
|
options.add_opt_simple(:private_key, 'OAuth JWT RSA private key PEM value (prefix file path with @file:)')
|
34
41
|
options.add_opt_simple(:passphrase, 'RSA private key passphrase')
|
42
|
+
options.add_opt_simple(:shared_folder, 'Shared folder source for package files')
|
35
43
|
options.set_option(:auth, :jwt)
|
36
44
|
options.parse_options!
|
37
45
|
end
|
38
46
|
|
39
47
|
def set_api
|
40
|
-
@
|
41
|
-
@
|
42
|
-
|
48
|
+
@faspex5_api_base_url = options.get_option(:url, is_type: :mandatory).gsub(%r{/+$}, '')
|
49
|
+
@faspex5_api_auth_url = "#{@faspex5_api_base_url}/auth"
|
50
|
+
faspex5_api_v5_url = "#{@faspex5_api_base_url}/api/v5"
|
43
51
|
case options.get_option(:auth, is_type: :mandatory)
|
44
52
|
when :boot
|
45
53
|
# the password here is the token copied directly from browser in developer mode
|
46
54
|
@api_v5 = Rest.new({
|
47
|
-
base_url:
|
55
|
+
base_url: faspex5_api_v5_url,
|
48
56
|
headers: {'Authorization' => options.get_option(:password, is_type: :mandatory)}
|
49
57
|
})
|
50
58
|
when :web
|
51
59
|
# opens a browser and ask user to auth using web
|
52
60
|
@api_v5 = Rest.new({
|
53
|
-
base_url:
|
61
|
+
base_url: faspex5_api_v5_url,
|
54
62
|
auth: {
|
55
|
-
type:
|
56
|
-
base_url:
|
57
|
-
|
58
|
-
client_id:
|
59
|
-
web:
|
63
|
+
type: :oauth2,
|
64
|
+
base_url: @faspex5_api_auth_url,
|
65
|
+
grant_method: :web,
|
66
|
+
client_id: options.get_option(:client_id, is_type: :mandatory),
|
67
|
+
web: {redirect_uri: options.get_option(:redirect_uri, is_type: :mandatory)}
|
60
68
|
}})
|
61
69
|
when :jwt
|
62
70
|
app_client_id = options.get_option(:client_id, is_type: :mandatory)
|
63
71
|
@api_v5 = Rest.new({
|
64
|
-
base_url:
|
72
|
+
base_url: faspex5_api_v5_url,
|
65
73
|
auth: {
|
66
|
-
type:
|
67
|
-
base_url:
|
68
|
-
|
69
|
-
client_id:
|
70
|
-
jwt:
|
74
|
+
type: :oauth2,
|
75
|
+
base_url: @faspex5_api_auth_url,
|
76
|
+
grant_method: :jwt,
|
77
|
+
client_id: app_client_id,
|
78
|
+
jwt: {
|
71
79
|
payload: {
|
72
80
|
iss: app_client_id, # issuer
|
73
81
|
aud: app_client_id, # audience TODO: ???
|
@@ -81,18 +89,77 @@ module Aspera
|
|
81
89
|
end
|
82
90
|
end
|
83
91
|
|
84
|
-
|
92
|
+
def normalize_recipients(parameters)
|
93
|
+
return unless parameters.key?('recipients')
|
94
|
+
raise 'Field recipients must be an Array' unless parameters['recipients'].is_a?(Array)
|
95
|
+
parameters['recipients'] = parameters['recipients'].map do |recipient_data|
|
96
|
+
# if just a string, assume it is the name
|
97
|
+
if recipient_data.is_a?(String)
|
98
|
+
result = @api_v5.read('contacts', {q: recipient_data, context: 'packages', type: [Rest::ARRAY_PARAMS, *RECIPIENT_TYPES]})[:data]
|
99
|
+
raise "No matching contact for #{recipient_data}" if result.empty?
|
100
|
+
raise "Multiple matching contact for #{recipient_data} : #{result['contacts'].map{|i|i['name']}.join(', ')}" unless 1.eql?(result['total_count'])
|
101
|
+
matched = result['contacts'].first
|
102
|
+
recipient_data = {
|
103
|
+
name: matched['name'],
|
104
|
+
recipient_type: matched['type']
|
105
|
+
}
|
106
|
+
end
|
107
|
+
# result for mapping
|
108
|
+
recipient_data
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def wait_for_complete_upload(id)
|
113
|
+
parameters = options.get_option(:value)
|
114
|
+
spinner = nil
|
115
|
+
progress = nil
|
116
|
+
while true
|
117
|
+
status = @api_v5.read("packages/#{id}/upload_details")[:data]
|
118
|
+
# user asked to not follow
|
119
|
+
break unless parameters
|
120
|
+
if status['upload_status'].eql?('submitted')
|
121
|
+
if spinner.nil?
|
122
|
+
spinner = TTY::Spinner.new('[:spinner] :title', format: :classic)
|
123
|
+
spinner.start
|
124
|
+
end
|
125
|
+
spinner.update(title: status['upload_status'])
|
126
|
+
spinner.spin
|
127
|
+
elsif progress.nil?
|
128
|
+
progress = ProgressBar.create(
|
129
|
+
format: '%a %B %p%% %r Mbps %e',
|
130
|
+
rate_scale: lambda{|rate|rate / Environment::BYTES_PER_MEBIBIT},
|
131
|
+
title: 'progress',
|
132
|
+
total: status['bytes_total'].to_i)
|
133
|
+
else
|
134
|
+
progress.progress = status['bytes_written'].to_i
|
135
|
+
end
|
136
|
+
break if PACKAGE_TERMINATED.include?(status['upload_status'])
|
137
|
+
sleep(0.5)
|
138
|
+
end
|
139
|
+
status['id'] = id
|
140
|
+
return status
|
141
|
+
end
|
142
|
+
|
143
|
+
def lookup_entity(entity_type, property, value)
|
144
|
+
# TODO: what if too many, use paging ?
|
145
|
+
all = @api_v5.read(entity_type)[:data][entity_type]
|
146
|
+
found = all.find{|i|i[property].eql?(value)}
|
147
|
+
raise "No #{entity_type} with #{property} = #{value}" if found.nil?
|
148
|
+
return found
|
149
|
+
end
|
150
|
+
|
151
|
+
ACTIONS = %i[health version user bearer_token package shared_folders admin gateway postprocessing].freeze
|
85
152
|
|
86
153
|
def execute_action
|
87
|
-
set_api
|
88
154
|
command = options.get_next_command(ACTIONS)
|
155
|
+
set_api unless command.eql?(:postprocessing)
|
89
156
|
case command
|
90
157
|
when :version
|
91
158
|
return { type: :single_object, data: @api_v5.read('version')[:data] }
|
92
159
|
when :health
|
93
160
|
nagios = Nagios.new
|
94
161
|
begin
|
95
|
-
result = Rest.new(base_url: @
|
162
|
+
result = Rest.new(base_url: @faspex5_api_base_url).read('health')[:data]
|
96
163
|
result.each do |k, v|
|
97
164
|
nagios.add_ok(k, v.to_s)
|
98
165
|
end
|
@@ -114,7 +181,7 @@ module Aspera
|
|
114
181
|
when :bearer_token
|
115
182
|
return {type: :text, data: @api_v5.oauth_token}
|
116
183
|
when :package
|
117
|
-
command = options.get_next_command(%i[list show send receive])
|
184
|
+
command = options.get_next_command(%i[list show send receive status])
|
118
185
|
case command
|
119
186
|
when :list
|
120
187
|
parameters = options.get_option(:value)
|
@@ -126,20 +193,40 @@ module Aspera
|
|
126
193
|
when :show
|
127
194
|
id = instance_identifier
|
128
195
|
return {type: :single_object, data: @api_v5.read("packages/#{id}")[:data]}
|
196
|
+
when :status
|
197
|
+
status = wait_for_complete_upload(instance_identifier)
|
198
|
+
return {type: :single_object, data: status}
|
129
199
|
when :send
|
130
200
|
parameters = options.get_option(:value, is_type: :mandatory)
|
131
|
-
raise CliBadArgument, '
|
201
|
+
raise CliBadArgument, 'Value must be Hash, refer to API' unless parameters.is_a?(Hash)
|
202
|
+
normalize_recipients(parameters)
|
132
203
|
package = @api_v5.create('packages', parameters)[:data]
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
204
|
+
shared_folder = options.get_option(:shared_folder)
|
205
|
+
if shared_folder.nil?
|
206
|
+
# TODO: option to send from remote source or httpgw
|
207
|
+
transfer_spec = @api_v5.call(
|
208
|
+
operation: 'POST',
|
209
|
+
subpath: "packages/#{package['id']}/transfer_spec/upload",
|
210
|
+
headers: {'Accept' => 'application/json'},
|
211
|
+
url_params: {transfer_type: TRANSFER_CONNECT},
|
212
|
+
json_params: {paths: transfer.source_list}
|
213
|
+
)[:data]
|
214
|
+
transfer_spec.delete('authentication')
|
215
|
+
return Main.result_transfer(transfer.start(transfer_spec))
|
216
|
+
else
|
217
|
+
if !shared_folder.to_i.to_s.eql?(shared_folder)
|
218
|
+
shared_folder = lookup_entity('shared_folders', 'name', shared_folder)['id']
|
219
|
+
end
|
220
|
+
transfer_request = {shared_folder_id: shared_folder, paths: transfer.source_list}
|
221
|
+
# start remote transfer and get first status
|
222
|
+
result = @api_v5.create("packages/#{package['id']}/remote_transfer", transfer_request)[:data]
|
223
|
+
result['id'] = package['id']
|
224
|
+
unless result['status'].eql?('completed')
|
225
|
+
formatter.display_status("Package #{package['id']}")
|
226
|
+
result = wait_for_complete_upload(package['id'])
|
227
|
+
end
|
228
|
+
return {type: :single_object, data: result}
|
229
|
+
end
|
143
230
|
when :receive
|
144
231
|
pkg_type = 'received'
|
145
232
|
pack_id = instance_identifier
|
@@ -166,39 +253,67 @@ module Aspera
|
|
166
253
|
package_ids.reject!{|i|skip_ids_data.include?(i)}
|
167
254
|
end
|
168
255
|
result_transfer = []
|
169
|
-
package_ids.each do |
|
256
|
+
package_ids.each do |pkg_id|
|
170
257
|
param_file_list = {}
|
171
258
|
begin
|
172
|
-
param_file_list['paths'] = transfer.
|
259
|
+
param_file_list['paths'] = transfer.source_list
|
173
260
|
rescue Aspera::Cli::CliBadArgument
|
174
261
|
# paths is optional
|
175
262
|
end
|
176
263
|
# TODO: allow from sent as well ?
|
177
264
|
transfer_spec = @api_v5.call(
|
178
265
|
operation: 'POST',
|
179
|
-
subpath: "packages/#{
|
266
|
+
subpath: "packages/#{pkg_id}/transfer_spec/download",
|
180
267
|
headers: {'Accept' => 'application/json'},
|
181
268
|
url_params: {transfer_type: TRANSFER_CONNECT, type: pkg_type},
|
182
269
|
json_params: param_file_list
|
183
270
|
)[:data]
|
184
271
|
transfer_spec.delete('authentication')
|
185
272
|
statuses = transfer.start(transfer_spec)
|
186
|
-
result_transfer.push({'package' =>
|
273
|
+
result_transfer.push({'package' => pkg_id, Main::STATUS_FIELD => statuses})
|
187
274
|
# skip only if all sessions completed
|
188
|
-
skip_ids_data.push(
|
275
|
+
skip_ids_data.push(pkg_id) if TransferAgent.session_status(statuses).eql?(:success)
|
189
276
|
end
|
190
277
|
skip_ids_persistency&.save
|
191
278
|
return Main.result_transfer_multiple(result_transfer)
|
192
279
|
end # case package
|
280
|
+
when :shared_folders
|
281
|
+
all_shared_folders = @api_v5.read('shared_folders')[:data]['shared_folders']
|
282
|
+
case options.get_next_command(%i[list browse])
|
283
|
+
when :list
|
284
|
+
return {type: :object_list, data: all_shared_folders}
|
285
|
+
when :browse
|
286
|
+
shared_folder_id = instance_identifier
|
287
|
+
path = options.get_next_argument('folder path', mandatory: false) || '/'
|
288
|
+
node = all_shared_folders.find{|i|i['id'].eql?(shared_folder_id)}
|
289
|
+
raise "No such shared folder id #{shared_folder_id}" if node.nil?
|
290
|
+
result = @api_v5.call({
|
291
|
+
operation: 'POST',
|
292
|
+
subpath: "nodes/#{node['node_id']}/shared_folders/#{shared_folder_id}/browse",
|
293
|
+
headers: {'Accept' => 'application/json', 'Content-Type' => 'application/json'},
|
294
|
+
json_params: {'path': path, 'filters': {'basenames': []}},
|
295
|
+
url_params: {offset: 0, limit: 100}
|
296
|
+
})[:data]
|
297
|
+
if result.key?('items')
|
298
|
+
return {type: :object_list, data: result['items']}
|
299
|
+
else
|
300
|
+
return {type: :single_object, data: result['self']}
|
301
|
+
end
|
302
|
+
end
|
193
303
|
when :admin
|
194
|
-
case options.get_next_command([
|
304
|
+
case options.get_next_command(%i[resource])
|
195
305
|
when :resource
|
196
|
-
res_type = options.get_next_command(%i[accounts contacts jobs workgroups shared_inboxes nodes oauth_clients registrations saml_configs metadata_profiles
|
306
|
+
res_type = options.get_next_command(%i[accounts contacts jobs workgroups shared_inboxes nodes oauth_clients registrations saml_configs metadata_profiles
|
307
|
+
email_notifications])
|
197
308
|
res_path = list_key = res_type.to_s
|
309
|
+
id_as_arg = false
|
198
310
|
case res_type
|
199
311
|
when :metadata_profiles
|
200
312
|
res_path = 'configuration/metadata_profiles'
|
201
313
|
list_key = 'profiles'
|
314
|
+
when :email_notifications
|
315
|
+
list_key = false
|
316
|
+
id_as_arg = 'type'
|
202
317
|
end
|
203
318
|
display_fields =
|
204
319
|
case res_type
|
@@ -207,10 +322,39 @@ module Aspera
|
|
207
322
|
end
|
208
323
|
adm_api = @api_v5
|
209
324
|
if res_type.eql?(:oauth_clients)
|
210
|
-
adm_api = Rest.new(@api_v5.params.merge({base_url: @
|
325
|
+
adm_api = Rest.new(@api_v5.params.merge({base_url: @faspex5_api_auth_url}))
|
211
326
|
end
|
212
|
-
return entity_action(adm_api, res_path, item_list_key: list_key, display_fields: display_fields)
|
327
|
+
return entity_action(adm_api, res_path, item_list_key: list_key, display_fields: display_fields, id_as_arg: id_as_arg)
|
213
328
|
end
|
329
|
+
when :gateway
|
330
|
+
require 'aspera/faspex_gw'
|
331
|
+
url = options.get_option(:value, is_type: :mandatory)
|
332
|
+
uri = URI.parse(url)
|
333
|
+
server = WebServerSimple.new(uri)
|
334
|
+
server.mount(uri.path, Faspex4GWServlet, @api_v5, nil)
|
335
|
+
trap('INT') { server.shutdown }
|
336
|
+
formatter.display_status("Faspex 4 gateway listening on #{url}")
|
337
|
+
Log.log.info("Listening on #{url}")
|
338
|
+
# this is blocking until server exits
|
339
|
+
server.start
|
340
|
+
return Main.result_status('Gateway terminated')
|
341
|
+
when :postprocessing
|
342
|
+
require 'aspera/faspex_postproc'
|
343
|
+
parameters = options.get_option(:value, is_type: :mandatory)
|
344
|
+
raise 'parameters must be Hash' unless parameters.is_a?(Hash)
|
345
|
+
parameters = parameters.symbolize_keys
|
346
|
+
raise 'Missing key: url' unless parameters.key?(:url)
|
347
|
+
uri = URI.parse(parameters[:url])
|
348
|
+
parameters[:processing] ||= {}
|
349
|
+
parameters[:processing][:root] = uri.path
|
350
|
+
server = WebServerSimple.new(uri, certificate: parameters[:certificate])
|
351
|
+
server.mount(uri.path, Faspex4PostProcServlet, parameters[:processing])
|
352
|
+
trap('INT') { server.shutdown }
|
353
|
+
formatter.display_status("Faspex 4 post processing listening on #{uri.port}")
|
354
|
+
Log.log.info("Listening on #{uri.port}")
|
355
|
+
# this is blocking until server exits
|
356
|
+
server.start
|
357
|
+
return Main.result_status('Gateway terminated')
|
214
358
|
end # case command
|
215
359
|
end # action
|
216
360
|
end # Faspex5
|
@@ -40,12 +40,14 @@ module Aspera
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
+
# spellchecker: disable
|
43
44
|
# SOAP API call to test central API
|
44
45
|
CENTRAL_SOAP_API_TEST = '<?xml version="1.0" encoding="UTF-8"?>'\
|
45
46
|
'<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="urn:Aspera:XML:FASPSessionNET:2009/11:Types">'\
|
46
47
|
'<soapenv:Header></soapenv:Header>'\
|
47
48
|
'<soapenv:Body><typ:GetSessionInfoRequest><SessionFilter><SessionStatus>running</SessionStatus></SessionFilter></typ:GetSessionInfoRequest></soapenv:Body>'\
|
48
49
|
'</soapenv:Envelope>'
|
50
|
+
# spellchecker: enable
|
49
51
|
|
50
52
|
# fields removed in result of search
|
51
53
|
SEARCH_REMOVE_FIELDS = %w[basename permissions].freeze
|
@@ -88,8 +90,8 @@ module Aspera
|
|
88
90
|
Aspera::Node.new(params: {
|
89
91
|
base_url: options.get_option(:url, is_type: :mandatory),
|
90
92
|
headers: {
|
91
|
-
Aspera::Node::
|
92
|
-
'Authorization'
|
93
|
+
Aspera::Node::HEADER_X_ASPERA_ACCESS_KEY => options.get_option(:username, is_type: :mandatory),
|
94
|
+
'Authorization' => options.get_option(:password, is_type: :mandatory)
|
93
95
|
}
|
94
96
|
})
|
95
97
|
else
|
@@ -141,10 +143,10 @@ module Aspera
|
|
141
143
|
end
|
142
144
|
|
143
145
|
# translates paths results into CLI result, and removes prefix
|
144
|
-
def c_result_translate_rem_prefix(
|
146
|
+
def c_result_translate_rem_prefix(response, type, success_msg, path_prefix)
|
145
147
|
errors = []
|
146
148
|
resres = { data: [], type: :object_list, fields: [type, 'result']}
|
147
|
-
JSON.parse(
|
149
|
+
JSON.parse(response[:http].body)['paths'].each do |p|
|
148
150
|
result = success_msg
|
149
151
|
if p.key?('error')
|
150
152
|
Log.log.error{"#{p['error']['user_message']} : #{p['path']}"}
|
@@ -162,10 +164,10 @@ module Aspera
|
|
162
164
|
|
163
165
|
# get path arguments from command line, and add prefix
|
164
166
|
def get_next_arg_add_prefix(path_prefix, name, number=:single)
|
165
|
-
|
166
|
-
return
|
167
|
-
return File.join(path_prefix,
|
168
|
-
return
|
167
|
+
path_or_list = options.get_next_argument(name, expected: number)
|
168
|
+
return path_or_list if path_prefix.nil?
|
169
|
+
return File.join(path_prefix, path_or_list) if path_or_list.is_a?(String)
|
170
|
+
return path_or_list.map {|p| File.join(path_prefix, p)} if path_or_list.is_a?(Array)
|
169
171
|
raise StandardError, 'expect: nil, String or Array'
|
170
172
|
end
|
171
173
|
|
@@ -185,8 +187,8 @@ module Aspera
|
|
185
187
|
result = { type: :object_list, data: resp[:data]['items']}
|
186
188
|
return Main.result_empty if result[:data].empty?
|
187
189
|
result[:fields] = result[:data].first.keys.reject{|i|SEARCH_REMOVE_FIELDS.include?(i)}
|
188
|
-
|
189
|
-
|
190
|
+
formatter.display_status("Items: #{resp[:data]['item_count']}/#{resp[:data]['total_count']}")
|
191
|
+
formatter.display_status("params: #{resp[:data]['parameters'].keys.map{|k|"#{k}:#{resp[:data]['parameters'][k]}"}.join(',')}")
|
190
192
|
return c_result_remove_prefix_path(result, 'path', prefix_path)
|
191
193
|
when :space
|
192
194
|
path_list = get_next_arg_add_prefix(prefix_path, 'folder path or ext.val. list')
|
@@ -217,8 +219,7 @@ module Aspera
|
|
217
219
|
resp = @api_node.create('files/rename', { 'paths' => [{ 'path' => path_base, 'source' => path_src, 'destination' => path_dst }] })
|
218
220
|
return c_result_translate_rem_prefix(resp, 'entry', 'moved', prefix_path)
|
219
221
|
when :browse
|
220
|
-
|
221
|
-
query = { path: thepath}
|
222
|
+
query = { path: get_next_arg_add_prefix(prefix_path, 'path')}
|
222
223
|
additional_query = options.get_option(:query)
|
223
224
|
query.merge!(additional_query) unless additional_query.nil?
|
224
225
|
send_result = @api_node.create('files/browse', query)[:data]
|
@@ -227,7 +228,7 @@ module Aspera
|
|
227
228
|
case send_result['self']['type']
|
228
229
|
when 'directory', 'container' # directory: node, container: shares
|
229
230
|
result = { data: send_result['items'], type: :object_list, textify: lambda { |table_data| c_textify_browse(table_data) } }
|
230
|
-
|
231
|
+
formatter.display_status("Items: #{send_result['item_count']}/#{send_result['total_count']}")
|
231
232
|
else # 'file','symbolic_link'
|
232
233
|
result = { data: send_result['self'], type: :single_object}
|
233
234
|
# result={ data: [send_result['self']] , type: :object_list, textify: lambda { |table_data| c_textify_browse(table_data) } }
|
@@ -351,7 +352,7 @@ module Aspera
|
|
351
352
|
case command_perm
|
352
353
|
when :list
|
353
354
|
# generic options : TODO: as arg ? option_url_query
|
354
|
-
list_options ||= {'include' => [
|
355
|
+
list_options ||= {'include' => [Rest::ARRAY_PARAMS, 'access_level', 'permission_count']}
|
355
356
|
# add which one to get
|
356
357
|
list_options['file_id'] = apifid[:file_id]
|
357
358
|
list_options['inherited'] ||= false
|
@@ -392,8 +393,7 @@ module Aspera
|
|
392
393
|
apifid = @api_node.resolve_api_fid(top_file_id, '')
|
393
394
|
return Node.new(@agents.merge(skip_basic_auth_options: true, skip_node_options: true, node_api: apifid[:api])).execute_action(command_legacy)
|
394
395
|
when :node_info, :bearer_token_node
|
395
|
-
|
396
|
-
apifid = @api_node.resolve_api_fid(top_file_id, thepath)
|
396
|
+
apifid = @api_node.resolve_api_fid(top_file_id, options.get_next_argument('path'))
|
397
397
|
result = {
|
398
398
|
url: apifid[:api].params[:base_url],
|
399
399
|
root_id: apifid[:file_id]
|
@@ -404,7 +404,7 @@ module Aspera
|
|
404
404
|
result[:username] = apifid[:api].params[:auth][:username]
|
405
405
|
result[:password] = apifid[:api].params[:auth][:password]
|
406
406
|
when :oauth2
|
407
|
-
result[:username] = apifid[:api].params[:headers][Aspera::Node::
|
407
|
+
result[:username] = apifid[:api].params[:headers][Aspera::Node::HEADER_X_ASPERA_ACCESS_KEY]
|
408
408
|
result[:password] = apifid[:api].oauth_token
|
409
409
|
else raise 'unknown'
|
410
410
|
end
|
@@ -412,38 +412,34 @@ module Aspera
|
|
412
412
|
raise 'not bearer token' unless result[:password].start_with?('Bearer ')
|
413
413
|
return Main.result_status(result[:password])
|
414
414
|
when :browse
|
415
|
-
|
416
|
-
apifid = @api_node.resolve_api_fid(top_file_id, thepath)
|
415
|
+
apifid = @api_node.resolve_api_fid(top_file_id, options.get_next_argument('path'))
|
417
416
|
file_info = apifid[:api].read("files/#{apifid[:file_id]}")[:data]
|
418
417
|
if file_info['type'].eql?('folder')
|
419
418
|
result = apifid[:api].read("files/#{apifid[:file_id]}/files", options.get_option(:value))
|
420
419
|
items = result[:data]
|
421
|
-
|
420
|
+
formatter.display_status("Items: #{result[:data].length}/#{result[:http]['X-Total-Count']}")
|
422
421
|
else
|
423
422
|
items = [file_info]
|
424
423
|
end
|
425
424
|
return {type: :object_list, data: items, fields: %w[name type recursive_size size modified_time access_level]}
|
426
425
|
when :find
|
427
|
-
|
428
|
-
apifid = @api_node.resolve_api_fid(top_file_id, thepath)
|
426
|
+
apifid = @api_node.resolve_api_fid(top_file_id, options.get_next_argument('path'))
|
429
427
|
test_block = Aspera::Node.file_matcher(options.get_option(:value))
|
430
428
|
return {type: :object_list, data: @api_node.find_files(apifid[:file_id], test_block), fields: ['path']}
|
431
429
|
when :mkdir
|
432
|
-
|
433
|
-
containing_folder_path = thepath.split(Aspera::Node::PATH_SEPARATOR)
|
430
|
+
containing_folder_path = options.get_next_argument('path').split(Aspera::Node::PATH_SEPARATOR)
|
434
431
|
new_folder = containing_folder_path.pop
|
435
432
|
apifid = @api_node.resolve_api_fid(top_file_id, containing_folder_path.join(Aspera::Node::PATH_SEPARATOR))
|
436
433
|
result = apifid[:api].create("files/#{apifid[:file_id]}/files", {name: new_folder, type: :folder})[:data]
|
437
434
|
return Main.result_status("created: #{result['name']} (id=#{result['id']})")
|
438
435
|
when :rename
|
439
|
-
|
436
|
+
file_path = options.get_next_argument('source path')
|
440
437
|
newname = options.get_next_argument('new name')
|
441
|
-
apifid = @api_node.resolve_api_fid(top_file_id,
|
438
|
+
apifid = @api_node.resolve_api_fid(top_file_id, file_path)
|
442
439
|
result = apifid[:api].update("files/#{apifid[:file_id]}", {name: newname})[:data]
|
443
|
-
return Main.result_status("renamed #{
|
440
|
+
return Main.result_status("renamed #{file_path} to #{newname}")
|
444
441
|
when :delete
|
445
|
-
|
446
|
-
return do_bulk_operation(thepath, 'deleted', id_result: 'path') do |l_path|
|
442
|
+
return do_bulk_operation(options.get_next_argument('path'), 'deleted', id_result: 'path') do |l_path|
|
447
443
|
raise "expecting String (path), got #{l_path.class.name} (#{l_path})" unless l_path.is_a?(String)
|
448
444
|
apifid = @api_node.resolve_api_fid(top_file_id, l_path)
|
449
445
|
result = apifid[:api].delete("files/#{apifid[:file_id]}")[:data]
|
@@ -517,7 +513,7 @@ module Aspera
|
|
517
513
|
asyncname = options.get_option(:sync_name)
|
518
514
|
if asyncname.nil?
|
519
515
|
asyncid = instance_identifier
|
520
|
-
if asyncid.eql?(
|
516
|
+
if asyncid.eql?(VAL_ALL) && %i[show delete].include?(command)
|
521
517
|
asyncids = @api_node.read('async/list')[:data]['sync_ids']
|
522
518
|
else
|
523
519
|
Integer(asyncid) # must be integer
|
@@ -540,7 +536,7 @@ module Aspera
|
|
540
536
|
when :show
|
541
537
|
resp = @api_node.create('async/summary', pdata)[:data]['sync_summaries']
|
542
538
|
return Main.result_empty if resp.empty?
|
543
|
-
return { type: :object_list, data: resp, fields: %w[snid name local_dir remote_dir] } if asyncid.eql?(
|
539
|
+
return { type: :object_list, data: resp, fields: %w[snid name local_dir remote_dir] } if asyncid.eql?(VAL_ALL)
|
544
540
|
return { type: :single_object, data: resp.first }
|
545
541
|
when :delete
|
546
542
|
resp = @api_node.create('async/delete', pdata)[:data]
|
@@ -628,16 +624,13 @@ module Aspera
|
|
628
624
|
resp = @api_node.create('streams', options.get_option(:value, is_type: :mandatory))
|
629
625
|
return { type: :single_object, data: resp[:data] }
|
630
626
|
when :show
|
631
|
-
|
632
|
-
resp = @api_node.read('ops/transfers/' + trid)
|
627
|
+
resp = @api_node.read("ops/transfers/#{options.get_next_argument('transfer id')}")
|
633
628
|
return { type: :other_struct, data: resp[:data] }
|
634
629
|
when :modify
|
635
|
-
|
636
|
-
resp = @api_node.update('streams/' + trid, options.get_option(:value, is_type: :mandatory))
|
630
|
+
resp = @api_node.update("streams/#{options.get_next_argument('transfer id')}", options.get_option(:value, is_type: :mandatory))
|
637
631
|
return { type: :other_struct, data: resp[:data] }
|
638
632
|
when :cancel
|
639
|
-
|
640
|
-
resp = @api_node.cancel('streams/' + trid)
|
633
|
+
resp = @api_node.cancel("streams/#{options.get_next_argument('transfer id')}")
|
641
634
|
return { type: :other_struct, data: resp[:data] }
|
642
635
|
else
|
643
636
|
raise 'error'
|
@@ -123,13 +123,16 @@ module Aspera
|
|
123
123
|
case command
|
124
124
|
when :status
|
125
125
|
options = {}
|
126
|
-
options[:id] = wf_id unless wf_id.eql?(
|
126
|
+
options[:id] = wf_id unless wf_id.eql?(VAL_ALL)
|
127
127
|
result = call_ao('workflows_status', options)[:data]
|
128
128
|
return {type: :object_list, data: result['workflows']['workflow']}
|
129
129
|
when :list
|
130
130
|
result = call_ao('workflows_list', id: 0)[:data]
|
131
|
-
return {
|
132
|
-
|
131
|
+
return {
|
132
|
+
type: :object_list,
|
133
|
+
data: result['workflows']['workflow'],
|
134
|
+
fields: %w[id portable_id name published_status published_revision_id latest_revision_id last_modification]
|
135
|
+
}
|
133
136
|
when :details
|
134
137
|
result = call_ao('workflow_details', id: wf_id)[:data]
|
135
138
|
return {type: :object_list, data: result['workflows']['workflow']['statuses']}
|
@@ -160,7 +163,7 @@ fields: %w[id portable_id name published_status published_revision_id latest_rev
|
|
160
163
|
raise "Expects: work_step:result_name format, but got #{expected}" if fields.length != 2
|
161
164
|
call_params['explicit_output_step'] = fields[0]
|
162
165
|
call_params['explicit_output_variable'] = fields[1]
|
163
|
-
#
|
166
|
+
# implicitly, call is synchronous
|
164
167
|
call_params['synchronous'] = true
|
165
168
|
end
|
166
169
|
if call_params['synchronous']
|