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

Merge branch 'master' into pr_2492

This commit is contained in:
Nick O'Leary 2020-04-08 12:42:33 +01:00
commit 821b5686f2
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
58 changed files with 1012 additions and 392 deletions

View File

@ -1,3 +1,42 @@
#### 1.0.5: Maintenance Release
Runtime
- #2500 Support for context stores using JSONata and evaluateNodeProperty()
- Add better handling of host-key-verify error with projects
- #2517 Handle false values in $env() properly
- #2514 Ensure complete node scope is remapped in subflows
- #2513 Flows/subflows must preinitialise their context objects
- Clear node.close timeout to avoid unnecessary work on restart
- #2532 Set flow.disabled when disabled property is false
- #2522 Ensure file context does not write 'undefined' to store
Editor
- #2489 Fix XPath in UI tests
- #2504 Fix paletteCategories order
- #2501 Add page objects for UI testing
- #2494 Check node props when deciding if pasted node can splice links
- #2521 Don't double-sanitize node name in debug sidebar
- #2519 German i18n updates
- #2523 Update nodeTabMap when replacing unknown nodes
- Update TypedInput to use flexbox and remove resizing code
- Handle nodes with no wires array
- Do not collapse whitespace in Debug string messages
Nodes
- File: Remove old legacy wording from file node info to stop confusing users.
- Join: Ensure join node handles missing buffer joiner when not in string mode
- Exec: make exec node logging consistent with itself. (only be verbose when in verbose mode)
- Trigger: reset default timeout value when switching away from wait for reset
- Join: Fix join to not crash on appending invalid types to buffer.
- MQTT out: Add warning if topic contains + or #
- #2502 WebSocket i18n update
- #2508 Add Japanese translation for join node
- TCP out: tidy up select of which rows to display
#### 1.0.4: Maintenance Release
Runtime

View File

