aspera-cli 4.6.0 → 4.7.0

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