Compare commits

..

6 Commits

Author SHA1 Message Date
Nick O'Leary
ca53712ee9 Deprecate synchronous access to jsonata 2023-03-03 11:43:06 +00:00
Nick O'Leary
e7c6178391 Merge pull request #4085 from node-red/link-call-timeout
Clear link-call timeouts when node is closed
2023-03-02 21:18:34 +00:00
Nick O'Leary
40b506b7b4 Merge pull request #4087 from node-red/junction-loops
Prevent loops being created with junction nodes
2023-03-02 21:18:21 +00:00
Nick O'Leary
b19a679d00 Merge pull request #4086 from node-red/version-check
Bump minimum nodejs version supported to match documented value
2023-03-02 21:16:24 +00:00
Nick O'Leary
81d4c60cbd Prevent loops being created with junction nodes
Fixes #3799
2023-03-02 21:13:39 +00:00
Nick O'Leary
5e9a815b06 Clear link-call timeouts when node is closed
Fixes #3959
2023-03-02 17:23:13 +00:00
7 changed files with 89 additions and 38 deletions

View File

@@ -1090,6 +1090,11 @@ RED.nodes = (function() {
return false;
}
function getDownstreamNodes(node) {
const downstreamLinks = nodeLinks[node.id].out
const downstreamNodes = new Set(downstreamLinks.map(l => l.target))
return Array.from(downstreamNodes)
}
function getAllDownstreamNodes(node) {
return getAllFlowNodes(node,'down').filter(function(n) { return n !== node });
}
@@ -3086,6 +3091,7 @@ RED.nodes = (function() {
getAllFlowNodes: getAllFlowNodes,
getAllUpstreamNodes: getAllUpstreamNodes,
getAllDownstreamNodes: getAllDownstreamNodes,
getDownstreamNodes: getDownstreamNodes,
getNodeIslands: getNodeIslands,
createExportableNodeSet: createExportableNodeSet,
createCompleteNodeSet: createCompleteNodeSet,

View File

@@ -82,15 +82,15 @@ RED.contextMenu = (function () {
dirty: true,
moved: true
}
const junction = RED.nodes.addJunction(nn);
const historyEvent = {
dirty: RED.nodes.dirty(),
t: 'add',
junctions: [nn]
junctions: [junction]
}
RED.nodes.addJunction(nn);
RED.history.push(historyEvent);
RED.nodes.dirty(true);
RED.view.select({nodes: [nn] });
RED.view.select({nodes: [junction] });
RED.view.redraw(true)
},
disabled: !canEdit

View File

@@ -294,32 +294,37 @@
}
try {
var result = expr.evaluate(legacyMode?{msg:parsedData}:parsedData);
if (usesContext) {
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) {
formattedResult = JSON.stringify(result,null,4);
} else {
formattedResult = RED._("expressionEditor.noMatch");
}
testResultEditor.setValue(formattedResult,-1);
} catch(err) {
expr.evaluate(legacyMode?{msg:parsedData}:parsedData, (err, result) => {
if (err) {
testResultEditor.setValue(RED._("expressionEditor.errors.eval",{message:err.message}),-1);
} else {
if (usesContext) {
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) {
formattedResult = JSON.stringify(result,null,4);
} else {
formattedResult = RED._("expressionEditor.noMatch");
}
testResultEditor.setValue(formattedResult,-1);
}
});
} catch(err) {
testResultEditor.setValue(RED._("expressionEditor.errors.eval",{message:err.message}),-1);
}
}

View File

@@ -3095,8 +3095,25 @@ RED.view = (function() {
(drag_line.portType === PORT_TYPE_INPUT && mouseup_node.type === "subflow" && (mouseup_node.direction === "status" || mouseup_node.direction === "out")) ||
(drag_line.portType === PORT_TYPE_OUTPUT && mouseup_node.type === "subflow" && mouseup_node.direction === "in")
)) {
let hasJunctionLoop = false
if (link.source.type === 'junction' && link.target.type === 'junction') {
// This is joining two junctions together. We want to avoid creating a loop
// of pure junction nodes as there is no way to break out of it.
const visited = new Set()
let toVisit = [link.target]
while (toVisit.length > 0) {
const next = toVisit.shift()
if (next === link.source) {
hasJunctionLoop = true
break
}
visited.add(next)
toVisit = toVisit.concat(RED.nodes.getDownstreamNodes(next).filter(n => n.type === 'junction' && !visited.has(n)))
}
}
var existingLink = RED.nodes.filterLinks({source:src,target:dst,sourcePort: src_port}).length !== 0;
if (!existingLink) {
if (!hasJunctionLoop && !existingLink) {
RED.nodes.addLink(link);
addedLinks.push(link);
}

View File

@@ -117,14 +117,21 @@ module.exports = function(RED) {
if (p.v) {
try {
var exp = RED.util.prepareJSONataExpression(p.v, node);
var val = RED.util.evaluateJSONataExpression(exp, msg);
RED.util.setMessageProperty(msg, property, val, true);
}
catch (err) {
RED.util.evaluateJSONataExpression(exp, msg, (err, newValue) => {
if (err) {
errors.push(err.toString())
} else {
RED.util.setMessageProperty(msg,property,newValue,true);
}
evaluateProperty(doneEvaluating)
});
} catch (err) {
errors.push(err.message);
evaluateProperty(doneEvaluating)
}
} else {
evaluateProperty(doneEvaluating)
}
evaluateProperty(doneEvaluating)
} else {
try {
RED.util.evaluateNodeProperty(value, valueType, node, msg, (err, newValue) => {

View File

@@ -248,6 +248,14 @@ module.exports = function(RED) {
}
});
this.on("close", function () {
for (const event of Object.values(messageEvents)) {
if (event.ts) {
clearTimeout(event.ts)
}
}
})
this.returnLinkMessage = function(eventId, msg) {
if (Array.isArray(msg._linkSource) && msg._linkSource.length === 0) {
delete msg._linkSource;

View File

@@ -25,7 +25,7 @@ const moment = require("moment-timezone");
const safeJSONStringify = require("json-stringify-safe");
const util = require("util");
const { hasOwnProperty } = Object.prototype;
const log = require("./log")
/**
* Safely returns the object construtor name.
* @return {String} the name of the object constructor if it exists, empty string otherwise.
@@ -671,8 +671,11 @@ function evaluateNodeProperty(value, type, node, msg, callback) {
} else if (type === 'bool') {
result = /^true$/i.test(value);
} else if (type === 'jsonata') {
var expr = prepareJSONataExpression(value,node);
result = evaluateJSONataExpression(expr,msg);
var expr = prepareJSONataExpression(value, node);
result = evaluateJSONataExpression(expr, msg, callback);
if (callback) {
return
}
} else if (type === 'env') {
result = evaluateEnvProperty(value, node);
}
@@ -767,6 +770,11 @@ function evaluateJSONataExpression(expr,msg,callback) {
})
});
}
} else {
log.warn('Deprecated API warning: Calls to RED.util.evaluateJSONataExpression must include a callback. '+
'This will not be optional in Node-RED 4.0. Please identify the node from the following stack '+
'and check for an update on npm. If none is available, please notify the node author.')
log.warn(new Error().stack)
}
return expr.evaluate(context, bindings, callback);
}