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

Add support for credential-stored env var in subflow

This commit is contained in:
Nick O'Leary 2019-11-01 14:14:48 +00:00
parent 87b9b56b65
commit cd210d9fbf
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
6 changed files with 606 additions and 396 deletions

View File

@ -398,6 +398,10 @@ RED.nodes = (function() {
paletteLabel: function() { return RED.nodes.subflow(sf.id).name }, paletteLabel: function() { return RED.nodes.subflow(sf.id).name },
inputLabels: function(i) { return sf.inputLabels?sf.inputLabels[i]:null }, inputLabels: function(i) { return sf.inputLabels?sf.inputLabels[i]:null },
outputLabels: function(i) { return sf.outputLabels?sf.outputLabels[i]:null }, outputLabels: function(i) { return sf.outputLabels?sf.outputLabels[i]:null },
oneditprepare: function() {
RED.subflow.buildEditForm("subflow",this);
RED.subflow.buildPropertiesForm(this);
},
oneditresize: function(size) { oneditresize: function(size) {
// var rows = $(".dialog-form>div:not(.node-input-env-container-row)"); // var rows = $(".dialog-form>div:not(.node-input-env-container-row)");
var height = size.height; var height = size.height;
@ -505,19 +509,33 @@ RED.nodes = (function() {
node[d] = n[d]; node[d] = n[d];
} }
} }
if(exportCreds && n.credentials) { if (exportCreds) {
var credentialSet = {}; var credentialSet = {};
node.credentials = {}; if (/^subflow:/.test(node.type) && n.credentials) {
for (var cred in n._def.credentials) { // A subflow instance node can have arbitrary creds
if (n._def.credentials.hasOwnProperty(cred)) { for (var sfCred in n.credentials) {
if (n._def.credentials[cred].type == 'password') { if (n.credentials.hasOwnProperty(sfCred)) {
if (!n.credentials._ || if (!n.credentials._ ||
n.credentials["has_"+cred] != n.credentials._["has_"+cred] || n.credentials["has_"+sfCred] != n.credentials._["has_"+sfCred] ||
(n.credentials["has_"+cred] && n.credentials[cred])) { (n.credentials["has_"+sfCred] && n.credentials[sfCred])) {
credentialSet[sfCred] = n.credentials[sfCred];
}
}
}
} else if (n.credentials) {
node.credentials = {};
// All other nodes have a well-defined list of possible credentials
for (var cred in n._def.credentials) {
if (n._def.credentials.hasOwnProperty(cred)) {
if (n._def.credentials[cred].type == 'password') {
if (!n.credentials._ ||
n.credentials["has_"+cred] != n.credentials._["has_"+cred] ||
(n.credentials["has_"+cred] && n.credentials[cred])) {
credentialSet[cred] = n.credentials[cred];
}
} else if (n.credentials[cred] != null && (!n.credentials._ || n.credentials[cred] != n.credentials._[cred])) {
credentialSet[cred] = n.credentials[cred]; credentialSet[cred] = n.credentials[cred];
} }
} else if (n.credentials[cred] != null && (!n.credentials._ || n.credentials[cred] != n.credentials._[cred])) {
credentialSet[cred] = n.credentials[cred];
} }
} }
} }
@ -568,7 +586,8 @@ RED.nodes = (function() {
return node; return node;
} }
function convertSubflow(n) { function convertSubflow(n, exportCreds) {
exportCreds = true;
var node = {}; var node = {};
node.id = n.id; node.id = n.id;
node.type = n.type; node.type = n.type;
@ -578,6 +597,24 @@ RED.nodes = (function() {
node.in = []; node.in = [];
node.out = []; node.out = [];
node.env = n.env; node.env = n.env;
if (exportCreds) {
var credentialSet = {};
// A subflow node can have arbitrary creds
for (var sfCred in n.credentials) {
if (n.credentials.hasOwnProperty(sfCred)) {
if (!n.credentials._ ||
n.credentials["has_"+sfCred] != n.credentials._["has_"+sfCred] ||
(n.credentials["has_"+sfCred] && n.credentials[sfCred])) {
credentialSet[sfCred] = n.credentials[sfCred];
}
}
}
if (Object.keys(credentialSet).length > 0) {
node.credentials = credentialSet;
}
}
node.color = n.color; node.color = n.color;
n.in.forEach(function(p) { n.in.forEach(function(p) {
@ -693,7 +730,7 @@ RED.nodes = (function() {
} }
for (i in subflows) { for (i in subflows) {
if (subflows.hasOwnProperty(i)) { if (subflows.hasOwnProperty(i)) {
nns.push(convertSubflow(subflows[i])); nns.push(convertSubflow(subflows[i], exportCredentials));
} }
} }
for (i in configNodes) { for (i in configNodes) {

View File

@ -490,8 +490,7 @@ RED.editor = (function() {
done(); done();
} }
} }
if (definition.credentials || /^subflow:/.test(definition.type)) {
if (definition.credentials) {
if (node.credentials) { if (node.credentials) {
populateCredentialsInputs(node, definition.credentials, node.credentials, prefix); populateCredentialsInputs(node, definition.credentials, node.credentials, prefix);
completePrepare(); completePrepare();
@ -499,7 +498,9 @@ RED.editor = (function() {
$.getJSON(getCredentialsURL(node.type, node.id), function (data) { $.getJSON(getCredentialsURL(node.type, node.id), function (data) {
node.credentials = data; node.credentials = data;
node.credentials._ = $.extend(true,{},data); node.credentials._ = $.extend(true,{},data);
populateCredentialsInputs(node, definition.credentials, node.credentials, prefix); if (!/^subflow:/.test(definition.type)) {
populateCredentialsInputs(node, definition.credentials, node.credentials, prefix);
}
completePrepare(); completePrepare();
}); });
} }
@ -576,8 +577,11 @@ RED.editor = (function() {
$(this).attr("data-i18n",keys.join(";")); $(this).attr("data-i18n",keys.join(";"));
}); });
if (type === "subflow-template" || type === "subflow") { if (type === "subflow-template") {
RED.subflow.buildEditForm(dialogForm,type,node); // This is the 'edit properties' dialog for a subflow template
// TODO: this needs to happen later in the dialog open sequence
// so that credentials can be loaded prior to building the form
RED.subflow.buildEditForm(type,node);
} }
// Add dummy fields to prevent 'Enter' submitting the form in some // Add dummy fields to prevent 'Enter' submitting the form in some
@ -1471,6 +1475,19 @@ RED.editor = (function() {
if (type === "subflow") { if (type === "subflow") {
var old_env = editing_node.env; var old_env = editing_node.env;
var new_env = RED.subflow.exportSubflowInstanceEnv(editing_node); var new_env = RED.subflow.exportSubflowInstanceEnv(editing_node);
if (new_env && new_env.length > 0) {
new_env.forEach(function(prop) {
if (prop.type === "cred") {
editing_node.credentials = editing_node.credentials || {_:{}};
editing_node.credentials[prop.name] = prop.value;
editing_node.credentials['has_'+prop.name] = (prop.value !== "");
if (prop.value !== '__PWRD__') {
changed = true;
}
delete prop.value;
}
});
}
if (!isSameObj(old_env, new_env)) { if (!isSameObj(old_env, new_env)) {
editing_node.env = new_env; editing_node.env = new_env;
changes.env = editing_node.env; changes.env = editing_node.env;
@ -1599,12 +1616,13 @@ RED.editor = (function() {
id: "editor-subflow-envProperties", id: "editor-subflow-envProperties",
label: RED._("editor-tab.envProperties"), label: RED._("editor-tab.envProperties"),
name: RED._("editor-tab.envProperties"), name: RED._("editor-tab.envProperties"),
content: $('<div>', {class:"red-ui-tray-content"}).appendTo(editorContent).hide(), content: $('<div>', {id:"editor-subflow-envProperties-content",class:"red-ui-tray-content"}).appendTo(editorContent).hide(),
iconClass: "fa fa-list" iconClass: "fa fa-list"
}; };
RED.subflow.buildPropertiesForm(subflowPropertiesTab.content,node);
editorTabs.addTab(subflowPropertiesTab); editorTabs.addTab(subflowPropertiesTab);
// This tab is populated by the oneditprepare function of this
// subflow. That ensures it is done *after* any credentials
// have been loaded for the instance.
} }
if (!node._def.defaults || !node._def.defaults.hasOwnProperty('info')) { if (!node._def.defaults || !node._def.defaults.hasOwnProperty('info')) {
@ -2252,6 +2270,21 @@ RED.editor = (function() {
var old_env = editing_node.env; var old_env = editing_node.env;
var new_env = RED.subflow.exportSubflowTemplateEnv($("#node-input-env-container").editableList("items")); var new_env = RED.subflow.exportSubflowTemplateEnv($("#node-input-env-container").editableList("items"));
if (new_env && new_env.length > 0) {
new_env.forEach(function(prop) {
if (prop.type === "cred") {
editing_node.credentials = editing_node.credentials || {_:{}};
editing_node.credentials[prop.name] = prop.value;
editing_node.credentials['has_'+prop.name] = (prop.value !== "");
if (prop.value !== '__PWRD__') {
changed = true;
}
delete prop.value;
}
});
}
if (!isSameObj(old_env, new_env)) { if (!isSameObj(old_env, new_env)) {
editing_node.env = new_env; editing_node.env = new_env;
changes.env = editing_node.env; changes.env = editing_node.env;
@ -2311,7 +2344,7 @@ RED.editor = (function() {
$("#node-input-env-container").editableList('height',height-95); $("#node-input-env-container").editableList('height',height-95);
} }
}, },
open: function(tray) { open: function(tray, done) {
var trayFooter = tray.find(".red-ui-tray-footer"); var trayFooter = tray.find(".red-ui-tray-footer");
var trayFooterLeft = $("<div/>", { var trayFooterLeft = $("<div/>", {
class: "red-ui-tray-footer-left" class: "red-ui-tray-footer-left"
@ -2362,7 +2395,6 @@ RED.editor = (function() {
content: $('<div>', {class:"red-ui-tray-content"}).appendTo(editorContent).hide(), content: $('<div>', {class:"red-ui-tray-content"}).appendTo(editorContent).hide(),
iconClass: "fa fa-cog" iconClass: "fa fa-cog"
}; };
buildEditForm(nodePropertiesTab.content,"dialog-form","subflow-template", undefined, editing_node);
editorTabs.addTab(nodePropertiesTab); editorTabs.addTab(nodePropertiesTab);
var descriptionTab = { var descriptionTab = {
@ -2391,11 +2423,18 @@ RED.editor = (function() {
buildAppearanceForm(appearanceTab.content,editing_node); buildAppearanceForm(appearanceTab.content,editing_node);
editorTabs.addTab(appearanceTab); editorTabs.addTab(appearanceTab);
$("#subflow-input-name").val(subflow.name); $.getJSON(getCredentialsURL("subflow", subflow.id), function (data) {
RED.text.bidi.prepareInput($("#subflow-input-name")); subflow.credentials = data;
subflow.credentials._ = $.extend(true,{},data);
trayBody.i18n(); buildEditForm(nodePropertiesTab.content,"dialog-form","subflow-template", undefined, editing_node);
finishedBuilding = true; $("#subflow-input-name").val(subflow.name);
RED.text.bidi.prepareInput($("#subflow-input-name"));
trayBody.i18n();
finishedBuilding = true;
done();
});
}, },
close: function() { close: function() {
if (RED.view.state() != RED.state.IMPORT_DRAGGING) { if (RED.view.state() != RED.state.IMPORT_DRAGGING) {

View File

@ -770,7 +770,7 @@ RED.subflow = (function() {
/** /**
* Create interface for controlling env var UI definition * Create interface for controlling env var UI definition
*/ */
function buildEnvControl(envList) { function buildEnvControl(envList,node) {
var tabs = RED.tabs.create({ var tabs = RED.tabs.create({
id: "subflow-env-tabs", id: "subflow-env-tabs",
@ -779,7 +779,7 @@ RED.subflow = (function() {
var inputContainer = $("#subflow-input-ui"); var inputContainer = $("#subflow-input-ui");
var list = envList.editableList("items"); var list = envList.editableList("items");
var exportedEnv = exportEnvList(list, true); var exportedEnv = exportEnvList(list, true);
buildEnvUI(inputContainer, exportedEnv); buildEnvUI(inputContainer, exportedEnv,node);
} }
$("#subflow-env-tabs-content").children().hide(); $("#subflow-env-tabs-content").children().hide();
$("#" + tab.id).show(); $("#" + tab.id).show();
@ -831,6 +831,9 @@ RED.subflow = (function() {
}); });
} }
var DEFAULT_ENV_TYPE_LIST = ['str','num','bool','json','bin','env'];
var DEFAULT_ENV_TYPE_LIST_INC_CRED = ['str','num','bool','json','bin','env','cred'];
/** /**
* Create env var edit interface * Create env var edit interface
* @param container - container * @param container - container
@ -841,7 +844,7 @@ RED.subflow = (function() {
var isTemplateNode = (node.type === "subflow"); var isTemplateNode = (node.type === "subflow");
if (isTemplateNode) { if (isTemplateNode) {
buildEnvControl(envContainer); buildEnvControl(envContainer, node);
} }
envContainer envContainer
.css({ .css({
@ -851,6 +854,9 @@ RED.subflow = (function() {
.editableList({ .editableList({
header: isTemplateNode?$('<div><div><div></div><div data-i18n="common.label.name"></div><div data-i18n="editor-tab.defaultValue"></div><div></div></div></div>'):undefined, header: isTemplateNode?$('<div><div><div></div><div data-i18n="common.label.name"></div><div data-i18n="editor-tab.defaultValue"></div><div></div></div></div>'):undefined,
addItem: function(container, i, opt) { addItem: function(container, i, opt) {
// If this is an instance node, these are properties unique to
// this instance - ie opt.parent will not be defined.
if (isTemplateNode) { if (isTemplateNode) {
container.addClass("red-ui-editor-subflow-env-editable") container.addClass("red-ui-editor-subflow-env-editable")
} }
@ -859,52 +865,64 @@ RED.subflow = (function() {
var nameField = null; var nameField = null;
var valueField = null; var valueField = null;
// if (opt.parent) { nameField = $('<input/>', {
// buildEnvUIRow(envRow,opt,opt.parent.ui||{}) class: "node-input-env-name",
// } else { type: "text",
nameField = $('<input/>', { placeholder: RED._("common.label.name")
class: "node-input-env-name", }).attr("autocomplete","disable").appendTo(envRow).val(opt.name);
type: "text", valueField = $('<input/>',{
placeholder: RED._("common.label.name") style: "width:100%",
}).attr("autocomplete","disable").appendTo(envRow).val(opt.name); class: "node-input-env-value",
valueField = $('<input/>',{ type: "text",
style: "width:100%", }).attr("autocomplete","disable").appendTo(envRow)
class: "node-input-env-value", valueField.typedInput({default:'str',types:isTemplateNode?DEFAULT_ENV_TYPE_LIST:DEFAULT_ENV_TYPE_LIST_INC_CRED});
type: "text", valueField.typedInput('type', opt.type);
}).attr("autocomplete","disable").appendTo(envRow) if (opt.type === "cred") {
valueField.typedInput({default:'str',types:['str','num','bool','json','bin','env']}); if (opt.value) {
valueField.typedInput('type', opt.parent?(opt.type||opt.parent.type):opt.type); valueField.typedInput('value', opt.value);
valueField.typedInput('value', opt.parent?((opt.value !== undefined)?opt.value:opt.parent.value):opt.value); } else if (node.credentials && node.credentials[opt.name]) {
// } valueField.typedInput('value', node.credentials[opt.name]);
} else if (node.credentials && node.credentials['has_'+opt.name]) {
valueField.typedInput('value', "__PWRD__");
} else {
valueField.typedInput('value', "");
}
} else {
valueField.typedInput('value', opt.value);
}
opt.nameField = nameField; opt.nameField = nameField;
opt.valueField = valueField; opt.valueField = valueField;
if (!opt.parent) { var actionButton = $('<a/>',{href:"#",class:"red-ui-editableList-item-remove red-ui-button red-ui-button-small"}).appendTo(envRow);
var actionButton = $('<a/>',{href:"#",class:"red-ui-editableList-item-remove red-ui-button red-ui-button-small"}).appendTo(envRow); $('<i/>',{class:"fa "+(opt.parent?"fa-reply":"fa-remove")}).appendTo(actionButton);
$('<i/>',{class:"fa "+(opt.parent?"fa-reply":"fa-remove")}).appendTo(actionButton); var removeTip = RED.popover.tooltip(actionButton,RED._("subflow.env.remove"));
var removeTip = RED.popover.tooltip(actionButton,RED._("subflow.env.remove")); actionButton.on("click", function(evt) {
actionButton.on("click", function(evt) { evt.preventDefault();
evt.preventDefault(); removeTip.close();
removeTip.close(); container.parent().addClass("red-ui-editableList-item-deleting")
container.parent().addClass("red-ui-editableList-item-deleting") container.fadeOut(300, function() {
container.fadeOut(300, function() { envContainer.editableList('removeItem',opt);
envContainer.editableList('removeItem',opt);
});
}); });
} });
if (isTemplateNode) { if (isTemplateNode) {
// Add the UI customisation row // Add the UI customisation row
// if `opt.ui` does not exist, then apply defaults. If these // if `opt.ui` does not exist, then apply defaults. If these
// defaults do not change then they will get stripped off // defaults do not change then they will get stripped off
// before saving. // before saving.
opt.ui = opt.ui || { if (opt.type === 'cred') {
icon: "", opt.ui = opt.ui || {
label: {}, icon: "",
type: "input", type: "cred"
opts: {types:['str','num','bool','json','bin','env']} }
} else {
opt.ui = opt.ui || {
icon: "",
type: "input",
opts: {types:DEFAULT_ENV_TYPE_LIST}
}
} }
opt.ui.label = opt.ui.label || {}; opt.ui.label = opt.ui.label || {};
opt.ui.type = opt.ui.type || "input"; opt.ui.type = opt.ui.type || "input";
@ -995,11 +1013,11 @@ RED.subflow = (function() {
var row = $('<div></div>').appendTo(container); var row = $('<div></div>').appendTo(container);
$('<div><i class="red-ui-editableList-item-handle fa fa-bars"></i></div>').appendTo(row); $('<div><i class="red-ui-editableList-item-handle fa fa-bars"></i></div>').appendTo(row);
var typeOptions = { var typeOptions = {
'input': {types:['str','num','bool','json','bin','env']}, 'input': {types:DEFAULT_ENV_TYPE_LIST},
'select': {opts:[]}, 'select': {opts:[]},
'spinner': {} 'spinner': {},
'cred': {}
}; };
if (ui.opts) { if (ui.opts) {
typeOptions[ui.type] = ui.opts; typeOptions[ui.type] = ui.opts;
@ -1062,314 +1080,341 @@ RED.subflow = (function() {
nameField.on('change',function(evt) { nameField.on('change',function(evt) {
labelInput.attr("placeholder",$(this).val()) labelInput.attr("placeholder",$(this).val())
}); });
var inputCell = $('<div></div>').appendTo(row); var inputCell = $('<div></div>').appendTo(row);
var inputCellInput = $('<input type="text">').css("width","100%").appendTo(inputCell); var inputCellInput = $('<input type="text">').css("width","100%").appendTo(inputCell);
if (ui.type === "input") { if (ui.type === "input") {
inputCellInput.val(ui.opts.types.join(",")); inputCellInput.val(ui.opts.types.join(","));
} }
var checkbox; var checkbox;
var selectBox; var selectBox;
inputCellInput.typedInput({ inputCellInput.typedInput({
types: [ types: [
{ {
value:"input", value:"input",
label:RED._("editor.inputs.input"), icon:"fa fa-i-cursor",showLabel:false,multiple:true,options:[ label:RED._("editor.inputs.input"), icon:"fa fa-i-cursor",showLabel:false,multiple:true,options:[
{value:"str",label:RED._("editor.types.str"),icon:"red/images/typedInput/az.svg"}, {value:"str",label:RED._("editor.types.str"),icon:"red/images/typedInput/az.svg"},
{value:"num",label:RED._("editor.types.num"),icon:"red/images/typedInput/09.svg"}, {value:"num",label:RED._("editor.types.num"),icon:"red/images/typedInput/09.svg"},
{value:"bool",label:RED._("editor.types.bool"),icon:"red/images/typedInput/bool.svg"}, {value:"bool",label:RED._("editor.types.bool"),icon:"red/images/typedInput/bool.svg"},
{value:"json",label:RED._("editor.types.json"),icon:"red/images/typedInput/json.svg"}, {value:"json",label:RED._("editor.types.json"),icon:"red/images/typedInput/json.svg"},
{value: "bin",label: RED._("editor.types.bin"),icon: "red/images/typedInput/bin.svg"}, {value: "bin",label: RED._("editor.types.bin"),icon: "red/images/typedInput/bin.svg"},
{value: "env",label: RED._("editor.types.env"),icon: "red/images/typedInput/env.svg"} {value: "env",label: RED._("editor.types.env"),icon: "red/images/typedInput/env.svg"},
], {value: "cred",label: RED._("editor.types.cred"),icon: "fa fa-lock"}
default: ['str','num','bool','json','bin','env'], ],
valueLabel: function(container,value) { default: DEFAULT_ENV_TYPE_LIST,
container.css("padding",0); valueLabel: function(container,value) {
var innerContainer = $('<div>').css({ container.css("padding",0);
"background":"white", var innerContainer = $('<div>').css({
"height":"100%", "background":"white",
"box-sizing": "border-box" "height":"100%",
}).appendTo(container); "box-sizing": "border-box"
}).appendTo(container);
var input = $('<div class="placeholder-input">').appendTo(innerContainer); var input = $('<div class="placeholder-input">').appendTo(innerContainer);
$('<span><i class="fa fa-i-cursor"></i></span>').appendTo(input); $('<span><i class="fa fa-i-cursor"></i></span>').appendTo(input);
if (value.length) { if (value.length) {
value.forEach(function(v) { value.forEach(function(v) {
$('<img>',{src:v.icon,style:"max-width:14px; padding: 0 3px; margin-top:-4px; margin-left: 3px"}).appendTo(input); if (!/^fa /.test(v.icon)) {
}) $('<img>',{src:v.icon,style:"max-width:14px; padding: 0 3px; margin-top:-4px; margin-left: 1px"}).appendTo(input);
} else { } else {
$("<span>").css({ var s = $('<span>',{style:"max-width:14px; padding: 0 3px; margin-top:-4px; margin-left: 1px"}).appendTo(input);
"color":"#aaa", $("<i>",{class: v.icon}).appendTo(s);
"padding-left": "4px" }
}).text("select types...").appendTo(input); })
} } else {
} $("<span>").css({
}, "color":"#aaa",
{ "padding-left": "4px"
value:"select", }).text("select types...").appendTo(input);
label:RED._("editor.inputs.select"), icon:"fa fa-tasks",showLabel:false, }
valueLabel: function(container,value) { }
container.css("padding","0"); },
{
value: "cred",
label: RED._("typedInput.type.cred"), icon:"fa fa-lock", showLabel: false,
valueLabel: function(container,value) {
container.css("padding",0);
var innerContainer = $('<div>').css({
"background":"white",
"height":"100%",
"box-sizing": "border-box",
"border-top-right-radius": "4px",
"border-bottom-right-radius": "4px"
}).appendTo(container);
$('<div class="placeholder-input">').html("&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;").appendTo(innerContainer);
}
},
{
value:"select",
label:RED._("editor.inputs.select"), icon:"fa fa-tasks",showLabel:false,
valueLabel: function(container,value) {
container.css("padding","0");
selectBox = $('<select></select>').appendTo(container); selectBox = $('<select></select>').appendTo(container);
if (ui.opts && Array.isArray(ui.opts.opts)) { if (ui.opts && Array.isArray(ui.opts.opts)) {
ui.opts.opts.forEach(function(o) { ui.opts.opts.forEach(function(o) {
var label = lookupLabel(o.l, o.l["en-US"]||o.v, currentLocale); var label = lookupLabel(o.l, o.l["en-US"]||o.v, currentLocale);
// $('<option>').val((o.t||'str')+":"+o.v).text(label).appendTo(selectBox); // $('<option>').val((o.t||'str')+":"+o.v).text(label).appendTo(selectBox);
$('<option>').val(o.v).text(label).appendTo(selectBox); $('<option>').val(o.v).text(label).appendTo(selectBox);
}) })
} }
selectBox.on('change', function(evt) { selectBox.on('change', function(evt) {
var v = selectBox.val(); var v = selectBox.val();
// var parts = v.split(":"); // var parts = v.split(":");
// var t = parts.shift(); // var t = parts.shift();
// v = parts.join(":"); // v = parts.join(":");
// //
// valueField.typedInput("type",'str') // valueField.typedInput("type",'str')
valueField.typedInput("value",v) valueField.typedInput("value",v)
}); });
selectBox.val(valueField.typedInput("value")); selectBox.val(valueField.typedInput("value"));
// selectBox.val(valueField.typedInput('type')+":"+valueField.typedInput("value")); // selectBox.val(valueField.typedInput('type')+":"+valueField.typedInput("value"));
}, },
expand: { expand: {
icon: "fa-caret-down", icon: "fa-caret-down",
minWidth: 400, minWidth: 400,
content: function(container) { content: function(container) {
var content = $('<div class="red-ui-editor-subflow-ui-edit-panel">').appendTo(container); var content = $('<div class="red-ui-editor-subflow-ui-edit-panel">').appendTo(container);
var optList = $('<ol>').appendTo(content).editableList({ var optList = $('<ol>').appendTo(content).editableList({
header:$("<div><div>"+RED._("editor.select.label")+"</div><div>"+RED._("editor.select.value")+"</div></div>"), header:$("<div><div>"+RED._("editor.select.label")+"</div><div>"+RED._("editor.select.value")+"</div></div>"),
addItem: function(row,index,itemData) { addItem: function(row,index,itemData) {
var labelDiv = $('<div>').appendTo(row); var labelDiv = $('<div>').appendTo(row);
var label = lookupLabel(itemData.l, "", currentLocale); var label = lookupLabel(itemData.l, "", currentLocale);
itemData.label = $('<input type="text">').val(label).appendTo(labelDiv); itemData.label = $('<input type="text">').val(label).appendTo(labelDiv);
itemData.label.on('keydown', function(evt) { itemData.label.on('keydown', function(evt) {
if (evt.keyCode === 13) { if (evt.keyCode === 13) {
itemData.input.focus(); itemData.input.focus();
evt.preventDefault(); evt.preventDefault();
} }
}); });
var labelIcon = $('<span class="red-ui-editor-subflow-env-lang-icon"><i class="fa fa-language"></i></span>').appendTo(labelDiv); var labelIcon = $('<span class="red-ui-editor-subflow-env-lang-icon"><i class="fa fa-language"></i></span>').appendTo(labelDiv);
RED.popover.tooltip(labelIcon,function() { RED.popover.tooltip(labelIcon,function() {
return currentLocale; return currentLocale;
}) })
itemData.input = $('<input type="text">').val(itemData.v).appendTo(row); itemData.input = $('<input type="text">').val(itemData.v).appendTo(row);
// Problem using a TI here: // Problem using a TI here:
// - this is in a popout panel // - this is in a popout panel
// - clicking the expand button in the TI will close the parent edit tray // - clicking the expand button in the TI will close the parent edit tray
// and open the type editor. // and open the type editor.
// - but it leaves the popout panel over the top. // - but it leaves the popout panel over the top.
// - there is no way to get back to the popout panel after closing the type editor // - there is no way to get back to the popout panel after closing the type editor
//.typedInput({default:itemData.t||'str', types:['str','num','bool','json','bin','env']}); //.typedInput({default:itemData.t||'str', types:DEFAULT_ENV_TYPE_LIST});
itemData.input.on('keydown', function(evt) { itemData.input.on('keydown', function(evt) {
if (evt.keyCode === 13) { if (evt.keyCode === 13) {
// Enter or Tab // Enter or Tab
var index = optList.editableList('indexOf',itemData); var index = optList.editableList('indexOf',itemData);
var length = optList.editableList('length'); var length = optList.editableList('length');
if (index + 1 === length) { if (index + 1 === length) {
var newItem = {}; var newItem = {};
optList.editableList('addItem',newItem); optList.editableList('addItem',newItem);
setTimeout(function() { setTimeout(function() {
if (newItem.label) { if (newItem.label) {
newItem.label.focus(); newItem.label.focus();
} }
},100) },100)
} else { } else {
var nextItem = optList.editableList('getItemAt',index+1); var nextItem = optList.editableList('getItemAt',index+1);
if (nextItem.label) { if (nextItem.label) {
nextItem.label.focus() nextItem.label.focus()
} }
} }
evt.preventDefault(); evt.preventDefault();
} }
}); });
}, },
sortable: true, sortable: true,
removable: true, removable: true,
height: 160 height: 160
}) })
if (ui.opts.opts.length > 0) { if (ui.opts.opts.length > 0) {
ui.opts.opts.forEach(function(o) { ui.opts.opts.forEach(function(o) {
optList.editableList('addItem',$.extend(true,{},o)) optList.editableList('addItem',$.extend(true,{},o))
}) })
} else { } else {
optList.editableList('addItem',{}) optList.editableList('addItem',{})
} }
return { return {
onclose: function() { onclose: function() {
var items = optList.editableList('items'); var items = optList.editableList('items');
var vals = []; var vals = [];
items.each(function (i,el) { items.each(function (i,el) {
var data = el.data('data'); var data = el.data('data');
var l = data.label.val().trim(); var l = data.label.val().trim();
var v = data.input.val(); var v = data.input.val();
// var t = data.input.typedInput('type'); // var t = data.input.typedInput('type');
// var v = data.input.typedInput('value'); // var v = data.input.typedInput('value');
if (l.length > 0) { if (l.length > 0) {
data.l = data.l || {}; data.l = data.l || {};
data.l[currentLocale] = l; data.l[currentLocale] = l;
} }
data.v = v; data.v = v;
if (l.length > 0 || v.length > 0) { if (l.length > 0 || v.length > 0) {
var val = {l:data.l,v:data.v}; var val = {l:data.l,v:data.v};
// if (t !== 'str') { // if (t !== 'str') {
// val.t = t; // val.t = t;
// } // }
vals.push(val); vals.push(val);
} }
}); });
ui.opts.opts = vals; ui.opts.opts = vals;
inputCellInput.typedInput('value',Date.now()) inputCellInput.typedInput('value',Date.now())
} }
} }
} }
} }
}, },
{ {
value:"checkbox", value:"checkbox",
label:RED._("editor.inputs.checkbox"), icon:"fa fa-check-square-o",showLabel:false, label:RED._("editor.inputs.checkbox"), icon:"fa fa-check-square-o",showLabel:false,
valueLabel: function(container,value) { valueLabel: function(container,value) {
container.css("padding",0); container.css("padding",0);
checkbox = $('<input type="checkbox">').appendTo(container); checkbox = $('<input type="checkbox">').appendTo(container);
checkbox.on('change', function(evt) { checkbox.on('change', function(evt) {
valueField.typedInput('value',$(this).prop('checked')?"true":"false"); valueField.typedInput('value',$(this).prop('checked')?"true":"false");
}) })
checkbox.prop('checked',valueField.typedInput('value')==="true"); checkbox.prop('checked',valueField.typedInput('value')==="true");
} }
}, },
{ {
value:"spinner", value:"spinner",
label:RED._("editor.inputs.spinner"), icon:"fa fa-sort-numeric-asc", showLabel:false, label:RED._("editor.inputs.spinner"), icon:"fa fa-sort-numeric-asc", showLabel:false,
valueLabel: function(container,value) { valueLabel: function(container,value) {
container.css("padding",0); container.css("padding",0);
var innerContainer = $('<div>').css({ var innerContainer = $('<div>').css({
"background":"white", "background":"white",
"height":"100%", "height":"100%",
"box-sizing": "border-box" "box-sizing": "border-box"
}).appendTo(container); }).appendTo(container);
var input = $('<div class="placeholder-input">').appendTo(innerContainer); var input = $('<div class="placeholder-input">').appendTo(innerContainer);
$('<span><i class="fa fa-sort-numeric-asc"></i></span>').appendTo(input); $('<span><i class="fa fa-sort-numeric-asc"></i></span>').appendTo(input);
var min = ui.opts && ui.opts.min; var min = ui.opts && ui.opts.min;
var max = ui.opts && ui.opts.max; var max = ui.opts && ui.opts.max;
var label = ""; var label = "";
if (min !== undefined && max !== undefined) { if (min !== undefined && max !== undefined) {
label = Math.min(min,max)+" - "+Math.max(min,max); label = Math.min(min,max)+" - "+Math.max(min,max);
} else if (min !== undefined) { } else if (min !== undefined) {
label = "> "+min; label = "> "+min;
} else if (max !== undefined) { } else if (max !== undefined) {
label = "< "+max; label = "< "+max;
} }
$('<span>').css("margin-left","15px").text(label).appendTo(input); $('<span>').css("margin-left","15px").text(label).appendTo(input);
}, },
expand: { expand: {
icon: "fa-caret-down", icon: "fa-caret-down",
content: function(container) { content: function(container) {
var content = $('<div class="red-ui-editor-subflow-ui-edit-panel">').appendTo(container); var content = $('<div class="red-ui-editor-subflow-ui-edit-panel">').appendTo(container);
content.css("padding","8px 5px") content.css("padding","8px 5px")
var min = ui.opts.min; var min = ui.opts.min;
var max = ui.opts.max; var max = ui.opts.max;
var minInput = $('<input type="number" style="margin-bottom:0; width:60px">'); var minInput = $('<input type="number" style="margin-bottom:0; width:60px">');
minInput.val(min); minInput.val(min);
var maxInput = $('<input type="number" style="margin-bottom:0; width:60px">'); var maxInput = $('<input type="number" style="margin-bottom:0; width:60px">');
maxInput.val(max); maxInput.val(max);
$('<div class="form-row" style="margin-bottom:3px"><label>'+RED._("editor.spinner.min")+'</label></div>').append(minInput).appendTo(content); $('<div class="form-row" style="margin-bottom:3px"><label>'+RED._("editor.spinner.min")+'</label></div>').append(minInput).appendTo(content);
$('<div class="form-row" style="margin-bottom:0"><label>'+RED._("editor.spinner.max")+'</label></div>').append(maxInput).appendTo(content); $('<div class="form-row" style="margin-bottom:0"><label>'+RED._("editor.spinner.max")+'</label></div>').append(maxInput).appendTo(content);
return { return {
onclose: function() { onclose: function() {
var min = minInput.val().trim(); var min = minInput.val().trim();
var max = maxInput.val().trim(); var max = maxInput.val().trim();
if (min !== "") { if (min !== "") {
ui.opts.min = parseInt(min); ui.opts.min = parseInt(min);
} else { } else {
delete ui.opts.min; delete ui.opts.min;
} }
if (max !== "") { if (max !== "") {
ui.opts.max = parseInt(max); ui.opts.max = parseInt(max);
} else { } else {
delete ui.opts.max; delete ui.opts.max;
} }
inputCellInput.typedInput('value',Date.now()) inputCellInput.typedInput('value',Date.now())
} }
} }
} }
} }
}, },
{ {
value:"none", value:"none",
label:RED._("editor.inputs.none"), icon:"fa fa-times",hasValue:false label:RED._("editor.inputs.none"), icon:"fa fa-times",hasValue:false
}, },
{ {
value:"hide", value:"hide",
label:RED._("editor.inputs.hidden"), icon:"fa fa-ban",hasValue:false label:RED._("editor.inputs.hidden"), icon:"fa fa-ban",hasValue:false
} }
], ],
default: 'none' default: 'none'
}).on("typedinputtypechange", function(evt,type) { }).on("typedinputtypechange", function(evt,type) {
ui.type = $(this).typedInput("type"); ui.type = $(this).typedInput("type");
ui.opts = typeOptions[ui.type]; ui.opts = typeOptions[ui.type];
if (ui.type === 'input') { if (ui.type === 'input') {
// In the case of 'input' type, the typedInput uses the multiple-option // In the case of 'input' type, the typedInput uses the multiple-option
// mode. Its value needs to be set to a comma-separately list of the // mode. Its value needs to be set to a comma-separately list of the
// selected options. // selected options.
inputCellInput.typedInput('value',ui.opts.types.join(",")) inputCellInput.typedInput('value',ui.opts.types.join(","))
} else { } else {
// No other type cares about `value`, but doing this will // No other type cares about `value`, but doing this will
// force a refresh of the label now that `ui.opts` has // force a refresh of the label now that `ui.opts` has
// been updated. // been updated.
inputCellInput.typedInput('value',Date.now()) inputCellInput.typedInput('value',Date.now())
} }
switch (ui.type) { switch (ui.type) {
case 'input': case 'input':
valueField.typedInput('types',ui.opts.types); valueField.typedInput('types',ui.opts.types);
break; break;
case 'select': case 'select':
valueField.typedInput('types',['str']); valueField.typedInput('types',['str']);
break; break;
case 'checkbox': case 'checkbox':
valueField.typedInput('types',['bool']); valueField.typedInput('types',['bool']);
break; break;
case 'spinner': case 'spinner':
valueField.typedInput('types',['num']) valueField.typedInput('types',['num']);
break;
case 'cred':
valueField.typedInput('types',['cred']);
break; break;
default: default:
valueField.typedInput('types',['str','num','bool','json','bin','env']) valueField.typedInput('types',DEFAULT_ENV_TYPE_LIST)
} }
if (ui.type === 'checkbox') { if (ui.type === 'checkbox') {
valueField.typedInput('type','bool'); valueField.typedInput('type','bool');
} else if (ui.type === 'spinner') { } else if (ui.type === 'spinner') {
valueField.typedInput('type','num'); valueField.typedInput('type','num');
} }
if (ui.type !== 'checkbox') { if (ui.type !== 'checkbox') {
checkbox = null; checkbox = null;
} }
}).on("change", function(evt,type) { }).on("change", function(evt,type) {
if (ui.type === 'input') { if (ui.type === 'input') {
ui.opts.types = inputCellInput.typedInput('value').split(","); ui.opts.types = inputCellInput.typedInput('value').split(",");
valueField.typedInput('types',ui.opts.types); valueField.typedInput('types',ui.opts.types);
} }
}); });
valueField.on("change", function(evt) { valueField.on("change", function(evt) {
if (checkbox) { if (checkbox) {
checkbox.prop('checked',$(this).typedInput('value')==="true") checkbox.prop('checked',$(this).typedInput('value')==="true")
} }
}) })
// Set the input to the right type. This will trigger the 'typedinputtypechange' // Set the input to the right type. This will trigger the 'typedinputtypechange'
// event handler (just above ^^) to update the value if needed // event handler (just above ^^) to update the value if needed
inputCellInput.typedInput('type',ui.type) inputCellInput.typedInput('type',ui.type)
} }
function buildEnvUIRow(row, tenv, ui) { function buildEnvUIRow(row, tenv, ui, node) {
ui.label = ui.label||{}; ui.label = ui.label||{};
if (!ui.type) { if ((tenv.type === "cred" || (tenv.parent && tenv.parent.type === "cred")) && !ui.type) {
ui.type = "cred";
ui.opts = {};
} else if (!ui.type) {
ui.type = "input"; ui.type = "input";
ui.opts = {types:['str','num','bool','json','bin','env']} ui.opts = {types:DEFAULT_ENV_TYPE_LIST}
} else { } else {
if (!ui.opts) { if (!ui.opts) {
ui.opts = (ui.type === "select") ? {opts:[]} : {}; ui.opts = (ui.type === "select") ? {opts:[]} : {};
@ -1467,6 +1512,24 @@ RED.subflow = (function() {
input.spinner(spinnerOpts).parent().width('70%'); input.spinner(spinnerOpts).parent().width('70%');
input.val(val.value); input.val(val.value);
break; break;
case "cred":
input = $('<input type="password">').css('width','70%').appendTo(row);
if (node.credentials) {
if (node.credentials[tenv.name]) {
input.val(node.credentials[tenv.name]);
} else if (node.credentials['has_'+tenv.name]) {
input.val("__PWRD__")
} else {
input.val("");
}
} else {
input.val("");
}
input.typedInput({
types: ['cred'],
default: 'cred'
})
break;
} }
if (input) { if (input) {
input.attr('id',getSubflowEnvPropertyName(tenv.name)) input.attr('id',getSubflowEnvPropertyName(tenv.name))
@ -1478,7 +1541,7 @@ RED.subflow = (function() {
* @param uiContainer - container for UI * @param uiContainer - container for UI
* @param envList - env var definitions of template * @param envList - env var definitions of template
*/ */
function buildEnvUI(uiContainer, envList) { function buildEnvUI(uiContainer, envList,node) {
uiContainer.empty(); uiContainer.empty();
var elementID = 0; var elementID = 0;
for (var i = 0; i < envList.length; i++) { for (var i = 0; i < envList.length; i++) {
@ -1487,7 +1550,7 @@ RED.subflow = (function() {
continue; continue;
} }
var row = $("<div/>", { class: "form-row" }).appendTo(uiContainer); var row = $("<div/>", { class: "form-row" }).appendTo(uiContainer);
buildEnvUIRow(row,tenv, tenv.ui || {}); buildEnvUIRow(row,tenv, tenv.ui || {}, node);
// console.log(ui); // console.log(ui);
} }
@ -1525,7 +1588,7 @@ RED.subflow = (function() {
// icon: "", // icon: "",
// label: {}, // label: {},
// type: "input", // type: "input",
// opts: {types:['str','num','bool','json','bin','env']} // opts: {types:DEFAULT_ENV_TYPE_LIST}
// } // }
if (!ui.icon) { if (!ui.icon) {
delete ui.icon; delete ui.icon;
@ -1535,13 +1598,19 @@ RED.subflow = (function() {
} }
switch (ui.type) { switch (ui.type) {
case "input": case "input":
if (JSON.stringify(ui.opts) === JSON.stringify({types:['str','num','bool','json','bin','env']})) { if (JSON.stringify(ui.opts) === JSON.stringify({types:DEFAULT_ENV_TYPE_LIST})) {
// This is the default input config. Delete it as it will // This is the default input config. Delete it as it will
// be applied automatically // be applied automatically
delete ui.type; delete ui.type;
delete ui.opts; delete ui.opts;
} }
break; break;
case "cred":
if (envItem.type === "cred") {
delete ui.type;
}
delete ui.opts;
break;
case "select": case "select":
if (ui.opts && $.isEmptyObject(ui.opts.opts)) { if (ui.opts && $.isEmptyObject(ui.opts.opts)) {
// This is the default select config. // This is the default select config.
@ -1585,7 +1654,7 @@ RED.subflow = (function() {
type: env.type, type: env.type,
value: env.value value: env.value
}, },
ui: env.ui ui: $.extend(true,{},env.ui)
} }
envList.push(item); envList.push(item);
parentEnv[env.name] = item; parentEnv[env.name] = item;
@ -1621,13 +1690,17 @@ RED.subflow = (function() {
var item; var item;
var ui = data.ui || {}; var ui = data.ui || {};
if (!ui.type) { if (!ui.type) {
ui.type = "input"; if (data.parent && data.parent.type === "cred") {
ui.opts = {types:['str','num','bool','json','bin','env']} ui.type = "cred";
} else {
ui.type = "input";
ui.opts = {types:DEFAULT_ENV_TYPE_LIST}
}
} else { } else {
ui.opts = ui.opts || {}; ui.opts = ui.opts || {};
} }
var input = $("#"+getSubflowEnvPropertyName(data.name)); var input = $("#"+getSubflowEnvPropertyName(data.name));
if (input.length) { if (input.length || ui.type === "cred") {
item = { name: data.name }; item = { name: data.name };
switch(ui.type) { switch(ui.type) {
case "input": case "input":
@ -1639,6 +1712,10 @@ RED.subflow = (function() {
item.type = 'str'; item.type = 'str';
} }
break; break;
case "cred":
item.value = input.val();
item.type = 'cred';
break;
case "spinner": case "spinner":
item.value = input.val(); item.value = input.val();
item.type = 'num'; item.type = 'num';
@ -1652,7 +1729,7 @@ RED.subflow = (function() {
item.value = ""+input.prop("checked"); item.value = ""+input.prop("checked");
break; break;
} }
if (item.type !== data.parent.type || item.value !== data.parent.value) { if (ui.type === "cred" || item.type !== data.parent.type || item.value !== data.parent.value) {
env.push(item); env.push(item);
} }
} }
@ -1702,14 +1779,17 @@ RED.subflow = (function() {
return defaultLabel; return defaultLabel;
} }
function buildEditForm(container,type,node) { function buildEditForm(type,node) {
if (type === "subflow-template") { if (type === "subflow-template") {
buildPropertiesList($('#node-input-env-container'), node); buildPropertiesList($('#node-input-env-container'), node);
} else if (type === "subflow") { } else if (type === "subflow") {
buildEnvUI($("#subflow-input-ui"), getSubflowInstanceParentEnv(node)); // This gets called by the subflow type `oneditprepare` function
// registered in nodes.js#addSubflow()
buildEnvUI($("#subflow-input-ui"), getSubflowInstanceParentEnv(node), node);
} }
} }
function buildPropertiesForm(container, node) { function buildPropertiesForm(node) {
var container = $('#editor-subflow-envProperties-content');
var form = $('<form class="dialog-form form-horizontal"></form>').appendTo(container); var form = $('<form class="dialog-form form-horizontal"></form>').appendTo(container);
var listContainer = $('<div class="form-row node-input-env-container-row"></div>').appendTo(form); var listContainer = $('<div class="form-row node-input-env-container-row"></div>').appendTo(form);
var list = $('<ol id="red-ui-editor-subflow-env-list" class="red-ui-editor-subflow-env-list"></ol>').appendTo(listContainer); var list = $('<ol id="red-ui-editor-subflow-env-list" class="red-ui-editor-subflow-env-list"></ol>').appendTo(listContainer);

View File

@ -238,17 +238,25 @@ var api = module.exports = {
if (!credentials) { if (!credentials) {
return resolve({}); return resolve({});
} }
var definition = runtime.nodes.getCredentialDefinition(opts.type) || {};
var sendCredentials = {}; var sendCredentials = {};
for (var cred in definition) { var cred;
if (definition.hasOwnProperty(cred)) { if (/^subflow(:|$)/.test(opts.type)) {
if (definition[cred].type == "password") { for (cred in credentials) {
var key = 'has_' + cred; if (credentials.hasOwnProperty(cred)) {
sendCredentials[key] = credentials[cred] != null && credentials[cred] !== ''; sendCredentials['has_'+cred] = credentials[cred] != null && credentials[cred] !== '';
continue; }
}
} else {
var definition = runtime.nodes.getCredentialDefinition(opts.type) || {};
for (cred in definition) {
if (definition.hasOwnProperty(cred)) {
if (definition[cred].type == "password") {
var key = 'has_' + cred;
sendCredentials[key] = credentials[cred] != null && credentials[cred] !== '';
continue;
}
sendCredentials[cred] = credentials[cred] || '';
} }
sendCredentials[cred] = credentials[cred] || '';
} }
} }
resolve(sendCredentials); resolve(sendCredentials);

View File

@ -341,33 +341,62 @@ var api = module.exports = {
extract: function(node) { extract: function(node) {
var nodeID = node.id; var nodeID = node.id;
var nodeType = node.type; var nodeType = node.type;
var cred;
var newCreds = node.credentials; var newCreds = node.credentials;
if (newCreds) { if (newCreds) {
delete node.credentials; delete node.credentials;
var savedCredentials = credentialCache[nodeID] || {}; var savedCredentials = credentialCache[nodeID] || {};
var dashedType = nodeType.replace(/\s+/g, '-'); if (/^subflow(:|$)/.test(nodeType)) {
var definition = credentialsDef[dashedType]; for (cred in newCreds) {
if (!definition) { if (newCreds.hasOwnProperty(cred)) {
log.warn(log._("nodes.credentials.not-registered",{type:nodeType})); if (newCreds[cred] === "__PWRD__") {
return; continue;
} }
if (0 === newCreds[cred].length || /^\s*$/.test(newCreds[cred])) {
delete savedCredentials[cred];
dirty = true;
continue;
}
if (!savedCredentials.hasOwnProperty(cred) || JSON.stringify(savedCredentials[cred]) !== JSON.stringify(newCreds[cred])) {
savedCredentials[cred] = newCreds[cred];
dirty = true;
}
for (var cred in definition) {
if (definition.hasOwnProperty(cred)) {
if (newCreds[cred] === undefined) {
continue;
} }
if (definition[cred].type == "password" && newCreds[cred] == '__PWRD__') { }
continue; for (cred in savedCredentials) {
if (savedCredentials.hasOwnProperty(cred)) {
if (!newCreds.hasOwnProperty(cred)) {
delete savedCredentials[cred];
dirty = true;
}
} }
if (0 === newCreds[cred].length || /^\s*$/.test(newCreds[cred])) { }
delete savedCredentials[cred]; } else {
dirty = true; var dashedType = nodeType.replace(/\s+/g, '-');
continue; var definition = credentialsDef[dashedType];
} if (!definition) {
if (!savedCredentials.hasOwnProperty(cred) || JSON.stringify(savedCredentials[cred]) !== JSON.stringify(newCreds[cred])) { log.warn(log._("nodes.credentials.not-registered",{type:nodeType}));
savedCredentials[cred] = newCreds[cred]; return;
dirty = true; }
for (cred in definition) {
if (definition.hasOwnProperty(cred)) {
if (newCreds[cred] === undefined) {
continue;
}
if (definition[cred].type == "password" && newCreds[cred] == '__PWRD__') {
continue;
}
if (0 === newCreds[cred].length || /^\s*$/.test(newCreds[cred])) {
delete savedCredentials[cred];
dirty = true;
continue;
}
if (!savedCredentials.hasOwnProperty(cred) || JSON.stringify(savedCredentials[cred]) !== JSON.stringify(newCreds[cred])) {
savedCredentials[cred] = newCreds[cred];
dirty = true;
}
} }
} }
} }

View File

@ -22,6 +22,9 @@ const util = require("util");
const redUtil = require("@node-red/util").util; const redUtil = require("@node-red/util").util;
const flowUtil = require("./util"); const flowUtil = require("./util");
const credentials = require("../credentials");
var Log; var Log;
/** /**
@ -38,6 +41,9 @@ function evaluateInputValue(value, type, node) {
if (type === "bool") { if (type === "bool") {
return (value === "true") || (value === true); return (value === "true") || (value === true);
} }
if (type === "cred") {
return value;
}
return redUtil.evaluateNodeProperty(value, type, node, null, null); return redUtil.evaluateNodeProperty(value, type, node, null, null);
} }
@ -142,10 +148,16 @@ class Subflow extends Flow {
this.node_map = node_map; this.node_map = node_map;
this.path = parent.path+"/"+(subflowInstance._alias||subflowInstance.id); this.path = parent.path+"/"+(subflowInstance._alias||subflowInstance.id);
this.templateCredentials = credentials.get(subflowDef.id);
this.instanceCredentials = credentials.get(this.id);
var env = []; var env = [];
if (this.subflowDef.env) { if (this.subflowDef.env) {
this.subflowDef.env.forEach(e => { this.subflowDef.env.forEach(e => {
env[e.name] = e; env[e.name] = e;
if (e.type === "cred") {
e.value = this.templateCredentials[e.name];
}
}); });
} }
if (this.subflowInstance.env) { if (this.subflowInstance.env) {
@ -156,6 +168,13 @@ class Subflow extends Flow {
if (ui) { if (ui) {
env[e.name].ui = ui; env[e.name].ui = ui;
} }
if (e.type === "cred") {
if (!old || this.instanceCredentials.hasOwnProperty(e.name) ) {
e.value = this.instanceCredentials[e.name];
} else if (old) {
e.value = this.templateCredentials[e.name];
}
}
}); });
} }
this.env = env; this.env = env;
@ -324,7 +343,6 @@ class Subflow extends Flow {
* @return {Object} val value of env var * @return {Object} val value of env var
*/ */
getSetting(name) { getSetting(name) {
this.trace("getSetting:"+name);
if (!/^\$parent\./.test(name)) { if (!/^\$parent\./.test(name)) {
var env = this.env; var env = this.env;
var is_info = name.endsWith("_info"); var is_info = name.endsWith("_info");
@ -333,7 +351,6 @@ class Subflow extends Flow {
if (env && env.hasOwnProperty(ename)) { if (env && env.hasOwnProperty(ename)) {
var val = env[ename]; var val = env[ename];
if (is_type) { if (is_type) {
return val ? val.type : undefined; return val ? val.type : undefined;
} }