mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Compare commits
25 Commits
protect-ho
...
3957-http-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20a63a3292 | ||
|
|
3a5cdbc8ae | ||
|
|
3ed5969e87 | ||
|
|
14c362d4ba | ||
|
|
fce43b4e1d | ||
|
|
1d547500e8 | ||
|
|
946def022f | ||
|
|
c62a101635 | ||
|
|
32999ffa84 | ||
|
|
f06c53f1f1 | ||
|
|
a9eec28360 | ||
|
|
5cda972872 | ||
|
|
087946876b | ||
|
|
318f0f1b7e | ||
|
|
87e7f3a61c | ||
|
|
e724f216bf | ||
|
|
b0abba15a6 | ||
|
|
81b4874a7c | ||
|
|
f11b9c1e18 | ||
|
|
e15ecc00ce | ||
|
|
3e4c45ac6a | ||
|
|
f872e2ab80 | ||
|
|
a81b1aa0cb | ||
|
|
efc0f1ab91 | ||
|
|
9fd4989142 |
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
@@ -5,6 +5,9 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
generate:
|
||||
name: 'Update node-red-docker image'
|
||||
|
||||
6
.github/workflows/tests.yml
vendored
6
.github/workflows/tests.yml
vendored
@@ -6,8 +6,14 @@ on:
|
||||
pull_request:
|
||||
branches: [ master, dev ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
permissions:
|
||||
checks: write # for coverallsapp/github-action to create new checks
|
||||
contents: read # for actions/checkout to fetch code
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
@@ -146,7 +146,7 @@
|
||||
{ value: "reset", source: ["delay","trigger","join","rbe"] },
|
||||
{ value: "responseCookies", source: ["http request"] },
|
||||
{ value: "responseTopic", source: ["mqtt"] },
|
||||
{ value: "responseURL", source: ["http request"] },
|
||||
{ value: "responseUrl", source: ["http request"] },
|
||||
{ value: "restartTimeout", source: ["join"] },
|
||||
{ value: "retain", source: ["mqtt"] },
|
||||
{ value: "schema", source: ["json"] },
|
||||
|
||||
@@ -318,7 +318,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
var r = node.rules[currentRule];
|
||||
if (r.t === "move") {
|
||||
if ((r.tot !== r.pt) || (r.p.indexOf(r.to) !== -1)) {
|
||||
if ((r.tot !== r.pt) || (r.p.indexOf(r.to) !== -1) && (r.p !== r.to)) {
|
||||
applyRule(msg,{t:"set", p:r.to, pt:r.tot, to:r.p, tot:r.pt},(err,msg) => {
|
||||
applyRule(msg,{t:"delete", p:r.p, pt:r.pt}, (err,msg) => {
|
||||
completeApplyingRules(msg,currentRule,done);
|
||||
|
||||
@@ -421,7 +421,11 @@
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
|
||||
var typedInputNoneOpt = { value: 'none', label: '', hasValue: false };
|
||||
var typedInputNoneOpt = {
|
||||
value: 'none',
|
||||
label: RED._("node-red:mqtt.label.none"),
|
||||
hasValue: false
|
||||
};
|
||||
var makeTypedInputOpt = function(value){
|
||||
return {
|
||||
value: value,
|
||||
@@ -436,7 +440,11 @@
|
||||
makeTypedInputOpt("text/csv"),
|
||||
makeTypedInputOpt("text/html"),
|
||||
makeTypedInputOpt("text/plain"),
|
||||
{value:"other", label:""}
|
||||
{
|
||||
value: "other",
|
||||
label: RED._("node-red:mqtt.label.other"),
|
||||
icon: "red/images/typedInput/az.svg"
|
||||
}
|
||||
];
|
||||
|
||||
function getDefaultContentType(value) {
|
||||
@@ -499,17 +507,17 @@
|
||||
cleansession: {value: true},
|
||||
birthTopic: {value:"", validate:validateMQTTPublishTopic},
|
||||
birthQos: {value:"0"},
|
||||
birthRetain: {value:false},
|
||||
birthRetain: {value:"false"},
|
||||
birthPayload: {value:""},
|
||||
birthMsg: { value: {}},
|
||||
closeTopic: {value:"", validate:validateMQTTPublishTopic},
|
||||
closeQos: {value:"0"},
|
||||
closeRetain: {value:false},
|
||||
closeRetain: {value:"false"},
|
||||
closePayload: {value:""},
|
||||
closeMsg: { value: {}},
|
||||
willTopic: {value:"", validate:validateMQTTPublishTopic},
|
||||
willQos: {value:"0"},
|
||||
willRetain: {value:false},
|
||||
willRetain: {value:"false"},
|
||||
willPayload: {value:""},
|
||||
willMsg: { value: {}},
|
||||
userProps: { value: ""},
|
||||
|
||||
@@ -459,7 +459,6 @@ module.exports = function(RED) {
|
||||
if(!opts || typeof opts !== "object") {
|
||||
return; //nothing to change, simply return
|
||||
}
|
||||
const originalBrokerURL = node.brokerurl;
|
||||
|
||||
//apply property changes (only if the property exists in the opts object)
|
||||
setIfHasProperty(opts, node, "url", init);
|
||||
@@ -468,7 +467,6 @@ module.exports = function(RED) {
|
||||
setIfHasProperty(opts, node, "clientid", init);
|
||||
setIfHasProperty(opts, node, "autoConnect", init);
|
||||
setIfHasProperty(opts, node, "usetls", init);
|
||||
setIfHasProperty(opts, node, "usews", init);
|
||||
setIfHasProperty(opts, node, "verifyservercert", init);
|
||||
setIfHasProperty(opts, node, "compatmode", init);
|
||||
setIfHasProperty(opts, node, "protocolVersion", init);
|
||||
@@ -571,9 +569,6 @@ module.exports = function(RED) {
|
||||
if (typeof node.usetls === 'undefined') {
|
||||
node.usetls = false;
|
||||
}
|
||||
if (typeof node.usews === 'undefined') {
|
||||
node.usews = false;
|
||||
}
|
||||
if (typeof node.verifyservercert === 'undefined') {
|
||||
node.verifyservercert = false;
|
||||
}
|
||||
@@ -1000,7 +995,7 @@ module.exports = function(RED) {
|
||||
node.client.publish(msg.topic, msg.payload, options, function (err) {
|
||||
if (done) {
|
||||
done(err)
|
||||
} else {
|
||||
} else if(err) {
|
||||
node.error(err, msg)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -33,11 +33,12 @@
|
||||
</div>
|
||||
|
||||
<div class="form-row node-input-paytoqs-row">
|
||||
<label for="node-input-paytoqs"><span data-i18n="common.label.payload"></span></label>
|
||||
<label for="node-input-paytoqs"><span data-i18n="httpin.label.paytoqs.label"></span></label>
|
||||
<select id="node-input-paytoqs" style="width: 70%;">
|
||||
<option value="ignore" data-i18n="httpin.label.paytoqs.ignore"></option>
|
||||
<option value="query" data-i18n="httpin.label.paytoqs.query"></option>
|
||||
<option value="body" data-i18n="httpin.label.paytoqs.body"></option>
|
||||
<option value="setby" data-i18n="httpin.label.paytoqs.setby"></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -290,7 +291,7 @@
|
||||
RED.tray.resize();
|
||||
});
|
||||
$("#node-input-method").on("change", function() {
|
||||
if ($(this).val() == "GET") {
|
||||
if ($(this).val() == "GET" || $(this).val() == "use") {
|
||||
$(".node-input-paytoqs-row").show();
|
||||
} else {
|
||||
$(".node-input-paytoqs-row").hide();
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
module.exports = function(RED) {
|
||||
"use strict";
|
||||
const got = require("got");
|
||||
const got = require("got").default;
|
||||
const {CookieJar} = require("tough-cookie");
|
||||
const { HttpProxyAgent, HttpsProxyAgent } = require('hpagent');
|
||||
const FormData = require('form-data');
|
||||
@@ -69,8 +69,6 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
||||
var nodeUrl = n.url;
|
||||
var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1;
|
||||
var nodeMethod = n.method || "GET";
|
||||
var paytoqs = false;
|
||||
var paytobody = false;
|
||||
var redirectList = [];
|
||||
var sendErrorsToCatch = n.senderr;
|
||||
node.headers = n.headers || [];
|
||||
@@ -78,15 +76,12 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
||||
if (n.tls) {
|
||||
var tlsNode = RED.nodes.getNode(n.tls);
|
||||
}
|
||||
this.ret = n.ret || "txt";
|
||||
this.authType = n.authType || "basic";
|
||||
if (RED.settings.httpRequestTimeout) { this.reqTimeout = parseInt(RED.settings.httpRequestTimeout) || 120000; }
|
||||
else { this.reqTimeout = 120000; }
|
||||
|
||||
if (n.paytoqs === true || n.paytoqs === "query") { paytoqs = true; }
|
||||
else if (n.paytoqs === "body") { paytobody = true; }
|
||||
|
||||
node.ret = n.ret || "txt";
|
||||
node.authType = n.authType || "basic";
|
||||
if (RED.settings.httpRequestTimeout) { node.reqTimeout = parseInt(RED.settings.httpRequestTimeout) || 120000; }
|
||||
else { node.reqTimeout = 120000; }
|
||||
node.insecureHTTPParser = n.insecureHTTPParser
|
||||
node.paytoqs = n.paytoqs
|
||||
|
||||
var prox, noprox;
|
||||
if (process.env.http_proxy) { prox = process.env.http_proxy; }
|
||||
@@ -196,20 +191,26 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
||||
}
|
||||
}
|
||||
|
||||
var method = nodeMethod.toUpperCase() || "GET";
|
||||
let method = nodeMethod.toUpperCase() || "GET";
|
||||
if (msg.method && n.method && (n.method !== "use")) { // warn if override option not set
|
||||
node.warn(RED._("common.errors.nooverride"));
|
||||
}
|
||||
if (msg.method && n.method && (n.method === "use")) {
|
||||
method = msg.method.toUpperCase(); // use the msg parameter
|
||||
method = msg.method.toUpperCase(); // use the msg parameter
|
||||
}
|
||||
|
||||
/** @type {boolean|'query'|'body'|'setby'} */
|
||||
let payloadHandling = node.paytoqs
|
||||
if (msg.payloadHandling && payloadHandling && (payloadHandling !== "setby")) { // warn if override option not set
|
||||
node.warn(RED._("common.errors.nooverride"));
|
||||
}
|
||||
if (msg.payloadHandling && payloadHandling && (payloadHandling === "setby")) {
|
||||
payloadHandling = msg.payloadHandling // use the msg parameter
|
||||
}
|
||||
if (payloadHandling === true) { payloadHandling = "query" }
|
||||
|
||||
// var isHttps = (/^https/i.test(url));
|
||||
|
||||
/** @type {import('got').Options} */
|
||||
var opts = {};
|
||||
// set defaultport, else when using HttpsProxyAgent, it's defaultPort of 443 will be used :(.
|
||||
// Had to remove this to get http->https redirect to work
|
||||
// opts.defaultPort = isHttps?443:80;
|
||||
opts.timeout = node.reqTimeout;
|
||||
opts.throwHttpErrors = false;
|
||||
// TODO: add UI option to auto decompress. Setting to false for 1.x compatibility
|
||||
@@ -472,7 +473,7 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
||||
}
|
||||
|
||||
|
||||
if (method == 'GET' && typeof msg.payload !== "undefined" && paytoqs) {
|
||||
if (method == "GET" && typeof msg.payload !== "undefined" && payloadHandling === "query") {
|
||||
if (typeof msg.payload === "object") {
|
||||
try {
|
||||
if (url.indexOf("?") !== -1) {
|
||||
@@ -481,18 +482,16 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
||||
url += "?" + querystring.stringify(msg.payload);
|
||||
}
|
||||
} catch(err) {
|
||||
|
||||
node.error(RED._("httpin.errors.invalid-payload"),msg);
|
||||
nodeDone();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
||||
node.error(RED._("httpin.errors.invalid-payload"),msg);
|
||||
nodeDone();
|
||||
return;
|
||||
}
|
||||
} else if ( method == "GET" && typeof msg.payload !== "undefined" && paytobody) {
|
||||
} else if ( method == "GET" && typeof msg.payload !== "undefined" && payloadHandling === "body") {
|
||||
opts.allowGetBody = true;
|
||||
if (typeof msg.payload === "object") {
|
||||
opts.body = JSON.stringify(msg.payload);
|
||||
|
||||
@@ -110,7 +110,12 @@ module.exports = function(RED) {
|
||||
if (msg.payload[s].hasOwnProperty(p)) {
|
||||
/* istanbul ignore else */
|
||||
if (typeof msg.payload[s][p] !== "object") {
|
||||
var q = "" + msg.payload[s][p];
|
||||
// Fix to honour include null values flag
|
||||
//if (typeof msg.payload[s][p] !== "object" || (node.include_null_values === true && msg.payload[s][p] === null)) {
|
||||
var q = "";
|
||||
if (msg.payload[s][p] !== undefined) {
|
||||
q += msg.payload[s][p];
|
||||
}
|
||||
if (q.indexOf(node.quo) !== -1) { // add double quotes if any quotes
|
||||
q = q.replace(/"/g, '""');
|
||||
ou += node.quo + q + node.quo + node.sep;
|
||||
@@ -130,9 +135,12 @@ module.exports = function(RED) {
|
||||
ou += node.sep;
|
||||
}
|
||||
else {
|
||||
var p = RED.util.ensureString(RED.util.getMessageProperty(msg,"payload["+s+"]['"+template[t]+"']"));
|
||||
var p = RED.util.getMessageProperty(msg,"payload["+s+"]['"+template[t]+"']");
|
||||
/* istanbul ignore else */
|
||||
if (p === "undefined") { p = ""; }
|
||||
if (p === undefined) { p = ""; }
|
||||
// fix to honour include null values flag
|
||||
//if (p === null && node.include_null_values !== true) { p = "";}
|
||||
p = RED.util.ensureString(p);
|
||||
if (p.indexOf(node.quo) !== -1) { // add double quotes if any quotes
|
||||
p = p.replace(/"/g, '""');
|
||||
ou += node.quo + p + node.quo + node.sep;
|
||||
|
||||
@@ -449,9 +449,11 @@
|
||||
"headers": "Kopfzeilen",
|
||||
"other": "andere",
|
||||
"paytoqs": {
|
||||
"label": "Payload (GET)",
|
||||
"ignore": "Ignorieren",
|
||||
"query": "Anfügen an query-string-Parameter",
|
||||
"body": "Senden als request-body"
|
||||
"body": "Senden als request-body",
|
||||
"setby": "Durch msg.payloadHandling festgelegt"
|
||||
},
|
||||
"utf8String": "UTF-8-String",
|
||||
"binaryBuffer": "Binärer Buffer",
|
||||
|
||||
@@ -446,7 +446,9 @@
|
||||
"staticTopic": "Subscribe to single topic",
|
||||
"dynamicTopic": "Dynamic subscription",
|
||||
"auto-connect": "Connect automatically",
|
||||
"auto-mode-depreciated": "This option is depreciated. Please use the new auto-detect mode."
|
||||
"auto-mode-depreciated": "This option is depreciated. Please use the new auto-detect mode.",
|
||||
"none": "none",
|
||||
"other": "other"
|
||||
},
|
||||
"sections-label": {
|
||||
"birth-message": "Message sent on connection (birth message)",
|
||||
@@ -507,9 +509,11 @@
|
||||
"headers": "Headers",
|
||||
"other": "other",
|
||||
"paytoqs": {
|
||||
"label": "Payload (GET)",
|
||||
"ignore": "Ignore",
|
||||
"query": "Append to query-string parameters",
|
||||
"body": "Send as request body"
|
||||
"body": "Send as request body",
|
||||
"setby": "- set by msg.payloadHandling -"
|
||||
},
|
||||
"utf8String": "UTF8 string",
|
||||
"binaryBuffer": "binary buffer",
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
<dd>If set, can be used to send cookies with the request.</dd>
|
||||
<dt class="optional">payload</dt>
|
||||
<dd>Sent as the body of the request.</dd>
|
||||
<dt class="optional">payloadHandling <span class="property-type">string</span></dt>
|
||||
<dd>Only valid with GET requests. If set to <b>"- use msg.payloadHandling -"</b> in the node configuration, this property
|
||||
indicates how the <code>payload</code> will be sent. <code>msg.payloadHandling</code> should contain either
|
||||
<code>"query"</code> or <code>"body"</code> otherwise the payload will not be sent with the GET request</dd>
|
||||
<dt class="optional">rejectUnauthorized</dt>
|
||||
<dd>If set to <code>false</code>, allows requests to be made to https sites that use
|
||||
self signed certificates.</dd>
|
||||
|
||||
@@ -446,7 +446,9 @@
|
||||
"staticTopic": "1つのトピックを購読",
|
||||
"dynamicTopic": "動的な購読",
|
||||
"auto-connect": "自動接続",
|
||||
"auto-mode-depreciated": "本オプションは非推奨になりました。新しい自動判定モードを使用してください。"
|
||||
"auto-mode-depreciated": "本オプションは非推奨になりました。新しい自動判定モードを使用してください。",
|
||||
"none": "なし",
|
||||
"other": "その他"
|
||||
},
|
||||
"sections-label": {
|
||||
"birth-message": "接続時の送信メッセージ(Birthメッセージ)",
|
||||
@@ -507,9 +509,11 @@
|
||||
"headers": "ヘッダ",
|
||||
"other": "その他",
|
||||
"paytoqs": {
|
||||
"label": "ペイロード (GET)",
|
||||
"ignore": "無視",
|
||||
"query": "クエリパラメータに追加",
|
||||
"body": "リクエストボディとして送信"
|
||||
"body": "リクエストボディとして送信",
|
||||
"setby": "- msg.payloadHandlingに定義 -"
|
||||
},
|
||||
"utf8String": "UTF8文字列",
|
||||
"binaryBuffer": "バイナリバッファ",
|
||||
|
||||
@@ -387,7 +387,13 @@
|
||||
"status": "상태코드",
|
||||
"headers": "헤더",
|
||||
"other": "그 외",
|
||||
"paytoqs" : "msg.payload를 쿼리 파라미터에 추가",
|
||||
"paytoqs": {
|
||||
"label": "페이로드(GET)",
|
||||
"ignore": "무시",
|
||||
"query": "msg.payload를 쿼리 파라미터에 추가",
|
||||
"body": "본문에 msg.payload 추가",
|
||||
"setby": "- msg.payloadHandling에 의해 설정됨 -"
|
||||
},
|
||||
"utf8String": "UTF8문자열",
|
||||
"binaryBuffer": "바이너리 버퍼",
|
||||
"jsonObject": "JSON오브젝트",
|
||||
|
||||
@@ -411,9 +411,11 @@
|
||||
"headers": "Заголовки",
|
||||
"other": "другое",
|
||||
"paytoqs": {
|
||||
"label": "Данные (GET)",
|
||||
"ignore": "Игнорировать",
|
||||
"query": "Добавлять к параметрам строки запроса",
|
||||
"body": "Отправлять как тело запроса"
|
||||
"body": "Отправлять как тело запроса",
|
||||
"setby": "- устанавливается через msg.payloadHandling -"
|
||||
},
|
||||
"utf8String": "Строка UTF8",
|
||||
"binaryBuffer": "двоичный буфер",
|
||||
|
||||
@@ -407,7 +407,13 @@
|
||||
"status": "状态码",
|
||||
"headers": "头",
|
||||
"other": "其他",
|
||||
"paytoqs": "将msg.payload附加为查询字符串参数",
|
||||
"paytoqs": {
|
||||
"label": "有效负载(仅限 GET)",
|
||||
"ignore": "漠视",
|
||||
"query": "将msg.payload附加为查询字符串参数",
|
||||
"body": "在请求正文中发送负载",
|
||||
"setby": "- 用 msg.payloadHandling 设定 -"
|
||||
},
|
||||
"utf8String": "UTF8格式的字符串",
|
||||
"binaryBuffer": "二进制buffer",
|
||||
"jsonObject": "解析的JSON对象",
|
||||
|
||||
@@ -411,7 +411,13 @@
|
||||
"status": "狀態碼",
|
||||
"headers": "Header",
|
||||
"other": "其他",
|
||||
"paytoqs": "將msg.payload附加為查詢字符串參數",
|
||||
"paytoqs": {
|
||||
"label": "有效負載(僅限 GET)",
|
||||
"ignore": "漠視",
|
||||
"query": "將msg.payload附加為查詢字符串參數",
|
||||
"body": "在請求正文中發送負載",
|
||||
"setby": "- 用 msg.payloadHandling 設定 -"
|
||||
},
|
||||
"utf8String": "UTF8格式的字符串",
|
||||
"binaryBuffer": "二進制buffer",
|
||||
"jsonObject": "解析的JSON對象",
|
||||
|
||||
@@ -818,16 +818,6 @@ function handlePreRoute(flow, sendEvent, reportError) {
|
||||
})
|
||||
}
|
||||
|
||||
function deliverMessageToDestination(sendEvent) {
|
||||
if (sendEvent?.destination?.node) {
|
||||
try {
|
||||
sendEvent.destination.node.receive(sendEvent.msg);
|
||||
} catch(err) {
|
||||
Log.error(`Error delivering message to node:${sendEvent.destination.node._path} [${sendEvent.destination.node.type}]`)
|
||||
Log.error(err.stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
function handlePreDeliver(flow,sendEvent, reportError) {
|
||||
// preDeliver - the local router has identified the node it is going to send to. At this point, the message has been cloned if needed.
|
||||
hooks.trigger("preDeliver",sendEvent,(err) => {
|
||||
@@ -837,10 +827,15 @@ function handlePreDeliver(flow,sendEvent, reportError) {
|
||||
} else if (err !== false) {
|
||||
if (asyncMessageDelivery) {
|
||||
setImmediate(function() {
|
||||
deliverMessageToDestination(sendEvent)
|
||||
if (sendEvent.destination.node) {
|
||||
sendEvent.destination.node.receive(sendEvent.msg);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
deliverMessageToDestination(sendEvent)
|
||||
if (sendEvent.destination.node) {
|
||||
sendEvent.destination.node.receive(sendEvent.msg);
|
||||
|
||||
}
|
||||
}
|
||||
// postDeliver - the message has been dispatched to be delivered asynchronously (unless the sync delivery flag is set, in which case it would be continue as synchronous delivery)
|
||||
hooks.trigger("postDeliver", sendEvent, function(err) {
|
||||
|
||||
@@ -1717,6 +1717,24 @@ describe('change Node', function() {
|
||||
changeNode1.receive({topic:{foo:{bar:1}}, payload:"String"});
|
||||
});
|
||||
});
|
||||
it('moves the value of a message property object to itself', function(done) {
|
||||
var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"move","p":"payload","pt":"msg","to":"payload","tot":"msg"}],"name":"changeNode","wires":[["helperNode1"]]},
|
||||
{id:"helperNode1", type:"helper", wires:[]}];
|
||||
helper.load(changeNode, flow, function() {
|
||||
var changeNode1 = helper.getNode("changeNode1");
|
||||
var helperNode1 = helper.getNode("helperNode1");
|
||||
helperNode1.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.have.property('payload');
|
||||
msg.payload.should.equal("bar");
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
changeNode1.receive({payload:"bar"});
|
||||
});
|
||||
});
|
||||
it('moves the value of a message property object to a sub-property', function(done) {
|
||||
var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"move","p":"payload","pt":"msg","to":"payload.foo","tot":"msg"}],"name":"changeNode","wires":[["helperNode1"]]},
|
||||
{id:"helperNode1", type:"helper", wires:[]}];
|
||||
|
||||
@@ -294,6 +294,19 @@ describe('HTTP Request Node', function() {
|
||||
url: req.originalUrl
|
||||
});
|
||||
})
|
||||
testApp.get('/getBodyParams', function(req,res) {
|
||||
// Either body-parser or express is discarding the body OR
|
||||
// GOT never sent a body. (there is nothing in params/query/body)
|
||||
// Oddly, if we set options.json (instead of options.body) in the GOT
|
||||
// request, then req.body will have the values!
|
||||
//I suspect the GOT lib *is* sending body on a GET request since an
|
||||
// error is thrown if we try to set options.body on a GET request without
|
||||
// setting options.allowGetBody to true.
|
||||
res.json({
|
||||
// body:JSON.parse(req.body), //req.body is empty!
|
||||
url: req.originalUrl
|
||||
});
|
||||
})
|
||||
testApp.get('/returnError/:code', function(req,res) {
|
||||
res.status(parseInt(req.params.code)).json({gotError:req.params.code});
|
||||
})
|
||||
@@ -1117,6 +1130,89 @@ describe('HTTP Request Node', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow the payload to be sent in the body for a GET request', function(done) {
|
||||
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",paytoqs:"body",ret:"obj",url:getTestURL('/getBodyParams')},
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(httpRequestNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
const payload = {a:"one",b:true,c:3}
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
// Either Express does not deliver the body of a GET request OR GOT doesn't send it!
|
||||
// That means we cannot test this! Oddly, if the HTTP-Req node sets options.json instead
|
||||
// of options.body, then the body is delivered.
|
||||
// msg.should.have.property('payload',{
|
||||
// body:payload,
|
||||
// url: '/getBodyParams'
|
||||
// });
|
||||
msg.should.have.property('payload').and.be.an.Object()
|
||||
msg.payload.should.have.property('url', '/getBodyParams')
|
||||
|
||||
msg.should.have.property('statusCode',200);
|
||||
msg.should.have.property('headers');
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
n1.receive({payload:payload});
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow the message to specify that the payload be append to querystring for a GET request', function(done) {
|
||||
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"use",paytoqs:"setby",ret:"obj",url:getTestURL('/getQueryParams')},
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(httpRequestNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.have.property('payload',{
|
||||
query:{a:'1',b:'2',c:'3'},
|
||||
url: '/getQueryParams?a=1&b=2&c=3'
|
||||
});
|
||||
msg.should.have.property('statusCode',200);
|
||||
msg.should.have.property('headers');
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
n1.receive({payload:{a:1,b:2,c:3},method:"get",payloadHandling:'query'});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should allow the message to specify that the payload be sent in the body for a GET request', function(done) {
|
||||
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"use",paytoqs:"setby",ret:"obj",url:getTestURL('/getBodyParams')},
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(httpRequestNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
const payload = {a:"one",b:true,c:3}
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
// Either Express does not deliver the body of a GET request OR GOT doesn't send it!
|
||||
// That means we cannot test this! Oddly, if the HTTP-Req node sets options.json instead
|
||||
// of options.body, then the body is delivered.
|
||||
// msg.should.have.property('payload',{
|
||||
// payload: payload,
|
||||
// url: '/getBodyParams'
|
||||
// });
|
||||
msg.should.have.property('payload').and.be.an.Object()
|
||||
msg.payload.should.have.property('url', '/getBodyParams')
|
||||
msg.should.have.property('statusCode',200);
|
||||
msg.should.have.property('headers');
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
n1.receive({payload:payload,method:"get",payloadHandling:'body'});
|
||||
});
|
||||
});
|
||||
|
||||
it('should send a msg for non-2xx response status - 400', function(done) {
|
||||
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/returnError/400')},
|
||||
{id:"n2", type:"helper"}];
|
||||
@@ -1529,7 +1625,7 @@ describe('HTTP Request Node', function() {
|
||||
msg.payload.headers.should.have.property('Content-Type').which.startWith('application/json');
|
||||
//msg.dynamicHeaderName should be present in headers with the value of msg.dynamicHeaderValue
|
||||
msg.payload.headers.should.have.property('dyn-header-name').which.startWith('dyn-header-value');
|
||||
//static (custom) header set in Flow UI should be present
|
||||
//static (custom) header set in Flow UI should be present
|
||||
msg.payload.headers.should.have.property('static-header-name').which.startWith('static-header-value');
|
||||
//msg.headers['location'] should be deleted because Flow UI "Location" header has a blank value
|
||||
//ensures headers with matching characters but different case are eliminated
|
||||
@@ -2281,7 +2377,7 @@ describe('HTTP Request Node', function() {
|
||||
let port = testPort++
|
||||
|
||||
let server;
|
||||
|
||||
|
||||
before(function() {
|
||||
server = net.createServer(function (socket) {
|
||||
socket.write("HTTP/1.0 200\nContent-Type: text/plain\n\nHelloWorld")
|
||||
@@ -2291,7 +2387,7 @@ describe('HTTP Request Node', function() {
|
||||
server.listen(port,'127.0.0.1', function(err) {
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
after(function() {
|
||||
server.close()
|
||||
});
|
||||
@@ -2322,14 +2418,14 @@ describe('HTTP Request Node', function() {
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on('input', function(msg) {
|
||||
try{
|
||||
msg.payload.should.equal(`RequestError: Parse Error: Missing expected CR after header value : http://localhost:${port}/`)
|
||||
msg.payload.should.match(/RequestError: Parse Error/)
|
||||
done()
|
||||
} catch (err) {
|
||||
done(err)
|
||||
}
|
||||
})
|
||||
n1.receive({payload: 'foo'})
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -489,6 +489,39 @@ describe('MQTT Nodes', function () {
|
||||
}
|
||||
testSendRecv(brokerOptions, { topic: brokerOptions.birthTopic }, {}, options, hooks);
|
||||
});
|
||||
itConditional('should safely discard bad birth topic', function (done) {
|
||||
if (skipTests) { return this.skip() }
|
||||
this.timeout = 2000;
|
||||
const baseTopic = nextTopic();
|
||||
const brokerOptions = {
|
||||
protocolVersion: 4,
|
||||
birthTopic: baseTopic + "#", // a publish topic should never have a wildcard
|
||||
birthPayload: "broker connected",
|
||||
birthQos: 2,
|
||||
}
|
||||
const options = {};
|
||||
const hooks = { done: null, beforeLoad: null, afterLoad: null, afterConnect: null };
|
||||
hooks.afterLoad = (helperNode, mqttBroker, mqttIn, mqttOut) => {
|
||||
helperNode.on("input", function (msg) {
|
||||
try {
|
||||
msg.should.have.a.property("error").type("object");
|
||||
msg.error.should.have.a.property("source").type("object");
|
||||
msg.error.source.should.have.a.property("id", mqttIn.id);
|
||||
done();
|
||||
} catch (err) {
|
||||
done(err)
|
||||
}
|
||||
});
|
||||
return true; //handled
|
||||
}
|
||||
options.expectMsg = null;
|
||||
try {
|
||||
testSendRecv(brokerOptions, { topic: brokerOptions.birthTopic }, {}, options, hooks);
|
||||
done()
|
||||
} catch(err) {
|
||||
done(e)
|
||||
}
|
||||
});
|
||||
itConditional('should publish close message', function (done) {
|
||||
if (skipTests) { return this.skip() }
|
||||
this.timeout = 2000;
|
||||
@@ -646,12 +679,13 @@ function testSendRecv(brokerOptions, inNodeOptions, outNodeOptions, options, hoo
|
||||
const mqttBroker = helper.getNode(brokerOptions.id);
|
||||
const mqttIn = helper.getNode(nodes.mqtt_in.id);
|
||||
const mqttOut = helper.getNode(nodes.mqtt_out.id);
|
||||
let afterLoadHandled = false;
|
||||
let afterLoadHandled = false, finished = false;
|
||||
if (hooks.afterLoad) {
|
||||
afterLoadHandled = hooks.afterLoad(helperNode, mqttBroker, mqttIn, mqttOut)
|
||||
}
|
||||
if (!afterLoadHandled) {
|
||||
helperNode.on("input", function (msg) {
|
||||
finished = true
|
||||
try {
|
||||
compareMsgToExpected(msg, expectMsg);
|
||||
if (hooks.done) { hooks.done(); }
|
||||
@@ -676,10 +710,12 @@ function testSendRecv(brokerOptions, inNodeOptions, outNodeOptions, options, hoo
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
if(finished) { return }
|
||||
if (hooks.done) { hooks.done(e); }
|
||||
else { throw e; }
|
||||
});
|
||||
} catch (err) {
|
||||
if(finished) { return }
|
||||
if (hooks.done) { hooks.done(err); }
|
||||
else { throw err; }
|
||||
}
|
||||
|
||||
@@ -693,19 +693,20 @@ describe('CSV node', function() {
|
||||
describe('json object to csv', function() {
|
||||
|
||||
it('should convert a simple object back to a csv', function(done) {
|
||||
var flow = [ { id:"n1", type:"csv", temp:"a,b,c,,e,f", wires:[["n2"]] },
|
||||
var flow = [ { id:"n1", type:"csv", temp:"a,b,c,,e,f,g,h,i,j,k", wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"} ];
|
||||
helper.load(csvNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on("input", function(msg) {
|
||||
// console.log("GOT",msg)
|
||||
try {
|
||||
msg.should.have.property('payload', '4,foo,true,,0,"Hello\nWorld"\n');
|
||||
msg.should.have.property('payload', '4,foo,true,,0,"Hello\nWorld",,,undefined,null,null\n');
|
||||
done();
|
||||
}
|
||||
catch(e) { done(e); }
|
||||
});
|
||||
var testJson = { e:0, d:1, b:"foo", c:true, a:4, f:"Hello\nWorld" };
|
||||
var testJson = { e:0, d:1, b:"foo", c:true, a:4, f:"Hello\nWorld", h:undefined, i:"undefined",j:null,k:"null" };
|
||||
n1.emit("input", {payload:testJson});
|
||||
});
|
||||
});
|
||||
@@ -717,13 +718,14 @@ describe('CSV node', function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on("input", function(msg) {
|
||||
// console.log("GOT",msg)
|
||||
try {
|
||||
msg.should.have.property('payload', '1,foo,"ba""r","di,ng"\n');
|
||||
msg.should.have.property('payload', '1,foo,"ba""r","di,ng",,undefined,null\n');
|
||||
done();
|
||||
}
|
||||
catch(e) { done(e); }
|
||||
});
|
||||
var testJson = { d:1, b:"foo", c:"ba\"r", a:"di,ng" };
|
||||
var testJson = { d:1, b:"foo", c:"ba\"r", a:"di,ng", e:undefined, f:"undefined", g:null,h:"null" };
|
||||
n1.emit("input", {payload:testJson});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,11 +15,14 @@
|
||||
**/
|
||||
|
||||
var fs = require("fs-extra");
|
||||
var os = require("os");
|
||||
var path = require("path");
|
||||
var should = require("should");
|
||||
var helper = require("node-red-node-test-helper");
|
||||
var watchNode = require("nr-test-utils").require("@node-red/nodes/core/storage/23-watch.js");
|
||||
|
||||
var arch = os.arch();
|
||||
var platform = os.platform();
|
||||
|
||||
describe('watch Node', function() {
|
||||
this.timeout(5000);
|
||||
@@ -89,7 +92,10 @@ describe('watch Node', function() {
|
||||
msg.should.have.property('payload', result.payload);
|
||||
msg.should.have.property('type', result.type);
|
||||
if('size' in result) {
|
||||
msg.should.have.property('size', result.size);
|
||||
if (!((arch === "arm64") && (platform === "darwin"))) {
|
||||
// On OSX/ARM, two change events occur and first event do not reflect file size change. So ignore size field in the case.
|
||||
msg.should.have.property('size', result.size);
|
||||
}
|
||||
}
|
||||
count++;
|
||||
if(count === len) {
|
||||
|
||||
Reference in New Issue
Block a user