Compare commits

..

48 Commits

Author SHA1 Message Date
Nick O'Leary
a40e5dbcd4 Add check that node sends object rather than primitive type
Fixes #3876
2022-10-04 11:49:49 +01:00
Nick O'Leary
93c1600980 Merge pull request #3886 from kazuhitoyokoi/master2
Limit number of ports in function node
2022-10-04 11:00:18 +01:00
Nick O'Leary
c3d1e6181f Merge pull request #3894 from hardillb/http-response-status-number
Ensure statusCode is a number
2022-10-04 10:28:04 +01:00
Ben Hardill
4115c13a65 Ensure statusCode is a number
Fixes #3893

Used parseInt instead of the suggested fix so that we don't end up
with statusCode = 200.5
2022-09-19 19:43:06 +01:00
Kazuhito Yokoi
ce31edc803 Fix handling of max and min values in function outputs 2022-09-18 02:22:52 +09:00
Kazuhito Yokoi
199caccbc3 Change the maximum number of ports to 500 2022-09-17 00:25:46 +09:00
Nick O'Leary
f060309002 Merge pull request #3872 from node-red-hitachi/fix-Japanese-editor-message-for-JSONata
Fix Japanese translation for JSONata editor
2022-09-15 21:25:30 +01:00
Nick O'Leary
fc3d86e6ff Merge pull request #3882 from kazuhitoyokoi/master-addenvinfo2template
Add information about environment variable to template node
2022-09-15 21:24:32 +01:00
Kazuhito Yokoi
7507a7b459 Limit number of ports in function node 2022-09-16 02:10:14 +09:00
Kazuhito Yokoi
d657817211 Add information about environment variable to template node 2022-09-13 22:08:22 +09:00
Hiroyasu Nishiyama
954649007c merge master 2022-09-13 10:52:48 +09:00
Nick O'Leary
b0d9903fe2 Merge pull request #3873 from node-red-hitachi/fix-describe-arg
Remove done from describe
2022-09-12 19:29:14 +01:00
Nick O'Leary
6375f3c445 Merge pull request #3841 from Steve-Mcl/fix-search-type-with-spaces
Fix search type with spaces
2022-09-12 19:27:09 +01:00
Nick O'Leary
2328f418be Merge pull request #3871 from node-red-hitachi/fix-extended-function-error-handling-of-jsonata-editor
Fix error hanndling of JSONata expression editor for extended functions
2022-09-12 19:26:15 +01:00
Nick O'Leary
22b6564847 Merge pull request #3880 from kazuhitoyokoi/master-replacedot4function
Remove dot from variable name for external module in function node
2022-09-12 19:25:39 +01:00
Nick O'Leary
25c8bfefe2 Merge pull request #3868 from Steve-Mcl/func-types-monaco-util-promisify
add function node monaco types util and promisify
2022-09-12 19:25:07 +01:00
Nick O'Leary
30f4524821 Merge pull request #3866 from kazuhitoyokoi/master-fixbutton4addsshkey
Add button type to the adding SSH key button
2022-09-12 19:18:49 +01:00
Nick O'Leary
9e4c3a7200 Merge pull request #3879 from kazuhitoyokoi/master-fixradiobutton4project
Check radio button as default in project dialog
2022-09-12 19:18:15 +01:00
Nick O'Leary
5b7e84c1b0 Merge pull request #3869 from Steve-Mcl/fix-mqtt-birth-bad-topic-crash
Prevent invalid mqtt birth topic crashing node-red
2022-09-12 19:17:43 +01:00
Nick O'Leary
e6097e4968 Merge pull request #3874 from node-red-hitachi/support-clone-in-monaco
Add $clone as supported function
2022-09-12 19:16:18 +01:00
Kazuhito Yokoi
0f2829097b Remove dot from variable name for external module in function node 2022-09-10 12:17:18 +09:00
Kazuhito Yokoi
e2a9f940e2 Check radio button as default in project dialog 2022-09-09 01:05:53 +09:00
Hiroyasu Nishiyama
3eb2b2ac5d add $clone as supported function 2022-09-06 18:52:13 +09:00
Hiroyasu Nishiyama
ce7b0a3b5e remove done from describe 2022-09-06 18:05:50 +09:00
Hiroyasu Nishiyama
7bd7c99dd4 Fix Japanese translation for JSONata editor 2022-09-06 16:53:38 +09:00
Hiroyasu Nishiyama
b0d12c4125 Fix error hanndling of JSONata expression editor for extennded functions 2022-09-06 15:38:49 +09:00
Kazuhito Yokoi
745607b5bc Add button type to buttons on node properties 2022-09-04 23:21:34 +09:00
Kazuhito Yokoi
cc5a770b16 Add button type to buttons on projects dialog 2022-09-04 21:58:02 +09:00
Steve-Mcl
a533943a40 add function node monaco types util and promisify
fixes #3851
2022-09-04 01:50:54 +01:00
Stephen McLaughlin
e11f17672c Update packages/node_modules/@node-red/editor-client/src/js/ui/palette.js 2022-09-03 22:01:54 +01:00
Stephen McLaughlin
c038c99f9d Update packages/node_modules/@node-red/editor-client/src/js/ui/search.js
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-09-03 21:54:41 +01:00
Stephen McLaughlin
5f159c1fbd Update packages/node_modules/@node-red/editor-client/src/js/ui/search.js
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-09-03 21:54:21 +01:00
Kazuhito Yokoi
cd8ca8981b Add button type to the adding SSH key button 2022-09-03 21:03:08 +09:00
Nick O'Leary
a5d7f7acce Merge pull request #3842 from hardillb/fix-broken-headers-tests
Add missing property to node object HTTPRequest
2022-09-02 20:46:22 +01:00
Nick O'Leary
a032c2e326 Merge pull request #3840 from Steve-Mcl/fix-mqtt-session-time
ensure sessionExpiry(Interval) is applied
2022-09-02 20:45:41 +01:00
Nick O'Leary
9d770ed436 Merge pull request #3857 from kazuhitoyokoi/master-fixeditablelist4httprequest
Support sortable list on property UI of http request and http response nodes
2022-09-02 20:45:08 +01:00
Nick O'Leary
ae753940f3 Merge pull request #3852 from kazuhitoyokoi/master-addjpn
Add Japanese translation for v3.0.2
2022-09-02 20:44:45 +01:00
Kazuhito Yokoi
d0d22c333c Add Japanese translation for v3.0.2 2022-08-28 02:04:13 +09:00
Kazuhito Yokoi
266ba17ebb Use sortable list in http response node and proxy setting 2022-08-18 11:52:56 +09:00
Kazuhito Yokoi
0d0d5bafb0 Make pre-defined header list sortable in http request node 2022-08-18 01:29:07 +09:00
Ben Hardill
58b951e134 Make port number dynamic in test 2022-08-14 15:09:48 +01:00
Ben Hardill
30956b5441 Add missing property to node object HTTPRequest
Also add tests for broken headers
2022-08-14 15:02:39 +01:00
Steve-Mcl
9540cfe749 fix flags search without val 2022-08-13 18:09:11 +01:00
Steve-Mcl
31b17faa2a fix MQTT test fail due to birth sent before connection done 2022-08-12 18:23:07 +01:00
Steve-Mcl
5c6b8e9e50 opportunistic tidy up MQTT tests 2022-08-12 18:21:36 +01:00
Steve-Mcl
5a36e8fb11 add tests for MQTT v5 (sessionExpiry property) 2022-08-12 18:20:11 +01:00
Steve-Mcl
7d4c857a43 ensure sessionExpiry(Interval) is applied 2022-08-12 15:47:15 +01:00
Steve-Mcl
598bcf675f fix searching by type when type name has a space 2022-08-12 15:45:12 +01:00
29 changed files with 322 additions and 132 deletions

