diff --git a/packages/node_modules/@node-red/runtime/lib/flows/Flow.js b/packages/node_modules/@node-red/runtime/lib/flows/Flow.js index 489ec42cb..2e1e97c4b 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/Flow.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/Flow.js @@ -416,23 +416,50 @@ class Flow { return this.activeNodes; } - /*! - * Get value of environment variable defined in group node. - * @param {String} group - group node - * @param {String} name - name of variable - * @return {Object} object containing the value in val property or null if not defined + + /** + * Group callback signature + * + * @callback GroupEnvCallback + * @param {Error} err The error object (or null) + * @param {[result: {val:Any}, name: String]} result The result of the callback + * @returns {void} */ - getGroupEnvSetting(node, group, name) { + + /** + * @function getGroupEnvSetting + * Get a group setting value synchronously. + * This currently automatically defers to the parent + * @overload + * @param {Object} node + * @param {Object} group + * @param {String} name + * @returns {Any} + * + * Get a group setting value asynchronously. + * @overload + * @param {Object} node + * @param {Object} group + * @param {String} name + * @param {GroupEnvCallback} callback + * @returns {void} + */ + + getGroupEnvSetting(node, group, name, callback) { + /** @type {GroupEnvCallback} */ + const returnOrCallback = (err, [result, newName]) => { + if (callback) { + callback(err, [result, newName]); + return + } + return [result, newName]; + } if (group) { if (name === "NR_GROUP_NAME") { - return [{ - val: group.name - }, null]; + return returnOrCallback(null, [{ val: group.name }, null]); } if (name === "NR_GROUP_ID") { - return [{ - val: group.id - }, null]; + return returnOrCallback(null, [{ val: group.id }, null]); } if (group.credentials === undefined) { @@ -457,33 +484,32 @@ class Flow { if (env) { let value = env.value; const type = env.type; - if ((type !== "env") || - (value !== name)) { + if ((type !== "env") || (value !== name)) { if (type === "env") { value = value.replace(new RegExp("\\${"+name+"}","g"),"${$parent."+name+"}"); - } - if (type === "bool") { - const val - = ((value === "true") || - (value === true)); - return [{ - val: val - }, null]; + } else if (type === "bool") { + const val = ((value === "true") || (value === true)); + return returnOrCallback(null, [{ val: val }, null]) } if (type === "cred") { - return [{ - val: value - }, null]; + return returnOrCallback(null, [{ val: value }, null]) } try { - var val = redUtil.evaluateNodeProperty(value, type, node, null, null); - return [{ - val: val - }, null]; + if (!callback) { + var val = redUtil.evaluateNodeProperty(value, type, node, null, null); + return [{ val: val }, null]; + } else { + redUtil.evaluateNodeProperty(value, type, node, null, (err, value) => { + return returnOrCallback(err, [{ val: value }, null]) + }); + return + } } catch (e) { - this.error(e); - return [null, null]; + if (!callback) { + this.error(e); + } + return returnOrCallback(e, null); } } } @@ -494,27 +520,47 @@ class Flow { } if (group.g) { const parent = this.getGroupNode(group.g); - return this.getGroupEnvSetting(node, parent, name); + const gVal = this.getGroupEnvSetting(node, parent, name, callback); + if (callback) { + return; + } + return gVal; } } - return [null, name]; + return returnOrCallback(null, [null, name]); } - + /** + * Settings callback signature + * + * @callback SettingsCallback + * @param {Error} err The error object (or null) + * @param {Any} result The result of the callback + * @returns {void} + */ /** * Get a flow setting value. This currently automatically defers to the parent * flow which, as defined in ./index.js returns `process.env[key]`. * This lays the groundwork for Subflow to have instance-specific settings - * @param {[type]} key [description] - * @return {[type]} [description] + * @param {String} key The settings key + * @param {SettingsCallback} callback Optional callback function + * @return {Any} */ - getSetting(key) { + getSetting(key, callback) { + /** @type {SettingsCallback} */ + const returnOrCallback = (err, result) => { + if (callback) { + callback(err, result); + return + } + return result; + } const flow = this.flow; if (key === "NR_FLOW_NAME") { - return flow.label; + return returnOrCallback(null, flow.label); } if (key === "NR_FLOW_ID") { - return flow.id; + return returnOrCallback(null, flow.id); } if (flow.credentials === undefined) { flow.credentials = credentials.get(flow.id) || {}; @@ -544,15 +590,14 @@ class Flow { } try { if (type === "bool") { - const val = ((value === "true") || - (value === true)); - return val; + const val = ((value === "true") || (value === true)); + return returnOrCallback(null, val); } if (type === "cred") { - return value; + return returnOrCallback(null, value); } var val = redUtil.evaluateNodeProperty(value, type, null, null, null); - return val; + return returnOrCallback(null, val); } catch (e) { this.error(e); @@ -564,7 +609,11 @@ class Flow { key = key.substring(8); } } - return this.parent.getSetting(key); + const pVal = this.parent.getSetting(key, callback); + if (callback) { + return; + } + return pVal; } /** diff --git a/packages/node_modules/@node-red/runtime/lib/flows/index.js b/packages/node_modules/@node-red/runtime/lib/flows/index.js index e18861f17..9c6133ea5 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/index.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/index.js @@ -780,7 +780,7 @@ const flowAPI = { getNode: getNode, handleError: () => false, handleStatus: () => false, - getSetting: k => flowUtil.getEnvVar(k), + getSetting: (k, callback) => flowUtil.getEnvVar(k, callback), log: m => log.log(m) } diff --git a/packages/node_modules/@node-red/runtime/lib/flows/util.js b/packages/node_modules/@node-red/runtime/lib/flows/util.js index 868657c7b..39ff820b4 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/util.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/util.js @@ -308,16 +308,34 @@ module.exports = { runtime.settings.envVarExcludes.forEach(v => envVarExcludes[v] = true); } }, - getEnvVar: function(k) { - if (!envVarExcludes[k]) { - const item = getGlobalEnv(k); + /** + * Get the value of an environment variable + * Call with a callback to get the value asynchronously + * or without to get the value synchronously + * @param {String} key The name of the environment variable + * @param {(err: Error, val: Any)} [callback] Optional callback for asynchronous call + * @returns {Any | void} The value of the environment variable or undefined if not found + */ + getEnvVar: function(key, callback) { + const returnOrCallback = function(err, val) { + if (callback) { + callback(err, val); + return; + } + return val; + } + if (!envVarExcludes[key]) { + const item = getGlobalEnv(key); if (item) { - const val = redUtil.evaluateNodeProperty(item.value, item.type, null, null, null); + const val = redUtil.evaluateNodeProperty(item.value, item.type, null, null, callback); + if (callback) { + return; + } return val; } - return process.env[k]; + return returnOrCallback(null, process.env[key]); } - return undefined; + return returnOrCallback(undefined); }, diffNodes: diffNodes, mapEnvVarProperties: mapEnvVarProperties, diff --git a/packages/node_modules/@node-red/util/lib/util.js b/packages/node_modules/@node-red/util/lib/util.js index ad05d45f1..8511376d7 100644 --- a/packages/node_modules/@node-red/util/lib/util.js +++ b/packages/node_modules/@node-red/util/lib/util.js @@ -18,6 +18,7 @@ /** * @mixin @node-red/util_util */ +/** @typedef {import('../../runtime/lib/flows/Flow.js').Flow} RuntimeLibFlowsFlow */ const clonedeep = require("lodash.clonedeep"); const jsonata = require("jsonata"); @@ -526,37 +527,67 @@ function setObjectProperty(msg,prop,value,createMissing) { return true; } -/*! +/** * Get value of environment variable. * @param {Node} node - accessing node * @param {String} name - name of variable + * @param {RuntimeLibFlowsFlow} flow_ - (optional) flow to check for setting + * @param {(err: Error, result: Any) => void} callback - (optional) called when the property is evaluated * @return {String} value of env var */ -function getSetting(node, name, flow_) { +function getSetting(node, name, flow_, callback) { + const returnOrCallback = (err, result) => { + if (callback) { + callback(err, result); + return + } + return result; + } if (node) { if (name === "NR_NODE_NAME") { - return node.name; + return returnOrCallback(null, node.name); } if (name === "NR_NODE_ID") { - return node.id; + return returnOrCallback(null, node.id); } if (name === "NR_NODE_PATH") { - return node._path; + return returnOrCallback(null, node._path); } } + + /** @type {RuntimeLibFlowsFlow} */ var flow = (flow_ ? flow_ : (node ? node._flow : null)); if (flow) { if (node && node.g) { const group = flow.getGroupNode(node.g); - const [result, newName] = flow.getGroupEnvSetting(node, group, name); - if (result) { - return result.val; + if (callback) { + flow.getGroupEnvSetting(node, group, name, (e, [result, newName]) => { + if (e) { + callback(e); + return + } + if (result) { + callback(null, result.val); + } + name = newName; + flow.getSetting(name, callback); + }); + return + } else { + const [result, newName] = flow.getGroupEnvSetting(node, group, name); + if (result) { + return result.val; + } + name = newName; } - name = newName; } - return flow.getSetting(name); + const fVal = flow.getSetting(name, callback) + if (callback) { + return + } + return fVal; } - return process.env[name]; + return returnOrCallback(null, process.env[name]); } @@ -568,19 +599,34 @@ function getSetting(node, name, flow_) { * will return `Hello Joe!`. * @param {String} value - the string to parse * @param {Node} node - the node evaluating the property + * @param {(err: Error, result: Any) => void} callback - (optional) called when the property is evaluated * @return {String} The parsed string * @memberof @node-red/util_util */ -function evaluateEnvProperty(value, node) { +function evaluateEnvProperty(value, node, callback) { + const returnOrCallback = (err, result) => { + if (callback) { + callback(err, result); + return + } + return result; + } + /** @type {RuntimeLibFlowsFlow} */ var flow = (node && hasOwnProperty.call(node, "_flow")) ? node._flow : null; var result; if (/^\${[^}]+}$/.test(value)) { // ${ENV_VAR} var name = value.substring(2,value.length-1); - result = getSetting(node, name, flow); + result = getSetting(node, name, flow, callback); + if (callback) { + return + } } else if (!/\${\S+}/.test(value)) { // ENV_VAR - result = getSetting(node, value, flow); + result = getSetting(node, value, flow, callback); + if (callback) { + return + } } else { // FOO${ENV_VAR}BAR return value.replace(/\${([^}]+)}/g, function(match, name) { @@ -588,8 +634,7 @@ function evaluateEnvProperty(value, node) { return (val === undefined)?"":val; }); } - return (result === undefined)?"":result; - + return returnOrCallback(null, (result === undefined)?"":result); } @@ -677,7 +722,10 @@ function evaluateNodeProperty(value, type, node, msg, callback) { return } } else if (type === 'env') { - result = evaluateEnvProperty(value, node); + result = evaluateEnvProperty(value, node, callback); + if (callback) { + return + } } if (callback) { callback(null,result);