mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Flows/subflows must preinitialise their context objects
Fixes #2513 If a node inside a subflow accessed its context object in its constructor, the subflow-instance flow context would not yet have been created. This would cause a place holder context to get created on its behalf, but that place holder doesn't have its parent set properly. This then breaks the usage of $parent inside such a subflow. This fix has changed it so flows (and subflows) create their flow context as part of their initial creation. That ensures it exists when individual nodes from the subflow are created, allowing them to safely access their context. This has also fixed a related issue where any attempt to use $parent to access beyond the root parent would seemingly hang as the callback was never being called. This would cause messages to get stuck in flows. The fix ensures the callback is used in the root context objects and undefined is returned.
This commit is contained in:
@@ -420,15 +420,39 @@ function createRootContext() {
|
||||
Object.defineProperties(obj, {
|
||||
get: {
|
||||
value: function(key, storage, callback) {
|
||||
if (!callback && typeof storage === 'function') {
|
||||
callback = storage;
|
||||
storage = undefined;
|
||||
}
|
||||
if (callback) {
|
||||
callback()
|
||||
return;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
set: {
|
||||
value: function(key, value, storage, callback) {
|
||||
if (!callback && typeof storage === 'function') {
|
||||
callback = storage;
|
||||
storage = undefined;
|
||||
}
|
||||
if (callback) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
}
|
||||
},
|
||||
keys: {
|
||||
value: function(storage, callback) {
|
||||
if (!callback && typeof storage === 'function') {
|
||||
callback = storage;
|
||||
storage = undefined;
|
||||
}
|
||||
if (callback) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -436,25 +460,48 @@ function createRootContext() {
|
||||
return obj;
|
||||
}
|
||||
|
||||
function getContext(localId,flowId,parent) {
|
||||
var contextId = localId;
|
||||
/**
|
||||
* Get a flow-level context object.
|
||||
* @param {string} flowId [description]
|
||||
* @param {string} parentFlowId the id of the parent flow. undefined
|
||||
* @return {object}} the context object
|
||||
*/
|
||||
function getFlowContext(flowId,parentFlowId) {
|
||||
if (contexts.hasOwnProperty(flowId)) {
|
||||
return contexts[flowId];
|
||||
}
|
||||
var parentContext = contexts[parentFlowId];
|
||||
if (!parentContext) {
|
||||
parentContext = createRootContext();
|
||||
contexts[parentFlowId] = parentContext;
|
||||
// throw new Error("Flow "+flowId+" is missing parent context "+parentFlowId);
|
||||
}
|
||||
var newContext = createContext(flowId,undefined,parentContext);
|
||||
contexts[flowId] = newContext;
|
||||
return newContext;
|
||||
|
||||
}
|
||||
|
||||
function getContext(nodeId, flowId) {
|
||||
var contextId = nodeId;
|
||||
if (flowId) {
|
||||
contextId = localId+":"+flowId;
|
||||
contextId = nodeId+":"+flowId;
|
||||
}
|
||||
if (contexts.hasOwnProperty(contextId)) {
|
||||
return contexts[contextId];
|
||||
}
|
||||
var newContext = createContext(contextId,undefined,parent);
|
||||
var newContext = createContext(contextId);
|
||||
|
||||
if (flowId) {
|
||||
var node = flows.get(flowId);
|
||||
var parent = undefined;
|
||||
if (node && node.type.startsWith("subflow:")) {
|
||||
parent = node.context().flow;
|
||||
var flowContext = contexts[flowId];
|
||||
if (!flowContext) {
|
||||
// This is most likely due to a unit test for a node which doesn't
|
||||
// initialise the flow properly.
|
||||
// To keep things working, initialise the missing context.
|
||||
// This *does not happen* in normal node-red operation
|
||||
flowContext = createContext(flowId,undefined,createRootContext());
|
||||
contexts[flowId] = flowContext;
|
||||
}
|
||||
else {
|
||||
parent = createRootContext();
|
||||
}
|
||||
var flowContext = getContext(flowId,undefined,parent);
|
||||
Object.defineProperty(newContext, 'flow', {
|
||||
value: flowContext
|
||||
});
|
||||
@@ -466,6 +513,39 @@ function getContext(localId,flowId,parent) {
|
||||
return newContext;
|
||||
}
|
||||
|
||||
//
|
||||
// function getContext(localId,flowId,parent) {
|
||||
// var contextId = localId;
|
||||
// if (flowId) {
|
||||
// contextId = localId+":"+flowId;
|
||||
// }
|
||||
// console.log("getContext",localId,flowId,"known?",contexts.hasOwnProperty(contextId));
|
||||
// if (contexts.hasOwnProperty(contextId)) {
|
||||
// return contexts[contextId];
|
||||
// }
|
||||
// var newContext = createContext(contextId,undefined,parent);
|
||||
// if (flowId) {
|
||||
// var node = flows.get(flowId);
|
||||
// console.log("flows,get",flowId,node&&node.type)
|
||||
// var parent = undefined;
|
||||
// if (node && node.type.startsWith("subflow:")) {
|
||||
// parent = node.context().flow;
|
||||
// }
|
||||
// else {
|
||||
// parent = createRootContext();
|
||||
// }
|
||||
// var flowContext = getContext(flowId,undefined,parent);
|
||||
// Object.defineProperty(newContext, 'flow', {
|
||||
// value: flowContext
|
||||
// });
|
||||
// }
|
||||
// Object.defineProperty(newContext, 'global', {
|
||||
// value: contexts['global']
|
||||
// })
|
||||
// contexts[contextId] = newContext;
|
||||
// return newContext;
|
||||
// }
|
||||
|
||||
function deleteContext(id,flowId) {
|
||||
if(!hasConfiguredStore){
|
||||
// only delete context if there's no configured storage.
|
||||
@@ -517,6 +597,7 @@ module.exports = {
|
||||
load: load,
|
||||
listStores: listStores,
|
||||
get: getContext,
|
||||
getFlowContext:getFlowContext,
|
||||
delete: deleteContext,
|
||||
clean: clean,
|
||||
close: close
|
||||
|
@@ -18,6 +18,7 @@ var clone = require("clone");
|
||||
var redUtil = require("@node-red/util").util;
|
||||
var flowUtil = require("./util");
|
||||
var events = require("../../events");
|
||||
const context = require('../context');
|
||||
|
||||
var Subflow;
|
||||
var Log;
|
||||
@@ -54,6 +55,8 @@ class Flow {
|
||||
this.catchNodes = [];
|
||||
this.statusNodes = [];
|
||||
this.path = this.id;
|
||||
// Ensure a context exists for this flow
|
||||
this.context = context.getFlowContext(this.id,this.parent.id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -16,7 +16,7 @@
|
||||
|
||||
const clone = require("clone");
|
||||
const Flow = require('./Flow').Flow;
|
||||
|
||||
const context = require('../context');
|
||||
const util = require("util");
|
||||
|
||||
const redUtil = require("@node-red/util").util;
|
||||
@@ -227,6 +227,10 @@ class Subflow extends Flow {
|
||||
this.node.on("input", function(msg) { this.send(msg);});
|
||||
this.node.on("close", function() { this.status({}); })
|
||||
this.node.status = status => this.parent.handleStatus(this.node,status);
|
||||
// Create a context instance
|
||||
console.log("Node.context",this.type,"id:",this._alias||this.id,"z:",this.z)
|
||||
this._context = context.get(this._alias||this.id,this.z);
|
||||
|
||||
|
||||
this.node._updateWires = this.node.updateWires;
|
||||
|
||||
|
Reference in New Issue
Block a user