From 52e05007f76f37074b4b916b5984546cc2ec069c Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Thu, 8 Aug 2024 09:40:26 +0200 Subject: [PATCH 01/67] update folder keys --- .../osparc/dashboard/FolderButtonItem.js | 2 +- .../source/class/osparc/data/model/Folder.js | 2 +- .../source/class/osparc/store/Folders.js | 26 +++++++++---------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js index fa21ec7c9b7..fe2b5f4171d 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js @@ -150,7 +150,7 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { this.set({ cardKey: "folder-" + folder.getId() }); - folder.bind("id", this, "folderId"); + folder.bind("folderId", this, "folderId"); folder.bind("parentId", this, "parentFolderId"); folder.bind("name", this, "title"); folder.bind("description", this, "description"); diff --git a/services/static-webserver/client/source/class/osparc/data/model/Folder.js b/services/static-webserver/client/source/class/osparc/data/model/Folder.js index 08b64a17d0a..c7d3d012030 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Folder.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Folder.js @@ -30,7 +30,7 @@ qx.Class.define("osparc.data.model.Folder", { this.set({ id: folderData.id, - parentId: folderData.parentFolder, + parentId: folderData.parentFolderId, name: folderData.name, description: folderData.description, myAccessRights: folderData.myAccessRights, diff --git a/services/static-webserver/client/source/class/osparc/store/Folders.js b/services/static-webserver/client/source/class/osparc/store/Folders.js index 3188e5b8f89..04c789bd6f3 100644 --- a/services/static-webserver/client/source/class/osparc/store/Folders.js +++ b/services/static-webserver/client/source/class/osparc/store/Folders.js @@ -29,8 +29,8 @@ qx.Class.define("osparc.store.Folders", { statics: { FOLDER_DATA_INIT: [{ - id: 1, - parentFolder: null, + folderId: 1, + parentFolderId: null, name: "Folder 1", description: "Description Folder One", owner: 3, @@ -49,8 +49,8 @@ qx.Class.define("osparc.store.Folders", { } }, }, { - id: 2, - parentFolder: null, + folderId: 2, + parentFolderId: null, name: "Folder 2", description: "Description Folder Two", owner: 3, @@ -74,8 +74,8 @@ qx.Class.define("osparc.store.Folders", { } }, }, { - id: 3, - parentFolder: 1, + folderId: 3, + parentFolderId: 1, name: "Folder 3", description: "Description Folder Three", owner: 3, @@ -94,8 +94,8 @@ qx.Class.define("osparc.store.Folders", { } } }, { - id: 4, - parentFolder: null, + folderId: 4, + parentFolderId: null, name: "Folder 4", description: "Description Folder Four", owner: 3, @@ -119,8 +119,8 @@ qx.Class.define("osparc.store.Folders", { } } }, { - id: 5, - parentFolder: null, + folderId: 5, + parentFolderId: null, name: "Folder 5", description: "Description Folder Five", owner: 3, @@ -152,7 +152,7 @@ qx.Class.define("osparc.store.Folders", { fetchFolders: function(parentId = null) { return new Promise(resolve => { this.self().FOLDER_DATA_INIT.forEach(folderData => { - if (folderData.parentFolder === parentId) { + if (folderData.parentFolderId === parentId) { const folder = new osparc.data.model.Folder(folderData); this.__addToCache(folder); } @@ -165,8 +165,8 @@ qx.Class.define("osparc.store.Folders", { return new Promise(resolve => { const myGroupId = osparc.auth.Data.getInstance().getGroupId(); const newFolderData = { - id: Math.floor(Math.random() * 1000), - parentFolder: parentId, + folderId: Math.floor(Math.random() * 1000), + parentFolderId: parentId, name: name, description: description || "", owner: myGroupId, From d19af2fb0152f33ef1b489bdd4a9f499dd5f94e6 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Thu, 8 Aug 2024 09:50:43 +0200 Subject: [PATCH 02/67] add new resources --- .../source/class/osparc/data/Resources.js | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/services/static-webserver/client/source/class/osparc/data/Resources.js b/services/static-webserver/client/source/class/osparc/data/Resources.js index 77973cd552d..609cd7aae88 100644 --- a/services/static-webserver/client/source/class/osparc/data/Resources.js +++ b/services/static-webserver/client/source/class/osparc/data/Resources.js @@ -276,6 +276,10 @@ qx.Class.define("osparc.data.Resources", { useCache: false, method: "GET", url: statics.API + "/projects/{studyId}/inactivity" + }, + moveToFolder: { + method: "PUT", + url: statics.API + "/projects/{studyId}/folders/{folderId}" } } }, @@ -293,6 +297,28 @@ qx.Class.define("osparc.data.Resources", { } } }, + "folders": { + useCache: true, + idField: "uuid", + endpoints: { + getPage: { + method: "GET", + url: statics.API + "/folders?offset={offset}&limit={limit}" + }, + post: { + method: "POST", + url: statics.API + "/folders" + }, + update: { + method: "PUT", + url: statics.API + "/folders/{folderId}" + }, + delete: { + method: "DELETE", + url: statics.API + "/folders/{folderId}" + } + } + }, "resourceUsage": { useCache: false, endpoints: { From 9bfbc2d230916a4ba9f3196c1d52a010ade8400b Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Thu, 8 Aug 2024 09:54:46 +0200 Subject: [PATCH 03/67] folders in main Store --- .../client/source/class/osparc/store/Store.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/static-webserver/client/source/class/osparc/store/Store.js b/services/static-webserver/client/source/class/osparc/store/Store.js index 4760436b0de..b24aa4735af 100644 --- a/services/static-webserver/client/source/class/osparc/store/Store.js +++ b/services/static-webserver/client/source/class/osparc/store/Store.js @@ -70,6 +70,10 @@ qx.Class.define("osparc.store.Store", { check: "Array", init: [] }, + folders: { + check: "Array", + init: [] + }, studyComments: { check: "Array", init: [] From 9a3b549a3aa804f3b58745dd26df59bda1a48f09 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Thu, 8 Aug 2024 10:40:03 +0200 Subject: [PATCH 04/67] list folders --- .../class/osparc/dashboard/StudyBrowser.js | 3 +- .../source/class/osparc/data/Resources.js | 10 +++++- .../source/class/osparc/store/Folders.js | 33 +++++++++++++------ 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 53055b72334..64d0e677c3d 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -184,7 +184,8 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { if (nStudies === 0) { Promise.all([ osparc.store.Store.getInstance().getTemplates(), - osparc.service.Store.getServicesLatest() + osparc.service.Store.getServicesLatest(), + osparc.store.Folders.getInstance().fetchFolders(), ]).then(values => { const templates = values[0]; const services = values[1]; diff --git a/services/static-webserver/client/source/class/osparc/data/Resources.js b/services/static-webserver/client/source/class/osparc/data/Resources.js index 609cd7aae88..abf2218a35d 100644 --- a/services/static-webserver/client/source/class/osparc/data/Resources.js +++ b/services/static-webserver/client/source/class/osparc/data/Resources.js @@ -303,7 +303,15 @@ qx.Class.define("osparc.data.Resources", { endpoints: { getPage: { method: "GET", - url: statics.API + "/folders?offset={offset}&limit={limit}" + url: statics.API + "/folders?folder_id={folderId}&offset={offset}&limit={limit}" + }, + getRootFolders: { + method: "GET", + url: statics.API + "/folders" + }, + getWithinFolder: { + method: "GET", + url: statics.API + "/folders?folder_id={folderId}" }, post: { method: "POST", diff --git a/services/static-webserver/client/source/class/osparc/store/Folders.js b/services/static-webserver/client/source/class/osparc/store/Folders.js index 04c789bd6f3..6cda3cc4aa5 100644 --- a/services/static-webserver/client/source/class/osparc/store/Folders.js +++ b/services/static-webserver/client/source/class/osparc/store/Folders.js @@ -23,8 +23,6 @@ qx.Class.define("osparc.store.Folders", { this.base(arguments); this.foldersCached = []; - - this.fetchFolders(); }, statics: { @@ -149,15 +147,30 @@ qx.Class.define("osparc.store.Folders", { members: { foldersCached: null, - fetchFolders: function(parentId = null) { + fetchFolders: function(folderId = null) { return new Promise(resolve => { - this.self().FOLDER_DATA_INIT.forEach(folderData => { - if (folderData.parentFolderId === parentId) { - const folder = new osparc.data.model.Folder(folderData); - this.__addToCache(folder); - } - }); - resolve(); + let promise = null; + if (folderId) { + const params = { + "url": { + folderId + } + }; + // osparc.data.Resources.getInstance().getAllPages("folders", params) + promise = osparc.data.Resources.getInstance().fetch("folders", "getWithinFolder", params); + } else { + promise = osparc.data.Resources.getInstance().fetch("folders", "getRootFolders"); + } + promise + .then(foldersData => { + foldersData.forEach(folderData => { + if (folderData.parentFolderId === folderId) { + const folder = new osparc.data.model.Folder(folderData); + this.__addToCache(folder); + } + }); + resolve(); + }) }); }, From 0727bb7db90d48d82ed3907badb6b614fc7b2d90 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Thu, 8 Aug 2024 11:31:58 +0200 Subject: [PATCH 05/67] working post --- .../osparc/dashboard/FolderButtonItem.js | 2 +- .../class/osparc/dashboard/FolderHeader.js | 2 +- .../class/osparc/dashboard/StudyBrowser.js | 2 +- .../source/class/osparc/data/Resources.js | 4 ++ .../source/class/osparc/data/model/Folder.js | 6 +-- .../class/osparc/share/CollaboratorsFolder.js | 6 +-- .../source/class/osparc/store/Folders.js | 42 ++++++++----------- 7 files changed, 30 insertions(+), 34 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js index fe2b5f4171d..85bee76d51c 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js @@ -148,7 +148,7 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { __applyFolder: function(folder) { this.getChildControl("icon"); this.set({ - cardKey: "folder-" + folder.getId() + cardKey: "folder-" + folder.getFolderId() }); folder.bind("folderId", this, "folderId"); folder.bind("parentId", this, "parentFolderId"); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderHeader.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderHeader.js index 7463c668649..bd4582fd0fc 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderHeader.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderHeader.js @@ -117,7 +117,7 @@ qx.Class.define("osparc.dashboard.FolderHeader", { } else { folderButton = new qx.ui.form.Button(this.tr("Home"), "@FontAwesome5Solid/home/14"); } - folderButton.addListener("execute", () => this.fireDataEvent("changeCurrentFolderId", folder ? folder.getId() : null), this); + folderButton.addListener("execute", () => this.fireDataEvent("changeCurrentFolderId", folder ? folder.getFolderId() : null), this); folderButton.set({ backgroundColor: "transparent", textColor: "text", diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 64d0e677c3d..4f7a88c6422 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -375,7 +375,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { newFolderCard.subscribeToFilterGroup("searchBarFilter"); newFolderCard.addListener("createFolder", e => { const data = e.getData(); - osparc.store.Folders.getInstance().postFolder(data.name, data.description, currentFolder ? currentFolder.getId() : null) + osparc.store.Folders.getInstance().postFolder(data.name, data.description, currentFolder ? currentFolder.getFolderId() : null) .then(() => this.__reloadFolders()) .catch(err => console.error(err)); }) diff --git a/services/static-webserver/client/source/class/osparc/data/Resources.js b/services/static-webserver/client/source/class/osparc/data/Resources.js index abf2218a35d..0a01431e577 100644 --- a/services/static-webserver/client/source/class/osparc/data/Resources.js +++ b/services/static-webserver/client/source/class/osparc/data/Resources.js @@ -313,6 +313,10 @@ qx.Class.define("osparc.data.Resources", { method: "GET", url: statics.API + "/folders?folder_id={folderId}" }, + getOne: { + method: "GET", + url: statics.API + "/folders/{folderId}" + }, post: { method: "POST", url: statics.API + "/folders" diff --git a/services/static-webserver/client/source/class/osparc/data/model/Folder.js b/services/static-webserver/client/source/class/osparc/data/model/Folder.js index c7d3d012030..6386b3e61bf 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Folder.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Folder.js @@ -29,19 +29,19 @@ qx.Class.define("osparc.data.model.Folder", { this.base(arguments); this.set({ - id: folderData.id, + folderId: folderData.folderId, parentId: folderData.parentFolderId, name: folderData.name, description: folderData.description, myAccessRights: folderData.myAccessRights, accessRights: folderData.accessRights, createdAt: new Date(folderData.createdAt), - lastModified: new Date(folderData.lastModified), + lastModified: new Date(folderData.modifiedAt), }); }, properties: { - id: { + folderId: { check: "Number", nullable: false, init: null, diff --git a/services/static-webserver/client/source/class/osparc/share/CollaboratorsFolder.js b/services/static-webserver/client/source/class/osparc/share/CollaboratorsFolder.js index cc0b7ce936a..d68df6a9dde 100644 --- a/services/static-webserver/client/source/class/osparc/share/CollaboratorsFolder.js +++ b/services/static-webserver/client/source/class/osparc/share/CollaboratorsFolder.js @@ -70,7 +70,7 @@ qx.Class.define("osparc.share.CollaboratorsFolder", { const newCollaborators = {}; gids.forEach(gid => newCollaborators[gid] = this.self().getCollaboratorAccessRight()); - osparc.store.Folders.getInstance().addCollaborators(this.__folder.getId(), newCollaborators) + osparc.store.Folders.getInstance().addCollaborators(this.__folder.getFolderId(), newCollaborators) .then(() => { this.fireDataEvent("updateAccessRights", this.__folder.serialize()); const text = this.tr("User(s) successfully added."); @@ -88,7 +88,7 @@ qx.Class.define("osparc.share.CollaboratorsFolder", { item.setEnabled(false); } - osparc.store.Folders.getInstance().removeCollaborator(this.__folder.getId(), collaborator["gid"]) + osparc.store.Folders.getInstance().removeCollaborator(this.__folder.getFolderId(), collaborator["gid"]) .then(() => { this.fireDataEvent("updateAccessRights", this.__folder.serialize()); osparc.FlashMessenger.getInstance().logAs(this.tr("Member successfully removed")); @@ -108,7 +108,7 @@ qx.Class.define("osparc.share.CollaboratorsFolder", { __make: function(collaboratorGId, newAccessRights, successMsg, failureMsg, item) { item.setEnabled(false); - osparc.store.Folders.getInstance().updateCollaborator(this.__folder.getId(), collaboratorGId, newAccessRights) + osparc.store.Folders.getInstance().updateCollaborator(this.__folder.getFolderId(), collaboratorGId, newAccessRights) .then(() => { this.fireDataEvent("updateAccessRights", this.__folder.serialize()); osparc.FlashMessenger.getInstance().logAs(successMsg); diff --git a/services/static-webserver/client/source/class/osparc/store/Folders.js b/services/static-webserver/client/source/class/osparc/store/Folders.js index 6cda3cc4aa5..2ae22e668c8 100644 --- a/services/static-webserver/client/source/class/osparc/store/Folders.js +++ b/services/static-webserver/client/source/class/osparc/store/Folders.js @@ -164,10 +164,8 @@ qx.Class.define("osparc.store.Folders", { promise .then(foldersData => { foldersData.forEach(folderData => { - if (folderData.parentFolderId === folderId) { - const folder = new osparc.data.model.Folder(folderData); - this.__addToCache(folder); - } + const folder = new osparc.data.model.Folder(folderData); + this.__addToCache(folder); }); resolve(); }) @@ -176,36 +174,30 @@ qx.Class.define("osparc.store.Folders", { postFolder: function(name, description, parentId = null) { return new Promise(resolve => { - const myGroupId = osparc.auth.Data.getInstance().getGroupId(); const newFolderData = { - folderId: Math.floor(Math.random() * 1000), parentFolderId: parentId, name: name, description: description || "", - owner: myGroupId, - createdAt: new Date().toString(), - lastModified: new Date().toString(), - myAccessRights: { - read: true, - write: true, - delete: true - }, - accessRights: {}, }; - newFolderData["accessRights"][myGroupId] = { - read: true, - write: true, - delete: true + const params = { + data: newFolderData }; - const newFolder = new osparc.data.model.Folder(newFolderData); - this.__addToCache(newFolder); - resolve(newFolder) + osparc.data.Resources.getInstance().fetch("folders", "post", params) + .then(resp => { + const foldersStore = osparc.store.Folders.getInstance(); + const folderId = resp["folderId"]; + foldersStore.fetchFolders(parentId) + .then(() => { + const newFolder = foldersStore.getFolder(folderId); + resolve(newFolder); + }); + }); }); }, deleteFolder: function(folderId) { return new Promise((resolve, reject) => { - const idx = this.foldersCached.findIndex(f => f.getId() === folderId); + const idx = this.foldersCached.findIndex(f => f.getFolderId() === folderId); if (idx > -1) { this.foldersCached.splice(idx, 1); resolve(); @@ -288,11 +280,11 @@ qx.Class.define("osparc.store.Folders", { }, getFolder: function(folderId = null) { - return this.foldersCached.find(f => f.getId() === folderId); + return this.foldersCached.find(f => f.getFolderId() === folderId); }, __addToCache: function(folder) { - const found = this.foldersCached.find(f => f.getId() === folder.getId()); + const found = this.foldersCached.find(f => f.getFolderId() === folder.getFolderId()); if (!found) { this.foldersCached.push(folder); } From cf3af7f3f87fd4f9ff5607170266040db727890c Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Thu, 8 Aug 2024 11:34:49 +0200 Subject: [PATCH 06/67] not needed --- .../source/class/osparc/store/Folders.js | 119 ------------------ 1 file changed, 119 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/store/Folders.js b/services/static-webserver/client/source/class/osparc/store/Folders.js index 2ae22e668c8..5237cddc516 100644 --- a/services/static-webserver/client/source/class/osparc/store/Folders.js +++ b/services/static-webserver/client/source/class/osparc/store/Folders.js @@ -25,125 +25,6 @@ qx.Class.define("osparc.store.Folders", { this.foldersCached = []; }, - statics: { - FOLDER_DATA_INIT: [{ - folderId: 1, - parentFolderId: null, - name: "Folder 1", - description: "Description Folder One", - owner: 3, - createdAt: "2024-07-11T06:28:28.527Z", - lastModified: "2024-07-13T06:28:28.527Z", - myAccessRights: { - read: true, - write: true, - delete: true - }, - accessRights: { - 3: { - read: true, - write: true, - delete: true - } - }, - }, { - folderId: 2, - parentFolderId: null, - name: "Folder 2", - description: "Description Folder Two", - owner: 3, - createdAt: "2024-07-13T06:28:28.527Z", - lastModified: "2024-07-15T06:28:28.527Z", - myAccessRights: { - read: true, - write: true, - delete: true - }, - accessRights: { - 3: { - read: true, - write: true, - delete: true - }, - 9: { - read: true, - write: true, - delete: false - } - }, - }, { - folderId: 3, - parentFolderId: 1, - name: "Folder 3", - description: "Description Folder Three", - owner: 3, - createdAt: "2024-07-16T06:28:28.527Z", - lastModified: "2024-07-17T06:28:28.527Z", - myAccessRights: { - read: true, - write: true, - delete: true - }, - accessRights: { - 3: { - read: true, - write: true, - delete: true - } - } - }, { - folderId: 4, - parentFolderId: null, - name: "Folder 4", - description: "Description Folder Four", - owner: 3, - createdAt: "2024-07-17T06:28:28.527Z", - lastModified: "2024-07-18T06:28:28.527Z", - myAccessRights: { - read: true, - write: true, - delete: false - }, - accessRights: { - 3: { - read: true, - write: true, - delete: false - }, - 9: { - read: true, - write: true, - delete: true - } - } - }, { - folderId: 5, - parentFolderId: null, - name: "Folder 5", - description: "Description Folder Five", - owner: 3, - createdAt: "2024-07-18T06:28:28.527Z", - lastModified: "2024-07-18T07:28:28.527Z", - myAccessRights: { - read: true, - write: false, - delete: false - }, - accessRights: { - 3: { - read: true, - write: false, - delete: false - }, - 9: { - read: true, - write: true, - delete: true - } - }, - }] - }, - members: { foldersCached: null, From f2f16e9be765c6d88d959c918f9cd11cc27efc43 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Thu, 8 Aug 2024 11:42:59 +0200 Subject: [PATCH 07/67] delete working --- .../source/class/osparc/store/Folders.js | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/store/Folders.js b/services/static-webserver/client/source/class/osparc/store/Folders.js index 5237cddc516..5d7f7932198 100644 --- a/services/static-webserver/client/source/class/osparc/store/Folders.js +++ b/services/static-webserver/client/source/class/osparc/store/Folders.js @@ -78,13 +78,22 @@ qx.Class.define("osparc.store.Folders", { deleteFolder: function(folderId) { return new Promise((resolve, reject) => { - const idx = this.foldersCached.findIndex(f => f.getFolderId() === folderId); - if (idx > -1) { - this.foldersCached.splice(idx, 1); - resolve(); - } else { - reject(); - } + const params = { + "url": { + folderId + } + }; + osparc.data.Resources.getInstance().fetch("folders", "delete", params) + .then(() => { + const idx = this.foldersCached.findIndex(f => f.getFolderId() === folderId); + if (idx > -1) { + this.foldersCached.splice(idx, 1); + resolve(); + } else { + reject(); + } + }) + .catch(err => reject(err)); }); }, From 2c0383ef38ae869bb80d2a09d4f85115b88ebcb8 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Thu, 8 Aug 2024 12:00:44 +0200 Subject: [PATCH 08/67] update folder --- .../osparc/dashboard/FolderButtonItem.js | 13 +++--- .../source/class/osparc/data/model/Folder.js | 4 +- .../source/class/osparc/store/Folders.js | 45 +++++++++++++------ 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js index 85bee76d51c..3622078bb6b 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js @@ -198,14 +198,11 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { folderEditor.addListener("updateFolder", () => { const newName = folderEditor.getLabel(); const newDescription = folderEditor.getDescription(); - const promises = []; - if (newName !== folder.getName()) { - promises.push(osparc.data.model.Folder.patchFolder(this.getFolderId(), "name", newName)); - } - if (newDescription !== folder.getDescription()) { - promises.push(osparc.data.model.Folder.patchFolder(this.getFolderId(), "description", newDescription)); - } - Promise.all(promises) + const updateData = { + "name": newName, + "description": newDescription + }; + osparc.data.model.Folder.putFolder(this.getFolderId(), updateData) .then(() => folder.set({ name: newName, description: newDescription diff --git a/services/static-webserver/client/source/class/osparc/data/model/Folder.js b/services/static-webserver/client/source/class/osparc/data/model/Folder.js index 6386b3e61bf..2d66252814b 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Folder.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Folder.js @@ -99,8 +99,8 @@ qx.Class.define("osparc.data.model.Folder", { }, statics: { - patchFolder: function(folderId, propKey, value) { - return osparc.store.Folders.getInstance().patchFolder(folderId, propKey, value); + putFolder: function(folderId, propKey, value) { + return osparc.store.Folders.getInstance().putFolder(folderId, propKey, value); }, getProperties: function() { diff --git a/services/static-webserver/client/source/class/osparc/store/Folders.js b/services/static-webserver/client/source/class/osparc/store/Folders.js index 5d7f7932198..720bdd764cd 100644 --- a/services/static-webserver/client/source/class/osparc/store/Folders.js +++ b/services/static-webserver/client/source/class/osparc/store/Folders.js @@ -85,9 +85,7 @@ qx.Class.define("osparc.store.Folders", { }; osparc.data.Resources.getInstance().fetch("folders", "delete", params) .then(() => { - const idx = this.foldersCached.findIndex(f => f.getFolderId() === folderId); - if (idx > -1) { - this.foldersCached.splice(idx, 1); + if (this.__deleteFromCache(folderId)) { resolve(); } else { reject(); @@ -97,18 +95,28 @@ qx.Class.define("osparc.store.Folders", { }); }, - patchFolder: function(folderId, propKey, value) { + putFolder: function(folderId, updateData) { return new Promise((resolve, reject) => { - const folder = this.getFolder(folderId); - const upKey = qx.lang.String.firstUp(propKey); - const setter = "set" + upKey; - if (folder && setter in folder) { - folder[setter](value); - folder.setLastModified(new Date()); - resolve(); - } else { - reject(); - } + const params = { + "url": { + folderId + }, + data: updateData + }; + osparc.data.Resources.getInstance().fetch("folders", "update", params) + .then(() => { + const folder = this.getFolder(folderId); + Object.keys(updateData).forEach(propKey => { + const upKey = qx.lang.String.firstUp(propKey); + const setter = "set" + upKey; + if (folder && setter in folder) { + folder[setter](updateData[propKey]); + } + }); + folder.setLastModified(new Date()); + resolve(); + }) + .catch(err => reject(err)); }); }, @@ -178,6 +186,15 @@ qx.Class.define("osparc.store.Folders", { if (!found) { this.foldersCached.push(folder); } + }, + + __deleteFromCache: function(folderId) { + const idx = this.foldersCached.findIndex(f => f.getFolderId() === folderId); + if (idx > -1) { + this.foldersCached.splice(idx, 1); + return true; + } + return false; } } }); From 1025deece081f9fe8d5f6e96cc01fb8d27f274da Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Thu, 8 Aug 2024 12:38:40 +0200 Subject: [PATCH 09/67] reload after put --- .../class/osparc/dashboard/FolderButtonItem.js | 12 ++++++++---- .../class/osparc/dashboard/ResourceBrowserBase.js | 5 +++++ .../osparc/dashboard/ResourceContainerManager.js | 2 ++ .../source/class/osparc/dashboard/StudyBrowser.js | 4 ++++ .../client/source/class/osparc/store/Folders.js | 4 +++- 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js index 3622078bb6b..242f6ef620c 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js @@ -44,6 +44,7 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { events: { "folderSelected": "qx.event.type.Data", + "folderUpdated": "qx.event.type.Data", "deleteFolderRequested": "qx.event.type.Data" }, @@ -203,10 +204,13 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { "description": newDescription }; osparc.data.model.Folder.putFolder(this.getFolderId(), updateData) - .then(() => folder.set({ - name: newName, - description: newDescription - })) + .then(() => { + folder.set({ + name: newName, + description: newDescription + }); + this.fireDataEvent("folderUpdated", folder.getFolderId()); + }) .catch(err => console.error(err)); win.close(); }); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js index fc253808cf3..d1219cec8e8 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js @@ -252,6 +252,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { resourcesContainer.addListener("tagClicked", e => this._searchBarFilter.addTagActiveFilter(e.getData())); resourcesContainer.addListener("emptyStudyClicked", e => this._deleteResourceRequested(e.getData())); resourcesContainer.addListener("folderSelected", e => this._folderSelected(e.getData())); + resourcesContainer.addListener("folderUpdated", e => this._folderUpdated(e.getData())); resourcesContainer.addListener("deleteFolderRequested", e => this._deleteFolderRequested(e.getData())); this._addToLayout(resourcesContainer); }, @@ -451,6 +452,10 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { throw new Error("Abstract method called!"); }, + _folderUpdated: function(folderId) { + throw new Error("Abstract method called!"); + }, + _deleteFolderRequested: function(folderId) { throw new Error("Abstract method called!"); }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js index 2b66627aae6..0084a07f4b2 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -77,6 +77,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { "changeSelection": "qx.event.type.Data", "changeVisibility": "qx.event.type.Data", "folderSelected": "qx.event.type.Data", + "folderUpdated": "qx.event.type.Data", "deleteFolderRequested": "qx.event.type.Data", }, @@ -239,6 +240,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { card.subscribeToFilterGroup("searchBarFilter"); [ "folderSelected", + "folderUpdated", "deleteFolderRequested", ].forEach(eName => card.addListener(eName, e => this.fireDataEvent(eName, e.getData()))); return card; diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 4f7a88c6422..529c98c0d71 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -392,6 +392,10 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { .then(() => this.__reloadFolders()); }, + _folderUpdated: function() { + this.__reloadFolders(); + }, + _deleteFolderRequested: function(folderId) { osparc.store.Folders.getInstance().deleteFolder(folderId) .then(() => this.__reloadFolders()) diff --git a/services/static-webserver/client/source/class/osparc/store/Folders.js b/services/static-webserver/client/source/class/osparc/store/Folders.js index 720bdd764cd..08dd78730c1 100644 --- a/services/static-webserver/client/source/class/osparc/store/Folders.js +++ b/services/static-webserver/client/source/class/osparc/store/Folders.js @@ -114,6 +114,8 @@ qx.Class.define("osparc.store.Folders", { } }); folder.setLastModified(new Date()); + this.__deleteFromCache(folderId); + this.__addToCache(folder); resolve(); }) .catch(err => reject(err)); @@ -184,7 +186,7 @@ qx.Class.define("osparc.store.Folders", { __addToCache: function(folder) { const found = this.foldersCached.find(f => f.getFolderId() === folder.getFolderId()); if (!found) { - this.foldersCached.push(folder); + this.foldersCached.unshift(folder); } }, From d729b90c008b811adb6a5bf0c85fd3fe6a8d0855 Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Thu, 8 Aug 2024 12:41:19 +0200 Subject: [PATCH 10/67] make folders optional --- .../application_settings_utils.py | 2 ++ .../folders/_folders_handlers.py | 7 +++++++ .../projects/_crud_api_read.py | 3 +++ .../projects/_folders_handlers.py | 5 +++++ .../simcore_service_webserver/projects/db.py | 18 +++++++++++++----- 5 files changed, 30 insertions(+), 5 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/application_settings_utils.py b/services/web/server/src/simcore_service_webserver/application_settings_utils.py index b962328bbcc..92ac39342e0 100644 --- a/services/web/server/src/simcore_service_webserver/application_settings_utils.py +++ b/services/web/server/src/simcore_service_webserver/application_settings_utils.py @@ -180,6 +180,7 @@ def convert_to_app_config(app_settings: ApplicationSettings) -> dict[str, Any]: "users": {"enabled": app_settings.WEBSERVER_USERS is not None}, "version_control": {"enabled": app_settings.WEBSERVER_VERSION_CONTROL}, "wallets": {"enabled": app_settings.WEBSERVER_WALLETS}, + "folders": {"enabled": app_settings.WEBSERVER_FOLDERS}, } @@ -323,6 +324,7 @@ def _set_if_disabled(field_name, section): "WEBSERVER_USERS", "WEBSERVER_VERSION_CONTROL", "WEBSERVER_WALLETS", + "WEBSERVER_FOLDERS", ): section_name = settings_name.replace("WEBSERVER_", "").lower() if section := cfg.get(section_name): diff --git a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py index 43ab014d442..e984ffc1d44 100644 --- a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py +++ b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py @@ -90,6 +90,13 @@ def validate_order_by_field(cls, v): class Config: extra = Extra.forbid + @validator("folder_id", pre=True, always=True) + @classmethod + def convert_null_to_none(cls, v): + if v is None or v == "null" or v == "none": + return None + return v + @routes.post(f"/{VTAG}/folders", name="create_folder") @login_required diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py b/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py index 508c1fa9cc2..44df76765aa 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_api_read.py @@ -16,6 +16,7 @@ from servicelib.utils import logged_gather from simcore_postgres_database.webserver_models import ProjectType as ProjectTypeDB +from ..application_settings import get_application_settings from ..catalog.client import get_services_for_user_in_product from . import projects_api from ._permalink_api import update_or_pop_permalink_in_project @@ -59,6 +60,7 @@ async def list_projects( folder_id: FolderID | None, ) -> tuple[list[ProjectDict], int]: app = request.app + settings = get_application_settings(app) db = ProjectDBAPI.get_from_app_context(app) user_available_services: list[dict] = await get_services_for_user_in_product( @@ -68,6 +70,7 @@ async def list_projects( db_projects, db_project_types, total_number_projects = await db.list_projects( user_id=user_id, product_name=product_name, + settings=settings, filter_by_project_type=ProjectTypeAPI.to_project_type_db(project_type), filter_by_services=user_available_services, offset=offset, diff --git a/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py index 2a919184937..8e1cacf57eb 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py @@ -10,6 +10,7 @@ from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from .._meta import api_version_prefix as VTAG +from ..application_settings import get_application_settings from ..login.decorators import login_required from ..security.decorators import permission_required from . import _folders_api, projects_api @@ -63,6 +64,10 @@ async def replace_project_folder(request: web.Request): req_ctx = RequestContext.parse_obj(request) path_params = parse_request_path_parameters_as(_ProjectsFoldersPathParams, request) + settings = get_application_settings(request.app) + if not settings.WEBSERVER_FOLDERS: + raise RuntimeError("Webserver folders plugin disabled") + # ensure the project exists await projects_api.get_project_for_user( request.app, diff --git a/services/web/server/src/simcore_service_webserver/projects/db.py b/services/web/server/src/simcore_service_webserver/projects/db.py index e0b389b87e4..3b2b6d9d3f0 100644 --- a/services/web/server/src/simcore_service_webserver/projects/db.py +++ b/services/web/server/src/simcore_service_webserver/projects/db.py @@ -57,6 +57,7 @@ from tenacity.asyncio import AsyncRetrying from tenacity.retry import retry_if_exception_type +from ..application_settings import ApplicationSettings from ..db.models import projects_to_wallet, study_tags from ..utils import now_str from ._comments_db import ( @@ -322,6 +323,7 @@ async def list_projects( # pylint: disable=too-many-arguments user_id: PositiveInt, *, product_name: str, + settings: ApplicationSettings, filter_by_project_type: ProjectType | None = None, filter_by_services: list[dict] | None = None, only_published: bool | None = False, @@ -368,6 +370,10 @@ async def list_projects( # pylint: disable=too-many-arguments projects.join(projects_to_products, isouter=True) .join(access_rights_subquery, isouter=True) .join(folders_to_projects, isouter=True) + if settings.WEBSERVER_FOLDERS + else projects.join(projects_to_products, isouter=True).join( + access_rights_subquery, isouter=True + ) ) .where( ( @@ -396,13 +402,15 @@ async def list_projects( # pylint: disable=too-many-arguments # This was added for backward compatibility, including old projects not in the projects_to_products table. | (projects_to_products.c.product_name.is_(None)) ) - & ( - (folders_to_projects.c.folder_id == f"{folder_id}") - if folder_id - else (folders_to_projects.c.folder_id.is_(None)) - ) ) ) + if settings.WEBSERVER_FOLDERS: + query = query.where( + folders_to_projects.c.folder_id == f"{folder_id}" + if folder_id + else folders_to_projects.c.folder_id.is_(None) + ) + if search: query = query.join(users, isouter=True) query = query.where( From dc27a0ad41e1dfa10d43859fff0b22f39a7a6980 Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Thu, 8 Aug 2024 14:30:26 +0200 Subject: [PATCH 11/67] review @sanderegg --- .../src/simcore_service_webserver/folders/_folders_handlers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py index e984ffc1d44..d2097660010 100644 --- a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py +++ b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py @@ -93,7 +93,7 @@ class Config: @validator("folder_id", pre=True, always=True) @classmethod def convert_null_to_none(cls, v): - if v is None or v == "null" or v == "none": + if v is None or v in ["null", "none"]: return None return v From 80138fa082ab41546f7654f12e6b564c3f13dba7 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Thu, 8 Aug 2024 16:48:24 +0200 Subject: [PATCH 12/67] separate calls for folders --- .../class/osparc/dashboard/StudyBrowser.js | 25 +++++++++++++++++++ .../source/class/osparc/data/Resources.js | 14 +++++++++++ 2 files changed, 39 insertions(+) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 529c98c0d71..c783be81160 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -497,6 +497,17 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { resolveWResponse: true }; + const currentFolderId = this.getCurrentFolderId(); + if (currentFolderId) { + params.url["folder_id"] = currentFolderId; + if (params.url.orderBy) { + return osparc.data.Resources.fetch("studies", "getPageSortBySearchFolder", params, undefined, options); + } else if (params.url.search) { + return osparc.data.Resources.fetch("studies", "getPageFilterSearchFolder", params, undefined, options); + } + return osparc.data.Resources.fetch("studies", "getPageFolder", params, undefined, options); + } + if (params.url.orderBy) { return osparc.data.Resources.fetch("studies", "getPageSortBySearch", params, undefined, options); } else if (params.url.search) { @@ -521,6 +532,13 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const options = { resolveWResponse: true }; + + const currentFolderId = this.getCurrentFolderId(); + if (currentFolderId) { + params.url["folder_id"] = currentFolderId; + return osparc.data.Resources.fetch("studies", "getPageFilterSearchFolder", params, undefined, options); + } + return osparc.data.Resources.fetch("studies", "getPageFilterSearch", params, undefined, options); }, @@ -540,6 +558,13 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const options = { resolveWResponse: true }; + + const currentFolderId = this.getCurrentFolderId(); + if (currentFolderId) { + params.url["folder_id"] = currentFolderId; + return osparc.data.Resources.fetch("studies", "getPageSortBySearchFolder", params, undefined, options); + } + return osparc.data.Resources.fetch("studies", "getPageSortBySearch", params, undefined, options); }, diff --git a/services/static-webserver/client/source/class/osparc/data/Resources.js b/services/static-webserver/client/source/class/osparc/data/Resources.js index 0a01431e577..1b84fafd8b8 100644 --- a/services/static-webserver/client/source/class/osparc/data/Resources.js +++ b/services/static-webserver/client/source/class/osparc/data/Resources.js @@ -133,6 +133,20 @@ qx.Class.define("osparc.data.Resources", { method: "GET", url: statics.API + "/projects?type=user&offset={offset}&limit={limit}&order_by={orderBy}" }, + getPageFolder: { + method: "GET", + url: statics.API + "/projects?type=user&offset={offset}&limit={limit}&folder_id={folderId}" + }, + getPageFilterSearchFolder: { + useCache: false, + method: "GET", + url: statics.API + "/projects?type=user&offset={offset}&limit={limit}&search={text}&folder_id={folderId}" + }, + getPageSortBySearchFolder: { + useCache: false, + method: "GET", + url: statics.API + "/projects?type=user&offset={offset}&limit={limit}&order_by={orderBy}&folder_id={folderId}" + }, getOne: { useCache: false, method: "GET", From 078d435b03cc047d3716b3169dbb19cf5d90a534 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Thu, 8 Aug 2024 22:17:02 +0200 Subject: [PATCH 13/67] minor --- .../source/class/osparc/desktop/credits/CreditsPerService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/desktop/credits/CreditsPerService.js b/services/static-webserver/client/source/class/osparc/desktop/credits/CreditsPerService.js index 7d63bcb4e00..546f2135217 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/credits/CreditsPerService.js +++ b/services/static-webserver/client/source/class/osparc/desktop/credits/CreditsPerService.js @@ -61,7 +61,7 @@ qx.Class.define("osparc.desktop.credits.CreditsPerService", { osparc.data.Resources.fetch("resourceUsage", "getUsagePerService", params) .then(entries => { this._removeAll(); - if (entries) { + if (entries && entries.length) { let totalCredits = 0; entries.forEach(entry => totalCredits+= entry["osparc_credits"]); let datas = []; From 933af0ecf42d13d8c3f5afa37d6d3f0bf036b121 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Thu, 8 Aug 2024 22:21:00 +0200 Subject: [PATCH 14/67] default one week --- .../source/class/osparc/desktop/credits/CreditsSummary.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/desktop/credits/CreditsSummary.js b/services/static-webserver/client/source/class/osparc/desktop/credits/CreditsSummary.js index 4bdecaeb69d..1d077749802 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/credits/CreditsSummary.js +++ b/services/static-webserver/client/source/class/osparc/desktop/credits/CreditsSummary.js @@ -103,7 +103,7 @@ qx.Class.define("osparc.desktop.credits.CreditsSummary", { topLayout.add(control); break; } - case "time-range-sb": + case "time-range-sb": { control = new qx.ui.form.SelectBox().set({ allowGrowX: false, alignX: "center", @@ -113,8 +113,14 @@ qx.Class.define("osparc.desktop.credits.CreditsSummary", { const trItem = new qx.ui.form.ListItem(tr.label, null, tr.key); control.add(trItem); }); + // default one week + const found = control.getSelectables().find(trItem => trItem.getModel() === 7); + if (found) { + control.setSelection([found]); + } this._add(control); break; + } case "services-consumption": control = new osparc.desktop.credits.CreditsPerService(); this._add(control); From dd18f2e9660494b26da8285187f5b2ac53caf9f1 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Thu, 8 Aug 2024 22:35:48 +0200 Subject: [PATCH 15/67] minor --- .../class/osparc/dashboard/StudyBrowser.js | 53 +++++++++---------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index c783be81160..795d8ca586f 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -921,7 +921,30 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const title = osparc.utils.Utils.getUniqueStudyName(minStudyData.name, this._resourcesList); minStudyData["name"] = title; minStudyData["description"] = ""; - this.__createStudy(minStudyData, null); + this._showLoadingPage(this.tr("Creating ") + (minStudyData.name || osparc.product.Utils.getStudyAlias())); + const params = { + data: minStudyData + }; + osparc.study.Utils.createStudyAndPoll(params) + .then(studyData => { + const openCB = () => this._hideLoadingPage(); + const cancelCB = () => { + this._hideLoadingPage(); + const params2 = { + url: { + "studyId": studyData["uuid"] + } + }; + osparc.data.Resources.fetch("studies", "delete", params2, studyData["uuid"]); + }; + const isStudyCreation = true; + this._startStudyById(studyData["uuid"], openCB, cancelCB, isStudyCreation); + }) + .catch(err => { + this._hideLoadingPage(); + osparc.FlashMessenger.getInstance().logAs(err.message, "ERROR"); + console.error(err); + }); }, __newPlanBtnClicked: function(templateData, newStudyName) { @@ -977,34 +1000,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }); }, - __createStudy: function(minStudyData) { - this._showLoadingPage(this.tr("Creating ") + (minStudyData.name || osparc.product.Utils.getStudyAlias())); - - const params = { - data: minStudyData - }; - osparc.study.Utils.createStudyAndPoll(params) - .then(studyData => { - const openCB = () => this._hideLoadingPage(); - const cancelCB = () => { - this._hideLoadingPage(); - const params2 = { - url: { - "studyId": studyData["uuid"] - } - }; - osparc.data.Resources.fetch("studies", "delete", params2, studyData["uuid"]); - }; - const isStudyCreation = true; - this._startStudyById(studyData["uuid"], openCB, cancelCB, isStudyCreation); - }) - .catch(err => { - this._hideLoadingPage(); - osparc.FlashMessenger.getInstance().logAs(err.message, "ERROR"); - console.error(err); - }); - }, - _updateStudyData: function(studyData) { studyData["resourceType"] = "study"; const studies = this._resourcesList; From 3dea9ef4c5c8877da1f689df5f27068cedb3b0e4 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Thu, 8 Aug 2024 22:43:13 +0200 Subject: [PATCH 16/67] minor --- .../client/source/class/osparc/dashboard/StudyBrowser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 795d8ca586f..79e385fa5a3 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -985,7 +985,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._hideLoadingPage(); const params = { url: { - "studyId": studyId + studyId } }; osparc.data.Resources.fetch("studies", "delete", params, studyId); From cf7b14c7d6a8daad2fe0897f597f13c177272592 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Thu, 8 Aug 2024 23:07:38 +0200 Subject: [PATCH 17/67] refactoring --- .../class/osparc/dashboard/StudyBrowser.js | 60 ++++++------------- 1 file changed, 18 insertions(+), 42 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 79e385fa5a3..5dd4063802a 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -926,20 +926,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { data: minStudyData }; osparc.study.Utils.createStudyAndPoll(params) - .then(studyData => { - const openCB = () => this._hideLoadingPage(); - const cancelCB = () => { - this._hideLoadingPage(); - const params2 = { - url: { - "studyId": studyData["uuid"] - } - }; - osparc.data.Resources.fetch("studies", "delete", params2, studyData["uuid"]); - }; - const isStudyCreation = true; - this._startStudyById(studyData["uuid"], openCB, cancelCB, isStudyCreation); - }) + .then(studyData => this.__startStudyAfterCreating(studyData["uuid"])) .catch(err => { this._hideLoadingPage(); osparc.FlashMessenger.getInstance().logAs(err.message, "ERROR"); @@ -954,20 +941,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { templateCopyData.name = title; this._showLoadingPage(this.tr("Creating ") + (newStudyName || osparc.product.Utils.getStudyAlias())); osparc.study.Utils.createStudyFromTemplate(templateCopyData, this._loadingPage) - .then(studyId => { - const openCB = () => this._hideLoadingPage(); - const cancelCB = () => { - this._hideLoadingPage(); - const params = { - url: { - "studyId": studyId - } - }; - osparc.data.Resources.fetch("studies", "delete", params, studyId); - }; - const isStudyCreation = true; - this._startStudyById(studyId, openCB, cancelCB, isStudyCreation); - }) + .then(studyId => this.__startStudyAfterCreating(studyId)) .catch(err => { this._hideLoadingPage(); osparc.FlashMessenger.getInstance().logAs(err.message, "ERROR"); @@ -979,20 +953,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { button.setValue(false); this._showLoadingPage(this.tr("Creating ") + osparc.product.Utils.getStudyAlias()); osparc.study.Utils.createStudyFromService(key, version, this._resourcesList, newStudyLabel) - .then(studyId => { - const openCB = () => this._hideLoadingPage(); - const cancelCB = () => { - this._hideLoadingPage(); - const params = { - url: { - studyId - } - }; - osparc.data.Resources.fetch("studies", "delete", params, studyId); - }; - const isStudyCreation = true; - this._startStudyById(studyId, openCB, cancelCB, isStudyCreation); - }) + .then(studyId => this.__startStudyAfterCreating(studyId)) .catch(err => { this._hideLoadingPage(); osparc.FlashMessenger.getInstance().logAs(err.message, "ERROR"); @@ -1000,6 +961,21 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }); }, + __startStudyAfterCreating: function(studyId) { + const openCB = () => this._hideLoadingPage(); + const cancelCB = () => { + this._hideLoadingPage(); + const params = { + url: { + studyId + } + }; + osparc.data.Resources.fetch("studies", "delete", params, studyId); + }; + const isStudyCreation = true; + this._startStudyById(studyId, openCB, cancelCB, isStudyCreation); + }, + _updateStudyData: function(studyData) { studyData["resourceType"] = "study"; const studies = this._resourcesList; From 1bcb8ed84a6cb04a280333062a5f5c979a11d404 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Thu, 8 Aug 2024 23:11:09 +0200 Subject: [PATCH 18/67] moveToFolder --- .../client/source/class/osparc/dashboard/StudyBrowser.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 5dd4063802a..56815b6d336 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -962,6 +962,15 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, __startStudyAfterCreating: function(studyId) { + if (this.getCurrentFolderId()) { + const params = { + url: { + studyId, + folderId: this.getCurrentFolderId() + } + } + osparc.data.Resources.fetch("studies", "moveToFolder", params); + } const openCB = () => this._hideLoadingPage(); const cancelCB = () => { this._hideLoadingPage(); From d2f57742838f65ce66ff2e61702c8449ab30dd45 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Thu, 8 Aug 2024 23:18:48 +0200 Subject: [PATCH 19/67] minor --- .../client/source/class/osparc/dashboard/StudyBrowser.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 56815b6d336..4b573df535f 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -499,7 +499,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const currentFolderId = this.getCurrentFolderId(); if (currentFolderId) { - params.url["folder_id"] = currentFolderId; + params.url.folderId = currentFolderId; if (params.url.orderBy) { return osparc.data.Resources.fetch("studies", "getPageSortBySearchFolder", params, undefined, options); } else if (params.url.search) { @@ -535,7 +535,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const currentFolderId = this.getCurrentFolderId(); if (currentFolderId) { - params.url["folder_id"] = currentFolderId; + params.url.folderId = currentFolderId; return osparc.data.Resources.fetch("studies", "getPageFilterSearchFolder", params, undefined, options); } @@ -561,7 +561,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const currentFolderId = this.getCurrentFolderId(); if (currentFolderId) { - params.url["folder_id"] = currentFolderId; + params.url.folderId = currentFolderId; return osparc.data.Resources.fetch("studies", "getPageSortBySearchFolder", params, undefined, options); } From 5ee7437732b0a011b73148f008540f18accc65fa Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Thu, 8 Aug 2024 23:41:14 +0200 Subject: [PATCH 20/67] minor --- .../client/source/class/osparc/dashboard/StudyBrowser.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 4b573df535f..145e771979f 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -389,7 +389,10 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { __applyCurrentFolderId: function(currentFolderId) { osparc.store.Folders.getInstance().fetchFolders(currentFolderId) - .then(() => this.__reloadFolders()); + .then(() => { + // this.__resetStudiesList(); + this.__reloadResources(); + }); }, _folderUpdated: function() { From fddf7c3338b98325a1239da4a22dc66f89b06c8d Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Fri, 9 Aug 2024 09:34:55 +0200 Subject: [PATCH 21/67] reset list in reloadResources --- .../client/source/class/osparc/dashboard/StudyBrowser.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 145e771979f..fcfebd13eb1 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -146,6 +146,8 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, __reloadResources: function() { + this._resourcesContainer.setResourcesToList([]); + this.__reloadFolders(); this.__reloadStudies(); }, @@ -389,10 +391,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { __applyCurrentFolderId: function(currentFolderId) { osparc.store.Folders.getInstance().fetchFolders(currentFolderId) - .then(() => { - // this.__resetStudiesList(); - this.__reloadResources(); - }); + .then(() => this.__reloadResources()); }, _folderUpdated: function() { From b9cca232e4cef170f073948895290a49a4708788 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Mon, 12 Aug 2024 08:27:15 +0200 Subject: [PATCH 22/67] reload resources after folder change --- .../source/class/osparc/dashboard/StudyBrowser.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index fcfebd13eb1..07ff3e4752c 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -146,8 +146,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, __reloadResources: function() { - this._resourcesContainer.setResourcesToList([]); - this.__reloadFolders(); this.__reloadStudies(); }, @@ -391,7 +389,13 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { __applyCurrentFolderId: function(currentFolderId) { osparc.store.Folders.getInstance().fetchFolders(currentFolderId) - .then(() => this.__reloadResources()); + .then(() => { + this._resourcesContainer.setResourcesToList([]); + this._resourcesList = []; + this.invalidateStudies(); + + this.__reloadResources(); + }); }, _folderUpdated: function() { From 5ac5a926d63b64c47568f7a77322ff75fd30ec78 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Mon, 12 Aug 2024 09:03:28 +0200 Subject: [PATCH 23/67] minor --- .../client/source/class/osparc/dashboard/StudyBrowser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 07ff3e4752c..56123cc5a9f 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -716,7 +716,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { _createLayout: function() { this._createResourcesLayout(); - const folderHeader = this._resourcesContainer.getFolderHeader() + const folderHeader = this._resourcesContainer.getFolderHeader(); if (folderHeader) { this.bind("currentFolderId", folderHeader, "currentFolderId"); folderHeader.addListener("changeCurrentFolderId", e => this.setCurrentFolderId(e.getData())); From eb87795a21c5333e286dcbd9d5bb9bceb55bb0ac Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Mon, 12 Aug 2024 12:45:36 +0200 Subject: [PATCH 24/67] navigate --- .../source/class/osparc/dashboard/ResourceContainerManager.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js index 0084a07f4b2..d4bf55b7c84 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -30,7 +30,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { this.__foldersList = []; this.__resourcesList = []; - const folders = new qx.ui.container.Composite(new qx.ui.layout.VBox(10)); + const folders = this.__foldersLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(10)); const folderHeader = this.__folderHeader = new osparc.dashboard.FolderHeader(); folders.add(folderHeader); const foldersContainer = this.__foldersContainer = new osparc.dashboard.ToggleButtonContainer(); @@ -104,6 +104,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { members: { __foldersList: null, __resourcesList: null, + __foldersLayout: null, __folderHeader: null, __foldersContainer: null, __nonGroupedContainer: null, @@ -262,6 +263,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { reloadCards: function(listId) { this.__cleanAll(); + this._add(this.__foldersLayout); if (this.getGroupBy()) { const noGroupContainer = this.__createGroupContainer("no-group", "No Group", "transparent"); this._add(noGroupContainer); From 4dfa215faecab07b159beea0005c53827e8250e7 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Mon, 12 Aug 2024 13:12:04 +0200 Subject: [PATCH 25/67] disable Share folder --- .../client/source/class/osparc/dashboard/FolderButtonItem.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js index 242f6ef620c..d23a952c53b 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js @@ -218,9 +218,12 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { }); menu.add(editButton); + /* + // disabled for now const shareButton = new qx.ui.menu.Button(this.tr("Share..."), "@FontAwesome5Solid/share-alt/12"); shareButton.addListener("execute", () => this.__openShareWith(), this); menu.add(shareButton); + */ menu.addSeparator(); From 02ad9cb761db6dcd84c840edd3a1253a6e2aa6eb Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Mon, 12 Aug 2024 13:18:27 +0200 Subject: [PATCH 26/67] disableShare --- .../class/osparc/dashboard/FolderButtonItem.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js index d23a952c53b..b3c1bec4da6 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js @@ -218,12 +218,9 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { }); menu.add(editButton); - /* - // disabled for now const shareButton = new qx.ui.menu.Button(this.tr("Share..."), "@FontAwesome5Solid/share-alt/12"); shareButton.addListener("execute", () => this.__openShareWith(), this); menu.add(shareButton); - */ menu.addSeparator(); @@ -264,10 +261,15 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { }, __openShareWith: function() { - const title = this.tr("Share Folder"); - const permissionsView = new osparc.share.CollaboratorsFolder(this.getFolder()); - osparc.ui.window.Window.popUpInWindow(permissionsView, title); - permissionsView.addListener("updateAccessRights", () => this.__applyAccessRights(this.getFolder().getAccessRights()), this); + const disableShare = true; + if (disableShare) { + osparc.FlashMessenger.getInstance().logAs(this.tr("Not yet implemented"), "WARNING"); + } else { + const title = this.tr("Share Folder"); + const permissionsView = new osparc.share.CollaboratorsFolder(this.getFolder()); + osparc.ui.window.Window.popUpInWindow(permissionsView, title); + permissionsView.addListener("updateAccessRights", () => this.__applyAccessRights(this.getFolder().getAccessRights()), this); + } }, __deleteStudyRequested: function() { From f2422f04c1efb18292ddd7b24509260e4499f82c Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Mon, 12 Aug 2024 13:26:00 +0200 Subject: [PATCH 27/67] isFoldersEnabled --- .../source/class/osparc/dashboard/ResourceContainerManager.js | 3 +-- .../client/source/class/osparc/utils/DisabledPlugins.js | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js index d4bf55b7c84..3cb0d1a1711 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -36,8 +36,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { const foldersContainer = this.__foldersContainer = new osparc.dashboard.ToggleButtonContainer(); folders.add(foldersContainer); this._add(folders); - // Only visible in master until it's connected to the backend - folders.setVisibility(osparc.utils.Utils.isDevelopmentPlatform() ? "visible" : "excluded"); + folders.setVisibility(osparc.utils.DisabledPlugins.isFoldersEnabled() ? "visible" : "excluded"); const nonGroupedContainer = this.__nonGroupedContainer = new osparc.dashboard.ToggleButtonContainer(); [ diff --git a/services/static-webserver/client/source/class/osparc/utils/DisabledPlugins.js b/services/static-webserver/client/source/class/osparc/utils/DisabledPlugins.js index b3e9f88583d..0b250d0d7db 100644 --- a/services/static-webserver/client/source/class/osparc/utils/DisabledPlugins.js +++ b/services/static-webserver/client/source/class/osparc/utils/DisabledPlugins.js @@ -30,6 +30,10 @@ qx.Class.define("osparc.utils.DisabledPlugins", { META_MODELING: "WEBSERVER_META_MODELING", CLUSTERS: "WEBSERVER_CLUSTERS", + isFoldersEnabled: function() { + return osparc.utils.Utils.isDevelopmentPlatform(); + }, + isExportDisabled: function() { return this.__isPluginDisabled(this.EXPORT); }, From df804c879372c0d7f47f20e51b4da3b645ed0d96 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Mon, 12 Aug 2024 15:40:07 +0200 Subject: [PATCH 28/67] Move To Folder Menu Button --- .../class/osparc/dashboard/FolderTreeItem.js | 32 +++++ .../class/osparc/dashboard/FoldersTree.js | 114 ++++++++++++++++++ .../osparc/dashboard/MoveStudyToFolder.js | 89 ++++++++++++++ .../class/osparc/dashboard/StudyBrowser.js | 30 +++++ 4 files changed, 265 insertions(+) create mode 100644 services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js create mode 100644 services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js create mode 100644 services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js new file mode 100644 index 00000000000..c1823c9bed4 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js @@ -0,0 +1,32 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2024 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.dashboard.FolderTreeItem", { + extend: qx.ui.tree.VirtualTreeItem, + + members: { + // overridden + _addWidgets: function() { + this.addIcon(); + this.addLabel(); + + this.addWidget(new qx.ui.core.Spacer(), { + flex: 1 + }); + } + } +}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js new file mode 100644 index 00000000000..72a9ec18c52 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js @@ -0,0 +1,114 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2024 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.dashboard.FoldersTree", { + extend: qx.ui.tree.VirtualTree, + + construct: function() { + this.base(arguments, null, "name", "children"); + + this.set({ + openMode: "none", + contentPadding: 0, + padding: 0, + decorator: "no-border", + font: "text-14" + }); + + this.__populateTree(); + }, + + events: { + "selectionChanged": "qx.event.type.Event" // tap + }, + + properties: { + currentFolderId: { + check: "Number", + nullable: true, + init: null, + event: "changeCurrentFolderId", + apply: "__applyCurrentFolderId" + } + }, + + statics: { + addLoadingChild: function(parent) { + const loadingModel = qx.data.marshal.Json.createModel({ + label: "Loading...", + location: null, + path: null, + children: [], + icon: "@FontAwesome5Solid/circle-notch/12" + }, true); + parent.getChildren().append(loadingModel); + }, + + removeLoadingChild: function(parent) { + for (let i = parent.getChildren().length - 1; i >= 0; i--) { + if (parent.getChildren().toArray()[i].getLabel() === "Loading...") { + parent.getChildren().toArray() + .splice(i, 1); + } + } + } + }, + + members: { + __populateTree: function() { + this.setDelegate({ + createItem: () => new osparc.dashboard.FolderTreeItem(), + bindItem: (c, item, id) => { + c.bindDefaultProperties(item, id); + c.bindProperty("folderId", "model", null, item, id); + c.bindProperty("name", "name", null, item, id); + }, + configureItem: item => { + item.addListener("tap", () => this.fireDataEvent("selectionChanged", item.getModel()), this); + + const openButton = item.getChildControl("open"); + openButton.addListener("tap", () => { + }); + }, + sorter: (a, b) => { + const aName = a.getName(); + if (aName === -1) { + return 1; + } + const bName = b.getName(); + if (bName === -1) { + return -1; + } + return aName - bName; + } + }); + + const rootFolder = { + folderId: null, + name: "Home", + children: [] + }; + const root = qx.data.marshal.Json.createModel(rootFolder, true); + this.setModel(root); + + this.self().addLoadingChild(this.getModel()); + }, + + __applyCurrentFolderId: function() { + } + } +}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js b/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js new file mode 100644 index 00000000000..a4968844626 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js @@ -0,0 +1,89 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2024 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.dashboard.MoveStudyToFolder", { + extend: qx.ui.core.Widget, + + construct: function(studyData, currentFolderId) { + this.base(arguments); + + this.__studyData = studyData; + this.__currentFolderId = currentFolderId; + + this._setLayout(new qx.ui.layout.VBox(10)); + + this.getChildControl("current-folder"); + this.getChildControl("folders-tree"); + this.getChildControl("cancel-btn"); + this.getChildControl("move-btn"); + }, + + events: { + "cancel": "qx.event.type.Event", + "moveToFolder": "qx.event.type.Data" + }, + + members: { + __studyData: null, + __currentFolderId: null, + + _createChildControlImpl: function(id) { + let control; + switch (id) { + case "current-folder": { + const folder = osparc.store.Folders.getFolder(this.__currentFolderId); + const currentFolderName = folder ? folder["name"] : "Home"; + control = new qx.ui.basic.Label(this.tr("Current location: ") + currentFolderName); + this._add(control); + break; + } + case "folders-tree": + control = new osparc.dashboard.FoldersTree(); + this._add(control); + break; + case "buttons-layout": + control = new qx.ui.container.Composite(new qx.ui.layout.HBox(10).set({ + alignX: "right" + })); + this._add(control); + break; + case "cancel-btn": { + const buttons = this.getChildControl("buttons-layout"); + control = new qx.ui.form.Button(this.tr("Cancel")).set({ + appearance: "form-button-text" + }); + control.addListener("execute", () => this.fireEvent("cancel"), this); + buttons.add(control); + break; + } + case "move-btn": { + const buttons = this.getChildControl("buttons-layout"); + control = new qx.ui.form.Button(this.tr("Move")).set({ + appearance: "form-button" + }); + control.addListener("execute", () => { + const folderId = this.getChildControl("url-field"); + this.fireDataEvent("moveToFolder", folderId); + }, this); + buttons.add(control); + break; + } + } + return control || this.base(arguments, id); + } + } +}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 56123cc5a9f..da11c043888 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -1049,7 +1049,9 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { if (shareButton) { menu.add(shareButton); } + } + if (writeAccess) { const tagsButton = this._getTagsMenuButton(card); if (tagsButton) { menu.add(tagsButton); @@ -1064,6 +1066,13 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { menu.add(billingsSettingsButton); } + if (writeAccess && osparc.utils.DisabledPlugins.isFoldersEnabled()) { + const moveToFolderButton = this.__getMoveToFolderMenuButton(studyData); + if (moveToFolderButton) { + menu.add(moveToFolderButton); + } + } + if (deleteAccess) { const deleteButton = this.__getDeleteStudyMenuButton(studyData, false); if (deleteButton) { @@ -1143,6 +1152,27 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { return studyBillingSettingsButton; }, + __getMoveToFolderMenuButton: function(studyData) { + const text = osparc.utils.Utils.capitalize(this.tr("Move to Folder...")); + const studyBillingSettingsButton = new qx.ui.menu.Button(text, "@FontAwesome5Solid/folder/12"); + studyBillingSettingsButton.addListener("tap", () => { + if (Object.keys(studyData["accessRights"]).length > 1) { + osparc.FlashMessenger.getInstance().logAs(this.tr("Shared projects can't be moved yet"), "WARNING"); + } else { + const title = this.tr("Move") + " " + studyData["name"]; + const moveStudyToFolder = new osparc.dashboard.MoveStudyToFolder(studyData, this.getCurrentFolderId()); + const win = osparc.ui.window.Window.popUpInWindow(moveStudyToFolder, title, 350, 280); + moveStudyToFolder.addListener("moveToFolder", e => { + win.close(); + const folderId = e.getData(); + this.__updateThumbnail(studyData, folderId); + }, this); + moveStudyToFolder.addListener("cancel", () => win.close()); + } + }, this); + return studyBillingSettingsButton; + }, + __getDuplicateMenuButton: function(studyData) { const duplicateButton = new qx.ui.menu.Button(this.tr("Duplicate"), "@FontAwesome5Solid/copy/12"); duplicateButton.addListener("execute", () => this.__duplicateStudy(studyData), this); From ade3b2993c0e8f0c2554e97ffdcfdd4abb75b591 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Mon, 12 Aug 2024 16:16:49 +0200 Subject: [PATCH 29/67] populate tree --- .../class/osparc/dashboard/FoldersTree.js | 27 ++++++++++++++++--- .../source/class/osparc/store/Folders.js | 4 ++- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js index 72a9ec18c52..e5002ee3bee 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js @@ -29,6 +29,7 @@ qx.Class.define("osparc.dashboard.FoldersTree", { font: "text-14" }); + this.__initTree(); this.__populateTree(); }, @@ -69,7 +70,7 @@ qx.Class.define("osparc.dashboard.FoldersTree", { }, members: { - __populateTree: function() { + __initTree: function() { this.setDelegate({ createItem: () => new osparc.dashboard.FolderTreeItem(), bindItem: (c, item, id) => { @@ -82,6 +83,7 @@ qx.Class.define("osparc.dashboard.FoldersTree", { const openButton = item.getChildControl("open"); openButton.addListener("tap", () => { + this.__fetchChildren(item); }); }, sorter: (a, b) => { @@ -96,16 +98,33 @@ qx.Class.define("osparc.dashboard.FoldersTree", { return aName - bName; } }); + }, + __populateTree: function() { const rootFolder = { folderId: null, name: "Home", children: [] }; - const root = qx.data.marshal.Json.createModel(rootFolder, true); - this.setModel(root); + const rootModel = qx.data.marshal.Json.createModel(rootFolder, true); + this.setModel(rootModel); - this.self().addLoadingChild(this.getModel()); + this.__fetchChildren(rootModel); + }, + + __fetchChildren: function(parentModel) { + this.self().addLoadingChild(parentModel); + + const folderId = parentModel.getModel(); + osparc.store.Folders.getInstance().fetchFolders(folderId) + .then(folders => { + this.self().removeLoadingChild(parentModel); + folders.forEach(folder => { + const folderData = folder.serialize(); + folderData["children"] = []; + parent.getChildren().append(qx.data.marshal.Json.createModel(folderData)); + }); + }); }, __applyCurrentFolderId: function() { diff --git a/services/static-webserver/client/source/class/osparc/store/Folders.js b/services/static-webserver/client/source/class/osparc/store/Folders.js index 08dd78730c1..2dfe0acfe0e 100644 --- a/services/static-webserver/client/source/class/osparc/store/Folders.js +++ b/services/static-webserver/client/source/class/osparc/store/Folders.js @@ -44,11 +44,13 @@ qx.Class.define("osparc.store.Folders", { } promise .then(foldersData => { + const folders = []; foldersData.forEach(folderData => { const folder = new osparc.data.model.Folder(folderData); this.__addToCache(folder); + folders.push(folder); }); - resolve(); + resolve(folders); }) }); }, From 8ec5b2f44b77491a063811b9d1b3c5a28feea65d Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Mon, 12 Aug 2024 16:31:00 +0200 Subject: [PATCH 30/67] build tree --- .../class/osparc/dashboard/FolderTreeItem.js | 32 ------------------- .../class/osparc/dashboard/FoldersTree.js | 22 ++++++------- .../osparc/dashboard/MoveStudyToFolder.js | 2 +- 3 files changed, 11 insertions(+), 45 deletions(-) delete mode 100644 services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js deleted file mode 100644 index c1823c9bed4..00000000000 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js +++ /dev/null @@ -1,32 +0,0 @@ -/* ************************************************************************ - - osparc - the simcore frontend - - https://osparc.io - - Copyright: - 2024 IT'IS Foundation, https://itis.swiss - - License: - MIT: https://opensource.org/licenses/MIT - - Authors: - * Odei Maiz (odeimaiz) - -************************************************************************ */ - -qx.Class.define("osparc.dashboard.FolderTreeItem", { - extend: qx.ui.tree.VirtualTreeItem, - - members: { - // overridden - _addWidgets: function() { - this.addIcon(); - this.addLabel(); - - this.addWidget(new qx.ui.core.Spacer(), { - flex: 1 - }); - } - } -}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js index e5002ee3bee..61e3e060a87 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js @@ -19,7 +19,7 @@ qx.Class.define("osparc.dashboard.FoldersTree", { extend: qx.ui.tree.VirtualTree, construct: function() { - this.base(arguments, null, "name", "children"); + this.base(arguments, null, "label", "children"); this.set({ openMode: "none", @@ -51,8 +51,6 @@ qx.Class.define("osparc.dashboard.FoldersTree", { addLoadingChild: function(parent) { const loadingModel = qx.data.marshal.Json.createModel({ label: "Loading...", - location: null, - path: null, children: [], icon: "@FontAwesome5Solid/circle-notch/12" }, true); @@ -72,11 +70,11 @@ qx.Class.define("osparc.dashboard.FoldersTree", { members: { __initTree: function() { this.setDelegate({ - createItem: () => new osparc.dashboard.FolderTreeItem(), + createItem: () => new qx.ui.tree.VirtualTreeItem(), bindItem: (c, item, id) => { c.bindDefaultProperties(item, id); c.bindProperty("folderId", "model", null, item, id); - c.bindProperty("name", "name", null, item, id); + c.bindProperty("name", "label", null, item, id); }, configureItem: item => { item.addListener("tap", () => this.fireDataEvent("selectionChanged", item.getModel()), this); @@ -87,15 +85,15 @@ qx.Class.define("osparc.dashboard.FoldersTree", { }); }, sorter: (a, b) => { - const aName = a.getName(); - if (aName === -1) { + const aLabel = a.getLabel(); + if (aLabel === -1) { return 1; } - const bName = b.getName(); - if (bName === -1) { + const bLabel = b.getLabel(); + if (bLabel === -1) { return -1; } - return aName - bName; + return aLabel - bLabel; } }); }, @@ -115,14 +113,14 @@ qx.Class.define("osparc.dashboard.FoldersTree", { __fetchChildren: function(parentModel) { this.self().addLoadingChild(parentModel); - const folderId = parentModel.getModel(); + const folderId = parentModel.getFolderId(); osparc.store.Folders.getInstance().fetchFolders(folderId) .then(folders => { this.self().removeLoadingChild(parentModel); folders.forEach(folder => { const folderData = folder.serialize(); folderData["children"] = []; - parent.getChildren().append(qx.data.marshal.Json.createModel(folderData)); + parentModel.getChildren().append(qx.data.marshal.Json.createModel(folderData)); }); }); }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js b/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js index a4968844626..1466147b92f 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js @@ -45,7 +45,7 @@ qx.Class.define("osparc.dashboard.MoveStudyToFolder", { let control; switch (id) { case "current-folder": { - const folder = osparc.store.Folders.getFolder(this.__currentFolderId); + const folder = osparc.store.Folders.getInstance().getFolder(this.__currentFolderId); const currentFolderName = folder ? folder["name"] : "Home"; control = new qx.ui.basic.Label(this.tr("Current location: ") + currentFolderName); this._add(control); From 683f77f90de45f360f2a2531461cea34fee08ab7 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Mon, 12 Aug 2024 18:22:52 +0200 Subject: [PATCH 31/67] __moveStudyToFolder --- .../osparc/dashboard/MoveStudyToFolder.js | 13 ++++++-- .../class/osparc/dashboard/StudyBrowser.js | 30 ++++++++++++++----- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js b/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js index 1466147b92f..0dee2f91439 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js @@ -27,9 +27,18 @@ qx.Class.define("osparc.dashboard.MoveStudyToFolder", { this._setLayout(new qx.ui.layout.VBox(10)); this.getChildControl("current-folder"); - this.getChildControl("folders-tree"); + const foldersTree = this.getChildControl("folders-tree"); this.getChildControl("cancel-btn"); - this.getChildControl("move-btn"); + const moveButton = this.getChildControl("move-btn"); + + moveButton.setEnabled(false) + foldersTree.addListener("selectionChanged", e => { + const folderId = e.getData(); + moveButton.setEnabled(); + moveButton.addListenerOnce("execute", () => { + this.fireDataEvent("moveToFolder", folderId); + }); + }); }, events: { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index da11c043888..f13b7d05644 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -969,13 +969,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { __startStudyAfterCreating: function(studyId) { if (this.getCurrentFolderId()) { - const params = { - url: { - studyId, - folderId: this.getCurrentFolderId() - } - } - osparc.data.Resources.fetch("studies", "moveToFolder", params); + this.__moveStudyToFolder(studyId, this.getCurrentFolderId()); } const openCB = () => this._hideLoadingPage(); const cancelCB = () => { @@ -991,6 +985,16 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._startStudyById(studyId, openCB, cancelCB, isStudyCreation); }, + __moveStudyToFolder: function(studyId, folderId) { + const params = { + url: { + studyId, + folderId + } + }; + return osparc.data.Resources.fetch("studies", "moveToFolder", params); + }, + _updateStudyData: function(studyData) { studyData["resourceType"] = "study"; const studies = this._resourcesList; @@ -1165,7 +1169,17 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { moveStudyToFolder.addListener("moveToFolder", e => { win.close(); const folderId = e.getData(); - this.__updateThumbnail(studyData, folderId); + this.__moveStudyToFolder(studyData["uuid"], folderId) + .then(() => { + this._resourcesContainer.setResourcesToList([]); + this._resourcesList = []; + this.invalidateStudies(); + + this.__reloadResources() + }) + .catch(err => { + // OM + }); }, this); moveStudyToFolder.addListener("cancel", () => win.close()); } From 0001b5daad6b890504545852f86ba2bc3b25f2b4 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 13 Aug 2024 10:14:49 +0200 Subject: [PATCH 32/67] minors --- .../client/source/class/osparc/dashboard/FoldersTree.js | 2 +- .../source/class/osparc/dashboard/MoveStudyToFolder.js | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js index 61e3e060a87..8630279c7d9 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js @@ -24,7 +24,7 @@ qx.Class.define("osparc.dashboard.FoldersTree", { this.set({ openMode: "none", contentPadding: 0, - padding: 0, + paddingLeft: -10, decorator: "no-border", font: "text-14" }); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js b/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js index 0dee2f91439..45a14a677e3 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js @@ -34,10 +34,8 @@ qx.Class.define("osparc.dashboard.MoveStudyToFolder", { moveButton.setEnabled(false) foldersTree.addListener("selectionChanged", e => { const folderId = e.getData(); - moveButton.setEnabled(); - moveButton.addListenerOnce("execute", () => { - this.fireDataEvent("moveToFolder", folderId); - }); + moveButton.setEnabled(this.__currentFolderId !== folderId); + moveButton.addListenerOnce("execute", () => this.fireDataEvent("moveToFolder", folderId)); }); }, From 7da278bda6f76894dcd2170180bbc43f607d9384 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 13 Aug 2024 10:35:48 +0200 Subject: [PATCH 33/67] not there yet --- .../class/osparc/dashboard/FoldersTree.js | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js index 8630279c7d9..e506c5e32ca 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js @@ -37,23 +37,17 @@ qx.Class.define("osparc.dashboard.FoldersTree", { "selectionChanged": "qx.event.type.Event" // tap }, - properties: { - currentFolderId: { - check: "Number", - nullable: true, - init: null, - event: "changeCurrentFolderId", - apply: "__applyCurrentFolderId" - } - }, - statics: { - addLoadingChild: function(parent) { - const loadingModel = qx.data.marshal.Json.createModel({ + getLoadingData: function() { + return { label: "Loading...", children: [], icon: "@FontAwesome5Solid/circle-notch/12" - }, true); + }; + }, + + addLoadingChild: function(parent) { + const loadingModel = qx.data.marshal.Json.createModel(this.self().getLoadingData(), true); parent.getChildren().append(loadingModel); }, @@ -107,25 +101,25 @@ qx.Class.define("osparc.dashboard.FoldersTree", { const rootModel = qx.data.marshal.Json.createModel(rootFolder, true); this.setModel(rootModel); + this.self().addLoadingChild(rootModel); this.__fetchChildren(rootModel); }, __fetchChildren: function(parentModel) { - this.self().addLoadingChild(parentModel); - const folderId = parentModel.getFolderId(); osparc.store.Folders.getInstance().fetchFolders(folderId) .then(folders => { this.self().removeLoadingChild(parentModel); folders.forEach(folder => { - const folderData = folder.serialize(); - folderData["children"] = []; - parentModel.getChildren().append(qx.data.marshal.Json.createModel(folderData)); + const folderData = { + folderId: folder.getFolderId(), + name: folder.getName(), + children: [this.self().getLoadingData()] + } + const folderModel = qx.data.marshal.Json.createModel(folderData); + parentModel.getChildren().append(folderModel); }); }); - }, - - __applyCurrentFolderId: function() { } } }); From 0967b08591727b47b30e56fb5fb776a55d65eb62 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 13 Aug 2024 10:52:31 +0200 Subject: [PATCH 34/67] simplify --- .../class/osparc/dashboard/FoldersTree.js | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js index e506c5e32ca..d07beee54e0 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js @@ -38,26 +38,14 @@ qx.Class.define("osparc.dashboard.FoldersTree", { }, statics: { - getLoadingData: function() { - return { + addLoadingChild: function(parentModel) { + const loadingModel = qx.data.marshal.Json.createModel({ label: "Loading...", children: [], icon: "@FontAwesome5Solid/circle-notch/12" - }; - }, - - addLoadingChild: function(parent) { - const loadingModel = qx.data.marshal.Json.createModel(this.self().getLoadingData(), true); - parent.getChildren().append(loadingModel); - }, - - removeLoadingChild: function(parent) { - for (let i = parent.getChildren().length - 1; i >= 0; i--) { - if (parent.getChildren().toArray()[i].getLabel() === "Loading...") { - parent.getChildren().toArray() - .splice(i, 1); - } - } + }, true); + parentModel.getChildren().removeAll(); + parentModel.getChildren().append(loadingModel); } }, @@ -77,6 +65,8 @@ qx.Class.define("osparc.dashboard.FoldersTree", { openButton.addListener("tap", () => { this.__fetchChildren(item); }); + // OM remove this + item.addListener("dbltap", () => this.__fetchChildren(item), this); }, sorter: (a, b) => { const aLabel = a.getLabel(); @@ -101,22 +91,24 @@ qx.Class.define("osparc.dashboard.FoldersTree", { const rootModel = qx.data.marshal.Json.createModel(rootFolder, true); this.setModel(rootModel); - this.self().addLoadingChild(rootModel); this.__fetchChildren(rootModel); }, __fetchChildren: function(parentModel) { - const folderId = parentModel.getFolderId(); + this.self().addLoadingChild(parentModel); + + const folderId = parentModel.getFolderId ? parentModel.getFolderId() : parentModel.getModel(); osparc.store.Folders.getInstance().fetchFolders(folderId) .then(folders => { - this.self().removeLoadingChild(parentModel); + parentModel.getChildren().removeAll(); folders.forEach(folder => { const folderData = { folderId: folder.getFolderId(), name: folder.getName(), - children: [this.self().getLoadingData()] + children: [] } const folderModel = qx.data.marshal.Json.createModel(folderData); + this.self().addLoadingChild(folderModel); parentModel.getChildren().append(folderModel); }); }); From bd485f3ebb93ee87249755592e85077a54bd6070 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 13 Aug 2024 11:05:18 +0200 Subject: [PATCH 35/67] minor --- .../client/source/class/osparc/dashboard/FoldersTree.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js index d07beee54e0..b8bac3ecdde 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js @@ -106,8 +106,8 @@ qx.Class.define("osparc.dashboard.FoldersTree", { folderId: folder.getFolderId(), name: folder.getName(), children: [] - } - const folderModel = qx.data.marshal.Json.createModel(folderData); + }; + const folderModel = qx.data.marshal.Json.createModel(folderData, true); this.self().addLoadingChild(folderModel); parentModel.getChildren().append(folderModel); }); From 82cc213b03f0dcd1e45b479fd4ec71e3370ced14 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 13 Aug 2024 11:32:11 +0200 Subject: [PATCH 36/67] loaded property --- .../class/osparc/dashboard/FoldersTree.js | 61 ++++++++++--------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js index b8bac3ecdde..61ce85d9359 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js @@ -19,7 +19,16 @@ qx.Class.define("osparc.dashboard.FoldersTree", { extend: qx.ui.tree.VirtualTree, construct: function() { - this.base(arguments, null, "label", "children"); + const rootFolder = { + folderId: null, + label: "Home", + children: [], + loaded: true, + }; + const root = qx.data.marshal.Json.createModel(rootFolder, true); + this.__fetchChildren(root); + + this.base(arguments, root, "label", "children"); this.set({ openMode: "none", @@ -30,7 +39,6 @@ qx.Class.define("osparc.dashboard.FoldersTree", { }); this.__initTree(); - this.__populateTree(); }, events: { @@ -38,12 +46,17 @@ qx.Class.define("osparc.dashboard.FoldersTree", { }, statics: { - addLoadingChild: function(parentModel) { - const loadingModel = qx.data.marshal.Json.createModel({ + getLoadingData: function() { + return { label: "Loading...", children: [], - icon: "@FontAwesome5Solid/circle-notch/12" - }, true); + icon: "@FontAwesome5Solid/circle-notch/12", + loaded: false, + }; + }, + + addLoadingChild: function(parentModel) { + const loadingModel = qx.data.marshal.Json.createModel(this.self().getLoadingData(), true); parentModel.getChildren().removeAll(); parentModel.getChildren().append(loadingModel); } @@ -56,17 +69,20 @@ qx.Class.define("osparc.dashboard.FoldersTree", { bindItem: (c, item, id) => { c.bindDefaultProperties(item, id); c.bindProperty("folderId", "model", null, item, id); - c.bindProperty("name", "label", null, item, id); + c.bindProperty("", "open", { + converter(value, model, source, target) { + const isOpen = target.isOpen(); + if (isOpen && !value.getLoaded()) { + value.setLoaded(true); + value.getChildren().removeAll(); + this.__fetchChildren(value); + } + return isOpen; + } + }, item, id); }, configureItem: item => { item.addListener("tap", () => this.fireDataEvent("selectionChanged", item.getModel()), this); - - const openButton = item.getChildControl("open"); - openButton.addListener("tap", () => { - this.__fetchChildren(item); - }); - // OM remove this - item.addListener("dbltap", () => this.__fetchChildren(item), this); }, sorter: (a, b) => { const aLabel = a.getLabel(); @@ -82,18 +98,6 @@ qx.Class.define("osparc.dashboard.FoldersTree", { }); }, - __populateTree: function() { - const rootFolder = { - folderId: null, - name: "Home", - children: [] - }; - const rootModel = qx.data.marshal.Json.createModel(rootFolder, true); - this.setModel(rootModel); - - this.__fetchChildren(rootModel); - }, - __fetchChildren: function(parentModel) { this.self().addLoadingChild(parentModel); @@ -104,8 +108,9 @@ qx.Class.define("osparc.dashboard.FoldersTree", { folders.forEach(folder => { const folderData = { folderId: folder.getFolderId(), - name: folder.getName(), - children: [] + label: folder.getName(), + children: [this.self().getLoadingData()], + loaded: false }; const folderModel = qx.data.marshal.Json.createModel(folderData, true); this.self().addLoadingChild(folderModel); From ec7c1db208254298e6a9c306adbb9abc207d2b0f Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 13 Aug 2024 11:45:54 +0200 Subject: [PATCH 37/67] minor --- .../client/source/class/osparc/dashboard/FoldersTree.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js index 61ce85d9359..1d6ee4f263a 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js @@ -31,9 +31,7 @@ qx.Class.define("osparc.dashboard.FoldersTree", { this.base(arguments, root, "label", "children"); this.set({ - openMode: "none", - contentPadding: 0, - paddingLeft: -10, + openMode: "dbltap", decorator: "no-border", font: "text-14" }); From d42d0d8f6a3a64bea0e31db198cedc10329879a7 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 13 Aug 2024 11:51:16 +0200 Subject: [PATCH 38/67] remove moved card --- .../source/class/osparc/dashboard/StudyBrowser.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index f13b7d05644..7185700a99f 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -1171,14 +1171,11 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const folderId = e.getData(); this.__moveStudyToFolder(studyData["uuid"], folderId) .then(() => { - this._resourcesContainer.setResourcesToList([]); - this._resourcesList = []; - this.invalidateStudies(); - - this.__reloadResources() + this.__removeFromStudyList(studyData["uuid"]); }) .catch(err => { - // OM + console.error(err); + osparc.FlashMessenger.logAs(err.message, "ERROR"); }); }, this); moveStudyToFolder.addListener("cancel", () => win.close()); @@ -1398,7 +1395,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { operationPromise = osparc.store.Store.getInstance().deleteStudy(studyData.uuid); } operationPromise - .then(() => this.__removeFromStudyList(studyData.uuid, false)) + .then(() => this.__removeFromStudyList(studyData.uuid)) .catch(err => { console.error(err); osparc.FlashMessenger.getInstance().logAs(err, "ERROR"); From be93d65599d17fd101bf97f3df2322f2a9f8f55b Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 13 Aug 2024 12:21:21 +0200 Subject: [PATCH 39/67] move to folder working --- .../client/source/class/osparc/dashboard/MoveStudyToFolder.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js b/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js index 45a14a677e3..f01d64f42b4 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/MoveStudyToFolder.js @@ -82,10 +82,6 @@ qx.Class.define("osparc.dashboard.MoveStudyToFolder", { control = new qx.ui.form.Button(this.tr("Move")).set({ appearance: "form-button" }); - control.addListener("execute", () => { - const folderId = this.getChildControl("url-field"); - this.fireDataEvent("moveToFolder", folderId); - }, this); buttons.add(control); break; } From 993f5dec057d9055a94bf5d8934194f5554be588 Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Tue, 13 Aug 2024 12:56:28 +0200 Subject: [PATCH 40/67] review @pcrespov --- .../folders/_folders_handlers.py | 11 +++++------ .../src/simcore_service_webserver/projects/db.py | 16 +++++++--------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py index d2097660010..cc2a05a5875 100644 --- a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py +++ b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py @@ -11,6 +11,7 @@ from models_library.rest_ordering import OrderBy, OrderDirection from models_library.rest_pagination import PageQueryParameters from models_library.users import UserID +from models_library.utils.common_validators import null_or_none_str_to_none_validator from pydantic import Extra, Field, Json, parse_obj_as, validator from servicelib.aiohttp.requests_validation import ( RequestParams, @@ -90,12 +91,10 @@ def validate_order_by_field(cls, v): class Config: extra = Extra.forbid - @validator("folder_id", pre=True, always=True) - @classmethod - def convert_null_to_none(cls, v): - if v is None or v in ["null", "none"]: - return None - return v + # validators + _null_or_none_str_to_none_validator = validator( + "folder_id", allow_reuse=True, pre=True + )(null_or_none_str_to_none_validator) @routes.post(f"/{VTAG}/folders", name="create_folder") diff --git a/services/web/server/src/simcore_service_webserver/projects/db.py b/services/web/server/src/simcore_service_webserver/projects/db.py index 3b2b6d9d3f0..ee008e97359 100644 --- a/services/web/server/src/simcore_service_webserver/projects/db.py +++ b/services/web/server/src/simcore_service_webserver/projects/db.py @@ -360,21 +360,19 @@ async def list_projects( # pylint: disable=too-many-arguments ).group_by(project_to_groups.c.project_uuid) ).subquery("access_rights_subquery") + _join_query = projects.join(projects_to_products, isouter=True).join( + access_rights_subquery, isouter=True + ) + if settings.WEBSERVER_FOLDERS: + _join_query = _join_query.join(folders_to_projects, isouter=True) + query = ( sa.select( *[col for col in projects.columns if col.name != "access_rights"], access_rights_subquery.c.access_rights, projects_to_products.c.product_name, ) - .select_from( - projects.join(projects_to_products, isouter=True) - .join(access_rights_subquery, isouter=True) - .join(folders_to_projects, isouter=True) - if settings.WEBSERVER_FOLDERS - else projects.join(projects_to_products, isouter=True).join( - access_rights_subquery, isouter=True - ) - ) + .select_from(_join_query) .where( ( (projects.c.type == filter_by_project_type.value) From ff821ff45f7b2bba92b590f5c90000b7ee91d783 Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Tue, 13 Aug 2024 14:15:50 +0200 Subject: [PATCH 41/67] review @pcrespov --- .../models_library/utils/common_validators.py | 6 ++++ .../tests/test_utils_common_validators.py | 28 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/packages/models-library/src/models_library/utils/common_validators.py b/packages/models-library/src/models_library/utils/common_validators.py index e16138ca4ad..c60586dfa92 100644 --- a/packages/models-library/src/models_library/utils/common_validators.py +++ b/packages/models-library/src/models_library/utils/common_validators.py @@ -63,3 +63,9 @@ def ensure_unique_dict_values_validator(dict_data: dict) -> dict: msg = f"Dictionary values must be unique, provided: {dict_data}" raise ValueError(msg) return dict_data + + +def null_or_none_str_to_none_validator(value: Any): + if isinstance(value, str) and value.lower() in ("null", "none"): + return None + return value diff --git a/packages/models-library/tests/test_utils_common_validators.py b/packages/models-library/tests/test_utils_common_validators.py index d185b16f7b0..d7fe367ab5d 100644 --- a/packages/models-library/tests/test_utils_common_validators.py +++ b/packages/models-library/tests/test_utils_common_validators.py @@ -5,6 +5,7 @@ create_enums_pre_validator, empty_str_to_none_pre_validator, none_to_empty_str_pre_validator, + null_or_none_str_to_none_validator, ) from pydantic import BaseModel, ValidationError, validator @@ -59,3 +60,30 @@ class Model(BaseModel): model = Model.parse_obj({"message": ""}) assert model == Model.parse_obj({"message": None}) + + +def test_null_or_none_str_to_none_validator(): + class Model(BaseModel): + message: str | None + + _null_or_none_str_to_none_validator = validator( + "message", allow_reuse=True, pre=True + )(null_or_none_str_to_none_validator) + + model = Model.parse_obj({"message": "none"}) + assert model == Model.parse_obj({"message": None}) + + model = Model.parse_obj({"message": "null"}) + assert model == Model.parse_obj({"message": None}) + + model = Model.parse_obj({"message": "NoNe"}) + assert model == Model.parse_obj({"message": None}) + + model = Model.parse_obj({"message": "NuLl"}) + assert model == Model.parse_obj({"message": None}) + + model = Model.parse_obj({"message": None}) + assert model == Model.parse_obj({"message": None}) + + model = Model.parse_obj({"message": ""}) + assert model == Model.parse_obj({"message": ""}) From fb284ea90393ac9a14b380e9dc6577cc7c664e36 Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Tue, 13 Aug 2024 14:45:43 +0200 Subject: [PATCH 42/67] improvements --- .../simcore_service_webserver/api/v0/openapi.yaml | 2 +- .../projects/_crud_handlers_models.py | 5 +++++ .../projects/_folders_handlers.py | 11 +++++------ ...projects_crud_handlers__list_with_query_params.py | 12 ++++++++++++ 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml index 7204cd82d4b..ca2eda1d103 100644 --- a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml +++ b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml @@ -3493,7 +3493,7 @@ paths: '403': description: ProjectInvalidRightsError '404': - description: UserDefaultWalletNotFoundError, ProjectNotFoundError + description: ProjectNotFoundError, UserDefaultWalletNotFoundError '409': description: ProjectTooManyProjectOpenedError '422': diff --git a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers_models.py b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers_models.py index a2b37542d07..b3542abdb3f 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_crud_handlers_models.py +++ b/services/web/server/src/simcore_service_webserver/projects/_crud_handlers_models.py @@ -11,6 +11,7 @@ from models_library.projects_nodes_io import NodeID from models_library.rest_ordering import OrderBy, OrderDirection from models_library.rest_pagination import PageQueryParameters +from models_library.utils.common_validators import null_or_none_str_to_none_validator from pydantic import BaseModel, Extra, Field, Json, root_validator, validator from servicelib.common_headers import ( UNDEFINED_DEFAULT_SIMCORE_USER_AGENT_VALUE, @@ -104,6 +105,10 @@ def search_check_empty_string(cls, v): return None return v + _null_or_none_str_to_none_validator = validator( + "folder_id", allow_reuse=True, pre=True + )(null_or_none_str_to_none_validator) + class ProjectListWithJsonStrParams(ProjectListParams): order_by: Json[OrderBy] = Field( # pylint: disable=unsubscriptable-object diff --git a/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py b/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py index 8e1cacf57eb..303a857c6d5 100644 --- a/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py +++ b/services/web/server/src/simcore_service_webserver/projects/_folders_handlers.py @@ -4,6 +4,7 @@ from aiohttp import web from models_library.folders import FolderID from models_library.projects import ProjectID +from models_library.utils.common_validators import null_or_none_str_to_none_validator from pydantic import BaseModel, Extra, validator from servicelib.aiohttp.requests_validation import parse_request_path_parameters_as from servicelib.aiohttp.typing_extension import Handler @@ -45,12 +46,10 @@ class _ProjectsFoldersPathParams(BaseModel): class Config: extra = Extra.forbid - @validator("folder_id", pre=True, always=True) - @classmethod - def convert_null_to_none(cls, v): - if v is None or v == "null" or v == "none": - return None - return v + # validators + _null_or_none_str_to_none_validator = validator( + "folder_id", allow_reuse=True, pre=True + )(null_or_none_str_to_none_validator) @routes.put( diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py index f91e34c5717..b93d33ef94b 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py @@ -455,6 +455,18 @@ async def test_list_projects_for_specific_folder_id( assert resp.status == 200 _assert_response_data(data, 3, 0, 3, "/v0/projects?offset=0&limit=20", 3) + # Now we will test listing of the root directory with provided folder id query + query_parameters = {"folder_id": "null"} + url = base_url.with_query(**query_parameters) + + resp = await client.get(url) + data = await resp.json() + + assert resp.status == 200 + _assert_response_data( + data, 3, 0, 3, "/v0/projects?folder_id=null&offset=0&limit=20", 3 + ) + # Now we will test listing for specific folder query_parameters = {"folder_id": f"{setup_folders_db}"} url = base_url.with_query(**query_parameters) From d08a5cd981192ecedba6095084bb7d188078a5d0 Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Tue, 13 Aug 2024 15:12:25 +0200 Subject: [PATCH 43/67] fix open api specs --- api/specs/web-server/_folders.py | 5 +- .../api_schemas_webserver/folders.py | 7 +- .../api/v0/openapi.yaml | 8 +- .../folders/_folders_api.py | 77 ++++++++++++++++++- .../folders/_folders_handlers.py | 8 +- 5 files changed, 87 insertions(+), 18 deletions(-) diff --git a/api/specs/web-server/_folders.py b/api/specs/web-server/_folders.py index c75d7d44bc5..e5cf2d6a0cb 100644 --- a/api/specs/web-server/_folders.py +++ b/api/specs/web-server/_folders.py @@ -15,7 +15,6 @@ FolderGet, PutFolderBodyParams, ) -from models_library.api_schemas_webserver.wallets import WalletGet from models_library.generics import Envelope from models_library.rest_pagination import PageQueryParameters from pydantic import Json @@ -39,7 +38,7 @@ @router.post( "/folders", - response_model=Envelope[WalletGet], + response_model=Envelope[FolderGet], status_code=status.HTTP_201_CREATED, ) async def create_folder(_body: CreateFolderBodyParams): @@ -83,7 +82,7 @@ async def replace_folder( @router.delete( "/folders/{folder_id}", - response_model=Envelope[FolderGet], + status_code=status.HTTP_204_NO_CONTENT, ) async def delete_folder(_path: Annotated[FoldersPathParams, Depends()]): ... diff --git a/packages/models-library/src/models_library/api_schemas_webserver/folders.py b/packages/models-library/src/models_library/api_schemas_webserver/folders.py index 6bd29666e71..093659548df 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/folders.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/folders.py @@ -4,7 +4,8 @@ from models_library.folders import FolderID from models_library.projects_access import AccessRights from models_library.users import GroupID -from pydantic import Extra +from models_library.utils.common_validators import null_or_none_str_to_none_validator +from pydantic import Extra, validator from ._base import InputSchema, OutputSchema @@ -29,6 +30,10 @@ class CreateFolderBodyParams(InputSchema): class Config: extra = Extra.forbid + _null_or_none_str_to_none_validator = validator( + "parent_folder_id", allow_reuse=True, pre=True + )(null_or_none_str_to_none_validator) + class PutFolderBodyParams(InputSchema): name: IDStr diff --git a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml index ca2eda1d103..d63508f32c3 100644 --- a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml +++ b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml @@ -1349,7 +1349,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Envelope_WalletGet_' + $ref: '#/components/schemas/Envelope_FolderGet_' /v0/folders/{folder_id}: get: tags: @@ -1414,12 +1414,8 @@ paths: name: folder_id in: path responses: - '200': + '204': description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/Envelope_FolderGet_' /v0/folders/{folder_id}/groups/{group_id}: put: tags: diff --git a/services/web/server/src/simcore_service_webserver/folders/_folders_api.py b/services/web/server/src/simcore_service_webserver/folders/_folders_api.py index 2ccdf0b39da..23d239f22c9 100644 --- a/services/web/server/src/simcore_service_webserver/folders/_folders_api.py +++ b/services/web/server/src/simcore_service_webserver/folders/_folders_api.py @@ -26,7 +26,7 @@ async def create_folder( description: str | None, parent_folder_id: FolderID | None, product_name: ProductName, -) -> FolderID: +) -> FolderGet: user = await get_user(app, user_id=user_id) engine: Engine = app[APP_DB_ENGINE_KEY] @@ -40,7 +40,28 @@ async def create_folder( description=description if description else "", parent=parent_folder_id, ) - return FolderID(folder_id) + folder_db: folders_db.FolderEntry = await folders_db.folder_get( + connection, + product_name=product_name, + folder_id=folder_id, + gid=user["primary_gid"], + ) + return FolderGet( + folder_id=folder_db.id, + parent_folder_id=folder_db.parent_folder, + name=folder_db.name, + description=folder_db.description, + created_at=folder_db.created, + modified_at=folder_db.modified, + owner=folder_db.owner, + my_access_rights=parse_obj_as( + AccessRights, folder_db.my_access_rights.to_dict() + ), + access_rights=parse_obj_as( + dict[GroupID, AccessRights], + {key: value.to_dict() for key, value in folder_db.access_rights.items()}, + ), + ) async def get_folder( @@ -49,7 +70,33 @@ async def get_folder( folder_id: FolderID, product_name: ProductName, ) -> FolderGet: - raise NotImplementedError + user = await get_user(app, user_id=user_id) + + engine: Engine = app[APP_DB_ENGINE_KEY] + async with engine.acquire() as connection: + # NOTE: folder permissions are checked inside the function + folder_db: folders_db.FolderEntry = await folders_db.folder_get( + connection, + product_name=product_name, + folder_id=folder_id, + gid=user["primary_gid"], + ) + return FolderGet( + folder_id=folder_db.id, + parent_folder_id=folder_db.parent_folder, + name=folder_db.name, + description=folder_db.description, + created_at=folder_db.created, + modified_at=folder_db.modified, + owner=folder_db.owner, + my_access_rights=parse_obj_as( + AccessRights, folder_db.my_access_rights.to_dict() + ), + access_rights=parse_obj_as( + dict[GroupID, AccessRights], + {key: value.to_dict() for key, value in folder_db.access_rights.items()}, + ), + ) async def list_folders( @@ -102,7 +149,7 @@ async def update_folder( name: str, description: str | None, product_name: ProductName, -) -> None: +) -> FolderGet: user = await get_user(app, user_id=user_id) engine: Engine = app[APP_DB_ENGINE_KEY] @@ -116,6 +163,28 @@ async def update_folder( name=name, description=description, ) + folder_db: folders_db.FolderEntry = await folders_db.folder_get( + connection, + product_name=product_name, + folder_id=folder_id, + gid=user["primary_gid"], + ) + return FolderGet( + folder_id=folder_db.id, + parent_folder_id=folder_db.parent_folder, + name=folder_db.name, + description=folder_db.description, + created_at=folder_db.created, + modified_at=folder_db.modified, + owner=folder_db.owner, + my_access_rights=parse_obj_as( + AccessRights, folder_db.my_access_rights.to_dict() + ), + access_rights=parse_obj_as( + dict[GroupID, AccessRights], + {key: value.to_dict() for key, value in folder_db.access_rights.items()}, + ), + ) async def delete_folder( diff --git a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py index cc2a05a5875..cb1bb4b1cb0 100644 --- a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py +++ b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py @@ -105,7 +105,7 @@ async def create_folder(request: web.Request): req_ctx = FoldersRequestContext.parse_obj(request) body_params = await parse_request_body_as(CreateFolderBodyParams, request) - folder_id: FolderID = await _folders_api.create_folder( + folder = await _folders_api.create_folder( request.app, user_id=req_ctx.user_id, folder_name=body_params.name, @@ -114,7 +114,7 @@ async def create_folder(request: web.Request): product_name=req_ctx.product_name, ) - return envelope_json_response({"folderId": folder_id}, web.HTTPCreated) + return envelope_json_response(folder, web.HTTPCreated) @routes.get(f"/{VTAG}/folders", name="list_folders") @@ -170,7 +170,7 @@ async def replace_folder(request: web.Request): path_params = parse_request_path_parameters_as(FoldersPathParams, request) body_params = await parse_request_body_as(PutFolderBodyParams, request) - await _folders_api.update_folder( + folder = await _folders_api.update_folder( app=request.app, user_id=req_ctx.user_id, folder_id=path_params.folder_id, @@ -178,7 +178,7 @@ async def replace_folder(request: web.Request): description=body_params.description, product_name=req_ctx.product_name, ) - raise web.HTTPNoContent(content_type=MIMETYPE_APPLICATION_JSON) + return envelope_json_response(folder) @routes.delete( From 87f1346947a04356766ba9eaccae01a1a1e095f3 Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Tue, 13 Aug 2024 15:21:36 +0200 Subject: [PATCH 44/67] adding test --- .../server/tests/unit/with_dbs/03/folders/test_folders.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/web/server/tests/unit/with_dbs/03/folders/test_folders.py b/services/web/server/tests/unit/with_dbs/03/folders/test_folders.py index 8a19278f104..800951027e5 100644 --- a/services/web/server/tests/unit/with_dbs/03/folders/test_folders.py +++ b/services/web/server/tests/unit/with_dbs/03/folders/test_folders.py @@ -7,6 +7,7 @@ import pytest from aiohttp.test_utils import TestClient +from models_library.api_schemas_webserver.folders import FolderGet from pytest_simcore.helpers.assert_checks import assert_status from pytest_simcore.helpers.webserver_login import UserInfoDict from servicelib.aiohttp import status @@ -35,6 +36,7 @@ async def test_folders_full_workflow( url.path, json={"name": "My first folder", "description": "Custom description"} ) added_folder, _ = await assert_status(resp, status.HTTP_201_CREATED) + assert FolderGet.parse_obj(added_folder) # list user folders url = client.app.router["list_folders"].url_for() @@ -55,7 +57,8 @@ async def test_folders_full_workflow( "description": "", }, ) - data, _ = await assert_status(resp, status.HTTP_204_NO_CONTENT) + data, _ = await assert_status(resp, status.HTTP_200_OK) + assert FolderGet.parse_obj(data) # list user folders url = client.app.router["list_folders"].url_for() From 0c7c64bb2dcb1192ed9b37e4ab20fb69c7175eb6 Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Tue, 13 Aug 2024 15:37:00 +0200 Subject: [PATCH 45/67] review @pcrespov --- ...s_crud_handlers__list_with_query_params.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py index b93d33ef94b..6684d3ec96d 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py @@ -19,6 +19,7 @@ from models_library.users import UserID from pydantic import BaseModel, PositiveInt from pytest_mock import MockerFixture +from pytest_simcore.helpers.typing_env import EnvVarsDict from pytest_simcore.helpers.webserver_login import UserInfoDict from pytest_simcore.helpers.webserver_parametrizations import ( ExpectedResponse, @@ -479,3 +480,40 @@ async def test_list_projects_for_specific_folder_id( _assert_response_data( data, 1, 0, 1, f"/v0/projects?folder_id={setup_folders_db}&offset=0&limit=20", 1 ) + + +@pytest.fixture +def app_environment( + app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatch +) -> EnvVarsDict: + # disable the garbage collector + monkeypatch.setenv("WEBSERVER_FOLDERS", "0") + return app_environment | {"WEBSERVER_FOLDERS": "0"} + + +@pytest.mark.parametrize(*standard_user_role()) +async def test_list_projects_with_disabled_project_folders_plugin( + client: TestClient, + app_environment: EnvVarsDict, + logged_user: UserDict, + expected: ExpectedResponse, + fake_project: ProjectDict, + tests_data_dir: Path, + osparc_product_name: str, + project_db_cleaner, + mock_catalog_api_get_services_for_user_in_product, + setup_folders_db, +): + """ + As the WEBSERVER_FOLDERS plugin is turned off, the project listing + should behave the same way as before, and therefore list all the projects + in the root directory, essentially ignoring the folders_to_projects table. + """ + base_url = client.app.router["list_projects"].url_for() + assert f"{base_url}" == f"/{api_version_prefix}/projects" + + resp = await client.get(base_url) + data = await resp.json() + + assert resp.status == 200 + _assert_response_data(data, 1, 0, 1, "/v0/projects?offset=0&limit=20", 1) From 39243c766f840697abae530cd6c0f73b9ba5321a Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Tue, 13 Aug 2024 16:34:36 +0200 Subject: [PATCH 46/67] small progress --- .../class/osparc/dashboard/FolderTreeItem.js | 41 +++++++++++++++++++ .../class/osparc/dashboard/FoldersTree.js | 12 ++++-- 2 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js new file mode 100644 index 00000000000..3d5e0bf3084 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js @@ -0,0 +1,41 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2024 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.dashboard.FolderTreeItem", { + extend: qx.ui.tree.VirtualTreeItem, + + members: { + _addWidgets: function() { + this.addSpacer(); + // this.addOpenButton(); + const openButton = this.getChildControl("open"); + openButton.addListener("changeOpen", () => { + console.log("changeOpen"); + }, this); + openButton.addListener("changeVisibility", e => { + console.log("changeVisibility", e.getData()); + openButton.show(); + }, this); + this._add(openButton); + this.addIcon(); + const label = this.getChildControl("label"); + this._add(label, { + flex: 1 + }); + } + } +}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js index 1d6ee4f263a..ca6651244b8 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js @@ -33,7 +33,9 @@ qx.Class.define("osparc.dashboard.FoldersTree", { this.set({ openMode: "dbltap", decorator: "no-border", - font: "text-14" + font: "text-14", + showLeafs: true, + padding: -10, }); this.__initTree(); @@ -46,6 +48,7 @@ qx.Class.define("osparc.dashboard.FoldersTree", { statics: { getLoadingData: function() { return { + folderId: -1, label: "Loading...", children: [], icon: "@FontAwesome5Solid/circle-notch/12", @@ -62,18 +65,19 @@ qx.Class.define("osparc.dashboard.FoldersTree", { members: { __initTree: function() { + const that = this; this.setDelegate({ - createItem: () => new qx.ui.tree.VirtualTreeItem(), + createItem: () => new osparc.dashboard.FolderTreeItem(), bindItem: (c, item, id) => { c.bindDefaultProperties(item, id); c.bindProperty("folderId", "model", null, item, id); c.bindProperty("", "open", { - converter(value, model, source, target) { + converter(value, _, __, target) { const isOpen = target.isOpen(); if (isOpen && !value.getLoaded()) { value.setLoaded(true); value.getChildren().removeAll(); - this.__fetchChildren(value); + that.__fetchChildren(value); } return isOpen; } From 8a18da495c5b3a72f5a71334f9e2c40330000cbc Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Tue, 13 Aug 2024 17:48:21 +0200 Subject: [PATCH 47/67] adding order by to folders --- .../utils_folders.py | 30 ++++++++++++++++--- .../utils_ordering.py | 19 ++++++++++++ .../folders/_folders_api.py | 2 ++ .../folders/_folders_handlers.py | 7 +++-- 4 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 packages/postgres-database/src/simcore_postgres_database/utils_ordering.py diff --git a/packages/postgres-database/src/simcore_postgres_database/utils_folders.py b/packages/postgres-database/src/simcore_postgres_database/utils_folders.py index 57a51094b35..1f82a43cf32 100644 --- a/packages/postgres-database/src/simcore_postgres_database/utils_folders.py +++ b/packages/postgres-database/src/simcore_postgres_database/utils_folders.py @@ -10,6 +10,7 @@ import sqlalchemy as sa from aiopg.sa.connection import SAConnection from aiopg.sa.result import RowProxy +from models_library.rest_ordering import OrderDirection from psycopg2.errors import ForeignKeyViolation from pydantic import ( BaseModel, @@ -20,6 +21,7 @@ parse_obj_as, ) from pydantic.errors import PydanticErrorMixin +from simcore_postgres_database.utils_ordering import OrderByDict from sqlalchemy.dialects import postgresql from sqlalchemy.sql.elements import ColumnElement from sqlalchemy.sql.selectable import ScalarSelect @@ -986,6 +988,9 @@ async def folder_list( *, offset: NonNegativeInt, limit: NonNegativeInt, + order_by: OrderByDict = OrderByDict( + field="modified", direction=OrderDirection.DESC + ), _required_permissions=_requires(_BasePermissions.LIST_FOLDERS), # noqa: B008 ) -> list[FolderEntry]: """ @@ -1015,7 +1020,7 @@ async def folder_list( access_via_gid = resolved_access_rights.gid access_via_folder_id = resolved_access_rights.folder_id - query = ( + base_query = ( sa.select( folders, folders_access_rights, @@ -1047,11 +1052,25 @@ async def folder_list( if folder_id is None else True ) - .offset(offset) - .limit(limit) ) - async for entry in connection.execute(query): + # Select total count from base_query + subquery = base_query.subquery() + count_query = sa.select(sa.func.count()).select_from(subquery) + count_result = await connection.execute(count_query) + + # Ordering and pagination + if order_by["direction"] == OrderDirection.ASC: + list_query = base_query.order_by( + sa.asc(getattr(folders.c, order_by["field"])) + ) + else: + list_query = base_query.order_by( + sa.desc(getattr(folders.c, order_by["field"])) + ) + list_query = list_query.offset(offset).limit(limit) + + async for entry in connection.execute(list_query): results.append(FolderEntry.from_orm(entry)) # noqa: PERF401s return results @@ -1113,3 +1132,6 @@ async def folder_get( ) return FolderEntry.from_orm(query_result) + + +__all__ = ["OrderByDict"] diff --git a/packages/postgres-database/src/simcore_postgres_database/utils_ordering.py b/packages/postgres-database/src/simcore_postgres_database/utils_ordering.py new file mode 100644 index 00000000000..4791692bd63 --- /dev/null +++ b/packages/postgres-database/src/simcore_postgres_database/utils_ordering.py @@ -0,0 +1,19 @@ +from enum import Enum +from typing import TypedDict + + +class OrderDirection(str, Enum): + ASC = "asc" + DESC = "desc" + + +class OrderByDict(TypedDict): + field: str + direction: OrderDirection + + +# Example usage +order_by_example: OrderByDict = { + "field": "example_field", + "direction": OrderDirection.ASC, +} diff --git a/services/web/server/src/simcore_service_webserver/folders/_folders_api.py b/services/web/server/src/simcore_service_webserver/folders/_folders_api.py index 23d239f22c9..9fec63df096 100644 --- a/services/web/server/src/simcore_service_webserver/folders/_folders_api.py +++ b/services/web/server/src/simcore_service_webserver/folders/_folders_api.py @@ -1,6 +1,7 @@ # pylint: disable=unused-argument import logging +from typing import cast from aiohttp import web from aiopg.sa.engine import Engine @@ -120,6 +121,7 @@ async def list_folders( gid=user["primary_gid"], offset=offset, limit=limit, + order_by=cast(folders_db.OrderByDict, order_by.dict()), ) return [ FolderGet( diff --git a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py index cb1bb4b1cb0..f53dcfb89e3 100644 --- a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py +++ b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py @@ -68,8 +68,8 @@ class FoldersPathParams(StrictRequestParams): class FolderListWithJsonStrQueryParams(PageQueryParameters): order_by: Json[OrderBy] = Field( # pylint: disable=unsubscriptable-object - default=OrderBy(field="name", direction=OrderDirection.DESC), - description="Order by field (name|description) and direction (asc|desc). The default sorting order is ascending.", + default=OrderBy(field="modified", direction=OrderDirection.DESC), + description="Order by field (modified_at|name|description) and direction (asc|desc). The default sorting order is ascending.", example='{"field": "name", "direction": "desc"}', alias="order_by", ) @@ -82,10 +82,13 @@ class FolderListWithJsonStrQueryParams(PageQueryParameters): @classmethod def validate_order_by_field(cls, v): if v.field not in { + "modified_at", "name", "description", }: raise ValueError(f"We do not support ordering by provided field {v.field}") + if v.field == "modified_at": + v.field = "modified" return v class Config: From d0b19cdf7b7102f0c9bf79499b63c82212b6cc61 Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Wed, 14 Aug 2024 07:22:59 +0200 Subject: [PATCH 48/67] adding pagination to folders --- api/specs/web-server/_folders.py | 7 +- .../api_schemas_webserver/folders.py | 8 ++- .../utils_folders.py | 6 +- .../tests/test_utils_folders.py | 3 +- .../api/v0/openapi.yaml | 65 +++++++++++++------ .../folders/_folders_api.py | 50 +++++++------- .../folders/_folders_handlers.py | 21 +++++- 7 files changed, 108 insertions(+), 52 deletions(-) diff --git a/api/specs/web-server/_folders.py b/api/specs/web-server/_folders.py index e5cf2d6a0cb..41e4130f2c0 100644 --- a/api/specs/web-server/_folders.py +++ b/api/specs/web-server/_folders.py @@ -13,6 +13,7 @@ from models_library.api_schemas_webserver.folders import ( CreateFolderBodyParams, FolderGet, + FolderGetPage, PutFolderBodyParams, ) from models_library.generics import Envelope @@ -47,17 +48,17 @@ async def create_folder(_body: CreateFolderBodyParams): @router.get( "/folders", - response_model=Envelope[list[FolderGet]], + response_model=Envelope[FolderGetPage], ) async def list_folders( params: Annotated[PageQueryParameters, Depends()], order_by: Annotated[ Json, Query( - description="Order by field (name|description) and direction (asc|desc). The default sorting order is ascending.", + description="Order by field (modified_at|name|description) and direction (asc|desc). The default sorting order is ascending.", example='{"field": "name", "direction": "desc"}', ), - ] = '{"field": "name", "direction": "desc"}', + ] = '{"field": "modified_at", "direction": "desc"}', ): ... diff --git a/packages/models-library/src/models_library/api_schemas_webserver/folders.py b/packages/models-library/src/models_library/api_schemas_webserver/folders.py index 093659548df..e971b1f8c73 100644 --- a/packages/models-library/src/models_library/api_schemas_webserver/folders.py +++ b/packages/models-library/src/models_library/api_schemas_webserver/folders.py @@ -1,11 +1,12 @@ from datetime import datetime +from typing import NamedTuple from models_library.basic_types import IDStr from models_library.folders import FolderID from models_library.projects_access import AccessRights from models_library.users import GroupID from models_library.utils.common_validators import null_or_none_str_to_none_validator -from pydantic import Extra, validator +from pydantic import Extra, PositiveInt, validator from ._base import InputSchema, OutputSchema @@ -22,6 +23,11 @@ class FolderGet(OutputSchema): access_rights: dict[GroupID, AccessRights] +class FolderGetPage(NamedTuple): + items: list[FolderGet] + total: PositiveInt + + class CreateFolderBodyParams(InputSchema): name: IDStr description: str diff --git a/packages/postgres-database/src/simcore_postgres_database/utils_folders.py b/packages/postgres-database/src/simcore_postgres_database/utils_folders.py index 1f82a43cf32..f4e5bf6b22b 100644 --- a/packages/postgres-database/src/simcore_postgres_database/utils_folders.py +++ b/packages/postgres-database/src/simcore_postgres_database/utils_folders.py @@ -5,7 +5,7 @@ from datetime import datetime from enum import Enum from functools import reduce -from typing import Any, ClassVar, Final, TypeAlias +from typing import Any, ClassVar, Final, TypeAlias, cast import sqlalchemy as sa from aiopg.sa.connection import SAConnection @@ -992,7 +992,7 @@ async def folder_list( field="modified", direction=OrderDirection.DESC ), _required_permissions=_requires(_BasePermissions.LIST_FOLDERS), # noqa: B008 -) -> list[FolderEntry]: +) -> tuple[int, list[FolderEntry]]: """ Raises: FolderNotFoundError @@ -1073,7 +1073,7 @@ async def folder_list( async for entry in connection.execute(list_query): results.append(FolderEntry.from_orm(entry)) # noqa: PERF401s - return results + return cast(int, count_result.scalar()), results async def folder_get( diff --git a/packages/postgres-database/tests/test_utils_folders.py b/packages/postgres-database/tests/test_utils_folders.py index 2fdbd6bce8d..a72721982ca 100644 --- a/packages/postgres-database/tests/test_utils_folders.py +++ b/packages/postgres-database/tests/test_utils_folders.py @@ -1633,9 +1633,10 @@ async def _list_folder_as( limit: NonNegativeInt = ALL_IN_ONE_PAGE_LIMIT, ) -> list[FolderEntry]: - return await folder_list( + total_count, folders_db = await folder_list( connection, default_product_name, folder_id, gid, offset=offset, limit=limit ) + return folders_db async def test_folder_list( diff --git a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml index 2d3f2f6c97e..80936d3f2d5 100644 --- a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml +++ b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml @@ -1296,14 +1296,14 @@ paths: summary: List Folders operationId: list_folders parameters: - - description: Order by field (name|description) and direction (asc|desc). The - default sorting order is ascending. + - description: Order by field (modified_at|name|description) and direction (asc|desc). + The default sorting order is ascending. required: false schema: title: Order By - description: Order by field (name|description) and direction (asc|desc). - The default sorting order is ascending. - default: '{"field": "name", "direction": "desc"}' + description: Order by field (modified_at|name|description) and direction + (asc|desc). The default sorting order is ascending. + default: '{"field": "modified_at", "direction": "desc"}' example: '{"field": "name", "direction": "desc"}' name: order_by in: query @@ -1331,7 +1331,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Envelope_list_models_library.api_schemas_webserver.folders.FolderGet__' + $ref: '#/components/schemas/Envelope_FolderGetPage_' post: tags: - folders @@ -6004,8 +6004,8 @@ components: quality: {} accessRights: '1': - execute_access: true - write_access: false + execute: true + write: false key: simcore/services/comp/itis/sleeper version: 2.2.1 version_display: 2 Xtreme @@ -6835,6 +6835,26 @@ components: $ref: '#/components/schemas/FileUploadSchema' error: title: Error + Envelope_FolderGetPage_: + title: Envelope[FolderGetPage] + type: object + properties: + data: + title: Data + maxItems: 2 + minItems: 2 + type: array + items: + - title: Items + type: array + items: + $ref: '#/components/schemas/FolderGet' + - title: Total + exclusiveMinimum: true + type: integer + minimum: 0 + error: + title: Error Envelope_FolderGet_: title: Envelope[FolderGet] type: object @@ -7390,17 +7410,6 @@ components: $ref: '#/components/schemas/ClusterGet' error: title: Error - Envelope_list_models_library.api_schemas_webserver.folders.FolderGet__: - title: Envelope[list[models_library.api_schemas_webserver.folders.FolderGet]] - type: object - properties: - data: - title: Data - type: array - items: - $ref: '#/components/schemas/FolderGet' - error: - title: Error Envelope_list_models_library.api_schemas_webserver.groups.GroupUserGet__: title: Envelope[list[models_library.api_schemas_webserver.groups.GroupUserGet]] type: object @@ -8014,6 +8023,23 @@ components: type: object additionalProperties: $ref: '#/components/schemas/AccessRights' + FolderGetPage: + title: FolderGetPage + required: + - items + - total + type: object + properties: + items: + title: Items + type: array + items: + $ref: '#/components/schemas/FolderGet' + total: + title: Total + exclusiveMinimum: true + type: integer + minimum: 0 FolderGroupGet: title: FolderGroupGet required: @@ -10784,6 +10810,7 @@ components: title: Write type: boolean default: false + additionalProperties: false ServiceInputGet: title: ServiceInputGet required: diff --git a/services/web/server/src/simcore_service_webserver/folders/_folders_api.py b/services/web/server/src/simcore_service_webserver/folders/_folders_api.py index 9fec63df096..8c0b9de3b5c 100644 --- a/services/web/server/src/simcore_service_webserver/folders/_folders_api.py +++ b/services/web/server/src/simcore_service_webserver/folders/_folders_api.py @@ -5,7 +5,7 @@ from aiohttp import web from aiopg.sa.engine import Engine -from models_library.api_schemas_webserver.folders import FolderGet +from models_library.api_schemas_webserver.folders import FolderGet, FolderGetPage from models_library.folders import FolderID from models_library.products import ProductName from models_library.projects_access import AccessRights @@ -108,13 +108,13 @@ async def list_folders( offset: NonNegativeInt, limit: int, order_by: OrderBy, -) -> list[FolderGet]: +) -> FolderGetPage: user = await get_user(app, user_id=user_id) engine: Engine = app[APP_DB_ENGINE_KEY] async with engine.acquire() as connection: # NOTE: folder permissions are checked inside the function - folder_list_db: list[folders_db.FolderEntry] = await folders_db.folder_list( + total_count, folder_list_db = await folders_db.folder_list( connection, product_name=product_name, folder_id=folder_id, @@ -123,25 +123,31 @@ async def list_folders( limit=limit, order_by=cast(folders_db.OrderByDict, order_by.dict()), ) - return [ - FolderGet( - folder_id=folder.id, - parent_folder_id=folder.parent_folder, - name=folder.name, - description=folder.description, - created_at=folder.created, - modified_at=folder.modified, - owner=folder.owner, - my_access_rights=parse_obj_as( - AccessRights, folder.my_access_rights.to_dict() - ), - access_rights=parse_obj_as( - dict[GroupID, AccessRights], - {key: value.to_dict() for key, value in folder.access_rights.items()}, - ), - ) - for folder in folder_list_db - ] + return FolderGetPage( + items=[ + FolderGet( + folder_id=folder.id, + parent_folder_id=folder.parent_folder, + name=folder.name, + description=folder.description, + created_at=folder.created, + modified_at=folder.modified, + owner=folder.owner, + my_access_rights=parse_obj_as( + AccessRights, folder.my_access_rights.to_dict() + ), + access_rights=parse_obj_as( + dict[GroupID, AccessRights], + { + key: value.to_dict() + for key, value in folder.access_rights.items() + }, + ), + ) + for folder in folder_list_db + ], + total=total_count, + ) async def update_folder( diff --git a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py index f53dcfb89e3..9737f74d065 100644 --- a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py +++ b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py @@ -5,11 +5,13 @@ from models_library.api_schemas_webserver.folders import ( CreateFolderBodyParams, FolderGet, + FolderGetPage, PutFolderBodyParams, ) from models_library.folders import FolderID from models_library.rest_ordering import OrderBy, OrderDirection -from models_library.rest_pagination import PageQueryParameters +from models_library.rest_pagination import Page, PageQueryParameters +from models_library.rest_pagination_utils import paginate_data from models_library.users import UserID from models_library.utils.common_validators import null_or_none_str_to_none_validator from pydantic import Extra, Field, Json, parse_obj_as, validator @@ -23,6 +25,7 @@ from servicelib.aiohttp.typing_extension import Handler from servicelib.mimetype_constants import MIMETYPE_APPLICATION_JSON from servicelib.request_keys import RQT_USERID_KEY +from servicelib.rest_constants import RESPONSE_MODEL_POLICY from .._constants import RQ_PRODUCT_KEY from .._meta import API_VTAG as VTAG @@ -130,7 +133,7 @@ async def list_folders(request: web.Request): FolderListWithJsonStrQueryParams, request ) - folders: list[FolderGet] = await _folders_api.list_folders( + folders: FolderGetPage = await _folders_api.list_folders( app=request.app, user_id=req_ctx.user_id, product_name=req_ctx.product_name, @@ -140,7 +143,19 @@ async def list_folders(request: web.Request): order_by=parse_obj_as(OrderBy, query_params.order_by), ) - return envelope_json_response(folders) + page = Page[dict[str, Any]].parse_obj( + paginate_data( + chunk=folders.items, + request_url=request.url, + total=folders.total, + limit=query_params.limit, + offset=query_params.offset, + ) + ) + return web.Response( + text=page.json(**RESPONSE_MODEL_POLICY), + content_type=MIMETYPE_APPLICATION_JSON, + ) @routes.get(f"/{VTAG}/folders/{{folder_id}}", name="get_folder") From 402548d79d320b0cec79385991c14ca6e15b0be9 Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Wed, 14 Aug 2024 07:56:18 +0200 Subject: [PATCH 49/67] adding pagination tests --- .../src/simcore_postgres_database/utils_folders.py | 3 ++- .../simcore_service_webserver/folders/_folders_handlers.py | 1 + .../server/tests/unit/with_dbs/03/folders/test_folders.py | 6 +++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/postgres-database/src/simcore_postgres_database/utils_folders.py b/packages/postgres-database/src/simcore_postgres_database/utils_folders.py index f4e5bf6b22b..465fade18d8 100644 --- a/packages/postgres-database/src/simcore_postgres_database/utils_folders.py +++ b/packages/postgres-database/src/simcore_postgres_database/utils_folders.py @@ -1058,6 +1058,7 @@ async def folder_list( subquery = base_query.subquery() count_query = sa.select(sa.func.count()).select_from(subquery) count_result = await connection.execute(count_query) + total_count = await count_result.scalar() # Ordering and pagination if order_by["direction"] == OrderDirection.ASC: @@ -1073,7 +1074,7 @@ async def folder_list( async for entry in connection.execute(list_query): results.append(FolderEntry.from_orm(entry)) # noqa: PERF401s - return cast(int, count_result.scalar()), results + return cast(int, total_count), results async def folder_get( diff --git a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py index 9737f74d065..6998b75745d 100644 --- a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py +++ b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py @@ -1,5 +1,6 @@ import functools import logging +from typing import Any from aiohttp import web from models_library.api_schemas_webserver.folders import ( diff --git a/services/web/server/tests/unit/with_dbs/03/folders/test_folders.py b/services/web/server/tests/unit/with_dbs/03/folders/test_folders.py index 800951027e5..5667ac3a769 100644 --- a/services/web/server/tests/unit/with_dbs/03/folders/test_folders.py +++ b/services/web/server/tests/unit/with_dbs/03/folders/test_folders.py @@ -41,10 +41,14 @@ async def test_folders_full_workflow( # list user folders url = client.app.router["list_folders"].url_for() resp = await client.get(url.path) - data, _ = await assert_status(resp, status.HTTP_200_OK) + data, _, meta, links = await assert_status( + resp, status.HTTP_200_OK, include_meta=True, include_links=True + ) assert len(data) == 1 assert data[0]["name"] == "My first folder" assert data[0]["description"] == "Custom description" + assert meta["count"] == 1 + assert links # update a folder url = client.app.router["replace_folder"].url_for( From e8e2a1f68fb62db39e25f6f621b9ed6c9b40f084 Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Wed, 14 Aug 2024 08:00:06 +0200 Subject: [PATCH 50/67] add filter by product name --- .../src/simcore_postgres_database/utils_folders.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/postgres-database/src/simcore_postgres_database/utils_folders.py b/packages/postgres-database/src/simcore_postgres_database/utils_folders.py index 465fade18d8..2ee24e410c3 100644 --- a/packages/postgres-database/src/simcore_postgres_database/utils_folders.py +++ b/packages/postgres-database/src/simcore_postgres_database/utils_folders.py @@ -1052,6 +1052,7 @@ async def folder_list( if folder_id is None else True ) + .where(folders.c.product_name == product_name) ) # Select total count from base_query @@ -1121,6 +1122,7 @@ async def folder_get( if folder_id is None else True ) + .where(folders.c.product_name == product_name) ) query_result: RowProxy | None = await ( From 0fa36a8bf4cb09f57031f3d65b1e8ee3cd9ae545 Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Wed, 14 Aug 2024 08:33:52 +0200 Subject: [PATCH 51/67] add WEBSERVER_FOLDERS env var --- .env-devel | 1 + services/docker-compose.yml | 1 + .../02/test_projects_crud_handlers__list_with_query_params.py | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.env-devel b/.env-devel index 27eeadc24ab..2a10c695ca1 100644 --- a/.env-devel +++ b/.env-devel @@ -318,6 +318,7 @@ WEBSERVER_DIAGNOSTICS={} WEBSERVER_EMAIL={} WEBSERVER_EXPORTER={} WEBSERVER_FRONTEND={} +WEBSERVER_FOLDERS=1 WEBSERVER_GARBAGE_COLLECTOR=null WEBSERVER_GROUPS=1 WEBSERVER_GUNICORN_CMD_ARGS=--timeout=180 diff --git a/services/docker-compose.yml b/services/docker-compose.yml index 4b8c95d4703..94edbb1f19d 100644 --- a/services/docker-compose.yml +++ b/services/docker-compose.yml @@ -725,6 +725,7 @@ services: WEBSERVER_TAGS: ${WEBSERVER_TAGS} WEBSERVER_USERS: ${WEBSERVER_USERS} WEBSERVER_VERSION_CONTROL: ${WEBSERVER_VERSION_CONTROL} + WEBSERVER_FOLDERS: ${WEBSERVER_FOLDERS} deploy: labels: diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py index 6684d3ec96d..b5d65c632b0 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py @@ -486,7 +486,7 @@ async def test_list_projects_for_specific_folder_id( def app_environment( app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatch ) -> EnvVarsDict: - # disable the garbage collector + # disable the webserver folders plugin monkeypatch.setenv("WEBSERVER_FOLDERS", "0") return app_environment | {"WEBSERVER_FOLDERS": "0"} From ba2efa4d41f16f6506bb42676a87d1f5ed0fba34 Mon Sep 17 00:00:00 2001 From: matusdrobuliak66 Date: Wed, 14 Aug 2024 09:06:51 +0200 Subject: [PATCH 52/67] fix test --- api/specs/web-server/_folders.py | 3 +- .../utils_folders.py | 2 +- .../api/v0/openapi.yaml | 50 +++++-------------- 3 files changed, 14 insertions(+), 41 deletions(-) diff --git a/api/specs/web-server/_folders.py b/api/specs/web-server/_folders.py index 41e4130f2c0..db5ca1416f7 100644 --- a/api/specs/web-server/_folders.py +++ b/api/specs/web-server/_folders.py @@ -13,7 +13,6 @@ from models_library.api_schemas_webserver.folders import ( CreateFolderBodyParams, FolderGet, - FolderGetPage, PutFolderBodyParams, ) from models_library.generics import Envelope @@ -48,7 +47,7 @@ async def create_folder(_body: CreateFolderBodyParams): @router.get( "/folders", - response_model=Envelope[FolderGetPage], + response_model=Envelope[list[FolderGet]], ) async def list_folders( params: Annotated[PageQueryParameters, Depends()], diff --git a/packages/postgres-database/src/simcore_postgres_database/utils_folders.py b/packages/postgres-database/src/simcore_postgres_database/utils_folders.py index 2ee24e410c3..4437a2e9353 100644 --- a/packages/postgres-database/src/simcore_postgres_database/utils_folders.py +++ b/packages/postgres-database/src/simcore_postgres_database/utils_folders.py @@ -10,7 +10,6 @@ import sqlalchemy as sa from aiopg.sa.connection import SAConnection from aiopg.sa.result import RowProxy -from models_library.rest_ordering import OrderDirection from psycopg2.errors import ForeignKeyViolation from pydantic import ( BaseModel, @@ -28,6 +27,7 @@ from .models.folders import folders, folders_access_rights, folders_to_projects from .models.groups import GroupType, groups +from .utils_ordering import OrderDirection _ProductName: TypeAlias = str _ProjectID: TypeAlias = uuid.UUID diff --git a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml index 80936d3f2d5..f8d1f57dfd1 100644 --- a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml +++ b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml @@ -1331,7 +1331,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Envelope_FolderGetPage_' + $ref: '#/components/schemas/Envelope_list_models_library.api_schemas_webserver.folders.FolderGet__' post: tags: - folders @@ -6835,26 +6835,6 @@ components: $ref: '#/components/schemas/FileUploadSchema' error: title: Error - Envelope_FolderGetPage_: - title: Envelope[FolderGetPage] - type: object - properties: - data: - title: Data - maxItems: 2 - minItems: 2 - type: array - items: - - title: Items - type: array - items: - $ref: '#/components/schemas/FolderGet' - - title: Total - exclusiveMinimum: true - type: integer - minimum: 0 - error: - title: Error Envelope_FolderGet_: title: Envelope[FolderGet] type: object @@ -7410,6 +7390,17 @@ components: $ref: '#/components/schemas/ClusterGet' error: title: Error + Envelope_list_models_library.api_schemas_webserver.folders.FolderGet__: + title: Envelope[list[models_library.api_schemas_webserver.folders.FolderGet]] + type: object + properties: + data: + title: Data + type: array + items: + $ref: '#/components/schemas/FolderGet' + error: + title: Error Envelope_list_models_library.api_schemas_webserver.groups.GroupUserGet__: title: Envelope[list[models_library.api_schemas_webserver.groups.GroupUserGet]] type: object @@ -8023,23 +8014,6 @@ components: type: object additionalProperties: $ref: '#/components/schemas/AccessRights' - FolderGetPage: - title: FolderGetPage - required: - - items - - total - type: object - properties: - items: - title: Items - type: array - items: - $ref: '#/components/schemas/FolderGet' - total: - title: Total - exclusiveMinimum: true - type: integer - minimum: 0 FolderGroupGet: title: FolderGroupGet required: From a441f6681dd62723ae37ed21160b6686dad5e69a Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Wed, 14 Aug 2024 09:29:37 +0200 Subject: [PATCH 53/67] minors --- .../client/source/class/osparc/dashboard/FolderTreeItem.js | 4 ++-- .../client/source/class/osparc/dashboard/FoldersTree.js | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js index 3d5e0bf3084..c43ecf4fcaf 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js @@ -24,10 +24,10 @@ qx.Class.define("osparc.dashboard.FolderTreeItem", { // this.addOpenButton(); const openButton = this.getChildControl("open"); openButton.addListener("changeOpen", () => { - console.log("changeOpen"); + console.log("changeOpen", this); }, this); openButton.addListener("changeVisibility", e => { - console.log("changeVisibility", e.getData()); + console.log("changeVisibility", e.getData(), this); openButton.show(); }, this); this._add(openButton); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js index ca6651244b8..365ed460988 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js @@ -77,6 +77,7 @@ qx.Class.define("osparc.dashboard.FoldersTree", { if (isOpen && !value.getLoaded()) { value.setLoaded(true); value.getChildren().removeAll(); + // eslint-disable-next-line no-underscore-dangle that.__fetchChildren(value); } return isOpen; From cf2cd9e7c0260f8d51d0dc54accd38af375b4528 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Wed, 14 Aug 2024 09:36:41 +0200 Subject: [PATCH 54/67] add todos --- .../client/source/class/osparc/dashboard/StudyBrowser.js | 4 ++++ .../client/source/class/osparc/store/Folders.js | 2 ++ 2 files changed, 6 insertions(+) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 7185700a99f..8d749a31243 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -315,6 +315,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this.__foldersList = folders; folders.forEach(folder => folder["resourceType"] = "folder"); + // OM: MD will fix this const sortByValueBE = this.getOrderBy().field; let sortByValue = null; switch (sortByValueBE) { @@ -514,6 +515,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { return osparc.data.Resources.fetch("studies", "getPageFolder", params, undefined, options); } + // OM: MD will fix this if (params.url.orderBy) { return osparc.data.Resources.fetch("studies", "getPageSortBySearch", params, undefined, options); } else if (params.url.search) { @@ -545,6 +547,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { return osparc.data.Resources.fetch("studies", "getPageFilterSearchFolder", params, undefined, options); } + // OM: MD will fix this return osparc.data.Resources.fetch("studies", "getPageFilterSearch", params, undefined, options); }, @@ -571,6 +574,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { return osparc.data.Resources.fetch("studies", "getPageSortBySearchFolder", params, undefined, options); } + // OM: MD will fix this return osparc.data.Resources.fetch("studies", "getPageSortBySearch", params, undefined, options); }, diff --git a/services/static-webserver/client/source/class/osparc/store/Folders.js b/services/static-webserver/client/source/class/osparc/store/Folders.js index 2dfe0acfe0e..90d0fc6450d 100644 --- a/services/static-webserver/client/source/class/osparc/store/Folders.js +++ b/services/static-webserver/client/source/class/osparc/store/Folders.js @@ -37,6 +37,7 @@ qx.Class.define("osparc.store.Folders", { folderId } }; + // OM: MD will fix this // osparc.data.Resources.getInstance().getAllPages("folders", params) promise = osparc.data.Resources.getInstance().fetch("folders", "getWithinFolder", params); } else { @@ -67,6 +68,7 @@ qx.Class.define("osparc.store.Folders", { }; osparc.data.Resources.getInstance().fetch("folders", "post", params) .then(resp => { + // OM: MD will fix this const foldersStore = osparc.store.Folders.getInstance(); const folderId = resp["folderId"]; foldersStore.fetchFolders(parentId) From 05dd37285a1cce75f639a580c4877afb033e6865 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Wed, 14 Aug 2024 10:26:42 +0200 Subject: [PATCH 55/67] owner property --- .../client/source/class/osparc/data/model/Folder.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/services/static-webserver/client/source/class/osparc/data/model/Folder.js b/services/static-webserver/client/source/class/osparc/data/model/Folder.js index 2d66252814b..9b8f7bcc630 100644 --- a/services/static-webserver/client/source/class/osparc/data/model/Folder.js +++ b/services/static-webserver/client/source/class/osparc/data/model/Folder.js @@ -35,6 +35,7 @@ qx.Class.define("osparc.data.model.Folder", { description: folderData.description, myAccessRights: folderData.myAccessRights, accessRights: folderData.accessRights, + owner: folderData.owner, createdAt: new Date(folderData.createdAt), lastModified: new Date(folderData.modifiedAt), }); @@ -83,6 +84,13 @@ qx.Class.define("osparc.data.model.Folder", { event: "changeAccessRights" }, + owner: { + check: "Number", + nullable: true, + init: null, + event: "changeOwner" + }, + createdAt: { check: "Date", nullable: true, From 5a2acf2e81d27fb0f77e63071a5becf208edd848 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Wed, 14 Aug 2024 10:32:16 +0200 Subject: [PATCH 56/67] paginated getFOlders response --- .../source/class/osparc/data/Resources.js | 8 -------- .../source/class/osparc/store/Folders.js | 20 ++++++------------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/data/Resources.js b/services/static-webserver/client/source/class/osparc/data/Resources.js index 2936ac1be71..ca34673ad0d 100644 --- a/services/static-webserver/client/source/class/osparc/data/Resources.js +++ b/services/static-webserver/client/source/class/osparc/data/Resources.js @@ -319,14 +319,6 @@ qx.Class.define("osparc.data.Resources", { method: "GET", url: statics.API + "/folders?folder_id={folderId}&offset={offset}&limit={limit}" }, - getRootFolders: { - method: "GET", - url: statics.API + "/folders" - }, - getWithinFolder: { - method: "GET", - url: statics.API + "/folders?folder_id={folderId}" - }, getOne: { method: "GET", url: statics.API + "/folders/{folderId}" diff --git a/services/static-webserver/client/source/class/osparc/store/Folders.js b/services/static-webserver/client/source/class/osparc/store/Folders.js index 90d0fc6450d..83524e9dbc7 100644 --- a/services/static-webserver/client/source/class/osparc/store/Folders.js +++ b/services/static-webserver/client/source/class/osparc/store/Folders.js @@ -30,20 +30,12 @@ qx.Class.define("osparc.store.Folders", { fetchFolders: function(folderId = null) { return new Promise(resolve => { - let promise = null; - if (folderId) { - const params = { - "url": { - folderId - } - }; - // OM: MD will fix this - // osparc.data.Resources.getInstance().getAllPages("folders", params) - promise = osparc.data.Resources.getInstance().fetch("folders", "getWithinFolder", params); - } else { - promise = osparc.data.Resources.getInstance().fetch("folders", "getRootFolders"); - } - promise + const params = { + "url": { + folderId + } + }; + osparc.data.Resources.getInstance().getAllPages("folders", params) .then(foldersData => { const folders = []; foldersData.forEach(folderData => { From a9f14cb0b5a41447bf6bb1b71f9d7a18d78b4a5b Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Wed, 14 Aug 2024 15:48:54 +0200 Subject: [PATCH 57/67] bad merge --- .../api/v0/openapi.yaml | 2 +- .../folders/_folders_handlers.py | 1 - ...s_crud_handlers__list_with_query_params.py | 40 +------------------ 3 files changed, 2 insertions(+), 41 deletions(-) diff --git a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml index f8d1f57dfd1..65586d41d10 100644 --- a/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml +++ b/services/web/server/src/simcore_service_webserver/api/v0/openapi.yaml @@ -3489,7 +3489,7 @@ paths: '403': description: ProjectInvalidRightsError '404': - description: ProjectNotFoundError, UserDefaultWalletNotFoundError + description: UserDefaultWalletNotFoundError, ProjectNotFoundError '409': description: ProjectTooManyProjectOpenedError '422': diff --git a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py index fdfb62e44c2..2601c6c65bf 100644 --- a/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py +++ b/services/web/server/src/simcore_service_webserver/folders/_folders_handlers.py @@ -1,6 +1,5 @@ import functools import logging -from typing import Any from aiohttp import web from models_library.api_schemas_webserver.folders import ( diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py index b5d65c632b0..f35f15ad6b2 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py @@ -19,7 +19,6 @@ from models_library.users import UserID from pydantic import BaseModel, PositiveInt from pytest_mock import MockerFixture -from pytest_simcore.helpers.typing_env import EnvVarsDict from pytest_simcore.helpers.webserver_login import UserInfoDict from pytest_simcore.helpers.webserver_parametrizations import ( ExpectedResponse, @@ -479,41 +478,4 @@ async def test_list_projects_for_specific_folder_id( assert resp.status == 200 _assert_response_data( data, 1, 0, 1, f"/v0/projects?folder_id={setup_folders_db}&offset=0&limit=20", 1 - ) - - -@pytest.fixture -def app_environment( - app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatch -) -> EnvVarsDict: - # disable the webserver folders plugin - monkeypatch.setenv("WEBSERVER_FOLDERS", "0") - return app_environment | {"WEBSERVER_FOLDERS": "0"} - - -@pytest.mark.parametrize(*standard_user_role()) -async def test_list_projects_with_disabled_project_folders_plugin( - client: TestClient, - app_environment: EnvVarsDict, - logged_user: UserDict, - expected: ExpectedResponse, - fake_project: ProjectDict, - tests_data_dir: Path, - osparc_product_name: str, - project_db_cleaner, - mock_catalog_api_get_services_for_user_in_product, - setup_folders_db, -): - """ - As the WEBSERVER_FOLDERS plugin is turned off, the project listing - should behave the same way as before, and therefore list all the projects - in the root directory, essentially ignoring the folders_to_projects table. - """ - base_url = client.app.router["list_projects"].url_for() - assert f"{base_url}" == f"/{api_version_prefix}/projects" - - resp = await client.get(base_url) - data = await resp.json() - - assert resp.status == 200 - _assert_response_data(data, 1, 0, 1, "/v0/projects?offset=0&limit=20", 1) + ) \ No newline at end of file From 2bde9533cab4d0c180f9441b90d7af5cf9c24f35 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Wed, 14 Aug 2024 15:49:34 +0200 Subject: [PATCH 58/67] minor --- .../02/test_projects_crud_handlers__list_with_query_params.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py index f35f15ad6b2..146cde3a87d 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py @@ -478,4 +478,5 @@ async def test_list_projects_for_specific_folder_id( assert resp.status == 200 _assert_response_data( data, 1, 0, 1, f"/v0/projects?folder_id={setup_folders_db}&offset=0&limit=20", 1 - ) \ No newline at end of file + ) + \ No newline at end of file From bd8d83b01507c9df107e54af021445fb505f6536 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Wed, 14 Aug 2024 15:51:48 +0200 Subject: [PATCH 59/67] minor --- .../02/test_projects_crud_handlers__list_with_query_params.py | 1 - 1 file changed, 1 deletion(-) diff --git a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py index 146cde3a87d..b93d33ef94b 100644 --- a/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py +++ b/services/web/server/tests/unit/with_dbs/02/test_projects_crud_handlers__list_with_query_params.py @@ -479,4 +479,3 @@ async def test_list_projects_for_specific_folder_id( _assert_response_data( data, 1, 0, 1, f"/v0/projects?folder_id={setup_folders_db}&offset=0&limit=20", 1 ) - \ No newline at end of file From 71ee9483c722a31d878f1440b90c429d47fd2d81 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Wed, 14 Aug 2024 16:03:40 +0200 Subject: [PATCH 60/67] postFolder and catch --- .../class/osparc/dashboard/FoldersTree.js | 3 +- .../class/osparc/dashboard/StudyBrowser.js | 3 +- .../source/class/osparc/store/Folders.js | 67 ++++++++----------- 3 files changed, 33 insertions(+), 40 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js index 365ed460988..baa71392142 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js @@ -119,7 +119,8 @@ qx.Class.define("osparc.dashboard.FoldersTree", { this.self().addLoadingChild(folderModel); parentModel.getChildren().append(folderModel); }); - }); + }) + .catch(console.error); } } }); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 8d749a31243..82253c31b19 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -396,7 +396,8 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this.invalidateStudies(); this.__reloadResources(); - }); + }) + .catch(console.error); }, _folderUpdated: function() { diff --git a/services/static-webserver/client/source/class/osparc/store/Folders.js b/services/static-webserver/client/source/class/osparc/store/Folders.js index 83524e9dbc7..cd942578047 100644 --- a/services/static-webserver/client/source/class/osparc/store/Folders.js +++ b/services/static-webserver/client/source/class/osparc/store/Folders.js @@ -29,47 +29,38 @@ qx.Class.define("osparc.store.Folders", { foldersCached: null, fetchFolders: function(folderId = null) { - return new Promise(resolve => { - const params = { - "url": { - folderId - } - }; - osparc.data.Resources.getInstance().getAllPages("folders", params) - .then(foldersData => { - const folders = []; - foldersData.forEach(folderData => { - const folder = new osparc.data.model.Folder(folderData); - this.__addToCache(folder); - folders.push(folder); - }); - resolve(folders); - }) - }); + const params = { + "url": { + folderId + } + }; + return osparc.data.Resources.getInstance().getAllPages("folders", params) + .then(foldersData => { + const folders = []; + foldersData.forEach(folderData => { + const folder = new osparc.data.model.Folder(folderData); + this.__addToCache(folder); + folders.push(folder); + }); + return folders; + }); }, postFolder: function(name, description, parentId = null) { - return new Promise(resolve => { - const newFolderData = { - parentFolderId: parentId, - name: name, - description: description || "", - }; - const params = { - data: newFolderData - }; - osparc.data.Resources.getInstance().fetch("folders", "post", params) - .then(resp => { - // OM: MD will fix this - const foldersStore = osparc.store.Folders.getInstance(); - const folderId = resp["folderId"]; - foldersStore.fetchFolders(parentId) - .then(() => { - const newFolder = foldersStore.getFolder(folderId); - resolve(newFolder); - }); - }); - }); + const newFolderData = { + parentFolderId: parentId, + name: name, + description: description || "", + }; + const params = { + data: newFolderData + }; + return osparc.data.Resources.getInstance().fetch("folders", "post", params) + .then(folderData => { + const newFolder = new osparc.data.model.Folder(folderData); + this.__addToCache(newFolder); + return newFolder; + }); }, deleteFolder: function(folderId) { From a63c863ae331f6c2c4b8e500792400fa83342f7c Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Wed, 14 Aug 2024 16:22:05 +0200 Subject: [PATCH 61/67] minor --- .../client/source/class/osparc/dashboard/StudyBrowser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 82253c31b19..c9a4ac3b5e4 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -323,7 +323,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { sortByValue = "name"; break; case "owner": - sortByValue = "prjOwner"; + sortByValue = "owner"; break; case "creation_date": sortByValue = "createdAt"; From 9a87eca86b7880b01b225c9aca7b32baeffdeca4 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Wed, 14 Aug 2024 16:22:39 +0200 Subject: [PATCH 62/67] folderId can be null --- .../class/osparc/dashboard/StudyBrowser.js | 36 +++++-------------- .../source/class/osparc/data/Resources.js | 14 -------- 2 files changed, 8 insertions(+), 42 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index c9a4ac3b5e4..b6a60c9604b 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -506,23 +506,13 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }; const currentFolderId = this.getCurrentFolderId(); - if (currentFolderId) { - params.url.folderId = currentFolderId; - if (params.url.orderBy) { - return osparc.data.Resources.fetch("studies", "getPageSortBySearchFolder", params, undefined, options); - } else if (params.url.search) { - return osparc.data.Resources.fetch("studies", "getPageFilterSearchFolder", params, undefined, options); - } - return osparc.data.Resources.fetch("studies", "getPageFolder", params, undefined, options); - } - - // OM: MD will fix this + params.url.folderId = currentFolderId; if (params.url.orderBy) { - return osparc.data.Resources.fetch("studies", "getPageSortBySearch", params, undefined, options); + return osparc.data.Resources.fetch("studies", "getPageSortBySearchFolder", params, undefined, options); } else if (params.url.search) { - return osparc.data.Resources.fetch("studies", "getPageFilterSearch", params, undefined, options); + return osparc.data.Resources.fetch("studies", "getPageFilterSearchFolder", params, undefined, options); } - return osparc.data.Resources.fetch("studies", "getPage", params, undefined, options); + return osparc.data.Resources.fetch("studies", "getPageFolder", params, undefined, options); }, __getTextFilteredNextRequest: function(text) { @@ -543,13 +533,8 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }; const currentFolderId = this.getCurrentFolderId(); - if (currentFolderId) { - params.url.folderId = currentFolderId; - return osparc.data.Resources.fetch("studies", "getPageFilterSearchFolder", params, undefined, options); - } - - // OM: MD will fix this - return osparc.data.Resources.fetch("studies", "getPageFilterSearch", params, undefined, options); + params.url.folderId = currentFolderId; + return osparc.data.Resources.fetch("studies", "getPageFilterSearchFolder", params, undefined, options); }, __getSortedByNextRequest: function() { @@ -570,13 +555,8 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }; const currentFolderId = this.getCurrentFolderId(); - if (currentFolderId) { - params.url.folderId = currentFolderId; - return osparc.data.Resources.fetch("studies", "getPageSortBySearchFolder", params, undefined, options); - } - - // OM: MD will fix this - return osparc.data.Resources.fetch("studies", "getPageSortBySearch", params, undefined, options); + params.url.folderId = currentFolderId; + return osparc.data.Resources.fetch("studies", "getPageSortBySearchFolder", params, undefined, options); }, invalidateStudies: function() { diff --git a/services/static-webserver/client/source/class/osparc/data/Resources.js b/services/static-webserver/client/source/class/osparc/data/Resources.js index ca34673ad0d..360646dc698 100644 --- a/services/static-webserver/client/source/class/osparc/data/Resources.js +++ b/services/static-webserver/client/source/class/osparc/data/Resources.js @@ -119,20 +119,6 @@ qx.Class.define("osparc.data.Resources", { method: "GET", url: statics.API + "/projects?type=user" }, - getPage: { - method: "GET", - url: statics.API + "/projects?type=user&offset={offset}&limit={limit}" - }, - getPageFilterSearch: { - useCache: false, - method: "GET", - url: statics.API + "/projects?type=user&offset={offset}&limit={limit}&search={text}" - }, - getPageSortBySearch: { - useCache: false, - method: "GET", - url: statics.API + "/projects?type=user&offset={offset}&limit={limit}&order_by={orderBy}" - }, getPageFolder: { method: "GET", url: statics.API + "/projects?type=user&offset={offset}&limit={limit}&folder_id={folderId}" From 27398f4d1ae2f81d570be0d6acbfc506162e93b2 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Wed, 14 Aug 2024 16:41:27 +0200 Subject: [PATCH 63/67] minor --- .../client/source/class/osparc/dashboard/StudyBrowser.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index b6a60c9604b..0ded589f386 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -315,14 +315,13 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this.__foldersList = folders; folders.forEach(folder => folder["resourceType"] = "folder"); - // OM: MD will fix this const sortByValueBE = this.getOrderBy().field; let sortByValue = null; switch (sortByValueBE) { case "name": sortByValue = "name"; break; - case "owner": + case "prj_owner": sortByValue = "owner"; break; case "creation_date": From ceb28446a982970750ba230466c8fad789a51536 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Wed, 14 Aug 2024 16:56:32 +0200 Subject: [PATCH 64/67] renaming --- .../client/source/class/osparc/dashboard/StudyBrowser.js | 8 ++++---- .../client/source/class/osparc/data/Resources.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 0ded589f386..ce42e27e10d 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -507,9 +507,9 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const currentFolderId = this.getCurrentFolderId(); params.url.folderId = currentFolderId; if (params.url.orderBy) { - return osparc.data.Resources.fetch("studies", "getPageSortBySearchFolder", params, undefined, options); + return osparc.data.Resources.fetch("studies", "getPageFolderSortBy", params, undefined, options); } else if (params.url.search) { - return osparc.data.Resources.fetch("studies", "getPageFilterSearchFolder", params, undefined, options); + return osparc.data.Resources.fetch("studies", "getPageFolderSearch", params, undefined, options); } return osparc.data.Resources.fetch("studies", "getPageFolder", params, undefined, options); }, @@ -533,7 +533,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const currentFolderId = this.getCurrentFolderId(); params.url.folderId = currentFolderId; - return osparc.data.Resources.fetch("studies", "getPageFilterSearchFolder", params, undefined, options); + return osparc.data.Resources.fetch("studies", "getPageFolderSearch", params, undefined, options); }, __getSortedByNextRequest: function() { @@ -555,7 +555,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const currentFolderId = this.getCurrentFolderId(); params.url.folderId = currentFolderId; - return osparc.data.Resources.fetch("studies", "getPageSortBySearchFolder", params, undefined, options); + return osparc.data.Resources.fetch("studies", "getPageFolderSortBy", params, undefined, options); }, invalidateStudies: function() { diff --git a/services/static-webserver/client/source/class/osparc/data/Resources.js b/services/static-webserver/client/source/class/osparc/data/Resources.js index 360646dc698..749e445cdbb 100644 --- a/services/static-webserver/client/source/class/osparc/data/Resources.js +++ b/services/static-webserver/client/source/class/osparc/data/Resources.js @@ -123,15 +123,15 @@ qx.Class.define("osparc.data.Resources", { method: "GET", url: statics.API + "/projects?type=user&offset={offset}&limit={limit}&folder_id={folderId}" }, - getPageFilterSearchFolder: { + getPageFolderSearch: { useCache: false, method: "GET", - url: statics.API + "/projects?type=user&offset={offset}&limit={limit}&search={text}&folder_id={folderId}" + url: statics.API + "/projects?type=user&offset={offset}&limit={limit}&folder_id={folderId}&search={text}" }, - getPageSortBySearchFolder: { + getPageFolderSortBy: { useCache: false, method: "GET", - url: statics.API + "/projects?type=user&offset={offset}&limit={limit}&order_by={orderBy}&folder_id={folderId}" + url: statics.API + "/projects?type=user&offset={offset}&limit={limit}&folder_id={folderId}&order_by={orderBy}" }, getOne: { useCache: false, From b85d54198a401c06777c2a9463279a81438b71b2 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Wed, 14 Aug 2024 17:24:36 +0200 Subject: [PATCH 65/67] minor --- .../client/source/class/osparc/dashboard/FolderTreeItem.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js index c43ecf4fcaf..15c379f98a7 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js @@ -27,8 +27,8 @@ qx.Class.define("osparc.dashboard.FolderTreeItem", { console.log("changeOpen", this); }, this); openButton.addListener("changeVisibility", e => { - console.log("changeVisibility", e.getData(), this); - openButton.show(); + console.log("changeVisibility", this.getLabel() + e.getData(), this); + // openButton.show(); }, this); this._add(openButton); this.addIcon(); From 579092797e69e25a1e0f932dde4388dd9fd3bdb0 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Wed, 14 Aug 2024 17:42:47 +0200 Subject: [PATCH 66/67] refactoring --- .../class/osparc/dashboard/FolderTreeItem.js | 4 +- .../class/osparc/dashboard/FoldersTree.js | 42 +++++++++++-------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js index 15c379f98a7..d3873d37b08 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderTreeItem.js @@ -27,8 +27,8 @@ qx.Class.define("osparc.dashboard.FolderTreeItem", { console.log("changeOpen", this); }, this); openButton.addListener("changeVisibility", e => { - console.log("changeVisibility", this.getLabel() + e.getData(), this); - // openButton.show(); + // console.log("changeVisibility", this.getLabel() + e.getData(), this); + openButton.show(); }, this); this._add(openButton); this.addIcon(); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js index baa71392142..a3e140b5fc1 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js @@ -19,12 +19,7 @@ qx.Class.define("osparc.dashboard.FoldersTree", { extend: qx.ui.tree.VirtualTree, construct: function() { - const rootFolder = { - folderId: null, - label: "Home", - children: [], - loaded: true, - }; + const rootFolder = this.self().createNewEntry(null); const root = qx.data.marshal.Json.createModel(rootFolder, true); this.__fetchChildren(root); @@ -46,6 +41,17 @@ qx.Class.define("osparc.dashboard.FoldersTree", { }, statics: { + createNewEntry: function(folder) { + return { + folderId: folder ? folder.getFolderId() : null, + label: folder ? folder.getName() : "Home", + children: [ + this.self().getLoadingData() + ], + loaded: false, + }; + }, + getLoadingData: function() { return { folderId: -1, @@ -58,8 +64,16 @@ qx.Class.define("osparc.dashboard.FoldersTree", { addLoadingChild: function(parentModel) { const loadingModel = qx.data.marshal.Json.createModel(this.self().getLoadingData(), true); - parentModel.getChildren().removeAll(); parentModel.getChildren().append(loadingModel); + }, + + removeLoadingChild: function(parent) { + for (let i = parent.getChildren().length - 1; i >= 0; i--) { + if (parent.getChildren().toArray()[i].getLabel() === "Loading...") { + parent.getChildren().toArray() + .splice(i, 1); + } + } } }, @@ -75,8 +89,6 @@ qx.Class.define("osparc.dashboard.FoldersTree", { converter(value, _, __, target) { const isOpen = target.isOpen(); if (isOpen && !value.getLoaded()) { - value.setLoaded(true); - value.getChildren().removeAll(); // eslint-disable-next-line no-underscore-dangle that.__fetchChildren(value); } @@ -102,21 +114,15 @@ qx.Class.define("osparc.dashboard.FoldersTree", { }, __fetchChildren: function(parentModel) { - this.self().addLoadingChild(parentModel); + parentModel.setLoaded(true); const folderId = parentModel.getFolderId ? parentModel.getFolderId() : parentModel.getModel(); osparc.store.Folders.getInstance().fetchFolders(folderId) .then(folders => { - parentModel.getChildren().removeAll(); + this.self().removeLoadingChild(parentModel); folders.forEach(folder => { - const folderData = { - folderId: folder.getFolderId(), - label: folder.getName(), - children: [this.self().getLoadingData()], - loaded: false - }; + const folderData = this.self().createNewEntry(folder); const folderModel = qx.data.marshal.Json.createModel(folderData, true); - this.self().addLoadingChild(folderModel); parentModel.getChildren().append(folderModel); }); }) From 97ea9f03419c4853204aaf81d344af0fa3736ad5 Mon Sep 17 00:00:00 2001 From: Odei Maiz Date: Wed, 14 Aug 2024 17:51:29 +0200 Subject: [PATCH 67/67] small win --- .../client/source/class/osparc/dashboard/FoldersTree.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js index a3e140b5fc1..2a597b08b92 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FoldersTree.js @@ -30,7 +30,7 @@ qx.Class.define("osparc.dashboard.FoldersTree", { decorator: "no-border", font: "text-14", showLeafs: true, - padding: -10, + paddingLeft: -10, }); this.__initTree(); @@ -68,10 +68,9 @@ qx.Class.define("osparc.dashboard.FoldersTree", { }, removeLoadingChild: function(parent) { - for (let i = parent.getChildren().length - 1; i >= 0; i--) { + for (let i = parent.getChildren().getLength() - 1; i >= 0; i--) { if (parent.getChildren().toArray()[i].getLabel() === "Loading...") { - parent.getChildren().toArray() - .splice(i, 1); + parent.getChildren().splice(i, 1); } } }