diff --git a/services/static-webserver/client/source/class/osparc/data/Permissions.js b/services/static-webserver/client/source/class/osparc/data/Permissions.js index 44d00471160..5c53b4da76d 100644 --- a/services/static-webserver/client/source/class/osparc/data/Permissions.js +++ b/services/static-webserver/client/source/class/osparc/data/Permissions.js @@ -138,7 +138,6 @@ qx.Class.define("osparc.data.Permissions", { "study.nodestree.uuid.read", "study.filestree.uuid.read", "study.logger.debug.read", - "statics.read" ], "product_owner": [ "user.invitation.generate", diff --git a/services/static-webserver/client/source/class/osparc/desktop/preferences/Preferences.js b/services/static-webserver/client/source/class/osparc/desktop/preferences/Preferences.js index 876019a22c6..d04b96ceeea 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/preferences/Preferences.js +++ b/services/static-webserver/client/source/class/osparc/desktop/preferences/Preferences.js @@ -32,9 +32,6 @@ qx.Class.define("osparc.desktop.preferences.Preferences", { if (osparc.product.Utils.showClusters()) { this.__addClustersPage(); } - if (osparc.data.Permissions.getInstance().canDo("statics.read")) { - this.__addTestersPage(); - } }, members: { @@ -85,12 +82,5 @@ qx.Class.define("osparc.desktop.preferences.Preferences", { .catch(err => console.error(err)); } }, - - __addTestersPage: function() { - const title = this.tr("Tester"); - const iconSrc = "@FontAwesome5Solid/user-md/24"; - const testerPage = new osparc.desktop.preferences.pages.TesterPage(); - this.addTab(title, iconSrc, testerPage); - } } }); diff --git a/services/static-webserver/client/source/class/osparc/desktop/preferences/pages/TesterPage.js b/services/static-webserver/client/source/class/osparc/desktop/preferences/pages/TesterPage.js deleted file mode 100644 index c886995c744..00000000000 --- a/services/static-webserver/client/source/class/osparc/desktop/preferences/pages/TesterPage.js +++ /dev/null @@ -1,88 +0,0 @@ -/* ************************************************************************ - - osparc - the simcore frontend - - https://osparc.io - - Copyright: - 2020 IT'IS Foundation, https://itis.swiss - - License: - MIT: https://opensource.org/licenses/MIT - - Authors: - * Odei Maiz (odeimaiz) - -************************************************************************ */ - -/** - * Tester Misc in preferences dialog - */ - -qx.Class.define("osparc.desktop.preferences.pages.TesterPage", { - extend: qx.ui.core.Widget, - - construct: function() { - this.base(arguments); - - this._setLayout(new qx.ui.layout.VBox(15)); - - const container = new qx.ui.container.Composite(new qx.ui.layout.VBox(10)); - - const statics = this.__createStaticsLayout(); - container.add(statics); - - const localStorage = this.__createLocalStorageLayout(); - container.add(localStorage); - - const scroll = new qx.ui.container.Scroll(container); - this._add(scroll, { - flex: 1 - }); - }, - - members: { - __createStaticsLayout: function() { - // layout - const box = osparc.ui.window.TabbedView.createSectionBox(this.tr("Statics")); - - const label = osparc.ui.window.TabbedView.createHelpLabel(this.tr( - "This is a list of the 'statics' resources" - )); - box.add(label); - - const statics = osparc.store.Store.getInstance().get("statics"); - const form = new qx.ui.form.Form(); - for (let [key, value] of Object.entries(statics)) { - const textField = new qx.ui.form.TextField().set({ - value: typeof value === "object" ? JSON.stringify(value) : value.toString(), - readOnly: true - }); - form.add(textField, key, null, key); - } - box.add(new qx.ui.form.renderer.Single(form)); - - return box; - }, - - __createLocalStorageLayout: function() { - // layout - const box = osparc.ui.window.TabbedView.createSectionBox(this.tr("Local Storage")); - - const items = { - ...window.localStorage - }; - const form = new qx.ui.form.Form(); - for (let [key, value] of Object.entries(items)) { - const textField = new qx.ui.form.TextField().set({ - value: typeof value === "object" ? JSON.stringify(value) : value.toString(), - readOnly: true - }); - form.add(textField, key, null, key); - } - box.add(new qx.ui.form.renderer.Single(form)); - - return box; - } - } -}); diff --git a/services/static-webserver/client/source/class/osparc/file/FolderViewer.js b/services/static-webserver/client/source/class/osparc/file/FolderViewer.js index 23c1d5e57e8..26fb4433bf3 100644 --- a/services/static-webserver/client/source/class/osparc/file/FolderViewer.js +++ b/services/static-webserver/client/source/class/osparc/file/FolderViewer.js @@ -201,24 +201,6 @@ qx.Class.define("osparc.file.FolderViewer", { return control || this.base(arguments, id); }, - __getEmptyEntry: function() { - const items = []; - if (this.getMode() === "list") { - items.push([ - "", - this.tr("Empty folder"), - "", - "", - "" - ]); - } else if (this.getMode() === "icons") { - items.push(this.self().getItemButton().set({ - label: this.tr("Empty folder") - })); - } - return items; - }, - __convertEntries: function(content) { const items = []; if (this.getMode() === "list") { diff --git a/services/static-webserver/client/source/class/osparc/navigation/UserMenu.js b/services/static-webserver/client/source/class/osparc/navigation/UserMenu.js index b18f0a4d7b3..8a736ef7d40 100644 --- a/services/static-webserver/client/source/class/osparc/navigation/UserMenu.js +++ b/services/static-webserver/client/source/class/osparc/navigation/UserMenu.js @@ -61,6 +61,11 @@ qx.Class.define("osparc.navigation.UserMenu", { control.addListener("execute", () => osparc.po.POCenterWindow.openWindow(), this); this.add(control); break; + case "tester-center": + control = new qx.ui.menu.Button(this.tr("Tester Center")); + control.addListener("execute", () => osparc.tester.TesterCenterWindow.openWindow(), this); + this.add(control); + break; case "billing-center": control = new qx.ui.menu.Button(this.tr("Billing Center")); osparc.utils.Utils.setIdToWidget(control, "userMenuBillingCenterBtn"); @@ -157,6 +162,9 @@ qx.Class.define("osparc.navigation.UserMenu", { if (osparc.data.Permissions.getInstance().isProductOwner()) { this.getChildControl("po-center"); } + if (osparc.data.Permissions.getInstance().isTester()) { + this.getChildControl("tester-center"); + } if (osparc.desktop.credits.Utils.areWalletsEnabled()) { this.getChildControl("billing-center"); } diff --git a/services/static-webserver/client/source/class/osparc/tester/Statics.js b/services/static-webserver/client/source/class/osparc/tester/Statics.js new file mode 100644 index 00000000000..b6655075e9f --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/tester/Statics.js @@ -0,0 +1,75 @@ +/* ************************************************************************ + + 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.tester.Statics", { + extend: osparc.po.BaseView, + + members: { + _createChildControlImpl: function(id) { + let control; + switch (id) { + case "statics-container": + control = osparc.ui.window.TabbedView.createSectionBox(this.tr("Statics")); + this._add(control, { + flex: 1 + }); + break; + case "statics-content": { + const statics = osparc.store.Store.getInstance().get("statics"); + const form = new qx.ui.form.Form(); + for (let [key, value] of Object.entries(statics)) { + const textField = new qx.ui.form.TextField().set({ + value: typeof value === "object" ? JSON.stringify(value) : value.toString(), + readOnly: true + }); + form.add(textField, key, null, key); + } + const renderer = new qx.ui.form.renderer.Single(form); + control = new qx.ui.container.Scroll(renderer); + this.getChildControl("statics-container").add(control); + break; + } + case "local-storage-container": + control = osparc.ui.window.TabbedView.createSectionBox(this.tr("Local Storage")); + this._add(control); + break; + case "local-storage-content": { + const items = { + ...window.localStorage + }; + const form = new qx.ui.form.Form(); + for (let [key, value] of Object.entries(items)) { + const textField = new qx.ui.form.TextField().set({ + value: typeof value === "object" ? JSON.stringify(value) : value.toString(), + readOnly: true + }); + form.add(textField, key, null, key); + } + control = new qx.ui.form.renderer.Single(form); + this.getChildControl("local-storage-container").add(control); + break; + } + } + return control || this.base(arguments, id); + }, + + _buildLayout: function() { + this.getChildControl("statics-content"); + this.getChildControl("local-storage-content"); + }, + } +}); diff --git a/services/static-webserver/client/source/class/osparc/tester/TesterCenter.js b/services/static-webserver/client/source/class/osparc/tester/TesterCenter.js new file mode 100644 index 00000000000..b456afebb32 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/tester/TesterCenter.js @@ -0,0 +1,48 @@ +/* ************************************************************************ + + 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.tester.TesterCenter", { + extend: osparc.ui.window.TabbedView, + + construct: function() { + this.base(arguments); + + const miniProfile = osparc.desktop.account.MyAccount.createMiniProfileView().set({ + paddingRight: 10 + }); + this.addWidgetOnTopOfTheTabs(miniProfile); + + this.__addSocketMessagesPage(); + this.__addStaticsPage(); + }, + + members: { + __addSocketMessagesPage: function() { + const title = this.tr("Socket Messages"); + const iconSrc = "@FontAwesome5Solid/exchange-alt/22"; + const maintenance = new osparc.tester.WebSocketMessages(); + this.addTab(title, iconSrc, maintenance); + }, + + __addStaticsPage: function() { + const title = this.tr("Statics"); + const iconSrc = "@FontAwesome5Solid/wrench/22"; + const maintenance = new osparc.tester.Statics(); + this.addTab(title, iconSrc, maintenance); + }, + } +}); diff --git a/services/static-webserver/client/source/class/osparc/tester/TesterCenterWindow.js b/services/static-webserver/client/source/class/osparc/tester/TesterCenterWindow.js new file mode 100644 index 00000000000..9b2a99c330d --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/tester/TesterCenterWindow.js @@ -0,0 +1,43 @@ +/* ************************************************************************ + + 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.tester.TesterCenterWindow", { + extend: osparc.ui.window.TabbedWindow, + + construct: function() { + this.base(arguments, "tester-center", this.tr("Tester Center")); + + const width = 800; + const maxHeight = 800; + this.set({ + width, + maxHeight, + }); + + const testerCenter = new osparc.tester.TesterCenter(); + this._setTabbedView(testerCenter); + }, + + statics: { + openWindow: function() { + const accountWindow = new osparc.tester.TesterCenterWindow(); + accountWindow.center(); + accountWindow.open(); + return accountWindow; + } + } +}); diff --git a/services/static-webserver/client/source/class/osparc/tester/WebSocketMessages.js b/services/static-webserver/client/source/class/osparc/tester/WebSocketMessages.js new file mode 100644 index 00000000000..0872301459f --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/tester/WebSocketMessages.js @@ -0,0 +1,138 @@ +/* ************************************************************************ + + 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.tester.WebSocketMessages", { + extend: osparc.po.BaseView, + construct: function() { + this.base(arguments); + }, + + members: { + _createChildControlImpl: function(id) { + let control; + switch (id) { + case "filter-message": { + control = new qx.ui.form.TextField().set({ + liveUpdate : true, + placeholder: this.tr("Search in Message"), + }); + this._add(control); + break; + } + case "messages-table": { + const tableModel = new qx.ui.table.model.Filtered(); + tableModel.setColumns([ + this.tr("Date"), + this.tr("Channel"), + this.tr("Message"), + ]); + const custom = { + tableColumnModel: function(obj) { + return new qx.ui.table.columnmodel.Resize(obj); + } + }; + control = new qx.ui.table.Table(tableModel, custom).set({ + selectable: true, + statusBarVisible: false, + showCellFocusIndicator: false, + forceLineHeight: false + }); + control.getTableColumnModel().setDataCellRenderer( + 1, + new qx.ui.table.cellrenderer.String().set({ + defaultCellStyle: "user-select: text" + }) + ); + control.getTableColumnModel().setDataCellRenderer( + 0, + new qx.ui.table.cellrenderer.String().set({ + defaultCellStyle: "user-select: text" + }) + ); + control.getTableColumnModel().setDataCellRenderer( + 2, + new osparc.ui.table.cellrenderer.Html().set({ + defaultCellStyle: "user-select: text; text-wrap: wrap" + }) + ); + control.setColumnWidth(0, 80); + control.setColumnWidth(1, 150); + + control.setDataRowRenderer(new osparc.ui.table.rowrenderer.ExpandSelection(control)); + this._add(control, { + flex: 1 + }); + break; + } + case "json-viewer": + control = new osparc.ui.basic.JsonTreeWidget(); + this._add(control); + break; + } + return control || this.base(arguments, id); + }, + + _buildLayout: function() { + const filterMessage = this.getChildControl("filter-message"); + const table = this.getChildControl("messages-table"); + const jsonViewer = this.getChildControl("json-viewer"); + + const model = table.getTableModel(); + filterMessage.addListener("changeValue", e => { + const value = e.getData(); + model.resetHiddenRows(); + model.addNotRegex(value, "Message", true); + model.applyFilters(); + }); + table.addListener("cellTap", e => { + const selectedRow = e.getRow(); + const rowData = table.getTableModel().getRowData(selectedRow); + jsonViewer.setJson(JSON.parse(rowData[2])); + }, this); + + this.__populateTable(); + }, + + __populateTable: function() { + const socket = osparc.wrapper.WebSocket.getInstance(); + const messagesObj = socket.getCachedMessages(); + const messagesArray = []; + for (const channel in messagesObj) { + messagesObj[channel].forEach(msg => { + messagesArray.push({ + date: msg.date, + channel, + message: msg.message, + }); + }); + } + messagesArray.sort((a, b) => { + return new Date(b.date) - new Date(a.date); // newest first + }); + const datas = []; + messagesArray.forEach(entry => { + const data = [ + new Date(entry.date).toLocaleTimeString(), + entry.channel, + JSON.stringify(entry.message), + ]; + datas.push(data); + }); + this.getChildControl("messages-table").getTableModel().setData(datas); + } + } +}); diff --git a/services/static-webserver/client/source/class/osparc/ui/basic/JsonTreeWidget.js b/services/static-webserver/client/source/class/osparc/ui/basic/JsonTreeWidget.js index 24453fe0bf8..60f5a55e5b1 100644 --- a/services/static-webserver/client/source/class/osparc/ui/basic/JsonTreeWidget.js +++ b/services/static-webserver/client/source/class/osparc/ui/basic/JsonTreeWidget.js @@ -36,11 +36,21 @@ qx.Class.define("osparc.ui.basic.JsonTreeWidget", { * @param data {Object} Json object to be displayed by JsonTreeViewer */ construct: function(data) { - const prettyJson = JSON.stringify(data, null, " ").replace(/\n/ig, "
"); - this.base(arguments, prettyJson); + this.base(arguments); this.set({ rich: true, selectable: true }); + + if (data) { + this.setJson(data); + } + }, + + members: { + setJson(data) { + const prettyJson = JSON.stringify(data, null, " ").replace(/\n/ig, "
"); + this.setValue(prettyJson); + } } }); diff --git a/services/static-webserver/client/source/class/osparc/widget/logger/LoggerView.js b/services/static-webserver/client/source/class/osparc/widget/logger/LoggerView.js index e77ff8c6840..7eb6bbb4081 100644 --- a/services/static-webserver/client/source/class/osparc/widget/logger/LoggerView.js +++ b/services/static-webserver/client/source/class/osparc/widget/logger/LoggerView.js @@ -21,7 +21,7 @@ * It consists of: * - a toolbar containing: * - clear button - * - filter as you type textfiled + * - filter as you type textfield * - some log type filtering buttons * - log messages table * diff --git a/services/static-webserver/client/source/class/osparc/wrapper/WebSocket.js b/services/static-webserver/client/source/class/osparc/wrapper/WebSocket.js index 3a300c433b9..081d790c01d 100644 --- a/services/static-webserver/client/source/class/osparc/wrapper/WebSocket.js +++ b/services/static-webserver/client/source/class/osparc/wrapper/WebSocket.js @@ -134,11 +134,13 @@ qx.Class.define("osparc.wrapper.WebSocket", { this.setNamespace(namespace); } this.__name = []; + this.__cache = {}; }, members: { // The name store an array of events __name: null, + __cache: null, /** * Trying to using socket.io to connect and plug every event from socket.io to qooxdoo one @@ -234,7 +236,7 @@ qx.Class.define("osparc.wrapper.WebSocket", { * Connect and event from socket.io like qooxdoo event * * @param {string} name The event name to watch - * @param {function} fn The function wich will catch event response + * @param {function} fn The function which will catch event response * @param {mixed} that A link to this * @returns {void} */ @@ -247,6 +249,21 @@ qx.Class.define("osparc.wrapper.WebSocket", { } else { socket.on(name, fn); } + + // add a duplicated slot listener to keep the messages cached + socket.on(name, message => { + if (!(name in this.__cache)) { + this.__cache[name] = []; + } + const info = { + date: new Date(), + message: message ? message : "", + } + this.__cache[name].unshift(info); + if (this.__cache[name].length > 20) { + this.__cache[name].length = 20; + } + }, this); } }, @@ -265,7 +282,11 @@ qx.Class.define("osparc.wrapper.WebSocket", { index = this.__name.indexOf(name); } } - } + }, + + getCachedMessages: function() { + return this.__cache; + }, }, /** @@ -281,6 +302,7 @@ qx.Class.define("osparc.wrapper.WebSocket", { } } this.__name = null; + this.__cache = null; this.removeAllBindings();