1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Merge pull request #4368 from node-red/4340-switch-validation

Improve validation of switch/change node rules
This commit is contained in:
Nick O'Leary 2023-09-25 18:04:19 +01:00 committed by GitHub
commit c261f6625a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 113 additions and 113 deletions

View File

@ -1215,11 +1215,9 @@
"validator": { "validator": {
"errors": { "errors": {
"invalid-json": "Invalid JSON data: __error__", "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": "Invalid property expression",
"invalid-prop-prop": "__prop__: invalid property expression",
"invalid-num": "Invalid number", "invalid-num": "Invalid number",
"invalid-num-prop": "__prop__: invalid number",
"invalid-regexp": "Invalid input pattern", "invalid-regexp": "Invalid input pattern",
"invalid-regex-prop": "__prop__: invalid input pattern", "invalid-regex-prop": "__prop__: invalid input pattern",
"missing-required-prop": "__prop__: property value missing", "missing-required-prop": "__prop__: property value missing",

View File

@ -1215,11 +1215,8 @@
"validator": { "validator": {
"errors": { "errors": {
"invalid-json": "Données JSON invalides : __error__", "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": "Expression de propriété non valide",
"invalid-prop-prop": "__prop__: expression de propriété invalide",
"invalid-num": "Numéro invalide", "invalid-num": "Numéro invalide",
"invalid-num-prop": "__prop__: numéro invalide",
"invalid-regexp": "Modèle d'entrée non valide", "invalid-regexp": "Modèle d'entrée non valide",
"invalid-regex-prop": "__prop__: 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", "missing-required-prop": "__prop__: valeur de la propriété manquante",

View File

@ -1215,11 +1215,8 @@
"validator": { "validator": {
"errors": { "errors": {
"invalid-json": "JSONデータが不正: __error__", "invalid-json": "JSONデータが不正: __error__",
"invalid-json-prop": "__prop__: JSONデータが不正: __error__",
"invalid-prop": "プロパティ式が不正", "invalid-prop": "プロパティ式が不正",
"invalid-prop-prop": "__prop__: プロパティ式が不正",
"invalid-num": "数値が不正", "invalid-num": "数値が不正",
"invalid-num-prop": "__prop__: 数値が不正",
"invalid-regexp": "入力パターンが不正", "invalid-regexp": "入力パターンが不正",
"invalid-regex-prop": "__prop__: 入力パターンが不正", "invalid-regex-prop": "__prop__: 入力パターンが不正",
"missing-required-prop": "__prop__: プロパティが未設定", "missing-required-prop": "__prop__: プロパティが未設定",

View File

@ -1186,11 +1186,8 @@
"validator": { "validator": {
"errors": { "errors": {
"invalid-json": "Dados JSON inválidos: __error__", "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": "Expressão de propriedade inválida",
"invalid-prop-prop": "__prop__: expressão de propriedade inválida",
"invalid-num": "Número inválido", "invalid-num": "Número inválido",
"invalid-num-prop": "__prop__: número inválido",
"invalid-regexp": "Padrão de entrada inválido", "invalid-regexp": "Padrão de entrada inválido",
"invalid-regex-prop": "__prop__: Padrão de entrada inválido", "invalid-regex-prop": "__prop__: Padrão de entrada inválido",
"missing-required-prop": "__prop__: valor de propriedade ausente", "missing-required-prop": "__prop__: valor de propriedade ausente",

View File

@ -1199,11 +1199,8 @@
"validator": { "validator": {
"errors": { "errors": {
"invalid-json": "无效的 JSON 数据: __error__", "invalid-json": "无效的 JSON 数据: __error__",
"invalid-json-prop": "__prop__: 无效的 JSON 数据: __error__",
"invalid-prop": "无效的属性表达式", "invalid-prop": "无效的属性表达式",
"invalid-prop-prop": "__prop__: 无效的属性表达式",
"invalid-num": "无效的数字", "invalid-num": "无效的数字",
"invalid-num-prop": "__prop__: 无效的数字",
"invalid-regexp": "输入格式无效", "invalid-regexp": "输入格式无效",
"invalid-regex-prop": "__prop__: 输入格式无效", "invalid-regex-prop": "__prop__: 输入格式无效",
"missing-required-prop": "__prop__: 缺少属性值", "missing-required-prop": "__prop__: 缺少属性值",

View File

@ -115,8 +115,9 @@ RED.editor = (function() {
var valid = validateNodeProperty(node, definition, prop, properties[prop]); var valid = validateNodeProperty(node, definition, prop, properties[prop]);
if ((typeof valid) === "string") { if ((typeof valid) === "string") {
result.push(valid); result.push(valid);
} } else if (Array.isArray(valid)) {
else if(!valid) { result = result.concat(valid)
} else if(!valid) {
result.push(prop); result.push(prop);
} }
} }
@ -165,7 +166,7 @@ RED.editor = (function() {
// If the validator takes two arguments, it is a 3.x validator that // If the validator takes two arguments, it is a 3.x validator that
// can return a String to mean 'invalid' and provide a reason // can return a String to mean 'invalid' and provide a reason
if ((definition[property].validate.length === 2) && if ((definition[property].validate.length === 2) &&
((typeof valid) === "string")) { ((typeof valid) === "string") || Array.isArray(valid)) {
return valid; return valid;
} else { } else {
// Otherwise, a 2.x returns a truth-like/false-like value that // Otherwise, a 2.x returns a truth-like/false-like value that

View File

@ -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) { function getMessageProperty(msg,expr) {
var result = null; var result = null;
var msgPropParts; var msgPropParts;
@ -1451,6 +1496,7 @@ RED.utils = (function() {
getDarkerColor: getDarkerColor, getDarkerColor: getDarkerColor,
parseModuleList: parseModuleList, parseModuleList: parseModuleList,
checkModuleAllowed: checkModuleAllowed, checkModuleAllowed: checkModuleAllowed,
getBrowserInfo: getBrowserInfo getBrowserInfo: getBrowserInfo,
validateTypedProperty: validateTypedProperty
} }
})(); })();

View File

@ -43,43 +43,13 @@ RED.validators = {
typedInput: function(ptypeName,isConfig,mopt) { typedInput: function(ptypeName,isConfig,mopt) {
return function(v, opt) { return function(v, opt) {
var ptype = $("#node-"+(isConfig?"config-":"")+"input-"+ptypeName).val() || this[ptypeName]; var ptype = $("#node-"+(isConfig?"config-":"")+"input-"+ptypeName).val() || this[ptypeName];
if (ptype === 'json') { const result = RED.utils.validateTypedProperty(v, ptype, opt)
try { if (result === true || opt) {
JSON.parse(v); // Valid, or opt provided - return result as-is
return true; return result
} 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;
} }
return true; // No opt - need to return false for backwards compatibilty
}; return false
}
} }
}; };

View File

@ -167,7 +167,33 @@
label:RED._("node-red:common.label.payload"), label:RED._("node-red:common.label.payload"),
validate: RED.validators.typedInput("propertyType", false)}, validate: RED.validators.typedInput("propertyType", false)},
propertyType: { value:"msg" }, 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<rules.length;i++) {
const opt = { label: RED._('node-red:switch.label.rule')+' '+(i+1) }
const r = rules[i];
if (r.hasOwnProperty('v')) {
if ((msg = RED.utils.validateTypedProperty(r.v,r.vt,opt)) !== true) {
errors.push(msg)
}
}
if (r.hasOwnProperty('v2')) {
if ((msg = RED.utils.validateTypedProperty(r.v2,r.v2t,opt)) !== true) {
errors.push(msg)
}
}
}
if (errors.length) {
console.log(errors)
return errors
}
return true;
}
},
checkall: {value:"true", required:true}, checkall: {value:"true", required:true},
repair: {value:false}, repair: {value:false},
outputs: {value:1} outputs: {value:1}

View File

@ -19,71 +19,42 @@
<script type="text/javascript"> <script type="text/javascript">
(function() { (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', { RED.nodes.registerType('change', {
color: "#E2D96E", color: "#E2D96E",
category: 'function', category: 'function',
defaults: { defaults: {
name: {value:""}, name: {value:""},
rules:{value:[{t:"set",p:"payload",pt:"msg",to:"",tot:"str"}],validate: function(rules, opt) { rules:{
var msg; value:[{t:"set",p:"payload",pt:"msg",to:"",tot:"str"}],
if (!rules || rules.length === 0) { return true } validate: function(rules, opt) {
for (var i=0;i<rules.length;i++) { let msg;
var r = rules[i]; const errors = []
if (r.t === 'set') { if (!rules || rules.length === 0) { return true }
if (msg = isInvalidProperty(r.p,r.pt)) { for (var i=0;i<rules.length;i++) {
return msg; const opt = { label: RED._('node-red:change.label.rule')+' '+(i+1) }
const r = rules[i];
if (r.t === 'set' || r.t === 'change' || r.t === 'delete' || r.t === 'move') {
if ((msg = RED.utils.validateTypedProperty(r.p,r.pt,opt)) !== true) {
errors.push(msg)
}
} }
if (msg = isInvalidProperty(r.to,r.tot)) { if (r.t === 'set' || r.t === 'change' || r.t === 'move') {
return msg; if ((msg = RED.utils.validateTypedProperty(r.to,r.tot,opt)) !== true) {
errors.push(msg)
}
} }
} else if (r.t === 'change') { if (r.t === 'change') {
if (msg = isInvalidProperty(r.p,r.pt)) { if ((msg = RED.utils.validateTypedProperty(r.from,r.fromt,opt)) !== true) {
return msg; errors.push(msg)
} }
if(msg = isInvalidProperty(r.from,r.fromt)) {
return msg;
}
if(msg = isInvalidProperty(r.to,r.tot)) {
return msg;
}
} else if (r.t === 'delete') {
if (msg = isInvalidProperty(r.p,r.pt)) {
return msg;
}
} else if (r.t === 'move') {
if (msg = isInvalidProperty(r.p,r.pt)) {
return msg;
}
if (msg = isInvalidProperty(r.to,r.tot)) {
return msg;
} }
} }
if (errors.length) {
return errors
}
return true;
} }
return true; },
}},
// legacy // legacy
action: {value:""}, action: {value:""},
property: {value:""}, property: {value:""},