From 51cb61940d82102633e0baa29128cac0747ccdc1 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 25 Sep 2023 17:33:59 +0100 Subject: [PATCH] Improve validation of switch/change node rules Fixes #4340 --- .../editor-client/locales/en-US/editor.json | 4 +- .../editor-client/locales/fr/editor.json | 3 - .../editor-client/locales/ja/editor.json | 3 - .../editor-client/locales/pt-BR/editor.json | 3 - .../editor-client/locales/zh-CN/editor.json | 3 - .../editor-client/src/js/ui/editor.js | 7 +- .../editor-client/src/js/ui/utils.js | 48 ++++++++++- .../editor-client/src/js/validators.js | 49 +++-------- .../nodes/core/function/10-switch.html | 28 ++++++- .../nodes/core/function/15-change.html | 81 ++++++------------- 10 files changed, 117 insertions(+), 112 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json index 8742a6c6f..c0317c90e 100644 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json @@ -1215,11 +1215,9 @@ "validator": { "errors": { "invalid-json": "Invalid JSON data: __error__", - "invalid-json-prop": "__prop__: invalid JSON data: __error__", + "invalid-expr": "Invalid JSONata expression: __error__", "invalid-prop": "Invalid property expression", - "invalid-prop-prop": "__prop__: invalid property expression", "invalid-num": "Invalid number", - "invalid-num-prop": "__prop__: invalid number", "invalid-regexp": "Invalid input pattern", "invalid-regex-prop": "__prop__: invalid input pattern", "missing-required-prop": "__prop__: property value missing", diff --git a/packages/node_modules/@node-red/editor-client/locales/fr/editor.json b/packages/node_modules/@node-red/editor-client/locales/fr/editor.json index 25e86f792..274cadb2a 100644 --- a/packages/node_modules/@node-red/editor-client/locales/fr/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/fr/editor.json @@ -1215,11 +1215,8 @@ "validator": { "errors": { "invalid-json": "Données JSON invalides : __error__", - "invalid-json-prop": "__prop__: données JSON invalides : __error__", "invalid-prop": "Expression de propriété non valide", - "invalid-prop-prop": "__prop__: expression de propriété invalide", "invalid-num": "Numéro invalide", - "invalid-num-prop": "__prop__: numéro invalide", "invalid-regexp": "Modèle d'entrée non valide", "invalid-regex-prop": "__prop__: modèle d'entrée non valide", "missing-required-prop": "__prop__: valeur de la propriété manquante", diff --git a/packages/node_modules/@node-red/editor-client/locales/ja/editor.json b/packages/node_modules/@node-red/editor-client/locales/ja/editor.json index 42257f6b0..ceb001a10 100644 --- a/packages/node_modules/@node-red/editor-client/locales/ja/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/ja/editor.json @@ -1215,11 +1215,8 @@ "validator": { "errors": { "invalid-json": "JSONデータが不正: __error__", - "invalid-json-prop": "__prop__: JSONデータが不正: __error__", "invalid-prop": "プロパティ式が不正", - "invalid-prop-prop": "__prop__: プロパティ式が不正", "invalid-num": "数値が不正", - "invalid-num-prop": "__prop__: 数値が不正", "invalid-regexp": "入力パターンが不正", "invalid-regex-prop": "__prop__: 入力パターンが不正", "missing-required-prop": "__prop__: プロパティが未設定", diff --git a/packages/node_modules/@node-red/editor-client/locales/pt-BR/editor.json b/packages/node_modules/@node-red/editor-client/locales/pt-BR/editor.json index 6e20bc32d..f65ec62e9 100644 --- a/packages/node_modules/@node-red/editor-client/locales/pt-BR/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/pt-BR/editor.json @@ -1186,11 +1186,8 @@ "validator": { "errors": { "invalid-json": "Dados JSON inválidos: __error__", - "invalid-json-prop": "__prop__: dados JSON inválidos: __error__", "invalid-prop": "Expressão de propriedade inválida", - "invalid-prop-prop": "__prop__: expressão de propriedade inválida", "invalid-num": "Número inválido", - "invalid-num-prop": "__prop__: número inválido", "invalid-regexp": "Padrão de entrada inválido", "invalid-regex-prop": "__prop__: Padrão de entrada inválido", "missing-required-prop": "__prop__: valor de propriedade ausente", diff --git a/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json b/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json index e55240cc5..afc63e4cb 100644 --- a/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json @@ -1199,11 +1199,8 @@ "validator": { "errors": { "invalid-json": "无效的 JSON 数据: __error__", - "invalid-json-prop": "__prop__: 无效的 JSON 数据: __error__", "invalid-prop": "无效的属性表达式", - "invalid-prop-prop": "__prop__: 无效的属性表达式", "invalid-num": "无效的数字", - "invalid-num-prop": "__prop__: 无效的数字", "invalid-regexp": "输入格式无效", "invalid-regex-prop": "__prop__: 输入格式无效", "missing-required-prop": "__prop__: 缺少属性值", diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js index 0ef3892e7..62ce8dbe9 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js @@ -115,8 +115,9 @@ RED.editor = (function() { var valid = validateNodeProperty(node, definition, prop, properties[prop]); if ((typeof valid) === "string") { result.push(valid); - } - else if(!valid) { + } else if (Array.isArray(valid)) { + result = result.concat(valid) + } else if(!valid) { result.push(prop); } } @@ -165,7 +166,7 @@ RED.editor = (function() { // If the validator takes two arguments, it is a 3.x validator that // can return a String to mean 'invalid' and provide a reason if ((definition[property].validate.length === 2) && - ((typeof valid) === "string")) { + ((typeof valid) === "string") || Array.isArray(valid)) { return valid; } else { // Otherwise, a 2.x returns a truth-like/false-like value that diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js index 4d8ccdd9d..638086ae1 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js @@ -917,6 +917,51 @@ RED.utils = (function() { } } + /** + * Checks a typed property is valid according to the type. + * Returns true if valid. + * Return String error message if invalid + * @param {*} propertyType + * @param {*} propertyValue + * @returns true if valid, String if invalid + */ + function validateTypedProperty(propertyValue, propertyType, opt) { + + let error + if (propertyType === 'json') { + try { + JSON.parse(propertyValue); + } catch(err) { + error = RED._("validator.errors.invalid-json", { + error: err.message + }) + } + } else if (propertyType === 'msg' || propertyType === 'flow' || propertyType === 'global' ) { + if (!RED.utils.validatePropertyExpression(propertyValue)) { + error = RED._("validator.errors.invalid-prop") + } + } else if (propertyType === 'num') { + if (!/^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/.test(propertyValue)) { + error = RED._("validator.errors.invalid-num") + } + } else if (propertyType === 'jsonata') { + try { + jsonata(propertyValue) + } catch(err) { + error = RED._("validator.errors.invalid-expr", { + error: err.message + }) + } + } + if (error) { + if (opt && opt.label) { + return opt.label+': '+error + } + return error + } + return true + } + function getMessageProperty(msg,expr) { var result = null; var msgPropParts; @@ -1451,6 +1496,7 @@ RED.utils = (function() { getDarkerColor: getDarkerColor, parseModuleList: parseModuleList, checkModuleAllowed: checkModuleAllowed, - getBrowserInfo: getBrowserInfo + getBrowserInfo: getBrowserInfo, + validateTypedProperty: validateTypedProperty } })(); diff --git a/packages/node_modules/@node-red/editor-client/src/js/validators.js b/packages/node_modules/@node-red/editor-client/src/js/validators.js index 4fb384cbf..8eeb7b1a7 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/validators.js +++ b/packages/node_modules/@node-red/editor-client/src/js/validators.js @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ +try { RED.validators = { number: function(blankAllowed,mopt){ return function(v, opt) { @@ -43,43 +44,17 @@ RED.validators = { typedInput: function(ptypeName,isConfig,mopt) { return function(v, opt) { var ptype = $("#node-"+(isConfig?"config-":"")+"input-"+ptypeName).val() || this[ptypeName]; - if (ptype === 'json') { - try { - JSON.parse(v); - return true; - } catch(err) { - if (opt && opt.label) { - return RED._("validator.errors.invalid-json-prop", { - error: err.message, - prop: opt.label, - }); - } - return opt ? RED._("validator.errors.invalid-json", { - error: err.message - }) : false; - } - } else if (ptype === 'msg' || ptype === 'flow' || ptype === 'global' ) { - if (RED.utils.validatePropertyExpression(v)) { - return true; - } - if (opt && opt.label) { - return RED._("validator.errors.invalid-prop-prop", { - prop: opt.label - }); - } - return opt ? RED._("validator.errors.invalid-prop") : false; - } else if (ptype === 'num') { - if (/^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/.test(v)) { - return true; - } - if (opt && opt.label) { - return RED._("validator.errors.invalid-num-prop", { - prop: opt.label - }); - } - return opt ? RED._("validator.errors.invalid-num") : false; + const result = RED.utils.validateTypedProperty(v, ptype, opt) + if (result === true || opt) { + // Valid, or opt provided - return result as-is + return result } - return true; - }; + // No opt - need to return false for backwards compatibilty + return false + } } }; +} catch(err) { + console.log(err) + debugger; +} \ No newline at end of file diff --git a/packages/node_modules/@node-red/nodes/core/function/10-switch.html b/packages/node_modules/@node-red/nodes/core/function/10-switch.html index 94b1ade33..01ec78e19 100644 --- a/packages/node_modules/@node-red/nodes/core/function/10-switch.html +++ b/packages/node_modules/@node-red/nodes/core/function/10-switch.html @@ -167,7 +167,33 @@ label:RED._("node-red:common.label.payload"), validate: RED.validators.typedInput("propertyType", false)}, propertyType: { value:"msg" }, - rules: {value:[{t:"eq", v:"", vt:"str"}]}, + rules: { + value:[{t:"eq", v:"", vt:"str"}], + validate: function (rules, opt) { + let msg; + const errors = [] + if (!rules || rules.length === 0) { return true } + for (var i=0;i (function() { - function isInvalidProperty(v,vt) { - if (/msg|flow|global/.test(vt)) { - if (!RED.utils.validatePropertyExpression(v)) { - return RED._("node-red:change.errors.invalid-prop", { - property: v - }); - } - } else if (vt === "jsonata") { - try{ jsonata(v); } catch(e) { - return RED._("node-red:change.errors.invalid-expr", { - error: e.message - }); - } - } else if (vt === "json") { - try{ JSON.parse(v); } catch(e) { - return RED._("node-red:change.errors.invalid-json-data", { - error: e.message - }); - } - } - return false; - } - RED.nodes.registerType('change', { color: "#E2D96E", category: 'function', defaults: { name: {value:""}, - rules:{value:[{t:"set",p:"payload",pt:"msg",to:"",tot:"str"}],validate: function(rules, opt) { - var msg; - if (!rules || rules.length === 0) { return true } - for (var i=0;i