From fca77a868faed8189051f4cd2810fd995234a993 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 1 Mar 2017 15:01:07 +0000 Subject: [PATCH 1/3] Allow a node to declare settings that should be exported --- nodes/core/io/21-httprequest.js | 8 ++++++++ red/api/info.js | 2 ++ red/runtime/nodes/index.js | 9 +++++++-- red/runtime/settings.js | 22 ++++++++++++++++++++++ test/red/api/index_spec.js | 2 +- test/red/api/info_spec.js | 11 ++++++++--- 6 files changed, 48 insertions(+), 6 deletions(-) diff --git a/nodes/core/io/21-httprequest.js b/nodes/core/io/21-httprequest.js index d6d6f42de..873e3e98e 100644 --- a/nodes/core/io/21-httprequest.js +++ b/nodes/core/io/21-httprequest.js @@ -216,6 +216,14 @@ module.exports = function(RED) { credentials: { user: {type:"text"}, password: {type: "password"} + }, + settings: { + httpRequestColour: { + value: "red", + // validate: function(v) { return IT MUST BE A NUMBER }, + // required: false, + exportable: true + } } }); } diff --git a/red/api/info.js b/red/api/info.js index e2661bdc3..9953ec1a2 100644 --- a/red/api/info.js +++ b/red/api/info.js @@ -48,6 +48,8 @@ module.exports = { safeSettings.editorTheme.palette = safeSettings.editorTheme.palette || {}; safeSettings.editorTheme.palette.editable = false; } + + settings.exportNodeSettings(safeSettings); res.json(safeSettings); } diff --git a/red/runtime/nodes/index.js b/red/runtime/nodes/index.js index 4fd0d1128..5be548549 100644 --- a/red/runtime/nodes/index.js +++ b/red/runtime/nodes/index.js @@ -50,8 +50,13 @@ function registerType(nodeSet,type,constructor,opts) { type = nodeSet; nodeSet = ""; } - if (opts && opts.credentials) { - credentials.register(type,opts.credentials); + if (opts) { + if (opts.credentials) { + credentials.register(type,opts.credentials); + } + if (opts.settings) { + settings.registerNodeSettings(type,opts.settings); + } } registry.registerType(nodeSet,type,constructor); } diff --git a/red/runtime/settings.js b/red/runtime/settings.js index bbb1031fd..0f8d21d65 100644 --- a/red/runtime/settings.js +++ b/red/runtime/settings.js @@ -21,6 +21,7 @@ var log = require("./log"); var userSettings = null; var globalSettings = null; +var nodeSettings = null; var storage = null; var persistentSettings = { @@ -38,6 +39,7 @@ var persistentSettings = { } } globalSettings = null; + nodeSettings = {}; }, load: function(_storage) { storage = _storage; @@ -99,6 +101,26 @@ var persistentSettings = { userSettings = null; globalSettings = null; storage = null; + }, + registerNodeSettings: function(type, opts) { + //console.log(type,opts); + // 1. TODO: validate the option names are allowed for the node type + + // 2. store this information against the node type + nodeSettings[type] = opts; + + + // TODO: remove the node settings if the node is disabled/removed from runtime + }, + exportNodeSettings: function(safeSettings) { + // 1. forEach type in nodeSettings... + // 2. forEach setting for that type... + // 3. if globalSettings has a property with the required name... + // 4. set safeSettings.property to that value + // 5. else if the setting has a default 'value' provided + // 6. set safeSettings.property to that value + + return safeSettings; } } diff --git a/test/red/api/index_spec.js b/test/red/api/index_spec.js index 0b564da73..8edd737d6 100644 --- a/test/red/api/index_spec.js +++ b/test/red/api/index_spec.js @@ -29,7 +29,7 @@ describe("api index", function() { describe("disables editor", function() { before(function() { api.init({},{ - settings:{httpNodeRoot:true, httpAdminRoot: true,disableEditor:true}, + settings:{httpNodeRoot:true, httpAdminRoot: true,disableEditor:true, exportNodeSettings: function() {}}, events: {on:function(){},removeListener: function(){}}, log: {info:function(){},_:function(){}}, nodes: {paletteEditorEnabled: function(){return true}} diff --git a/test/red/api/info_spec.js b/test/red/api/info_spec.js index 70f477a35..c222aec5c 100644 --- a/test/red/api/info_spec.js +++ b/test/red/api/info_spec.js @@ -42,7 +42,10 @@ describe("info api", function() { foo: 123, httpNodeRoot: "testHttpNodeRoot", version: "testVersion", - paletteCategories :["red","blue","green"] + paletteCategories :["red","blue","green"], + exportNodeSettings: function(obj) { + obj.testNodeSetting = "helloWorld"; + } }, nodes: { paletteEditorEnabled: function() { return true; } @@ -59,7 +62,9 @@ describe("info api", function() { res.body.should.have.property("version","testVersion"); res.body.should.have.property("paletteCategories",["red","blue","green"]); res.body.should.have.property("editorTheme",{test:456}); + res.body.should.have.property("testNodeSetting","helloWorld"); res.body.should.not.have.property("foo",123); + done(); }); }); @@ -68,8 +73,8 @@ describe("info api", function() { settings: { httpNodeRoot: "testHttpNodeRoot", version: "testVersion", - paletteCategories :["red","blue","green"] - + paletteCategories :["red","blue","green"], + exportNodeSettings: function() {} }, nodes: { paletteEditorEnabled: function() { return false; } From 34089aec70912232043508097bea79fc70114487 Mon Sep 17 00:00:00 2001 From: Kazuki-Nakanishi Date: Wed, 8 Mar 2017 14:38:33 +0000 Subject: [PATCH 2/3] Allow a node to declare what settings should be made available to the editor. (#1185) * Implement register/exportNodeSettings. * Change normaliseRegisterTypeName to normaliseNodeTypeName. Force it to name in a camel case. --- nodes/core/io/21-httprequest.js | 8 --- red/runtime/nodes/registry/registry.js | 2 + red/runtime/settings.js | 56 +++++++++++++----- red/runtime/util.js | 15 ++++- test/red/runtime/settings_spec.js | 81 ++++++++++++++++++++++++++ test/red/runtime/util_spec.js | 17 ++++++ 6 files changed, 156 insertions(+), 23 deletions(-) diff --git a/nodes/core/io/21-httprequest.js b/nodes/core/io/21-httprequest.js index 873e3e98e..d6d6f42de 100644 --- a/nodes/core/io/21-httprequest.js +++ b/nodes/core/io/21-httprequest.js @@ -216,14 +216,6 @@ module.exports = function(RED) { credentials: { user: {type:"text"}, password: {type: "password"} - }, - settings: { - httpRequestColour: { - value: "red", - // validate: function(v) { return IT MUST BE A NUMBER }, - // required: false, - exportable: true - } } }); } diff --git a/red/runtime/nodes/registry/registry.js b/red/runtime/nodes/registry/registry.js index fbba8d95e..5372d7be6 100644 --- a/red/runtime/nodes/registry/registry.js +++ b/red/runtime/nodes/registry/registry.js @@ -493,6 +493,7 @@ function enableNodeSet(typeOrId) { delete config.err; config.enabled = true; nodeConfigCache = null; + settings.enableNodeSettings(config.types); return saveNodeList().then(function() { return filterNodeInfo(config); }); @@ -515,6 +516,7 @@ function disableNodeSet(typeOrId) { // TODO: persist setting config.enabled = false; nodeConfigCache = null; + settings.disableNodeSettings(config.types); return saveNodeList().then(function() { return filterNodeInfo(config); }); diff --git a/red/runtime/settings.js b/red/runtime/settings.js index 0f8d21d65..ce0a14f97 100644 --- a/red/runtime/settings.js +++ b/red/runtime/settings.js @@ -18,10 +18,12 @@ var when = require("when"); var clone = require("clone"); var assert = require("assert"); var log = require("./log"); +var util = require("./util"); var userSettings = null; var globalSettings = null; var nodeSettings = null; +var disableNodeSettings = null; var storage = null; var persistentSettings = { @@ -40,6 +42,7 @@ var persistentSettings = { } globalSettings = null; nodeSettings = {}; + disableNodeSettings = {}; }, load: function(_storage) { storage = _storage; @@ -103,24 +106,49 @@ var persistentSettings = { storage = null; }, registerNodeSettings: function(type, opts) { - //console.log(type,opts); - // 1. TODO: validate the option names are allowed for the node type - - // 2. store this information against the node type - nodeSettings[type] = opts; - - - // TODO: remove the node settings if the node is disabled/removed from runtime + try { + for (var property in opts) { + if (opts.hasOwnProperty(property)) { + var normalisedType = util.normaliseNodeTypeName(type); + if (!property.startsWith(normalisedType)) { + throw new Error("The name of node setting property " + property + " must start with \"" + normalisedType + "\" (case sensitive)."); + } + } + } + nodeSettings[type] = opts; + } catch (err) { + console.log(err.toString()); + } }, exportNodeSettings: function(safeSettings) { - // 1. forEach type in nodeSettings... - // 2. forEach setting for that type... - // 3. if globalSettings has a property with the required name... - // 4. set safeSettings.property to that value - // 5. else if the setting has a default 'value' provided - // 6. set safeSettings.property to that value + safeSettings["nodeSettings"] = {}; + for (var type in nodeSettings) { + if (nodeSettings.hasOwnProperty(type) && !disableNodeSettings[type]) { + var nodeTypeSettings = nodeSettings[type]; + for (var property in nodeTypeSettings) { + if (nodeTypeSettings.hasOwnProperty(property)) { + var setting = nodeTypeSettings[property]; + if (userSettings.hasOwnProperty(property)) { + safeSettings["nodeSettings"][property] = userSettings[property]; + } else if (setting.exportable) { + safeSettings["nodeSettings"][property] = setting.value; + } + } + } + } + } return safeSettings; + }, + enableNodeSettings: function(types) { + types.forEach(function(type) { + disableNodeSettings[type] = false; + }); + }, + disableNodeSettings: function(types) { + types.forEach(function(type) { + disableNodeSettings[type] = true; + }); } } diff --git a/red/runtime/util.js b/red/runtime/util.js index cc3cff330..55c3d8a57 100644 --- a/red/runtime/util.js +++ b/red/runtime/util.js @@ -328,6 +328,18 @@ function evaluateNodeProperty(value, type, node, msg) { return value; } +function normaliseNodeTypeName(name) { + var result = name.replace(/[^a-zA-Z0-9]/g, " "); + result = result.trim(); + result = result.replace(/ +/g, " "); + result = result.replace(/ ./g, + function(s) { + return s.charAt(1).toUpperCase(); + } + ); + result = result.charAt(0).toLowerCase() + result.slice(1); + return result; +} module.exports = { ensureString: ensureString, @@ -338,5 +350,6 @@ module.exports = { getMessageProperty: getMessageProperty, setMessageProperty: setMessageProperty, evaluateNodeProperty: evaluateNodeProperty, - normalisePropertyExpression: normalisePropertyExpression + normalisePropertyExpression: normalisePropertyExpression, + normaliseNodeTypeName: normaliseNodeTypeName }; diff --git a/test/red/runtime/settings_spec.js b/test/red/runtime/settings_spec.js index 3ce086739..1dbb80d15 100644 --- a/test/red/runtime/settings_spec.js +++ b/test/red/runtime/settings_spec.js @@ -142,4 +142,85 @@ describe("red/settings", function() { settings.should.not.have.property("c"); }); + + it('registers node settings and exports them', function() { + var userSettings = {}; + settings.init(userSettings); + settings.registerNodeSettings("inject", {injectColor:{value:"red", exportable:true}, injectSize:{value:"100", exportable:true}} ); + settings.registerNodeSettings("mqtt", {mqttColor:{value:"purple", exportable:false}, mqttSize:{value:"50", exportable:true}} ); + settings.registerNodeSettings("http request", {httpRequest1:{value:"a1", exportable:true}} ); + settings.registerNodeSettings(" http--request<> ", {httpRequest2:{value:"a2", exportable:true}} ); + settings.registerNodeSettings("_http_request_", {httpRequest3:{value:"a3", exportable:true}} ); + settings.registerNodeSettings("mQtT", {mQtTColor:{value:"purple", exportable:true}} ); + settings.registerNodeSettings("abc123", {abc123:{value:"def456", exportable:true}} ); + var safeSettings = {}; + settings.exportNodeSettings(safeSettings); + safeSettings["nodeSettings"].should.have.property("injectColor", "red"); + safeSettings["nodeSettings"].should.have.property("injectSize", "100"); + safeSettings["nodeSettings"].should.not.have.property("mqttColor"); + safeSettings["nodeSettings"].should.have.property("mqttSize", "50"); + safeSettings["nodeSettings"].should.have.property("httpRequest1", "a1"); + safeSettings["nodeSettings"].should.have.property("httpRequest2", "a2"); + safeSettings["nodeSettings"].should.have.property("httpRequest3", "a3"); + safeSettings["nodeSettings"].should.have.property("mQtTColor", "purple"); + safeSettings["nodeSettings"].should.have.property("abc123", "def456"); + }); + + it('prohibits registering the property whose name do not start with type name', function() { + var userSettings = {}; + settings.init(userSettings); + settings.registerNodeSettings("inject", {color:{value:"red", exportable:true}} ); + settings.registerNodeSettings("_a_b_1_", {ab1Color:{value:"red", exportable:true}} ); + settings.registerNodeSettings("AB2", {AB2Color:{value:"red", exportable:true}} ); + settings.registerNodeSettings("abcDef", {abcColor:{value:"red", exportable:true}} ); + var safeSettings = {}; + settings.exportNodeSettings(safeSettings); + safeSettings["nodeSettings"].should.not.have.property("color"); + safeSettings["nodeSettings"].should.not.have.property("ab1Color", "blue"); + safeSettings["nodeSettings"].should.not.have.property("AB2Color"); + safeSettings["nodeSettings"].should.not.have.property("abcColor"); + }); + + it('overwrites node settings with user settings', function() { + var userSettings = { + injectColor: "green", + mqttColor: "yellow", + c: [1,2,3] + } + settings.init(userSettings); + settings.registerNodeSettings("inject", {injectColor:{value:"red", exportable:true}} ); + var safeSettings = {}; + settings.exportNodeSettings(safeSettings); + safeSettings["nodeSettings"].should.have.property("injectColor", "green"); + safeSettings["nodeSettings"].should.not.have.property("mqttColor"); + }); + + it('disables/enables node settings', function() { + var userSettings = {}; + settings.init(userSettings); + + var safeSettings = {}; + settings.registerNodeSettings("inject", {injectColor:{value:"red", exportable:true}} ); + settings.registerNodeSettings("mqtt", {mqttColor:{value:"purple", exportable:true}} ); + settings.registerNodeSettings("http request", {httpRequestColor:{value:"yellow", exportable:true}} ); + settings.exportNodeSettings(safeSettings); + safeSettings["nodeSettings"].should.have.property("injectColor", "red"); + safeSettings["nodeSettings"].should.have.property("mqttColor", "purple"); + safeSettings["nodeSettings"].should.have.property("httpRequestColor", "yellow"); + + var types = ["inject", "mqtt"]; + settings.disableNodeSettings(types); + settings.exportNodeSettings(safeSettings); + safeSettings["nodeSettings"].should.not.have.property("injectColor"); + safeSettings["nodeSettings"].should.not.have.property("mqttColor"); + safeSettings["nodeSettings"].should.have.property("httpRequestColor", "yellow"); + + types = ["inject"]; + settings.enableNodeSettings(types); + settings.exportNodeSettings(safeSettings); + safeSettings["nodeSettings"].should.have.property("injectColor", "red"); + safeSettings["nodeSettings"].should.not.have.property("mqttColor"); + safeSettings["nodeSettings"].should.have.property("httpRequestColor", "yellow"); + }); + }); diff --git a/test/red/runtime/util_spec.js b/test/red/runtime/util_spec.js index 64c1dc6db..ffec6ce0b 100644 --- a/test/red/runtime/util_spec.js +++ b/test/red/runtime/util_spec.js @@ -364,4 +364,21 @@ describe("red/util", function() { it("fail ",function() { testInvalid("");}) }); + + describe('normaliseNodeTypeName', function() { + function normalise(input, expected) { + var result = util.normaliseNodeTypeName(input); + result.should.eql(expected); + } + + it('pass blank',function() { normalise("", "") }); + it('pass ab1',function() { normalise("ab1", "ab1") }); + it('pass AB1',function() { normalise("AB1", "aB1") }); + it('pass a b 1',function() { normalise("a b 1", "aB1") }); + it('pass a-b-1',function() { normalise("a-b-1", "aB1") }); + it('pass ab1 ',function() { normalise(" ab1 ", "ab1") }); + it('pass _a_b_1_',function() { normalise("_a_b_1_", "aB1") }); + it('pass http request',function() { normalise("http request", "httpRequest") }); + it('pass HttpRequest',function() { normalise("HttpRequest", "httpRequest") }); + }); }); From 3b3d696e459fba0235ae7bdbcfa12f4e5a91203d Mon Sep 17 00:00:00 2001 From: Kazuki-Nakanishi Date: Thu, 9 Mar 2017 19:58:34 +0000 Subject: [PATCH 3/3] Add the node setting tlsConfigDisableLocalFiles for tls node. (#1190) * Add the node setting tlsConfigDisableLocalFiles for tls node. * Fix the bug that shows node setting when specified in settings.js and exportable is false. --- nodes/core/io/05-tls.js | 9 ++++++++- red/runtime/settings.js | 10 ++++++---- test/red/runtime/settings_spec.js | 4 +++- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/nodes/core/io/05-tls.js b/nodes/core/io/05-tls.js index a0929d988..9e470d28b 100644 --- a/nodes/core/io/05-tls.js +++ b/nodes/core/io/05-tls.js @@ -48,7 +48,14 @@ module.exports = function(RED) { return; } } - RED.nodes.registerType("tls-config",TLSConfig); + RED.nodes.registerType("tls-config",TLSConfig,{ + settings: { + tlsConfigDisableLocalFiles: { + value: true, + exportable: false + } + } + }); TLSConfig.prototype.addTLSOptions = function(opts) { if (this.valid) { diff --git a/red/runtime/settings.js b/red/runtime/settings.js index ce0a14f97..cb7ae91bc 100644 --- a/red/runtime/settings.js +++ b/red/runtime/settings.js @@ -128,10 +128,12 @@ var persistentSettings = { for (var property in nodeTypeSettings) { if (nodeTypeSettings.hasOwnProperty(property)) { var setting = nodeTypeSettings[property]; - if (userSettings.hasOwnProperty(property)) { - safeSettings["nodeSettings"][property] = userSettings[property]; - } else if (setting.exportable) { - safeSettings["nodeSettings"][property] = setting.value; + if (setting.exportable) { + if (userSettings.hasOwnProperty(property)) { + safeSettings["nodeSettings"][property] = userSettings[property]; + } else { + safeSettings["nodeSettings"][property] = setting.value; + } } } } diff --git a/test/red/runtime/settings_spec.js b/test/red/runtime/settings_spec.js index 1dbb80d15..ec995155b 100644 --- a/test/red/runtime/settings_spec.js +++ b/test/red/runtime/settings_spec.js @@ -185,14 +185,16 @@ describe("red/settings", function() { var userSettings = { injectColor: "green", mqttColor: "yellow", - c: [1,2,3] + abColor: [1,2,3] } settings.init(userSettings); settings.registerNodeSettings("inject", {injectColor:{value:"red", exportable:true}} ); + settings.registerNodeSettings("ab", {abColor:{value:"red", exportable:false}} ); var safeSettings = {}; settings.exportNodeSettings(safeSettings); safeSettings["nodeSettings"].should.have.property("injectColor", "green"); safeSettings["nodeSettings"].should.not.have.property("mqttColor"); + safeSettings["nodeSettings"].should.not.have.property("abColor"); }); it('disables/enables node settings', function() {