aspera-cli 4.13.0 → 4.15.0
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 +81 -7
- data/CONTRIBUTING.md +22 -6
- data/README.md +2038 -1080
- data/bin/ascli +18 -9
- data/bin/asession +12 -14
- data/examples/dascli +1 -1
- data/examples/proxy.pac +1 -1
- data/examples/rubyc +24 -0
- data/lib/aspera/aoc.rb +219 -159
- data/lib/aspera/ascmd.rb +25 -14
- data/lib/aspera/cli/basic_auth_plugin.rb +12 -9
- data/lib/aspera/cli/error.rb +17 -0
- data/lib/aspera/cli/extended_value.rb +47 -12
- data/lib/aspera/cli/formatter.rb +260 -179
- data/lib/aspera/cli/hints.rb +80 -0
- data/lib/aspera/cli/main.rb +104 -156
- data/lib/aspera/cli/manager.rb +259 -209
- data/lib/aspera/cli/plugin.rb +123 -63
- data/lib/aspera/cli/plugins/alee.rb +2 -3
- data/lib/aspera/cli/plugins/aoc.rb +341 -261
- data/lib/aspera/cli/plugins/ats.rb +22 -21
- data/lib/aspera/cli/plugins/bss.rb +5 -5
- data/lib/aspera/cli/plugins/config.rb +578 -627
- data/lib/aspera/cli/plugins/console.rb +44 -6
- data/lib/aspera/cli/plugins/cos.rb +15 -17
- data/lib/aspera/cli/plugins/faspex.rb +114 -100
- data/lib/aspera/cli/plugins/faspex5.rb +411 -264
- data/lib/aspera/cli/plugins/node.rb +354 -259
- data/lib/aspera/cli/plugins/orchestrator.rb +61 -29
- data/lib/aspera/cli/plugins/preview.rb +82 -90
- data/lib/aspera/cli/plugins/server.rb +79 -32
- data/lib/aspera/cli/plugins/shares.rb +55 -42
- data/lib/aspera/cli/sync_actions.rb +68 -0
- data/lib/aspera/cli/transfer_agent.rb +66 -73
- data/lib/aspera/cli/transfer_progress.rb +74 -0
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/colors.rb +12 -8
- data/lib/aspera/command_line_builder.rb +14 -11
- data/lib/aspera/cos_node.rb +3 -2
- data/lib/aspera/data/6 +0 -0
- data/lib/aspera/environment.rb +24 -9
- data/lib/aspera/fasp/agent_aspera.rb +126 -0
- data/lib/aspera/fasp/agent_base.rb +31 -77
- data/lib/aspera/fasp/agent_connect.rb +25 -21
- data/lib/aspera/fasp/agent_direct.rb +89 -103
- data/lib/aspera/fasp/agent_httpgw.rb +231 -149
- data/lib/aspera/fasp/agent_node.rb +41 -34
- data/lib/aspera/fasp/agent_trsdk.rb +75 -32
- data/lib/aspera/fasp/error_info.rb +4 -2
- data/lib/aspera/fasp/faux_file.rb +52 -0
- data/lib/aspera/fasp/installation.rb +53 -195
- data/lib/aspera/fasp/management.rb +244 -0
- data/lib/aspera/fasp/parameters.rb +71 -37
- data/lib/aspera/fasp/parameters.yaml +76 -8
- data/lib/aspera/fasp/products.rb +162 -0
- data/lib/aspera/fasp/resume_policy.rb +3 -3
- data/lib/aspera/fasp/transfer_spec.rb +7 -6
- data/lib/aspera/fasp/uri.rb +26 -24
- data/lib/aspera/faspex_gw.rb +2 -2
- data/lib/aspera/faspex_postproc.rb +2 -2
- data/lib/aspera/hash_ext.rb +14 -4
- data/lib/aspera/json_rpc.rb +49 -0
- data/lib/aspera/keychain/macos_security.rb +13 -13
- data/lib/aspera/line_logger.rb +23 -0
- data/lib/aspera/log.rb +58 -16
- data/lib/aspera/node.rb +157 -92
- data/lib/aspera/oauth.rb +37 -19
- data/lib/aspera/open_application.rb +4 -4
- data/lib/aspera/persistency_action_once.rb +1 -1
- data/lib/aspera/persistency_folder.rb +2 -2
- data/lib/aspera/preview/file_types.rb +4 -2
- data/lib/aspera/preview/generator.rb +22 -35
- data/lib/aspera/preview/options.rb +2 -0
- data/lib/aspera/preview/terminal.rb +73 -16
- data/lib/aspera/preview/utils.rb +21 -28
- data/lib/aspera/proxy_auto_config.js +2 -2
- data/lib/aspera/rest.rb +136 -68
- data/lib/aspera/rest_call_error.rb +1 -1
- data/lib/aspera/rest_error_analyzer.rb +15 -14
- data/lib/aspera/rest_errors_aspera.rb +37 -34
- data/lib/aspera/secret_hider.rb +18 -15
- data/lib/aspera/ssh.rb +5 -2
- data/lib/aspera/sync.rb +127 -119
- data/lib/aspera/temp_file_manager.rb +10 -3
- data/lib/aspera/web_auth.rb +10 -7
- data/lib/aspera/web_server_simple.rb +9 -4
- data.tar.gz.sig +0 -0
- metadata +34 -17
- metadata.gz.sig +0 -0
- data/docs/test_env.conf +0 -186
- data/lib/aspera/cli/listener/line_dump.rb +0 -19
- data/lib/aspera/cli/listener/logger.rb +0 -22
- data/lib/aspera/cli/listener/progress.rb +0 -50
- data/lib/aspera/cli/listener/progress_multi.rb +0 -84
- data/lib/aspera/cli/plugins/sync.rb +0 -44
- data/lib/aspera/data/7 +0 -0
- data/lib/aspera/fasp/listener.rb +0 -13
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# spellchecker: ignore workgroups
|
3
|
+
# spellchecker: ignore workgroups mypackages passcode
|
4
4
|
|
5
5
|
require 'aspera/cli/basic_auth_plugin'
|
6
6
|
require 'aspera/persistency_action_once'
|
@@ -8,7 +8,6 @@ require 'aspera/id_generator'
|
|
8
8
|
require 'aspera/nagios'
|
9
9
|
require 'aspera/environment'
|
10
10
|
require 'securerandom'
|
11
|
-
require 'ruby-progressbar'
|
12
11
|
require 'tty-spinner'
|
13
12
|
|
14
13
|
module Aspera
|
@@ -18,98 +17,154 @@ module Aspera
|
|
18
17
|
RECIPIENT_TYPES = %w[user workgroup external_user distribution_list shared_inbox].freeze
|
19
18
|
PACKAGE_TERMINATED = %w[completed failed].freeze
|
20
19
|
API_DETECT = 'api/v5/configuration/ping'
|
21
|
-
# list of supported mailbox types
|
22
|
-
|
23
|
-
PACKAGE_TYPE_RECEIVED = 'received'
|
20
|
+
# list of supported mailbox types (to list packages)
|
21
|
+
API_LIST_MAILBOX_TYPES = %w[inbox inbox_history inbox_all inbox_all_history outbox outbox_history pending pending_history all].freeze
|
24
22
|
PACKAGE_ALL_INIT = 'INIT'
|
25
|
-
|
23
|
+
PACKAGE_SEND_FROM_REMOTE_SOURCE = 'remote_source'
|
24
|
+
# Faspex API v5: get transfer spec for connect
|
25
|
+
TRANSFER_CONNECT = 'connect'
|
26
|
+
ADMIN_RESOURCES = %i[
|
27
|
+
accounts contacts jobs workgroups shared_inboxes nodes oauth_clients registrations saml_configs
|
28
|
+
metadata_profiles email_notifications
|
29
|
+
].freeze
|
30
|
+
JOB_RUNNING = %w[queued working].freeze
|
31
|
+
STANDARD_PATH = '/aspera/faspex'
|
32
|
+
private_constant(*%i[JOB_RUNNING RECIPIENT_TYPES PACKAGE_TERMINATED API_DETECT API_LIST_MAILBOX_TYPES PACKAGE_SEND_FROM_REMOTE_SOURCE])
|
26
33
|
class << self
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
34
|
+
def application_name
|
35
|
+
'Faspex'
|
36
|
+
end
|
37
|
+
|
38
|
+
def detect(address_or_url)
|
39
|
+
address_or_url = "https://#{address_or_url}" unless address_or_url.match?(%r{^[a-z]{1,6}://})
|
40
|
+
urls = [address_or_url]
|
41
|
+
urls.push("#{address_or_url}#{STANDARD_PATH}") unless address_or_url.end_with?(STANDARD_PATH)
|
42
|
+
|
43
|
+
urls.each do |base_url|
|
44
|
+
next unless base_url.start_with?('https://')
|
45
|
+
api = Rest.new(base_url: base_url, redirect_max: 1)
|
46
|
+
result = api.read(API_DETECT)
|
47
|
+
next unless result[:http].code.start_with?('2') && result[:http].body.strip.empty?
|
48
|
+
url_length = -2 - API_DETECT.length
|
49
|
+
# take redirect if any
|
32
50
|
return {
|
33
51
|
version: result[:http]['x-ibm-aspera'] || '5',
|
34
|
-
url: result[:http].uri.to_s[0..
|
52
|
+
url: result[:http].uri.to_s[0..url_length]
|
53
|
+
}
|
54
|
+
rescue StandardError => e
|
55
|
+
Log.log.debug{"detect error: #{e}"}
|
35
56
|
end
|
36
57
|
return nil
|
37
58
|
end
|
38
|
-
end
|
39
59
|
|
40
|
-
|
60
|
+
def wizard(object:, private_key_path:, pub_key_pem:)
|
61
|
+
options = object.options
|
62
|
+
formatter = object.formatter
|
63
|
+
instance_url = options.get_option(:url, mandatory: true)
|
64
|
+
wiz_username = options.get_option(:username, mandatory: true)
|
65
|
+
raise "Username shall be an email in Faspex: #{wiz_username}" if !(wiz_username =~ /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i)
|
66
|
+
if options.get_option(:client_id).nil? || options.get_option(:client_secret).nil?
|
67
|
+
formatter.display_status('Ask the ascli client id and secret to your Administrator.'.red)
|
68
|
+
formatter.display_status("Admin should login to: #{instance_url}")
|
69
|
+
OpenApplication.instance.uri(instance_url)
|
70
|
+
formatter.display_status('Navigate to: 𓃑 → Admin → Configurations → API clients')
|
71
|
+
formatter.display_status('Create an API client with:')
|
72
|
+
formatter.display_status('- name: ascli')
|
73
|
+
formatter.display_status('- JWT: enabled')
|
74
|
+
formatter.display_status("Then, logged in as #{wiz_username.red} go to your profile:")
|
75
|
+
formatter.display_status('👤 → Account Settings → Preferences -> Public Key in PEM:')
|
76
|
+
formatter.display_status(pub_key_pem)
|
77
|
+
formatter.display_status('Once set, fill in the parameters:')
|
78
|
+
end
|
79
|
+
return {
|
80
|
+
preset_value: {
|
81
|
+
url: instance_url,
|
82
|
+
username: wiz_username,
|
83
|
+
auth: :jwt.to_s,
|
84
|
+
private_key: "@file:#{private_key_path}",
|
85
|
+
client_id: options.get_option(:client_id, mandatory: true),
|
86
|
+
client_secret: options.get_option(:client_secret, mandatory: true)
|
87
|
+
},
|
88
|
+
test_args: 'user profile show'
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
def public_link?(url)
|
93
|
+
url.include?('/public/')
|
94
|
+
end
|
95
|
+
end
|
41
96
|
|
42
97
|
def initialize(env)
|
43
98
|
super(env)
|
44
|
-
options.
|
45
|
-
options.
|
46
|
-
options.
|
47
|
-
options.
|
48
|
-
options.
|
49
|
-
options.
|
50
|
-
options.
|
51
|
-
options.
|
52
|
-
options.
|
53
|
-
options.set_option(:auth, :jwt)
|
54
|
-
options.set_option(:box, 'inbox')
|
99
|
+
options.declare(:client_id, 'OAuth client identifier')
|
100
|
+
options.declare(:client_secret, 'OAuth client secret')
|
101
|
+
options.declare(:redirect_uri, 'OAuth redirect URI for web authentication')
|
102
|
+
options.declare(:auth, 'OAuth type of authentication', values: %i[boot].concat(Oauth::STD_AUTH_TYPES), default: :jwt)
|
103
|
+
options.declare(:private_key, 'OAuth JWT RSA private key PEM value (prefix file path with @file:)')
|
104
|
+
options.declare(:passphrase, 'OAuth JWT RSA private key passphrase')
|
105
|
+
options.declare(:box, "Package inbox, either shared inbox name or one of #{API_LIST_MAILBOX_TYPES} or #{ExtendedValue::ALL}", default: 'inbox')
|
106
|
+
options.declare(:shared_folder, 'Send package with files from shared folder')
|
107
|
+
options.declare(:group_type, 'Type of shared box', values: %i[shared_inboxes workgroups], default: :shared_inboxes)
|
55
108
|
options.parse_options!
|
56
109
|
end
|
57
110
|
|
111
|
+
def api_url
|
112
|
+
return "#{@faspex5_api_base_url}/api/v5"
|
113
|
+
end
|
114
|
+
|
115
|
+
def auth_api_url
|
116
|
+
return "#{@faspex5_api_base_url}/auth"
|
117
|
+
end
|
118
|
+
|
58
119
|
def set_api
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
args = URI.decode_www_form(uri.query).each_with_object({}){|v, h|h[v.first] = v.last; }
|
71
|
-
Log.dump(:args, args)
|
72
|
-
context = args['context']
|
73
|
-
raise 'missing context' if context.nil?
|
74
|
-
@pub_link_context = JSON.parse(Base64.decode64(context))
|
75
|
-
Log.dump(:@pub_link_context, @pub_link_context)
|
120
|
+
# get endpoint, remove unnecessary trailing slashes
|
121
|
+
@faspex5_api_base_url = options.get_option(:url, mandatory: true).gsub(%r{/+$}, '')
|
122
|
+
auth_type = self.class.public_link?(@faspex5_api_base_url) ? :public_link : options.get_option(:auth, mandatory: true)
|
123
|
+
case auth_type
|
124
|
+
when :public_link
|
125
|
+
encoded_context = Rest.decode_query(URI.parse(@faspex5_api_base_url).query)['context']
|
126
|
+
raise 'Bad faspex5 public link, missing context in query' if encoded_context.nil?
|
127
|
+
@pub_link_context = JSON.parse(Base64.decode64(encoded_context))
|
128
|
+
Log.log.trace1{Log.dump(:@pub_link_context, @pub_link_context)}
|
129
|
+
# ok, we have the additional parameters, get the base url
|
130
|
+
@faspex5_api_base_url = @faspex5_api_base_url.gsub(%r{/public/.*}, '').gsub(/\?.*/, '')
|
76
131
|
@api_v5 = Rest.new({
|
77
|
-
base_url:
|
132
|
+
base_url: api_url,
|
78
133
|
headers: {'Passcode' => @pub_link_context['passcode']}
|
79
134
|
})
|
80
135
|
when :boot
|
81
136
|
# the password here is the token copied directly from browser in developer mode
|
82
137
|
@api_v5 = Rest.new({
|
83
|
-
base_url:
|
84
|
-
headers: {'Authorization' => options.get_option(:password,
|
138
|
+
base_url: api_url,
|
139
|
+
headers: {'Authorization' => options.get_option(:password, mandatory: true)}
|
85
140
|
})
|
86
141
|
when :web
|
87
142
|
# opens a browser and ask user to auth using web
|
88
143
|
@api_v5 = Rest.new({
|
89
|
-
base_url:
|
144
|
+
base_url: api_url,
|
90
145
|
auth: {
|
91
146
|
type: :oauth2,
|
92
|
-
base_url:
|
147
|
+
base_url: auth_api_url,
|
93
148
|
grant_method: :web,
|
94
|
-
client_id: options.get_option(:client_id,
|
95
|
-
web: {redirect_uri: options.get_option(:redirect_uri,
|
149
|
+
client_id: options.get_option(:client_id, mandatory: true),
|
150
|
+
web: {redirect_uri: options.get_option(:redirect_uri, mandatory: true)}
|
96
151
|
}})
|
97
152
|
when :jwt
|
98
|
-
app_client_id = options.get_option(:client_id,
|
153
|
+
app_client_id = options.get_option(:client_id, mandatory: true)
|
99
154
|
@api_v5 = Rest.new({
|
100
|
-
base_url:
|
155
|
+
base_url: api_url,
|
101
156
|
auth: {
|
102
157
|
type: :oauth2,
|
103
|
-
base_url:
|
158
|
+
base_url: auth_api_url,
|
104
159
|
grant_method: :jwt,
|
105
160
|
client_id: app_client_id,
|
106
161
|
jwt: {
|
107
162
|
payload: {
|
108
163
|
iss: app_client_id, # issuer
|
109
164
|
aud: app_client_id, # audience (this field is not clear...)
|
110
|
-
sub: "user:#{options.get_option(:username,
|
165
|
+
sub: "user:#{options.get_option(:username, mandatory: true)}" # subject is a user
|
111
166
|
},
|
112
|
-
private_key_obj: OpenSSL::PKey::RSA.new(options.get_option(:private_key,
|
167
|
+
private_key_obj: OpenSSL::PKey::RSA.new(options.get_option(:private_key, mandatory: true), options.get_option(:passphrase)),
|
113
168
|
headers: {typ: 'JWT'}
|
114
169
|
}
|
115
170
|
}})
|
@@ -121,13 +176,16 @@ module Aspera
|
|
121
176
|
def normalize_recipients(parameters)
|
122
177
|
return unless parameters.key?('recipients')
|
123
178
|
raise 'Field recipients must be an Array' unless parameters['recipients'].is_a?(Array)
|
124
|
-
|
179
|
+
recipient_types = RECIPIENT_TYPES
|
180
|
+
if parameters.key?('recipient_types')
|
181
|
+
recipient_types = parameters['recipient_types']
|
182
|
+
parameters.delete('recipient_types')
|
183
|
+
recipient_types = [recipient_types] unless recipient_types.is_a?(Array)
|
184
|
+
end
|
185
|
+
parameters['recipients'].map! do |recipient_data|
|
125
186
|
# if just a string, assume it is the name
|
126
187
|
if recipient_data.is_a?(String)
|
127
|
-
|
128
|
-
raise "No matching contact for #{recipient_data}" if 0.eql?(result['total_count'])
|
129
|
-
raise "Multiple matching contact for #{recipient_data} : #{result['contacts'].map{|i|i['name']}.join(', ')}" unless 1.eql?(result['total_count'])
|
130
|
-
matched = result['contacts'].first
|
188
|
+
matched = @api_v5.lookup_by_name('contacts', recipient_data, {context: 'packages', type: Rest.array_params(recipient_types)})
|
131
189
|
recipient_data = {
|
132
190
|
name: matched['name'],
|
133
191
|
recipient_type: matched['type']
|
@@ -139,44 +197,60 @@ module Aspera
|
|
139
197
|
end
|
140
198
|
|
141
199
|
# wait for package status to be in provided list
|
142
|
-
def wait_package_status(id, status_list
|
143
|
-
|
144
|
-
|
145
|
-
progress = nil
|
146
|
-
while true
|
200
|
+
def wait_package_status(id, status_list: PACKAGE_TERMINATED)
|
201
|
+
total_sent = false
|
202
|
+
loop do
|
147
203
|
status = @api_v5.read("packages/#{id}/upload_details")[:data]
|
204
|
+
status['id'] = id
|
148
205
|
# user asked to not follow
|
149
|
-
|
206
|
+
return status if status_list.nil?
|
150
207
|
if status['upload_status'].eql?('submitted')
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
spinner.spin
|
157
|
-
elsif progress.nil?
|
158
|
-
progress = ProgressBar.create(
|
159
|
-
format: '%a %B %p%% %r Mbps %e',
|
160
|
-
rate_scale: lambda{|rate|rate / Environment::BYTES_PER_MEBIBIT},
|
161
|
-
title: 'progress',
|
162
|
-
total: status['bytes_total'].to_i)
|
208
|
+
config.progress_bar&.event(session_id: nil, type: :pre_start, info: status['upload_status'])
|
209
|
+
elsif !total_sent
|
210
|
+
config.progress_bar&.event(session_id: id, type: :session_start)
|
211
|
+
config.progress_bar&.event(session_id: id, type: :session_size, info: status['bytes_total'].to_i)
|
212
|
+
total_sent = true
|
163
213
|
else
|
164
|
-
|
214
|
+
config.progress_bar&.event(session_id: id, type: :transfer, info: status['bytes_written'].to_i)
|
215
|
+
end
|
216
|
+
if status_list.include?(status['upload_status'])
|
217
|
+
# if status['upload_status'].eql?('completed')
|
218
|
+
config.progress_bar&.event(session_id: id, type: :end)
|
219
|
+
return status
|
220
|
+
# end
|
165
221
|
end
|
166
|
-
|
222
|
+
sleep(1.0)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def wait_for_job(job_id)
|
227
|
+
spinner = nil
|
228
|
+
loop do
|
229
|
+
status = @api_v5.read("jobs/#{job_id}", {type: :formatted})[:data]
|
230
|
+
return status unless JOB_RUNNING.include?(status['status'])
|
231
|
+
if spinner.nil?
|
232
|
+
spinner = TTY::Spinner.new('[:spinner] :title', format: :classic)
|
233
|
+
spinner.start
|
234
|
+
end
|
235
|
+
spinner.update(title: status['status'])
|
236
|
+
spinner.spin
|
167
237
|
sleep(0.5)
|
168
238
|
end
|
169
|
-
|
170
|
-
return status
|
239
|
+
raise 'internal error'
|
171
240
|
end
|
172
241
|
|
173
|
-
# get a list of all entities of a given type
|
174
|
-
# @param
|
175
|
-
# @param query [Hash] additional query parameters
|
176
|
-
# @param
|
177
|
-
|
178
|
-
|
179
|
-
|
242
|
+
# get a (full or partial) list of all entities of a given type
|
243
|
+
# @param type [String] the type of entity to list (just a name)
|
244
|
+
# @param query [Hash,nil] additional query parameters
|
245
|
+
# @param path [String] optional prefix to add to the path (nil or empty string: no prefix)
|
246
|
+
# @param item_list_key [String] key in the result to get the list of items
|
247
|
+
def list_entities(type:, path: nil, query: nil, item_list_key: nil)
|
248
|
+
query = {} if query.nil?
|
249
|
+
type = type.to_s if type.is_a?(Symbol)
|
250
|
+
item_list_key = type if item_list_key.nil?
|
251
|
+
raise "internal error: Invalid type #{type.class}" unless type.is_a?(String)
|
252
|
+
full_path = type
|
253
|
+
full_path = "#{path}/#{full_path}" unless path.nil? || path.empty?
|
180
254
|
result = []
|
181
255
|
offset = 0
|
182
256
|
max_items = query.delete(MAX_ITEMS)
|
@@ -185,45 +259,198 @@ module Aspera
|
|
185
259
|
query = {'limit'=> 100}.merge(query)
|
186
260
|
loop do
|
187
261
|
query['offset'] = offset
|
188
|
-
|
189
|
-
result.concat(
|
262
|
+
page_result = @api_v5.read(full_path, query)[:data]
|
263
|
+
result.concat(page_result[item_list_key])
|
190
264
|
# reach the limit set by user ?
|
191
265
|
if !max_items.nil? && (result.length >= max_items)
|
192
266
|
result = result.slice(0, max_items)
|
193
267
|
break
|
194
268
|
end
|
195
|
-
break if result.length >=
|
269
|
+
break if result.length >= page_result['total_count']
|
196
270
|
remain_pages -= 1 unless remain_pages.nil?
|
197
271
|
break if remain_pages == 0
|
198
|
-
offset +=
|
272
|
+
offset += page_result[item_list_key].length
|
199
273
|
end
|
200
274
|
return result
|
201
275
|
end
|
202
276
|
|
203
277
|
# lookup an entity id from its name
|
204
|
-
def
|
205
|
-
|
278
|
+
def lookup_entity_by_field(type:, value:, field: 'name', query: :default, path: nil, item_list_key: nil)
|
279
|
+
query = {'q'=> value} if query.eql?(:default)
|
280
|
+
found = list_entities(type: type, path: path, query: query, item_list_key: item_list_key).select{|i|i[field].eql?(value)}
|
206
281
|
case found.length
|
207
|
-
when 0 then raise "No #{
|
208
|
-
when 1 then return found.first
|
209
|
-
else raise "
|
282
|
+
when 0 then raise "No #{type} with #{field} = #{value}"
|
283
|
+
when 1 then return found.first
|
284
|
+
else raise "Found #{found.length} #{path} with #{field} = #{value}"
|
210
285
|
end
|
211
286
|
end
|
212
287
|
|
213
|
-
#
|
214
|
-
def
|
215
|
-
|
288
|
+
# list all packages with optional filter
|
289
|
+
def list_packages_with_filter
|
290
|
+
filter = options.get_next_argument('filter', mandatory: false, type: Proc, default: ->(_x){true})
|
291
|
+
# translate box name to API prefix (with ending slash)
|
292
|
+
box = options.get_option(:box)
|
293
|
+
api_path =
|
294
|
+
case box
|
295
|
+
when ExtendedValue::ALL then '' # only admin can list all packages globally
|
296
|
+
when *API_LIST_MAILBOX_TYPES then box
|
297
|
+
else
|
298
|
+
group_type = options.get_option(:group_type)
|
299
|
+
"#{group_type}/#{lookup_entity_by_field(type: group_type, value: box)['id']}"
|
300
|
+
end
|
301
|
+
return list_entities(
|
302
|
+
type: 'packages',
|
303
|
+
query: query_read_delete(default: {}),
|
304
|
+
path: api_path).select(&filter)
|
305
|
+
end
|
306
|
+
|
307
|
+
def package_receive(package_ids)
|
308
|
+
# prepare persistency if needed
|
309
|
+
skip_ids_persistency = nil
|
310
|
+
if options.get_option(:once_only, mandatory: true)
|
311
|
+
# read ids from persistency
|
312
|
+
skip_ids_persistency = PersistencyActionOnce.new(
|
313
|
+
manager: @agents[:persistency],
|
314
|
+
data: [],
|
315
|
+
id: IdGenerator.from_list([
|
316
|
+
'faspex_recv',
|
317
|
+
options.get_option(:url, mandatory: true),
|
318
|
+
options.get_option(:username, mandatory: true)]))
|
319
|
+
end
|
320
|
+
case package_ids
|
321
|
+
when PACKAGE_ALL_INIT
|
322
|
+
raise 'Only with option once_only' unless skip_ids_persistency
|
323
|
+
skip_ids_persistency.data.clear.concat(list_packages_with_filter.map{|p|p['id']})
|
324
|
+
skip_ids_persistency.save
|
325
|
+
return Main.result_status("Initialized skip for #{skip_ids_persistency.data.count} package(s)")
|
326
|
+
when ExtendedValue::ALL
|
327
|
+
# TODO: if packages have same name, they will overwrite ?
|
328
|
+
package_ids = list_packages_with_filter.map{|p|p['id']}
|
329
|
+
Log.log.debug{Log.dump(:package_ids, package_ids)}
|
330
|
+
Log.log.debug{Log.dump(:skip_ids, skip_ids_persistency.data)}
|
331
|
+
package_ids.reject!{|i|skip_ids_persistency.data.include?(i)} if skip_ids_persistency
|
332
|
+
Log.log.debug{Log.dump(:package_ids, package_ids)}
|
333
|
+
end
|
334
|
+
# a single id was provided
|
335
|
+
# TODO: check package_ids is a list of strings
|
336
|
+
package_ids = [package_ids] if package_ids.is_a?(String)
|
337
|
+
result_transfer = []
|
338
|
+
param_file_list = {}
|
339
|
+
begin
|
340
|
+
param_file_list['paths'] = transfer.source_list.map{|source|{'path'=>source}}
|
341
|
+
rescue Cli::BadArgument
|
342
|
+
# paths is optional
|
343
|
+
end
|
344
|
+
download_params = {
|
345
|
+
type: 'received',
|
346
|
+
transfer_type: TRANSFER_CONNECT
|
347
|
+
}
|
348
|
+
box = options.get_option(:box)
|
216
349
|
case box
|
217
|
-
when
|
218
|
-
when *
|
219
|
-
else
|
350
|
+
when /outbox/ then download_params[:type] = 'sent'
|
351
|
+
when *API_LIST_MAILBOX_TYPES then nil # nothing to do
|
352
|
+
else # shared inbox / workgroup
|
353
|
+
download_params[:recipient_workgroup_id] = lookup_entity_by_field(type: options.get_option(:group_type), value: box)['id']
|
220
354
|
end
|
355
|
+
package_ids.each do |pkg_id|
|
356
|
+
formatter.display_status("Receiving package #{pkg_id}")
|
357
|
+
# TODO: allow from sent as well ?
|
358
|
+
transfer_spec = @api_v5.call(
|
359
|
+
operation: 'POST',
|
360
|
+
subpath: "packages/#{pkg_id}/transfer_spec/download",
|
361
|
+
headers: {'Accept' => 'application/json'},
|
362
|
+
url_params: download_params,
|
363
|
+
json_params: param_file_list
|
364
|
+
)[:data]
|
365
|
+
# delete flag for Connect Client
|
366
|
+
transfer_spec.delete('authentication')
|
367
|
+
statuses = transfer.start(transfer_spec)
|
368
|
+
result_transfer.push({'package' => pkg_id, Main::STATUS_FIELD => statuses})
|
369
|
+
# skip only if all sessions completed
|
370
|
+
if TransferAgent.session_status(statuses).eql?(:success) && skip_ids_persistency
|
371
|
+
skip_ids_persistency.data.push(pkg_id)
|
372
|
+
skip_ids_persistency.save
|
373
|
+
end
|
374
|
+
end
|
375
|
+
return Main.result_transfer_multiple(result_transfer)
|
221
376
|
end
|
222
377
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
378
|
+
def package_action
|
379
|
+
command = options.get_next_command(%i[show browse status delete receive send list])
|
380
|
+
package_id =
|
381
|
+
if %i[receive show browse status delete].include?(command)
|
382
|
+
@pub_link_context&.key?('package_id') ? @pub_link_context['package_id'] : instance_identifier
|
383
|
+
end
|
384
|
+
case command
|
385
|
+
when :show
|
386
|
+
return {type: :single_object, data: @api_v5.read("packages/#{package_id}")[:data]}
|
387
|
+
when :browse
|
388
|
+
path = options.get_next_argument('path', expected: :single, mandatory: false) || '/'
|
389
|
+
# TODO: support multi-page listing ?
|
390
|
+
params = {
|
391
|
+
# recipient_user_id: 25,
|
392
|
+
# offset: 0,
|
393
|
+
# limit: 25
|
394
|
+
}
|
395
|
+
result = @api_v5.call({
|
396
|
+
operation: 'POST',
|
397
|
+
subpath: "packages/#{package_id}/files/received",
|
398
|
+
headers: {'Accept' => 'application/json'},
|
399
|
+
url_params: params,
|
400
|
+
json_params: {'path' => path, 'filters' => {'basenames'=>[]}}})[:data]
|
401
|
+
formatter.display_item_count(result['item_count'], result['total_count'])
|
402
|
+
return {type: :object_list, data: result['items']}
|
403
|
+
when :status
|
404
|
+
status = wait_package_status(package_id, status_list: nil)
|
405
|
+
return {type: :single_object, data: status}
|
406
|
+
when :delete
|
407
|
+
ids = package_id
|
408
|
+
ids = [ids] unless ids.is_a?(Array)
|
409
|
+
raise 'Package identifier must be a single id or an Array' unless ids.is_a?(Array) && ids.all?(String)
|
410
|
+
# API returns 204, empty on success
|
411
|
+
@api_v5.call({operation: 'DELETE', subpath: 'packages', headers: {'Accept' => 'application/json'}, json_params: {ids: ids}})
|
412
|
+
return Main.result_status('Package(s) deleted')
|
413
|
+
when :receive
|
414
|
+
return package_receive(package_id)
|
415
|
+
when :send
|
416
|
+
parameters = value_create_modify(command: command)
|
417
|
+
normalize_recipients(parameters)
|
418
|
+
package = @api_v5.create('packages', parameters)[:data]
|
419
|
+
shared_folder = options.get_option(:shared_folder)
|
420
|
+
if shared_folder.nil?
|
421
|
+
# send from local files
|
422
|
+
transfer_spec = @api_v5.call(
|
423
|
+
operation: 'POST',
|
424
|
+
subpath: "packages/#{package['id']}/transfer_spec/upload",
|
425
|
+
headers: {'Accept' => 'application/json'},
|
426
|
+
url_params: {transfer_type: TRANSFER_CONNECT},
|
427
|
+
json_params: {paths: transfer.source_list}
|
428
|
+
)[:data]
|
429
|
+
# well, we asked a TS for connect, but we actually want a generic one
|
430
|
+
transfer_spec.delete('authentication')
|
431
|
+
return Main.result_transfer(transfer.start(transfer_spec))
|
432
|
+
else
|
433
|
+
# send from remote shared folder
|
434
|
+
if (m = shared_folder.match(REGEX_LOOKUP_ID_BY_FIELD))
|
435
|
+
shared_folder = lookup_entity_by_field(type: 'shared_folders', value: m[2])['id']
|
436
|
+
end
|
437
|
+
transfer_request = {shared_folder_id: shared_folder, paths: transfer.source_list}
|
438
|
+
# start remote transfer and get first status
|
439
|
+
result = @api_v5.create("packages/#{package['id']}/remote_transfer", transfer_request)[:data]
|
440
|
+
result['id'] = package['id']
|
441
|
+
unless result['status'].eql?('completed')
|
442
|
+
formatter.display_status("Package #{package['id']}")
|
443
|
+
result = wait_package_status(package['id'])
|
444
|
+
end
|
445
|
+
return {type: :single_object, data: result}
|
446
|
+
end
|
447
|
+
when :list
|
448
|
+
return {
|
449
|
+
type: :object_list,
|
450
|
+
data: list_packages_with_filter,
|
451
|
+
fields: %w[id title release_date total_bytes total_files created_time state]
|
452
|
+
}
|
453
|
+
end # case package
|
227
454
|
end
|
228
455
|
|
229
456
|
ACTIONS = %i[health version user bearer_token packages shared_folders admin gateway postprocessing].freeze
|
@@ -259,148 +486,19 @@ module Aspera
|
|
259
486
|
when :bearer_token
|
260
487
|
return {type: :text, data: @api_v5.oauth_token}
|
261
488
|
when :packages
|
262
|
-
|
263
|
-
case command
|
264
|
-
when :list
|
265
|
-
return {
|
266
|
-
type: :object_list,
|
267
|
-
data: list_packages,
|
268
|
-
fields: %w[id title release_date total_bytes total_files created_time state]
|
269
|
-
}
|
270
|
-
when :show
|
271
|
-
id = @pub_link_context['package_id'] if @pub_link_context&.key?('package_id')
|
272
|
-
id ||= instance_identifier
|
273
|
-
return {type: :single_object, data: @api_v5.read("packages/#{id}")[:data]}
|
274
|
-
when :browse
|
275
|
-
id = @pub_link_context['package_id'] if @pub_link_context&.key?('package_id')
|
276
|
-
id ||= instance_identifier
|
277
|
-
path = options.get_next_argument('path', expected: :single, mandatory: false) || '/'
|
278
|
-
# TODO: support multi-page listing ?
|
279
|
-
params = {
|
280
|
-
# recipient_user_id: 25,
|
281
|
-
# offset: 0,
|
282
|
-
# limit: 25
|
283
|
-
}
|
284
|
-
result = @api_v5.call({
|
285
|
-
operation: 'POST',
|
286
|
-
subpath: "packages/#{id}/files/received",
|
287
|
-
headers: {'Accept' => 'application/json'},
|
288
|
-
url_params: params,
|
289
|
-
json_params: {'path' => path, 'filters' => {'basenames'=>[]}}})[:data]
|
290
|
-
formatter.display_item_count(result['item_count'], result['total_count'])
|
291
|
-
return {type: :object_list, data: result['items']}
|
292
|
-
when :status
|
293
|
-
status = wait_package_status(instance_identifier)
|
294
|
-
return {type: :single_object, data: status}
|
295
|
-
when :delete
|
296
|
-
ids = instance_identifier
|
297
|
-
ids = [ids] unless ids.is_a?(Array)
|
298
|
-
raise 'Package identifier must be a single id or an Array' unless ids.is_a?(Array) && ids.all?(String)
|
299
|
-
# API returns 204, empty on success
|
300
|
-
@api_v5.call({operation: 'DELETE', subpath: 'packages', headers: {'Accept' => 'application/json'}, json_params: {ids: ids}})
|
301
|
-
return Main.result_status('Package(s) deleted')
|
302
|
-
when :send
|
303
|
-
parameters = options.get_option(:value, is_type: :mandatory)
|
304
|
-
raise CliBadArgument, 'Value must be Hash, refer to API' unless parameters.is_a?(Hash)
|
305
|
-
normalize_recipients(parameters)
|
306
|
-
package = @api_v5.create('packages', parameters)[:data]
|
307
|
-
shared_folder = options.get_option(:shared_folder)
|
308
|
-
if shared_folder.nil?
|
309
|
-
# TODO: option to send from remote source or httpgw
|
310
|
-
transfer_spec = @api_v5.call(
|
311
|
-
operation: 'POST',
|
312
|
-
subpath: "packages/#{package['id']}/transfer_spec/upload",
|
313
|
-
headers: {'Accept' => 'application/json'},
|
314
|
-
url_params: {transfer_type: TRANSFER_CONNECT},
|
315
|
-
json_params: {paths: transfer.source_list}
|
316
|
-
)[:data]
|
317
|
-
# well, we asked a TS for connect, but we actually want a generic one
|
318
|
-
transfer_spec.delete('authentication')
|
319
|
-
return Main.result_transfer(transfer.start(transfer_spec))
|
320
|
-
else
|
321
|
-
if !shared_folder.to_i.to_s.eql?(shared_folder)
|
322
|
-
shared_folder = lookup_name_to_id('shared_folders', shared_folder)
|
323
|
-
end
|
324
|
-
transfer_request = {shared_folder_id: shared_folder, paths: transfer.source_list}
|
325
|
-
# start remote transfer and get first status
|
326
|
-
result = @api_v5.create("packages/#{package['id']}/remote_transfer", transfer_request)[:data]
|
327
|
-
result['id'] = package['id']
|
328
|
-
unless result['status'].eql?('completed')
|
329
|
-
formatter.display_status("Package #{package['id']}")
|
330
|
-
result = wait_package_status(package['id'])
|
331
|
-
end
|
332
|
-
return {type: :single_object, data: result}
|
333
|
-
end
|
334
|
-
when :receive
|
335
|
-
# prepare persistency if needed
|
336
|
-
skip_ids_persistency = nil
|
337
|
-
if options.get_option(:once_only, is_type: :mandatory)
|
338
|
-
# read ids from persistency
|
339
|
-
skip_ids_persistency = PersistencyActionOnce.new(
|
340
|
-
manager: @agents[:persistency],
|
341
|
-
data: [],
|
342
|
-
id: IdGenerator.from_list([
|
343
|
-
'faspex_recv',
|
344
|
-
options.get_option(:url, is_type: :mandatory),
|
345
|
-
options.get_option(:username, is_type: :mandatory),
|
346
|
-
PACKAGE_TYPE_RECEIVED]))
|
347
|
-
end
|
348
|
-
# one or several packages
|
349
|
-
package_ids = @pub_link_context['package_id'] if @pub_link_context&.key?('package_id')
|
350
|
-
package_ids ||= instance_identifier
|
351
|
-
case package_ids
|
352
|
-
when PACKAGE_ALL_INIT
|
353
|
-
raise 'Only with option once_only' unless skip_ids_persistency
|
354
|
-
skip_ids_persistency.data.clear.concat(list_packages.map{|p|p['id']})
|
355
|
-
skip_ids_persistency.save
|
356
|
-
return Main.result_status("Initialized skip for #{skip_ids_persistency.data.count} package(s)")
|
357
|
-
when VAL_ALL
|
358
|
-
# TODO: if packages have same name, they will overwrite ?
|
359
|
-
package_ids = list_packages.map{|p|p['id']}
|
360
|
-
Log.dump(:package_ids, package_ids)
|
361
|
-
Log.dump(:package_ids, skip_ids_persistency.data)
|
362
|
-
package_ids.reject!{|i|skip_ids_persistency.data.include?(i)} if skip_ids_persistency
|
363
|
-
Log.dump(:package_ids, package_ids)
|
364
|
-
end
|
365
|
-
# a single id was provided
|
366
|
-
# TODO: check package_ids is a list of strings
|
367
|
-
package_ids = [package_ids] if package_ids.is_a?(String)
|
368
|
-
result_transfer = []
|
369
|
-
package_ids.each do |pkg_id|
|
370
|
-
formatter.display_status("Receiving package #{pkg_id}")
|
371
|
-
param_file_list = {}
|
372
|
-
begin
|
373
|
-
param_file_list['paths'] = transfer.source_list.map{|source|{'path'=>source}}
|
374
|
-
rescue Aspera::Cli::CliBadArgument
|
375
|
-
# paths is optional
|
376
|
-
end
|
377
|
-
# TODO: allow from sent as well ?
|
378
|
-
transfer_spec = @api_v5.call(
|
379
|
-
operation: 'POST',
|
380
|
-
subpath: "packages/#{pkg_id}/transfer_spec/download",
|
381
|
-
headers: {'Accept' => 'application/json'},
|
382
|
-
url_params: {transfer_type: TRANSFER_CONNECT, type: PACKAGE_TYPE_RECEIVED},
|
383
|
-
json_params: param_file_list
|
384
|
-
)[:data]
|
385
|
-
# delete flag for Connect Client
|
386
|
-
transfer_spec.delete('authentication')
|
387
|
-
statuses = transfer.start(transfer_spec)
|
388
|
-
result_transfer.push({'package' => pkg_id, Main::STATUS_FIELD => statuses})
|
389
|
-
# skip only if all sessions completed
|
390
|
-
if TransferAgent.session_status(statuses).eql?(:success) && skip_ids_persistency
|
391
|
-
skip_ids_persistency.data.push(pkg_id)
|
392
|
-
skip_ids_persistency.save
|
393
|
-
end
|
394
|
-
end
|
395
|
-
return Main.result_transfer_multiple(result_transfer)
|
396
|
-
end # case package
|
489
|
+
return package_action
|
397
490
|
when :shared_folders
|
398
491
|
all_shared_folders = @api_v5.read('shared_folders')[:data]['shared_folders']
|
399
492
|
case options.get_next_command(%i[list browse])
|
400
493
|
when :list
|
401
494
|
return {type: :object_list, data: all_shared_folders}
|
402
495
|
when :browse
|
403
|
-
shared_folder_id = instance_identifier
|
496
|
+
shared_folder_id = instance_identifier do |field, value|
|
497
|
+
matches = all_shared_folders.select{|i|i[field].eql?(value)}
|
498
|
+
raise "no match for #{field} = #{value}" if matches.empty?
|
499
|
+
raise "multiple matches for #{field} = #{value}" if matches.length > 1
|
500
|
+
matches.first['id']
|
501
|
+
end
|
404
502
|
path = options.get_next_argument('folder path', mandatory: false) || '/'
|
405
503
|
node = all_shared_folders.find{|i|i['id'].eql?(shared_folder_id)}
|
406
504
|
raise "No such shared folder id #{shared_folder_id}" if node.nil?
|
@@ -418,12 +516,14 @@ module Aspera
|
|
418
516
|
end
|
419
517
|
end
|
420
518
|
when :admin
|
421
|
-
case options.get_next_command(%i[resource smtp])
|
519
|
+
case options.get_next_command(%i[resource smtp].freeze)
|
422
520
|
when :resource
|
423
|
-
res_type = options.get_next_command(
|
424
|
-
email_notifications])
|
521
|
+
res_type = options.get_next_command(ADMIN_RESOURCES)
|
425
522
|
res_path = list_key = res_type.to_s
|
426
523
|
id_as_arg = false
|
524
|
+
display_fields = nil
|
525
|
+
adm_api = @api_v5
|
526
|
+
available_commands = [].concat(Plugin::ALL_OPS)
|
427
527
|
case res_type
|
428
528
|
when :metadata_profiles
|
429
529
|
res_path = 'configuration/metadata_profiles'
|
@@ -431,50 +531,96 @@ module Aspera
|
|
431
531
|
when :email_notifications
|
432
532
|
list_key = false
|
433
533
|
id_as_arg = 'type'
|
534
|
+
when :accounts
|
535
|
+
display_fields = Formatter.all_but('user_profile_data_attributes')
|
536
|
+
when :oauth_clients
|
537
|
+
display_fields = Formatter.all_but('public_key')
|
538
|
+
adm_api = Rest.new(@api_v5.params.merge({base_url: auth_api_url}))
|
539
|
+
when :shared_inboxes, :workgroups
|
540
|
+
available_commands.push(:members, :saml_groups, :invite_external_collaborator)
|
434
541
|
end
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
542
|
+
res_command = options.get_next_command(available_commands)
|
543
|
+
case res_command
|
544
|
+
when *Plugin::ALL_OPS
|
545
|
+
return entity_command(res_command, adm_api, res_path, item_list_key: list_key, display_fields: display_fields, id_as_arg: id_as_arg)
|
546
|
+
when :invite_external_collaborator
|
547
|
+
shared_inbox_id = instance_identifier { |field, value| lookup_entity_by_field(type: res_type.to_s, field: field, value: value)['id']}
|
548
|
+
creation_payload = value_create_modify(command: res_command, type: [Hash, String])
|
549
|
+
creation_payload = {'email_address' => creation_payload} if creation_payload.is_a?(String)
|
550
|
+
res_path = "#{res_type}/#{shared_inbox_id}/external_collaborator"
|
551
|
+
result = adm_api.create(res_path, creation_payload)[:data]
|
552
|
+
formatter.display_status(result['message'])
|
553
|
+
result = lookup_entity_by_field(type: 'members', path: "#{res_type}/#{shared_inbox_id}", value: creation_payload['email_address'], query: {})
|
554
|
+
return {type: :single_object, data: result}
|
555
|
+
when :members, :saml_groups
|
556
|
+
res_id = instance_identifier { |field, value| lookup_entity_by_field(type: res_type.to_s, field: field, value: value)['id']}
|
557
|
+
res_prefix = "#{res_type}/#{res_id}"
|
558
|
+
res_path = "#{res_prefix}/#{res_command}"
|
559
|
+
list_key = res_command.to_s
|
560
|
+
list_key = 'groups' if res_command.eql?(:saml_groups)
|
561
|
+
sub_command = options.get_next_command(%i[create list modify delete])
|
562
|
+
if sub_command.eql?(:create) && options.get_option(:value).nil?
|
563
|
+
raise "use option 'value' to provide saml group_id and access (refer to API)" unless res_command.eql?(:members)
|
564
|
+
# first arg is one user name or list of users
|
565
|
+
users = options.get_next_argument('user id, or email, or list of')
|
566
|
+
users = [users] unless users.is_a?(Array)
|
567
|
+
users = users.map do |user|
|
568
|
+
if (m = user.match(REGEX_LOOKUP_ID_BY_FIELD))
|
569
|
+
lookup_entity_by_field(
|
570
|
+
type: 'accounts', field: m[1], value: m[2],
|
571
|
+
query: {type: Rest.array_params(%w{local_user saml_user self_registered_user external_user})})['id']
|
572
|
+
else
|
573
|
+
# it's the user id (not member id...)
|
574
|
+
user
|
575
|
+
end
|
576
|
+
end
|
577
|
+
access = options.get_next_argument('level', mandatory: false, expected: %i[submit_only standard shared_inbox_admin], default: :standard)
|
578
|
+
# TODO: unshift to command line parameters instead of using deprecated option "value"
|
579
|
+
options.set_option(:value, {user: users.map{|u|{id: u, access: access}}})
|
439
580
|
end
|
440
|
-
|
441
|
-
|
442
|
-
|
581
|
+
return entity_command(sub_command, adm_api, res_path, item_list_key: list_key) do |field, value|
|
582
|
+
lookup_entity_by_field(
|
583
|
+
type: 'accounts', field: field, value: value,
|
584
|
+
query: {type: Rest.array_params(%w{local_user saml_user self_registered_user external_user})})['id']
|
585
|
+
end
|
443
586
|
end
|
444
|
-
return entity_action(adm_api, res_path, item_list_key: list_key, display_fields: display_fields, id_as_arg: id_as_arg)
|
445
587
|
when :smtp
|
446
588
|
smtp_path = 'configuration/smtp'
|
447
|
-
|
589
|
+
smtp_cmd = options.get_next_command(%i[show create modify delete test])
|
590
|
+
case smtp_cmd
|
448
591
|
when :show
|
449
592
|
return { type: :single_object, data: @api_v5.read(smtp_path)[:data] }
|
450
593
|
when :create
|
451
|
-
return { type: :single_object, data: @api_v5.create(smtp_path,
|
594
|
+
return { type: :single_object, data: @api_v5.create(smtp_path, value_create_modify(command: smtp_cmd))[:data] }
|
452
595
|
when :modify
|
453
|
-
return { type: :single_object, data: @api_v5.
|
596
|
+
return { type: :single_object, data: @api_v5.update(smtp_path, value_create_modify(command: smtp_cmd))[:data] }
|
454
597
|
when :delete
|
455
598
|
return { type: :single_object, data: @api_v5.delete(smtp_path)[:data] }
|
456
599
|
when :test
|
457
600
|
test_data = options.get_next_argument('Email or test data, see API')
|
458
601
|
test_data = {test_email_recipient: test_data} if test_data.is_a?(String)
|
459
|
-
|
602
|
+
creation = @api_v5.create(File.join(smtp_path, 'test'), test_data)[:data]
|
603
|
+
result = wait_for_job(creation['job_id'])
|
604
|
+
result['serialized_args'] = JSON.parse(result['serialized_args']) rescue result['serialized_args']
|
605
|
+
return { type: :single_object, data: result }
|
460
606
|
end
|
461
607
|
end
|
462
608
|
when :gateway
|
463
609
|
require 'aspera/faspex_gw'
|
464
|
-
url =
|
610
|
+
url = value_create_modify(command: command, type: String)
|
465
611
|
uri = URI.parse(url)
|
466
612
|
server = WebServerSimple.new(uri)
|
467
613
|
server.mount(uri.path, Faspex4GWServlet, @api_v5, nil)
|
614
|
+
# on ctrl-c, tell server main loop to exit
|
468
615
|
trap('INT') { server.shutdown }
|
469
|
-
formatter.display_status("Faspex 4
|
616
|
+
formatter.display_status("Gateway for Faspex 4-style API listening on #{url}")
|
470
617
|
Log.log.info("Listening on #{url}")
|
471
618
|
# this is blocking until server exits
|
472
619
|
server.start
|
473
620
|
return Main.result_status('Gateway terminated')
|
474
621
|
when :postprocessing
|
475
|
-
require 'aspera/faspex_postproc'
|
476
|
-
parameters =
|
477
|
-
raise 'parameters must be Hash' unless parameters.is_a?(Hash)
|
622
|
+
require 'aspera/faspex_postproc' # cspell:disable-line
|
623
|
+
parameters = value_create_modify(command: command)
|
478
624
|
parameters = parameters.symbolize_keys
|
479
625
|
raise 'Missing key: url' unless parameters.key?(:url)
|
480
626
|
uri = URI.parse(parameters[:url])
|
@@ -482,8 +628,9 @@ module Aspera
|
|
482
628
|
parameters[:processing][:root] = uri.path
|
483
629
|
server = WebServerSimple.new(uri, certificate: parameters[:certificate])
|
484
630
|
server.mount(uri.path, Faspex4PostProcServlet, parameters[:processing])
|
631
|
+
# on ctrl-c, tell server main loop to exit
|
485
632
|
trap('INT') { server.shutdown }
|
486
|
-
formatter.display_status("Faspex 4 post processing listening on #{uri.port}")
|
633
|
+
formatter.display_status("Web-hook for Faspex 4-style post processing listening on #{uri.port}")
|
487
634
|
Log.log.info("Listening on #{uri.port}")
|
488
635
|
# this is blocking until server exits
|
489
636
|
server.start
|