From 9f98b4b082db0a56740941072cda02eb65eb3d5d Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Mon, 14 Feb 2022 10:40:49 +0900 Subject: [PATCH 1/4] add support of property validation message --- .../editor-client/locales/en-US/editor.json | 16 ++++ .../editor-client/locales/ja/editor.json | 16 ++++ .../editor-client/src/js/ui/common/popover.js | 5 +- .../editor-client/src/js/ui/editor.js | 70 +++++++++++++++- .../editor-client/src/js/validators.js | 82 +++++++++++++++---- .../nodes/core/common/20-inject.html | 29 +++++-- .../@node-red/nodes/core/common/60-link.html | 6 +- .../nodes/core/function/10-function.html | 26 ++++-- .../nodes/core/function/10-switch.html | 6 +- .../nodes/core/function/15-change.html | 52 +++++++++--- .../nodes/core/function/16-range.html | 27 ++++-- .../nodes/core/function/80-template.html | 5 +- .../nodes/core/function/89-delay.html | 50 +++++++++-- .../nodes/core/function/89-trigger.html | 20 ++++- .../@node-red/nodes/core/function/rbe.html | 11 ++- .../@node-red/nodes/core/network/05-tls.html | 14 +++- .../nodes/core/network/06-httpproxy.html | 11 ++- .../@node-red/nodes/core/network/10-mqtt.html | 39 ++++++--- .../nodes/core/network/21-httpin.html | 9 +- .../nodes/core/network/21-httprequest.html | 18 +++- .../nodes/core/network/22-websocket.html | 27 ++++-- .../nodes/core/network/31-tcpin.html | 53 ++++++++++-- .../@node-red/nodes/core/network/32-udp.html | 18 +++- .../@node-red/nodes/core/parsers/70-CSV.html | 7 +- .../@node-red/nodes/core/parsers/70-JSON.html | 3 +- .../@node-red/nodes/core/parsers/70-XML.html | 3 +- .../@node-red/nodes/core/parsers/70-YAML.html | 3 +- .../nodes/core/sequence/17-split.html | 7 +- .../nodes/core/sequence/19-batch.html | 30 ++++++- .../nodes/core/storage/23-watch.html | 3 +- .../nodes/locales/en-US/messages.json | 73 +++++++++++++---- .../@node-red/nodes/locales/ja/messages.json | 74 +++++++++++++---- 32 files changed, 672 insertions(+), 141 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 1ab148217..2db512ec8 100755 --- 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 @@ -1156,5 +1156,21 @@ "ru": "Russian", "zh-CN": "Chinese(Simplified)", "zh-TW": "Chinese(Traditional)" + }, + "validator": { + "errors": { + "invalid-json": "Invalid JSON data: __error__", + "invalid-json-prop": "__prop__: invalid JSON data: __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", + "invalid-config": "__prop__: invalid configuration node", + "missing-config": "__prop__: missing configuration node", + "validation-error": "__prop__: validation error: __node__, __id__: __error__" + } } } 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 04818601e..acf432b53 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 @@ -1288,5 +1288,21 @@ "zoom-out": "ズームアウト", "zoom-reset": "ズームリセット", "toggle-navigator": "ナビゲータ表示切替" + }, + "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__: プロパティが未設定", + "invalid-config": "__prop__: 設定ノードが不正", + "missing-config": "__prop__: 設定ノードが存在しません", + "validation-error": "__prop__: チェックエラー: __node__, __id__: __error__" + } } } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js index f060af37d..53b52afe7 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js @@ -349,7 +349,6 @@ RED.popover = (function() { } } } - if (trigger === 'hover') { target.on('mouseenter',function(e) { clearTimeout(timer); @@ -461,6 +460,10 @@ RED.popover = (function() { popover.setAction = function(newAction) { action = newAction; } + popover.delete = function() { + target.off("mouseenter"); + target.off("mouseleave"); + }; return popover; }, 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 6017fb687..a9cda7069 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 @@ -110,7 +110,11 @@ RED.editor = (function() { var result = []; for (var prop in definition) { if (definition.hasOwnProperty(prop)) { - if (!validateNodeProperty(node, definition, prop, properties[prop])) { + var valid = validateNodeProperty(node, definition, prop, properties[prop]); + if ((typeof valid) === "string") { + result.push(valid); + } + else if(!valid) { result.push(prop); } } @@ -136,22 +140,64 @@ RED.editor = (function() { if (/^\$\{[a-zA-Z_][a-zA-Z0-9_]*\}$/.test(value)) { return true; } + var label = null; + if (("label" in definition[property]) && + ((typeof definition[property].label) == "string")) { + label = definition[property].label; + } if ("required" in definition[property] && definition[property].required) { valid = value !== ""; + if (!valid && label) { + return RED._("validator.errors.missing-required-prop", { + prop: label + }); + } } if (valid && "validate" in definition[property]) { try { - valid = definition[property].validate.call(node,value); + var opt = {}; + valid = definition[property].validate.call(node,value, opt); + if ((typeof valid) === "string") { + return valid; + } } catch(err) { console.log("Validation error:",node.type,node.id,"property: "+property,"value:",value,err); + return RED._("validator.errors.validation-error", { + prop: property, + node: node.type, + id: node.id, + error: err.message + }); } } if (valid && definition[property].type && RED.nodes.getType(definition[property].type) && !("validate" in definition[property])) { if (!value || value == "_ADD_") { valid = definition[property].hasOwnProperty("required") && !definition[property].required; + if (!valid && label) { + return RED._("validator.errors.missing-required-prop", { + prop: label + }); + } } else { var configNode = RED.nodes.node(value); - valid = (configNode && (configNode.valid == null || configNode.valid)); + if (configNode) { + if ((configNode.valid == null) || configNode.valid) { + return true; + } + if (label) { + return RED._("validator.errors.invalid-config", { + prop: label + }); + } + } + else { + if (label) { + return RED._("validator.errors.missing-config", { + prop: label + }); + } + } + return false; } } return valid; @@ -179,10 +225,26 @@ RED.editor = (function() { if (defaults[property].hasOwnProperty("format") && defaults[property].format !== "" && input[0].nodeName === "DIV") { value = input.text(); } - if (!validateNodeProperty(node, defaults, property,value)) { + var valid = validateNodeProperty(node, defaults, property,value); + if (((typeof valid) === "string") || !valid) { input.addClass("input-error"); + if ((typeof valid) === "string") { + var tooltip = input.data("tooltip"); + if (tooltip) { + tooltip.setContent(valid); + } + else { + tooltip = RED.popover.tooltip(input, valid); + input.data("tooltip", tooltip); + } + } } else { input.removeClass("input-error"); + var tooltip = input.data("tooltip"); + if (tooltip) { + input.data("tooltip", null); + tooltip.delete(); + } } } } 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 833472b24..5cbb6211e 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 @@ -14,22 +14,72 @@ * limitations under the License. **/ RED.validators = { - number: function(blankAllowed){return function(v) { return (blankAllowed&&(v===''||v===undefined)) || (v!=='' && !isNaN(v));}}, - regex: function(re){return function(v) { return re.test(v);}}, - typedInput: function(ptypeName,isConfig) { return function(v) { - var ptype = $("#node-"+(isConfig?"config-":"")+"input-"+ptypeName).val() || this[ptypeName]; - if (ptype === 'json') { - try { - JSON.parse(v); + number: function(blankAllowed,mopt){ + return function(v, opt) { + if ((blankAllowed&&(v===''||v===undefined)) || (v!=='' && !isNaN(v))) { return true; - } catch(err) { - return false; } - } else if (ptype === 'msg' || ptype === 'flow' || ptype === 'global' ) { - return RED.utils.validatePropertyExpression(v); - } else if (ptype === 'num') { - return /^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/.test(v); - } - return true; - }} + if (mopt && mopt.property) { + return opt ? RED._("validator.errors.invalid-num-prop", { + prop: mopt.property + }) : false; + } + return opt ? RED._("validator.errors.invalid-num") : false; + }; + }, + regex: function(re, mopt) { + return function(v, opt) { + if (re.test(v)) { + return true; + } + if (mopt && mopt.property) { + return opt ? RED._("validator.errors.invalid-regex-prop", { + prop: mopt.property + }) : false; + } + return opt ? RED._("validator.errors.invalid-regexp") : false; + }; + }, + 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 (mopt && mopt.property) { + return opt ? RED._("validator.errors.invalid-json-prop", { + error: err.message, + prop: mopt.property, + }) : false; + } + 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 (mopt && mopt.property) { + return opt ? RED._("validator.errors.invalid-prop-prop", { + prop: mopt.property + }) : false; + } + 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 (mopt && mopt.property) { + return opt ? RED._("validator.errors.invalid-num-prop", { + prop: mopt.property + }) : false; + } + return opt ? RED._("validator.errors.invalid-num") : false; + } + return true; + }; + } }; diff --git a/packages/node_modules/@node-red/nodes/core/common/20-inject.html b/packages/node_modules/@node-red/nodes/core/common/20-inject.html index f233c62fd..24f331ca1 100644 --- a/packages/node_modules/@node-red/nodes/core/common/20-inject.html +++ b/packages/node_modules/@node-red/nodes/core/common/20-inject.html @@ -225,28 +225,45 @@ color:"#a6bbcf", defaults: { name: {value:""}, - props:{value:[{p:"payload"},{p:"topic",vt:"str"}], validate:function(v) { + props:{value:[{p:"payload"},{p:"topic",vt:"str"}], validate:function(v, opt) { if (!v || v.length === 0) { return true } for (var i=0;i= 0) && (v <= 2147483))) }}, + repeat: { + value:"", validate: function(v, opt) { + if ((v === "") || + (RED.validators.number(v) && + (v >= 0) && (v <= 2147483))) { + return true; + } + return RED._("node-red:inject.errors.invalid-repeat"); + } + }, crontab: {value:""}, once: {value:false}, onceDelay: {value:0.1}, topic: {value:""}, - payload: {value:"", validate: RED.validators.typedInput("payloadType")}, + payload: {value:"", validate: RED.validators.typedInput("payloadType", false, { + property: "payload" + }) }, payloadType: {value:"date"}, }, icon: "inject.svg", diff --git a/packages/node_modules/@node-red/nodes/core/common/60-link.html b/packages/node_modules/@node-red/nodes/core/common/60-link.html index f3fabba59..9de1dc0a7 100644 --- a/packages/node_modules/@node-red/nodes/core/common/60-link.html +++ b/packages/node_modules/@node-red/nodes/core/common/60-link.html @@ -259,7 +259,11 @@ defaults: { name: {value:""}, links: { value: [], type:"link in[]"}, - timeout: { value: "30", validate:RED.validators.number(true) } + timeout: { value: "30", + validate:RED.validators.number(true, { + property: RED._("node-red:link.timeout") + }) + } }, inputs: 1, outputs: 1, diff --git a/packages/node_modules/@node-red/nodes/core/function/10-function.html b/packages/node_modules/@node-red/nodes/core/function/10-function.html index a1ed14f6d..e3407b1fa 100644 --- a/packages/node_modules/@node-red/nodes/core/function/10-function.html +++ b/packages/node_modules/@node-red/nodes/core/function/10-function.html @@ -358,24 +358,38 @@ name: {value:""}, func: {value:"\nreturn msg;"}, outputs: {value:1}, - noerr: {value:0,required:true,validate:function(v) { return !v; }}, + noerr: {value:0,required:true, + validate: function(v, opt) { + if (!v) { + return true; + } + return RED._("node-red:function.error.invalid-js"); + }}, initialize: {value:""}, finalize: {value:""}, - libs: {value: [], validate: function(v) { + libs: {value: [], validate: function(v, opt) { if (!v) { return true; } for (var i=0,l=v.length;i -1) { - return false; + return RED._("node-red:function.error.missing-module", { + module: m.module + }); } if (invalidModuleVNames.indexOf(m.var) !== -1){ - return false; + return RED._("node-red:function.error.moduleNameError", { + name: m.var + }); } } return true; 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 f6fc82f0a..438e3ea41 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 @@ -163,7 +163,11 @@ category: 'function', defaults: { name: {value:""}, - property: {value:"payload", required:true, validate: RED.validators.typedInput("propertyType")}, + property: {value:"payload", required:true, + label:RED._("node-red:common.label.payload"), + validate: RED.validators.typedInput("propertyType", false, { + property: RED._("node-red:common.label.payload") + })}, propertyType: { value:"msg" }, rules: {value:[{t:"eq", v:"", vt:"str"}]}, checkall: {value:"true", required:true}, diff --git a/packages/node_modules/@node-red/nodes/core/function/15-change.html b/packages/node_modules/@node-red/nodes/core/function/15-change.html index b40039028..e53f522d9 100644 --- a/packages/node_modules/@node-red/nodes/core/function/15-change.html +++ b/packages/node_modules/@node-red/nodes/core/function/15-change.html @@ -19,38 +19,66 @@