aspera-cli 4.0.0.pre3 → 4.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +695 -205
- data/bin/dascli +13 -0
- data/docs/README.erb.md +615 -157
- data/docs/test_env.conf +23 -5
- data/docs/transfer_spec.html +1 -1
- data/examples/aoc.rb +14 -3
- data/examples/faspex4.rb +78 -0
- data/lib/aspera/aoc.rb +87 -108
- data/lib/aspera/cli/formater.rb +2 -0
- data/lib/aspera/cli/main.rb +46 -34
- data/lib/aspera/cli/plugin.rb +9 -4
- data/lib/aspera/cli/plugins/alee.rb +1 -1
- data/lib/aspera/cli/plugins/aoc.rb +207 -182
- data/lib/aspera/cli/plugins/ats.rb +2 -2
- data/lib/aspera/cli/plugins/config.rb +173 -117
- data/lib/aspera/cli/plugins/console.rb +2 -2
- data/lib/aspera/cli/plugins/faspex.rb +51 -36
- data/lib/aspera/cli/plugins/faspex5.rb +82 -41
- data/lib/aspera/cli/plugins/node.rb +3 -3
- data/lib/aspera/cli/plugins/preview.rb +35 -25
- data/lib/aspera/cli/plugins/server.rb +23 -8
- data/lib/aspera/cli/transfer_agent.rb +7 -6
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cos_node.rb +33 -28
- data/lib/aspera/environment.rb +2 -2
- data/lib/aspera/fasp/connect.rb +28 -21
- data/lib/aspera/fasp/http_gw.rb +140 -28
- data/lib/aspera/fasp/installation.rb +101 -53
- data/lib/aspera/fasp/local.rb +88 -45
- data/lib/aspera/fasp/manager.rb +15 -0
- data/lib/aspera/fasp/node.rb +4 -4
- data/lib/aspera/fasp/parameters.rb +6 -18
- data/lib/aspera/fasp/resume_policy.rb +13 -12
- data/lib/aspera/log.rb +1 -1
- data/lib/aspera/node.rb +61 -1
- data/lib/aspera/oauth.rb +49 -46
- data/lib/aspera/persistency_folder.rb +9 -4
- data/lib/aspera/preview/file_types.rb +53 -21
- data/lib/aspera/preview/generator.rb +3 -3
- data/lib/aspera/rest.rb +29 -18
- data/lib/aspera/secrets.rb +20 -0
- data/lib/aspera/temp_file_manager.rb +19 -0
- data/lib/aspera/web_auth.rb +105 -0
- metadata +42 -22
data/docs/test_env.conf
CHANGED
@@ -5,8 +5,8 @@ default:
|
|
5
5
|
config: cli_default
|
6
6
|
aoc: tst_aoc1
|
7
7
|
faspex: tst_faspex
|
8
|
-
faspex5:
|
9
|
-
shares:
|
8
|
+
faspex5: tst_faspex5_web
|
9
|
+
shares: tst_shares_1
|
10
10
|
shares2: tst_shares2
|
11
11
|
node: tst_node
|
12
12
|
server: tst_server
|
@@ -19,7 +19,6 @@ default:
|
|
19
19
|
cli_default:
|
20
20
|
interactive: your value here
|
21
21
|
smtp: your value here
|
22
|
-
ascp_path: your value here
|
23
22
|
local_user:
|
24
23
|
ssh_keys: your value here
|
25
24
|
smtp_config:
|
@@ -48,10 +47,24 @@ tst_node_faspex:
|
|
48
47
|
url: your value here
|
49
48
|
username: your value here
|
50
49
|
password: your value here
|
51
|
-
|
50
|
+
tst_faspex5_boot:
|
52
51
|
url: your value here
|
52
|
+
auth: your value here
|
53
53
|
username: your value here
|
54
54
|
password: your value here
|
55
|
+
tst_faspex5_web:
|
56
|
+
url: your value here
|
57
|
+
auth: your value here
|
58
|
+
redirect_uri: your value here
|
59
|
+
client_id: your value here
|
60
|
+
client_secret: your value here
|
61
|
+
tst_faspex5:
|
62
|
+
url: your value here
|
63
|
+
auth: your value here
|
64
|
+
client_id: your value here
|
65
|
+
client_secret: your value here
|
66
|
+
private_key: your value here
|
67
|
+
username: your value here
|
55
68
|
tst_shares:
|
56
69
|
url: your value here
|
57
70
|
username: your value here
|
@@ -94,6 +107,7 @@ tst_ak_preview:
|
|
94
107
|
url: your value here
|
95
108
|
username: your value here
|
96
109
|
password: your value here
|
110
|
+
mimemagic: your value here
|
97
111
|
tst_node_preview:
|
98
112
|
url: your value here
|
99
113
|
username: your value here
|
@@ -110,6 +124,8 @@ misc:
|
|
110
124
|
faspex_publink_send_to_fxuser: your value here
|
111
125
|
faspex_publink_send_to_dropbox: your value here
|
112
126
|
shares_upload: your value here
|
127
|
+
console_smart_id: your value here
|
128
|
+
console_smart_file: your value here
|
113
129
|
orch_workflow_id: your value here
|
114
130
|
file_dcm: your value here
|
115
131
|
file_pdf: your value here
|
@@ -122,6 +138,7 @@ misc:
|
|
122
138
|
aoc_publink_recv_from_aocuser: your value here
|
123
139
|
aoc_publink_send_shd_inbox: your value here
|
124
140
|
aoc_publink_send_aoc_user: your value here
|
141
|
+
aoc_publink_send_use_pass: your value here
|
125
142
|
aoc_publink_folder: your value here
|
126
143
|
aoc_shbx_ws: your value here
|
127
144
|
aoc_shbx_name: your value here
|
@@ -137,4 +154,5 @@ misc:
|
|
137
154
|
email_internal: your value here
|
138
155
|
email_external: your value here
|
139
156
|
aoc_org: your value here
|
140
|
-
|
157
|
+
aoc_user_email: your value here
|
158
|
+
http_gw_fqdn_port: your value here
|
data/docs/transfer_spec.html
CHANGED
@@ -28,7 +28,7 @@ arg: related ascp argument or env var suffix (PASS for ASPERA_SCP_PASS)
|
|
28
28
|
</p>
|
29
29
|
<p>
|
30
30
|
UNDER CONSTRUCTION<br/>
|
31
|
-
<a href="https://developer.ibm.com/
|
31
|
+
<a href="https://developer.ibm.com/apis/catalog/?search=aspera">Aspera API Documentation</a>→Node API→/opt/transfers<br/>
|
32
32
|
</p>
|
33
33
|
|
34
34
|
<table>
|
data/examples/aoc.rb
CHANGED
@@ -4,11 +4,22 @@ require 'aspera/log'
|
|
4
4
|
|
5
5
|
Aspera::Log.instance.level=:debug
|
6
6
|
|
7
|
+
if ! ARGV.length.eql?(3)
|
8
|
+
Aspera::Log.log.error("wrong number of args: #{ARGV.length}")
|
9
|
+
Aspera::Log.log.error("Usage: #{$0} <aoc URL> <aoc username> <aoc private key content>")
|
10
|
+
Aspera::Log.log.error("Example: #{$0} https://myorg.ibmaspera.com john@example.com $(cat /home/john/my_key.pem)")
|
11
|
+
Process.exit(1)
|
12
|
+
end
|
13
|
+
|
14
|
+
aoc_url=ARGV[0]
|
15
|
+
aoc_user=ARGV[1]
|
16
|
+
aoc_key_value=ARGV[2]
|
17
|
+
|
7
18
|
aocapi=Aspera::AoC.new(
|
8
|
-
url:
|
19
|
+
url: aoc_url,
|
9
20
|
auth: :jwt,
|
10
|
-
private_key:
|
11
|
-
username:
|
21
|
+
private_key: aoc_key_value,
|
22
|
+
username: aoc_user,
|
12
23
|
scope: 'user:all',
|
13
24
|
subpath: 'api/v1')
|
14
25
|
|
data/examples/faspex4.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'aspera/rest'
|
3
|
+
require 'aspera/log'
|
4
|
+
require 'aspera/fasp/local'
|
5
|
+
require 'aspera/cli/listener/line_dump'
|
6
|
+
require 'aspera/cli/extended_value'
|
7
|
+
|
8
|
+
# set high log level for the example
|
9
|
+
Aspera::Log.instance.level=:debug
|
10
|
+
|
11
|
+
# set folder where SDK is installed
|
12
|
+
# (if ascp is not there, the lib will try to find in usual locations)
|
13
|
+
Aspera::Fasp::Installation.instance.folder='.'
|
14
|
+
|
15
|
+
if ! ARGV.length.eql?(3)
|
16
|
+
Aspera::Log.log.error("wrong number of args: #{ARGV.length}")
|
17
|
+
Aspera::Log.log.error("Usage: #{$0} <faspex URL> <faspex username> <faspex password>")
|
18
|
+
Aspera::Log.log.error("Example: #{$0} https://faspex.com/aspera/faspex john p@sSw0rd")
|
19
|
+
Process.exit(1)
|
20
|
+
end
|
21
|
+
|
22
|
+
faspex_url=ARGV[0]
|
23
|
+
faspex_user=ARGV[1]
|
24
|
+
faspex_pass=ARGV[2]
|
25
|
+
|
26
|
+
# 1: demo API v3
|
27
|
+
#---------------
|
28
|
+
|
29
|
+
# create REST API object
|
30
|
+
api_v3=Aspera::Rest.new({
|
31
|
+
:base_url => faspex_url,
|
32
|
+
:auth => {
|
33
|
+
:type => :basic,
|
34
|
+
:username => faspex_user,
|
35
|
+
:password => faspex_pass
|
36
|
+
}})
|
37
|
+
|
38
|
+
api_v3.read('me')
|
39
|
+
|
40
|
+
# 2: send a package
|
41
|
+
#---------------
|
42
|
+
|
43
|
+
# create a sample file to send
|
44
|
+
file_to_send='./myfile.bin'
|
45
|
+
File.open(file_to_send, "w") {|f| f.write("sample data") }
|
46
|
+
# package creation parameters
|
47
|
+
package_create_params={'delivery'=>{'title'=>'test package','recipients'=>['aspera.user1@gmail.com'],'sources'=>[{'paths'=>[file_to_send]}]}}
|
48
|
+
pkg_created=api_v3.create('send',package_create_params)[:data]
|
49
|
+
# get transfer specification
|
50
|
+
transfer_spec=pkg_created['xfer_sessions'].first
|
51
|
+
# set paths of files to send
|
52
|
+
transfer_spec['paths']=['source'=>file_to_send]
|
53
|
+
# get the local agent (i.e. ascp)
|
54
|
+
client=Aspera::Fasp::Local.new
|
55
|
+
# disable ascp output on stdout to not mix with JSON events
|
56
|
+
client.quiet=true
|
57
|
+
# start transfer (asynchronous)
|
58
|
+
job_id=client.start_transfer(transfer_spec)
|
59
|
+
result=client.wait_for_transfers_completion
|
60
|
+
# notify of any transfer error
|
61
|
+
result.select{|i|!i.eql?(:success)}.each do |e|
|
62
|
+
Aspera::Log.log.error("A transfer error occured: #{e.message}")
|
63
|
+
end
|
64
|
+
|
65
|
+
# 1: demo API v4
|
66
|
+
#---------------
|
67
|
+
api_v4=Aspera::Rest.new({
|
68
|
+
:base_url => faspex_url+'/api',
|
69
|
+
:auth => {
|
70
|
+
:type => :oauth2,
|
71
|
+
:base_url => faspex_url+'/auth/oauth2',
|
72
|
+
:grant => :header_userpass,
|
73
|
+
:user_name => faspex_user,
|
74
|
+
:user_pass => faspex_pass,
|
75
|
+
:scope => 'admin'
|
76
|
+
}})
|
77
|
+
|
78
|
+
Aspera::Log.dump('users',api_v4.read('users')[:data])
|
data/lib/aspera/aoc.rb
CHANGED
@@ -9,6 +9,8 @@ module Aspera
|
|
9
9
|
private
|
10
10
|
@@use_standard_ports = true
|
11
11
|
|
12
|
+
API_V1='api/v1'
|
13
|
+
|
12
14
|
PRODUCT_NAME='Aspera on Cloud'
|
13
15
|
# Production domain of AoC
|
14
16
|
PROD_DOMAIN='ibmaspera.com'
|
@@ -42,11 +44,6 @@ module Aspera
|
|
42
44
|
FILES_APP='files'
|
43
45
|
PACKAGES_APP='packages'
|
44
46
|
|
45
|
-
CLIENT_RANDOM={
|
46
|
-
'aspera.global-cli-client' => 'frpmsRsG4mjZ0PlxCgdJlvONqBg4Vlpz_IX7gXmBMAfsgMLy2FO6CXLodKfKAuhqnCqSptLbe_wdmnm9JRuEPO-PpFqpq_Kb',
|
47
|
-
'aspera.drive' => 'UegzQ3LcbLht5dLYAXaR-7ZMnJ6-kwPEXWEXaqLSOMGmtzNA9r6kPFLElqBfq66BfgMabdO96k5sPXV-H8M3vsx9LbGlewF1'
|
48
|
-
}
|
49
|
-
|
50
47
|
def self.get_client_info(client_name=CLIENT_APPS.first)
|
51
48
|
client_index=CLIENT_APPS.index(client_name)
|
52
49
|
raise "no such pre-defined client: #{client_name}" if client_index.nil?
|
@@ -68,9 +65,14 @@ module Aspera
|
|
68
65
|
return organization,instance_domain
|
69
66
|
end
|
70
67
|
|
68
|
+
# base API url depends on domain, which could be "qa.xxx"
|
69
|
+
def self.api_base_url(api_domain=PROD_DOMAIN)
|
70
|
+
return "https://api.#{api_domain}"
|
71
|
+
end
|
72
|
+
|
71
73
|
def self.metering_api(entitlement_id,customer_id,api_domain=PROD_DOMAIN)
|
72
74
|
return Rest.new({
|
73
|
-
:base_url => "
|
75
|
+
:base_url => "#{api_base_url(api_domain)}/metering/v1",
|
74
76
|
:headers => {'X-Aspera-Entitlement-Authorization' => Rest.basic_creds(entitlement_id,customer_id)}
|
75
77
|
})
|
76
78
|
end
|
@@ -121,12 +123,12 @@ module Aspera
|
|
121
123
|
raise RuntimeError,'too many redirections'
|
122
124
|
end
|
123
125
|
|
124
|
-
# @param :link,:url,:auth,:client_id,:client_secret,:scope,:redirect_uri,:private_key,:username,:subpath
|
126
|
+
# @param :link,:url,:auth,:client_id,:client_secret,:scope,:redirect_uri,:private_key,:username,:subpath,:password (for pub link)
|
125
127
|
def initialize(opt)
|
126
128
|
# access key secrets are provided out of band to get node api access
|
127
129
|
# key: access key
|
128
130
|
# value: associated secret
|
129
|
-
@
|
131
|
+
@key_chain=nil
|
130
132
|
|
131
133
|
# init rest params
|
132
134
|
aoc_rest_p={:auth=>{:type =>:oauth2}}
|
@@ -147,12 +149,12 @@ module Aspera
|
|
147
149
|
|
148
150
|
# get org name and domain from url
|
149
151
|
organization,instance_domain=self.class.parse_url(opt[:url])
|
150
|
-
# this is the base API url
|
151
|
-
api_base_url
|
152
|
-
#
|
153
|
-
aoc_rest_p[:base_url]="#{
|
152
|
+
# this is the base API url
|
153
|
+
api_url_base=self.class.api_base_url(instance_domain)
|
154
|
+
# API URL, including subpath (version ...)
|
155
|
+
aoc_rest_p[:base_url]="#{api_url_base}/#{opt[:subpath]}"
|
154
156
|
# base auth URL
|
155
|
-
aoc_auth_p[:base_url] = "#{
|
157
|
+
aoc_auth_p[:base_url] = "#{api_url_base}/#{OAUTH_API_SUBPATH}/#{organization}"
|
156
158
|
aoc_auth_p[:client_id]=opt[:client_id]
|
157
159
|
aoc_auth_p[:client_secret] = opt[:client_secret]
|
158
160
|
|
@@ -185,23 +187,19 @@ module Aspera
|
|
185
187
|
aoc_auth_p[:jwt_subject] = opt[:username]
|
186
188
|
aoc_auth_p[:jwt_private_key_obj] = OpenSSL::PKey::RSA.new(private_key_PEM_string)
|
187
189
|
when :url_token
|
190
|
+
aoc_auth_p[:password]=opt[:password] unless opt[:password].nil?
|
188
191
|
# nothing more
|
189
192
|
else raise "ERROR: unsupported auth method: #{aoc_auth_p[:grant]}"
|
190
193
|
end
|
191
194
|
super(aoc_rest_p)
|
192
195
|
end
|
193
196
|
|
194
|
-
def
|
195
|
-
@
|
196
|
-
|
197
|
+
def key_chain=(keychain)
|
198
|
+
raise "keychain already set" unless @key_chain.nil?
|
199
|
+
@key_chain=keychain
|
197
200
|
nil
|
198
201
|
end
|
199
202
|
|
200
|
-
def has_secret(ak)
|
201
|
-
Log.log.debug("has key:#{ak} -> #{@secrets.has_key?(ak)}")
|
202
|
-
return @secrets.has_key?(ak)
|
203
|
-
end
|
204
|
-
|
205
203
|
# additional transfer spec (tags) for package information
|
206
204
|
def self.package_tags(package_info,operation)
|
207
205
|
return {'tags'=>{'aspera'=>{'files'=>{
|
@@ -293,13 +291,14 @@ module Aspera
|
|
293
291
|
# no scope: requires secret
|
294
292
|
# if secret provided beforehand: use it
|
295
293
|
def get_node_api(node_info,node_scope=nil)
|
294
|
+
# X-Aspera-AccessKey required for bearer token only
|
296
295
|
node_rest_params={
|
297
296
|
:base_url => node_info['url'],
|
298
297
|
:headers => {'X-Aspera-AccessKey'=>node_info['access_key']},
|
299
298
|
}
|
300
|
-
ak_secret=@
|
299
|
+
ak_secret=@key_chain.get_secret(node_info['access_key'],false)
|
301
300
|
if ak_secret.nil? and node_scope.nil?
|
302
|
-
raise
|
301
|
+
raise "There must be at least one of: 'secret' or 'scope' for access key #{node_info['access_key']}"
|
303
302
|
end
|
304
303
|
# if secret provided on command line or if there is no scope
|
305
304
|
if !ak_secret.nil? or node_scope.nil?
|
@@ -312,7 +311,7 @@ module Aspera
|
|
312
311
|
node_rest_params[:auth]=self.params[:auth].clone
|
313
312
|
node_rest_params[:auth][:scope]=self.class.node_scope(node_info['access_key'],node_scope)
|
314
313
|
end
|
315
|
-
return
|
314
|
+
return Node.new(node_rest_params)
|
316
315
|
end
|
317
316
|
|
318
317
|
# check that parameter has necessary types
|
@@ -328,102 +327,82 @@ module Aspera
|
|
328
327
|
return node_info,file_id
|
329
328
|
end
|
330
329
|
|
331
|
-
#
|
332
|
-
def
|
333
|
-
|
334
|
-
|
330
|
+
# add entry to list if test block is success
|
331
|
+
def process_find_files(entry,path)
|
332
|
+
begin
|
333
|
+
# add to result if match filter
|
334
|
+
@find_state[:found].push(entry.merge({'path'=>path})) if @find_state[:test_block].call(entry)
|
335
|
+
# process link
|
336
|
+
if entry[:type].eql?('link')
|
337
|
+
sub_node_info=self.read("nodes/#{entry['target_node_id']}")[:data]
|
338
|
+
sub_opt={method: process_find_files, top_file_id: entry['target_id'], top_file_path: path}
|
339
|
+
get_node_api(sub_node_info,SCOPE_NODE_USER).crawl(self,sub_opt)
|
340
|
+
end
|
341
|
+
rescue => e
|
342
|
+
Log.log.error("#{path}: #{e.message}")
|
343
|
+
end
|
344
|
+
# process all folders
|
345
|
+
return true
|
335
346
|
end
|
336
347
|
|
337
|
-
# @returns list of file paths that match given regex
|
338
348
|
def find_files( top_node_file, test_block )
|
339
349
|
top_node_info,top_file_id=check_get_node_file(top_node_file)
|
340
350
|
Log.log.debug("find_files: node_info=#{top_node_info}, fileid=#{top_file_id}")
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
351
|
+
@find_state={found: [], test_block: test_block}
|
352
|
+
get_node_api(top_node_info,SCOPE_NODE_USER).crawl(self,{method: :process_find_files, top_file_id: top_file_id})
|
353
|
+
result=@find_state[:found]
|
354
|
+
@find_state=nil
|
355
|
+
return result
|
356
|
+
end
|
357
|
+
|
358
|
+
def process_resolve_node_file(entry,path)
|
359
|
+
# stop digging here if not in right path
|
360
|
+
return false unless entry['name'].eql?(@resolve_state[:path].first)
|
361
|
+
# ok it matches, so we remove the match
|
362
|
+
@resolve_state[:path].shift
|
363
|
+
case entry['type']
|
364
|
+
when 'file'
|
365
|
+
# file must be terminal
|
366
|
+
raise "#{entry['name']} is a file, expecting folder to find: #{@resolve_state[:path]}" unless @resolve_state[:path].empty?
|
367
|
+
@resolve_state[:result][:file_id]=entry['id']
|
368
|
+
when 'link'
|
369
|
+
@resolve_state[:result][:node_info]=self.read("nodes/#{entry['target_node_id']}")[:data]
|
370
|
+
if @resolve_state[:path].empty?
|
371
|
+
@resolve_state[:result][:file_id]=entry['target_id']
|
372
|
+
else
|
373
|
+
get_node_api(@resolve_state[:result][:node_info],SCOPE_NODE_USER).crawl(self,{method: :process_resolve_node_file, top_file_id: entry['target_id']})
|
356
374
|
end
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
begin
|
363
|
-
# does item match ?
|
364
|
-
result.push(current_file_info.merge({'path'=>item_path})) if test_block.call(current_file_info)
|
365
|
-
# does it need further processing ?
|
366
|
-
case current_file_info['type']
|
367
|
-
when 'file'
|
368
|
-
Log.log.debug("testing : #{current_file_info['name']}")
|
369
|
-
when 'folder'
|
370
|
-
items_to_explore.push({:node_api=>current_item[:node_api],:folder_id=>current_file_info['id'],:path=>item_path})
|
371
|
-
when 'link' # .*.asp-lnk
|
372
|
-
items_to_explore.push(read_asplnk(current_file_info).merge({:path=>item_path}))
|
373
|
-
else
|
374
|
-
Log.log.error("unknown folder item type: #{current_file_info['type']}")
|
375
|
-
end
|
376
|
-
rescue => e
|
377
|
-
Log.log.error("#{item_path}: #{e.message}")
|
378
|
-
end
|
375
|
+
when 'folder'
|
376
|
+
if @resolve_state[:path].empty?
|
377
|
+
# found: store
|
378
|
+
@resolve_state[:result][:file_id]=entry['id']
|
379
|
+
return false
|
379
380
|
end
|
381
|
+
else
|
382
|
+
Log.log.warn("unknown element type: #{entry['type']}")
|
380
383
|
end
|
381
|
-
|
384
|
+
# continue to dig folder
|
385
|
+
return true
|
382
386
|
end
|
383
387
|
|
384
|
-
# @return
|
388
|
+
# @return Array(node_info,file_id) for the given path
|
389
|
+
# @param top_node_file Array [root node,file id]
|
390
|
+
# @param element_path_string String path of element
|
385
391
|
# supports links to secondary nodes
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
current_node_api=get_node_api(current_node_info,SCOPE_NODE_USER) if current_node_api.nil?
|
399
|
-
# get folder content
|
400
|
-
folder_contents = current_node_api.read("files/#{current_file_id}/files")
|
401
|
-
Log.dump(:folder_contents,folder_contents)
|
402
|
-
matching_folders = folder_contents[:data].select { |i| i['name'].eql?(current_item)}
|
403
|
-
#Log.log.debug "matching_folders: #{matching_folders}"
|
404
|
-
raise "no such folder: #{current_item} in #{folder_contents[:data].map { |i| i['name']}}" if matching_folders.empty?
|
405
|
-
current_file_info = matching_folders.first
|
406
|
-
# process type of file
|
407
|
-
case current_file_info['type']
|
408
|
-
when 'file'
|
409
|
-
current_file_id=current_file_info['id']
|
410
|
-
# a file shall be terminal
|
411
|
-
if !items_to_explore.empty? then
|
412
|
-
raise "#{current_item} is a file, expecting folder to find: #{items_to_explore}"
|
413
|
-
end
|
414
|
-
when 'link'
|
415
|
-
current_node_info=self.read("nodes/#{current_file_info['target_node_id']}")[:data]
|
416
|
-
current_file_id=current_file_info['target_id']
|
417
|
-
# need to switch node
|
418
|
-
current_node_api=nil
|
419
|
-
when 'folder'
|
420
|
-
current_file_id=current_file_info['id']
|
421
|
-
else
|
422
|
-
Log.log.warn("unknown element type: #{current_file_info['type']}")
|
423
|
-
end
|
392
|
+
def resolve_node_file( top_node_file, element_path_string )
|
393
|
+
top_node_info,top_file_id=check_get_node_file(top_node_file)
|
394
|
+
path_elements=element_path_string.split(PATH_SEPARATOR).select{|i| !i.empty?}
|
395
|
+
result={node_info: top_node_info, file_id: nil}
|
396
|
+
if path_elements.empty?
|
397
|
+
result[:file_id]=top_file_id
|
398
|
+
else
|
399
|
+
@resolve_state={path: path_elements, result: result}
|
400
|
+
get_node_api(top_node_info,SCOPE_NODE_USER).crawl(self,{method: :process_resolve_node_file, top_file_id: top_file_id})
|
401
|
+
not_found=@resolve_state[:path]
|
402
|
+
@resolve_state=nil
|
403
|
+
raise "entry not found: #{not_found}" if result[:file_id].nil?
|
424
404
|
end
|
425
|
-
|
426
|
-
return {node_info: current_node_info, file_id: current_file_id}
|
405
|
+
return result
|
427
406
|
end
|
428
407
|
|
429
408
|
end # AoC
|