mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Compare commits
41 Commits
2.0.2
...
fs-promise
Author | SHA1 | Date | |
---|---|---|---|
|
2fe78cf971 | ||
|
bbf066f030 | ||
|
17f9829498 | ||
|
3b460fb8fa | ||
|
ee15e9acc5 | ||
|
48fce35fb3 | ||
|
78899378c2 | ||
|
2144407e41 | ||
|
26d83bb9ea | ||
|
8e89b1bdf2 | ||
|
630d2ca926 | ||
|
34cb93794c | ||
|
122b5ba468 | ||
|
7f2627dbc8 | ||
|
e93734b209 | ||
|
9e5218f6b4 | ||
|
f1e7ec0c6b | ||
|
23765d9139 | ||
|
43febe269c | ||
|
40233c7702 | ||
|
27ed81614b | ||
|
889d23e9bd | ||
|
f8571023f6 | ||
|
6364e00202 | ||
|
a76c6f86c6 | ||
|
555e815402 | ||
|
ee9234b2c6 | ||
|
735b9c5844 | ||
|
064f3eb3bc | ||
|
f1775d4fd1 | ||
|
a9bc111c4f | ||
|
c100612473 | ||
|
26087f8dc7 | ||
|
36e75cb728 | ||
|
142176f194 | ||
|
c5892fc17e | ||
|
6e69cfbca4 | ||
|
775181f761 | ||
|
36e83d628e | ||
|
5f6fcb2bc0 | ||
|
7b106e5650 |
57
CHANGELOG.md
57
CHANGELOG.md
@@ -1,3 +1,60 @@
|
||||
#### 2.0.6: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Fix typo in ko editor.json Fixes #3119
|
||||
- Change fade color when hovering an inactive tab (#3106) @bonanitech
|
||||
- Ensure treeList row has suitable min-height when no content Fixes #3109
|
||||
|
||||
Runtime
|
||||
|
||||
- Update tar to latest (#3128) @aksswami
|
||||
- Give passport verify callback the same arity as the original callback (#3117) @dschmidt
|
||||
- Handle HTTPS Key and certificate as string or buffer (#3115) @bartbutenaers
|
||||
|
||||
#### 2.0.5: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Remove default ctrl-enter keybinding from monaco editor Fixes #3093
|
||||
|
||||
Runtime
|
||||
|
||||
- Update tar dependency
|
||||
- Add support for maintenance streams in generate-publish-script
|
||||
|
||||
|
||||
Nodes
|
||||
|
||||
- Fix regression in Join node when manual joining array with msg.parts present Fixes #3096
|
||||
|
||||
#### 2.0.4: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Fix tab fade CSS for when using themes (#3085) @bonanitech
|
||||
- Handle just-copied-but-not-deployed node with credentials in editor Fixes #3090
|
||||
|
||||
Nodes
|
||||
|
||||
- Filter: Fix RBE node handling of default topi property Fixes #3087
|
||||
- HTTP Request: Handle partially encoded url query strings in request node
|
||||
- HTTP Request: Fix support for supplied CA certs (#3089) @hardillb
|
||||
- HTTP Request: Ensure TLS Cert is used (#3092) @hardillb
|
||||
- Inject: Fix inject now button unable to send empty props
|
||||
- Inject: Inject now button success notification should use label with updated props
|
||||
|
||||
#### 2.0.3: Maintenance Release
|
||||
|
||||
Nodes
|
||||
|
||||
- HTML: Fix HTML parsing when body is included in the select tag Fixes #3079
|
||||
- HTTP Request: Preserve case of user-provided http headers in request node Fixes #3081
|
||||
- HTTP Request: Set decompress to false for HTTP Request to keep 1.x compatibility Fixes #3083
|
||||
- HTTP Request: Add unit tests for HTTP Request encodeURI and error response
|
||||
- HTTP Request: Do not throw HTTP errors in request node Fixes #3082
|
||||
- HTTP Request: Ensure uri is properly encoded before passing to got module Fixes #3080
|
||||
|
||||
#### 2.0.2: Maintenance Release
|
||||
|
||||
Runtime
|
||||
|
26
package.json
26
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.6",
|
||||
"description": "Low-code programming for event-driven applications",
|
||||
"homepage": "http://nodered.org",
|
||||
"license": "Apache-2.0",
|
||||
@@ -28,8 +28,8 @@
|
||||
"dependencies": {
|
||||
"acorn": "8.4.1",
|
||||
"acorn-walk": "8.1.1",
|
||||
"ajv": "8.6.0",
|
||||
"async-mutex": "0.3.1",
|
||||
"ajv": "8.6.2",
|
||||
"async-mutex": "0.3.2",
|
||||
"basic-auth": "2.0.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"body-parser": "1.19.0",
|
||||
@@ -55,14 +55,14 @@
|
||||
"is-utf8": "0.2.1",
|
||||
"js-yaml": "3.14.1",
|
||||
"json-stringify-safe": "5.0.1",
|
||||
"jsonata": "1.8.4",
|
||||
"jsonata": "1.8.5",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"media-typer": "1.1.0",
|
||||
"memorystore": "1.6.6",
|
||||
"mime": "2.5.2",
|
||||
"moment-timezone": "0.5.33",
|
||||
"mqtt": "4.2.8",
|
||||
"multer": "1.4.2",
|
||||
"multer": "1.4.3",
|
||||
"mustache": "4.2.0",
|
||||
"node-red-admin": "^2.2.0",
|
||||
"nopt": "5.0.0",
|
||||
@@ -73,9 +73,9 @@
|
||||
"passport-oauth2-client-password": "0.1.2",
|
||||
"raw-body": "2.4.1",
|
||||
"semver": "7.3.5",
|
||||
"tar": "6.1.0",
|
||||
"tar": "6.1.11",
|
||||
"tough-cookie": "4.0.0",
|
||||
"uglify-js": "3.13.10",
|
||||
"uglify-js": "3.14.1",
|
||||
"uuid": "8.3.2",
|
||||
"ws": "7.5.1",
|
||||
"xml2js": "0.4.23"
|
||||
@@ -84,7 +84,7 @@
|
||||
"bcrypt": "5.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"dompurify": "2.2.9",
|
||||
"dompurify": "2.3.1",
|
||||
"grunt": "1.4.1",
|
||||
"grunt-chmod": "~1.1.1",
|
||||
"grunt-cli": "~1.4.3",
|
||||
@@ -109,15 +109,15 @@
|
||||
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
|
||||
"marked": "2.1.3",
|
||||
"minami": "1.2.3",
|
||||
"mocha": "9.0.1",
|
||||
"mocha": "9.1.1",
|
||||
"node-red-node-test-helper": "^0.2.7",
|
||||
"nodemon": "2.0.8",
|
||||
"nodemon": "2.0.12",
|
||||
"proxy": "^1.0.2",
|
||||
"sass": "1.35.1",
|
||||
"sass": "1.39.0",
|
||||
"should": "13.2.3",
|
||||
"sinon": "11.1.1",
|
||||
"sinon": "11.1.2",
|
||||
"stoppable": "^1.1.0",
|
||||
"supertest": "6.1.3"
|
||||
"supertest": "6.1.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
|
@@ -173,27 +173,30 @@ function genericStrategy(adminApp,strategy) {
|
||||
adminApp.use(passport.session());
|
||||
|
||||
var options = strategy.options;
|
||||
var verify = function() {
|
||||
var originalDone = arguments[arguments.length-1];
|
||||
if (options.verify) {
|
||||
var args = Array.from(arguments);
|
||||
args[args.length-1] = function(err,profile) {
|
||||
if (err) {
|
||||
return originalDone(err);
|
||||
} else {
|
||||
return completeVerify(profile,originalDone);
|
||||
}
|
||||
};
|
||||
|
||||
passport.use(new strategy.strategy(options,
|
||||
function() {
|
||||
var originalDone = arguments[arguments.length-1];
|
||||
if (options.verify) {
|
||||
var args = Array.from(arguments);
|
||||
args[args.length-1] = function(err,profile) {
|
||||
if (err) {
|
||||
return originalDone(err);
|
||||
} else {
|
||||
return completeVerify(profile,originalDone);
|
||||
}
|
||||
};
|
||||
options.verify.apply(null,args);
|
||||
} else {
|
||||
var profile = arguments[arguments.length - 2];
|
||||
return completeVerify(profile,originalDone);
|
||||
}
|
||||
|
||||
options.verify.apply(null,args);
|
||||
} else {
|
||||
var profile = arguments[arguments.length - 2];
|
||||
return completeVerify(profile,originalDone);
|
||||
}
|
||||
));
|
||||
};
|
||||
// Give our callback the same arity as the original one from options
|
||||
if (options.verify) {
|
||||
Object.defineProperty(verify, "length", { value: options.verify.length })
|
||||
}
|
||||
|
||||
passport.use(new strategy.strategy(options, verify));
|
||||
|
||||
adminApp.get('/auth/strategy',
|
||||
passport.authenticate(strategy.name, {session:false, failureRedirect: settings.httpAdminRoot }),
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/editor-api",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.6",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@@ -16,8 +16,8 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/util": "2.0.2",
|
||||
"@node-red/editor-client": "2.0.2",
|
||||
"@node-red/util": "2.0.6",
|
||||
"@node-red/editor-client": "2.0.6",
|
||||
"bcryptjs": "2.4.3",
|
||||
"body-parser": "1.19.0",
|
||||
"clone": "2.1.2",
|
||||
@@ -26,7 +26,7 @@
|
||||
"express": "4.17.1",
|
||||
"memorystore": "1.6.6",
|
||||
"mime": "2.5.2",
|
||||
"multer": "1.4.2",
|
||||
"multer": "1.4.3",
|
||||
"mustache": "4.2.0",
|
||||
"oauth2orize": "1.11.0",
|
||||
"passport-http-bearer": "1.0.1",
|
||||
|
@@ -56,7 +56,7 @@
|
||||
"displayConfig": "설정노드 보기",
|
||||
"import": "가져오기",
|
||||
"export": "내보내기",
|
||||
"search": "플로우 겅색",
|
||||
"search": "플로우 검색",
|
||||
"searchInput": "플로우 검색",
|
||||
"subflows": "보조 플로우",
|
||||
"createSubflow": "보조 플로우 생성",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/editor-client",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.6",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@@ -664,6 +664,8 @@ RED.tabs = (function() {
|
||||
link.on("dblclick", function(evt) { evt.stopPropagation(); evt.preventDefault(); })
|
||||
|
||||
|
||||
$('<span class="red-ui-tabs-fade"></span>').appendTo(li);
|
||||
|
||||
if (tab.closeable) {
|
||||
li.addClass("red-ui-tabs-closeable")
|
||||
var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close"}).appendTo(li);
|
||||
@@ -674,8 +676,6 @@ RED.tabs = (function() {
|
||||
});
|
||||
}
|
||||
|
||||
$('<span class="red-ui-tabs-fade"></span>').appendTo(li);
|
||||
|
||||
var badges = $('<span class="red-ui-tabs-badges"></span>').appendTo(li);
|
||||
if (options.onselect) {
|
||||
$('<i class="red-ui-tabs-badge-changed fa fa-circle"></i>').appendTo(badges);
|
||||
|
@@ -407,8 +407,10 @@ RED.editor = (function() {
|
||||
*/
|
||||
function updateNodeCredentials(node, credDefinition, prefix) {
|
||||
var changed = false;
|
||||
if(!node.credentials) {
|
||||
if (!node.credentials) {
|
||||
node.credentials = {_:{}};
|
||||
} else if (!node.credentials._) {
|
||||
node.credentials._ = {};
|
||||
}
|
||||
|
||||
for (var cred in credDefinition) {
|
||||
|
@@ -31,7 +31,7 @@
|
||||
function setMode(mode, cb)
|
||||
function getRange();
|
||||
function replace(range, text)
|
||||
function selectAll
|
||||
function selectAll
|
||||
function clearSelection
|
||||
function getSelectedText()
|
||||
function destroy()
|
||||
@@ -153,9 +153,9 @@ RED.editor.codeEditor.monaco = (function() {
|
||||
|
||||
function init(options) {
|
||||
|
||||
//Handles orphaned models
|
||||
//Handles orphaned models
|
||||
//ensure loaded models that are not explicitly destroyed by a call to .destroy() are disposed
|
||||
RED.events.on("editor:close",function() {
|
||||
RED.events.on("editor:close",function() {
|
||||
let models = window.monaco ? monaco.editor.getModels() : null;
|
||||
if(models && models.length) {
|
||||
console.warn("Cleaning up monaco models left behind. Any node that calls createEditor() should call .destroy().")
|
||||
@@ -744,10 +744,10 @@ RED.editor.codeEditor.monaco = (function() {
|
||||
|
||||
//by default, set javascript editors to text mode.
|
||||
//when element becomes visible, it will be (re) set to javascript mode
|
||||
//this is to ensure multiple editors sharing the model dont present its
|
||||
//this is to ensure multiple editors sharing the model dont present its
|
||||
//consts & lets to each other
|
||||
if(editorOptions.language == "javascript") {
|
||||
editorOptions._language = editorOptions.language;
|
||||
editorOptions._language = editorOptions.language;
|
||||
editorOptions.language = "text"
|
||||
}
|
||||
|
||||
@@ -921,6 +921,15 @@ RED.editor.codeEditor.monaco = (function() {
|
||||
/*********** Create the monaco editor ***************/
|
||||
var ed = monaco.editor.create(el, editorOptions);
|
||||
|
||||
//Unbind ctrl-Enter (default action is to insert a newline in editor) This permits the shortcut to close the tray.
|
||||
try {
|
||||
ed._standaloneKeybindingService.addDynamicKeybinding(
|
||||
'-editor.action.insertLineAfter', // command ID prefixed by '-'
|
||||
null, // keybinding
|
||||
() => {} // need to pass an empty handler
|
||||
);
|
||||
} catch (error) { }
|
||||
|
||||
ed.nodered = {
|
||||
refreshModuleLibs: refreshModuleLibs //expose this for function node externalModules refresh
|
||||
}
|
||||
@@ -967,7 +976,7 @@ RED.editor.codeEditor.monaco = (function() {
|
||||
if (cb && typeof cb == "function") {
|
||||
cb();
|
||||
}
|
||||
if(resize) {
|
||||
if(resize) {
|
||||
this.resize(); //cause a call to layout()
|
||||
}
|
||||
}
|
||||
@@ -1218,7 +1227,7 @@ RED.editor.codeEditor.monaco = (function() {
|
||||
}
|
||||
ed._mode = editorOptions.language;
|
||||
|
||||
//as models are signleton, consts and let are avialable to other javascript instances
|
||||
//as models are signleton, consts and let are avialable to other javascript instances
|
||||
//so when not focused, set editor mode to text temporarily to avoid multiple defs
|
||||
|
||||
if(editorOptions._language) {
|
||||
@@ -1240,15 +1249,15 @@ RED.editor.codeEditor.monaco = (function() {
|
||||
try {
|
||||
var options = {
|
||||
root: $(element).closest("div.red-ui-tray-content")[0] || document,
|
||||
attributes: true,
|
||||
childList: true,
|
||||
attributes: true,
|
||||
childList: true,
|
||||
};
|
||||
var observer = new IntersectionObserver(function(entries, observer) {
|
||||
entries.forEach(function(entry) {
|
||||
callback(entry.intersectionRatio > 0, 5, entry.target);
|
||||
});
|
||||
}, options);
|
||||
observer.observe(element);
|
||||
observer.observe(element);
|
||||
} catch (e1) {
|
||||
//browser not supporting IntersectionObserver? then fall back to polling!
|
||||
try {
|
||||
@@ -1267,7 +1276,7 @@ RED.editor.codeEditor.monaco = (function() {
|
||||
function onVisibilityChange(visible, delay, element) {
|
||||
if(visible) {
|
||||
if(ed._mode == "javascript" && ed._tempMode == "text") {
|
||||
ed._tempMode = "";
|
||||
ed._tempMode = "";
|
||||
setTimeout(function() {
|
||||
if(element.parentElement) { //ensure el is still in DOM
|
||||
ed.setMode('javascript', undefined, false);
|
||||
@@ -1277,7 +1286,7 @@ RED.editor.codeEditor.monaco = (function() {
|
||||
} else if(ed._mode == "javascript" && ed._tempMode != "text") {
|
||||
if(element.parentElement) { //ensure el is still in DOM
|
||||
ed.setMode('text', undefined, false);
|
||||
ed._tempMode = "text";
|
||||
ed._tempMode = "text";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1353,4 +1362,4 @@ RED.editor.codeEditor.monaco = (function() {
|
||||
*/
|
||||
create: create
|
||||
}
|
||||
})();
|
||||
})();
|
||||
|
@@ -104,7 +104,7 @@
|
||||
}
|
||||
|
||||
.red-ui-tabs-fade {
|
||||
background-image: linear-gradient(to right, rgba(255,255,255,0.001), $tab-background-active);
|
||||
background-image: linear-gradient(to right, change-color($tab-background-active, $alpha: 0.001), $tab-background-active);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -112,7 +112,7 @@
|
||||
&:not(.active) {
|
||||
background: $tab-background-selected;
|
||||
.red-ui-tabs-fade {
|
||||
background-image: linear-gradient(to right, rgba(255,255,255,0.001), $tab-background-selected);
|
||||
background-image: linear-gradient(to right, change-color($tab-background-selected, $alpha: 0.001), $tab-background-selected);
|
||||
}
|
||||
.red-ui-tabs-badge-selected {
|
||||
background: $tab-background-selected;
|
||||
@@ -131,6 +131,9 @@
|
||||
&:not(.active) a:hover {
|
||||
color: $workspace-button-color-hover;
|
||||
background: $tab-background-hover;
|
||||
&+.red-ui-tabs-fade {
|
||||
background-image: linear-gradient(to right, change-color($tab-background-hover, $alpha: 0.001), $tab-background-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -308,7 +311,7 @@
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 15px;
|
||||
background-image: linear-gradient(to right, rgba(255,255,255,0.001), $tab-background-inactive);
|
||||
background-image: linear-gradient(to right, change-color($tab-background-inactive, $alpha: 0.001), $tab-background-inactive);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
@@ -101,6 +101,9 @@
|
||||
}
|
||||
.red-ui-treeList-label-text {
|
||||
margin-left: 4px;
|
||||
&:empty {
|
||||
min-height: 20px;
|
||||
}
|
||||
}
|
||||
.red-ui-treeList-sublabel-text {
|
||||
top: 0;
|
||||
|
@@ -193,7 +193,7 @@
|
||||
}
|
||||
/** Perform inject, optionally sending a custom msg (refactored for re-use in the form inject button)*/
|
||||
function doInject(node, customMsg) {
|
||||
var label = node._def.label.call(node);
|
||||
var label = node._def.label.call(node,customMsg?customMsg.__user_inject_props__:undefined);
|
||||
if (label.length > 30) {
|
||||
label = label.substring(0, 50) + "...";
|
||||
}
|
||||
@@ -201,7 +201,8 @@
|
||||
$.ajax({
|
||||
url: "inject/" + node.id,
|
||||
type: "POST",
|
||||
data: customMsg,
|
||||
data: JSON.stringify(customMsg||{}),
|
||||
contentType: "application/json; charset=utf-8",
|
||||
success: function (resp) {
|
||||
RED.notify(node._("inject.success", { label: label }), { type: "success", id: "inject", timeout: 2000 });
|
||||
},
|
||||
@@ -291,7 +292,7 @@
|
||||
}
|
||||
return lab;
|
||||
},
|
||||
label: function() {
|
||||
label: function(customProps) {
|
||||
var suffix = "";
|
||||
// if fire once then add small indication
|
||||
if (this.once) {
|
||||
@@ -304,11 +305,23 @@
|
||||
if (this.name) {
|
||||
return this.name+suffix;
|
||||
}
|
||||
|
||||
var payload = this.payload || "";
|
||||
var payloadType = this.payloadType || "str";
|
||||
var topic = this.topic || "";
|
||||
|
||||
var payload = "";
|
||||
var payloadType = "str";
|
||||
var topic = "";
|
||||
if (customProps) {
|
||||
for (var i=0;i<customProps.length;i++) {
|
||||
if (customProps[i].p === "payload") {
|
||||
payload = customProps[i].v;
|
||||
payloadType = customProps[i].vt;
|
||||
} else if (customProps[i].p === "topic") {
|
||||
topic = customProps[i].v;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
payload = this.payload || "";
|
||||
payloadType = this.payloadType || "str";
|
||||
topic = this.topic || "";
|
||||
}
|
||||
if (payloadType === "string" ||
|
||||
payloadType === "str" ||
|
||||
payloadType === "num" ||
|
||||
@@ -496,11 +509,8 @@
|
||||
label: node._("inject.injectNow"),
|
||||
click: function(e) {
|
||||
var items = eList.editableList('items');
|
||||
var result = getProps(items);
|
||||
var m = {__user_inject_props__: []};
|
||||
if (result && result.props && result.props.length) {
|
||||
m.__user_inject_props__ = result.props;
|
||||
}
|
||||
var props = getProps(items);
|
||||
var m = {__user_inject_props__: props.props};
|
||||
doInject(node, m);
|
||||
}
|
||||
}
|
||||
|
@@ -101,7 +101,7 @@ module.exports = function(RED) {
|
||||
this.on("input", function(msg, send, done) {
|
||||
var errors = [];
|
||||
var props = this.props;
|
||||
if(msg.__user_inject_props__ && Array.isArray(msg.__user_inject_props__)) {
|
||||
if (msg.__user_inject_props__ && Array.isArray(msg.__user_inject_props__)) {
|
||||
props = msg.__user_inject_props__;
|
||||
}
|
||||
delete msg.__user_inject_props__;
|
||||
|
@@ -27,10 +27,14 @@
|
||||
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="node-red:common.label.property"></span></label>
|
||||
<input type="text" id="node-input-property" style="width:70%;"/>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-row" style="margin-bottom: 0px;">
|
||||
<label> </label>
|
||||
<input type="checkbox" id="node-input-septopics" style="display:inline-block; width:20px; vertical-align:baseline;">
|
||||
<span data-i18n="rbe.label.septopics"></span> <input type="text" id="node-input-topi" style="width:27%;"/>
|
||||
<label style="width: auto" for="node-input-septopics" data-i18n="rbe.label.septopics"></label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label> </label>
|
||||
<input type="text" id="node-input-topi" style="width:70%;"/>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="rbe.label.name"></span></label>
|
||||
@@ -70,6 +74,10 @@
|
||||
if (this.septopics === undefined) {
|
||||
$("#node-input-septopics").prop('checked', true);
|
||||
}
|
||||
if (this.topi === undefined) {
|
||||
$("#node-input-topi").val("topic");
|
||||
}
|
||||
|
||||
$("#node-input-property").typedInput({default:'msg',types:['msg']});
|
||||
$("#node-input-topi").typedInput({default:'msg',types:['msg']});
|
||||
//$( "#node-input-gap" ).spinner({min:0});
|
||||
@@ -88,6 +96,11 @@
|
||||
$("#node-startvalue").hide();
|
||||
}
|
||||
});
|
||||
$("#node-input-septopics").on("change", function() {
|
||||
$("#node-input-topi").typedInput("disable",!this.checked);
|
||||
})
|
||||
$("#node-input-topi").typedInput("disable",!!!this.septopics);
|
||||
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@@ -135,6 +135,28 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
||||
}
|
||||
}
|
||||
|
||||
// The Request module used in Node-RED 1.x was tolerant of query strings that
|
||||
// were partially encoded. For example - "?a=hello%20there&b=20%"
|
||||
// The GOT module doesn't like that.
|
||||
// The following is an attempt to normalise the url to ensure it is properly
|
||||
// encoded. We cannot just encode it directly as we don't want any valid
|
||||
// encoded entity to end up doubly encoded.
|
||||
if (url.indexOf("?") > -1) {
|
||||
// Only do this if there is a query string to deal with
|
||||
const [hostPath, ...queryString] = url.split("?")
|
||||
const query = queryString.join("?");
|
||||
if (query) {
|
||||
// Look for any instance of % not followed by two hex chars.
|
||||
// Replace any we find with %25.
|
||||
const escapedQueryString = query.replace(/(%.?.?)/g, function(v) {
|
||||
if (/^%[a-f0-9]{2}/i.test(v)) {
|
||||
return v;
|
||||
}
|
||||
return v.replace(/%/,"%25")
|
||||
})
|
||||
url = hostPath+"?"+escapedQueryString;
|
||||
}
|
||||
}
|
||||
|
||||
var method = nodeMethod.toUpperCase() || "GET";
|
||||
if (msg.method && n.method && (n.method !== "use")) { // warn if override option not set
|
||||
@@ -151,6 +173,9 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
||||
// 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
|
||||
opts.decompress = false;
|
||||
opts.method = method;
|
||||
opts.headers = {};
|
||||
opts.retry = 0;
|
||||
@@ -168,8 +193,23 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
||||
opts.timeout = msg.requestTimeout;
|
||||
}
|
||||
}
|
||||
const originalHeaderMap = {};
|
||||
|
||||
opts.hooks = {
|
||||
beforeRequest: [
|
||||
options => {
|
||||
// Whilst HTTP headers are meant to be case-insensitive,
|
||||
// in the real world, there are servers that aren't so compliant.
|
||||
// GOT will lower case all headers given a chance, so we need
|
||||
// to restore the case of any headers the user has set.
|
||||
Object.keys(options.headers).forEach(h => {
|
||||
if (originalHeaderMap[h] && originalHeaderMap[h] !== h) {
|
||||
options.headers[originalHeaderMap[h]] = options.headers[h];
|
||||
delete options.headers[h];
|
||||
}
|
||||
})
|
||||
}
|
||||
],
|
||||
beforeRedirect: [
|
||||
(options, response) => {
|
||||
let redirectInfo = {
|
||||
@@ -280,7 +320,7 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
||||
})
|
||||
if (normalisedHeaders['www-authenticate']) {
|
||||
let authHeader = buildDigestHeader(digestCreds.user,digestCreds.password, options.method, requestUrl.pathname, normalisedHeaders['www-authenticate'])
|
||||
options.headers.authorization = authHeader;
|
||||
options.headers.Authorization = authHeader;
|
||||
}
|
||||
sentCreds = true;
|
||||
return retry(options);
|
||||
@@ -431,11 +471,27 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
||||
if (tlsNode) {
|
||||
opts.https = {};
|
||||
tlsNode.addTLSOptions(opts.https);
|
||||
if (opts.https.ca) {
|
||||
opts.https.certificateAuthority = opts.https.ca;
|
||||
delete opts.https.ca;
|
||||
}
|
||||
if (opts.https.cert) {
|
||||
opts.https.certificate = opts.https.cert;
|
||||
delete opts.https.cert;
|
||||
}
|
||||
} else {
|
||||
if (msg.hasOwnProperty('rejectUnauthorized')) {
|
||||
opts.https = { rejectUnauthorized: msg.rejectUnauthorized };
|
||||
}
|
||||
}
|
||||
|
||||
// Now we have established all of our own headers, take a snapshot
|
||||
// of their case so we can restore it prior to the request being sent.
|
||||
if (opts.headers) {
|
||||
Object.keys(opts.headers).forEach(h => {
|
||||
originalHeaderMap[h.toLowerCase()] = h
|
||||
})
|
||||
}
|
||||
got(url,opts).then(res => {
|
||||
msg.statusCode = res.statusCode;
|
||||
msg.headers = res.headers;
|
||||
|
@@ -372,12 +372,13 @@ module.exports = function(RED) {
|
||||
socket.setKeepAlive(true,120000);
|
||||
if (socketTimeout !== null) { socket.setTimeout(socketTimeout); }
|
||||
node.log(RED._("tcpin.status.connection-from",{host:socket.remoteAddress, port:socket.remotePort}));
|
||||
connectedSockets.push(socket);
|
||||
node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})});
|
||||
socket.on('timeout', function() {
|
||||
node.log(RED._("tcpin.errors.timeout",{port:node.port}));
|
||||
socket.end();
|
||||
});
|
||||
socket.on('data', function(d) {
|
||||
// console.log("DATA",d)
|
||||
});
|
||||
socket.on('close',function() {
|
||||
node.log(RED._("tcpin.status.connection-closed",{host:socket.remoteAddress, port:socket.remotePort}));
|
||||
connectedSockets.splice(connectedSockets.indexOf(socket),1);
|
||||
@@ -388,6 +389,8 @@ module.exports = function(RED) {
|
||||
connectedSockets.splice(connectedSockets.indexOf(socket),1);
|
||||
node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})});
|
||||
});
|
||||
connectedSockets.push(socket);
|
||||
node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})});
|
||||
});
|
||||
|
||||
node.on("input", function(msg, nodeSend, nodeDone) {
|
||||
|
@@ -32,7 +32,7 @@ module.exports = function(RED) {
|
||||
var tag = node.tag;
|
||||
if (msg.hasOwnProperty("select")) { tag = node.tag || msg.select; }
|
||||
try {
|
||||
var $ = cheerio.load(value,null,false);
|
||||
var $ = cheerio.load(value);
|
||||
var pay = [];
|
||||
var count = 0;
|
||||
$(tag).each(function() {
|
||||
|
@@ -629,9 +629,6 @@ module.exports = function(RED) {
|
||||
if (node.build === 'object') {
|
||||
propertyKey = RED.util.getMessageProperty(msg,node.key);
|
||||
}
|
||||
if (msg.hasOwnProperty("parts")) {
|
||||
propertyIndex = msg.parts.index;
|
||||
}
|
||||
}
|
||||
|
||||
if (msg.hasOwnProperty("reset")) {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/nodes",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.6",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -17,7 +17,7 @@
|
||||
"dependencies": {
|
||||
"acorn": "8.4.1",
|
||||
"acorn-walk": "8.1.1",
|
||||
"ajv": "8.6.0",
|
||||
"ajv": "8.6.2",
|
||||
"body-parser": "1.19.0",
|
||||
"cheerio": "1.0.0-rc.10",
|
||||
"content-type": "1.0.4",
|
||||
@@ -37,7 +37,7 @@
|
||||
"js-yaml": "3.14.1",
|
||||
"media-typer": "1.1.0",
|
||||
"mqtt": "4.2.8",
|
||||
"multer": "1.4.2",
|
||||
"multer": "1.4.3",
|
||||
"mustache": "4.2.0",
|
||||
"on-headers": "1.0.2",
|
||||
"raw-body": "2.4.1",
|
||||
|
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var fs = require('fs-extra');
|
||||
var fs = require('fs');
|
||||
var fspath = require('path');
|
||||
|
||||
var runtime;
|
||||
@@ -25,7 +25,7 @@ var exampleFlows = null;
|
||||
async function getFlowsFromPath(path) {
|
||||
var result = {};
|
||||
var validFiles = [];
|
||||
return fs.readdir(path).then(files => {
|
||||
return fs.promises.readdir(path).then(files => {
|
||||
var promises = [];
|
||||
if (files) {
|
||||
files.forEach(function(file) {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/registry",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.6",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@@ -16,11 +16,11 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/util": "2.0.2",
|
||||
"@node-red/util": "2.0.6",
|
||||
"clone": "2.1.2",
|
||||
"fs-extra": "10.0.0",
|
||||
"semver": "7.3.5",
|
||||
"tar": "6.1.0",
|
||||
"uglify-js": "3.13.10"
|
||||
"tar": "6.1.11",
|
||||
"uglify-js": "3.14.1"
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/runtime",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.6",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@@ -16,9 +16,9 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/registry": "2.0.2",
|
||||
"@node-red/util": "2.0.2",
|
||||
"async-mutex": "0.3.1",
|
||||
"@node-red/registry": "2.0.6",
|
||||
"@node-red/util": "2.0.6",
|
||||
"async-mutex": "0.3.2",
|
||||
"clone": "2.1.2",
|
||||
"express": "4.17.1",
|
||||
"fs-extra": "10.0.0",
|
||||
|
@@ -23,7 +23,7 @@
|
||||
var i18n = require("i18next");
|
||||
|
||||
var path = require("path");
|
||||
var fs = require("fs-extra");
|
||||
var fs = require("fs");
|
||||
|
||||
var defaultLang = "en-US";
|
||||
|
||||
@@ -89,7 +89,7 @@ async function readFile(lng, ns) {
|
||||
return resourceCache[ns][lng];
|
||||
} else if (resourceMap[ns]) {
|
||||
const file = path.join(resourceMap[ns].basedir, lng, resourceMap[ns].file);
|
||||
const content = await fs.readFile(file, "utf8");
|
||||
const content = await fs.promises.readFile(file, "utf8");
|
||||
resourceCache[ns] = resourceCache[ns] || {};
|
||||
resourceCache[ns][lng] = JSON.parse(content.replace(/^\uFEFF/, ''));
|
||||
var baseLng = lng.split('-')[0];
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/util",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.6",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -18,7 +18,7 @@
|
||||
"fs-extra": "10.0.0",
|
||||
"i18next": "20.3.2",
|
||||
"json-stringify-safe": "5.0.1",
|
||||
"jsonata": "1.8.4",
|
||||
"jsonata": "1.8.5",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"moment-timezone": "0.5.33"
|
||||
}
|
||||
|
10
packages/node_modules/node-red/package.json
vendored
10
packages/node_modules/node-red/package.json
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.6",
|
||||
"description": "Low-code programming for event-driven applications",
|
||||
"homepage": "http://nodered.org",
|
||||
"license": "Apache-2.0",
|
||||
@@ -31,10 +31,10 @@
|
||||
"flow"
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/editor-api": "2.0.2",
|
||||
"@node-red/runtime": "2.0.2",
|
||||
"@node-red/util": "2.0.2",
|
||||
"@node-red/nodes": "2.0.2",
|
||||
"@node-red/editor-api": "2.0.6",
|
||||
"@node-red/runtime": "2.0.6",
|
||||
"@node-red/util": "2.0.6",
|
||||
"@node-red/nodes": "2.0.6",
|
||||
"basic-auth": "2.0.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"express": "4.17.1",
|
||||
|
7
packages/node_modules/node-red/red.js
vendored
7
packages/node_modules/node-red/red.js
vendored
@@ -234,8 +234,13 @@ httpsPromise.then(function(startupHttps) {
|
||||
// Get the result of the function, because createServer doesn't accept functions as input
|
||||
Promise.resolve(settings.https()).then(function(refreshedHttps) {
|
||||
if (refreshedHttps) {
|
||||
// The key/cert needs to be updated in the NodeJs http(s) server, when no key/cert is yet available or when the key/cert has changed.
|
||||
// Note that the refreshed key/cert can be supplied as a string or a buffer.
|
||||
var updateKey = (server.key == undefined || (Buffer.isBuffer(server.key) && !server.key.equals(refreshedHttps.key)) || (typeof server.key == "string" && server.key != refreshedHttps.key));
|
||||
var updateCert = (server.cert == undefined || (Buffer.isBuffer(server.cert) && !server.cert.equals(refreshedHttps.cert)) || (typeof server.cert == "string" && server.cert != refreshedHttps.cert));
|
||||
|
||||
// Only update the credentials in the server when key or cert has changed
|
||||
if(!server.key || !server.cert || !server.key.equals(refreshedHttps.key) || !server.cert.equals(refreshedHttps.cert)) {
|
||||
if(updateKey || updateCert) {
|
||||
server.setSecureContext(refreshedHttps);
|
||||
RED.log.info(RED.log._("server.https.settings-refreshed"));
|
||||
}
|
||||
|
@@ -4,6 +4,8 @@ const path = require("path");
|
||||
const fs = require("fs-extra");
|
||||
const should = require("should");
|
||||
|
||||
const LATEST = "2";
|
||||
|
||||
function generateScript() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const packages = [
|
||||
@@ -18,7 +20,13 @@ function generateScript() {
|
||||
const rootPackage = require(path.join(__dirname,"..","package.json"));
|
||||
const version = rootPackage.version;
|
||||
|
||||
const tagArg = /-/.test(version) ? "--tag next" : ""
|
||||
const versionParts = version.split(".");
|
||||
let tagArg = "";
|
||||
if (versionParts[0] !== LATEST) {
|
||||
tagArg = `--tag v${versionParts[0]}-maintenance`
|
||||
} else if (/-/.test(version)) {
|
||||
tagArg = "--tag next"
|
||||
}
|
||||
|
||||
const lines = [];
|
||||
|
||||
|
@@ -42,6 +42,8 @@ describe('HTTP Request Node', function() {
|
||||
var testProxyPort = 10444;
|
||||
var testProxyServerAuth;
|
||||
var testProxyAuthPort = 10554;
|
||||
var testSslClientServer;
|
||||
var testSslClientPort = 10664;
|
||||
|
||||
//save environment variables
|
||||
var preEnvHttpProxyLowerCase;
|
||||
@@ -57,6 +59,7 @@ describe('HTTP Request Node', function() {
|
||||
testServer = stoppable(http.createServer(testApp));
|
||||
testServer.listen(testPort,function(err) {
|
||||
testSslPort += 1;
|
||||
console.log("ssl port", testSslPort);
|
||||
var sslOptions = {
|
||||
key: fs.readFileSync('test/resources/ssl/server.key'),
|
||||
cert: fs.readFileSync('test/resources/ssl/server.crt')
|
||||
@@ -75,11 +78,29 @@ describe('HTTP Request Node', function() {
|
||||
*/
|
||||
};
|
||||
testSslServer = stoppable(https.createServer(sslOptions,testApp));
|
||||
testSslServer.listen(testSslPort);
|
||||
testSslServer.listen(testSslPort, function(err){
|
||||
if (err) {
|
||||
console.log(err);
|
||||
} else {
|
||||
console.log("started testSslServer");
|
||||
}
|
||||
});
|
||||
|
||||
testSslClientPort += 1;
|
||||
var sslClientOptions = {
|
||||
key: fs.readFileSync('test/resources/ssl/server.key'),
|
||||
cert: fs.readFileSync('test/resources/ssl/server.crt'),
|
||||
ca: fs.readFileSync('test/resources/ssl/server.crt'),
|
||||
requestCert: true
|
||||
};
|
||||
testSslClientServer = stoppable(https.createServer(sslClientOptions, testApp));
|
||||
testSslClientServer.listen(testSslClientPort, function(err){
|
||||
console.log("ssl-client", err)
|
||||
});
|
||||
|
||||
testProxyPort += 1;
|
||||
testProxyServer = stoppable(httpProxy(http.createServer()))
|
||||
|
||||
testProxyServer = stoppable(httpProxy(http.createServer()))
|
||||
|
||||
testProxyServer.on('request', function(req,res){
|
||||
if (!res.headersSent) {
|
||||
res.setHeader("x-testproxy-header", "foobar")
|
||||
@@ -121,6 +142,10 @@ describe('HTTP Request Node', function() {
|
||||
return "https://localhost:"+testSslPort+url;
|
||||
}
|
||||
|
||||
function getSslClientTestURL(url) {
|
||||
return "https://localhost:"+testSslClientPort+url;
|
||||
}
|
||||
|
||||
function getDifferentTestURL(url) {
|
||||
return "http://127.0.0.1:"+testPort+url;
|
||||
}
|
||||
@@ -267,6 +292,27 @@ describe('HTTP Request Node', function() {
|
||||
url: req.originalUrl
|
||||
});
|
||||
})
|
||||
testApp.get('/returnError/:code', function(req,res) {
|
||||
res.status(parseInt(req.params.code)).json({gotError:req.params.code});
|
||||
})
|
||||
|
||||
testApp.get('/rawHeaders', function(req,res) {
|
||||
const result = {};
|
||||
for (let i=0;i<req.rawHeaders.length;i++) {
|
||||
result[req.rawHeaders[i]] = req.rawHeaders[i+1]
|
||||
}
|
||||
res.json({
|
||||
headers:result
|
||||
});
|
||||
})
|
||||
|
||||
testApp.get('/getClientCert', function(req,res) {
|
||||
if (req.client.authorized) {
|
||||
res.send('hello');
|
||||
} else {
|
||||
res.status(401).send();
|
||||
}
|
||||
})
|
||||
startServer(function(err) {
|
||||
if (err) {
|
||||
done(err);
|
||||
@@ -280,7 +326,9 @@ describe('HTTP Request Node', function() {
|
||||
testProxyServer.stop(() => {
|
||||
testProxyServerAuth.stop(() => {
|
||||
testSslServer.stop(() => {
|
||||
helper.stopServer(done);
|
||||
testSslClientServer.stop(() => {
|
||||
helper.stopServer(done);
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1044,8 +1092,6 @@ describe('HTTP Request Node', function() {
|
||||
n1.receive({payload:"foo", requestTimeout: 100});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should append query params to url - obj', function(done) {
|
||||
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",paytoqs:true,ret:"obj",url:getTestURL('/getQueryParams')},
|
||||
{id:"n2", type:"helper"}];
|
||||
@@ -1068,6 +1114,84 @@ describe('HTTP Request Node', function() {
|
||||
n1.receive({payload:{a:1,b:2,c:3}});
|
||||
});
|
||||
});
|
||||
|
||||
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"}];
|
||||
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',{ gotError: '400' });
|
||||
msg.should.have.property('statusCode',400);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
n1.receive({});
|
||||
})
|
||||
});
|
||||
it('should send a msg for non-2xx response status - 404', function(done) {
|
||||
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/returnError/404')},
|
||||
{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',{ gotError: '404' });
|
||||
msg.should.have.property('statusCode',404);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
n1.receive({});
|
||||
})
|
||||
});
|
||||
it('should send a msg for non-2xx response status - 500', function(done) {
|
||||
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/returnError/500')},
|
||||
{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',{ gotError: '500' });
|
||||
msg.should.have.property('statusCode',500);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
n1.receive({});
|
||||
})
|
||||
});
|
||||
|
||||
it('should encode the url to handle special characters', function(done) {
|
||||
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj"},
|
||||
{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: 'b', c:[ 'T24,0°|H80%|W S8,3m/s' ] },
|
||||
url: '/getQueryParams?a=b&c[0].Text=T24,0%C2%B0|H80%25|W%20S8,3m/s'
|
||||
});
|
||||
msg.should.have.property('statusCode',200);
|
||||
msg.should.have.property('headers');
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
n1.receive({url: getTestURL('/getQueryParams')+"?a=b&c[0].Text=T24,0°|H80%|W%20S8,3m/s"});
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
describe('HTTP header', function() {
|
||||
@@ -1269,6 +1393,8 @@ describe('HTTP Request Node', function() {
|
||||
});
|
||||
|
||||
it('should convert all HTTP headers into lower case', function(done) {
|
||||
// This is a bad test. Express lower-cases headers in the `req.headers` object,
|
||||
// so this is actually testing express, not the original request.
|
||||
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')},
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(httpRequestNode, flow, function() {
|
||||
@@ -1290,6 +1416,26 @@ describe('HTTP Request Node', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should keep HTTP header case as provided by the user', function(done) {
|
||||
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/rawHeaders')},
|
||||
{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('statusCode',200);
|
||||
msg.payload.should.have.property('headers');
|
||||
msg.payload.headers.should.have.property('Content-Type').which.startWith('text/plain');
|
||||
msg.payload.headers.should.have.property('X-Test-HEAD', "foo");
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
n1.receive({payload:"foo", headers: { 'Content-Type':'text/plain', "X-Test-HEAD": "foo"}});
|
||||
});
|
||||
});
|
||||
it('should receive HTTP header', function(done) {
|
||||
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getTestURL('/headersInspect')},
|
||||
{id:"n2", type:"helper"}];
|
||||
@@ -1407,6 +1553,60 @@ describe('HTTP Request Node', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should use tls-config and verify serverCert', function(done) {
|
||||
var flow = [
|
||||
{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getSslTestURL('/text'),tls:"n3"},
|
||||
{id:"n2", type:"helper"},
|
||||
{id:"n3", type:"tls-config", cert:"test/resources/ssl/server.crt", key:"test/resources/ssl/server.key", ca:"test/resources/ssl/server.crt", verifyservercert:true}];
|
||||
var testNodes = [httpRequestNode, tlsNode];
|
||||
helper.load(testNodes, flow, function() {
|
||||
var n3 = helper.getNode("n3");
|
||||
var n2 = helper.getNode("n2");
|
||||
var n1 = helper.getNode("n1");
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.have.property('payload','hello');
|
||||
msg.should.have.property('statusCode',200);
|
||||
msg.should.have.property('headers');
|
||||
msg.headers.should.have.property('content-length',''+('hello'.length));
|
||||
msg.headers.should.have.property('content-type').which.startWith('text/html');
|
||||
msg.should.have.property('responseUrl').which.startWith('https://');
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
n1.receive({payload:"foo"});
|
||||
});
|
||||
});
|
||||
|
||||
it('should use tls-config and send client cert', function(done) {
|
||||
var flow = [
|
||||
{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"txt",url:getSslClientTestURL('/getClientCert'),tls:"n3"},
|
||||
{id:"n2", type:"helper"},
|
||||
{id:"n3", type:"tls-config", cert:"test/resources/ssl/server.crt", key:"test/resources/ssl/server.key", ca:"test/resources/ssl/server.crt", verifyservercert:false}];
|
||||
var testNodes = [httpRequestNode,tlsNode];
|
||||
helper.load(testNodes, flow, function() {
|
||||
var n3 = helper.getNode("n3");
|
||||
var n2 = helper.getNode("n2");
|
||||
var n1 = helper.getNode("n1");
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.have.property('payload','hello');
|
||||
msg.should.have.property('statusCode',200);
|
||||
msg.should.have.property('headers');
|
||||
msg.headers.should.have.property('content-length',''+('hello'.length));
|
||||
msg.headers.should.have.property('content-type').which.startWith('text/html');
|
||||
msg.should.have.property('responseUrl').which.startWith('https://');
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
n1.receive({payload:"foo"});
|
||||
})
|
||||
});
|
||||
|
||||
//Removing HTTP Proxy testcases as GOT + Proxy_Agent doesn't work with mock'd proxy
|
||||
/* */
|
||||
it('should use http_proxy', function(done) {
|
||||
@@ -1600,7 +1800,7 @@ describe('HTTP Request Node', function() {
|
||||
n1.receive({payload:"foo"});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
describe('authentication', function() {
|
||||
|
||||
@@ -1664,7 +1864,7 @@ describe('HTTP Request Node', function() {
|
||||
});
|
||||
});
|
||||
|
||||
// Removed the Proxy Tests until a new mock proxy can be replaced with
|
||||
// Removed the Proxy Tests until a new mock proxy can be replaced with
|
||||
// one that supports HTTP Connect verb
|
||||
/* */
|
||||
it('should authenticate on proxy server', function(done) {
|
||||
@@ -1771,7 +1971,7 @@ describe('HTTP Request Node', function() {
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('file-upload', function() {
|
||||
|
@@ -68,7 +68,9 @@ describe('HTML node', function() {
|
||||
done(err)
|
||||
}
|
||||
});
|
||||
n1.receive({payload:data,topic:"bar",select:"h1"});
|
||||
// include 'body' in the select to verify we're in document mode
|
||||
// for the parser. See https://github.com/node-red/node-red/issues/3079
|
||||
n1.receive({payload:data,topic:"bar",select:"body h1"});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -516,6 +516,28 @@ describe('JOIN node', function() {
|
||||
n1.receive({payload:{a:1}});
|
||||
});
|
||||
});
|
||||
it('should join things into an array ignoring msg.parts.index in manual mode', function(done) {
|
||||
var flow = [{id:"n1", type:"join", wires:[["n2"]], count:3, joiner:",",mode:"custom"},
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(joinNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.have.property("payload");
|
||||
msg.payload.should.be.an.Array();
|
||||
msg.payload[0].should.equal(1);
|
||||
msg.payload[1].should.equal(true);
|
||||
//msg.payload[2].a.should.equal(1);
|
||||
done();
|
||||
}
|
||||
catch(e) {done(e);}
|
||||
});
|
||||
n1.receive({payload:1, parts: {index: 3}});
|
||||
n1.receive({payload:true, parts: {index: 0}});
|
||||
n1.receive({payload:{a:1}, parts: {index: 9}});
|
||||
});
|
||||
});
|
||||
|
||||
it('should join things into an array after a count with a buffer join set', function(done) {
|
||||
var flow = [{id:"n1", type:"join", wires:[["n2"]], count:3, joinerType:"bin", joiner:"" ,mode:"custom"},
|
||||
@@ -1646,7 +1668,7 @@ describe('JOIN node', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle join an array when using msg.parts and duplicate indexed parts arrive', function (done) {
|
||||
it('should handle join an array when mode is auto and duplicate indexed parts arrive', function (done) {
|
||||
var flow = [{ id: "n1", type: "join", wires: [["n2"]], joiner: "[44]", joinerType: "bin", build: "array", mode: "auto" },
|
||||
{ id: "n2", type: "helper" }];
|
||||
helper.load(joinNode, flow, function () {
|
||||
|
Reference in New Issue
Block a user