Skip to content

Commit c18dc67

Browse files
authored
FrontEnd talks to WebServer about files (#289)
FileManager/FilePicker features: * List MyDocuments * List Node Files * Upload file * Download file * Delete file * Copy from simcore to simcore * Copy from simcore to datcore
1 parent 3b62a0f commit c18dc67

File tree

7 files changed

+171
-66
lines changed

7 files changed

+171
-66
lines changed

services/web/client/source/class/qxapp/component/widget/FileManager.js

+52-12
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ qx.Class.define("qxapp.component.widget.FileManager", {
2222
treesLayout.add(nodeTree, {
2323
flex: 1
2424
});
25-
nodeTree.getSelection().addListener("change", this.__itemSelected, this);
25+
nodeTree.getSelection().addListener("change", this.__nodeItemSelected, this);
2626

2727
let userTree = this.__userTree = this._createChildControlImpl("userTree");
2828
treesLayout.add(userTree, {
2929
flex: 1
3030
});
31-
userTree.getSelection().addListener("change", this.__itemSelected, this);
31+
userTree.getSelection().addListener("change", this.__userItemSelected, this);
3232

3333
let selectedFileLayout = this._createChildControlImpl("selectedFileLayout");
3434
{
@@ -116,7 +116,7 @@ qx.Class.define("qxapp.component.widget.FileManager", {
116116

117117
__reloadNodeTree: function() {
118118
let filesTreePopulator = new qxapp.utils.FilesTreePopulator(this.__nodeTree);
119-
filesTreePopulator.populateNodeFiles();
119+
filesTreePopulator.populateNodeFiles(this.getNodeModel().getNodeId());
120120

121121
let that = this;
122122
let delegate = this.__nodeTree.getDelegate();
@@ -190,30 +190,59 @@ qx.Class.define("qxapp.component.widget.FileManager", {
190190
if (e.supportsType("osparc-filePath")) {
191191
const from = e.getRelatedTarget();
192192
const to = e.getCurrentTarget();
193+
let store = qxapp.data.Store.getInstance();
193194
console.log("Copy", from.getFileId(), "to", to.getPath());
195+
store.copyFile(from.getLocation(), from.getFileId(), to.getLocation(), to.getPath());
196+
store.addListenerOnce("FileCopied", ev => {
197+
this.__reloadUserTree();
198+
}, this);
194199
}
195200
}, this);
196201
},
197202

198-
__itemSelected: function() {
203+
__nodeItemSelected: function() {
199204
let selectedItem = this.__nodeTree.getSelection();
200205
if (selectedItem.length < 1) {
201206
return;
202207
}
208+
this.__userTree.resetSelection();
203209
selectedItem = selectedItem.toArray();
204-
if (this.__isFile(selectedItem[0])) {
205-
this.__selection = selectedItem[0].getFileId();
206-
this.__selectedLabel.setValue(selectedItem[0].getFileId());
210+
this.__itemSelected(selectedItem[0]);
211+
},
212+
213+
__userItemSelected: function() {
214+
let selectedItem = this.__userTree.getSelection();
215+
if (selectedItem.length < 1) {
216+
return;
217+
}
218+
this.__nodeTree.resetSelection();
219+
selectedItem = selectedItem.toArray();
220+
this.__itemSelected(selectedItem[0]);
221+
},
222+
223+
__itemSelected: function(selectedItem) {
224+
if (this.__isFile(selectedItem)) {
225+
this.__selection = selectedItem;
226+
this.__selectedLabel.setValue(selectedItem.getFileId());
207227
} else {
208228
this.__selection = null;
209229
this.__selectedLabel.setValue("");
210230
}
211231
},
212232

233+
__getItemSelected: function() {
234+
let selectedItem = this.__selection;
235+
if (selectedItem && this.__isFile(selectedItem)) {
236+
return selectedItem;
237+
}
238+
return null;
239+
},
240+
213241
// Request to the server an download
214242
__retrieveURLAndDownload: function() {
215-
if (this.__selection !== null) {
216-
const fileId = this.__selection;
243+
let selection = this.__getItemSelected();
244+
if (selection) {
245+
const fileId = selection.getFileId();
217246
let fileName = fileId.split("/");
218247
fileName = fileName[fileName.length-1];
219248
let store = qxapp.data.Store.getInstance();
@@ -225,7 +254,7 @@ qx.Class.define("qxapp.component.widget.FileManager", {
225254
}
226255
}, this);
227256
const download = true;
228-
const locationId = 0;
257+
const locationId = selection.getLocation();
229258
store.getPresginedLink(download, locationId, fileId);
230259
}
231260
},
@@ -237,7 +266,7 @@ qx.Class.define("qxapp.component.widget.FileManager", {
237266
xhr.onload = () => {
238267
console.log("onload", xhr);
239268
if (xhr.status == 200) {
240-
var blob = new Blob(xhr.response);
269+
let blob = new Blob([xhr.response]);
241270
let urlBlob = window.URL.createObjectURL(blob);
242271
let downloadAnchorNode = document.createElement("a");
243272
downloadAnchorNode.setAttribute("href", urlBlob);
@@ -250,7 +279,18 @@ qx.Class.define("qxapp.component.widget.FileManager", {
250279
},
251280

252281
__deleteFile: function() {
253-
console.log("Delete ", this.__selection);
282+
let selection = this.__getItemSelected();
283+
if (selection) {
284+
console.log("Delete ", selection);
285+
const fileId = selection.getFileId();
286+
const locationId = selection.getLocation();
287+
let store = qxapp.data.Store.getInstance();
288+
store.addListenerOnce("DeleteFile", e => {
289+
this.__reloadNodeTree();
290+
this.__reloadUserTree();
291+
}, this);
292+
store.deleteFile(locationId, fileId);
293+
}
254294
}
255295
}
256296
});

services/web/client/source/class/qxapp/component/widget/FileTreeItem.js

+6
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ qx.Class.define("qxapp.component.widget.FileTreeItem", {
3232
nullable : true
3333
},
3434

35+
location : {
36+
check : "String",
37+
event: "changePath",
38+
nullable : true
39+
},
40+
3541
size : {
3642
check : "String",
3743
event: "changeSize",

services/web/client/source/class/qxapp/data/Converters.js

+58-44
Original file line numberDiff line numberDiff line change
@@ -39,58 +39,72 @@ qx.Class.define("qxapp.data.Converters", {
3939
}
4040
},
4141

42+
__isLocationValid: function(locID) {
43+
if (locID === 0 || locID === "0" ||
44+
locID === 1 || locID === "1") {
45+
return true;
46+
}
47+
return false;
48+
},
49+
4250
fromDSMToVirtualTreeModel: function(files) {
4351
let children = [];
4452
for (let i=0; i<files.length; i++) {
4553
const file = files[i];
46-
let fileInTree = {
47-
label: file["location"],
48-
path: file["location"],
49-
children: []
50-
};
51-
if (file["location_id"] === 0 || file["location_id"] === "0") {
52-
// simcore files
53-
let splitted = file["file_uuid"].split("/");
54-
if (splitted.length === 3) {
55-
const prjId = splitted[0];
56-
const nodejId = splitted[1];
57-
const fileId = splitted[2];
58-
// node file
59-
fileInTree.children.push({
60-
label: file["project_name"] ? file["project_name"] : prjId,
61-
path: fileInTree.path +"/"+ file["project_name"],
62-
children: [{
63-
label: file["node_name"] ? file["node_name"] : nodejId,
64-
path: fileInTree.path +"/"+ file["project_name"] +"/"+ file["node_name"],
54+
if (this.__isLocationValid(file["location_id"])) {
55+
let fileInTree = {
56+
label: file["location"],
57+
location: file["location_id"],
58+
path: "",
59+
children: []
60+
};
61+
if (file["location_id"] === 0 || file["location_id"] === "0") {
62+
// simcore files
63+
let splitted = file["file_uuid"].split("/");
64+
if (splitted.length === 3) {
65+
const prjId = splitted[0];
66+
const nodejId = splitted[1];
67+
const fileId = splitted[2];
68+
// node file
69+
fileInTree.children.push({
70+
label: file["project_name"] === "" ? prjId : file["project_name"],
71+
location: file["location_id"],
72+
path: prjId,
6573
children: [{
66-
label: file["file_name"] ? file["file_name"] : fileId,
67-
fileId: file["file_uuid"],
68-
location: file["location_id"]
74+
label: file["node_name"] === "" ? nodejId : file["node_name"],
75+
location: file["location_id"],
76+
path: prjId +"/"+ nodejId,
77+
children: [{
78+
label: file["file_name"] === "" ? fileId : file["file_name"],
79+
location: file["location_id"],
80+
fileId: file["file_uuid"]
81+
}]
6982
}]
70-
}]
71-
});
72-
this.mergeChildren(children, fileInTree);
73-
}
74-
} else if (file["location_id"] === 1 || file["location_id"] === "1") {
75-
// datcore files
76-
let parent = fileInTree;
77-
let splitted = file["file_uuid"].split("/");
78-
for (let j=0; j<splitted.length-1; j++) {
79-
const newItem = {
80-
label: splitted[j],
81-
path: parent.path +"/"+ splitted[j],
82-
children: []
83+
});
84+
this.mergeChildren(children, fileInTree);
85+
}
86+
} else if (file["location_id"] === 1 || file["location_id"] === "1") {
87+
// datcore files
88+
let parent = fileInTree;
89+
let splitted = file["file_uuid"].split("/");
90+
for (let j=0; j<splitted.length-1; j++) {
91+
const newItem = {
92+
label: splitted[j],
93+
location: file["location_id"],
94+
path: parent.path === "" ? splitted[j] : parent.path +"/"+ splitted[j],
95+
children: []
96+
};
97+
parent.children.push(newItem);
98+
parent = newItem;
99+
}
100+
let fileInfo = {
101+
label: splitted[splitted.length-1],
102+
location: file["location_id"],
103+
fileId: file["file_uuid"]
83104
};
84-
parent.children.push(newItem);
85-
parent = newItem;
105+
parent.children.push(fileInfo);
106+
this.mergeChildren(children, fileInTree);
86107
}
87-
let fileInfo = {
88-
label: splitted[splitted.length-1],
89-
fileId: file["file_uuid"],
90-
location: file["location_id"]
91-
};
92-
parent.children.push(fileInfo);
93-
this.mergeChildren(children, fileInTree);
94108
}
95109
}
96110

services/web/client/source/class/qxapp/data/Store.js

+38-5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ qx.Class.define("qxapp.data.Store", {
99
"MyDocuments": "qx.event.type.Event",
1010
"NodeFiles": "qx.event.type.Event",
1111
"PresginedLink": "qx.event.type.Event",
12+
"FileCopied": "qx.event.type.Event",
1213
"DeleteFile": "qx.event.type.Event",
1314
"FakeFiles": "qx.event.type.Event"
1415
},
@@ -721,8 +722,8 @@ qx.Class.define("qxapp.data.Store", {
721722
this.fireDataEvent("FakeFiles", data);
722723
},
723724

724-
getNodeFiles: function(prjId, nodeId) {
725-
const filter = "?uuid_filter=" + nodeId;
725+
getNodeFiles: function(nodeId) {
726+
const filter = "?uuid_filter=" + encodeURIComponent(nodeId);
726727
let endPoint = "/storage/locations/0/files/metadata";
727728
endPoint += filter;
728729
let reqFiles = new qxapp.io.request.ApiRequest(endPoint, "GET");
@@ -740,7 +741,7 @@ qx.Class.define("qxapp.data.Store", {
740741
const {
741742
error
742743
} = e.getTarget().getResponse();
743-
console.log("Failed getting NodeF iles list", error);
744+
console.log("Failed getting Node Files list", error);
744745
});
745746

746747
reqFiles.send();
@@ -821,9 +822,41 @@ qx.Class.define("qxapp.data.Store", {
821822
req.send();
822823
},
823824

825+
copyFile: function(fromLoc, fileUuid, toLoc, pathId) {
826+
// "/v0/locations/1/files/{}?user_id={}&extra_location={}&extra_source={}".format(quote(datcore_uuid, safe=''),
827+
let fileName = fileUuid.split("/");
828+
fileName = fileName[fileName.length-1];
829+
let endPoint = "/storage/locations/"+toLoc+"/files/";
830+
let parameters = encodeURIComponent(pathId + "/" + fileName);
831+
parameters += "?extra_location=";
832+
parameters += fromLoc;
833+
parameters += "&extra_source=";
834+
parameters += encodeURIComponent(fileUuid);
835+
endPoint += parameters;
836+
let req = new qxapp.io.request.ApiRequest(endPoint, "PUT");
837+
838+
req.addListener("success", e => {
839+
const {
840+
data
841+
} = e.getTarget().getResponse();
842+
this.fireDataEvent("FileCopied", data);
843+
}, this);
844+
845+
req.addListener("fail", e => {
846+
const {
847+
error
848+
} = e.getTarget().getResponse();
849+
console.log(error);
850+
console.log("Failed copying file", fileUuid, "to", pathId);
851+
});
852+
853+
req.send();
854+
},
855+
824856
deleteFile: function(locationId, fileUuid) {
825857
// Deletes File
826-
const endPoint = "/storage/locations/" + locationId + "/files/" + fileUuid;
858+
let parameters = encodeURIComponent(fileUuid);
859+
const endPoint = "/storage/locations/" + locationId + "/files/" + parameters;
827860
let req = new qxapp.io.request.ApiRequest(endPoint, "DELETE");
828861

829862
req.addListener("success", e => {
@@ -837,7 +870,7 @@ qx.Class.define("qxapp.data.Store", {
837870
const {
838871
error
839872
} = e.getTarget().getResponse();
840-
console.log("Failed getting Presgined Link", error);
873+
console.log("Failed deleting file", error);
841874
});
842875

843876
req.send();

services/web/client/source/class/qxapp/utils/FilesTreePopulator.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ qx.Class.define("qxapp.utils.FilesTreePopulator", {
1010
members: {
1111
__tree: null,
1212

13-
populateNodeFiles: function(prjId, nodeId) {
13+
populateNodeFiles: function(nodeId) {
1414
const treeName = "Node files";
1515
this.__resetTree(treeName);
1616
let store = qxapp.data.Store.getInstance();
@@ -21,7 +21,7 @@ qx.Class.define("qxapp.utils.FilesTreePopulator", {
2121
this.__addTreeData(newChildren);
2222
}, this);
2323

24-
store.getNodeFiles(prjId, nodeId);
24+
store.getNodeFiles(nodeId);
2525
},
2626

2727
populateMyDocuments: function() {
@@ -50,6 +50,9 @@ qx.Class.define("qxapp.utils.FilesTreePopulator", {
5050
this.__tree.resetModel();
5151
let data = {
5252
label: treeName,
53+
fileId: null,
54+
location: null,
55+
path: null,
5356
children: []
5457
};
5558
let emptyModel = qx.data.marshal.Json.createModel(data, true);
@@ -59,6 +62,7 @@ qx.Class.define("qxapp.utils.FilesTreePopulator", {
5962
bindItem: (c, item, id) => {
6063
c.bindDefaultProperties(item, id);
6164
c.bindProperty("fileId", "fileId", null, item, id);
65+
c.bindProperty("location", "location", null, item, id);
6266
c.bindProperty("path", "path", null, item, id);
6367
c.bindProperty("size", "size", null, item, id);
6468
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from aiohttp import web
2+
3+
4+
APP_LOGIN_CONFIG = __name__ + ".config"
5+
CFG_LOGIN_STORAGE = "STORAGE" # Needs to match login.cfg!!!
6+
7+
8+
def get_storage(app: web.Application):
9+
return app[APP_LOGIN_CONFIG][CFG_LOGIN_STORAGE]

0 commit comments

Comments
 (0)