aspera-cli 4.6.0 → 4.7.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.
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