@ -34,11 +34,11 @@
"view" : "Ansicht",
"grid" : "Gitter",
"showGrid" : "Raster anzeigen",
"snapGrid" : "Einrasten am Raster",
"snapGrid" : "Am Raster ausrichten",
"gridSize" : "Rastergröße",
"textDir" : "Textrichtung",
"defaultDir" : "Standard",
"ltr" : "Links-nach-rechts",
"ltr" : "Von links nach rechts",
"rtl" : "Von rechts nach links",
"auto" : "Kontextuell"
},
@ -53,15 +53,15 @@
"import" : "Import",
"export" : "Exportieren",
"search" : "Flows durchsuchen",
"searchInput" : "durchsuchen Sie Ihre Flows",
"searchInput" : "Flows durchsuchen",
"subflows" : "Subflow",
"createSubflow" : "Subflow erstellen",
"selectionToSubflow" : "Auswahl für Subflow",
"selectionToSubflow" : "Auswahl zu Subflow",
"flows" : "Flows",
"add" : "Hinzufügen",
"rename" : "Umbenennen",
"delete" : "Löschen",
"keyboardShortcuts" : "Tastaturkurzbefehle",
"keyboardShortcuts" : "Tastenkürzel",
"login" : "Anmelden",
"logout" : "Abmelden",
"editPalette" : "Palette verwalten",
@ -217,7 +217,7 @@
"remote" : "Ferne Änderungen",
"reviewChanges" : "Änderungen prüfen",
"noBinaryFileShowed" : "Der Inhalt der Binärdatei kann nicht angezeigt",
"viewCommitDiff" : "Änderungen festschreiben",
"viewCommitDiff" : "Änderungen committen",
"compareChanges" : "Änderungen vergleichen",
"saveConflict" : "Konfliktlösung speichern",
"conflictHeader" : "<span> __resolved__ </span> von <span> __unresolved__ </span> -Konflikten behoben",
@ -226,8 +226,8 @@
"newVersionError" : "Neue Version enthält keine gültige JSON-Datei:"
},
"subflow" : {
"editSubflow" : "Flowschablone bearbeiten: __name__",
"edit" : "Flowsschablone bearbeiten",
"editSubflow" : "Subflow bearbeiten: __name__",
"edit" : "Subflow bearbeiten",
"subflowInstances" : "Es ist __count__ Instanz dieser Subflow-Vorlage vorhanden.",
"subflowInstances_plural" : "Es gibt __count__ Instanzen dieser Subflow-Vorlage.",
"editSubflowProperties" : "Eigenschaften bearbeiten",
@ -266,7 +266,7 @@
}
},
"keyboard" : {
"title" : "Tastaturkurzbefehle",
"title" : "Tastenkürzel",
"keyboard" : "Tastatur",
"filterActions" : "Filteraktionen",
"shortcut" : "Direktaufruf",
@ -283,7 +283,7 @@
"exportNode" : "Node exportieren",
"nudgeNode" : "Ausgewählte Nodes verschieben (1px)",
"moveNode" : "Ausgewählte Nodes verschieben (20px)",
"toggleSidebar" : "Seitenleiste ein-/ausschalten",
"toggleSidebar" : "Seitenleiste ein-/ausblenden",
"copyNode" : "Ausgewählte Nodes kopieren",
"cutNode" : "Ausgewählte Nodes ausschneiden",
"pasteNode" : "Node einfügen",
@ -308,7 +308,7 @@
},
"palette" : {
"noInfo" : "Keine Informationen verfügbar",
"filter" : "Filter Nodes",
"filter" : "Nodes filtern",
"search" : "Suchmodule",
"addCategory" : "Neu hinzufügen ...",
"label" : {
@ -366,11 +366,11 @@
"remove" : "entfernen",
"update" : "Update auf __version__",
"updated" : "aktualisiert",
"install" : "installieren",
"installed" : "installiert",
"install" : "Installieren",
"installed" : "Installiert",
"loading" : "Kataloge werden geladen ...",
"tab-nodes" : "Nodes",
"tab-install" : "installieren",
"tab-install" : "Installieren",
"sort" : "Sortierung:",
"sortAZ" : "a-z",
"sortRecent" : "kürzlich",
@ -452,7 +452,7 @@
"name" : "Kontextdaten",
"label" : "Kontext",
"none" : "keine ausgewählt",
"refresh" : "Aktualisierung zum Laden",
"refresh" : "Zum Aktualisieren neu laden",
"empty" : "leer",
"node" : "Node",
"flow" : "Flow",
@ -477,7 +477,7 @@
"none" : "Keine",
"install" : "installieren",
"removeFromProject" : "Aus Projekt entfernen",
"addToProject" : "zu Projekt hinzufügen",
"addToProject" : "Zu Projekt hinzufügen",
"files" : "Dateien",
"flow" : "Flow",
"credentials" : "Berechtigungsnachweis",
@ -510,7 +510,7 @@
},
"userSettings" : {
"committerDetail" : "Committer-Details",
"committerTip" : "Leer Wert für Systemstandardwert belassen",
"committerTip" : "Leer lassen für Systemstandard",
"userName" : "Benutzername",
"email" : "E-Mail",
"sshKeys" : "SSH-Schlüssel",
@ -544,7 +544,7 @@
"revertChanges" : "Änderungen zurücksetzen",
"localChanges" : "Lokale Änderungen",
"none" : "Keine",
"conflictResolve" : "Alle Konflikte wurden aufgelöst. Festschreiben der Änderungen, um den Mischvorgang abzuschließen.",
"conflictResolve" : "Alle Konflikte wurden aufgelöst. Committe die Änderungen, um den Merge Request abzuschließen.",
"localFiles" : "Lokale Dateien",
"all" : "alle",
"unmergedChanges" : "Nicht zusammengeführte Änderungen",

View File

@ -977,7 +977,8 @@
"passphrase": "Passphrase",
"retry": "Retry",
"update-failed": "Failed to update auth",
"unhandled": "Unhandled error response"
"unhandled": "Unhandled error response",
"host-key-verify-failed": "<p>Host key verification failed.</p><p>The repository host key could not be verified. Please update your <code>known_hosts</code> file and try again."
},
"create-branch-list": {
"invalid": "Invalid branch",

View File

@ -1109,7 +1109,7 @@ RED.nodes = (function() {
defaults: {},
label: "unknown: "+n.type,
labelStyle: "red-ui-flow-node-label-italic",
outputs: n.outputs||n.wires.length,
outputs: n.outputs|| (n.wires && n.wires.length) || 0,
set: registry.getNodeSet("node-red/unknown")
}
} else {
@ -1434,6 +1434,9 @@ RED.nodes = (function() {
delete configNodes[n.id];
} else {
nodes.splice(nodes.indexOf(n),1);
if (nodeTabMap[n.z]) {
delete nodeTabMap[n.z][n.id];
}
}
reimportList.push(convertNode(n));
});

View File

@ -488,56 +488,6 @@
done(labelWidth);
}
},
_resize: function() {
var that = this;
if (this.uiWidth !== null) {
this.uiSelect.width(this.uiWidth);
}
var type = this.typeMap[this.propertyType];
if (type && type.hasValue === false) {
this.selectTrigger.addClass("red-ui-typedInput-full-width");
} else {
this.selectTrigger.removeClass("red-ui-typedInput-full-width");
this._getLabelWidth(this.selectTrigger, function(labelWidth) {
that.elementDiv.css('left',labelWidth+"px");
that.valueLabelContainer.css('left',labelWidth+"px");
if (that.optionExpandButton.shown) {
that.elementDiv.css('right',"22px");
that.valueLabelContainer.css('right',"22px");
} else {
that.elementDiv.css('right','0');
that.valueLabelContainer.css('right','0');
that.input.css({
'border-top-right-radius': '4px',
'border-bottom-right-radius': '4px'
});
}
if (that.optionSelectTrigger) {
if (type && type.options && type.hasValue === true) {
that.optionSelectLabel.css({'left':'auto'})
that._getLabelWidth(that.optionSelectLabel, function(lw) {
that.optionSelectTrigger.css({'width':(23+lw)+"px"});
that.elementDiv.css('right',(23+lw)+"px");
that.input.css({
'border-top-right-radius': 0,
'border-bottom-right-radius': 0
});
});
} else {
that.optionSelectLabel.css({'left':'0'})
that.optionSelectTrigger.css({'width':'calc( 100% - '+labelWidth+'px )'});
if (!that.optionExpandButton.shown) {
that.elementDiv.css({'right':0});
that.input.css({
'border-top-right-radius': '4px',
'border-bottom-right-radius': '4px'
});
}
}
}
});
}
},
_updateOptionSelectLabel: function(o) {
var opt = this.typeMap[this.propertyType];
this.optionSelectLabel.empty();
@ -565,7 +515,6 @@
}
if (opt.hasValue) {
this.optionValue = o.value;
this._resize();
this.input.trigger('change',this.propertyType,this.value());
}
} else {
@ -605,11 +554,12 @@
this.propertyType = null;
this.type(currentType);
}
setTimeout(function() {that._resize();},0);
},
width: function(desiredWidth) {
this.uiWidth = desiredWidth;
this._resize();
if (this.uiWidth !== null) {
this.uiSelect.width(this.uiWidth);
}
},
value: function(value) {
var that = this;
@ -680,8 +630,6 @@
}
else if (opt.icon.indexOf("/") !== -1) {
image = new Image();
image.onload = function() { that._resize(); }
image.onerror = function() { that._resize(); }
image.name = opt.icon;
image.src = mapDeprecatedIcon(opt.icon);
$('<img>',{src:mapDeprecatedIcon(opt.icon),style:"margin-right: 4px;height: 18px;"}).prependTo(this.selectLabel);
@ -693,6 +641,12 @@
if (opt.hasValue === false || (opt.showLabel !== false && !opt.icon)) {
this.selectLabel.text(opt.label);
}
if (opt.hasValue === false) {
this.selectTrigger.addClass("red-ui-typedInput-full-width");
} else {
this.selectTrigger.removeClass("red-ui-typedInput-full-width");
}
if (this.optionMenu) {
this.optionMenu.remove();
this.optionMenu = null;
@ -881,9 +835,6 @@
this._trigger("typechange",null,this.propertyType);
this.input.trigger('change',this.propertyType,this.value());
}
if (!image) {
this._resize();
}
}
}
},
@ -910,7 +861,6 @@
},
show: function() {
this.uiSelect.show();
this._resize();
},
hide: function() {
this.uiSelect.hide();

View File

@ -1939,13 +1939,15 @@ RED.projects = (function() {
}
}).fail(function(xhr,textStatus,err) {
var responses;
if (options.responses && options.responses[xhr.status]) {
responses = options.responses[xhr.status];
if (typeof responses === 'function') {
resultCallback = responses;
resultCallbackArgs = {error:responses.statusText};
return;
} else if (options.handleAuthFail !== false && xhr.responseJSON.code === 'git_auth_failed') {
} else if (options.handleAuthFail !== false && (xhr.responseJSON.code === 'git_auth_failed' || xhr.responseJSON.code === 'git_host_key_verification_failed')) {
if (xhr.responseJSON.code === 'git_auth_failed') {
var url = activeProject.git.remotes[xhr.responseJSON.remote||options.remote||'origin'].fetch;
var message = $('<div>'+
@ -2033,6 +2035,25 @@ RED.projects = (function() {
]
});
return;
} else if (xhr.responseJSON.code === 'git_host_key_verification_failed') {
var message = $('<div>'+
'<div class="form-row">'+RED._("projects.send-req.host-key-verify-failed")+'</div>'+
'</div>');
var notification = RED.notify(message,{
type:"error",
fixed: true,
modal: true,
buttons: [
{
text: RED._("common.label.close"),
click: function() {
notification.close();
}
}
]
});
return;
}
} else if (responses[xhr.responseJSON.code]) {
resultCallback = responses[xhr.responseJSON.code];
resultCallbackArgs = xhr.responseJSON;

View File

@ -3531,8 +3531,10 @@ RED.view = (function() {
if (new_ms.length === 1) {
node = new_ms[0];
spliceActive = node.n.hasOwnProperty("_def") &&
node.n._def.inputs > 0 &&
node.n._def.outputs > 0;
((node.n.hasOwnProperty("inputs") && node.n.inputs > 0) || (!node.n.hasOwnProperty("inputs") && node.n._def.inputs > 0)) &&
((node.n.hasOwnProperty("outputs") && node.n.outputs > 0) || (!node.n.hasOwnProperty("outputs") && node.n._def.outputs > 0))
}
}
RED.keyboard.add("*","escape",function(){

View File

@ -217,6 +217,10 @@
.red-ui-debug-msg-type-number { color: $debug-message-text-color-msg-type-number; };
.red-ui-debug-msg-type-number-toggle { cursor: pointer;}
.red-ui-debug-msg-type-string {
white-space: pre-wrap;
}
.red-ui-debug-msg-row {
display: block;
padding: 4px 2px 2px;

View File

@ -18,7 +18,7 @@
border: 1px solid $form-input-border-color;
border-radius: 4px;
height: 34px;
display: inline-block;
display: inline-flex;
padding: 0;
margin: 0;
vertical-align: middle;
@ -26,12 +26,7 @@
overflow:visible;
position: relative;
.red-ui-typedInput-input-wrap {
position: absolute;
left:0;
right:0;
top:0;
bottom:0;
outline: red;
flex-grow: 1;
}
input.red-ui-typedInput-input {
width: 100%;
@ -49,7 +44,7 @@
border-color: $form-input-focus-color !important;
}
.red-ui-typedInput-value-label {
position: absolute;
flex-grow: 1;
display: inline-block;
height: 32px;
box-sizing: border-box;
@ -104,12 +99,11 @@ button.red-ui-typedInput-option-trigger
{
text-align: left;
border: none;
position: absolute;
flex-basis: auto;
box-sizing: border-box;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
padding: 0 1px 0 5px;
display:inline-block;
background: $form-button-background;
height: 32px;
line-height: 30px;
@ -151,7 +145,7 @@ button.red-ui-typedInput-option-trigger
text-decoration: none;
}
&.red-ui-typedInput-full-width {
width: 100%;
flex-grow: 1;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
@ -167,7 +161,6 @@ button.red-ui-typedInput-option-expand {
border-bottom-right-radius: 4px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
right: 0;
}
button.red-ui-typedInput-option-trigger {
@ -176,8 +169,8 @@ button.red-ui-typedInput-option-trigger {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
padding: 0 0 0 0;
position:absolute;
right: 0;
position:relative;
flex-grow: 1;
.red-ui-typedInput-option-label {
background:$form-button-background;
color: $form-text-color;

View File

@ -457,7 +457,7 @@ RED.debug = (function() {
var metaRow = $('<div class="red-ui-debug-msg-meta"></div>').appendTo(msg);
$('<span class="red-ui-debug-msg-date">'+ getTimestamp()+'</span>').appendTo(metaRow);
if (sourceNode) {
$('<a>',{href:"#",class:"red-ui-debug-msg-name"}).text('node: '+sanitize(o.name||sourceNode.name||sourceNode.id))
$('<a>',{href:"#",class:"red-ui-debug-msg-name"}).text('node: '+(o.name||sourceNode.name||sourceNode.id))
.appendTo(metaRow)
.on("click", function(evt) {
evt.preventDefault();

View File

@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="trigger">
<script type="text/html" data-template-name="trigger">
<div class="form-row">
<label data-i18n="trigger.send" for="node-input-op1"></label>
<input type="hidden" id="node-input-op1type">
@ -109,9 +109,11 @@
$(".node-type-duration").hide();
}
else if ($(this).val() == "loop") {
if ($("#node-input-duration").val() == 0) { $("#node-input-duration").val(250); }
$(".node-type-wait").hide();
$(".node-type-duration").show();
} else {
if ($("#node-input-duration").val() == 0) { $("#node-input-duration").val(250); }
$(".node-type-wait").show();
$(".node-type-duration").show();
}
@ -176,8 +178,6 @@
if ($("#node-then-type").val() == "loop") {
$("#node-input-duration").val($("#node-input-duration").val() * -1);
}
}
});
</script>

View File

@ -145,7 +145,7 @@ module.exports = function(RED) {
if (error.signal) { msg3.payload.signal = error.signal; }
if (error.code === null) { node.status({fill:"red",shape:"dot",text:"killed"}); }
else { node.status({fill:"red",shape:"dot",text:"error:"+error.code}); }
node.log('error:' + error);
if (RED.settings.verbose) { node.log('error:' + error); }
}
else if (node.oldrc === "false") {
msg3 = RED.util.cloneMessage(msg);

View File

@ -469,6 +469,7 @@ module.exports = function(RED) {
this.broker = n.broker;
this.brokerConn = RED.nodes.getNode(this.broker);
var node = this;
var chk = /[\+#]/;
if (this.brokerConn) {
this.status({fill:"red",shape:"ring",text:"node-red:common.status.disconnected"});
@ -487,6 +488,7 @@ module.exports = function(RED) {
}
if ( msg.hasOwnProperty("payload")) {
if (msg.hasOwnProperty("topic") && (typeof msg.topic === "string") && (msg.topic !== "")) { // topic must exist
if (chk.test(msg.topic)) { node.warn(RED._("mqtt.errors.invalid-topic")); }
this.brokerConn.publish(msg, done); // send the message
} else {
node.warn(RED._("mqtt.errors.invalid-topic"));

View File

@ -163,7 +163,7 @@
if (root === "") {
$("#node-config-ws-tip").hide();
} else {
$("#node-config-ws-path").html(root);
$("#node-config-ws-path").html(RED._("node-red:websocket.tip.path2", { path: root }));
$("#node-config-ws-tip").show();
}
}
@ -235,7 +235,7 @@
</div>
<div class="form-tips">
<span data-i18n="[html]websocket.tip.path1"></span>
<p id="node-config-ws-tip"><span data-i18n="[html]websocket.tip.path2"></span><code><span id="node-config-ws-path"></span></code>.</p>
<p id="node-config-ws-tip"><span id="node-config-ws-path"></span></p>
</div>
</script>

View File

@ -170,15 +170,14 @@
$("#node-input-port-row").hide();
$("#node-input-host-row").hide();
$("#node-input-end-row").hide();
} else if (sockettype == "client"){
$("#node-input-port-row").show();
$("#node-input-host-row").show();
$("#node-input-end-row").show();
} else {
$("#node-input-port-row").show();
$("#node-input-end-row").show();
}
if (sockettype == "client") {
$("#node-input-host-row").show();
} else {
$("#node-input-host-row").hide();
$("#node-input-end-row").show();
}
};
updateOptions();

View File

@ -630,8 +630,14 @@ module.exports = function(RED) {
var group = inflight[partId];
if (payloadType === 'buffer') {
if (property !== undefined) {
if (Buffer.isBuffer(property) || (typeof property === "string") || Array.isArray(property)) {
inflight[partId].bufferLen += property.length;
}
else {
node.error(RED._("join.errors.invalid-type",{error:(typeof property)}),msg);
return;
}
}
}
if (payloadType === 'object') {
group.payload[propertyKey] = property;

View File

@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="file">
<script type="text/html" data-template-name="file">
<div class="form-row node-input-filename">
<label for="node-input-filename"><i class="fa fa-file"></i> <span data-i18n="file.label.filename"></span></label>
<input id="node-input-filename" type="text">
@ -34,7 +34,7 @@
<div class="form-tips"><span data-i18n="file.tip"></span></div>
</script>
<script type="text/x-red" data-template-name="file in">
<script type="text/html" data-template-name="file in">
<div class="form-row">
<label for="node-input-filename"><i class="fa fa-file"></i> <span data-i18n="file.label.filename"></span></label>
<input id="node-input-filename" type="text" data-i18n="[placeholder]file.label.filename">

View File

@ -397,7 +397,7 @@
"message" : "gesamte Nachricht",
"tip" : {
"path1" : "Standardmäßig enthält <code> Nutzdaten </code> die Daten, die über einen Websocket gesendet oder von einem Websocket empfangen werden. Der Listener kann so konfiguriert werden, dass er das gesamte Nachrichtenobjekt als eine JSON-formatierte Zeichenfolge sendet oder empfängt.",
"path2" : "Dieser Pfad ist relativ zu ",
"path2" : "Dieser Pfad ist relativ zu <code>__path__</code>.",
"url1" : "URL sollte ws: &#47; & #47; oder wss: &#47; & #47; Schema verwenden und auf einen vorhandenen Websocket-Listener verweisen.",
"url2" : "Standardmäßig enthält <code> Nutzdaten </code> die Daten, die über einen Websocket gesendet oder von einem Websocket empfangen werden. Der Client kann so konfiguriert werden, dass er das gesamte Nachrichtenobjekt als eine JSON-formatierte Zeichenfolge sendet oder empfängt."
},

View File

@ -37,30 +37,6 @@
<p>Dieser Konfigurations-Node erstellt einen WebSocket Server-Endpunkt unter Verwendung des angegebenen Pfades.</p>
</script>
<!-- WebSocket Client configuration node -->
<script type="text/x-red" data-template-name="websocket-client">
<div class="form-row">
<label for="node-config-input-path"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.url"></span></label>
<input id="node-config-input-path" type="text" placeholder="ws://example.com/ws">
</div>
<div class="form-row node-config-row-tls hide">
<label for="node-config-input-tls" data-i18n="httpin.tls-config"></label>
<input type="text" id="node-config-input-tls">
</div>
<div class="form-row">
<label for="node-config-input-wholemsg" data-i18n="websocket.sendrec"></label>
<select type="text" id="node-config-input-wholemsg" style="width: 70%;">
<option value="false" data-i18n="websocket.payload"></option>
<option value="true" data-i18n="websocket.message"></option>
</select>
</div>
<div class="form-tips">
<p><span data-i18n="[html]websocket.tip.url1"></span></p>
<span data-i18n="[html]websocket.tip.url2"></span>
</div>
</script>
<script type="text/x-red" data-help-name="websocket-client">
<p>Dieser Konfigurations-Node verbindet einen WebSocket-Client mit der angegebenen URL.</p>
</script>

View File

@ -455,7 +455,7 @@
"message": "entire message",
"tip": {
"path1": "By default, <code>payload</code> will contain the data to be sent over, or received from a websocket. The listener can be configured to send or receive the entire message object as a JSON formatted string.",
"path2": "This path will be relative to ",
"path2": "This path will be relative to <code>__path__</code>.",
"url1": "URL should use ws:&#47;&#47; or wss:&#47;&#47; scheme and point to an existing websocket listener.",
"url2": "By default, <code>payload</code> will contain the data to be sent over, or received from a websocket. The client can be configured to send or receive the entire message object as a JSON formatted string."
},
@ -894,7 +894,8 @@
"fixup": "Fix-up exp"
},
"errors": {
"invalid-expr": "Invalid JSONata expression: __error__"
"invalid-expr": "Invalid JSONata expression: __error__",
"invalid-type": "Cannot join __error__ to buffer"
}
},
"sort" : {

View File

@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="split">
<script type="text/html" data-help-name="split">
<p>Splits a message into a sequence of messages.</p>
<h3>Inputs</h3>
@ -63,7 +63,7 @@
means it cannot be used with the <b>join</b> node in its automatic mode</p>
</script>
<script type="text/x-red" data-help-name="join">
<script type="text/html" data-help-name="join">
<p>Joins sequences of messages into a single message.</p>
<p>There are three modes available:</p>
<dl>
@ -91,7 +91,8 @@
</ul>
</dd>
<dt class="optional">complete</dt>
<dd>If set, the node will send its output message in its current state.</dd>
<dd>If set, the node will append the payload, and then send the output message in its current state.
If you don't wish to append the payload, delete it from the msg.</dd>
</dl>
<h3>Details</h3>

View File

@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="file">
<script type="text/html" data-help-name="file">
<p>Writes <code>msg.payload</code> to a file, either adding to the end or replacing the existing content.
Alternatively, it can delete the file.</p>
<h3>Inputs</h3>
@ -36,7 +36,7 @@
<p>Alternatively, this node can be configured to delete the file.</p>
</script>
<script type="text/x-red" data-help-name="file in">
<script type="text/html" data-help-name="file in">
<p>Reads the contents of a file as either a string or binary buffer.</p>
<h3>Inputs</h3>
<dl class="message-properties">
@ -49,12 +49,6 @@
<dd>The contents of the file as either a string or binary buffer.</dd>
<dt class="optional">filename <span class="property-type">string</span></dt>
<dd>If not configured in the node, this optional property sets the name of the file to be read.</dd>
<dt class="optional">error <span class="property-type">object</span></dt>
<dd><i>deprecated</i>: If enabled in the node, when the node hits an error
reading the file, it will send a message with no <code>payload</code>
and this <code>error</code> property set to the error details. This
mode of behaviour is deprecated and not enabled by default for new
instances of the node. See below for more information.</dd>
</dl>
<h3>Details</h3>
<p>The filename should be an absolute path, otherwise it will be relative to
@ -65,11 +59,5 @@
<p>When split into multiple messages, each message will have a <code>parts</code>
property set, forming a complete message sequence.</p>
<p>Encoding of input data can be specified from list of encodings if output format is string.</p>
<h4>Legacy error handling</h4>
<p>Before Node-RED 0.17, if this node hit an error whilst reading the file, it would
send a message with no <code>msg.payload</code> and <code>msg.error</code> set to the
details of the error. This is a deprecated mode of behaviour for the node that new
instances will not do. If required, this mode can be re-enabled within the node
configuration.</p>
<p>Errors should be caught and handled using a Catch node.</p>
</script>

View File

@ -455,7 +455,7 @@
"message": "メッセージ全体を送信/受信",
"tip": {
"path1": "標準では <code>payload</code> がwebsocketから送信、受信されるデータを持ちます。クライアントはJSON形式の文字列としてメッセージ全体を送信、受信するよう設定できます。",
"path2": "This path will be relative to ",
"path2": "このパスは <code>__path__</code> の相対パスになります。",
"url1": "URLには ws:&#47;&#47; または wss:&#47;&#47; スキーマを使用して、存在するwebsocketリスナを設定してください。",
"url2": "標準では <code>payload</code> がwebsocketから送信、受信されるデータを持ちます。クライアントはJSON形式の文字列としてメッセージ全体を送信、受信するよう設定できます。"
},
@ -892,7 +892,8 @@
"fixup": "最終調整式"
},
"errors": {
"invalid-expr": "JSONata式が不正: __error__"
"invalid-expr": "JSONata式が不正: __error__",
"invalid-type": "__error__ をバッファに連結できません"
}
},
"sort": {

View File

@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="split">
<script type="text/html" data-help-name="split">
<p>メッセージをメッセージ列に分割します。</p>
<h3>入力</h3>
@ -52,8 +52,7 @@
<p>このモードで処理する際には、メッセージ数を予め知ることができないため、<code>msg.parts.count</code>プロパティは設定されません。従って、<b>join</b>ノードの「自動モード」と組み合わせることはできません。</p>
</script>
<script type="text/x-red" data-help-name="join">
<script type="text/html" data-help-name="join">
<p>メッセージ列を結合して一つのメッセージにします。</p>
<p>メッセージの結合には次の3つのモードが利用できます。</p>
<dl>
@ -79,7 +78,7 @@
</ul>
</dd>
<dt class="optional">complete</dt>
<dd>設定されている場合、保持しているメッセージを結合して送信します。</dd>
<dd>設定されている場合、本ノードはペイロードを追加し、保持しているメッセージを送信します。ペイロードを追加したくない場合は、msgから削除してください</dd>
</dl>
<h3>詳細</h3>

View File

@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="file">
<script type="text/html" data-help-name="file">
<p><code>msg.payload</code>をファイルに書き出します。書き出しは、ファイルの最後に追記もしくは既存の内容の置き換えを選択できます。この他、ファイルの削除を行うことも可能です。</p>
<h3>入力</h3>
<dl class="message-properties">
@ -31,7 +31,7 @@
<p>この他、ファイルの削除を行うことも可能です。</p>
</script>
<script type="text/x-red" data-help-name="file in">
<script type="text/html" data-help-name="file in">
<p>ファイルの内容を文字列もしくはバイナリバッファとして読み出します。</p>
<h3>入力</h3>
<dl class="message-properties">
@ -44,8 +44,6 @@
<dd>ファイルの内容を文字列もしくはバッファで表現します</dd>
<dt class="optional">filename <span class="property-type">文字列</span></dt>
<dd>読み出し対象のファイル名をノードに設定していない場合、このプロパティでファイルを指定します</dd>
<dt class="optional">error <span class="property-type">オブジェクト</span></dt>
<dd><i>非推奨</i>: 設定で有効にした場合、ファイルの読み込み時にエラーが発生すると<code>payload</code>を持たず<code>error</code>プロパティにエラーの詳細情報を設定したメッセージを送信します。この動作モードは非推奨であり、新しいノード実装ではデフォルトでは無効としています。詳細については、以下を参照してください。</dd>
</dl>
<h3>詳細</h3>
<p>ファイルネームは絶対パスでの指定を推奨します。絶対パスを指定しない場合は、Node-REDプロセスのワーキングディレクトリからの相対パスとして扱います。</p>
@ -53,7 +51,5 @@
<p>テキストファイルの場合、行毎に分割して各々メッセージを送信することができます。また、バイナリファイルの場合、小さな塊のバッファに分割して送信できます。バッファの分割単位はオペレーティングシステム依存ですが、一般に64k(Linux/Mac)もしくは41k(Windows)です。</p>
<p>複数のメッセージに分割する場合、各メッセージには<code>parts</code>プロパティが設定され、メッセージ列を構成します。</p>
<p>出力形式が文字列の場合、入力データのエンコーディングをエンコーディングリストから選択できます。</p>
<h4>旧式のエラー処理</h4>
<p>Node-RED 0.17より前の版では、ファイルの読み込み時にエラーが発生すると<code>payload</code>を持たず<code>error</code>プロパティにエラーの詳細情報を設定したメッセージを送信します。この動作モードは非推奨であり、新しいノード実装ではデフォルトでは無効としています。ノードの設定により、必要に応じてこのモードを有効にできます。</p>
<p>エラーはcatchードで補足して処理することを推奨します。</p>
</script>

View File

@ -446,7 +446,7 @@
"message": "메세지 전체를 송신/수신",
"tip": {
"path1": "표준으로는 <code>payload</code> 가 websocket에서 송신, 수신된 데이터를 기다립니다. 클라이언트는 JSON형식의 문자열로 메세지전체를 송신, 수신하도록 설정할 수 있습니다.",
"path2": "This path will be relative to ",
"path2": "This path will be relative to <code>__path__</code>.",
"url1": "URL에는 ws:&#47;&#47; 또는 wss:&#47;&#47; 스키마를 사용하여, 존재하는 websocket리스너를 설정해 주세요.",
"url2": "표준으로는 <code>payload</code> 가 websocket에서 송신,수신될 데이터를 기다립니다.클라이언트는 JSON형식의 문자열로 메세지전체를 송신, 수신하도록 설정할 수 있습니다."
},

View File

@ -455,7 +455,7 @@
"message": "完整信息",
"tip": {
"path1": "默认情况下,<code>payload</code>将包含要发送或从Websocket接收的数据。侦听器可以配置为以JSON格式的字符串发送或接收整个消息对象.",
"path2": "这条路径将相对于 ",
"path2": "这条路径将相对于 <code>__path__</code>.",
"url1": "URL 应该使用ws:&#47;&#47;或者wss:&#47;&#47;方案并指向现有的websocket侦听器.",
"url2": "默认情况下,<code>payload</code> 将包含要发送或从Websocket接收的数据。可以将客户端配置为以JSON格式的字符串发送或接收整个消息对象."
},

View File

@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="file">
<script type="text/html" data-help-name="file">
<p><code>msg.payload</code>写入文件,添加到末尾或替换现有内容。或者,它也可以删除文件。</p>
<h3>输入</h3>
<dl class="message-properties">
@ -31,7 +31,7 @@
<p>您可以将此节点配置为删除文件。</p>
</script>
<script type="text/x-red" data-help-name="file in">
<script type="text/html" data-help-name="file in">
<p>以字符串或二进制缓冲区的形式读取文件的内容。</p>
<h3>输入</h3>
<dl class="message-properties">
@ -44,8 +44,6 @@
<dd>文件的内容可以是字符串也可以是二进制的buffer。</dd>
<dt class="optional">filename <span class="property-type">字符串</span></dt>
<dd>如果未在节点配置中设置,该属性可以选择要读取的文件名。</dd>
<dt class="optional">error <span class="property-type">object</span></dt>
<dd><i>已不推荐使用</i>: 如果在节点中启用,则当节点在读取文件时遇到错误时,它将发送一条没有<code>有效荷载</code>的消息,且将消息的<code>error</code>属性设置为错误的详细信息。在默认情况下,此行为模式已弃用且未启用。 请参阅下面的详细信息。</dd>
</dl>
<h3>详细</h3>
<p>文件名应该是绝对路径否则将相对于Node-RED进程的工作目录。</p>
@ -53,7 +51,5 @@
<p>可以选择将文本文件拆分为几行每行输出一条消息或者将二进制文件拆分为较小的buffer块-块大小取决于操作系统但通常为64kLinux/Mac或41kWindows</p>
<p>当拆分为多条消息时,每条消息将具有<code>parts</code>属性集,从而形成完整的消息序列。</p>
<p>如果输出格式为字符串,则可以从编码列表中指定输入数据的编码。</p>
<h4>旧版的错误处理</h4>
<p>在Node-RED 0.17之前,如果此节点在读取文件时遇到错误,它将发送一条不包含<code>msg.payload</code>,但包含<code>msg.error</code>的消息。在<code>msg.error</code>中记录详细的错误内容。但这是模式已被弃用,默认未启用。如有需要,您可以在节点配置中重新启用该模式。</p>
<p>应该使用Catch节点来捕获并处理错误。</p>
</script>

View File

@ -455,7 +455,7 @@
"message": "完整資訊",
"tip": {
"path1": "預設情況下,<code>payload</code>將包含要發送或從Websocket接收的資料。偵聽器可以配置為以JSON格式的字串發送或接收整個消息物件.",
"path2": "這條路徑將相對於 ",
"path2": "這條路徑將相對於 <code>__path__</code>.",
"url1": "URL 應該使用ws:&#47;&#47;或者wss:&#47;&#47;方案並指向現有的websocket監聽器.",
"url2": "預設情況下,<code>payload</code> 將包含要發送或從Websocket接收的資料。可以將使用者端配置為以JSON格式的字串發送或接收整個消息物件."
},

View File

@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="file">
<script type="text/html" data-help-name="file">
<p><code>msg.payload</code>寫入文件,添加到末尾或替換現有內容。或者,它也可以刪除文件。</p>
<h3>輸入</h3>
<dl class="message-properties">
@ -31,7 +31,7 @@
<p>您可以將此節點配置爲刪除文件。</p>
</script>
<script type="text/x-red" data-help-name="file in">
<script type="text/html" data-help-name="file in">
<p>以字符串或二進制緩衝區的形式讀取文件的內容。</p>
<h3>輸入</h3>
<dl class="message-properties">
@ -44,8 +44,6 @@
<dd>文件的內容可以是字符串也可以是二進制的buffer。</dd>
<dt class="optional">filename <span class="property-type">字符串</span></dt>
<dd>如果未在節點配置中設置,該屬性可以選擇要讀取的文件名。</dd>
<dt class="optional">error <span class="property-type">object</span></dt>
<dd><i>已不推薦使用</i>: 如果在節點中啓用,則當節點在讀取文件時遇到錯誤時,它將發送一條沒有<code>有效荷載</code>的消息,且將消息的<code>error</code>屬性設置爲錯誤的詳細信息。在默認情況下,此行爲模式已棄用且未啓用。 請參閱下面的詳細信息。</dd>
</dl>
<h3>詳細</h3>
<p>文件名應該是絕對路徑否則將相對于Node-RED進程的工作目錄。</p>
@ -53,7 +51,5 @@
<p>可以選擇將文本文件拆分爲幾行每行輸出一條消息或者將二進制文件拆分爲較小的buffer塊-塊大小取決于操作系統但通常爲64kLinux/Mac或41kWindows</p>
<p>當拆分爲多條消息時,每條消息將具有<code>parts</code>屬性集,從而形成完整的消息序列。</p>
<p>如果輸出格式爲字符串,則可以從編碼列表中指定輸入數據的編碼。</p>
<h4>舊版的錯誤處理</h4>
<p>在Node-RED 0.17之前,如果此節點在讀取文件時遇到錯誤,它將發送一條不包含<code>msg.payload</code>,但包含<code>msg.error</code>的消息。在<code>msg.error</code>中記錄詳細的錯誤內容。但這是模式已被棄用,默認未啓用。如有需要,您可以在節點配置中重新啓用該模式。</p>
<p>應該使用Catch節點來捕獲並處理錯誤。</p>
</script>

View File

@ -420,15 +420,39 @@ function createRootContext() {
Object.defineProperties(obj, {
get: {
value: function(key, storage, callback) {
if (!callback && typeof storage === 'function') {
callback = storage;
storage = undefined;
}
if (callback) {
callback()
return;
}
return undefined;
}
},
set: {
value: function(key, value, storage, callback) {
if (!callback && typeof storage === 'function') {
callback = storage;
storage = undefined;
}
if (callback) {
callback()
return
}
}
},
keys: {
value: function(storage, callback) {
if (!callback && typeof storage === 'function') {
callback = storage;
storage = undefined;
}
if (callback) {
callback();
return;
}
return undefined;
}
}
@ -436,25 +460,48 @@ function createRootContext() {
return obj;
}
function getContext(localId,flowId,parent) {
var contextId = localId;
/**
* Get a flow-level context object.
* @param {string} flowId [description]
* @param {string} parentFlowId the id of the parent flow. undefined
* @return {object}} the context object
*/
function getFlowContext(flowId,parentFlowId) {
if (contexts.hasOwnProperty(flowId)) {
return contexts[flowId];
}
var parentContext = contexts[parentFlowId];
if (!parentContext) {
parentContext = createRootContext();
contexts[parentFlowId] = parentContext;
// throw new Error("Flow "+flowId+" is missing parent context "+parentFlowId);
}
var newContext = createContext(flowId,undefined,parentContext);
contexts[flowId] = newContext;
return newContext;
}
function getContext(nodeId, flowId) {
var contextId = nodeId;
if (flowId) {
contextId = localId+":"+flowId;
contextId = nodeId+":"+flowId;
}
if (contexts.hasOwnProperty(contextId)) {
return contexts[contextId];
}
var newContext = createContext(contextId,undefined,parent);
var newContext = createContext(contextId);
if (flowId) {
var node = flows.get(flowId);
var parent = undefined;
if (node && node.type.startsWith("subflow:")) {
parent = node.context().flow;
var flowContext = contexts[flowId];
if (!flowContext) {
// This is most likely due to a unit test for a node which doesn't
// initialise the flow properly.
// To keep things working, initialise the missing context.
// This *does not happen* in normal node-red operation
flowContext = createContext(flowId,undefined,createRootContext());
contexts[flowId] = flowContext;
}
else {
parent = createRootContext();
}
var flowContext = getContext(flowId,undefined,parent);
Object.defineProperty(newContext, 'flow', {
value: flowContext
});
@ -466,6 +513,39 @@ function getContext(localId,flowId,parent) {
return newContext;
}
//
// function getContext(localId,flowId,parent) {
// var contextId = localId;
// if (flowId) {
// contextId = localId+":"+flowId;
// }
// console.log("getContext",localId,flowId,"known?",contexts.hasOwnProperty(contextId));
// if (contexts.hasOwnProperty(contextId)) {
// return contexts[contextId];
// }
// var newContext = createContext(contextId,undefined,parent);
// if (flowId) {
// var node = flows.get(flowId);
// console.log("flows,get",flowId,node&&node.type)
// var parent = undefined;
// if (node && node.type.startsWith("subflow:")) {
// parent = node.context().flow;
// }
// else {
// parent = createRootContext();
// }
// var flowContext = getContext(flowId,undefined,parent);
// Object.defineProperty(newContext, 'flow', {
// value: flowContext
// });
// }
// Object.defineProperty(newContext, 'global', {
// value: contexts['global']
// })
// contexts[contextId] = newContext;
// return newContext;
// }
function deleteContext(id,flowId) {
if(!hasConfiguredStore){
// only delete context if there's no configured storage.
@ -517,6 +597,7 @@ module.exports = {
load: load,
listStores: listStores,
get: getContext,
getFlowContext:getFlowContext,
delete: deleteContext,
clean: clean,
close: close

View File

@ -203,10 +203,10 @@ LocalFileSystem.prototype.open = function(){
var newContext = self.cache._export();
scopes.forEach(function(scope) {
var storagePath = getStoragePath(self.storageBaseDir,scope);
var context = newContext[scope];
var context = newContext[scope] || {};
var stringifiedContext = stringify(context);
if (stringifiedContext.circular && !self.knownCircularRefs[scope]) {
log.warn(log._("error-circular",{scope:scope}));
log.warn(log._("context.localfilesystem.error-circular",{scope:scope}));
self.knownCircularRefs[scope] = true;
} else {
delete self.knownCircularRefs[scope];
@ -324,7 +324,7 @@ LocalFileSystem.prototype.set = function(scope, key, value, callback) {
}
var stringifiedContext = stringify(obj);
if (stringifiedContext.circular && !self.knownCircularRefs[scope]) {
log.warn(log._("error-circular",{scope:scope}));
log.warn(log._("context.localfilesystem.error-circular",{scope:scope}));
self.knownCircularRefs[scope] = true;
} else {
delete self.knownCircularRefs[scope];

View File

@ -18,6 +18,7 @@ var clone = require("clone");
var redUtil = require("@node-red/util").util;
var flowUtil = require("./util");
var events = require("../../events");
const context = require('../context');
var Subflow;
var Log;
@ -54,6 +55,8 @@ class Flow {
this.catchNodes = [];
this.statusNodes = [];
this.path = this.id;
// Ensure a context exists for this flow
this.context = context.getFlowContext(this.id,this.parent.id);
}
/**
@ -568,15 +571,18 @@ function stopNode(node,removed) {
Log.trace("Stopping node "+node.type+":"+node.id+(removed?" removed":""));
const start = Date.now();
const closePromise = node.close(removed);
let closeTimer = null;
const closeTimeout = new Promise((resolve,reject) => {
setTimeout(() => {
closeTimer = setTimeout(() => {
reject("Close timed out");
}, nodeCloseTimeout);
});
return Promise.race([closePromise,closeTimeout]).then(() => {
clearTimeout(closeTimer);
var delta = Date.now() - start;
Log.trace("Stopped node "+node.type+":"+node.id+" ("+delta+"ms)" );
}).catch(err => {
clearTimeout(closeTimer);
node.error(Log._("nodes.flows.stopping-error",{message:err}));
Log.debug(err.stack);
})

View File

@ -16,7 +16,7 @@
const clone = require("clone");
const Flow = require('./Flow').Flow;
const context = require('../context');
const util = require("util");
const redUtil = require("@node-red/util").util;
@ -227,6 +227,10 @@ class Subflow extends Flow {
this.node.on("input", function(msg) { this.send(msg);});
this.node.on("close", function() { this.status({}); })
this.node.status = status => this.parent.handleStatus(this.node,status);
// Create a context instance
// console.log("Node.context",this.type,"id:",this._alias||this.id,"z:",this.z)
this._context = context.get(this._alias||this.id,this.z);
this.node._updateWires = this.node.updateWires;
@ -466,7 +470,7 @@ function createNodeInSubflow(subflowInstanceId, def) {
* properties in the nodes object to reference the new node ids.
* This handles:
* - node.wires,
* - node.scope of Catch and Status nodes,
* - node.scope of Complete, Catch and Status nodes,
* - node.XYZ for any property where XYZ is recognised as an old property
* @param {[type]} nodes [description]
* @param {[type]} nodeMap [description]
@ -489,7 +493,7 @@ function remapSubflowNodes(nodes,nodeMap) {
}
}
}
if ((node.type === 'catch' || node.type === 'status') && node.scope) {
if ((node.type === 'complete' || node.type === 'catch' || node.type === 'status') && node.scope) {
node.scope = node.scope.map(function(id) {
return nodeMap[id]?nodeMap[id].id:""
})

View File

@ -553,7 +553,7 @@ function getFlow(id) {
if (flow.label) {
result.label = flow.label;
}
if (flow.disabled) {
if (flow.hasOwnProperty('disabled')) {
result.disabled = flow.disabled;
}
if (flow.hasOwnProperty('info')) {

View File

@ -41,6 +41,9 @@ function runGitCommand(args,cwd,env,emit) {
err.code = "git_connection_failed";
} else if (/Connection timed out/i.test(stderr)) {
err.code = "git_connection_failed";
} else if(/Host key verification failed/i.test(stderr)) {
// TODO: handle host key verification errors separately
err.code = "git_host_key_verification_failed";
} else if (/fatal: could not read/i.test(stderr)) {
// Username/Password
err.code = "git_auth_failed";
@ -48,9 +51,6 @@ function runGitCommand(args,cwd,env,emit) {
err.code = "git_auth_failed";
} else if(/Permission denied \(publickey\)/i.test(stderr)) {
err.code = "git_auth_failed";
} else if(/Host key verification failed/i.test(stderr)) {
// TODO: handle host key verification errors separately
err.code = "git_auth_failed";
} else if (/commit your changes or stash/i.test(stderr)) {
err.code = "git_local_overwrite";
} else if (/CONFLICT/.test(err.stdout)) {

View File

@ -558,15 +558,19 @@ function evaluateNodeProperty(value, type, node, msg, callback) {
*/
function prepareJSONataExpression(value,node) {
var expr = jsonata(value);
expr.assign('flowContext',function(val) {
return node.context().flow.get(val);
expr.assign('flowContext',function(val, store) {
return node.context().flow.get(val, store);
});
expr.assign('globalContext',function(val) {
return node.context().global.get(val);
expr.assign('globalContext',function(val, store) {
return node.context().global.get(val, store);
});
expr.assign('env', function(name) {
var val = getSetting(node, name);
return (val ? val : "");
if (typeof val !== 'undefined') {
return val;
} else {
return ""
}
})
expr.registerFunction('clone', cloneMessage, '<(oa)-:o>');
expr._legacyMode = /(^|[^a-zA-Z0-9_'"])msg([^a-zA-Z0-9_'"]|$)/.test(value);

View File

@ -243,7 +243,7 @@ module.exports = {
// palette. If a node's category is not in the list, the category will get
// added to the end of the palette.
// If not set, the following default order is used:
//paletteCategories: ['subflows','flow','input','output','function','parser','social','mobile','storage','analysis','advanced'],
//paletteCategories: ['subflows', 'common', 'function', 'network', 'sequence', 'parser', 'storage'],
// Configure the logging output
logging: {

View File

@ -24,16 +24,21 @@ var idMap = {
"comment": ".red-ui-palette-node[data-palette-type='comment']",
// function
"function": ".red-ui-palette-node[data-palette-type='function']",
"switch": ".red-ui-palette-node[data-palette-type='switch']",
"change": ".red-ui-palette-node[data-palette-type='change']",
"range": ".red-ui-palette-node[data-palette-type='range']",
"template": ".red-ui-palette-node[data-palette-type='template']",
"delay": ".red-ui-palette-node[data-palette-type='delay']",
"trigger": ".red-ui-palette-node[data-palette-type='trigger']",
"exec": ".red-ui-palette-node[data-palette-type='exec']",
// network
"mqttIn": ".red-ui-palette-node[data-palette-type='mqtt in']",
"mqttOut": ".red-ui-palette-node[data-palette-type='mqtt out']",
"httpIn": ".red-ui-palette-node[data-palette-type='http in']",
"httpResponse": ".red-ui-palette-node[data-palette-type='http response']",
"httpRequest": ".red-ui-palette-node[data-palette-type='http request']",
"websocketIn": ".red-ui-palette-node[data-palette-type='websocket in']",
"websocketOut": ".red-ui-palette-node[data-palette-type='websocket out']",
// sequence
"split": ".red-ui-palette-node[data-palette-type='split']",
"join": ".red-ui-palette-node[data-palette-type='join']",

View File

@ -18,8 +18,6 @@ var util = require("util");
var nodePage = require("../../node_page");
var keyPage = require("../../../util/key_page");
function debugNode(id) {
nodePage.call(this, id);
}

View File

@ -0,0 +1,234 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var util = require('util');
var nodePage = require('../../node_page');
function switchNode(id) {
nodePage.call(this, id);
}
util.inherits(switchNode, nodePage);
var vtType = {
"msg": 1,
"flow": 2,
"global": 3,
"str": 4,
"num": 5,
"jsonata": 6,
"env": 7,
"prev": 8
};
function setT(t, index) {
browser.selectWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/select', t);
}
function setV(v, vt, index) {
if (vt) {
browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/button[1]');
browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + vtType[vt] + ']');
}
if (v) {
browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/div/input', v);
}
}
function setBetweenV(v, vt, v2, v2t, index) {
if (vt) {
browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div[2]/button[1]');
browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + vtType[vt] + ']');
}
if (v) {
browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div[2]/div[1]/input', v);
}
if (v2t) {
browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[3]/div/button[1]');
browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + vtType[v2t] + ']');
}
if (v2) {
browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[3]/div/div[1]/input', v2);
}
}
function setSequenceV(v, vt, index) {
var sType = {
"flow": 1,
"global": 2,
"num": 3,
"jsonata": 4,
"env": 5,
};
if (vt) {
browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div[2]/button[1]');
browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + sType[vt] + ']');
}
if (v) {
browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div[2]/div[1]/input', v);
}
}
switchNode.prototype.ruleEqual = function (v, vt, index) {
index = index || 1;
setT('eq', index);
setV(v, vt, index);
}
switchNode.prototype.ruleNotEqual = function (v, vt, index) {
index = index || 1;
setT('neq', index);
setV(v, vt, index);
}
switchNode.prototype.ruleLessThan = function (v, vt, index) {
index = index || 1;
setT('lt', index);
setV(v, vt, index);
}
switchNode.prototype.ruleLessThanOrEqual = function (v, vt, index) {
index = index || 1;
setT('lte', index);
setV(v, vt, index);
}
switchNode.prototype.ruleGreaterThan = function (v, vt, index) {
index = index || 1;
setT('gt', index);
setV(v, vt, index);
}
switchNode.prototype.ruleGreaterThanOrEqual = function (v, vt, index) {
index = index || 1;
setT('gte', index);
setV(v, vt, index);
}
switchNode.prototype.ruleHasKey = function (v, vt, index) {
index = index || 1;
setT('hask', index);
setV(v, vt, index);
}
switchNode.prototype.ruleIsBetween = function (v, vt, v2, v2t, index) {
index = index || 1;
setT('btwn', index);
setBetweenV(v, vt, v2, v2t, index);
}
switchNode.prototype.ruleContains = function (v, vt, index) {
index = index || 1;
setT('cont', index);
setV(v, vt, index);
}
switchNode.prototype.ruleMatchesRegex = function (v, vt, index) {
index = index || 1;
setT('regex', index);
setV(v, vt, index);
}
switchNode.prototype.ruleIsTrue = function (index) {
index = index || 1;
setT('true', index);
}
switchNode.prototype.ruleIsFalse = function (index) {
index = index || 1;
setT('false', index);
}
switchNode.prototype.ruleIsNull = function (index) {
index = index || 1;
setT('null', index);
}
switchNode.prototype.ruleIsNotNull = function (index) {
index = index || 1;
setT('nnull', index);
}
switchNode.prototype.ruleIsOfType = function (t, index) {
index = index || 1;
setT('istype', index);
var tType = {
"str": 1,
"num": 2,
"boolean": 3,
"array": 4,
"buffer": 5,
"object": 6,
"json": 7,
"undefined": 8,
"null": 9
};
if (t) {
browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div[2]/button[1]');
browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + tType[t] + ']');
}
}
switchNode.prototype.ruleIsEmpty = function (index) {
index = index || 1;
setT('empty', index);
}
switchNode.prototype.ruleIsNotEmpty = function (index) {
index = index || 1;
setT('nempty', index);
}
switchNode.prototype.ruleHead = function (v, vt, index) {
index = index || 1;
setT('head', index);
setSequenceV(v, vt, index);
}
switchNode.prototype.ruleIndexBetween = function (v, vt, v2, v2t, index) {
index = index || 1;
setT('index', index);
setBetweenV(v, vt, v2, v2t, index);
}
switchNode.prototype.ruleTail = function (v, vt, index) {
index = index || 1;
setT('tail', index);
setSequenceV(v, vt, index);
}
switchNode.prototype.ruleJSONataExp = function (v, index) {
index = index || 1;
setT('jsonata_exp', index);
if (v) {
browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div[2]/div[1]/input', v);
}
}
switchNode.prototype.ruleOtherwise = function (index) {
index = index || 1;
setT('else', index);
}
switchNode.prototype.addRule = function () {
browser.clickWithWait('//div[contains(@class, "red-ui-editableList")]/a');
}
module.exports = switchNode;

View File

@ -51,41 +51,82 @@ function setT(t, index) {
// It is better to create a function whose input value is the object type in the future,
changeNode.prototype.ruleSet = function (p, pt, to, tot, index) {
index = index || 1;
setT("set", index);
setT('set', index);
if (pt) {
browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/button[1]');
var num = 5 * (index - 1) + 1;
var ptXPath = '//div[contains(@class, "red-ui-typedInput-options")][' + num + ']/a[' + ptType[pt] + ']';
browser.clickWithWait(ptXPath);
browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + ptType[pt] + ']');
}
if (p) {
browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/div/input', p);
}
if (tot) {
browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[2]/div[2]/button[1]');
var num = 5 * (index - 1) + 2;
var totXPath = '//div[contains(@class, "red-ui-typedInput-options")][' + num + ']/a[' + totType[tot] + ']';
browser.clickWithWait(totXPath);
browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + totType[tot] + ']');
}
if (to) {
browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[2]/div[2]/div/input', to);
}
}
changeNode.prototype.ruleDelete = function (index) {
changeNode.prototype.ruleChange = function (p, pt, from, fromt, to, tot, index) {
index = index || 1;
setT("delete", index);
setT('change', index);
if (pt) {
browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/button[1]');
browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + ptType[pt] + ']');
}
if (p) {
browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/div/input', p);
}
if (fromt) {
browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[3]/div[1]/div[2]/button[1]');
browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + totType[pt] + ']');
}
if (from) {
browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[3]/div[1]/div[2]/div[1]/input', from);
}
if (tot) {
browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[3]/div[2]/div[2]/button[1]');
browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + totType[pt] + ']');
}
if (to) {
browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[3]/div[2]/div[2]/div[1]/input', to);
}
}
changeNode.prototype.ruleMove = function (p, to, index) {
changeNode.prototype.ruleDelete = function (p, pt, index) {
index = index || 1;
setT("move", index);
setT('delete', index);
if (pt) {
browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/button[1]');
browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + ptType[pt] + ']');
}
if (p) {
browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/div/input', p);
}
}
changeNode.prototype.ruleMove = function (p, pt, to, tot, index) {
index = index || 1;
setT('move', index);
if (pt) {
browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/button[1]');
browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + ptType[pt] + ']');
}
if (p) {
browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/div/div/input', p);
}
if (tot) {
browser.clickWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[4]/div[2]/button[1]');
browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + totType[pt] + ']');
}
if (to) {
browser.setValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[4]/div[2]/div/input', to);
}
}
changeNode.prototype.addRule = function () {
browser.clickWithWait('//*[@id="dialog-form"]/div[5]/div/a');
browser.clickWithWait('//div[contains(@class, "red-ui-editableList")]/a');
}
module.exports = changeNode;

View File

@ -18,8 +18,6 @@ var util = require("util");
var nodePage = require("../../node_page");
var keyPage = require("../../../util/key_page");
function delayNode(id) {
nodePage.call(this, id);
}

View File

@ -0,0 +1,83 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var util = require("util");
var nodePage = require("../../node_page");
function triggerNode(id) {
nodePage.call(this, id);
}
util.inherits(triggerNode, nodePage);
triggerNode.prototype.setSend = function (send, sendt) {
var sendType = {
"flow": 1,
"global": 2,
"str": 3,
"num": 4,
"bool": 5,
"json": 6,
"bin": 7,
"date": 8,
"env": 9,
"pay": 10,
"nul": 11
};
if (sendt) {
browser.clickWithWait('//*[@id="dialog-form"]/div[1]/div/button[1]');
browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + sendType[sendt] + ']');
}
if (send) {
browser.setValue('//*[@id="dialog-form"]/div[1]/div/div[1]/input', send);
}
}
triggerNode.prototype.setDuration = function (duration, units) {
browser.setValue('//*[@id="node-input-duration"]', duration);
if (units) {
browser.selectWithWait('//*[@id="node-input-units"]', units);
}
}
triggerNode.prototype.setThenSend = function (thenSend, thenSendt) {
var thenSendType = {
"flow": 1,
"global": 2,
"str": 3,
"num": 4,
"bool": 5,
"json": 6,
"bin": 7,
"date": 8,
"env": 9,
"pay": 10,
"payl": 11,
"nul": 12
};
if (thenSendt) {
browser.clickWithWait('//*[@id="dialog-form"]/div[5]/div/button[1]');
browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + thenSendType[thenSendt] + ']');
}
if (thenSend) {
browser.setValue('//*[@id="dialog-form"]/div[5]/div/div[1]/input', thenSend);
}
}
module.exports = triggerNode;

View File

@ -18,18 +18,20 @@ var util = require("util");
var nodePage = require("../../node_page");
function mqttInNode(id) {
function execNode(id) {
nodePage.call(this, id);
}
util.inherits(mqttInNode, nodePage);
util.inherits(execNode, nodePage);
mqttInNode.prototype.setTopic = function (topic) {
browser.setValue('#node-input-topic', topic);
execNode.prototype.setCommand = function (command) {
browser.setValue('//*[@id="node-input-command"]', command);
}
mqttInNode.prototype.setQoS = function (qos) {
browser.selectWithWait('#node-input-qos', qos);
execNode.prototype.setAppend = function (checkbox) {
if (browser.isSelected('#node-input-addpay') !== checkbox) {
browser.click('#node-input-addpay');
}
}
module.exports = mqttInNode;
module.exports = execNode;

View File

@ -14,27 +14,61 @@
* limitations under the License.
**/
function setServer(broker, port) {
var util = require("util");
var nodePage = require("../../node_page");
var mqttBrokerNode = {};
mqttBrokerNode.setServer = function (broker, port) {
browser.setValue('#node-config-input-broker', broker);
port = port ? port : 1883;
browser.setValue('#node-config-input-port', port);
}
};
function clickOk() {
mqttBrokerNode.clickOk = function () {
browser.clickWithWait('#node-config-dialog-ok');
// Wait until an config dialog closes.
browser.waitForVisible('#node-config-dialog-ok', 10000, true);
}
};
function edit() {
mqttBrokerNode.edit = function () {
browser.waitForVisible('#node-input-lookup-broker');
browser.click('#node-input-lookup-broker');
// Wait until a config dialog opens.
browser.waitForVisible('#node-config-dialog-ok', 10000);
};
function mqttInNode(id) {
nodePage.call(this, id);
}
module.exports = {
setServer: setServer,
clickOk: clickOk,
edit: edit
util.inherits(mqttInNode, nodePage);
mqttInNode.prototype.setTopic = function (topic) {
browser.setValue('#node-input-topic', topic);
};
mqttInNode.prototype.setQoS = function (qos) {
browser.selectWithWait('#node-input-qos', qos);
};
mqttInNode.prototype.mqttBrokerNode = mqttBrokerNode;
module.exports.mqttInNode = mqttInNode;
function mqttOutNode(id) {
nodePage.call(this, id);
}
util.inherits(mqttOutNode, nodePage);
mqttOutNode.prototype.setTopic = function (topic) {
browser.setValue('#node-input-topic', topic);
};
mqttOutNode.prototype.setRetain = function (retain) {
browser.selectWithWait('#node-input-retain', retain);
};
mqttOutNode.prototype.mqttBrokerNode = mqttBrokerNode;
module.exports.mqttOutNode = mqttOutNode;

View File

@ -1,35 +0,0 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var util = require("util");
var nodePage = require("../../node_page");
function mqttOutNode(id) {
nodePage.call(this, id);
}
util.inherits(mqttOutNode, nodePage);
mqttOutNode.prototype.setTopic = function(topic) {
browser.setValue('#node-input-topic', topic);
}
mqttOutNode.prototype.setRetain = function (retain) {
browser.selectWithWait('#node-input-retain', retain);
}
module.exports = mqttOutNode;

View File

@ -0,0 +1,93 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var util = require("util");
var nodePage = require("../../node_page");
var websocketListenerNode = {};
websocketListenerNode.setPath = function (path) {
browser.setValue('#node-config-input-path', path);
};
websocketListenerNode.setSendReceive = function (wholemsg) {
browser.selectWithWait('#node-config-input-wholemsg', wholemsg);
};
websocketListenerNode.clickOk = function () {
browser.clickWithWait('#node-config-dialog-ok');
// Wait until an config dialog closes.
browser.waitForVisible('#node-config-dialog-ok', 10000, true);
};
websocketListenerNode.edit = function () {
browser.waitForVisible('#node-input-lookup-server');
browser.click('#node-input-lookup-server');
// Wait until a config dialog opens.
browser.waitForVisible('#node-config-dialog-ok', 10000);
};
var websocketClientNode = {};
websocketClientNode.setPath = function (path) {
browser.setValue('#node-config-input-path', path);
};
websocketClientNode.setSendReceive = function (wholemsg) {
browser.selectWithWait('#node-config-input-wholemsg', wholemsg);
};
websocketClientNode.clickOk = function () {
browser.clickWithWait('#node-config-dialog-ok');
// Wait until an config dialog closes.
browser.waitForVisible('#node-config-dialog-ok', 10000, true);
};
websocketClientNode.edit = function () {
browser.waitForVisible('#node-input-lookup-client');
browser.click('#node-input-lookup-client');
// Wait until a config dialog opens.
browser.waitForVisible('#node-config-dialog-ok', 10000);
};
function websocketInNode(id) {
nodePage.call(this, id);
}
util.inherits(websocketInNode, nodePage);
websocketInNode.prototype.setType = function (type) {
browser.selectWithWait('#node-input-mode', type);
};
websocketInNode.prototype.websocketListenerNode = websocketListenerNode;
websocketInNode.prototype.websocketClientNode = websocketClientNode;
module.exports.websocketInNode = websocketInNode;
function websocketOutNode(id) {
nodePage.call(this, id);
}
util.inherits(websocketOutNode, nodePage);
websocketOutNode.prototype.setType = function (type) {
browser.selectWithWait('#node-input-mode', type);
};
websocketOutNode.prototype.websocketListenerNode = websocketListenerNode;
websocketOutNode.prototype.websocketClientNode = websocketClientNode;
module.exports.websocketOutNode = websocketOutNode;

View File

@ -29,7 +29,7 @@ jsonNode.prototype.setAction = function (action) {
}
jsonNode.prototype.setProperty = function (property) {
browser.setValue('//*[@id="dialog-form"]/div[4]/div/div[1]/input', property);
browser.setValue('//*[contains(@class, "red-ui-typedInput-container")]/div[1]/input', property);
}
module.exports = jsonNode;

View File

@ -29,7 +29,7 @@ xmlNode.prototype.setAction = function (action) {
}
xmlNode.prototype.setProperty = function (property) {
browser.setValue('//*[@id="dialog-form"]/div[3]/div/div[1]/input', property);
browser.setValue('//*[contains(@class, "red-ui-typedInput-container")]/div[1]/input', property);
}
module.exports = xmlNode;

View File

@ -29,7 +29,7 @@ yamlNode.prototype.setAction = function (action) {
}
yamlNode.prototype.setProperty = function (property) {
browser.setValue('//*[@id="dialog-form"]/div[3]/div/div[1]/input', property);
browser.setValue('//*[contains(@class, "red-ui-typedInput-container")]/div[1]/input', property);
}
module.exports = yamlNode;

View File

@ -24,7 +24,19 @@ function splitNode(id) {
util.inherits(splitNode, nodePage);
module.exports = splitNode;
splitNode.prototype.setSplitUsing = function (splt, spltt) {
var spltType = {
"str": 1,
"bin": 2,
"len": 3
};
browser.clickWithWait('//*[@id="dialog-form"]/div[3]/div/button[1]');
browser.clickWithWait('//div[contains(@class, "red-ui-typedInput-options") and not(contains(@style, "display: none"))]/a[' + spltType[spltt] + ']');
browser.setValue('//*[@id="dialog-form"]/div[3]/div/div[1]/input', splt);
};
module.exports.splitNode = splitNode;
function joinNode(id) {
nodePage.call(this, id);
@ -32,4 +44,4 @@ function joinNode(id) {
util.inherits(joinNode, nodePage);
module.exports = joinNode;
module.exports.joinNode = joinNode;

View File

@ -35,10 +35,11 @@ Node.prototype.clickOk = function () {
browser.pause(50);
}
Node.prototype.connect = function (targetNode) {
var outputPort = this.id + '/*[@class="red-ui-flow-port-output"]';
Node.prototype.connect = function (targetNode, port) {
port = port || 1;
var outputPort = this.id + '/*[@class="red-ui-flow-port-output"][' + port + ']';
var inputPort = targetNode.id + '/*[@class="red-ui-flow-port-input"]';
browser.dragAndDrop(outputPort, inputPort)
browser.dragAndDrop(outputPort, inputPort);
}
Node.prototype.clickLeftButton = function () {

View File

@ -21,17 +21,22 @@ var catchNode = require('./core/common/25-catch_page');
var statusNode = require('./core/common/25-status_page');
var commentNode = require('./core/common/90-comment_page');
var functionNode = require('./core/function/10-function_page');
var switchNode = require('./core/function/10-switch_page');
var changeNode = require('./core/function/15-change_page');
var rangeNode = require('./core/function/16-range_page');
var templateNode = require('./core/function/80-template_page');
var delayNode = require('./core/function/89-delay_page');
var mqttInNode = require('./core/network/10-mqttin_page');
var mqttOutNode = require('./core/network/10-mqttout_page');
var triggerNode = require('./core/function/89-trigger_page');
var execNode = require('./core/function/90-exec_page');
var mqttInNode = require('./core/network/10-mqtt_page').mqttInNode;
var mqttOutNode = require('./core/network/10-mqtt_page').mqttOutNode;
var httpInNode = require('./core/network/21-httpin_page');
var httpResponseNode = require('./core/network/21-httpresponse_page');
var httpRequestNode = require('./core/network/21-httprequest_page');
var splitNode = require('./core/sequence/17-split_page');
var joinNode = require('./core/sequence/17-split_page');
var websocketInNode = require('./core/network/22-websocket_page').websocketInNode;
var websocketOutNode = require('./core/network/22-websocket_page').websocketOutNode;
var splitNode = require('./core/sequence/17-split_page').splitNode;
var joinNode = require('./core/sequence/17-split_page').joinNode;
var batchNode = require('./core/sequence/19-batch_page');
var csvNode = require('./core/parsers/70-CSV_page');
var htmlNode = require('./core/parsers/70-HTML_page');
@ -50,16 +55,21 @@ var nodeCatalog = {
"comment": commentNode,
// function
"function": functionNode,
"switch": switchNode,
"change": changeNode,
"range": rangeNode,
"template": templateNode,
"delay": delayNode,
"trigger": triggerNode,
"exec": execNode,
// network
"mqttIn": mqttInNode,
"mqttOut": mqttOutNode,
"httpIn": httpInNode,
"httpResponse": httpResponseNode,
"httpRequest": httpRequestNode,
"websocketIn": websocketInNode,
"websocketOut": websocketOutNode,
// sequence
"split": splitNode,
"join": joinNode,

View File

@ -70,7 +70,7 @@ function init() {
var ret = repeatUntilSuccess(function(args) {
return browser.selectByValue(args[0], args[1]);
}, [selector, value]);
}, [selector, value.toString()]);
return ret;
} catch (e) {
console.trace();

View File

@ -88,7 +88,7 @@ describe('cookbook', function () {
injectNode.clickOk();
changeNode.edit();
changeNode.ruleMove('topic', 'payload');
changeNode.ruleMove('topic', 'msg', 'payload', 'msg');
changeNode.clickOk();
injectNode.connect(changeNode);

View File

@ -22,7 +22,6 @@ var helper = require("../../editor_helper");
var debugTab = require('../../pageobjects/editor/debugTab_page');
var workspace = require('../../pageobjects/editor/workspace_page');
var specUtil = require('../../pageobjects/util/spec_util_page');
var mqttConfig = require('../../pageobjects/nodes/core/network/10-mqttconfig_page.js');
var httpNodeRoot = "/api";
@ -73,9 +72,9 @@ describe('cookbook', function () {
var mqttOutNode = workspace.addNode("mqttOut");
mqttOutNode.edit();
mqttConfig.edit();
mqttConfig.setServer("localhost", moscaSettings.port);
mqttConfig.clickOk();
mqttOutNode.mqttBrokerNode.edit();
mqttOutNode.mqttBrokerNode.setServer("localhost", moscaSettings.port);
mqttOutNode.mqttBrokerNode.clickOk();
mqttOutNode.clickOk();
workspace.deploy();

View File

@ -32,17 +32,20 @@ describe('context', function() {
return Context.close();
});
it('stores local property',function() {
var flowContext = Context.getFlowContext("flowA")
var context1 = Context.get("1","flowA");
should.not.exist(context1.get("foo"));
context1.set("foo","test");
context1.get("foo").should.equal("test");
});
it('stores local property - creates parent properties',function() {
var flowContext = Context.getFlowContext("flowA")
var context1 = Context.get("1","flowA");
context1.set("foo.bar","test");
context1.get("foo").should.eql({bar:"test"});
});
it('deletes local property',function() {
var flowContext = Context.getFlowContext("flowA")
var context1 = Context.get("1","flowA");
context1.set("foo.abc.bar1","test1");
context1.set("foo.abc.bar2","test2");
@ -55,12 +58,14 @@ describe('context', function() {
should.not.exist(context1.get("foo"));
});
it('stores flow property',function() {
var flowContext = Context.getFlowContext("flowA")
var context1 = Context.get("1","flowA");
should.not.exist(context1.flow.get("foo"));
context1.flow.set("foo","test");
context1.flow.get("foo").should.equal("test");
});
it('stores global property',function() {
var flowContext = Context.getFlowContext("flowA")
var context1 = Context.get("1","flowA");
should.not.exist(context1.global.get("foo"));
context1.global.set("foo","test");
@ -68,6 +73,7 @@ describe('context', function() {
});
it('keeps local context local', function() {
var flowContext = Context.getFlowContext("flowA")
var context1 = Context.get("1","flowA");
var context2 = Context.get("2","flowA");
@ -79,6 +85,7 @@ describe('context', function() {
should.not.exist(context2.get("foo"));
});
it('flow context accessible to all flow nodes', function() {
var flowContext = Context.getFlowContext("flowA")
var context1 = Context.get("1","flowA");
var context2 = Context.get("2","flowA");
@ -91,6 +98,8 @@ describe('context', function() {
});
it('flow context not shared to nodes on other flows', function() {
var flowContextA = Context.getFlowContext("flowA")
var flowContextB = Context.getFlowContext("flowB")
var context1 = Context.get("1","flowA");
var context2 = Context.get("2","flowB");
@ -103,6 +112,9 @@ describe('context', function() {
});
it('global context shared to all nodes', function() {
var flowContextA = Context.getFlowContext("flowA")
var flowContextB = Context.getFlowContext("flowB")
var context1 = Context.get("1","flowA");
var context2 = Context.get("2","flowB");
@ -115,6 +127,7 @@ describe('context', function() {
});
it('context.flow/global are not enumerable', function() {
var flowContextA = Context.getFlowContext("flowA")
var context1 = Context.get("1","flowA");
Object.keys(context1).length.should.equal(0);
Object.keys(context1.flow).length.should.equal(0);
@ -122,6 +135,7 @@ describe('context', function() {
})
it('context.flow/global cannot be deleted', function() {
var flowContextA = Context.getFlowContext("flowA")
var context1 = Context.get("1","flowA");
delete context1.flow;
should.exist(context1.flow);
@ -130,6 +144,7 @@ describe('context', function() {
})
it('deletes context',function() {
var flowContextA = Context.getFlowContext("flowA")
var context = Context.get("1","flowA");
should.not.exist(context.get("foo"));
context.set("foo","abc");
@ -142,6 +157,7 @@ describe('context', function() {
});
it('enumerates context keys - sync', function() {
var flowContextA = Context.getFlowContext("flowA")
var context = Context.get("1","flowA");
var keys = context.keys();
@ -160,6 +176,7 @@ describe('context', function() {
});
it('enumerates context keys - async', function(done) {
var flowContextA = Context.getFlowContext("flowA")
var context = Context.get("1","flowA");
var keys = context.keys(function(err,keys) {
@ -183,6 +200,7 @@ describe('context', function() {
it('should enumerate only context keys when GlobalContext was given - sync', function() {
Context.init({functionGlobalContext: {foo:"bar"}});
Context.load().then(function(){
var flowContextA = Context.getFlowContext("flowA")
var context = Context.get("1","flowA");
context.global.set("foo2","bar2");
var keys = context.global.keys();
@ -195,6 +213,7 @@ describe('context', function() {
it('should enumerate only context keys when GlobalContext was given - async', function(done) {
Context.init({functionGlobalContext: {foo:"bar"}});
Context.load().then(function(){
var flowContextA = Context.getFlowContext("flowA")
var context = Context.get("1","flowA");
context.global.set("foo2","bar2");
context.global.keys(function(err,keys) {
@ -210,6 +229,7 @@ describe('context', function() {
it('returns functionGlobalContext value if store value undefined', function() {
Context.init({functionGlobalContext: {foo:"bar"}});
return Context.load().then(function(){
var flowContextA = Context.getFlowContext("flowA")
var context = Context.get("1","flowA");
var v = context.global.get('foo');
v.should.equal('bar');
@ -219,6 +239,7 @@ describe('context', function() {
it('returns functionGlobalContext sub-value if store value undefined', function() {
Context.init({functionGlobalContext: {foo:{bar:123}}});
return Context.load().then(function(){
var flowContextA = Context.getFlowContext("flowA")
var context = Context.get("1","flowA");
var v = context.global.get('foo.bar');
should.equal(v,123);
@ -227,40 +248,67 @@ describe('context', function() {
describe("$parent", function() {
it('should get undefined for $parent without key', function() {
var flowContextA = Context.getFlowContext("flowA")
var flowContextB = Context.getFlowContext("flowB","flowA")
var context0 = Context.get("0","flowA");
var context1 = Context.get("1","flowB", context0);
var context1 = Context.get("1","flowB");
var parent = context1.get("$parent");
should.equal(parent, undefined);
});
it('should get undefined for $parent of root', function() {
var flowContextA = Context.getFlowContext("flowA")
var flowContextB = Context.getFlowContext("flowB","flowA")
var context0 = Context.get("0","flowA");
var context1 = Context.get("1","flowB", context0);
var parent = context1.get("$parent.$parent.K");
var context1 = Context.get("1","flowB");
var parent = context1.flow.get("$parent.$parent.K");
should.equal(parent, undefined);
});
it('should get value in $parent', function() {
it('should get undefined for $parent of root - callback', function(done) {
var flowContextA = Context.getFlowContext("flowA")
var flowContextB = Context.getFlowContext("flowB","flowA")
var context0 = Context.get("0","flowA");
var context1 = Context.get("1","flowB", context0);
context0.set("K", "v");
var v = context1.get("$parent.K");
var context1 = Context.get("1","flowB");
context1.flow.get("$parent.$parent.K", function(err, result) {
try {
should.equal(err, undefined);
should.equal(result, undefined);
done();
} catch(err) {
done(err);
}
});
});
it('should get value in $parent', function() {
var flowContextA = Context.getFlowContext("flowA")
var flowContextB = Context.getFlowContext("flowB","flowA")
var context0 = Context.get("0","flowA");
var context1 = Context.get("1","flowB");
flowContextA.set("K", "v");
var v = context1.flow.get("$parent.K");
should.equal(v, "v");
});
it('should set value in $parent', function() {
var flowContextA = Context.getFlowContext("flowA")
var flowContextB = Context.getFlowContext("flowB","flowA")
var context0 = Context.get("0","flowA");
var context1 = Context.get("1","flowB", context0);
context1.set("$parent.K", "v");
var v = context0.get("K");
var context1 = Context.get("1","flowB");
context1.flow.set("$parent.K", "v");
var v = flowContextA.get("K");
should.equal(v, "v");
});
it('should not contain $parent in keys', function() {
var flowContextA = Context.getFlowContext("flowA")
var flowContextB = Context.getFlowContext("flowB","flowA")
var context0 = Context.get("0","flowA");
var context1 = Context.get("1","flowB", context0);
var context1 = Context.get("1","flowB");
var parent = context1.get("$parent");
context0.set("K0", "v0");
flowContextA.set("K0", "v0");
context1.set("K1", "v1");
var keys = context1.keys();
keys.should.have.length(1);
@ -366,6 +414,7 @@ describe('context', function() {
it('should ignore reserved storage name `_`', function(done) {
Context.init({contextStorage:{_:{module:testPlugin}}});
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow")
var context = Context.get("1","flow");
var cb = function(){}
context.set("foo","bar","_",cb);
@ -452,6 +501,7 @@ describe('context', function() {
Context.init({contextStorage:contextStorage});
var cb = function(){done("An error occurred")}
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
context.set("foo","bar","test",cb);
context.get("foo","test",cb);
@ -465,6 +515,7 @@ describe('context', function() {
it('should store flow property to external context storage',function(done) {
Context.init({contextStorage:contextStorage});
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
var cb = function(){done("An error occurred")}
context.flow.set("foo","bar","test",cb);
@ -479,6 +530,7 @@ describe('context', function() {
it('should store global property to external context storage',function(done) {
Context.init({contextStorage:contextStorage});
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
var cb = function(){done("An error occurred")}
context.global.set("foo","bar","test",cb);
@ -493,6 +545,7 @@ describe('context', function() {
it('should store data to the default context when non-existent context storage was specified', function(done) {
Context.init({contextStorage:contextDefaultStorage});
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
var cb = function(){done("An error occurred")}
context.set("foo","bar","nonexist",cb);
@ -510,6 +563,7 @@ describe('context', function() {
it('should use the default context', function(done) {
Context.init({contextStorage:contextDefaultStorage});
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
var cb = function(){done("An error occurred")}
context.set("foo","bar","default",cb);
@ -527,6 +581,7 @@ describe('context', function() {
it('should use the alias of default context', function(done) {
Context.init({contextStorage:contextDefaultStorage});
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
var cb = function(){done("An error occurred")}
context.set("foo","alias",cb);
@ -545,6 +600,7 @@ describe('context', function() {
it('should allow the store name to be provide in the key', function(done) {
Context.init({contextStorage:contextDefaultStorage});
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
var cb = function(){done("An error occurred")}
context.set("#:(test)::foo","bar");
@ -561,6 +617,7 @@ describe('context', function() {
it('should use default as the alias of other context', function(done) {
Context.init({contextStorage:contextAlias});
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
var cb = function(){done("An error occurred")}
context.set("foo","alias",cb);
@ -575,6 +632,7 @@ describe('context', function() {
it('should not throw an error using undefined storage for local context', function(done) {
Context.init({contextStorage:contextStorage});
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
var cb = function(){done("An error occurred")}
context.get("local","nonexist",cb);
@ -584,6 +642,7 @@ describe('context', function() {
it('should throw an error using undefined storage for flow context', function(done) {
Context.init({contextStorage:contextStorage});
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
var cb = function(){done("An error occurred")}
context.flow.get("flow","nonexist",cb);
@ -595,6 +654,7 @@ describe('context', function() {
var fGC = { "foo": 456 };
Context.init({contextStorage:memoryStorage, functionGlobalContext:fGC });
Context.load().then(function() {
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
// Get foo - should be value from fGC
var v = context.global.get("foo");
@ -615,6 +675,7 @@ describe('context', function() {
var fGC = { "foo": 456 };
Context.init({contextStorage:memoryStorage, functionGlobalContext:fGC });
Context.load().then(function() {
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
// Get foo - should be value from fGC
context.global.get("foo", function(err, v) {
@ -647,6 +708,7 @@ describe('context', function() {
it('should return multiple values if key is an array', function(done) {
Context.init({contextStorage:memoryStorage});
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
context.set("foo1","bar1","memory");
context.set("foo2","bar2","memory");
@ -667,6 +729,7 @@ describe('context', function() {
var fGC = { "foo1": 456, "foo2": {"bar":789} };
Context.init({contextStorage:memoryStorage, functionGlobalContext:fGC });
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
context.global.get(["foo1","foo2.bar","foo3"], "memory", function(err,foo1,foo2,foo3){
if (err) {
@ -685,6 +748,7 @@ describe('context', function() {
Context.init({contextStorage:contextStorage});
stubGet.onFirstCall().callsArgWith(2, "error2", "bar1");
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow")
var context = Context.get("1","flow");
context.global.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){
if (err === "error2") {
@ -702,6 +766,7 @@ describe('context', function() {
stubGet.onSecondCall().callsArgWith(2, null, "bar2");
stubGet.onThirdCall().callsArgWith(2, "error3");
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
context.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){
if (err === "error1") {
@ -716,6 +781,7 @@ describe('context', function() {
it('should store multiple properties if key and value are arrays', function(done) {
Context.init({contextStorage:memoryStorage});
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
context.set(["foo1","foo2","foo3"], ["bar1","bar2","bar3"], "memory", function(err){
if (err) {
@ -739,6 +805,7 @@ describe('context', function() {
it('should deletes multiple properties', function(done) {
Context.init({contextStorage:memoryStorage});
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
context.set(["foo1","foo2","foo3"], ["bar1","bar2","bar3"], "memory", function(err){
if (err) {
@ -777,6 +844,7 @@ describe('context', function() {
it('should use null for missing values if the value array is shorter than the key array', function(done) {
Context.init({contextStorage:memoryStorage});
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
context.set(["foo1","foo2","foo3"], ["bar1","bar2"], "memory", function(err){
if (err) {
@ -804,6 +872,7 @@ describe('context', function() {
it('should use null for missing values if the value is not array', function(done) {
Context.init({contextStorage:memoryStorage});
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
context.set(["foo1","foo2","foo3"], "bar1", "memory", function(err){
if (err) {
@ -831,6 +900,7 @@ describe('context', function() {
it('should ignore the extra values if the value array is longer than the key array', function(done) {
Context.init({contextStorage:memoryStorage});
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
context.set(["foo1","foo2","foo3"], ["bar1","bar2","bar3","ignored"], "memory", function(err){
if (err) {
@ -859,6 +929,7 @@ describe('context', function() {
Context.init({contextStorage:contextStorage});
stubSet.onFirstCall().callsArgWith(3, "error2");
Context.load().then(function(){
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1","flow");
context.set(["foo1","foo2","foo3"], ["bar1","bar2","bar3"], "memory", function(err){
if (err === "error2") {
@ -873,6 +944,7 @@ describe('context', function() {
it('should throw an error if callback of context.get is not a function', function (done) {
Context.init({ contextStorage: memoryStorage });
Context.load().then(function () {
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1", "flow");
context.get("foo", "memory", "callback");
done("should throw an error.");
@ -884,6 +956,7 @@ describe('context', function() {
it('should not throw an error if callback of context.get is not specified', function (done) {
Context.init({ contextStorage: memoryStorage });
Context.load().then(function () {
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1", "flow");
context.get("foo", "memory");
done();
@ -893,6 +966,7 @@ describe('context', function() {
it('should throw an error if callback of context.set is not a function', function (done) {
Context.init({ contextStorage: memoryStorage });
Context.load().then(function () {
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1", "flow");
context.set("foo", "bar", "memory", "callback");
done("should throw an error.");
@ -904,6 +978,7 @@ describe('context', function() {
it('should not throw an error if callback of context.set is not specified', function (done) {
Context.init({ contextStorage: memoryStorage });
Context.load().then(function () {
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1", "flow");
context.set("foo", "bar", "memory");
done();
@ -913,6 +988,7 @@ describe('context', function() {
it('should throw an error if callback of context.keys is not a function', function (done) {
Context.init({ contextStorage: memoryStorage });
Context.load().then(function () {
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1", "flow");
context.keys("memory", "callback");
done("should throw an error.");
@ -924,6 +1000,7 @@ describe('context', function() {
it('should not throw an error if callback of context.keys is not specified', function (done) {
Context.init({ contextStorage: memoryStorage });
Context.load().then(function () {
var flowContext = Context.getFlowContext("flow");
var context = Context.get("1", "flow");
context.keys("memory");
done();
@ -953,7 +1030,6 @@ describe('context', function() {
}).catch(done);
});
});
describe('delete context',function(){
it('should not call delete() when external context storage is used', function(done) {
Context.init({contextStorage:contextDefaultStorage});