From f1e84e27291afa7882017ba2ce1661d3bfc62b90 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 13 Feb 2025 13:43:58 +0100 Subject: [PATCH 01/10] product_ui schema --- .../source/resource/schemas/product-ui.json | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 services/static-webserver/client/source/resource/schemas/product-ui.json diff --git a/services/static-webserver/client/source/resource/schemas/product-ui.json b/services/static-webserver/client/source/resource/schemas/product-ui.json new file mode 100644 index 000000000000..b3a6cd032621 --- /dev/null +++ b/services/static-webserver/client/source/resource/schemas/product-ui.json @@ -0,0 +1,134 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "categories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": ["id", "title"] + } + }, + "resources": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "properties": { + "resourceType": { + "const": "study" + }, + "title": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "newStudyLabel": { + "type": "string" + }, + "idToWidget": { + "type": "string" + } + }, + "required": ["resourceType", "title"] + }, + { + "type": "object", + "properties": { + "resourceType": { + "const": "template" + }, + "expectedTemplateLabel": { + "type": "string" + }, + "title": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "newStudyLabel": { + "type": "string" + }, + "category": { + "type": "string" + }, + "idToWidget": { + "type": "string" + } + }, + "required": ["resourceType", "expectedTemplateLabel", "title"] + }, + { + "type": "object", + "properties": { + "resourceType": { + "const": "service" + }, + "expectedKey": { + "type": "string" + }, + "title": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "newStudyLabel": { + "type": "string" + }, + "category": { + "type": "string" + }, + "idToWidget": { + "type": "string" + } + }, + "required": ["resourceType", "expectedKey", "title"] + }, + { + "type": "object", + "properties": { + "showDisabled": { + "type": "boolean", + "const": true + }, + "title": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "newStudyLabel": { + "type": "string" + }, + "category": { + "type": "string" + }, + "idToWidget": { + "type": "string" + } + }, + "required": ["showDisabled", "title"] + } + ] + } + } + } +} From a0d47c80ac4102f16e841202ac9d5e7fd30fe70e Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 13 Feb 2025 13:44:08 +0100 Subject: [PATCH 02/10] validate --- .../source/class/osparc/store/Products.js | 63 ++++++++++++------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/store/Products.js b/services/static-webserver/client/source/class/osparc/store/Products.js index 2a359d27fb6b..60acac536e74 100644 --- a/services/static-webserver/client/source/class/osparc/store/Products.js +++ b/services/static-webserver/client/source/class/osparc/store/Products.js @@ -17,6 +17,10 @@ /** * @asset(osparc/ui_config.json") + * @asset(form/product-ui.json) + * @asset(object-path/object-path-0-11-4.min.js) + * @asset(ajv/ajv-6-11-0.min.js) + * @ignore(Ajv) */ qx.Class.define("osparc.store.Products", { @@ -27,31 +31,48 @@ qx.Class.define("osparc.store.Products", { __uiConfig: null, fetchUiConfig: function() { - if (osparc.auth.Data.getInstance().isGuest()) { - return new Promise(resolve => { + return new Promise(resolve => { + if (osparc.auth.Data.getInstance().isGuest()) { this.__uiConfig = {}; resolve(this.__uiConfig); - }); - } + } - return Promise.all([ - osparc.data.Resources.fetch("productMetadata", "getUiConfig"), - osparc.utils.Utils.fetchJSON("/resource/osparc/ui_config.json"), - ]) - .then(values => { - let uiConfig = {}; - if (values[0] && values[0]["ui"] && Object.keys(values[0]["ui"]).length) { - uiConfig = values[0]["ui"]; - } else { - const product = osparc.product.Utils.getProductName(); - if (values[1] && product in values[1]) { - uiConfig = values[1][product]; + Promise.all([ + osparc.data.Resources.fetch("productMetadata", "getUiConfig"), + osparc.utils.Utils.fetchJSON("/resource/osparc/ui_config.json"), + osparc.utils.Utils.fetchJSON("/resource/form/product-ui.json"), + ]) + .then(values => { + let uiConfig = {}; + if (values[0] && values[0]["ui"] && Object.keys(values[0]["ui"]).length) { + uiConfig = values[0]["ui"]; + } else { + const product = osparc.product.Utils.getProductName(); + if (values[1] && product in values[1]) { + uiConfig = values[1][product]; + } } - } - this.__uiConfig = uiConfig; - return this.__uiConfig; - }) - .catch(console.error); + const schema = values[2]; + const ajvLoader = new qx.util.DynamicScriptLoader([ + "/resource/ajv/ajv-6-11-0.min.js", + "/resource/object-path/object-path-0-11-4.min.js" + ]); + ajvLoader.addListener("ready", () => { + this.__ajv = new Ajv(); + if (this.__validate(schema.$schema, schema)) { + // Schema is valid + if (this.__validate(schema, uiConfig)) { + // Validate data if present + this.__uiConfig = uiConfig; + } + resolve(this.__uiConfig); + } else { + console.error("wrong ui_config") + } + }); + }) + .catch(console.error); + }); }, getPlusButtonUiConfig: function() { From 4c8dd3ba27950c3aa7677e352f3fd44e97fef02c Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 13 Feb 2025 14:06:49 +0100 Subject: [PATCH 03/10] more compact --- .../source/resource/schemas/product-ui.json | 112 +++++------------- 1 file changed, 28 insertions(+), 84 deletions(-) diff --git a/services/static-webserver/client/source/resource/schemas/product-ui.json b/services/static-webserver/client/source/resource/schemas/product-ui.json index b3a6cd032621..adcc6a4c8d2b 100644 --- a/services/static-webserver/client/source/resource/schemas/product-ui.json +++ b/services/static-webserver/client/source/resource/schemas/product-ui.json @@ -7,15 +7,9 @@ "items": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string" - } + "id": { "type": "string" }, + "title": { "type": "string" }, + "description": { "type": "string" } }, "required": ["id", "title"] } @@ -27,75 +21,37 @@ { "type": "object", "properties": { - "resourceType": { - "const": "study" - }, - "title": { - "type": "string" - }, - "icon": { - "type": "string" - }, - "newStudyLabel": { - "type": "string" - }, - "idToWidget": { - "type": "string" - } + "resourceType": { "const": "study" }, + "title": { "type": "string" }, + "icon": { "type": "string" }, + "newStudyLabel": { "type": "string" }, + "idToWidget": { "type": "string" } }, "required": ["resourceType", "title"] }, { "type": "object", "properties": { - "resourceType": { - "const": "template" - }, - "expectedTemplateLabel": { - "type": "string" - }, - "title": { - "type": "string" - }, - "icon": { - "type": "string" - }, - "newStudyLabel": { - "type": "string" - }, - "category": { - "type": "string" - }, - "idToWidget": { - "type": "string" - } + "resourceType": { "const": "template" }, + "expectedTemplateLabel": { "type": "string" }, + "title": { "type": "string" }, + "icon": { "type": "string" }, + "newStudyLabel": { "type": "string" }, + "category": { "type": "string" }, + "idToWidget": { "type": "string" } }, "required": ["resourceType", "expectedTemplateLabel", "title"] }, { "type": "object", "properties": { - "resourceType": { - "const": "service" - }, - "expectedKey": { - "type": "string" - }, - "title": { - "type": "string" - }, - "icon": { - "type": "string" - }, - "newStudyLabel": { - "type": "string" - }, - "category": { - "type": "string" - }, - "idToWidget": { - "type": "string" - } + "resourceType": { "const": "service" }, + "expectedKey": { "type": "string" }, + "title": { "type": "string" }, + "icon": { "type": "string" }, + "newStudyLabel": { "type": "string" }, + "category": { "type": "string" }, + "idToWidget": { "type": "string" } }, "required": ["resourceType", "expectedKey", "title"] }, @@ -106,24 +62,12 @@ "type": "boolean", "const": true }, - "title": { - "type": "string" - }, - "icon": { - "type": "string" - }, - "reason": { - "type": "string" - }, - "newStudyLabel": { - "type": "string" - }, - "category": { - "type": "string" - }, - "idToWidget": { - "type": "string" - } + "title": { "type": "string" }, + "icon": { "type": "string" }, + "reason": { "type": "string" }, + "newStudyLabel": { "type": "string" }, + "category": { "type": "string" }, + "idToWidget": { "type": "string" } }, "required": ["showDisabled", "title"] } From 400e5c91a549230d81163ef6ed0316744617e654 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 13 Feb 2025 14:41:24 +0100 Subject: [PATCH 04/10] validate --- .../source/class/osparc/store/Products.js | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/store/Products.js b/services/static-webserver/client/source/class/osparc/store/Products.js index 60acac536e74..4ca9f6aa5802 100644 --- a/services/static-webserver/client/source/class/osparc/store/Products.js +++ b/services/static-webserver/client/source/class/osparc/store/Products.js @@ -17,7 +17,7 @@ /** * @asset(osparc/ui_config.json") - * @asset(form/product-ui.json) + * @asset(schemas/product-ui.json) * @asset(object-path/object-path-0-11-4.min.js) * @asset(ajv/ajv-6-11-0.min.js) * @ignore(Ajv) @@ -40,7 +40,7 @@ qx.Class.define("osparc.store.Products", { Promise.all([ osparc.data.Resources.fetch("productMetadata", "getUiConfig"), osparc.utils.Utils.fetchJSON("/resource/osparc/ui_config.json"), - osparc.utils.Utils.fetchJSON("/resource/form/product-ui.json"), + osparc.utils.Utils.fetchJSON("/resource/schemas/product-ui.json"), ]) .then(values => { let uiConfig = {}; @@ -52,24 +52,40 @@ qx.Class.define("osparc.store.Products", { uiConfig = values[1][product]; } } - const schema = values[2]; + // const schema = values[2]; + const schema = { + "type": "object", + "properties": { + "resourceType": { "type": "string" }, + "title": { "type": "string" }, + "icon": { "type": "string" }, + "newStudyLabel": { "type": "string" }, + "idToWidget": { "type": "string" } + }, + "required": ["resourceType", "title"], + }; const ajvLoader = new qx.util.DynamicScriptLoader([ "/resource/ajv/ajv-6-11-0.min.js", "/resource/object-path/object-path-0-11-4.min.js" ]); ajvLoader.addListener("ready", () => { - this.__ajv = new Ajv(); - if (this.__validate(schema.$schema, schema)) { - // Schema is valid - if (this.__validate(schema, uiConfig)) { - // Validate data if present - this.__uiConfig = uiConfig; - } + const ajv = new Ajv({ + allErrors: true, + strictDefaults: true, + useDefaults: true, + }); + const validate = ajv.compile(schema); + const valid = validate(uiConfig); + if (valid) { + // Validate data if present + this.__uiConfig = uiConfig; resolve(this.__uiConfig); } else { console.error("wrong ui_config") } }); + ajvLoader.addListener("failed", console.error, this); + ajvLoader.start(); }) .catch(console.error); }); From cca424efcb745d90584848e4736d426efae22c28 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 13 Feb 2025 14:45:18 +0100 Subject: [PATCH 05/10] enum instead of const --- .../client/source/resource/schemas/product-ui.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/static-webserver/client/source/resource/schemas/product-ui.json b/services/static-webserver/client/source/resource/schemas/product-ui.json index adcc6a4c8d2b..033057a5ad11 100644 --- a/services/static-webserver/client/source/resource/schemas/product-ui.json +++ b/services/static-webserver/client/source/resource/schemas/product-ui.json @@ -21,7 +21,7 @@ { "type": "object", "properties": { - "resourceType": { "const": "study" }, + "resourceType": { "enum": ["study"] }, "title": { "type": "string" }, "icon": { "type": "string" }, "newStudyLabel": { "type": "string" }, @@ -32,7 +32,7 @@ { "type": "object", "properties": { - "resourceType": { "const": "template" }, + "resourceType": { "enum": ["template"] }, "expectedTemplateLabel": { "type": "string" }, "title": { "type": "string" }, "icon": { "type": "string" }, @@ -45,7 +45,7 @@ { "type": "object", "properties": { - "resourceType": { "const": "service" }, + "resourceType": { "enum": ["service"] }, "expectedKey": { "type": "string" }, "title": { "type": "string" }, "icon": { "type": "string" }, @@ -60,7 +60,7 @@ "properties": { "showDisabled": { "type": "boolean", - "const": true + "enum": [true] }, "title": { "type": "string" }, "icon": { "type": "string" }, From 1b8c79bca95c6ffb1451b7240066ecde96559b36 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Thu, 13 Feb 2025 14:52:54 +0100 Subject: [PATCH 06/10] validate --- .../source/class/osparc/store/Products.js | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/store/Products.js b/services/static-webserver/client/source/class/osparc/store/Products.js index 4ca9f6aa5802..246a11871868 100644 --- a/services/static-webserver/client/source/class/osparc/store/Products.js +++ b/services/static-webserver/client/source/class/osparc/store/Products.js @@ -44,26 +44,17 @@ qx.Class.define("osparc.store.Products", { ]) .then(values => { let uiConfig = {}; - if (values[0] && values[0]["ui"] && Object.keys(values[0]["ui"]).length) { - uiConfig = values[0]["ui"]; + const beUiConfig = values[0]; + const feUiConfig = values[1]; + const schema = values[2]; + if (beUiConfig && beUiConfig["ui"] && Object.keys(beUiConfig["ui"]).length) { + uiConfig = beUiConfig["ui"]; } else { const product = osparc.product.Utils.getProductName(); - if (values[1] && product in values[1]) { - uiConfig = values[1][product]; + if (feUiConfig && product in feUiConfig) { + uiConfig = feUiConfig[product]; } } - // const schema = values[2]; - const schema = { - "type": "object", - "properties": { - "resourceType": { "type": "string" }, - "title": { "type": "string" }, - "icon": { "type": "string" }, - "newStudyLabel": { "type": "string" }, - "idToWidget": { "type": "string" } - }, - "required": ["resourceType", "title"], - }; const ajvLoader = new qx.util.DynamicScriptLoader([ "/resource/ajv/ajv-6-11-0.min.js", "/resource/object-path/object-path-0-11-4.min.js" @@ -81,7 +72,10 @@ qx.Class.define("osparc.store.Products", { this.__uiConfig = uiConfig; resolve(this.__uiConfig); } else { - console.error("wrong ui_config") + console.error("Wrong UI Config"); + validate.errors.forEach(err => { + console.error(`Error at ${err.dataPath}: ${err.message}`); + }); } }); ajvLoader.addListener("failed", console.error, this); From e6f67cca5c92df9c59b6495c2301f0d3dad30c45 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 14 Feb 2025 18:08:36 +0100 Subject: [PATCH 07/10] additionalProperties --- .../client/source/resource/schemas/product-ui.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/services/static-webserver/client/source/resource/schemas/product-ui.json b/services/static-webserver/client/source/resource/schemas/product-ui.json index 033057a5ad11..49e1fbae64c5 100644 --- a/services/static-webserver/client/source/resource/schemas/product-ui.json +++ b/services/static-webserver/client/source/resource/schemas/product-ui.json @@ -72,7 +72,9 @@ "required": ["showDisabled", "title"] } ] - } + }, + "additionalProperties": false } - } + }, + "additionalProperties": false } From baafa2bd8db13df9accd480935423d1a47641266 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 14 Feb 2025 18:15:58 +0100 Subject: [PATCH 08/10] working --- .../source/class/osparc/store/Products.js | 1 + .../source/resource/schemas/product-ui.json | 141 ++++++++++-------- 2 files changed, 76 insertions(+), 66 deletions(-) diff --git a/services/static-webserver/client/source/class/osparc/store/Products.js b/services/static-webserver/client/source/class/osparc/store/Products.js index 246a11871868..5595fe8f5db1 100644 --- a/services/static-webserver/client/source/class/osparc/store/Products.js +++ b/services/static-webserver/client/source/class/osparc/store/Products.js @@ -64,6 +64,7 @@ qx.Class.define("osparc.store.Products", { allErrors: true, strictDefaults: true, useDefaults: true, + strictTypes: true, }); const validate = ajv.compile(schema); const valid = validate(uiConfig); diff --git a/services/static-webserver/client/source/resource/schemas/product-ui.json b/services/static-webserver/client/source/resource/schemas/product-ui.json index 49e1fbae64c5..5b5c09f986b8 100644 --- a/services/static-webserver/client/source/resource/schemas/product-ui.json +++ b/services/static-webserver/client/source/resource/schemas/product-ui.json @@ -2,79 +2,88 @@ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { - "categories": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { "type": "string" }, - "title": { "type": "string" }, - "description": { "type": "string" } - }, - "required": ["id", "title"] - } + "plusButton": { + "$ref": "#/definitions/buttonConfig" }, - "resources": { - "type": "array", - "items": { - "oneOf": [ - { - "type": "object", - "properties": { - "resourceType": { "enum": ["study"] }, - "title": { "type": "string" }, - "icon": { "type": "string" }, - "newStudyLabel": { "type": "string" }, - "idToWidget": { "type": "string" } - }, - "required": ["resourceType", "title"] - }, - { - "type": "object", - "properties": { - "resourceType": { "enum": ["template"] }, - "expectedTemplateLabel": { "type": "string" }, - "title": { "type": "string" }, - "icon": { "type": "string" }, - "newStudyLabel": { "type": "string" }, - "category": { "type": "string" }, - "idToWidget": { "type": "string" } - }, - "required": ["resourceType", "expectedTemplateLabel", "title"] - }, - { - "type": "object", - "properties": { - "resourceType": { "enum": ["service"] }, - "expectedKey": { "type": "string" }, - "title": { "type": "string" }, - "icon": { "type": "string" }, - "newStudyLabel": { "type": "string" }, - "category": { "type": "string" }, - "idToWidget": { "type": "string" } - }, - "required": ["resourceType", "expectedKey", "title"] - }, - { + "newStudies": { + "$ref": "#/definitions/buttonConfig" + } + }, + "additionalProperties": false, + "definitions": { + "buttonConfig": { + "type": "object", + "properties": { + "categories": { + "type": "array", + "items": { "type": "object", "properties": { - "showDisabled": { - "type": "boolean", - "enum": [true] - }, + "id": { "type": "string" }, "title": { "type": "string" }, - "icon": { "type": "string" }, - "reason": { "type": "string" }, - "newStudyLabel": { "type": "string" }, - "category": { "type": "string" }, - "idToWidget": { "type": "string" } + "description": { "type": "string" } }, - "required": ["showDisabled", "title"] + "required": ["id", "title"] } - ] + }, + "resources": { + "type": "array", + "items": { + "oneOf": [{ + "type": "object", + "properties": { + "resourceType": { "enum": ["study"] }, + "title": { "type": "string" }, + "icon": { "type": "string" }, + "newStudyLabel": { "type": "string" }, + "idToWidget": { "type": "string" } + }, + "required": ["resourceType", "title"] + }, { + "type": "object", + "properties": { + "resourceType": { "enum": ["template"] }, + "expectedTemplateLabel": { "type": "string" }, + "title": { "type": "string" }, + "icon": { "type": "string" }, + "newStudyLabel": { "type": "string" }, + "category": { "type": "string" }, + "idToWidget": { "type": "string" } + }, + "required": ["resourceType", "expectedTemplateLabel", "title"] + }, { + "type": "object", + "properties": { + "resourceType": { "enum": ["service"] }, + "expectedKey": { "type": "string" }, + "title": { "type": "string" }, + "icon": { "type": "string" }, + "newStudyLabel": { "type": "string" }, + "category": { "type": "string" }, + "idToWidget": { "type": "string" } + }, + "required": ["resourceType", "expectedKey", "title"] + }, { + "type": "object", + "properties": { + "showDisabled": { + "type": "boolean", + "enum": [true] + }, + "title": { "type": "string" }, + "icon": { "type": "string" }, + "reason": { "type": "string" }, + "newStudyLabel": { "type": "string" }, + "category": { "type": "string" }, + "idToWidget": { "type": "string" } + }, + "required": ["showDisabled", "title"] + }] + }, + "additionalProperties": false + } }, "additionalProperties": false } - }, - "additionalProperties": false + } } From d554a0bfbd4fdbf20a0f1681fd1cac8ca8495899 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 14 Feb 2025 18:17:10 +0100 Subject: [PATCH 09/10] minor --- .../client/source/class/osparc/store/Products.js | 1 - 1 file changed, 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/store/Products.js b/services/static-webserver/client/source/class/osparc/store/Products.js index 5595fe8f5db1..3b54bb859ddd 100644 --- a/services/static-webserver/client/source/class/osparc/store/Products.js +++ b/services/static-webserver/client/source/class/osparc/store/Products.js @@ -69,7 +69,6 @@ qx.Class.define("osparc.store.Products", { const validate = ajv.compile(schema); const valid = validate(uiConfig); if (valid) { - // Validate data if present this.__uiConfig = uiConfig; resolve(this.__uiConfig); } else { From c195fcbd0b99cf1cf5674a4d35989db4f42a5bf8 Mon Sep 17 00:00:00 2001 From: odeimaiz Date: Fri, 14 Feb 2025 18:23:24 +0100 Subject: [PATCH 10/10] fm --- .../client/source/class/osparc/store/Products.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/static-webserver/client/source/class/osparc/store/Products.js b/services/static-webserver/client/source/class/osparc/store/Products.js index 3b54bb859ddd..b0cef978485e 100644 --- a/services/static-webserver/client/source/class/osparc/store/Products.js +++ b/services/static-webserver/client/source/class/osparc/store/Products.js @@ -72,7 +72,7 @@ qx.Class.define("osparc.store.Products", { this.__uiConfig = uiConfig; resolve(this.__uiConfig); } else { - console.error("Wrong UI Config"); + osparc.FlashMessenger.getInstance().logAs("Wrong product.ui config", "ERROR"); validate.errors.forEach(err => { console.error(`Error at ${err.dataPath}: ${err.message}`); });