aspera-cli 4.4.0 → 4.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2095 -1503
- data/bin/ascli +2 -1
- data/bin/asession +4 -5
- data/docs/test_env.conf +3 -0
- data/examples/aoc.rb +4 -3
- data/examples/faspex4.rb +25 -25
- data/examples/proxy.pac +1 -1
- data/examples/transfer.rb +17 -17
- data/lib/aspera/aoc.rb +238 -185
- data/lib/aspera/ascmd.rb +93 -83
- data/lib/aspera/ats_api.rb +11 -10
- data/lib/aspera/cli/basic_auth_plugin.rb +13 -14
- data/lib/aspera/cli/extended_value.rb +42 -33
- data/lib/aspera/cli/formater.rb +142 -108
- data/lib/aspera/cli/info.rb +17 -0
- data/lib/aspera/cli/listener/line_dump.rb +3 -2
- data/lib/aspera/cli/listener/logger.rb +2 -1
- data/lib/aspera/cli/listener/progress.rb +16 -18
- data/lib/aspera/cli/listener/progress_multi.rb +18 -21
- data/lib/aspera/cli/main.rb +173 -149
- data/lib/aspera/cli/manager.rb +163 -168
- data/lib/aspera/cli/plugin.rb +43 -31
- data/lib/aspera/cli/plugins/alee.rb +6 -6
- data/lib/aspera/cli/plugins/aoc.rb +405 -370
- data/lib/aspera/cli/plugins/ats.rb +86 -79
- data/lib/aspera/cli/plugins/bss.rb +14 -16
- data/lib/aspera/cli/plugins/config.rb +580 -362
- data/lib/aspera/cli/plugins/console.rb +23 -19
- data/lib/aspera/cli/plugins/cos.rb +18 -18
- data/lib/aspera/cli/plugins/faspex.rb +201 -158
- data/lib/aspera/cli/plugins/faspex5.rb +80 -57
- data/lib/aspera/cli/plugins/node.rb +183 -166
- data/lib/aspera/cli/plugins/orchestrator.rb +71 -67
- data/lib/aspera/cli/plugins/preview.rb +92 -96
- data/lib/aspera/cli/plugins/server.rb +79 -75
- data/lib/aspera/cli/plugins/shares.rb +35 -19
- data/lib/aspera/cli/plugins/sync.rb +20 -22
- data/lib/aspera/cli/transfer_agent.rb +76 -113
- data/lib/aspera/cli/version.rb +2 -1
- data/lib/aspera/colors.rb +35 -27
- data/lib/aspera/command_line_builder.rb +48 -34
- data/lib/aspera/cos_node.rb +29 -21
- data/lib/aspera/data_repository.rb +3 -2
- data/lib/aspera/environment.rb +50 -45
- data/lib/aspera/fasp/{manager.rb → agent_base.rb} +28 -25
- data/lib/aspera/fasp/{connect.rb → agent_connect.rb} +52 -43
- data/lib/aspera/fasp/{local.rb → agent_direct.rb} +58 -72
- data/lib/aspera/fasp/{http_gw.rb → agent_httpgw.rb} +37 -43
- data/lib/aspera/fasp/{node.rb → agent_node.rb} +35 -16
- data/lib/aspera/fasp/agent_trsdk.rb +104 -0
- data/lib/aspera/fasp/error.rb +2 -1
- data/lib/aspera/fasp/error_info.rb +68 -52
- data/lib/aspera/fasp/installation.rb +152 -124
- data/lib/aspera/fasp/listener.rb +1 -0
- data/lib/aspera/fasp/parameters.rb +87 -92
- data/lib/aspera/fasp/parameters.yaml +305 -249
- data/lib/aspera/fasp/resume_policy.rb +11 -14
- data/lib/aspera/fasp/transfer_spec.rb +26 -0
- data/lib/aspera/fasp/uri.rb +22 -21
- data/lib/aspera/faspex_gw.rb +55 -89
- data/lib/aspera/hash_ext.rb +4 -3
- data/lib/aspera/id_generator.rb +8 -7
- data/lib/aspera/keychain/encrypted_hash.rb +121 -0
- data/lib/aspera/keychain/macos_security.rb +90 -0
- data/lib/aspera/log.rb +55 -37
- data/lib/aspera/nagios.rb +13 -12
- data/lib/aspera/node.rb +30 -25
- data/lib/aspera/oauth.rb +175 -226
- data/lib/aspera/open_application.rb +4 -3
- data/lib/aspera/persistency_action_once.rb +6 -6
- data/lib/aspera/persistency_folder.rb +5 -9
- data/lib/aspera/preview/file_types.rb +6 -5
- data/lib/aspera/preview/generator.rb +25 -24
- data/lib/aspera/preview/options.rb +16 -14
- data/lib/aspera/preview/utils.rb +98 -98
- data/lib/aspera/{proxy_auto_config.erb.js → proxy_auto_config.js} +23 -31
- data/lib/aspera/proxy_auto_config.rb +111 -20
- data/lib/aspera/rest.rb +154 -135
- data/lib/aspera/rest_call_error.rb +2 -2
- data/lib/aspera/rest_error_analyzer.rb +23 -25
- data/lib/aspera/rest_errors_aspera.rb +15 -14
- data/lib/aspera/ssh.rb +12 -10
- data/lib/aspera/sync.rb +42 -41
- data/lib/aspera/temp_file_manager.rb +18 -14
- data/lib/aspera/timer_limiter.rb +2 -1
- data/lib/aspera/uri_reader.rb +7 -5
- data/lib/aspera/web_auth.rb +79 -76
- metadata +116 -29
- data/docs/Makefile +0 -66
- data/docs/README.erb.md +0 -3973
- data/docs/README.md +0 -13
- data/docs/diagrams.txt +0 -49
- data/docs/doc_tools.rb +0 -58
- data/lib/aspera/api_detector.rb +0 -60
- data/lib/aspera/cli/plugins/shares2.rb +0 -114
- data/lib/aspera/secrets.rb +0 -20
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'aspera/cli/basic_auth_plugin'
|
2
3
|
require 'aspera/cli/plugins/node'
|
3
4
|
require 'aspera/cli/plugins/config'
|
@@ -6,6 +7,7 @@ require 'aspera/cli/transfer_agent'
|
|
6
7
|
require 'aspera/persistency_action_once'
|
7
8
|
require 'aspera/open_application'
|
8
9
|
require 'aspera/fasp/uri'
|
10
|
+
require 'aspera/fasp/transfer_spec'
|
9
11
|
require 'aspera/nagios'
|
10
12
|
require 'aspera/id_generator'
|
11
13
|
require 'xmlsimple'
|
@@ -16,8 +18,9 @@ module Aspera
|
|
16
18
|
module Cli
|
17
19
|
module Plugins
|
18
20
|
class Faspex < BasicAuthPlugin
|
19
|
-
|
20
|
-
|
21
|
+
# required hash key for source in config
|
22
|
+
KEY_NODE='node' # value must be hash with url, username, password
|
23
|
+
KEY_PATH='path' # value must be same sub-path as in Faspex's node
|
21
24
|
VAL_ALL='ALL'
|
22
25
|
# added field in result that identifies the package
|
23
26
|
PACKAGE_MATCH_FIELD='package_id'
|
@@ -25,71 +28,85 @@ module Aspera
|
|
25
28
|
ATOM_MAILBOXES=[:inbox, :archive, :sent]
|
26
29
|
# allowed parameters for inbox.atom
|
27
30
|
ATOM_PARAMS=['page', 'count', 'startIndex']
|
28
|
-
# with special parameters (from Plugin class)
|
31
|
+
# with special parameters (from Plugin class) : max and pmax
|
29
32
|
ATOM_EXT_PARAMS=ATOM_PARAMS+[MAX_ITEMS, MAX_PAGES]
|
30
33
|
# sub path in url for public link delivery
|
31
34
|
PUB_LINK_EXTERNAL_MATCH='external_deliveries/'
|
32
35
|
private_constant :KEY_NODE,:KEY_PATH,:VAL_ALL,:PACKAGE_MATCH_FIELD,:ATOM_MAILBOXES,
|
33
36
|
:ATOM_PARAMS,:ATOM_EXT_PARAMS,:PUB_LINK_EXTERNAL_MATCH
|
34
37
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
38
|
+
class << self
|
39
|
+
def detect(base_url)
|
40
|
+
api=Rest.new({base_url: base_url})
|
41
|
+
result=api.call({operation: 'POST',subpath: 'aspera/faspex',headers: {'Accept'=>'application/xrds+xml'},text_body_params: ''})
|
42
|
+
# 4.x
|
43
|
+
if result[:http].body.start_with?('<?xml')
|
44
|
+
res_s=XmlSimple.xml_in(result[:http].body, {'ForceArray' => false})
|
45
|
+
version=res_s['XRD']['application']['version']
|
46
|
+
return {version: version}
|
47
|
+
end
|
48
|
+
return nil
|
49
|
+
end
|
48
50
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
51
|
+
# extract elements from anonymous faspex link
|
52
|
+
def get_link_data(publink)
|
53
|
+
publink_uri=URI.parse(publink)
|
54
|
+
raise CliBadArgument, 'public link does not match Faspex format' unless (m=publink_uri.path.match(/^(.*)\/(external.*)$/))
|
53
55
|
base=m[1]
|
54
56
|
subpath=m[2]
|
55
|
-
|
56
|
-
|
57
|
+
port_add=publink_uri.port.eql?(publink_uri.default_port) ? '' : ":#{publink_uri.port}"
|
58
|
+
result={
|
59
|
+
base_url: "#{publink_uri.scheme}://#{publink_uri.host}#{port_add}#{base}",
|
60
|
+
subpath: subpath,
|
61
|
+
query: URI.decode_www_form(publink_uri.query).each_with_object({}){|v,h|h[v.first]=v.last;}
|
62
|
+
}
|
63
|
+
Log.dump('publink',result)
|
64
|
+
return result
|
57
65
|
end
|
58
|
-
port_add=publink_uri.port.eql?(publink_uri.default_port)?'':":#{publink_uri.port}"
|
59
|
-
result={
|
60
|
-
:base_url => "#{publink_uri.scheme}://#{publink_uri.host}#{port_add}#{base}",
|
61
|
-
:subpath => subpath,
|
62
|
-
:query => URI::decode_www_form(publink_uri.query).inject({}){|h,v|h[v.first]=v.last;h}
|
63
|
-
}
|
64
|
-
Log.dump('publink',result)
|
65
|
-
return result
|
66
|
-
end
|
67
66
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
67
|
+
# get faspe: URI from entry in xml, and fix problems..
|
68
|
+
def get_fasp_uri_from_entry(entry, raise_no_link: true)
|
69
|
+
unless entry.has_key?('link')
|
70
|
+
raise CliBadArgument, 'package has no link (deleted?)' if raise_no_link
|
71
|
+
return nil
|
72
|
+
end
|
73
|
+
result=entry['link'].select{|e| e['rel'].eql?('package')}.first['href']
|
74
|
+
# tags in the end of URL is not well % encoded... there are "=" that should be %3D
|
75
|
+
# TODO: enter ticket to Faspex ?
|
76
|
+
###XXif m=result.match(/(=+)$/);result.gsub!(/=+$/,"#{"%3D"*m[1].length}");end
|
77
|
+
return result
|
78
|
+
end
|
77
79
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
80
|
+
def textify_package_list(table_data)
|
81
|
+
return table_data.map do |e|
|
82
|
+
e.keys.each {|k| e[k]=e[k].first if e[k].is_a?(Array) && (e[k].length == 1)}
|
83
|
+
e['items'] = e.has_key?('link') ? e['link'].length : 0
|
84
|
+
e
|
85
|
+
end
|
86
|
+
end
|
85
87
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
88
|
+
# field_sym : :id or :name
|
89
|
+
def get_source_id(source_list,source_name)
|
90
|
+
source_ids=source_list.select { |i| i['name'].eql?(source_name) }
|
91
|
+
if source_ids.empty?
|
92
|
+
raise CliError,%Q(No such Faspex source "#{source_name}" in [#{source_list.map{|i| %Q("#{i['name']}")}.join(', ')}])
|
93
|
+
end
|
94
|
+
return source_ids.first['id']
|
91
95
|
end
|
92
|
-
|
96
|
+
end
|
97
|
+
|
98
|
+
def initialize(env)
|
99
|
+
@api_v3=nil
|
100
|
+
@api_v4=nil
|
101
|
+
super(env)
|
102
|
+
options.add_opt_simple(:link,'public link for specific operation')
|
103
|
+
options.add_opt_simple(:delivery_info,'package delivery information (extended value)')
|
104
|
+
options.add_opt_simple(:source_name,'create package from remote source (by name)')
|
105
|
+
options.add_opt_simple(:storage,'Faspex local storage definition')
|
106
|
+
options.add_opt_simple(:recipient,'use if recipient is a dropbox (with *)')
|
107
|
+
options.add_opt_list(:box,ATOM_MAILBOXES,'package box')
|
108
|
+
options.set_option(:box,:inbox)
|
109
|
+
options.parse_options!
|
93
110
|
end
|
94
111
|
|
95
112
|
def api_v3
|
@@ -101,41 +118,45 @@ module Aspera
|
|
101
118
|
|
102
119
|
def api_v4
|
103
120
|
if @api_v4.nil?
|
104
|
-
faspex_api_base=
|
121
|
+
faspex_api_base=options.get_option(:url,:mandatory)
|
105
122
|
@api_v4=Rest.new({
|
106
|
-
:
|
107
|
-
:
|
108
|
-
|
109
|
-
:
|
110
|
-
:
|
111
|
-
:
|
112
|
-
:
|
113
|
-
:
|
123
|
+
base_url: faspex_api_base+'/api',
|
124
|
+
auth: {
|
125
|
+
type: :oauth2,
|
126
|
+
base_url: faspex_api_base+'/auth/oauth2',
|
127
|
+
auth: {type: :basic, username: options.get_option(:username,:mandatory), password: options.get_option(:password,:mandatory)},
|
128
|
+
crtype: :generic,
|
129
|
+
generic: {grant_type: 'password'},
|
130
|
+
scope: 'admin'
|
114
131
|
}})
|
115
132
|
end
|
116
133
|
return @api_v4
|
117
134
|
end
|
118
135
|
|
119
|
-
# query supports : {"startIndex":10,"count":1,"page":109}
|
120
|
-
def
|
121
|
-
|
136
|
+
# query supports : {"startIndex":10,"count":1,"page":109,"max":2,"pmax":1}
|
137
|
+
def mailbox_filtered_entries(stop_at_id: nil)
|
138
|
+
recipient_names=[options.get_option(:recipient,:optional) || options.get_option(:username,:mandatory)]
|
139
|
+
# some workgroup messages have no star in recipient name
|
140
|
+
recipient_names.push(recipient_names.first[1..-1]) if recipient_names.first.start_with?('*')
|
122
141
|
# mailbox is in ATOM_MAILBOXES
|
123
|
-
mailbox=
|
142
|
+
mailbox=options.get_option(:box,:mandatory)
|
124
143
|
# parameters
|
125
|
-
mailbox_query=
|
144
|
+
mailbox_query=options.get_option(:query,:optional)
|
126
145
|
max_items=nil
|
127
146
|
max_pages=nil
|
128
147
|
result=[]
|
129
148
|
if !mailbox_query.nil?
|
130
|
-
raise
|
149
|
+
raise 'query: must be Hash or nil' unless mailbox_query.is_a?(Hash)
|
131
150
|
raise "query: supported params: #{ATOM_EXT_PARAMS}" unless (mailbox_query.keys-ATOM_EXT_PARAMS).empty?
|
132
|
-
raise
|
151
|
+
raise 'query: startIndex and page are exclusive' if mailbox_query.has_key?('startIndex') && mailbox_query.has_key?('page')
|
133
152
|
max_items=mailbox_query[MAX_ITEMS]
|
134
153
|
mailbox_query.delete(MAX_ITEMS)
|
135
154
|
max_pages=mailbox_query[MAX_PAGES]
|
136
155
|
mailbox_query.delete(MAX_PAGES)
|
137
156
|
end
|
138
157
|
loop do
|
158
|
+
# get a batch of package information
|
159
|
+
# order: first batch is latest packages, and then in a batch ids are increasing
|
139
160
|
atom_xml=api_v3.call({operation: 'GET',subpath: "#{mailbox}.atom",headers: {'Accept'=>'application/xml'},url_params: mailbox_query})[:http].body
|
140
161
|
box_data=XmlSimple.xml_in(atom_xml, {'ForceArray' => true})
|
141
162
|
Log.dump(:box_data,box_data)
|
@@ -143,29 +164,39 @@ module Aspera
|
|
143
164
|
Log.log.debug("new items: #{items.count}")
|
144
165
|
# it is the end if page is empty
|
145
166
|
break if items.empty?
|
146
|
-
|
147
|
-
|
167
|
+
stop_condition=false
|
168
|
+
# results will be sorted in reverse id
|
169
|
+
items.reverse.each do |package|
|
170
|
+
package[PACKAGE_MATCH_FIELD]=
|
171
|
+
case mailbox
|
148
172
|
when :inbox,:archive
|
149
|
-
recipient=package['to'].select{|i|i['name'].first
|
173
|
+
recipient=package['to'].select{|i|recipient_names.include?(i['name'].first)}.first
|
150
174
|
recipient.nil? ? nil : recipient['recipient_delivery_id'].first
|
151
175
|
else # :sent
|
152
176
|
package['delivery_id'].first
|
153
177
|
end
|
154
|
-
#
|
178
|
+
# if we look for a specific package
|
179
|
+
stop_condition=true if !stop_at_id.nil? && stop_at_id.eql?(package[PACKAGE_MATCH_FIELD])
|
180
|
+
# keep only those for the specified recipient,
|
155
181
|
result.push(package) unless package[PACKAGE_MATCH_FIELD].nil?
|
156
182
|
end
|
183
|
+
break if stop_condition
|
184
|
+
#result.push({PACKAGE_MATCH_FIELD=>'======'})
|
157
185
|
Log.log.debug("total items: #{result.count}")
|
158
186
|
# reach the limit ?
|
159
|
-
|
187
|
+
if !max_items.nil? && (result.count >= max_items)
|
188
|
+
result=result.slice(0,max_items) if result.count > max_items
|
189
|
+
break
|
190
|
+
end
|
160
191
|
link=box_data['link'].select{|i|i['rel'].eql?('next')}.first
|
161
192
|
Log.log.debug("link: #{link}")
|
162
193
|
# no next link
|
163
194
|
break if link.nil?
|
164
195
|
# replace parameters with the ones from next link
|
165
196
|
params=CGI.parse(URI.parse(link['href']).query)
|
166
|
-
mailbox_query=params.keys.
|
197
|
+
mailbox_query=params.keys.each_with_object({}){|i,m|;m[i]=params[i].first;}
|
167
198
|
Log.log.debug("query: #{mailbox_query}")
|
168
|
-
break if !max_pages.nil?
|
199
|
+
break if !max_pages.nil? && (mailbox_query['page'].to_i > max_pages)
|
169
200
|
end
|
170
201
|
return result
|
171
202
|
end
|
@@ -179,15 +210,15 @@ module Aspera
|
|
179
210
|
raise CliBadArgument,"pub link is #{link_data[:subpath]}, expecting external/submissions/new"
|
180
211
|
end
|
181
212
|
create_path=link_data[:subpath].split('/')[0..-2].join('/')
|
182
|
-
package_create_params.merge!({:
|
213
|
+
package_create_params.merge!({passcode: link_data[:query]['passcode']})
|
183
214
|
delivery_info.merge!({
|
184
|
-
:
|
185
|
-
:
|
186
|
-
api_public_link=Rest.new({:
|
215
|
+
transfer_type: 'connect',
|
216
|
+
source_paths_list: transfer.ts_source_paths.map{|i|i['source']}.join("\r\n")})
|
217
|
+
api_public_link=Rest.new({base_url: link_data[:base_url]})
|
187
218
|
# Hum, as this does not always work (only user, but not dropbox), we get the javascript and need hack
|
188
219
|
#pkg_created=api_public_link.create(create_path,package_create_params)[:data]
|
189
220
|
# so extract data from javascript
|
190
|
-
pkgdatares=api_public_link.call({:
|
221
|
+
pkgdatares=api_public_link.call({operation: 'POST',subpath: create_path,json_params: package_create_params,headers: {'Accept'=>'text/javascript'}})[:http].body
|
191
222
|
# get args of function call
|
192
223
|
pkgdatares.gsub!("\n",'') # one line
|
193
224
|
pkgdatares.gsub!(/^[^"]+\("\{/,'{') # delete header
|
@@ -197,51 +228,51 @@ module Aspera
|
|
197
228
|
begin
|
198
229
|
pkgdatares=JSON.parse("[#{pkgdatares}]")
|
199
230
|
rescue JSON::ParserError # => e
|
200
|
-
raise
|
231
|
+
raise 'Link not valid'
|
201
232
|
end
|
202
233
|
return pkgdatares.first
|
203
234
|
end
|
204
235
|
|
205
|
-
ACTIONS=[
|
236
|
+
ACTIONS=[:health,:package, :source, :me, :dropbox, :v4, :address_book, :login_methods].freeze
|
206
237
|
|
207
238
|
def execute_action
|
208
|
-
command=
|
239
|
+
command=options.get_next_command(ACTIONS)
|
209
240
|
case command
|
210
241
|
when :health
|
211
242
|
nagios=Nagios.new
|
212
243
|
begin
|
213
244
|
api_v3.read('me')
|
214
245
|
nagios.add_ok('faspex api','accessible')
|
215
|
-
rescue => e
|
246
|
+
rescue StandardError => e
|
216
247
|
nagios.add_critical('faspex api',e.to_s)
|
217
248
|
end
|
218
249
|
return nagios.result
|
219
250
|
when :package
|
220
|
-
command_pkg=
|
251
|
+
command_pkg=options.get_next_command([:send, :recv, :list])
|
221
252
|
case command_pkg
|
222
253
|
when :list
|
223
|
-
return {:
|
254
|
+
return {type: :object_list,data: mailbox_filtered_entries,fields: [PACKAGE_MATCH_FIELD,'title','items'], textify: lambda {|table_data|Faspex.textify_package_list(table_data)} }
|
224
255
|
when :send
|
225
|
-
delivery_info=
|
256
|
+
delivery_info=options.get_option(:delivery_info,:mandatory)
|
226
257
|
raise CliBadArgument,'delivery_info must be hash, refer to doc' unless delivery_info.is_a?(Hash)
|
227
258
|
# actual parameter to faspex API
|
228
259
|
package_create_params={'delivery'=>delivery_info}
|
229
|
-
public_link_url=
|
260
|
+
public_link_url=options.get_option(:link,:optional)
|
230
261
|
if public_link_url.nil?
|
231
262
|
# authenticated user
|
232
263
|
delivery_info['sources']||=[{'paths'=>[]}]
|
233
264
|
first_source=delivery_info['sources'].first
|
234
|
-
first_source['paths'].push(*
|
235
|
-
source_name=
|
265
|
+
first_source['paths'].push(*transfer.ts_source_paths.map{|i|i['source']})
|
266
|
+
source_name=options.get_option(:source_name,:optional)
|
236
267
|
if !source_name.nil?
|
237
|
-
source_list=api_v3.call({:
|
268
|
+
source_list=api_v3.call({operation: 'GET',subpath: 'source_shares',headers: {'Accept'=>'application/json'}})[:data]['items']
|
238
269
|
source_id=self.class.get_source_id(source_list,source_name)
|
239
270
|
first_source['id']=source_id
|
240
271
|
end
|
241
|
-
pkg_created=api_v3.call({:
|
272
|
+
pkg_created=api_v3.call({operation: 'POST',subpath: 'send',json_params: package_create_params,headers: {'Accept'=>'application/json'}})[:data]
|
242
273
|
if !source_name.nil?
|
243
274
|
# no transfer spec if remote source
|
244
|
-
return {:
|
275
|
+
return {data: [pkg_created['links']['status']],type: :value_list,name: 'link'}
|
245
276
|
end
|
246
277
|
raise CliBadArgument,'expecting one session exactly' if pkg_created['xfer_sessions'].length != 1
|
247
278
|
transfer_spec=pkg_created['xfer_sessions'].first
|
@@ -251,52 +282,55 @@ module Aspera
|
|
251
282
|
transfer_spec=send_publink_to_ts(public_link_url,package_create_params)
|
252
283
|
end
|
253
284
|
#Log.dump('transfer_spec',transfer_spec)
|
254
|
-
return Main.result_transfer(
|
285
|
+
return Main.result_transfer(transfer.start(transfer_spec,{src: :node_gen3}))
|
255
286
|
when :recv
|
256
|
-
link_url=
|
287
|
+
link_url=options.get_option(:link,:optional)
|
257
288
|
# list of faspex ID/URI to download
|
258
289
|
pkg_id_uri=nil
|
259
290
|
skip_ids_data=[]
|
260
291
|
skip_ids_persistency=nil
|
261
292
|
case link_url
|
262
293
|
when nil # usual case: no link
|
263
|
-
if
|
294
|
+
if options.get_option(:once_only,:mandatory)
|
264
295
|
skip_ids_persistency=PersistencyActionOnce.new(
|
265
296
|
manager: @agents[:persistency],
|
266
297
|
data: skip_ids_data,
|
267
|
-
id: IdGenerator.from_list(['faspex_recv',
|
298
|
+
id: IdGenerator.from_list(['faspex_recv',options.get_option(:url,:mandatory),options.get_option(:username,:mandatory),options.get_option(:box,:mandatory).to_s]))
|
268
299
|
end
|
269
300
|
# get command line parameters
|
270
|
-
delivid=
|
301
|
+
delivid=instance_identifier()
|
302
|
+
raise 'empty id' if delivid.empty?
|
303
|
+
recipient=options.get_option(:recipient,:optional)
|
271
304
|
if delivid.eql?(VAL_ALL)
|
272
|
-
pkg_id_uri=
|
273
|
-
# TODO : remove ids from skip not present in inbox
|
305
|
+
pkg_id_uri=mailbox_filtered_entries.map{|i|{id: i[PACKAGE_MATCH_FIELD],uri: self.class.get_fasp_uri_from_entry(i, raise_no_link: false)}}
|
306
|
+
# TODO : remove ids from skip not present in inbox to avoid growing too big
|
274
307
|
# skip_ids_data.select!{|id|pkg_id_uri.select{|p|p[:id].eql?(id)}}
|
275
|
-
pkg_id_uri.
|
308
|
+
pkg_id_uri.reject!{|i|skip_ids_data.include?(i[:id])}
|
309
|
+
elsif !recipient.nil? && recipient.start_with?('*')
|
310
|
+
found_package_link=mailbox_filtered_entries(stop_at_id: delivid).select{|p|p[PACKAGE_MATCH_FIELD].eql?(delivid)}.first['link'].first['href']
|
311
|
+
raise 'Not Found. Dropbox and Workgroup packages can use the link option with faspe:' if found_package_link.nil?
|
312
|
+
pkg_id_uri=[{id: delivid,uri: found_package_link}]
|
276
313
|
else
|
277
|
-
recipient=options.get_option(:recipient,:optional)
|
278
|
-
if !recipient.nil? and recipient.start_with?('*')
|
279
|
-
raise "Dropbox and Workgroup packages should use link option with faspe:"
|
280
|
-
end
|
281
314
|
# TODO: delivery id is the right one if package was receive by workgroup
|
282
|
-
endpoint=
|
283
|
-
|
284
|
-
when :
|
315
|
+
endpoint=
|
316
|
+
case options.get_option(:box,:mandatory)
|
317
|
+
when :inbox,:archive then'received'
|
318
|
+
when :sent then 'sent'
|
285
319
|
end
|
286
|
-
entry_xml=api_v3.call({:
|
320
|
+
entry_xml=api_v3.call({operation: 'GET',subpath: "#{endpoint}/#{delivid}",headers: {'Accept'=>'application/xml'}})[:http].body
|
287
321
|
package_entry=XmlSimple.xml_in(entry_xml, {'ForceArray' => true})
|
288
|
-
pkg_id_uri=[{:
|
322
|
+
pkg_id_uri=[{id: delivid,uri: self.class.get_fasp_uri_from_entry(package_entry)}]
|
289
323
|
end
|
290
324
|
when /^faspe:/
|
291
|
-
pkg_id_uri=[{:
|
325
|
+
pkg_id_uri=[{id: 'package',uri: link_url}]
|
292
326
|
else
|
293
327
|
link_data=self.class.get_link_data(link_url)
|
294
328
|
if !link_data[:subpath].start_with?(PUB_LINK_EXTERNAL_MATCH)
|
295
329
|
raise CliBadArgument,"Pub link is #{link_data[:subpath]}. Expecting #{PUB_LINK_EXTERNAL_MATCH}"
|
296
330
|
end
|
297
|
-
# Note: unauthenticated API (
|
298
|
-
api_public_link=Rest.new({:
|
299
|
-
pkgdatares=api_public_link.call({:
|
331
|
+
# Note: unauthenticated API (authorization is in url params)
|
332
|
+
api_public_link=Rest.new({base_url: link_data[:base_url]})
|
333
|
+
pkgdatares=api_public_link.call({operation: 'GET',subpath: link_data[:subpath],url_params: {passcode: link_data[:query]['passcode']}, headers: {'Accept'=>'application/xml'}})
|
300
334
|
if !pkgdatares[:http].body.start_with?('<?xml ')
|
301
335
|
OpenApplication.instance.uri(link_url)
|
302
336
|
raise CliError, 'no such package'
|
@@ -304,21 +338,30 @@ module Aspera
|
|
304
338
|
package_entry=XmlSimple.xml_in(pkgdatares[:http].body, {'ForceArray' => false})
|
305
339
|
Log.dump(:package_entry,package_entry)
|
306
340
|
transfer_uri=self.class.get_fasp_uri_from_entry(package_entry)
|
307
|
-
pkg_id_uri=[{:
|
341
|
+
pkg_id_uri=[{id: package_entry['id'],uri: transfer_uri}]
|
308
342
|
end # public link
|
309
343
|
Log.dump(:pkg_id_uri,pkg_id_uri)
|
310
344
|
return Main.result_status('no package') if pkg_id_uri.empty?
|
311
345
|
result_transfer=[]
|
312
346
|
pkg_id_uri.each do |id_uri|
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
347
|
+
if id_uri[:uri].nil?
|
348
|
+
# skip package with no link: empty or content deleted
|
349
|
+
statuses=[:success]
|
350
|
+
else
|
351
|
+
transfer_spec=Fasp::Uri.new(id_uri[:uri]).transfer_spec
|
352
|
+
# NOTE: only external users have token in faspe: link !
|
353
|
+
if !transfer_spec.has_key?('token')
|
354
|
+
sanitized=id_uri[:uri].gsub('&','&')
|
355
|
+
xmlpayload=%Q(<?xml version="1.0" encoding="UTF-8"?><url-list xmlns="http://schemas.asperasoft.com/xml/url-list"><url href="#{sanitized}"/></url-list>)
|
356
|
+
transfer_spec['token']=api_v3.call({
|
357
|
+
operation: 'POST',
|
358
|
+
subpath: 'issue-token?direction=down',
|
359
|
+
headers: {'Accept'=>'text/plain','Content-Type'=>'application/vnd.aspera.url-list+xml'},
|
360
|
+
text_body_params: xmlpayload})[:http].body
|
361
|
+
end
|
362
|
+
transfer_spec['direction']=Fasp::TransferSpec::DIRECTION_RECEIVE
|
363
|
+
statuses=transfer.start(transfer_spec,{src: :node_gen3})
|
319
364
|
end
|
320
|
-
transfer_spec['direction']='receive'
|
321
|
-
statuses=self.transfer.start(transfer_spec,{:src=>:node_gen3})
|
322
365
|
result_transfer.push({'package'=>id_uri[:id],Main::STATUS_FIELD=>statuses})
|
323
366
|
# skip only if all sessions completed
|
324
367
|
skip_ids_data.push(id_uri[:id]) if TransferAgent.session_status(statuses).eql?(:success)
|
@@ -327,21 +370,21 @@ module Aspera
|
|
327
370
|
return Main.result_transfer_multiple(result_transfer)
|
328
371
|
end
|
329
372
|
when :source
|
330
|
-
command_source=
|
331
|
-
source_list=api_v3.call({:
|
373
|
+
command_source=options.get_next_command([:list, :id, :name])
|
374
|
+
source_list=api_v3.call({operation: 'GET',subpath: 'source_shares',headers: {'Accept'=>'application/json'}})[:data]['items']
|
332
375
|
case command_source
|
333
376
|
when :list
|
334
|
-
return {:
|
377
|
+
return {type: :object_list,data: source_list}
|
335
378
|
else # :id or :name
|
336
|
-
source_match_val=
|
379
|
+
source_match_val=options.get_next_argument('source id or name')
|
337
380
|
source_ids=source_list.select { |i| i[command_source.to_s].to_s.eql?(source_match_val) }
|
338
381
|
if source_ids.empty?
|
339
|
-
raise CliError,"No such Faspex source #{command_source
|
382
|
+
raise CliError,"No such Faspex source #{command_source}: #{source_match_val} in [#{source_list.map{|i| i[command_source.to_s]}.join(', ')}]"
|
340
383
|
end
|
341
384
|
# get id and name
|
342
385
|
source_name=source_ids.first['name']
|
343
|
-
source_id=source_ids.first['id']
|
344
|
-
source_hash=
|
386
|
+
#source_id=source_ids.first['id']
|
387
|
+
source_hash=options.get_option(:storage,:mandatory)
|
345
388
|
# check value of option
|
346
389
|
raise CliError,'storage option must be a Hash' unless source_hash.is_a?(Hash)
|
347
390
|
source_hash.each do |name,storage|
|
@@ -355,56 +398,56 @@ module Aspera
|
|
355
398
|
end
|
356
399
|
source_info=source_hash[source_name]
|
357
400
|
Log.log.debug("source_info: #{source_info}")
|
358
|
-
command_node=
|
401
|
+
command_node=options.get_next_command([:info, :node])
|
359
402
|
case command_node
|
360
403
|
when :info
|
361
|
-
return {:
|
404
|
+
return {data: source_info,type: :single_object}
|
362
405
|
when :node
|
363
406
|
node_config=ExtendedValue.instance.evaluate(source_info[KEY_NODE])
|
364
407
|
raise CliError,"bad type for: \"#{source_info[KEY_NODE]}\"" unless node_config.is_a?(Hash)
|
365
408
|
Log.log.debug("node=#{node_config}")
|
366
409
|
api_node=Rest.new({
|
367
|
-
:
|
368
|
-
:
|
369
|
-
:
|
370
|
-
:
|
371
|
-
:
|
372
|
-
command=
|
410
|
+
base_url: node_config['url'],
|
411
|
+
auth: {
|
412
|
+
type: :basic,
|
413
|
+
username: node_config['username'],
|
414
|
+
password: node_config['password']}})
|
415
|
+
command=options.get_next_command(Node::COMMON_ACTIONS)
|
373
416
|
return Node.new(@agents.merge(skip_basic_auth_options: true, node_api: api_node)).execute_action(command,source_info[KEY_PATH])
|
374
417
|
end
|
375
418
|
end
|
376
419
|
when :me
|
377
|
-
my_info=api_v3.call({:
|
378
|
-
return {:
|
420
|
+
my_info=api_v3.call({operation: 'GET',subpath: 'me',headers: {'Accept'=>'application/json'}})[:data]
|
421
|
+
return {data: my_info, type: :single_object}
|
379
422
|
when :dropbox
|
380
|
-
command_pkg=
|
423
|
+
command_pkg=options.get_next_command([:list])
|
381
424
|
case command_pkg
|
382
425
|
when :list
|
383
|
-
dropbox_list=api_v3.call({:
|
384
|
-
return {:
|
426
|
+
dropbox_list=api_v3.call({operation: 'GET',subpath: 'dropboxes',headers: {'Accept'=>'application/json'}})[:data]
|
427
|
+
return {type: :object_list, data: dropbox_list['items'], fields: ['name','id','description','can_read','can_write']}
|
385
428
|
end
|
386
429
|
when :v4
|
387
|
-
command=
|
430
|
+
command=options.get_next_command([:package,:dropbox, :dmembership, :workgroup,:wmembership,:user,:metadata_profile])
|
388
431
|
case command
|
389
432
|
when :dropbox
|
390
|
-
return
|
433
|
+
return entity_action(api_v4,'admin/dropboxes',display_fields: ['id','e_wg_name','e_wg_desc','created_at'])
|
391
434
|
when :dmembership
|
392
|
-
return
|
435
|
+
return entity_action(api_v4,'dropbox_memberships')
|
393
436
|
when :workgroup
|
394
|
-
return
|
437
|
+
return entity_action(api_v4,'admin/workgroups',display_fields: ['id','e_wg_name','e_wg_desc','created_at'])
|
395
438
|
when :wmembership
|
396
|
-
return
|
439
|
+
return entity_action(api_v4,'workgroup_memberships')
|
397
440
|
when :user
|
398
|
-
return
|
441
|
+
return entity_action(api_v4,'users',display_fields: ['id','name','first_name','last_name'])
|
399
442
|
when :metadata_profile
|
400
|
-
return
|
443
|
+
return entity_action(api_v4,'metadata_profiles')
|
401
444
|
when :package
|
402
|
-
pkg_box_type=
|
403
|
-
pkg_box_id=
|
404
|
-
return
|
445
|
+
pkg_box_type=options.get_next_command([:users])
|
446
|
+
pkg_box_id=instance_identifier()
|
447
|
+
return entity_action(api_v4,"#{pkg_box_type}/#{pkg_box_id}/packages")
|
405
448
|
end
|
406
449
|
when :address_book
|
407
|
-
result=api_v3.call({:
|
450
|
+
result=api_v3.call({operation: 'GET',subpath: 'address-book',headers: {'Accept'=>'application/json'},url_params: {'format'=>'json','count'=>100_000}})[:data]
|
408
451
|
self.format.display_status("users: #{result['itemsPerPage']}/#{result['totalResults']}, start:#{result['startIndex']}")
|
409
452
|
users=result['entry']
|
410
453
|
# add missing entries
|
@@ -420,11 +463,11 @@ module Aspera
|
|
420
463
|
u['first_name'],u['last_name'] = u['displayName'].split(' ',2)
|
421
464
|
u['x']=true
|
422
465
|
end
|
423
|
-
return {:
|
466
|
+
return {type: :object_list,data: users}
|
424
467
|
when :login_methods
|
425
|
-
login_meths=api_v3.call({:
|
468
|
+
login_meths=api_v3.call({operation: 'GET',subpath: 'login/new',headers: {'Accept'=>'application/xrds+xml'}})[:http].body
|
426
469
|
login_methods=XmlSimple.xml_in(login_meths, {'ForceArray' => false})
|
427
|
-
return {:
|
470
|
+
return {type: :object_list, data: login_methods['XRD']['Service']}
|
428
471
|
end # command
|
429
472
|
end
|
430
473
|
end
|