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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/README.md +1267 -999
- data/bin/ascli +20 -1
- data/bin/asession +37 -34
- data/docs/test_env.conf +7 -3
- data/examples/aoc.rb +13 -12
- data/examples/dascli +23 -0
- data/examples/faspex4.rb +34 -29
- data/examples/{transfer.rb → node.rb} +31 -59
- data/examples/server.rb +93 -0
- data/lib/aspera/aoc.rb +153 -143
- data/lib/aspera/ascmd.rb +56 -45
- data/lib/aspera/ats_api.rb +9 -6
- data/lib/aspera/cli/basic_auth_plugin.rb +18 -16
- data/lib/aspera/cli/extended_value.rb +33 -30
- data/lib/aspera/cli/formater.rb +105 -111
- data/lib/aspera/cli/info.rb +3 -2
- data/lib/aspera/cli/listener/line_dump.rb +1 -0
- data/lib/aspera/cli/listener/logger.rb +1 -0
- data/lib/aspera/cli/listener/progress.rb +13 -12
- data/lib/aspera/cli/listener/progress_multi.rb +21 -20
- data/lib/aspera/cli/main.rb +110 -90
- data/lib/aspera/cli/manager.rb +99 -88
- data/lib/aspera/cli/plugin.rb +98 -39
- data/lib/aspera/cli/plugins/alee.rb +6 -5
- data/lib/aspera/cli/plugins/aoc.rb +581 -450
- data/lib/aspera/cli/plugins/ats.rb +84 -83
- data/lib/aspera/cli/plugins/bss.rb +30 -27
- data/lib/aspera/cli/plugins/config.rb +488 -397
- data/lib/aspera/cli/plugins/console.rb +17 -15
- data/lib/aspera/cli/plugins/cos.rb +26 -35
- data/lib/aspera/cli/plugins/faspex.rb +206 -172
- data/lib/aspera/cli/plugins/faspex5.rb +109 -74
- data/lib/aspera/cli/plugins/node.rb +379 -189
- data/lib/aspera/cli/plugins/orchestrator.rb +71 -65
- data/lib/aspera/cli/plugins/preview.rb +131 -122
- data/lib/aspera/cli/plugins/server.rb +50 -150
- data/lib/aspera/cli/plugins/shares.rb +61 -27
- data/lib/aspera/cli/plugins/sync.rb +15 -14
- data/lib/aspera/cli/transfer_agent.rb +75 -64
- data/lib/aspera/cli/version.rb +2 -1
- data/lib/aspera/colors.rb +29 -28
- data/lib/aspera/command_line_builder.rb +50 -43
- data/lib/aspera/cos_node.rb +64 -38
- data/lib/aspera/data_repository.rb +1 -0
- data/lib/aspera/environment.rb +33 -10
- data/lib/aspera/fasp/agent_base.rb +35 -30
- data/lib/aspera/fasp/agent_connect.rb +35 -30
- data/lib/aspera/fasp/agent_direct.rb +68 -60
- data/lib/aspera/fasp/agent_httpgw.rb +71 -64
- data/lib/aspera/fasp/agent_node.rb +24 -23
- data/lib/aspera/fasp/agent_trsdk.rb +19 -20
- data/lib/aspera/fasp/error.rb +2 -1
- data/lib/aspera/fasp/error_info.rb +79 -68
- data/lib/aspera/fasp/installation.rb +130 -126
- data/lib/aspera/fasp/listener.rb +1 -0
- data/lib/aspera/fasp/parameters.rb +71 -60
- data/lib/aspera/fasp/parameters.yaml +69 -17
- data/lib/aspera/fasp/resume_policy.rb +14 -11
- data/lib/aspera/fasp/transfer_spec.rb +6 -5
- data/lib/aspera/fasp/uri.rb +25 -24
- data/lib/aspera/faspex_gw.rb +83 -72
- data/lib/aspera/hash_ext.rb +23 -13
- data/lib/aspera/id_generator.rb +16 -13
- data/lib/aspera/keychain/encrypted_hash.rb +61 -46
- data/lib/aspera/keychain/macos_security.rb +26 -24
- data/lib/aspera/log.rb +35 -39
- data/lib/aspera/nagios.rb +36 -28
- data/lib/aspera/node.rb +19 -19
- data/lib/aspera/oauth.rb +120 -100
- data/lib/aspera/open_application.rb +25 -22
- data/lib/aspera/persistency_action_once.rb +9 -8
- data/lib/aspera/persistency_folder.rb +13 -9
- data/lib/aspera/preview/file_types.rb +261 -266
- data/lib/aspera/preview/generator.rb +74 -73
- data/lib/aspera/preview/image_error.png +0 -0
- data/lib/aspera/preview/options.rb +7 -6
- data/lib/aspera/preview/utils.rb +30 -33
- data/lib/aspera/preview/video_error.png +0 -0
- data/lib/aspera/proxy_auto_config.rb +27 -23
- data/lib/aspera/rest.rb +73 -74
- data/lib/aspera/rest_call_error.rb +1 -0
- data/lib/aspera/rest_error_analyzer.rb +23 -19
- data/lib/aspera/rest_errors_aspera.rb +43 -40
- data/lib/aspera/secret_hider.rb +74 -0
- data/lib/aspera/ssh.rb +13 -10
- data/lib/aspera/sync.rb +49 -47
- data/lib/aspera/temp_file_manager.rb +7 -5
- data/lib/aspera/timer_limiter.rb +9 -8
- data/lib/aspera/uri_reader.rb +17 -18
- data/lib/aspera/web_auth.rb +17 -15
- data.tar.gz.sig +5 -0
- metadata +119 -35
- metadata.gz.sig +0 -0
- data/bin/dascli +0 -13
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'aspera/cli/plugins/node'
|
3
4
|
require 'aspera/cli/plugins/ats'
|
4
5
|
require 'aspera/cli/basic_auth_plugin'
|
@@ -18,161 +19,166 @@ module Aspera
|
|
18
19
|
class Aoc < BasicAuthPlugin
|
19
20
|
class << self
|
20
21
|
def detect(base_url)
|
21
|
-
api=Rest.new({base_url: base_url})
|
22
|
+
api = Rest.new({base_url: base_url})
|
22
23
|
# either in standard domain, or product name in page
|
23
24
|
if URI.parse(base_url).host.end_with?(Aspera::AoC::PROD_DOMAIN) ||
|
24
|
-
api.call({operation: 'GET', redirect_max: 1, headers: {'Accept'=>'text/html'}})[:http].body.include?(Aspera::AoC::PRODUCT_NAME)
|
25
|
+
api.call({operation: 'GET', redirect_max: 1, headers: {'Accept' => 'text/html'}})[:http].body.include?(Aspera::AoC::PRODUCT_NAME)
|
25
26
|
return {product: :aoc,version: 'SaaS' }
|
26
27
|
end
|
27
28
|
return nil
|
28
29
|
end
|
29
30
|
end
|
30
31
|
# special value for package id
|
31
|
-
VAL_ALL='ALL'
|
32
|
-
ID_AK_ADMIN='ASPERA_ACCESS_KEY_ADMIN'
|
32
|
+
VAL_ALL = 'ALL'
|
33
|
+
ID_AK_ADMIN = 'ASPERA_ACCESS_KEY_ADMIN'
|
34
|
+
KNOWN_AOC_RES=%i[
|
35
|
+
self organization user group group_membership client contact dropbox node operation package saml_configuration
|
36
|
+
workspace workspace_membership dropbox_membership short_link application client_registration_token client_access_key
|
37
|
+
kms_profile].freeze
|
33
38
|
|
34
39
|
def initialize(env)
|
35
40
|
super(env)
|
36
|
-
@
|
37
|
-
@
|
38
|
-
@
|
39
|
-
@
|
40
|
-
@
|
41
|
-
@api_aoc=nil
|
42
|
-
@url_token_data=nil
|
43
|
-
@api_aoc=nil
|
41
|
+
@workspace_info = nil
|
42
|
+
@persist_ids = nil
|
43
|
+
@home_node_file = nil
|
44
|
+
@api_aoc = nil
|
45
|
+
@url_token_data = nil
|
46
|
+
@api_aoc = nil
|
44
47
|
options.add_opt_list(:auth,Oauth::STD_AUTH_TYPES,'OAuth type of authentication')
|
45
|
-
options.add_opt_list(:operation,[
|
48
|
+
options.add_opt_list(:operation, %i[push pull],'client operation for transfers')
|
46
49
|
options.add_opt_simple(:client_id,'OAuth API client identifier in application')
|
47
50
|
options.add_opt_simple(:client_secret,'OAuth API client passcode')
|
48
51
|
options.add_opt_simple(:redirect_uri,'OAuth API client redirect URI')
|
49
|
-
options.add_opt_simple(:private_key,'OAuth JWT RSA private key PEM value (prefix file path with @
|
52
|
+
options.add_opt_simple(:private_key,'OAuth JWT RSA private key PEM value (prefix file path with @file:)')
|
53
|
+
options.add_opt_simple(:passphrase,'RSA private key passphrase')
|
50
54
|
options.add_opt_simple(:workspace,'name of workspace')
|
51
55
|
options.add_opt_simple(:name,'resource name')
|
52
56
|
options.add_opt_simple(:path,'file or folder path')
|
53
57
|
options.add_opt_simple(:link,'public link to shared resource')
|
54
|
-
options.add_opt_simple(:new_user_option,'new user creation option')
|
58
|
+
options.add_opt_simple(:new_user_option,'new user creation option for unknown package recipients')
|
55
59
|
options.add_opt_simple(:from_folder,'share to share source folder')
|
56
60
|
options.add_opt_simple(:scope,'OAuth scope for AoC API calls')
|
57
|
-
options.add_opt_boolean(:bulk,'bulk operation')
|
58
61
|
options.add_opt_boolean(:default_ports,'use standard FASP ports or get from node api')
|
59
|
-
options.
|
62
|
+
options.add_opt_boolean(:validate_metadata,'validate shared inbox metadata')
|
60
63
|
options.set_option(:default_ports,:yes)
|
61
|
-
options.set_option(:
|
64
|
+
options.set_option(:validate_metadata,:yes)
|
65
|
+
options.set_option(:new_user_option,{'package_contact' => true})
|
62
66
|
options.set_option(:operation,:push)
|
63
67
|
options.set_option(:auth,:jwt)
|
64
68
|
options.set_option(:scope,AoC::SCOPE_FILES_USER)
|
65
|
-
options.set_option(:private_key,'@file:'+env[:private_key_path]) if env[:private_key_path].is_a?(String)
|
69
|
+
options.set_option(:private_key,'@file:' + env[:private_key_path]) if env[:private_key_path].is_a?(String)
|
70
|
+
options.set_option(:workspace,:default)
|
66
71
|
options.parse_options!
|
67
|
-
AoC.use_standard_ports=options.get_option(:default_ports)
|
72
|
+
AoC.use_standard_ports = options.get_option(:default_ports)
|
68
73
|
return if env[:man_only]
|
69
74
|
end
|
70
75
|
|
71
76
|
def aoc_api
|
72
77
|
if @api_aoc.nil?
|
73
|
-
@api_aoc=AoC.new(aoc_params(AoC::API_V1))
|
78
|
+
@api_aoc = AoC.new(aoc_params(AoC::API_V1))
|
74
79
|
# add keychain for access key secrets
|
75
|
-
@api_aoc.key_chain
|
80
|
+
@api_aoc.key_chain = @agents[:config]
|
76
81
|
end
|
77
82
|
return @api_aoc
|
78
83
|
end
|
79
84
|
|
80
85
|
# starts transfer using transfer agent
|
81
86
|
def transfer_start(app,direction,node_file,ts_add)
|
82
|
-
ts_add.deep_merge!(AoC.analytics_ts(app,direction,@
|
87
|
+
ts_add.deep_merge!(AoC.analytics_ts(app,direction,@workspace_info['id'],@workspace_info['name']))
|
83
88
|
ts_add.deep_merge!(aoc_api.console_ts(app))
|
84
89
|
return transfer.start(*aoc_api.tr_spec(app,direction,node_file,ts_add))
|
85
90
|
end
|
86
91
|
|
87
|
-
|
92
|
+
NODE4_CMD_PATH = %i[bearer_token_node node_info browse find]
|
93
|
+
NODE4_COMMANDS = [NODE4_CMD_PATH,%i[mkdir rename delete upload download transfer http_node_download v3 file]].flatten.freeze
|
88
94
|
|
89
95
|
def execute_node_gen4_command(command_repo,top_node_file)
|
90
96
|
case command_repo
|
91
97
|
when :bearer_token_node
|
92
|
-
thepath=options.get_next_argument('path')
|
98
|
+
thepath = options.get_next_argument('path')
|
93
99
|
node_file = aoc_api.resolve_node_file(top_node_file,thepath)
|
94
|
-
node_api=aoc_api.get_node_api(node_file[:node_info], use_secret: false)
|
100
|
+
node_api = aoc_api.get_node_api(node_file[:node_info], use_secret: false)
|
95
101
|
return Main.result_status(node_api.oauth_token)
|
96
102
|
when :node_info
|
97
|
-
thepath=options.get_next_argument('path')
|
103
|
+
thepath = options.get_next_argument('path')
|
98
104
|
node_file = aoc_api.resolve_node_file(top_node_file,thepath)
|
99
|
-
node_api=aoc_api.get_node_api(node_file[:node_info], use_secret: false)
|
105
|
+
node_api = aoc_api.get_node_api(node_file[:node_info], use_secret: false)
|
100
106
|
return {type: :single_object,data: {
|
101
|
-
url:
|
107
|
+
url: node_file[:node_info]['url'],
|
102
108
|
username: node_file[:node_info]['access_key'],
|
103
109
|
password: node_api.oauth_token,
|
104
|
-
root_id:
|
105
|
-
|
110
|
+
root_id: node_file[:file_id]
|
111
|
+
}}
|
106
112
|
when :browse
|
107
|
-
thepath=options.get_next_argument('path')
|
113
|
+
thepath = options.get_next_argument('path')
|
108
114
|
node_file = aoc_api.resolve_node_file(top_node_file,thepath)
|
109
|
-
node_api=aoc_api.get_node_api(node_file[:node_info])
|
115
|
+
node_api = aoc_api.get_node_api(node_file[:node_info])
|
110
116
|
file_info = node_api.read("files/#{node_file[:file_id]}")[:data]
|
111
117
|
if file_info['type'].eql?('folder')
|
112
|
-
result=node_api.read("files/#{node_file[:file_id]}/files",options.get_option(:value
|
113
|
-
items=result[:data]
|
118
|
+
result = node_api.read("files/#{node_file[:file_id]}/files",options.get_option(:value))
|
119
|
+
items = result[:data]
|
114
120
|
self.format.display_status("Items: #{result[:data].length}/#{result[:http]['X-Total-Count']}")
|
115
121
|
else
|
116
|
-
items=[file_info]
|
122
|
+
items = [file_info]
|
117
123
|
end
|
118
|
-
return {type: :object_list,data: items,fields: [
|
124
|
+
return {type: :object_list,data: items,fields: %w[name type recursive_size size modified_time access_level]}
|
119
125
|
when :find
|
120
|
-
thepath=options.get_next_argument('path')
|
121
|
-
node_file=aoc_api.resolve_node_file(top_node_file,thepath)
|
122
|
-
test_block=Aspera::Node.file_matcher(options.get_option(:value
|
126
|
+
thepath = options.get_next_argument('path')
|
127
|
+
node_file = aoc_api.resolve_node_file(top_node_file,thepath)
|
128
|
+
test_block = Aspera::Node.file_matcher(options.get_option(:value))
|
123
129
|
return {type: :object_list,data: aoc_api.find_files(node_file,test_block),fields: ['path']}
|
124
130
|
when :mkdir
|
125
|
-
thepath=options.get_next_argument('path')
|
131
|
+
thepath = options.get_next_argument('path')
|
126
132
|
containing_folder_path = thepath.split(AoC::PATH_SEPARATOR)
|
127
|
-
new_folder=containing_folder_path.pop
|
133
|
+
new_folder = containing_folder_path.pop
|
128
134
|
node_file = aoc_api.resolve_node_file(top_node_file,containing_folder_path.join(AoC::PATH_SEPARATOR))
|
129
|
-
node_api=aoc_api.get_node_api(node_file[:node_info])
|
130
|
-
result=node_api.create("files/#{node_file[:file_id]}/files",{name: new_folder,type: :folder})[:data]
|
135
|
+
node_api = aoc_api.get_node_api(node_file[:node_info])
|
136
|
+
result = node_api.create("files/#{node_file[:file_id]}/files",{name: new_folder,type: :folder})[:data]
|
131
137
|
return Main.result_status("created: #{result['name']} (id=#{result['id']})")
|
132
138
|
when :rename
|
133
|
-
thepath=options.get_next_argument('source path')
|
134
|
-
newname=options.get_next_argument('new name')
|
139
|
+
thepath = options.get_next_argument('source path')
|
140
|
+
newname = options.get_next_argument('new name')
|
135
141
|
node_file = aoc_api.resolve_node_file(top_node_file,thepath)
|
136
|
-
node_api=aoc_api.get_node_api(node_file[:node_info])
|
137
|
-
result=node_api.update("files/#{node_file[:file_id]}",{name: newname})[:data]
|
142
|
+
node_api = aoc_api.get_node_api(node_file[:node_info])
|
143
|
+
result = node_api.update("files/#{node_file[:file_id]}",{name: newname})[:data]
|
138
144
|
return Main.result_status("renamed #{thepath} to #{newname}")
|
139
145
|
when :delete
|
140
|
-
thepath=options.get_next_argument('path')
|
141
|
-
return do_bulk_operation(thepath,'deleted','path') do |l_path|
|
146
|
+
thepath = options.get_next_argument('path')
|
147
|
+
return do_bulk_operation(thepath,'deleted',id_result: 'path') do |l_path|
|
142
148
|
raise "expecting String (path), got #{l_path.class.name} (#{l_path})" unless l_path.is_a?(String)
|
143
149
|
node_file = aoc_api.resolve_node_file(top_node_file,l_path)
|
144
|
-
node_api=aoc_api.get_node_api(node_file[:node_info])
|
145
|
-
result=node_api.delete("files/#{node_file[:file_id]}")[:data]
|
146
|
-
{'path'=>l_path}
|
150
|
+
node_api = aoc_api.get_node_api(node_file[:node_info])
|
151
|
+
result = node_api.delete("files/#{node_file[:file_id]}")[:data]
|
152
|
+
{'path' => l_path}
|
147
153
|
end
|
148
154
|
when :transfer
|
149
155
|
# client side is agent
|
150
156
|
# server side is protocol server
|
151
157
|
# in same workspace
|
152
|
-
server_home_node_file=client_home_node_file=top_node_file
|
158
|
+
server_home_node_file = client_home_node_file = top_node_file
|
153
159
|
# default is push
|
154
|
-
case options.get_option(:operation
|
160
|
+
case options.get_option(:operation,is_type: :mandatory)
|
155
161
|
when :push
|
156
|
-
client_tr_oper=Fasp::TransferSpec::DIRECTION_SEND
|
157
|
-
client_folder=options.get_option(:from_folder
|
158
|
-
server_folder=transfer.destination_folder(client_tr_oper)
|
162
|
+
client_tr_oper = Fasp::TransferSpec::DIRECTION_SEND
|
163
|
+
client_folder = options.get_option(:from_folder,is_type: :mandatory)
|
164
|
+
server_folder = transfer.destination_folder(client_tr_oper)
|
159
165
|
when :pull
|
160
|
-
client_tr_oper=Fasp::TransferSpec::DIRECTION_RECEIVE
|
161
|
-
client_folder=transfer.destination_folder(client_tr_oper)
|
162
|
-
server_folder=options.get_option(:from_folder
|
166
|
+
client_tr_oper = Fasp::TransferSpec::DIRECTION_RECEIVE
|
167
|
+
client_folder = transfer.destination_folder(client_tr_oper)
|
168
|
+
server_folder = options.get_option(:from_folder,is_type: :mandatory)
|
163
169
|
end
|
164
170
|
client_node_file = aoc_api.resolve_node_file(client_home_node_file,client_folder)
|
165
171
|
server_node_file = aoc_api.resolve_node_file(server_home_node_file,server_folder)
|
166
172
|
# force node as transfer agent
|
167
|
-
client_node_api=aoc_api.get_node_api(client_node_file[:node_info], use_secret: false)
|
168
|
-
@agents[:transfer].agent_instance=Fasp::AgentNode.new({
|
169
|
-
url:
|
173
|
+
client_node_api = aoc_api.get_node_api(client_node_file[:node_info], use_secret: false)
|
174
|
+
@agents[:transfer].agent_instance = Fasp::AgentNode.new({
|
175
|
+
url: client_node_api.params[:base_url],
|
170
176
|
username: client_node_file[:node_info]['access_key'],
|
171
177
|
password: client_node_api.oauth_token,
|
172
178
|
root_id: client_node_file[:file_id]
|
173
179
|
})
|
174
180
|
# additional node to node TS info
|
175
|
-
add_ts={
|
181
|
+
add_ts = {
|
176
182
|
'remote_access_key' => server_node_file[:node_info]['access_key'],
|
177
183
|
'destination_root_id' => server_node_file[:file_id],
|
178
184
|
'source_root_id' => client_node_file[:file_id]
|
@@ -180,160 +186,187 @@ module Aspera
|
|
180
186
|
return Main.result_transfer(transfer_start(AoC::FILES_APP,client_tr_oper,server_node_file,add_ts))
|
181
187
|
when :upload
|
182
188
|
node_file = aoc_api.resolve_node_file(top_node_file,transfer.destination_folder(Fasp::TransferSpec::DIRECTION_SEND))
|
183
|
-
add_ts={'tags'=>{'aspera'=>{'files'=>{'parentCwd'=>"#{node_file[:node_info]['id']}:#{node_file[:file_id]}"}}}}
|
189
|
+
add_ts = {'tags' => {'aspera' => {'files' => {'parentCwd' => "#{node_file[:node_info]['id']}:#{node_file[:file_id]}"}}}}
|
184
190
|
return Main.result_transfer(transfer_start(AoC::FILES_APP,Fasp::TransferSpec::DIRECTION_SEND,node_file,add_ts))
|
185
191
|
when :download
|
186
|
-
source_paths=transfer.ts_source_paths
|
192
|
+
source_paths = transfer.ts_source_paths
|
187
193
|
# special case for AoC : all files must be in same folder
|
188
|
-
source_folder=source_paths.shift['source']
|
194
|
+
source_folder = source_paths.shift['source']
|
189
195
|
# if a single file: split into folder and path
|
190
196
|
if source_paths.empty?
|
191
|
-
source_folder=source_folder.split(AoC::PATH_SEPARATOR)
|
192
|
-
source_paths=[{'source'=>source_folder.pop}]
|
193
|
-
source_folder=source_folder.join(AoC::PATH_SEPARATOR)
|
197
|
+
source_folder = source_folder.split(AoC::PATH_SEPARATOR)
|
198
|
+
source_paths = [{'source' => source_folder.pop}]
|
199
|
+
source_folder = source_folder.join(AoC::PATH_SEPARATOR)
|
194
200
|
end
|
195
201
|
node_file = aoc_api.resolve_node_file(top_node_file,source_folder)
|
196
202
|
# override paths with just filename
|
197
|
-
add_ts={'tags'=>{'aspera'=>{'files'=>{'parentCwd'=>"#{node_file[:node_info]['id']}:#{node_file[:file_id]}"}}}}
|
198
|
-
add_ts
|
203
|
+
add_ts = {'tags' => {'aspera' => {'files' => {'parentCwd' => "#{node_file[:node_info]['id']}:#{node_file[:file_id]}"}}}}
|
204
|
+
add_ts['paths'] = source_paths
|
199
205
|
return Main.result_transfer(transfer_start(AoC::FILES_APP,Fasp::TransferSpec::DIRECTION_RECEIVE,node_file,add_ts))
|
200
206
|
when :http_node_download
|
201
|
-
source_paths=transfer.ts_source_paths
|
202
|
-
source_folder=source_paths.shift['source']
|
207
|
+
source_paths = transfer.ts_source_paths
|
208
|
+
source_folder = source_paths.shift['source']
|
203
209
|
if source_paths.empty?
|
204
|
-
source_folder=source_folder.split(AoC::PATH_SEPARATOR)
|
205
|
-
source_paths=[{'source'=>source_folder.pop}]
|
206
|
-
source_folder=source_folder.join(AoC::PATH_SEPARATOR)
|
210
|
+
source_folder = source_folder.split(AoC::PATH_SEPARATOR)
|
211
|
+
source_paths = [{'source' => source_folder.pop}]
|
212
|
+
source_folder = source_folder.join(AoC::PATH_SEPARATOR)
|
207
213
|
end
|
208
214
|
raise CliBadArgument,'one file at a time only in HTTP mode' if source_paths.length > 1
|
209
215
|
file_name = source_paths.first['source']
|
210
216
|
node_file = aoc_api.resolve_node_file(top_node_file,File.join(source_folder,file_name))
|
211
|
-
node_api=aoc_api.get_node_api(node_file[:node_info])
|
212
|
-
node_api.call(
|
213
|
-
|
217
|
+
node_api = aoc_api.get_node_api(node_file[:node_info])
|
218
|
+
node_api.call(
|
219
|
+
operation: 'GET',
|
220
|
+
subpath: "files/#{node_file[:file_id]}/content",
|
221
|
+
save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTION_RECEIVE),file_name))
|
214
222
|
return Main.result_status("downloaded: #{file_name}")
|
215
223
|
when :v3
|
216
224
|
# Note: other common actions are unauthorized with user scope
|
217
|
-
command_legacy=options.get_next_command(Node::SIMPLE_ACTIONS)
|
225
|
+
command_legacy = options.get_next_command(Node::SIMPLE_ACTIONS)
|
218
226
|
# TODO: shall we support all methods here ? what if there is a link ?
|
219
|
-
node_api=aoc_api.get_node_api(top_node_file[:node_info])
|
227
|
+
node_api = aoc_api.get_node_api(top_node_file[:node_info])
|
220
228
|
return Node.new(@agents.merge(skip_basic_auth_options: true, node_api: node_api)).execute_action(command_legacy)
|
221
229
|
when :file
|
222
|
-
command_node_file=options.get_next_command([
|
223
|
-
file_path=options.get_option(:path
|
230
|
+
command_node_file = options.get_next_command(%i[show permission modify])
|
231
|
+
file_path = options.get_option(:path)
|
224
232
|
node_file =
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
233
|
+
if !file_path.nil?
|
234
|
+
# directly returns node_file info
|
235
|
+
aoc_api.resolve_node_file(top_node_file,file_path) # TODO: allow follow link ?
|
236
|
+
else
|
237
|
+
# build node_file info from next argument on command line
|
238
|
+
{node_info: top_node_file[:node_info],file_id: instance_identifier}
|
239
|
+
end
|
240
|
+
node_api = aoc_api.get_node_api(node_file[:node_info])
|
231
241
|
case command_node_file
|
232
242
|
when :show
|
233
|
-
items=node_api.read("files/#{node_file[:file_id]}")[:data]
|
243
|
+
items = node_api.read("files/#{node_file[:file_id]}")[:data]
|
234
244
|
return {type: :single_object,data: items}
|
235
245
|
when :modify
|
236
|
-
update_param=options.get_next_argument('update data (Hash)')
|
237
|
-
res=node_api.update("files/#{node_file[:file_id]}",update_param)[:data]
|
246
|
+
update_param = options.get_next_argument('update data (Hash)')
|
247
|
+
res = node_api.update("files/#{node_file[:file_id]}",update_param)[:data]
|
238
248
|
return {type: :single_object,data: res}
|
239
249
|
when :permission
|
240
|
-
command_perm=options.get_next_command([
|
250
|
+
command_perm = options.get_next_command(%i[list create delete])
|
241
251
|
case command_perm
|
242
252
|
when :list
|
243
253
|
# generic options : TODO: as arg ? option_url_query
|
244
|
-
list_options||={'include'=>['[]','access_level','permission_count']}
|
254
|
+
list_options ||= {'include' => ['[]','access_level','permission_count']}
|
245
255
|
# special value: ALL will show all permissions
|
246
256
|
if !VAL_ALL.eql?(node_file[:file_id])
|
247
257
|
# add which one to get
|
248
|
-
list_options['file_id']=node_file[:file_id]
|
249
|
-
list_options['inherited']||=false
|
258
|
+
list_options['file_id'] = node_file[:file_id]
|
259
|
+
list_options['inherited'] ||= false
|
250
260
|
end
|
251
|
-
items=node_api.read('permissions',list_options)[:data]
|
261
|
+
items = node_api.read('permissions',list_options)[:data]
|
252
262
|
return {type: :object_list,data: items}
|
263
|
+
when :delete
|
264
|
+
perm_id=instance_identifier
|
265
|
+
return do_bulk_operation(perm_id,'deleted') do |one_id|
|
266
|
+
node_api.delete("permissions/#{perm_id}")
|
267
|
+
{'id' => one_id}
|
268
|
+
end
|
269
|
+
#node_api.delete("permissions/#{perm_id}")
|
253
270
|
when :create
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
'
|
260
|
-
'
|
261
|
-
'
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
271
|
+
create_param=options.get_next_argument('creation data (Hash)')
|
272
|
+
#access_id = "#{ID_AK_ADMIN}_WS_#{@workspace_info['id']}"
|
273
|
+
default_params = {
|
274
|
+
'file_id' => node_file[:file_id], # mandatory
|
275
|
+
#'access_type' => 'user', # mandatory: user or group
|
276
|
+
#'access_id' => access_id, # id of user or group
|
277
|
+
'access_levels' => Aspera::Node::ACCESS_LEVELS,
|
278
|
+
'tags' => {'aspera' => {'files' => {'workspace' => {
|
279
|
+
'id' => @workspace_info['id'],
|
280
|
+
'workspace_name' => @workspace_info['name'],
|
281
|
+
'user_name' => aoc_api.user_info['name'],
|
282
|
+
'shared_by_user_id' => aoc_api.user_info['id'],
|
283
|
+
'shared_by_name' => aoc_api.user_info['name'],
|
284
|
+
'shared_by_email' => aoc_api.user_info['email'],
|
285
|
+
#'shared_with_name' => access_id,
|
286
|
+
'access_key' => node_file[:node_info]['access_key'],
|
287
|
+
'node' => node_file[:node_info]['name']}}}}}
|
288
|
+
create_param = default_params.deep_merge(create_param)
|
289
|
+
if create_param.has_key?('with')
|
290
|
+
contact_info = aoc_api.lookup_entity_by_name(
|
291
|
+
'contacts',
|
292
|
+
create_param['with'],
|
293
|
+
{'current_workspace_id' => @workspace_info['id'],'context'=> 'share_folder'})
|
294
|
+
create_param.delete('with')
|
295
|
+
create_param['access_type']=contact_info['source_type']
|
296
|
+
create_param['access_id']=contact_info['source_id']
|
297
|
+
create_param['tags']['aspera']['files']['workspace']['shared_with_name']=contact_info['email']
|
298
|
+
end
|
299
|
+
opt_link_name=nil
|
300
|
+
if create_param.has_key?('link_name')
|
301
|
+
opt_link_name=create_param['link_name']
|
302
|
+
create_param.delete('link_name')
|
303
|
+
end
|
304
|
+
# for admin type:
|
305
|
+
#node_file[:node_info]
|
306
|
+
#node_api = aoc_api.get_node_api(node_file[:node_info])
|
307
|
+
created_data = node_api.create('permissions',create_param)[:data]
|
308
|
+
event_creation={
|
309
|
+
'types' => ['permission.created'],
|
310
|
+
'node_id' => node_file[:node_info]['id'],
|
311
|
+
'workspace_id' => @workspace_info['id'],
|
312
|
+
'data' => created_data # Response from previous step
|
313
|
+
}
|
314
|
+
#(optional). The name of the folder to be displayed to the destination user. Use it if its value is different from the "share_as" field.
|
315
|
+
event_creation['link_name']=opt_link_name unless opt_link_name.nil?
|
316
|
+
aoc_api.create('events',event_creation)
|
317
|
+
return { type: :single_object, data: created_data}
|
275
318
|
else raise "internal error:shall not reach here (#{command_perm})"
|
276
319
|
end
|
277
320
|
else raise "internal error:shall not reach here (#{command_node_file})"
|
278
321
|
end
|
279
322
|
end # command_repo
|
280
|
-
raise '
|
323
|
+
raise 'internal error:shall not reach here'
|
281
324
|
end # execute_node_gen4_command
|
282
|
-
|
325
|
+
AOC_PARAMS_COPY=%i[link url auth client_id client_secret scope redirect_uri private_key passphrase username password].freeze
|
283
326
|
# build constructor option list for AoC based on options of CLI
|
284
327
|
def aoc_params(subpath)
|
285
328
|
# copy command line options to args
|
286
|
-
opt=
|
287
|
-
opt[:subpath]=subpath
|
329
|
+
opt = AOC_PARAMS_COPY.each_with_object({}){|i,m|m[i] = options.get_option(i)}
|
330
|
+
opt[:subpath] = subpath
|
288
331
|
return opt
|
289
332
|
end
|
290
333
|
|
291
334
|
# initialize apis and authentication
|
292
335
|
# set:
|
293
|
-
# @
|
294
|
-
# @workspace_name
|
295
|
-
# @workspace_id
|
336
|
+
# @workspace_info
|
296
337
|
# @persist_ids
|
297
338
|
# returns nil
|
298
339
|
def set_workspace_info
|
299
|
-
@url_token_data=aoc_api.url_token_data
|
340
|
+
@url_token_data = aoc_api.url_token_data
|
300
341
|
if @url_token_data.nil?
|
301
|
-
|
302
|
-
@persist_ids=[aoc_api.user_info['id']]
|
342
|
+
default_workspace_id = aoc_api.user_info['default_workspace_id']
|
343
|
+
@persist_ids = [aoc_api.user_info['id']]
|
303
344
|
else
|
304
|
-
@
|
305
|
-
@persist_ids=[] # TODO : @url_token_data['id'] ?
|
345
|
+
default_workspace_id = @url_token_data['data']['workspace_id']
|
346
|
+
@persist_ids = [] # TODO : @url_token_data['id'] ?
|
306
347
|
end
|
307
348
|
|
308
|
-
ws_name=options.get_option(:workspace
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
349
|
+
ws_name = options.get_option(:workspace)
|
350
|
+
ws_id =
|
351
|
+
case ws_name
|
352
|
+
when :default
|
353
|
+
Log.log.debug('Using default workspace'.green)
|
354
|
+
raise CliError,'No default workspace defined for user, please specify workspace' if default_workspace_id.nil?
|
355
|
+
default_workspace_id
|
356
|
+
when String then aoc_api.lookup_entity_by_name('workspaces',ws_name)['id']
|
357
|
+
when NilClass then nil
|
358
|
+
else raise CliError,'unexpected value type for workspace'
|
313
359
|
end
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
case wss.length
|
321
|
-
when 0
|
322
|
-
raise CliBadArgument,"no such workspace: #{ws_name}"
|
323
|
-
when 1
|
324
|
-
@workspace_id=wss.first['id']
|
325
|
-
else
|
326
|
-
raise 'unexpected case'
|
360
|
+
@workspace_info =
|
361
|
+
begin
|
362
|
+
aoc_api.read("workspaces/#{ws_id}")[:data]
|
363
|
+
rescue Aspera::RestCallError => e
|
364
|
+
Log.log.debug(e.message)
|
365
|
+
{ 'id' => :undefined, 'name' => :undefined }
|
327
366
|
end
|
328
|
-
|
329
|
-
@workspace_data=aoc_api.read("workspaces/#{@workspace_id}")[:data]
|
330
|
-
Log.log.debug("workspace_id=#{@workspace_id},@workspace_data=#{@workspace_data}".red)
|
331
|
-
|
332
|
-
@workspace_name||=@workspace_data['name']
|
333
|
-
Log.log.info('current workspace is '+@workspace_name.red)
|
334
|
-
|
367
|
+
Log.dump(:workspace_data,@workspace_info)
|
335
368
|
# display workspace
|
336
|
-
self.format.display_status("Current Workspace: #{@
|
369
|
+
self.format.display_status("Current Workspace: #{@workspace_info['name'].to_s.red}#{@workspace_info['id'] == default_workspace_id ? ' (default)' : ''}")
|
337
370
|
return nil
|
338
371
|
end
|
339
372
|
|
@@ -341,51 +374,33 @@ save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTIO
|
|
341
374
|
def set_home_node_file
|
342
375
|
if !@url_token_data.nil?
|
343
376
|
assert_public_link_types(['view_shared_file'])
|
344
|
-
home_node_id
|
345
|
-
home_file_id
|
377
|
+
home_node_id = @url_token_data['data']['node_id']
|
378
|
+
home_file_id = @url_token_data['data']['file_id']
|
346
379
|
end
|
347
|
-
home_node_id
|
348
|
-
home_file_id
|
349
|
-
raise '
|
350
|
-
@home_node_file={
|
380
|
+
home_node_id ||= @workspace_info['home_node_id'] || @workspace_info['node_id']
|
381
|
+
home_file_id ||= @workspace_info['home_file_id']
|
382
|
+
raise "Cannot get user's home node id, check your default workspace or specify one" if home_node_id.to_s.empty?
|
383
|
+
@home_node_file = {
|
351
384
|
node_info: aoc_api.read("nodes/#{home_node_id}")[:data],
|
352
|
-
file_id:
|
385
|
+
file_id: home_file_id
|
353
386
|
}
|
354
387
|
aoc_api.check_get_node_file(@home_node_file)
|
355
388
|
|
356
389
|
return nil
|
357
390
|
end
|
358
391
|
|
359
|
-
def do_bulk_operation(ids_or_one,success_msg,id_result='id',&do_action)
|
360
|
-
ids_or_one=[ids_or_one] unless options.get_option(:bulk)
|
361
|
-
raise 'expecting Array' unless ids_or_one.is_a?(Array)
|
362
|
-
result_list=[]
|
363
|
-
ids_or_one.each do |id|
|
364
|
-
one={id_result=>id}
|
365
|
-
begin
|
366
|
-
res=do_action.call(id)
|
367
|
-
one=res if id.is_a?(Hash) # if block returns a has, let's use this
|
368
|
-
one['status']=success_msg
|
369
|
-
rescue StandardError => e
|
370
|
-
one['status']=e.to_s
|
371
|
-
end
|
372
|
-
result_list.push(one)
|
373
|
-
end
|
374
|
-
return {type: :object_list,data: result_list,fields: [id_result,'status']}
|
375
|
-
end
|
376
|
-
|
377
392
|
# get identifier or name from command line
|
378
393
|
# @return identifier
|
379
394
|
def get_resource_id_from_args(resource_class_path)
|
380
|
-
l_res_id=options.get_option(:id)
|
381
|
-
l_res_name=options.get_option(:name)
|
395
|
+
l_res_id = options.get_option(:id)
|
396
|
+
l_res_name = options.get_option(:name)
|
382
397
|
raise 'Provide either option id or name, not both' unless l_res_id.nil? || l_res_name.nil?
|
383
398
|
# try to find item by name (single partial match or exact match)
|
384
|
-
l_res_id=aoc_api.lookup_entity_by_name(resource_class_path,l_res_name)['id'] unless l_res_name.nil?
|
399
|
+
l_res_id = aoc_api.lookup_entity_by_name(resource_class_path,l_res_name)['id'] unless l_res_name.nil?
|
385
400
|
# if no name or id option, taken on command line (after command)
|
386
401
|
if l_res_id.nil?
|
387
|
-
l_res_id=options.get_next_argument('identifier')
|
388
|
-
l_res_id=aoc_api.lookup_entity_by_name(resource_class_path,options.get_next_argument('identifier'))['id'] if l_res_id.eql?('name')
|
402
|
+
l_res_id = options.get_next_argument('identifier')
|
403
|
+
l_res_id = aoc_api.lookup_entity_by_name(resource_class_path,options.get_next_argument('identifier'))['id'] if l_res_id.eql?('name')
|
389
404
|
end
|
390
405
|
return l_res_id
|
391
406
|
end
|
@@ -394,28 +409,41 @@ save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTIO
|
|
394
409
|
return "#{resource_class_path}/#{get_resource_id_from_args(resource_class_path)}"
|
395
410
|
end
|
396
411
|
|
397
|
-
# package creation
|
412
|
+
# Normalize package creation recipient lists as expected by AoC API
|
413
|
+
# AoC expects {type: , id: }, but ascli allows providing either the native values or just a name
|
414
|
+
# in that case, the name is resolved and replaced with {type: , id: }
|
415
|
+
# @param package_data The whole package creation payload
|
416
|
+
# @param recipient_list_field The field in structure, i.e. recipients or bcc_recipients
|
417
|
+
# @return nil package_data is modified
|
398
418
|
def resolve_package_recipients(package_data,recipient_list_field)
|
399
419
|
return unless package_data.has_key?(recipient_list_field)
|
400
420
|
raise CliBadArgument,"#{recipient_list_field} must be an Array" unless package_data[recipient_list_field].is_a?(Array)
|
401
|
-
new_user_option=options.get_option(:new_user_option
|
421
|
+
new_user_option = options.get_option(:new_user_option,is_type: :mandatory)
|
402
422
|
# list with resolved elements
|
403
|
-
resolved_list=[]
|
423
|
+
resolved_list = []
|
404
424
|
package_data[recipient_list_field].each do |short_recipient_info|
|
405
425
|
case short_recipient_info
|
406
|
-
when Hash # native
|
407
|
-
raise "#{recipient_list_field} element shall have fields: id and type" unless short_recipient_info.keys.sort.eql?([
|
408
|
-
when String # need to resolve name to type/id
|
426
|
+
when Hash # native API information, check keys
|
427
|
+
raise "#{recipient_list_field} element shall have fields: id and type" unless short_recipient_info.keys.sort.eql?(%w[id type])
|
428
|
+
when String # CLI helper: need to resolve provided name to type/id
|
409
429
|
# email: user, else dropbox
|
410
|
-
entity_type=short_recipient_info.include?('@') ? 'contacts' : 'dropboxes'
|
430
|
+
entity_type = short_recipient_info.include?('@') ? 'contacts' : 'dropboxes'
|
411
431
|
begin
|
412
|
-
full_recipient_info=aoc_api.lookup_entity_by_name(entity_type,short_recipient_info,{'current_workspace_id'
|
432
|
+
full_recipient_info = aoc_api.lookup_entity_by_name(entity_type,short_recipient_info,{'current_workspace_id' => @workspace_info['id']})
|
413
433
|
rescue RuntimeError => e
|
414
|
-
raise e unless e.message.
|
415
|
-
|
416
|
-
|
434
|
+
raise e unless e.message.start_with?(Aspera::AoC::ENTITY_NOT_FOUND)
|
435
|
+
# dropboxes cannot be created on the fly
|
436
|
+
raise "no such shared inbox in workspace #{@workspace_info['name']}" if entity_type.eql?('dropboxes')
|
437
|
+
# unknown user: create it as external user
|
438
|
+
full_recipient_info = aoc_api.create('contacts',{
|
439
|
+
'current_workspace_id' => @workspace_info['id'],
|
440
|
+
'email' => short_recipient_info}.merge(new_user_option))[:data]
|
441
|
+
end
|
442
|
+
short_recipient_info = if entity_type.eql?('dropboxes')
|
443
|
+
{'id' => full_recipient_info['id'],'type' => 'dropbox'}
|
444
|
+
else
|
445
|
+
{'id' => full_recipient_info['source_id'],'type' => full_recipient_info['source_type']}
|
417
446
|
end
|
418
|
-
short_recipient_info=entity_type.eql?('dropboxes') ? {'id'=>full_recipient_info['id'],'type'=>'dropbox'} : {'id'=>full_recipient_info['source_id'],'type'=>full_recipient_info['source_type']}
|
419
447
|
else # unexpected extended value, must be String or Hash
|
420
448
|
raise "#{recipient_list_field} item must be a String (email, shared inbox) or Hash (id,type)"
|
421
449
|
end # type of recipient info
|
@@ -423,32 +451,65 @@ save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTIO
|
|
423
451
|
resolved_list.push(short_recipient_info)
|
424
452
|
end
|
425
453
|
# replace with resolved elements
|
426
|
-
package_data[recipient_list_field]=resolved_list
|
454
|
+
package_data[recipient_list_field] = resolved_list
|
455
|
+
return nil
|
427
456
|
end
|
428
457
|
|
429
458
|
def normalize_metadata(pkg_data)
|
430
459
|
case pkg_data['metadata']
|
431
|
-
when NilClass
|
432
|
-
when Array then return
|
460
|
+
when Array,NilClass # no action
|
433
461
|
when Hash
|
434
|
-
api_meta=[]
|
462
|
+
api_meta = []
|
435
463
|
pkg_data['metadata'].each do |k,v|
|
436
464
|
api_meta.push({
|
437
465
|
#'input_type' => 'single-dropdown',
|
438
|
-
'name'
|
439
|
-
'values'
|
466
|
+
'name' => k,
|
467
|
+
'values' => v.is_a?(Array) ? v : [v]
|
440
468
|
})
|
441
469
|
end
|
442
|
-
pkg_data['metadata']=api_meta
|
470
|
+
pkg_data['metadata'] = api_meta
|
443
471
|
else raise "metadata field if not of expected type: #{pkg_meta.class}"
|
444
472
|
end
|
445
|
-
nil
|
473
|
+
return nil
|
474
|
+
end
|
475
|
+
|
476
|
+
# Check metadata: remove when validation is done server side
|
477
|
+
def validate_metadata(pkg_data)
|
478
|
+
# validate only for shared inboxes
|
479
|
+
return unless
|
480
|
+
pkg_data['recipients'].is_a?(Array) &&
|
481
|
+
pkg_data['recipients'].first.is_a?(Hash) &&
|
482
|
+
pkg_data['recipients'].first.has_key?('type') &&
|
483
|
+
pkg_data['recipients'].first['type'].eql?('dropbox')
|
484
|
+
|
485
|
+
shbx_kid = pkg_data['recipients'].first['id']
|
486
|
+
meta_schema = aoc_api.read("dropboxes/#{shbx_kid}")[:data]['metadata_schema']
|
487
|
+
if meta_schema.nil? || meta_schema.empty?
|
488
|
+
Log.log.debug('no metadata in shared inbox')
|
489
|
+
return
|
490
|
+
end
|
491
|
+
pkg_meta = pkg_data['metadata']
|
492
|
+
raise "package requires metadata: #{meta_schema}" unless pkg_data.has_key?('metadata')
|
493
|
+
raise 'metadata must be an Array' unless pkg_meta.is_a?(Array)
|
494
|
+
Log.dump(:metadata,pkg_meta)
|
495
|
+
pkg_meta.each do |field|
|
496
|
+
raise 'metadata field must be Hash' unless field.is_a?(Hash)
|
497
|
+
raise 'metadata field must have name' unless field.has_key?('name')
|
498
|
+
raise 'metadata field must have values' unless field.has_key?('values')
|
499
|
+
raise 'metadata values must be an Array' unless field['values'].is_a?(Array)
|
500
|
+
raise "unknown metadata field: #{field['name']}" if meta_schema.select{|i|i['name'].eql?(field['name'])}.empty?
|
501
|
+
end
|
502
|
+
meta_schema.each do |field|
|
503
|
+
provided=pkg_meta.select{|i|i['name'].eql?(field['name'])}
|
504
|
+
raise "only one field with name #{field['name']} allowed" if provided.count > 1
|
505
|
+
raise "missing mandatory field: #{field['name']}" if field['required'] && provided.empty?
|
506
|
+
end
|
446
507
|
end
|
447
508
|
|
448
509
|
# private
|
449
510
|
def option_url_query(default)
|
450
|
-
query=options.get_option(:query
|
451
|
-
query=default if query.nil?
|
511
|
+
query = options.get_option(:query)
|
512
|
+
query = default if query.nil?
|
452
513
|
Log.log.debug("Query=#{query}".bg_red)
|
453
514
|
begin
|
454
515
|
# check it is suitable
|
@@ -460,59 +521,61 @@ save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTIO
|
|
460
521
|
end
|
461
522
|
|
462
523
|
def assert_public_link_types(expected)
|
463
|
-
raise CliBadArgument,"public link type is #{@url_token_data['purpose']} but action requires one of #{expected.join(',')}"
|
524
|
+
raise CliBadArgument,"public link type is #{@url_token_data['purpose']} but action requires one of #{expected.join(',')}" \
|
525
|
+
unless expected.include?(@url_token_data['purpose'])
|
464
526
|
end
|
465
527
|
|
466
528
|
# Call aoc_api.read with same parameters.
|
467
529
|
# Use paging if necessary to get all results
|
530
|
+
# @return {list: , total: }
|
468
531
|
def read_with_paging(resource_class_path,base_query)
|
469
532
|
raise 'Query must be Hash' unless base_query.is_a?(Hash)
|
470
533
|
# set default large page if user does not specify own parameters. AoC Caps to 1000 anyway
|
471
|
-
base_query['per_page']=1000 unless base_query.has_key?('per_page')
|
472
|
-
max_items=base_query[MAX_ITEMS]
|
534
|
+
base_query['per_page'] = 1000 unless base_query.has_key?('per_page')
|
535
|
+
max_items = base_query[MAX_ITEMS]
|
473
536
|
base_query.delete(MAX_ITEMS)
|
474
|
-
max_pages=base_query[MAX_PAGES]
|
537
|
+
max_pages = base_query[MAX_PAGES]
|
475
538
|
base_query.delete(MAX_PAGES)
|
476
|
-
item_list=[]
|
477
|
-
total_count=nil
|
478
|
-
current_page=base_query['page']
|
479
|
-
current_page=1 if current_page.nil?
|
480
|
-
page_count=0
|
539
|
+
item_list = []
|
540
|
+
total_count = nil
|
541
|
+
current_page = base_query['page']
|
542
|
+
current_page = 1 if current_page.nil?
|
543
|
+
page_count = 0
|
481
544
|
loop do
|
482
|
-
query=base_query.clone
|
483
|
-
query['page']=current_page
|
484
|
-
result=aoc_api.read(resource_class_path,query)
|
485
|
-
total_count=result[:http]['X-Total-Count']
|
486
|
-
page_count+=1
|
487
|
-
current_page+=1
|
488
|
-
add_items=result[:data]
|
545
|
+
query = base_query.clone
|
546
|
+
query['page'] = current_page
|
547
|
+
result = aoc_api.read(resource_class_path,query)
|
548
|
+
total_count = result[:http]['X-Total-Count']
|
549
|
+
page_count += 1
|
550
|
+
current_page += 1
|
551
|
+
add_items = result[:data]
|
489
552
|
break if add_items.empty?
|
490
553
|
# append new items to full list
|
491
554
|
item_list += add_items
|
492
555
|
break if !max_pages.nil? && page_count > max_pages
|
493
556
|
break if !max_items.nil? && item_list.count > max_items
|
494
557
|
end
|
495
|
-
return item_list,total_count
|
558
|
+
return {list: item_list,total: total_count}
|
496
559
|
end
|
497
560
|
|
498
561
|
def execute_admin_action
|
499
562
|
# upgrade scope to admin
|
500
|
-
aoc_api.oauth.
|
501
|
-
command_admin=options.get_next_command([
|
563
|
+
aoc_api.oauth.gparams[:scope] = AoC::SCOPE_FILES_ADMIN
|
564
|
+
command_admin = options.get_next_command(%i[ats resource usage_reports analytics subscription auth_providers])
|
502
565
|
case command_admin
|
503
566
|
when :auth_providers
|
504
|
-
command_auth_prov=options.get_next_command([
|
567
|
+
command_auth_prov = options.get_next_command(%i[list update])
|
505
568
|
case command_auth_prov
|
506
569
|
when :list
|
507
|
-
providers=aoc_api.read('admin/auth_providers')[:data]
|
570
|
+
providers = aoc_api.read('admin/auth_providers')[:data]
|
508
571
|
return {type: :object_list,data: providers}
|
509
572
|
when :update
|
510
573
|
raise 'not implemented'
|
511
574
|
end
|
512
575
|
when :subscription
|
513
|
-
org=aoc_api.read('organization')[:data]
|
514
|
-
bss_api=AoC.new(aoc_params('bss/platform'))
|
515
|
-
graphql_query="
|
576
|
+
org = aoc_api.read('organization')[:data]
|
577
|
+
bss_api = AoC.new(aoc_params('bss/platform'))
|
578
|
+
graphql_query = "
|
516
579
|
query ($organization_id: ID!) {
|
517
580
|
aoc (organization_id: $organization_id) {
|
518
581
|
bssSubscription {
|
@@ -560,55 +623,55 @@ save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTIO
|
|
560
623
|
}
|
561
624
|
}
|
562
625
|
"
|
563
|
-
result=bss_api.create('graphql',{'variables'=>{'organization_id'=>org['id']},'query'=>graphql_query})[:data]['data']
|
626
|
+
result = bss_api.create('graphql',{'variables' => {'organization_id' => org['id']},'query' => graphql_query})[:data]['data']
|
564
627
|
return {type: :single_object,data: result['aoc']['bssSubscription']}
|
565
628
|
when :ats
|
566
629
|
ats_api = Rest.new(aoc_api.params.deep_merge({
|
567
|
-
base_url: aoc_api.params[:base_url]+'/admin/ats/pub/v1',
|
568
|
-
auth:
|
630
|
+
base_url: aoc_api.params[:base_url] + '/admin/ats/pub/v1',
|
631
|
+
auth: {scope: AoC::SCOPE_FILES_ADMIN_USER}
|
569
632
|
}))
|
570
633
|
return Ats.new(@agents).execute_action_gen(ats_api)
|
571
634
|
when :analytics
|
572
635
|
analytics_api = Rest.new(aoc_api.params.deep_merge({
|
573
|
-
base_url: aoc_api.params[:base_url].gsub('/api/v1','')+'/analytics/v2',
|
574
|
-
auth:
|
636
|
+
base_url: aoc_api.params[:base_url].gsub('/api/v1','') + '/analytics/v2',
|
637
|
+
auth: {scope: AoC::SCOPE_FILES_ADMIN_USER}
|
575
638
|
}))
|
576
|
-
command_analytics=options.get_next_command([
|
639
|
+
command_analytics = options.get_next_command(%i[application_events transfers])
|
577
640
|
case command_analytics
|
578
641
|
when :application_events
|
579
|
-
event_type=command_analytics.to_s
|
580
|
-
events=analytics_api.read("organizations/#{aoc_api.user_info['organization_id']}/#{event_type}")[:data][event_type]
|
642
|
+
event_type = command_analytics.to_s
|
643
|
+
events = analytics_api.read("organizations/#{aoc_api.user_info['organization_id']}/#{event_type}")[:data][event_type]
|
581
644
|
return {type: :object_list,data: events}
|
582
645
|
when :transfers
|
583
|
-
event_type=command_analytics.to_s
|
584
|
-
filter_resource=options.get_option(:name
|
585
|
-
filter_id=options.get_option(:id
|
646
|
+
event_type = command_analytics.to_s
|
647
|
+
filter_resource = options.get_option(:name) || 'organizations'
|
648
|
+
filter_id = options.get_option(:id) ||
|
586
649
|
case filter_resource
|
587
650
|
when 'organizations' then aoc_api.user_info['organization_id']
|
588
651
|
when 'users' then aoc_api.user_info['id']
|
589
|
-
when 'nodes' then aoc_api.user_info['id']
|
652
|
+
when 'nodes' then aoc_api.user_info['id'] # TODO: consistent ? # rubocop:disable Lint/DuplicateBranch
|
590
653
|
else raise 'organizations or users for option --name'
|
591
654
|
end
|
592
|
-
filter=options.get_option(:query
|
655
|
+
filter = options.get_option(:query) || {}
|
593
656
|
raise 'query must be Hash' unless filter.is_a?(Hash)
|
594
|
-
filter['limit']||=100
|
595
|
-
if options.get_option(:once_only
|
596
|
-
saved_date=[]
|
597
|
-
startdate_persistency=PersistencyActionOnce.new(
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
start_datetime=saved_date.first
|
602
|
-
stop_datetime=Time.now.utc.strftime('%FT%T.%LZ')
|
657
|
+
filter['limit'] ||= 100
|
658
|
+
if options.get_option(:once_only,is_type: :mandatory)
|
659
|
+
saved_date = []
|
660
|
+
startdate_persistency = PersistencyActionOnce.new(
|
661
|
+
manager: @agents[:persistency],
|
662
|
+
data: saved_date,
|
663
|
+
ids: IdGenerator.from_list(['aoc_ana_date',options.get_option(:url,is_type: :mandatory),@workspace_info['name']].push(filter_resource,filter_id)))
|
664
|
+
start_datetime = saved_date.first
|
665
|
+
stop_datetime = Time.now.utc.strftime('%FT%T.%LZ')
|
603
666
|
#Log.log().error("start: #{start_datetime}")
|
604
667
|
#Log.log().error("end: #{stop_datetime}")
|
605
|
-
saved_date[0]=stop_datetime
|
668
|
+
saved_date[0] = stop_datetime
|
606
669
|
filter['start_time'] = start_datetime unless start_datetime.nil?
|
607
670
|
filter['stop_time'] = stop_datetime
|
608
671
|
end
|
609
|
-
events=analytics_api.read("#{filter_resource}/#{filter_id}/#{event_type}",option_url_query(filter))[:data][event_type]
|
610
|
-
startdate_persistency
|
611
|
-
if !options.get_option(:notif_to
|
672
|
+
events = analytics_api.read("#{filter_resource}/#{filter_id}/#{event_type}",option_url_query(filter))[:data][event_type]
|
673
|
+
startdate_persistency&.save
|
674
|
+
if !options.get_option(:notif_to).nil?
|
612
675
|
events.each do |tr_event|
|
613
676
|
config.send_email_template({ev: tr_event})
|
614
677
|
end
|
@@ -616,153 +679,197 @@ save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTIO
|
|
616
679
|
return {type: :object_list,data: events}
|
617
680
|
end
|
618
681
|
when :resource
|
619
|
-
resource_type=options.get_next_argument('resource',
|
620
|
-
[:self,:organization,:user,:group,:client,:contact,:dropbox,:node,:operation,:package,:saml_configuration, :workspace, :dropbox_membership,:short_link,:workspace_membership,
|
621
|
-
:application,:client_registration_token,:client_access_key,:kms_profile])
|
682
|
+
resource_type = options.get_next_argument('resource',expected: KNOWN_AOC_RES)
|
622
683
|
# get path on API, resource type is singular, but api is plural
|
623
|
-
resource_class_path=
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
684
|
+
resource_class_path =
|
685
|
+
case resource_type
|
686
|
+
# special cases: singleton, in admin, with x
|
687
|
+
when :self,:organization then resource_type
|
688
|
+
when :client_registration_token,:client_access_key then "admin/#{resource_type}s"
|
689
|
+
when :application then 'admin/apps_new'
|
690
|
+
when :dropbox then resource_type.to_s + 'es'
|
691
|
+
when :kms_profile then "integrations/#{resource_type}s"
|
692
|
+
else "#{resource_type}s"
|
693
|
+
end
|
633
694
|
# build list of supported operations
|
634
|
-
singleton_object=[
|
635
|
-
global_operations=[
|
636
|
-
supported_operations=[
|
695
|
+
singleton_object = %i[self organization].include?(resource_type)
|
696
|
+
global_operations = %i[create list]
|
697
|
+
supported_operations = %i[show modify]
|
637
698
|
supported_operations.push(:delete,*global_operations) unless singleton_object
|
638
699
|
supported_operations.push(:v4,:v3) if resource_type.eql?(:node)
|
639
700
|
supported_operations.push(:set_pub_key) if resource_type.eql?(:client)
|
640
|
-
supported_operations.push(:
|
641
|
-
command=options.get_next_command(supported_operations)
|
701
|
+
supported_operations.push(:shared_folder) if resource_type.eql?(:workspace)
|
702
|
+
command = options.get_next_command(supported_operations)
|
642
703
|
# require identifier for non global commands
|
643
704
|
if !singleton_object && !global_operations.include?(command)
|
644
|
-
res_id=get_resource_id_from_args(resource_class_path)
|
645
|
-
resource_instance_path="#{resource_class_path}/#{res_id}"
|
705
|
+
res_id = get_resource_id_from_args(resource_class_path)
|
706
|
+
resource_instance_path = "#{resource_class_path}/#{res_id}"
|
646
707
|
end
|
647
|
-
resource_instance_path=resource_class_path if singleton_object
|
708
|
+
resource_instance_path = resource_class_path if singleton_object
|
648
709
|
case command
|
649
710
|
when :create
|
650
|
-
id_result='id'
|
651
|
-
id_result='token' if resource_class_path.eql?('admin/client_registration_tokens')
|
711
|
+
id_result = 'id'
|
712
|
+
id_result = 'token' if resource_class_path.eql?('admin/client_registration_tokens')
|
652
713
|
# TODO: report inconsistency: creation url is !=, and does not return id.
|
653
|
-
resource_class_path='admin/client_registration/token' if resource_class_path.eql?('admin/client_registration_tokens')
|
654
|
-
list_or_one=options.get_next_argument('creation data (Hash)')
|
655
|
-
return do_bulk_operation(list_or_one,'created',id_result) do |params|
|
714
|
+
resource_class_path = 'admin/client_registration/token' if resource_class_path.eql?('admin/client_registration_tokens')
|
715
|
+
list_or_one = options.get_next_argument('creation data (Hash)')
|
716
|
+
return do_bulk_operation(list_or_one,'created',id_result: id_result) do |params|
|
656
717
|
raise 'expecting Hash' unless params.is_a?(Hash)
|
657
718
|
aoc_api.create(resource_class_path,params)[:data]
|
658
719
|
end
|
659
720
|
when :list
|
660
|
-
default_fields=['id']
|
661
|
-
default_query={}
|
721
|
+
default_fields = ['id']
|
722
|
+
default_query = {}
|
662
723
|
case resource_type
|
663
|
-
when :application then default_query={organization_apps: true};
|
724
|
+
when :application then default_query = {organization_apps: true};
|
664
725
|
default_fields.push('app_type','app_name','available','direct_authorizations_allowed','workspace_authorizations_allowed')
|
665
726
|
when :client,:client_access_key,:dropbox,:group,:package,:saml_configuration,:workspace then default_fields.push('name')
|
666
727
|
when :client_registration_token then default_fields.push('value','data.client_subject_scopes','created_at')
|
667
|
-
when :contact then default_fields=[
|
728
|
+
when :contact then default_fields = %w[email name source_id source_type]
|
668
729
|
when :node then default_fields.push('name','host','access_key')
|
669
|
-
when :operation then default_fields=nil
|
730
|
+
when :operation then default_fields = nil
|
670
731
|
when :short_link then default_fields.push('short_url','data.url_token_data.purpose')
|
671
732
|
when :user then default_fields.push('name','email')
|
733
|
+
when :group_membership then default_fields.push(*%w[group_id member_type member_id])
|
734
|
+
when :workspace_membership then default_fields.push(*%w[workspace_id member_type member_id])
|
672
735
|
end
|
673
|
-
|
674
|
-
count_msg="Items: #{
|
675
|
-
count_msg=count_msg.bg_red unless
|
736
|
+
items = read_with_paging(resource_class_path,option_url_query(default_query))
|
737
|
+
count_msg = "Items: #{items[:list].length}/#{items[:total]}"
|
738
|
+
count_msg = count_msg.bg_red unless items[:list].length.eql?(items[:total].to_i)
|
676
739
|
self.format.display_status(count_msg)
|
677
|
-
return {type: :object_list,data:
|
740
|
+
return {type: :object_list,data: items[:list],fields: default_fields}
|
678
741
|
when :show
|
679
|
-
object=aoc_api.read(resource_instance_path)[:data]
|
680
|
-
fields=object.keys.reject{|k|k.eql?('certificate')}
|
742
|
+
object = aoc_api.read(resource_instance_path)[:data]
|
743
|
+
fields = object.keys.reject{|k|k.eql?('certificate')}
|
681
744
|
return { type: :single_object, data: object, fields: fields }
|
682
745
|
when :modify
|
683
|
-
changes=options.get_next_argument('modified parameters (hash)')
|
746
|
+
changes = options.get_next_argument('modified parameters (hash)')
|
684
747
|
aoc_api.update(resource_instance_path,changes)
|
685
748
|
return Main.result_status('modified')
|
686
749
|
when :delete
|
687
750
|
return do_bulk_operation(res_id,'deleted') do |one_id|
|
688
751
|
aoc_api.delete("#{resource_class_path}/#{one_id}")
|
689
|
-
{'id'=>one_id}
|
752
|
+
{'id' => one_id}
|
690
753
|
end
|
691
754
|
when :set_pub_key
|
692
755
|
# special : reads private and generate public
|
693
|
-
the_private_key=options.get_next_argument('private_key')
|
694
|
-
the_public_key=OpenSSL::PKey::RSA.new(the_private_key).public_key.to_s
|
756
|
+
the_private_key = options.get_next_argument('private_key')
|
757
|
+
the_public_key = OpenSSL::PKey::RSA.new(the_private_key).public_key.to_s
|
695
758
|
aoc_api.update(resource_instance_path,{jwt_grant_enabled: true, public_key: the_public_key})
|
696
759
|
return Main.result_success
|
697
760
|
when :v3,:v4
|
698
|
-
res_data=aoc_api.read(resource_instance_path)[:data]
|
699
|
-
api_node=aoc_api.get_node_api(res_data)
|
761
|
+
res_data = aoc_api.read(resource_instance_path)[:data]
|
762
|
+
api_node = aoc_api.get_node_api(res_data)
|
700
763
|
return Node.new(@agents.merge(skip_basic_auth_options: true, node_api: api_node)).execute_action if command.eql?(:v3)
|
701
|
-
|
702
|
-
command_repo=options.get_next_command(NODE4_COMMANDS)
|
703
|
-
return execute_node_gen4_command(command_repo,{node_info: res_data, file_id:
|
704
|
-
when :
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
764
|
+
ak_root_file_id = api_node.read("access_keys/#{res_data['access_key']}")[:data]['root_file_id']
|
765
|
+
command_repo = options.get_next_command(NODE4_COMMANDS)
|
766
|
+
return execute_node_gen4_command(command_repo,{node_info: res_data, file_id: ak_root_file_id})
|
767
|
+
when :shared_folder
|
768
|
+
Log.log.warn('ATTENTION: alpha, under development, do not use')
|
769
|
+
# inside a workspace
|
770
|
+
command_shared = options.get_next_command(%i[list create delete]) # member
|
771
|
+
core_api=Rest.new(aoc_api.params.merge(base_url: aoc_api.params[:base_url].gsub('/api.','/sedemo.')))
|
772
|
+
# generic permission created for each shared folder
|
773
|
+
access_id = "#{ID_AK_ADMIN}_WS_#{res_id}"
|
774
|
+
case command_shared
|
775
|
+
when :list
|
776
|
+
query=options.get_option(:query)
|
777
|
+
query={'admin' => true, 'access_id' => access_id, 'access_type' => 'user'} if query.nil?
|
778
|
+
res_data = aoc_api.read("#{resource_instance_path}/permissions",query)[:data]
|
779
|
+
return { type: :object_list, data: res_data, fields: %w[id node_id file_id node_name file.path tags.aspera.files.workspace.share_as access_id]}
|
780
|
+
when :member
|
781
|
+
#https://sedemo.ibmaspera.com/api/v1/node/8669/permissions_and_members/3270?inherited=false&aspera-node-basic=8669&admin=true&page=1&per_page=25
|
782
|
+
when :delete
|
783
|
+
shared_id=instance_identifier
|
784
|
+
all_shared=aoc_api.read("#{resource_instance_path}/permissions",query)[:data].select{|i|i['id'].eql?(shared_id)}
|
785
|
+
raise 'no such shared folder id' if all_shared.empty?
|
786
|
+
raise 'error' unless all_shared.length.eql?(1)
|
787
|
+
shared_info=all_shared.first
|
788
|
+
#return { type: :single_object, data: shared_info}
|
789
|
+
node_id=shared_info['node_id']
|
790
|
+
Log.log.warn('ATTENTION: under dev: user vars: V1 and V2')
|
791
|
+
core_api.call(
|
792
|
+
operation: 'DELETE',
|
793
|
+
subpath: "node/#{node_id}/permissions",
|
794
|
+
headers: {
|
795
|
+
'Accept' => 'application/json',
|
796
|
+
'aspera-node-auth' => ENV['V1'],
|
797
|
+
'aspera-node-tokens' => ENV['V2']
|
798
|
+
},
|
799
|
+
url_params: {
|
800
|
+
'ids' => shared_id,
|
801
|
+
'aspera-node-basic' => node_id,
|
802
|
+
'aspera-node-prefer-basic' => node_id
|
803
|
+
}
|
804
|
+
)
|
805
|
+
return Main.result_success
|
806
|
+
when :create
|
807
|
+
# workspace information
|
808
|
+
ws_info = aoc_api.read(resource_instance_path)[:data]
|
809
|
+
shared_create_data = options.get_next_argument('creation data',type: Hash)
|
810
|
+
# node is either provided by user, or by default the one of workspace
|
811
|
+
node_id = shared_create_data.has_key?('node_id') ? shared_create_data['node_id'] : ws_info['node_id']
|
812
|
+
# remove from creation data if present, as it is not a standard argument
|
813
|
+
shared_create_data.delete('node_id')
|
814
|
+
# get optional link_name
|
815
|
+
#opt_link_name=shared_create_data['link_name']
|
816
|
+
shared_create_data.delete('link_name')
|
817
|
+
raise 'missing node information: path' unless shared_create_data.has_key?('path')
|
818
|
+
folder_path=shared_create_data['path']
|
819
|
+
shared_create_data.delete('path')
|
820
|
+
node_file={node_info: aoc_api.read("nodes/#{node_id}")[:data], file_id: 1}
|
821
|
+
node_file = aoc_api.resolve_node_file(node_file,folder_path)
|
822
|
+
node_api = aoc_api.get_node_api(node_file[:node_info])
|
823
|
+
access_id = "#{ID_AK_ADMIN}_WS_#{ws_info['id']}"
|
824
|
+
# use can specify: tags.aspera.files.workspace.share_as to File.basename(folder_path)
|
825
|
+
default_create_data = {
|
826
|
+
'file_id' => node_file[:file_id],
|
827
|
+
'access_type' => 'user',
|
828
|
+
'access_id' => access_id,
|
829
|
+
'access_levels' => %w[list read write delete mkdir rename preview],
|
830
|
+
'tags' => {'aspera' => {'files' => {'workspace' => {
|
831
|
+
'id' => ws_info['id'],
|
832
|
+
'workspace_name' => ws_info['name'],
|
833
|
+
'user_name' => aoc_api.user_info['name'],
|
834
|
+
'shared_by_user_id' => aoc_api.user_info['id'],
|
835
|
+
'shared_by_name' => aoc_api.user_info['name'],
|
836
|
+
'shared_by_email' => aoc_api.user_info['email'],
|
837
|
+
'shared_with_name' => access_id,
|
838
|
+
'access_key' => node_file[:node_info]['access_key'],
|
839
|
+
'node' => node_file[:node_info]['name']}
|
746
840
|
}}}}
|
747
|
-
|
748
|
-
|
749
|
-
|
841
|
+
shared_create_data = default_create_data.deep_merge(default_create_data) # ?aspera-node-basic=#{node_id}&aspera-node-prefer-basic=#{node_id}
|
842
|
+
created_data = node_api.create('permissions',shared_create_data)[:data]
|
843
|
+
# new API:
|
844
|
+
#created_data=aoc_api.create("node/#{node_id}/permissions",shared_create_data)[:data]
|
845
|
+
# TODO: send event
|
846
|
+
event_creation={
|
847
|
+
'types' => ['permission.created'],
|
848
|
+
'node_id' => node_file[:node_info]['id'],
|
849
|
+
'workspace_id' => ws_info['id'],
|
850
|
+
'data' => created_data # Response from previous step
|
851
|
+
}
|
852
|
+
#(optional). The name of the folder to be displayed to the destination user. Use it if its value is different from the "share_as" field.
|
853
|
+
#event_creation['link_name']=opt_link_name unless opt_link_name.nil?
|
854
|
+
aoc_api.create('events',event_creation)
|
855
|
+
return { type: :single_object, data: created_data}
|
856
|
+
end
|
750
857
|
else raise 'unknown command'
|
751
858
|
end
|
752
859
|
when :usage_reports
|
753
|
-
return {type: :object_list,data: aoc_api.read('usage_reports',{workspace_id: @
|
860
|
+
return {type: :object_list,data: aoc_api.read('usage_reports',{workspace_id: @workspace_info['id']})[:data]}
|
754
861
|
end
|
755
862
|
end
|
756
863
|
|
757
864
|
# must be public
|
758
|
-
ACTIONS=[
|
865
|
+
ACTIONS = %i[reminder servers bearer_token organization tier_restrictions user packages files admin automation gateway].freeze
|
759
866
|
|
760
867
|
def execute_action
|
761
|
-
command=options.get_next_command(ACTIONS)
|
868
|
+
command = options.get_next_command(ACTIONS)
|
762
869
|
case command
|
763
870
|
when :reminder
|
764
871
|
# send an email reminder with list of orgs
|
765
|
-
user_email=options.get_option(:username
|
872
|
+
user_email = options.get_option(:username,is_type: :mandatory)
|
766
873
|
Rest.new(base_url: "#{AoC.api_base_url}/#{AoC::API_V1}").create('organization_reminders',{email: user_email})[:data]
|
767
874
|
return Main.result_status("List of organizations user is member of, has been sent by e-mail to #{user_email}")
|
768
875
|
when :servers
|
@@ -774,19 +881,19 @@ save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTIO
|
|
774
881
|
when :tier_restrictions
|
775
882
|
return { type: :single_object, data: aoc_api.read('tier_restrictions')[:data] }
|
776
883
|
when :user
|
777
|
-
case options.get_next_command([
|
884
|
+
case options.get_next_command(%i[workspaces profile])
|
778
885
|
# when :settings
|
779
886
|
# return {type: :object_list,data: aoc_api.read('client_settings/')[:data]}
|
780
887
|
when :workspaces
|
781
|
-
case options.get_next_command([
|
888
|
+
case options.get_next_command(%i[list current])
|
782
889
|
when :list
|
783
|
-
return {type: :object_list,data: aoc_api.read('workspaces')[:data],fields: [
|
890
|
+
return {type: :object_list,data: aoc_api.read('workspaces')[:data],fields: %w[id name]}
|
784
891
|
when :current
|
785
892
|
set_workspace_info
|
786
|
-
return { type: :single_object, data: @
|
893
|
+
return { type: :single_object, data: @workspace_info }
|
787
894
|
end
|
788
895
|
when :profile
|
789
|
-
case options.get_next_command([
|
896
|
+
case options.get_next_command(%i[show modify])
|
790
897
|
when :show
|
791
898
|
return { type: :single_object, data: aoc_api.user_info }
|
792
899
|
when :modify
|
@@ -796,30 +903,33 @@ save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTIO
|
|
796
903
|
end
|
797
904
|
when :packages
|
798
905
|
set_workspace_info if @url_token_data.nil?
|
799
|
-
|
906
|
+
package_command = options.get_next_command([%i[shared_inboxes send recv list show delete],NODE4_CMD_PATH].flatten)
|
907
|
+
case package_command
|
800
908
|
when :shared_inboxes
|
801
|
-
case options.get_next_command([
|
909
|
+
case options.get_next_command(%i[list show])
|
802
910
|
when :list
|
803
|
-
query=option_url_query(nil)
|
911
|
+
query = option_url_query(nil)
|
804
912
|
if query.nil?
|
805
|
-
query={'embed[]'=>'dropbox','
|
913
|
+
query = {'embed[]' => 'dropbox','aggregate_permissions_by_dropbox' => true,'sort' => 'dropbox_name'}
|
914
|
+
query['workspace_id']=@workspace_info['id'] unless @workspace_info['id'].eql?(:undefined)
|
806
915
|
end
|
807
916
|
return {type: :object_list,data: aoc_api.read('dropbox_memberships',query)[:data],fields: ['dropbox_id','dropbox.name']}
|
808
917
|
when :show
|
809
918
|
return {type: :single_object,data: aoc_api.read(get_resource_path_from_args('dropboxes'),query)[:data]}
|
810
919
|
end
|
811
920
|
when :send
|
812
|
-
package_data=options.get_option(:value
|
921
|
+
package_data = options.get_option(:value,is_type: :mandatory)
|
813
922
|
raise CliBadArgument,'value must be hash, refer to doc' unless package_data.is_a?(Hash)
|
814
923
|
|
815
924
|
if !@url_token_data.nil?
|
816
|
-
assert_public_link_types([
|
817
|
-
box_type
|
818
|
-
package_data['recipients']=[{'id'
|
819
|
-
|
925
|
+
assert_public_link_types(%w[send_package_to_user send_package_to_dropbox])
|
926
|
+
box_type = @url_token_data['purpose'].split('_').last
|
927
|
+
package_data['recipients'] = [{'id' => @url_token_data['data']["#{box_type}_id"],'type' => box_type}]
|
928
|
+
# TODO: probably this line is not needed
|
929
|
+
@workspace_info['id'] = @url_token_data['data']['workspace_id']
|
820
930
|
end
|
821
931
|
|
822
|
-
package_data['workspace_id']
|
932
|
+
package_data['workspace_id'] = @workspace_info['id']
|
823
933
|
|
824
934
|
# list of files to include in package, optional
|
825
935
|
#package_data['file_names']=self.transfer.ts_source_paths.map{|i|File.basename(i['source'])}
|
@@ -828,17 +938,18 @@ save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTIO
|
|
828
938
|
resolve_package_recipients(package_data,'recipients')
|
829
939
|
resolve_package_recipients(package_data,'bcc_recipients')
|
830
940
|
normalize_metadata(package_data)
|
941
|
+
validate_metadata(package_data) if options.get_option(:validate_metadata)
|
831
942
|
|
832
943
|
# create a new package container
|
833
|
-
package_info=aoc_api.create('packages',package_data)[:data]
|
944
|
+
package_info = aoc_api.create('packages',package_data)[:data]
|
834
945
|
|
835
946
|
# get node information for the node on which package must be created
|
836
|
-
node_info=aoc_api.read("nodes/#{package_info['node_id']}")[:data]
|
947
|
+
node_info = aoc_api.read("nodes/#{package_info['node_id']}")[:data]
|
837
948
|
|
838
949
|
# tell AoC what to expect in package: 1 transfer (can also be done after transfer)
|
839
950
|
# TODO: if multisession was used we should probably tell
|
840
951
|
# also, currently no "multi-source" , i.e. only from client-side files, unless "node" agent is used
|
841
|
-
aoc_api.update("packages/#{package_info['id']}",{'sent'=>true,'transfers_expected'=>1})[:data]
|
952
|
+
aoc_api.update("packages/#{package_info['id']}",{'sent' => true,'transfers_expected' => 1})[:data]
|
842
953
|
|
843
954
|
# get destination: package folder
|
844
955
|
node_file = {node_info: node_info, file_id: package_info['contents_file_id']}
|
@@ -852,130 +963,149 @@ save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTIO
|
|
852
963
|
options.set_option(:id,@url_token_data['data']['package_id'])
|
853
964
|
end
|
854
965
|
# scalar here
|
855
|
-
ids_to_download=instance_identifier
|
856
|
-
skip_ids_data=[]
|
857
|
-
skip_ids_persistency=nil
|
858
|
-
if options.get_option(:once_only
|
859
|
-
skip_ids_persistency=PersistencyActionOnce.new(
|
860
|
-
|
861
|
-
|
862
|
-
|
966
|
+
ids_to_download = instance_identifier
|
967
|
+
skip_ids_data = []
|
968
|
+
skip_ids_persistency = nil
|
969
|
+
if options.get_option(:once_only,is_type: :mandatory)
|
970
|
+
skip_ids_persistency = PersistencyActionOnce.new(
|
971
|
+
manager: @agents[:persistency],
|
972
|
+
data: skip_ids_data,
|
973
|
+
id: IdGenerator.from_list(['aoc_recv',options.get_option(:url,is_type: :mandatory),@workspace_info['id']].push(*@persist_ids)))
|
863
974
|
end
|
864
975
|
if ids_to_download.eql?(VAL_ALL)
|
865
976
|
# get list of packages in inbox
|
866
|
-
package_info=aoc_api.read('packages',{
|
977
|
+
package_info = aoc_api.read('packages',{
|
978
|
+
'archived' => false,
|
979
|
+
'exclude_dropbox_packages' => true,
|
980
|
+
'has_content' => true,
|
981
|
+
'received' => true,
|
982
|
+
'workspace_id' => @workspace_info['id']})[:data]
|
867
983
|
# remove from list the ones already downloaded
|
868
|
-
ids_to_download=package_info.map{|e|e['id']}
|
984
|
+
ids_to_download = package_info.map{|e|e['id']}
|
869
985
|
# array here
|
870
986
|
ids_to_download.reject!{|id|skip_ids_data.include?(id)}
|
871
987
|
end # ALL
|
872
988
|
# list here
|
873
989
|
ids_to_download = [ids_to_download] unless ids_to_download.is_a?(Array)
|
874
|
-
result_transfer=[]
|
990
|
+
result_transfer = []
|
875
991
|
self.format.display_status("found #{ids_to_download.length} package(s).")
|
876
992
|
ids_to_download.each do |package_id|
|
877
|
-
package_info=aoc_api.read("packages/#{package_id}")[:data]
|
878
|
-
node_info=aoc_api.read("nodes/#{package_info['node_id']}")[:data]
|
993
|
+
package_info = aoc_api.read("packages/#{package_id}")[:data]
|
994
|
+
node_info = aoc_api.read("nodes/#{package_info['node_id']}")[:data]
|
879
995
|
self.format.display_status("downloading package: #{package_info['name']}")
|
880
|
-
add_ts={'paths'=>[{'source'=>'.'}]}
|
996
|
+
add_ts = {'paths' => [{'source' => '.'}]}
|
881
997
|
node_file = {node_info: node_info, file_id: package_info['contents_file_id']}
|
882
|
-
statuses=transfer_start(AoC::PACKAGES_APP,Fasp::TransferSpec::DIRECTION_RECEIVE,node_file,AoC.package_tags(package_info,'download').merge(add_ts))
|
883
|
-
result_transfer.push({'package'=>package_id,Main::STATUS_FIELD=>statuses})
|
998
|
+
statuses = transfer_start(AoC::PACKAGES_APP,Fasp::TransferSpec::DIRECTION_RECEIVE,node_file,AoC.package_tags(package_info,'download').merge(add_ts))
|
999
|
+
result_transfer.push({'package' => package_id,Main::STATUS_FIELD => statuses})
|
884
1000
|
# update skip list only if all transfer sessions completed
|
885
1001
|
if TransferAgent.session_status(statuses).eql?(:success)
|
886
1002
|
skip_ids_data.push(package_id)
|
887
|
-
skip_ids_persistency
|
1003
|
+
skip_ids_persistency&.save
|
888
1004
|
end
|
889
1005
|
end
|
890
1006
|
return Main.result_transfer_multiple(result_transfer)
|
891
1007
|
when :show
|
892
|
-
package_id=options.get_next_argument('package ID')
|
893
|
-
package_info=aoc_api.read("packages/#{package_id}")[:data]
|
1008
|
+
package_id = options.get_next_argument('package ID')
|
1009
|
+
package_info = aoc_api.read("packages/#{package_id}")[:data]
|
894
1010
|
return { type: :single_object, data: package_info }
|
895
1011
|
when :list
|
896
|
-
|
1012
|
+
display_fields=%w[id name bytes_transferred]
|
1013
|
+
query = option_url_query({'archived' => false,'exclude_dropbox_packages' => true,'has_content' => true,'received' => true})
|
897
1014
|
if query.has_key?('dropbox_name')
|
898
1015
|
# convenience: specify name instead of id
|
899
1016
|
raise 'not both dropbox_name and dropbox_id' if query.has_key?('dropbox_id')
|
900
|
-
query['dropbox_id']=aoc_api.lookup_entity_by_name('dropboxes',query['dropbox_name'])['id']
|
1017
|
+
query['dropbox_id'] = aoc_api.lookup_entity_by_name('dropboxes',query['dropbox_name'])['id']
|
901
1018
|
query.delete('dropbox_name')
|
902
1019
|
end
|
903
1020
|
raise 'option must be Hash' unless query.is_a?(Hash)
|
904
|
-
|
905
|
-
|
906
|
-
|
1021
|
+
if @workspace_info['id'].eql?(:undefined)
|
1022
|
+
display_fields.push('workspace_id')
|
1023
|
+
else
|
1024
|
+
query['workspace_id'] ||= @workspace_info['id']
|
1025
|
+
end
|
1026
|
+
packages = aoc_api.read('packages',query)[:data]
|
1027
|
+
return {type: :object_list,data: packages,fields: display_fields}
|
907
1028
|
when :delete
|
908
|
-
list_or_one=instance_identifier
|
1029
|
+
list_or_one = instance_identifier
|
909
1030
|
return do_bulk_operation(list_or_one,'deleted') do |id|
|
910
1031
|
raise 'expecting String identifier' unless id.is_a?(String) || id.is_a?(Integer)
|
911
1032
|
aoc_api.delete("packages/#{id}")[:data]
|
912
1033
|
end
|
1034
|
+
when *NODE4_CMD_PATH
|
1035
|
+
package_id = options.get_next_argument('package ID')
|
1036
|
+
#path = options.get_next_argument('path', mandatory: false) || '/'
|
1037
|
+
package_info = aoc_api.read("packages/#{package_id}")[:data]
|
1038
|
+
package_node_file = {
|
1039
|
+
node_info: aoc_api.read("nodes/#{package_info['node_id']}")[:data],
|
1040
|
+
file_id: package_info['file_id']
|
1041
|
+
}
|
1042
|
+
return execute_node_gen4_command(package_command,package_node_file)
|
913
1043
|
end
|
914
1044
|
when :files
|
915
1045
|
# get workspace related information
|
916
1046
|
set_workspace_info
|
917
1047
|
set_home_node_file
|
918
|
-
command_repo=options.get_next_command([NODE4_COMMANDS,:short_link].flatten)
|
1048
|
+
command_repo = options.get_next_command([NODE4_COMMANDS,:short_link].flatten)
|
919
1049
|
case command_repo
|
920
1050
|
when *NODE4_COMMANDS then return execute_node_gen4_command(command_repo,@home_node_file)
|
921
1051
|
when :short_link
|
922
|
-
folder_dest=options.get_option(:to_folder
|
923
|
-
value_option=options.get_option(:value
|
1052
|
+
folder_dest = options.get_option(:to_folder)
|
1053
|
+
value_option = options.get_option(:value)
|
924
1054
|
case value_option
|
925
|
-
when 'public' then value_option={'purpose'=>'token_auth_redirection'}
|
926
|
-
when 'private' then value_option={'purpose'=>'shared_folder_auth_link'}
|
1055
|
+
when 'public' then value_option = {'purpose' => 'token_auth_redirection'}
|
1056
|
+
when 'private' then value_option = {'purpose' => 'shared_folder_auth_link'}
|
927
1057
|
when NilClass,Hash then nil # keep value
|
928
1058
|
else raise 'value must be either: public, private, Hash or nil'
|
929
1059
|
end
|
930
|
-
create_params=nil
|
931
|
-
node_file=nil
|
1060
|
+
create_params = nil
|
1061
|
+
node_file = nil
|
932
1062
|
if !folder_dest.nil?
|
933
1063
|
node_file = aoc_api.resolve_node_file(@home_node_file,folder_dest)
|
934
|
-
create_params={
|
935
|
-
file_id:
|
936
|
-
node_id:
|
937
|
-
workspace_id: @
|
1064
|
+
create_params = {
|
1065
|
+
file_id: node_file[:file_id],
|
1066
|
+
node_id: node_file[:node_info]['id'],
|
1067
|
+
workspace_id: @workspace_info['id']
|
938
1068
|
}
|
939
1069
|
end
|
940
1070
|
if !value_option.nil? && !create_params.nil?
|
941
1071
|
case value_option['purpose']
|
942
1072
|
when 'shared_folder_auth_link'
|
943
|
-
value_option['data']=create_params
|
944
|
-
value_option['user_selected_name']=nil
|
1073
|
+
value_option['data'] = create_params
|
1074
|
+
value_option['user_selected_name'] = nil
|
945
1075
|
when 'token_auth_redirection'
|
946
|
-
create_params['name']=''
|
947
|
-
value_option['data']={
|
948
|
-
aoc:
|
1076
|
+
create_params['name'] = ''
|
1077
|
+
value_option['data'] = {
|
1078
|
+
aoc: true,
|
949
1079
|
url_token_data: {
|
950
|
-
|
951
|
-
|
1080
|
+
data: create_params,
|
1081
|
+
purpose: 'view_shared_file'
|
952
1082
|
}
|
953
1083
|
}
|
954
|
-
value_option['user_selected_name']=nil
|
1084
|
+
value_option['user_selected_name'] = nil
|
955
1085
|
else
|
956
1086
|
raise 'purpose must be one of: token_auth_redirection or shared_folder_auth_link'
|
957
1087
|
end
|
958
1088
|
options.set_option(:value,value_option)
|
959
1089
|
end
|
960
|
-
result=entity_action(@api_aoc,'short_links',id_default: 'self')
|
1090
|
+
result = entity_action(@api_aoc,'short_links',id_default: 'self')
|
961
1091
|
if result[:data].is_a?(Hash) && result[:data].has_key?('created_at') && result[:data]['resource_type'].eql?('UrlToken')
|
962
|
-
node_api=aoc_api.get_node_api(node_file[:node_info])
|
1092
|
+
node_api = aoc_api.get_node_api(node_file[:node_info])
|
963
1093
|
# TODO: access level as arg
|
964
|
-
access_levels=Aspera::Node::ACCESS_LEVELS #['delete','list','mkdir','preview','read','rename','write']
|
965
|
-
perm_data={
|
966
|
-
'file_id'
|
967
|
-
'access_type'
|
968
|
-
'access_id'
|
969
|
-
'access_levels'=>access_levels,
|
970
|
-
'tags'
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
1094
|
+
access_levels = Aspera::Node::ACCESS_LEVELS #['delete','list','mkdir','preview','read','rename','write']
|
1095
|
+
perm_data = {
|
1096
|
+
'file_id' => node_file[:file_id],
|
1097
|
+
'access_type' => 'user',
|
1098
|
+
'access_id' => result[:data]['resource_id'],
|
1099
|
+
'access_levels' => access_levels,
|
1100
|
+
'tags' => {
|
1101
|
+
'url_token' => true,
|
1102
|
+
'workspace_id' => @workspace_info['id'],
|
1103
|
+
'workspace_name' => @workspace_info['name'],
|
1104
|
+
'folder_name' => 'my folder',
|
1105
|
+
'created_by_name' => aoc_api.user_info['name'],
|
1106
|
+
'created_by_email' => aoc_api.user_info['email'],
|
1107
|
+
'access_key' => node_file[:node_info]['access_key'],
|
1108
|
+
'node' => node_file[:node_info]['host']
|
979
1109
|
}
|
980
1110
|
}
|
981
1111
|
node_api.create("permissions?file_id=#{node_file[:file_id]}",perm_data)
|
@@ -983,36 +1113,36 @@ save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTIO
|
|
983
1113
|
end
|
984
1114
|
return result
|
985
1115
|
end # files command
|
986
|
-
throw
|
1116
|
+
throw('Error: shall not reach this line')
|
987
1117
|
when :automation
|
988
1118
|
Log.log.warn('BETA: work under progress')
|
989
1119
|
# automation api is not in the same place
|
990
|
-
automation_rest_params=aoc_api.params.clone
|
1120
|
+
automation_rest_params = aoc_api.params.clone
|
991
1121
|
automation_rest_params[:base_url].gsub!('/api/','/automation/')
|
992
|
-
automation_api=Rest.new(automation_rest_params)
|
993
|
-
command_automation=options.get_next_command([
|
1122
|
+
automation_api = Rest.new(automation_rest_params)
|
1123
|
+
command_automation = options.get_next_command(%i[workflows instances])
|
994
1124
|
case command_automation
|
995
1125
|
when :instances
|
996
1126
|
return entity_action(@api_aoc,'workflow_instances')
|
997
1127
|
when :workflows
|
998
|
-
wf_command=options.get_next_command([Plugin::ALL_OPS,:action,:launch].flatten)
|
1128
|
+
wf_command = options.get_next_command([Plugin::ALL_OPS,:action,:launch].flatten)
|
999
1129
|
case wf_command
|
1000
1130
|
when *Plugin::ALL_OPS
|
1001
1131
|
return entity_command(wf_command,automation_api,'workflows',id_default: :id)
|
1002
1132
|
when :launch
|
1003
|
-
wf_id=instance_identifier
|
1004
|
-
data=automation_api.create("workflows/#{wf_id}/launch",{})[:data]
|
1133
|
+
wf_id = instance_identifier
|
1134
|
+
data = automation_api.create("workflows/#{wf_id}/launch",{})[:data]
|
1005
1135
|
return {type: :single_object,data: data}
|
1006
1136
|
when :action
|
1007
1137
|
#TODO: not complete
|
1008
|
-
wf_id=instance_identifier
|
1009
|
-
wf_action_cmd=options.get_next_command([
|
1138
|
+
wf_id = instance_identifier
|
1139
|
+
wf_action_cmd = options.get_next_command(%i[list create show])
|
1010
1140
|
Log.log.warn("Not implemented: #{wf_action_cmd}")
|
1011
|
-
step=automation_api.create('steps',{'workflow_id'=>wf_id})[:data]
|
1012
|
-
automation_api.update("workflows/#{wf_id}",{'step_order'=>[step['id']]})
|
1013
|
-
action=automation_api.create('actions',{'step_id'=>step['id'],'type'=>'manual'})[:data]
|
1014
|
-
automation_api.update("steps/#{step['id']}",{'action_order'=>[action['id']]})
|
1015
|
-
wf=automation_api.read("workflows/#{wf_id}")[:data]
|
1141
|
+
step = automation_api.create('steps',{'workflow_id' => wf_id})[:data]
|
1142
|
+
automation_api.update("workflows/#{wf_id}",{'step_order' => [step['id']]})
|
1143
|
+
action = automation_api.create('actions',{'step_id' => step['id'],'type' => 'manual'})[:data]
|
1144
|
+
automation_api.update("steps/#{step['id']}",{'action_order' => [action['id']]})
|
1145
|
+
wf = automation_api.read("workflows/#{wf_id}")[:data]
|
1016
1146
|
return {type: :single_object,data: wf}
|
1017
1147
|
end
|
1018
1148
|
end
|
@@ -1021,14 +1151,15 @@ save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTIO
|
|
1021
1151
|
when :gateway
|
1022
1152
|
set_workspace_info
|
1023
1153
|
require 'aspera/faspex_gw'
|
1024
|
-
FaspexGW.new(@api_aoc,@
|
1154
|
+
FaspexGW.new(@api_aoc,@workspace_info['id']).start_server
|
1025
1155
|
else
|
1026
1156
|
raise "internal error: #{command}"
|
1027
1157
|
end # action
|
1028
1158
|
raise 'internal error: command shall return'
|
1029
1159
|
end
|
1030
1160
|
|
1031
|
-
private :aoc_params,:set_workspace_info,:set_home_node_file,:do_bulk_operation,:resolve_package_recipients,:option_url_query,:assert_public_link_types
|
1161
|
+
private :aoc_params,:set_workspace_info,:set_home_node_file,:do_bulk_operation,:resolve_package_recipients,:option_url_query,:assert_public_link_types,
|
1162
|
+
:execute_admin_action
|
1032
1163
|
private_constant :VAL_ALL,:NODE4_COMMANDS, :ID_AK_ADMIN
|
1033
1164
|
end # AoC
|
1034
1165
|
end # Plugins
|