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