View File

@@ -936,6 +936,9 @@
"invalid-expr": "Invalid JSONata expression:\n __message__",
"invalid-msg": "Invalid example JSON message:\n __message__",
"context-unsupported": "Cannot test context functions\n $flowContext or $globalContext",
"env-unsupported": "Cannot test $env function",
"moment-unsupported": "Cannot test $moment function",
"clone-unsupported": "Cannot test $clone function",
"eval": "Error evaluating expression:\n __message__"
}
},

View File

@@ -935,8 +935,11 @@
"errors": {
"invalid-expr": "不正なJSONata式:\n __message__",
"invalid-msg": "不正なJSONメッセージ例:\n __message__",
"context-unsupported": "$flowContext や $globalContextの\nコンテキスト機能をテストできません",
"eval": "表現評価エラー:\n __message__"
"context-unsupported": "$flowContext や $globalContextの\nコンテキスト関数をテストできません",
"env-unsupported": "$env関数はテストできません",
"moment-unsupported": "$moment関数はテストできません",
"clone-unsupported": "$clone関数はテストできません",
"eval": "式評価エラー:\n __message__"
}
},
"monaco": {
@@ -1168,8 +1171,7 @@
"takeATour": "ツアーを開始",
"start": "開始",
"next": "次へ",
"welcomeTours": "ウェルカムツアー",
"tours": "ツアー"
"welcomeTours": "ウェルカムツアー"
},
"diagnostics": {
"title": "システム情報"

View File

@@ -100,7 +100,7 @@ RED.editor.codeEditor.monaco = (function() {
"node-red-util": {package: "node-red", module: "util", path: "node-red/util.d.ts" },
"node-red-func": {package: "node-red", module: "func", path: "node-red/func.d.ts" },
}
const defaultServerSideTypes = [ knownModules["node-red-util"], knownModules["node-red-func"], knownModules["globals"], knownModules["console"], knownModules["buffer"] ];
const defaultServerSideTypes = [ knownModules["node-red-util"], knownModules["node-red-func"], knownModules["globals"], knownModules["console"], knownModules["buffer"] , knownModules["util"] ];
const modulesCache = {};

View File

@@ -255,6 +255,9 @@
var currentExpression = expressionEditor.getValue();
var expr;
var usesContext = false;
var usesEnv = false;
var usesMoment = false;
var usesClone = false;
var legacyMode = /(^|[^a-zA-Z0-9_'".])msg([^a-zA-Z0-9_'"]|$)/.test(currentExpression);
$(".red-ui-editor-type-expression-legacy").toggle(legacyMode);
try {
@@ -267,6 +270,18 @@
usesContext = true;
return null;
});
expr.assign("env", function(name) {
usesEnv = true;
return null;
});
expr.assign("moment", function(name) {
usesMoment = true;
return null;
});
expr.assign("clone", function(name) {
usesClone = true;
return null;
});
} catch(err) {
testResultEditor.setValue(RED._("expressionEditor.errors.invalid-expr",{message:err.message}),-1);
return;
@@ -284,6 +299,18 @@
testResultEditor.setValue(RED._("expressionEditor.errors.context-unsupported"),-1);
return;
}
if (usesEnv) {
testResultEditor.setValue(RED._("expressionEditor.errors.env-unsupported"),-1);
return;
}
if (usesMoment) {
testResultEditor.setValue(RED._("expressionEditor.errors.moment-unsupported"),-1);
return;
}
if (usesClone) {
testResultEditor.setValue(RED._("expressionEditor.errors.clone-unsupported"),-1);
return;
}
var formattedResult;
if (result !== undefined) {

View File

@@ -175,9 +175,19 @@ RED.palette = (function() {
$('<button type="button" onclick="RED.workspaces.show(\''+type.substring(8).replace(/'/g,"\\'")+'\'); return false;" class="red-ui-button red-ui-button-small" style="float: right; margin-left: 5px;"><i class="fa fa-pencil"></i></button>').appendTo(popOverContent)
}
var safeType = type.replace(/'/g,"\\'");
const safeType = type.replace(/'/g,"\\'");
const wrapStr = function (str) {
if(str.indexOf(' ') >= 0) {
return '"' + str + '"'
}
return str
}
$('<button type="button" onclick="RED.search.show(\'type:'+safeType+'\'); return false;" class="red-ui-button red-ui-button-small" style="float: right; margin-left: 5px;"><i class="fa fa-search"></i></button>').appendTo(popOverContent)
$('<button type="button"; return false;" class="red-ui-button red-ui-button-small" style="float: right; margin-left: 5px;"><i class="fa fa-search"></i></button>')
.appendTo(popOverContent)
.on('click', function() {
RED.search.show('type:' + wrapStr(safeType))
})
$('<button type="button" onclick="RED.sidebar.help.show(\''+safeType+'\'); return false;" class="red-ui-button red-ui-button-small" style="float: right; margin-left: 5px;"><i class="fa fa-book"></i></button>').appendTo(popOverContent)
$('<p>',{style:"font-size: 0.8em"}).text(metaData).appendTo(popOverContent);

View File

@@ -545,7 +545,7 @@ RED.projects = (function() {
var sshwarningRow = $('<div class="red-ui-projects-dialog-screen-create-row-auth-error-no-keys"></div>').hide().appendTo(subrow);
$('<div class="form-row"><i class="fa fa-warning"></i> '+RED._("projects.clone-project.ssh-key-desc")+'</div>').appendTo(sshwarningRow);
subrow = $('<div style="text-align: center">').appendTo(sshwarningRow);
$('<button class="red-ui-button red-ui-projects-dialog-button">'+RED._("projects.clone-project.ssh-key-add")+'</button>').appendTo(subrow).on("click", function(e) {
$('<button type="button" class="red-ui-button red-ui-projects-dialog-button">'+RED._("projects.clone-project.ssh-key-add")+'</button>').appendTo(subrow).on("click", function(e) {
e.preventDefault();
dialog.dialog( "close" );
RED.userSettings.show('gitconfig');
@@ -1171,11 +1171,11 @@ RED.projects = (function() {
row = $('<div class="form-row button-group"></div>').appendTo(container);
var openProject = $('<button data-type="open" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-folder-open"></i><br/>'+RED._("projects.create.open")+'</button>').appendTo(row);
var createAsEmpty = $('<button data-type="empty" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>'+RED._("projects.create.create")+'</button>').appendTo(row);
// var createAsCopy = $('<button data-type="copy" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i class="fa fa-long-arrow-right fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Copy existing</button>').appendTo(row);
var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>'+RED._("projects.create.clone")+'</button>').appendTo(row);
// var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-git fa-2x"></i><i class="fa fa-arrows-h fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Clone Repository</button>').appendTo(row);
var openProject = $('<button type="button" data-type="open" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-folder-open"></i><br/>'+RED._("projects.create.open")+'</button>').appendTo(row);
var createAsEmpty = $('<button type="button" data-type="empty" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>'+RED._("projects.create.create")+'</button>').appendTo(row);
// var createAsCopy = $('<button type="button" data-type="copy" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i class="fa fa-long-arrow-right fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Copy existing</button>').appendTo(row);
var createAsClone = $('<button type="button" data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>'+RED._("projects.create.clone")+'</button>').appendTo(row);
// var createAsClone = $('<button type="button" data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-git fa-2x"></i><i class="fa fa-arrows-h fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Clone Repository</button>').appendTo(row);
row.find(".red-ui-projects-dialog-screen-create-type").on("click", function(evt) {
evt.preventDefault();
container.find(".red-ui-projects-dialog-screen-create-type").removeClass('selected');
@@ -1271,7 +1271,7 @@ RED.projects = (function() {
var credentialsLeftBox = $('<div class="red-ui-projects-dialog-credentials-box-left">').appendTo(credentialsBox);
var credentialsEnabledBox = $('<div class="form-row red-ui-projects-dialog-credentials-box-enabled"></div>').appendTo(credentialsLeftBox);
$('<label class="red-ui-projects-edit-form-inline-label"><input type="radio" name="projects-encryption-type" value="enabled"> <i class="fa fa-lock"></i> <span>'+RED._("projects.encryption-config.enable")+'</span></label>').appendTo(credentialsEnabledBox);
$('<label class="red-ui-projects-edit-form-inline-label"><input type="radio" name="projects-encryption-type" value="enabled" checked> <i class="fa fa-lock"></i> <span>'+RED._("projects.encryption-config.enable")+'</span></label>').appendTo(credentialsEnabledBox);
var credentialsDisabledBox = $('<div class="form-row red-ui-projects-dialog-credentials-box-disabled disabled"></div>').appendTo(credentialsLeftBox);
$('<label class="red-ui-projects-edit-form-inline-label"><input type="radio" name="projects-encryption-type" value="disabled"> <i class="fa fa-unlock"></i> <span>'+RED._("projects.encryption-config.disable")+'</span></label>').appendTo(credentialsDisabledBox);
@@ -1397,7 +1397,7 @@ RED.projects = (function() {
var sshwarningRow = $('<div class="red-ui-projects-dialog-screen-create-row-auth-error-no-keys"></div>').hide().appendTo(subrow);
$('<div class="form-row"><i class="fa fa-warning"></i> '+RED._("projects.create.desc2")+'</div>').appendTo(sshwarningRow);
subrow = $('<div style="text-align: center">').appendTo(sshwarningRow);
$('<button class="red-ui-button red-ui-projects-dialog-button">'+RED._("projects.create.add-ssh-key")+'</button>').appendTo(subrow).on("click", function(e) {
$('<button type="button" class="red-ui-button red-ui-projects-dialog-button">'+RED._("projects.create.add-ssh-key")+'</button>').appendTo(subrow).on("click", function(e) {
e.preventDefault();
$('#red-ui-projects-dialog-cancel').trigger("click");
RED.userSettings.show('gitconfig');
@@ -1631,14 +1631,14 @@ RED.projects = (function() {
function deleteProject(row,name,done) {
var cover = $('<div class="red-ui-projects-dialog-project-list-entry-delete-confirm"></div>').on("click", function(evt) { evt.stopPropagation(); }).appendTo(row);
$('<span>').text(RED._("projects.delete.confirm")).appendTo(cover);
$('<button class="red-ui-button red-ui-projects-dialog-button">'+RED._("common.label.cancel")+'</button>')
$('<button type="button" class="red-ui-button red-ui-projects-dialog-button">'+RED._("common.label.cancel")+'</button>')
.appendTo(cover)
.on("click", function(e) {
e.stopPropagation();
cover.remove();
done(true);
});
$('<button class="red-ui-button red-ui-projects-dialog-button primary">'+RED._("common.label.delete")+'</button>')
$('<button type="button" class="red-ui-button red-ui-projects-dialog-button primary">'+RED._("common.label.delete")+'</button>')
.appendTo(cover)
.on("click", function(e) {
e.stopPropagation();
@@ -1822,7 +1822,7 @@ RED.projects = (function() {
header.addClass("selectable");
var tools = $('<div class="red-ui-projects-dialog-project-list-entry-tools"></div>').appendTo(header);
$('<button class="red-ui-button red-ui-projects-dialog-button red-ui-button-small" style="float: right;"><i class="fa fa-trash"></i></button>')
$('<button type="button" class="red-ui-button red-ui-projects-dialog-button red-ui-button-small" style="float: right;"><i class="fa fa-trash"></i></button>')
.appendTo(tools)
.on("click", function(e) {
e.stopPropagation();

View File

@@ -106,38 +106,51 @@ RED.search = (function() {
return val;
}
function search(val) {
var results = [];
var typeFilter;
var m = /(?:^| )type:([^ ]+)/.exec(val);
if (m) {
val = val.replace(/(?:^| )type:[^ ]+/,"");
typeFilter = m[1];
function extractType(val, flags) {
// extracts: type:XYZ & type:"X Y Z"
const regEx = /(?:type):\s*(?:"([^"]+)"|([^" ]+))/;
let m
while ((m = regEx.exec(val)) !== null) {
// avoid infinite loops with zero-width matches
if (m.index === regEx.lastIndex) {
regEx.lastIndex++;
}
val = val.replace(m[0]," ").trim()
const flag = m[2] || m[1] // quoted entries in capture group 1, unquoted in capture group 2
flags.type = flags.type || [];
flags.type.push(flag);
}
var flags = {};
return val;
}
function search(val) {
const results = [];
const flags = {};
val = extractFlag(val,"invalid",flags);
val = extractFlag(val,"unused",flags);
val = extractFlag(val,"config",flags);
val = extractFlag(val,"subflow",flags);
val = extractFlag(val,"hidden",flags);
val = extractFlag(val,"modified",flags);
val = extractValue(val,"flow",flags);// flow:active or flow:<flow-id>
val = extractValue(val,"flow",flags);// flow:current or flow:<flow-id>
val = extractValue(val,"uses",flags);// uses:<node-id>
val = extractType(val,flags);// type:<node-type>
val = val.trim();
var hasFlags = Object.keys(flags).length > 0;
const hasFlags = Object.keys(flags).length > 0;
const hasTypeFilter = flags.type && flags.type.length > 0
if (flags.flow && flags.flow.indexOf("current") >= 0) {
let idx = flags.flow.indexOf("current");
flags.flow[idx] = RED.workspaces.active();//convert active to flow ID
flags.flow[idx] = RED.workspaces.active();//convert 'current' to active flow ID
}
if (flags.flow && flags.flow.length) {
flags.flow = [ ...new Set(flags.flow) ]; //deduplicate
}
if (val.length > 0 || typeFilter || hasFlags) {
if (val.length > 0 || hasFlags) {
val = val.toLowerCase();
var i;
var j;
var list = [];
var nodes = {};
let i;
let j;
let list = [];
const nodes = {};
let keys = [];
if (flags.uses) {
keys = flags.uses;
@@ -145,10 +158,10 @@ RED.search = (function() {
keys = Object.keys(index);
}
for (i=0;i<keys.length;i++) {
var key = keys[i];
var kpos = keys[i].indexOf(val);
if (kpos > -1) {
var ids = Object.keys(index[key]||{});
const key = keys[i];
const kpos = val ? keys[i].indexOf(val) : -1;
if (kpos > -1 || (val === "" && hasFlags)) {
const ids = Object.keys(index[key]||{});
for (j=0;j<ids.length;j++) {
var node = index[key][ids[j]];
var isConfigNode = node.node._def.category === "config" && node.node.type !== 'group';
@@ -156,7 +169,7 @@ RED.search = (function() {
continue;
}
if (flags.hasOwnProperty("invalid")) {
var nodeIsValid = !node.node.hasOwnProperty("valid") || node.node.valid;
const nodeIsValid = !node.node.hasOwnProperty("valid") || node.node.valid;
if (flags.invalid === nodeIsValid) {
continue;
}
@@ -186,7 +199,7 @@ RED.search = (function() {
}
}
if (flags.hasOwnProperty("unused")) {
var isUnused = (node.node.type === 'subflow' && node.node.instances.length === 0) ||
const isUnused = (node.node.type === 'subflow' && node.node.instances.length === 0) ||
(isConfigNode && node.node.users.length === 0 && node.node._def.hasUsers !== false)
if (flags.unused !== isUnused) {
continue;
@@ -197,12 +210,16 @@ RED.search = (function() {
continue;
}
}
if (!typeFilter || node.node.type === typeFilter) {
nodes[node.node.id] = nodes[node.node.id] = {
let typeIndex = -1
if(hasTypeFilter) {
typeIndex = flags.type.indexOf(node.node.type)
}
if (!hasTypeFilter || typeIndex > -1) {
nodes[node.node.id] = nodes[node.node.id] || {
node: node.node,
label: node.label
};
nodes[node.node.id].index = Math.min(nodes[node.node.id].index||Infinity,kpos);
nodes[node.node.id].index = Math.min(nodes[node.node.id].index || Infinity, typeIndex > -1 ? typeIndex : kpos);
}
}
}

View File

@@ -14,6 +14,9 @@ declare var msg: NodeMessage;
/** @type {string} the id of the incoming `msg` (alias of msg._msgid) */
declare const __msgid__:string;
declare const util:typeof import('util')
declare const promisify:typeof import('util').promisify
/**
* @typedef NodeStatus
* @type {object}

View File

@@ -160,6 +160,7 @@
'$base64encode':{ args:[ ]},
'$boolean':{ args:[ 'arg' ]},
'$ceil':{ args:[ 'number' ]},
'$clone': { args:[ 'arg' ]},
'$contains':{ args:[ 'str', 'pattern' ]},
'$count':{ args:[ 'array' ]},
'$decodeUrl':{ args:[ 'str' ]},

View File

@@ -1,6 +1,6 @@
<script type="text/html" data-template-name="complete">
<div class="form-row node-input-target-row">
<button id="node-input-complete-target-select" class="red-ui-button" data-i18n="common.label.selectNodes"></button>
<button type="button" id="node-input-complete-target-select" class="red-ui-button" data-i18n="common.label.selectNodes"></button>
</div>
<div class="form-row node-input-target-row node-input-target-list-row" style="position: relative; min-height: 100px">
<div style="position: absolute; top: -30px; right: 0;"><input type="text" id="node-input-complete-target-filter"></div>

View File

@@ -12,7 +12,7 @@
<label for="node-input-uncaught" style="width: auto" data-i18n="catch.label.uncaught"></label>
</div>
<div class="form-row node-input-target-row">
<button id="node-input-catch-target-select" class="red-ui-button" data-i18n="common.label.selectNodes"></button>
<button type="button" id="node-input-catch-target-select" class="red-ui-button" data-i18n="common.label.selectNodes"></button>
</div>
<div class="form-row node-input-target-row node-input-target-list-row" style="position: relative; min-height: 100px">
<div style="position: absolute; top: -30px; right: 0;"><input type="text" id="node-input-catch-target-filter"></div>

View File

@@ -8,7 +8,7 @@
</select>
</div>
<div class="form-row node-input-target-row">
<button id="node-input-status-target-select" class="red-ui-button" data-i18n="common.label.selectNodes"></button>
<button type="button" id="node-input-status-target-select" class="red-ui-button" data-i18n="common.label.selectNodes"></button>
</div>
<div class="form-row node-input-target-row node-input-target-list-row" style="position: relative; min-height: 100px">
<div style="position: absolute; top: -30px; right: 0;"><input type="text" id="node-input-status-target-filter"></div>

View File

@@ -91,21 +91,21 @@
<div id="func-tab-init" style="display:none">
<div class="form-row node-text-editor-row" style="position:relative">
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-init-editor" ></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button id="node-init-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button type="button" id="node-init-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
</div>
</div>
<div id="func-tab-body" style="display:none">
<div class="form-row node-text-editor-row" style="position:relative">
<div style="height: 220px; min-height:150px;" class="node-text-editor" id="node-input-func-editor" ></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button id="node-function-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button type="button" id="node-function-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
</div>
</div>
<div id="func-tab-finalize" style="display:none">
<div class="form-row node-text-editor-row" style="position:relative">
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-finalize-editor" ></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button id="node-finalize-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button type="button" id="node-finalize-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
</div>
</div>
@@ -294,7 +294,7 @@
if (val === "_custom_") {
val = $(this).val();
}
var varName = val.trim().replace(/^@/,"").replace(/@.*$/,"").replace(/[-_/].?/g, function(v) { return v[1]?v[1].toUpperCase():"" });
var varName = val.trim().replace(/^@/,"").replace(/@.*$/,"").replace(/[-_/\.].?/g, function(v) { return v[1]?v[1].toUpperCase():"" });
fvar.val(varName);
fvar.trigger("change");
@@ -451,11 +451,13 @@
tabs.activateTab("func-tab-body");
$( "#node-input-outputs" ).spinner({
min:0,
min: 0,
max: 500,
change: function(event, ui) {
var value = this.value;
if (!value.match(/^\d+$/)) { value = 1; }
else if (value < this.min) { value = this.min; }
var value = parseInt(this.value);
value = isNaN(value) ? 1 : value;
value = Math.max(value, parseInt($(this).attr("aria-valuemin")));
value = Math.min(value, parseInt($(this).attr("aria-valuemax")));
if (value !== this.value) { $(this).spinner("value", value); }
}
});

View File

@@ -26,7 +26,7 @@
<option value="yaml">YAML</option>
<option value="text" data-i18n="template.label.none"></option>
</select>
<button id="node-template-expand-editor" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button>
<button type="button" id="node-template-expand-editor" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button>
</div>
</div>
<div class="form-row node-text-editor-row">

View File

@@ -25,7 +25,7 @@
<label class="red-ui-button" for="node-config-input-certfile"><i class="fa fa-upload"></i> <span data-i18n="tls.label.upload"></span></label>
<input class="hide" type="file" id="node-config-input-certfile">
<span id="tls-config-certname" style="width: calc(100% - 280px); overflow: hidden; line-height:34px; height:34px; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: middle;"> </span>
<button class="red-ui-button red-ui-button-small" id="tls-config-button-cert-clear" style="margin-left: 10px"><i class="fa fa-times"></i></button>
<button type="button" class="red-ui-button red-ui-button-small" id="tls-config-button-cert-clear" style="margin-left: 10px"><i class="fa fa-times"></i></button>
</span>
<input type="hidden" id="node-config-input-certname">
<input type="hidden" id="node-config-input-certdata">
@@ -37,7 +37,7 @@
<label class="red-ui-button" for="node-config-input-keyfile"><i class="fa fa-upload"></i> <span data-i18n="tls.label.upload"></span></label>
<input class="hide" type="file" id="node-config-input-keyfile">
<span id="tls-config-keyname" style="width: calc(100% - 280px); overflow: hidden; line-height:34px; height:34px; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: middle;"> </span>
<button class="red-ui-button red-ui-button-small" id="tls-config-button-key-clear" style="margin-left: 10px"><i class="fa fa-times"></i></button>
<button type="button" class="red-ui-button red-ui-button-small" id="tls-config-button-key-clear" style="margin-left: 10px"><i class="fa fa-times"></i></button>
</span>
<input type="hidden" id="node-config-input-keyname">
<input type="hidden" id="node-config-input-keydata">
@@ -53,7 +53,7 @@
<label class="red-ui-button" for="node-config-input-cafile"><i class="fa fa-upload"></i> <span data-i18n="tls.label.upload"></span></label>
<input class="hide" type="file" title=" " id="node-config-input-cafile">
<span id="tls-config-caname" style="width: calc(100% - 280px); overflow: hidden; line-height:34px; height:34px; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: middle;"> </span>
<button class="red-ui-button red-ui-button-small" id="tls-config-button-ca-clear" style="margin-left: 10px"><i class="fa fa-times"></i></button>
<button type="button" class="red-ui-button red-ui-button-small" id="tls-config-button-ca-clear" style="margin-left: 10px"><i class="fa fa-times"></i></button>
</span>
<input type="hidden" id="node-config-input-caname">
<input type="hidden" id="node-config-input-cadata">

View File

@@ -101,6 +101,7 @@
hostField.val(data.host);
}
},
sortable: true,
removable: true
});
if (this.noproxy) {

View File

@@ -459,6 +459,7 @@ 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);
@@ -467,12 +468,12 @@ 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);
setIfHasProperty(opts, node, "keepalive", init);
setIfHasProperty(opts, node, "cleansession", init);
setIfHasProperty(opts, node, "sessionExpiry", init);
setIfHasProperty(opts, node, "topicAliasMaximum", init);
setIfHasProperty(opts, node, "maximumPacketSize", init);
setIfHasProperty(opts, node, "receiveMaximum", init);
@@ -482,6 +483,11 @@ module.exports = function(RED) {
} else if (hasProperty(opts, "userProps")) {
node.userProperties = opts.userProps;
}
if (hasProperty(opts, "sessionExpiry")) {
node.sessionExpiryInterval = opts.sessionExpiry;
} else if (hasProperty(opts, "sessionExpiryInterval")) {
node.sessionExpiryInterval = opts.sessionExpiryInterval
}
function createLWT(topic, payload, qos, retain, v5opts, v5SubPropName) {
let message = undefined;
@@ -565,6 +571,9 @@ 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;
}
@@ -777,7 +786,9 @@ module.exports = function(RED) {
// Send any birth message
if (node.birthMessage) {
node.publish(node.birthMessage);
setTimeout(() => {
node.publish(node.birthMessage);
}, 1);
}
});
node._clientOn("reconnect", function() {
@@ -989,7 +1000,7 @@ module.exports = function(RED) {
node.client.publish(msg.topic, msg.payload, options, function (err) {
if (done) {
done(err)
} else if(err) {
} else {
node.error(err, msg)
}
})

View File

@@ -227,6 +227,7 @@
}
});
},
sortable: true,
removable: true
});

View File

@@ -282,7 +282,7 @@ module.exports = function(RED) {
RED.nodes.createNode(this,n);
var node = this;
this.headers = n.headers||{};
this.statusCode = n.statusCode;
this.statusCode = parseInt(n.statusCode);
this.on("input",function(msg,_send,done) {
if (msg.res) {
var headers = RED.util.cloneMessage(node.headers);
@@ -323,7 +323,7 @@ module.exports = function(RED) {
}
}
}
var statusCode = node.statusCode || msg.statusCode || 200;
var statusCode = node.statusCode || parseInt(msg.statusCode) || 200;
if (typeof msg.payload == "object" && !Buffer.isBuffer(msg.payload)) {
msg.res._res.status(statusCode).jsonp(msg.payload);
} else {

View File

@@ -417,6 +417,7 @@
});
},
sortable: true,
removable: true
});
if (node.headers) {

View File

@@ -86,6 +86,7 @@ in your Node-RED user directory (${RED.settings.userDir}).
if (n.paytoqs === true || n.paytoqs === "query") { paytoqs = true; }
else if (n.paytoqs === "body") { paytobody = true; }
node.insecureHTTPParser = n.insecureHTTPParser
var prox, noprox;
if (process.env.http_proxy) { prox = process.env.http_proxy; }

View File

@@ -52,4 +52,7 @@
used to mark the templated sections. For example, to use <code>[[ ]]</code>
instead, add the following line to the top of the template:</p>
<pre>{{=[[ ]]=}}</pre>
<h4>Using environment variables</h4>
<p>The template node can access environment variables using the syntax:</p>
<pre>My favourite colour is {{env.COLOUR}}.</pre>
</script>

View File

@@ -48,4 +48,7 @@
<p><b>: </b>デフォルトでは、<i>mustache</i>形式は置換対象のHTML要素をエスケープしますこれを抑止するには<code>{{{三重}}}</code>使</p>
<p>もしコンテンツの中で<code>{{ }}</code>使<code>[[ ]]</code></p>
<pre>{{=[[ ]]=}}</pre>
<h4>環境変数の利用</h4>
<p>templateードでは次の構文を用いると環境変数にアクセスできます:</p>
<pre>私の好きな色は{{env.COLOUR}}です</pre>
</script>

View File

@@ -554,7 +554,8 @@
},
"status": {
"requesting": "要求中"
}
},
"insecureHTTPParser": "厳密なHTTPパース処理を無効化"
},
"websocket": {
"label": {

View File

@@ -373,6 +373,11 @@ Node.prototype.send = function(msg) {
if (msg === null || typeof msg === "undefined") {
return;
} else if (!util.isArray(msg)) {
// A single message has been passed in
if (typeof msg !== 'object') {
this.error(Log._("nodes.flow.non-message-returned", { type: typeof msg }));
return
}
if (this._wire) {
// A single message and a single wire on output 0
// TODO: pre-load flows.get calls - cannot do in constructor
@@ -425,27 +430,31 @@ Node.prototype.send = function(msg) {
for (k = 0; k < msgs.length; k++) {
var m = msgs[k];
if (m !== null && m !== undefined) {
if (!m._msgid) {
hasMissingIds = true;
if (typeof m !== 'object') {
this.error(Log._("nodes.flow.non-message-returned", { type: typeof m }));
} else {
if (!m._msgid) {
hasMissingIds = true;
}
/* istanbul ignore else */
if (!sentMessageId) {
sentMessageId = m._msgid;
}
sendEvents.push({
msg: m,
source: {
id: this.id,
node: this,
port: i
},
destination: {
id: wires[j],
node: undefined
},
cloneMessage: msgSent
});
msgSent = true;
}
/* istanbul ignore else */
if (!sentMessageId) {
sentMessageId = m._msgid;
}
sendEvents.push({
msg: m,
source: {
id: this.id,
node: this,
port: i
},
destination: {
id: wires[j],
node: undefined
},
cloneMessage: msgSent
});
msgSent = true;
}
}
}

View File

@@ -134,7 +134,8 @@
"flow": {
"unknown-type": "Unknown type: __type__",
"missing-types": "missing types",
"error-loop": "Message exceeded maximum number of catches"
"error-loop": "Message exceeded maximum number of catches",
"non-message-returned": "Node tried to send a message of type __type__"
},
"index": {
"unrecognised-id": "Unrecognised id: __id__",

View File

@@ -31,6 +31,8 @@ var multer = require("multer");
var RED = require("nr-test-utils").require("node-red/lib/red");
var fs = require('fs-extra');
var auth = require('basic-auth');
const { version } = require("os");
const net = require('net')
describe('HTTP Request Node', function() {
var testApp;
@@ -2265,4 +2267,71 @@ describe('HTTP Request Node', function() {
});
});
});
describe('should parse broken headers', function() {
const versions = process.versions.node.split('.')
if (( versions[0] == 14 && versions[1] >= 20 ) ||
( versions[0] == 16 && versions[1] >= 16 ) ||
( versions[0] == 18 && versions[1] >= 5 ) ||
( versions[0] > 18)) {
// only test if on new enough NodeJS version
let port = testPort++
let server;
before(function() {
server = net.createServer(function (socket) {
socket.write("HTTP/1.0 200\nContent-Type: text/plain\n\nHelloWorld")
socket.end()
})
server.listen(port,'127.0.0.1', function(err) {
})
});
after(function() {
server.close()
});
it('should accept broken headers', function (done) {
var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:`http://localhost:${port}/`, insecureHTTPParser: true},
{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.payload.should.equal('HelloWorld')
done()
} catch (err) {
done(err)
}
})
n1.receive({payload: 'foo'})
});
});
it('should reject broken headers', function (done) {
var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:`http://localhost:${port}/`},
{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.payload.should.equal(`RequestError: Parse Error: Missing expected CR after header value : http://localhost:${port}/`)
done()
} catch (err) {
done(err)
}
})
n1.receive({payload: 'foo'})
});
});
}
});
});

View File

@@ -27,7 +27,7 @@ describe('MQTT Nodes', function () {
} catch (error) { }
});
it('should be loaded and have default values', function (done) {
it('should be loaded and have default values (MQTT V4)', function (done) {
this.timeout = 2000;
const { flow, nodes } = buildBasicMQTTSendRecvFlow({ id: "mqtt.broker", name: "mqtt_broker", autoConnect: false }, { id: "mqtt.in", topic: "in_topic" }, { id: "mqtt.out", topic: "out_topic" });
helper.load(mqttNodes, flow, function () {
@@ -61,6 +61,52 @@ describe('MQTT Nodes', function () {
mqttBroker.options.clientId.should.containEql('nodered_');
mqttBroker.options.should.have.property('keepalive').type("number");
mqttBroker.options.should.have.property('reconnectPeriod').type("number");
//as this is not a v5 connection, ensure v5 properties are not present
mqttBroker.options.should.not.have.property('protocolVersion', 5);
mqttBroker.options.should.not.have.property('properties');
done();
} catch (error) {
done(error)
}
});
});
it('should be loaded and have default values (MQTT V5)', function (done) {
this.timeout = 2000;
const { flow, nodes } = buildBasicMQTTSendRecvFlow({ id: "mqtt.broker", name: "mqtt_broker", autoConnect: false, cleansession: false, clientid: 'clientid', keepalive: 35, sessionExpiry: '6000', protocolVersion: '5', userProps: {"prop": "val"}}, { id: "mqtt.in", topic: "in_topic" }, { id: "mqtt.out", topic: "out_topic" });
helper.load(mqttNodes, flow, function () {
try {
const mqttIn = helper.getNode("mqtt.in");
const mqttOut = helper.getNode("mqtt.out");
const mqttBroker = helper.getNode("mqtt.broker");
should(mqttIn).be.type("object", "mqtt in node should be an object")
mqttIn.should.have.property('broker', nodes.mqtt_broker.id); //should be the id of the broker node
mqttIn.should.have.property('datatype', 'utf8'); //default: 'utf8'
mqttIn.should.have.property('isDynamic', false); //default: false
mqttIn.should.have.property('inputs', 0); //default: 0
mqttIn.should.have.property('qos', 2); //default: 2
mqttIn.should.have.property('topic', "in_topic");
mqttIn.should.have.property('wires', [["helper.node"]]);
should(mqttOut).be.type("object", "mqtt out node should be an object")
mqttOut.should.have.property('broker', nodes.mqtt_broker.id); //should be the id of the broker node
mqttOut.should.have.property('topic', "out_topic");
should(mqttBroker).be.type("object", "mqtt broker node should be an object")
mqttBroker.should.have.property('broker', BROKER_HOST);
mqttBroker.should.have.property('port', BROKER_PORT);
mqttBroker.should.have.property('brokerurl');
// mqttBroker.should.have.property('autoUnsubscribe', true);//default: true
mqttBroker.should.have.property('autoConnect', false);//Set "autoConnect:false" in brokerOptions
mqttBroker.should.have.property('options');
mqttBroker.options.should.have.property('clean', false);
mqttBroker.options.should.have.property('clientId', 'clientid');
mqttBroker.options.should.have.property('keepalive').type("number", 35);
mqttBroker.options.should.have.property('reconnectPeriod').type("number");
//as this IS a v5 connection, ensure v5 properties are not present
mqttBroker.options.should.have.property('protocolVersion', 5);
mqttBroker.options.should.have.property('properties');
mqttBroker.options.properties.should.have.property('sessionExpiryInterval');
done();
} catch (error) {
done(error)
@@ -173,7 +219,7 @@ describe('MQTT Nodes', function () {
topic: nextTopic(),
payload: '{prop:"value3", "num":3}', // send invalid JSON ...
}
const hooks = { done: done, beforeLoad: null, afterLoad: null, afterConnect: null }
const hooks = { done: null, beforeLoad: null, afterLoad: null, afterConnect: null }
hooks.afterLoad = (helperNode, mqttBroker, mqttIn, mqttOut) => {
helperNode.on("input", function (msg) {
try {
@@ -299,7 +345,7 @@ describe('MQTT Nodes', function () {
topic: nextTopic(),
payload: '{prop:"value3", "num":3}', contentType: "application/json", // send invalid JSON ...
}
const hooks = { done: done, beforeLoad: null, afterLoad: null, afterConnect: null }
const hooks = { done: null, beforeLoad: null, afterLoad: null, afterConnect: null }
hooks.afterLoad = (helperNode, mqttBroker, mqttIn, mqttOut) => {
helperNode.on("input", function (msg) {
try {
@@ -385,7 +431,7 @@ describe('MQTT Nodes', function () {
if (skipTests) { return this.skip() }
this.timeout = 2000;
const options = {}
const hooks = { done: done, beforeLoad: null, afterLoad: null, afterConnect: null }
const hooks = { beforeLoad: null, afterLoad: null, afterConnect: null }
hooks.beforeLoad = (flow) => { //add a status node pointed at MQTT Out node (to watch for connection status change)
flow.push({ "id": "status.node", "type": "status", "name": "status_node", "scope": ["mqtt.out"], "wires": [["helper.node"]] });//add status node to watch mqtt_out
}
@@ -416,52 +462,32 @@ describe('MQTT Nodes', function () {
this.timeout = 2000;
const baseTopic = nextTopic();
const brokerOptions = {
autoConnect: false,
protocolVersion: 4,
birthTopic: baseTopic + "/birth",
birthPayload: "broker connected",
birthPayload: "broker birth",
birthQos: 2,
}
const options = {};
const hooks = { done: done, beforeLoad: null, afterLoad: null, afterConnect: null };
options.expectMsg = {
const expectMsg = {
topic: brokerOptions.birthTopic,
payload: brokerOptions.birthPayload,
qos: brokerOptions.birthQos
};
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 };
const options = { };
const hooks = { };
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);
compareMsgToExpected(msg, expectMsg);
done();
} catch (err) {
done(err)
} catch (error) {
done(error)
}
});
})
mqttIn.receive({ "action": "connect" }); //now request connect action
return true; //handled
}
options.expectMsg = null;
try {
testSendRecv(brokerOptions, { topic: brokerOptions.birthTopic }, {}, options, hooks);
done()
} catch(err) {
done(e)
}
}
testSendRecv(brokerOptions, { topic: brokerOptions.birthTopic }, {}, options, hooks);
});
itConditional('should publish close message', function (done) {
if (skipTests) { return this.skip() }
@@ -620,13 +646,12 @@ 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, finished = false;
let afterLoadHandled = 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(); }
@@ -651,12 +676,10 @@ 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; }
}
@@ -702,6 +725,7 @@ function buildMQTTBrokerNode(id, name, brokerHost, brokerPort, options) {
node.cleansession = String(options.cleansession) == "false" ? false : true;
node.autoUnsubscribe = String(options.autoUnsubscribe) == "false" ? false : true;
node.autoConnect = String(options.autoConnect) == "false" ? false : true;
node.sessionExpiry = options.sessionExpiry ? options.sessionExpiry : undefined;
if (options.birthTopic) {
node.birthTopic = options.birthTopic;
@@ -796,8 +820,8 @@ function waitBrokerConnect(broker, timeLimit) {
let waitConnected = (broker, timeLimit) => {
const brokers = Array.isArray(broker) ? broker : [broker];
timeLimit = timeLimit || 1000;
let timer, resolved = false;
return new Promise( (resolve, reject) => {
let timer, resolved = false;
timer = wait();
function wait() {
if (brokers.every(e => e.connected == true)) {

View File

@@ -61,7 +61,7 @@ describe("api/index", function() {
should.not.exist(api.httpAdmin);
done();
});
describe('initalises admin api without adminAuth', function(done) {
describe('initalises admin api without adminAuth', function() {
before(function() {
beforeEach();
api.init({},{},{},{});
@@ -78,7 +78,7 @@ describe("api/index", function() {
})
});
describe('initalises admin api without editor', function(done) {
describe('initalises admin api without editor', function() {
before(function() {
beforeEach();
api.init({ disableEditor: true },{},{},{});
@@ -95,7 +95,7 @@ describe("api/index", function() {
})
});
describe('initialises api with admin middleware', function(done) {
describe('initialises api with admin middleware', function() {
it('ignores non-function values',function(done) {
api.init({ httpAdminRoot: true, httpAdminMiddleware: undefined },{},{},{});
const middlewareFound = api.httpAdmin._router.stack.filter((layer) => layer.name === 'testMiddleware')
@@ -112,7 +112,7 @@ describe("api/index", function() {
});
});
describe('initialises api with authentication enabled', function(done) {
describe('initialises api with authentication enabled', function() {
it('enables an oauth/openID based authentication mechanism',function(done) {
const stub = sinon.stub(apiAuth, 'genericStrategy').callsFake(function(){});
@@ -135,7 +135,7 @@ describe("api/index", function() {
});
describe('initialises api with custom cors config', function (done) {
describe('initialises api with custom cors config', function () {
const httpAdminCors = {
origin: "*",
methods: "GET,PUT,POST,DELETE"
@@ -156,7 +156,7 @@ describe("api/index", function() {
})
});
describe('editor start', function (done) {
describe('editor start', function () {
it('cannot be started when editor is disabled', function (done) {
const stub = sinon.stub(apiEditor, 'start').callsFake(function () {