diff --git a/packages/node_modules/@node-red/runtime/lib/nodes/context/index.js b/packages/node_modules/@node-red/runtime/lib/nodes/context/index.js index ceeb1222b..1423b5237 100644 --- a/packages/node_modules/@node-red/runtime/lib/nodes/context/index.js +++ b/packages/node_modules/@node-red/runtime/lib/nodes/context/index.js @@ -18,6 +18,7 @@ var clone = require("clone"); var log = require("@node-red/util").log; var util = require("@node-red/util").util; var memory = require("./memory"); +var flows; var settings; @@ -47,6 +48,7 @@ function logUnknownStore(name) { } function init(_settings) { + flows = require("../flows"); settings = _settings; contexts = {}; stores = {}; @@ -198,8 +200,24 @@ function getContextStorage(storage) { } } +function followParentContext(parent, key) { + if (key === "$parent") { + return [parent, undefined]; + } + else if (key.startsWith("$parent.")) { + var len = "$parent.".length; + var new_key = key.substring(len); + var ctx = parent; + while (ctx && new_key.startsWith("$parent.")) { + ctx = ctx.$parent; + new_key = new_key.substring(len); + } + return [ctx, new_key]; + } + return null; +} -function createContext(id,seed) { +function createContext(id,seed,parent) { // Seed is only set for global context - sourced from functionGlobalContext var scope = id; var obj = seed || {}; @@ -254,6 +272,21 @@ function createContext(id,seed) { if (!storage) { storage = keyParts.store || "_"; } + var result = followParentContext(parent, key); + if (result) { + var [ctx, new_key] = result; + if (ctx && new_key) { + return ctx.get(new_key, storage, callback); + } + else { + if (callback) { + return callback(undefined); + } + else { + return undefined; + } + } + } } else { if (!storage) { storage = "_"; @@ -321,6 +354,19 @@ function createContext(id,seed) { if (!storage) { storage = keyParts.store || "_"; } + var result = followParentContext(parent, key); + if (result) { + var [ctx, new_key] = result; + if (ctx && new_key) { + return ctx.set(new_key, value, storage, callback); + } + else { + if (callback) { + return callback(); + } + return undefined; + } + } } else { if (!storage) { storage = "_"; @@ -361,10 +407,36 @@ function createContext(id,seed) { } } }); + if (parent) { + Object.defineProperty(obj, "$parent", { + value: parent + }); + } return obj; } -function getContext(localId,flowId) { +function createRootContext() { + var obj = {}; + Object.defineProperties(obj, { + get: { + value: function(key, storage, callback) { + return undefined; + } + }, + set: { + value: function(key, value, storage, callback) { + } + }, + keys: { + value: function(storage, callback) { + return undefined; + } + } + }); + return obj; +} + +function getContext(localId,flowId,parent) { var contextId = localId; if (flowId) { contextId = localId+":"+flowId; @@ -372,10 +444,19 @@ function getContext(localId,flowId) { if (contexts.hasOwnProperty(contextId)) { return contexts[contextId]; } - var newContext = createContext(contextId); + var newContext = createContext(contextId,undefined,parent); if (flowId) { + var node = flows.get(flowId); + 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: getContext(flowId) + value: flowContext }); } Object.defineProperty(newContext, 'global', { diff --git a/test/unit/@node-red/runtime/lib/nodes/context/index_spec.js b/test/unit/@node-red/runtime/lib/nodes/context/index_spec.js index 396c7f12e..3c9030b03 100644 --- a/test/unit/@node-red/runtime/lib/nodes/context/index_spec.js +++ b/test/unit/@node-red/runtime/lib/nodes/context/index_spec.js @@ -225,7 +225,48 @@ describe('context', function() { }); }) + describe("$parent", function() { + it('should get undefined for $parent without key', function() { + var context0 = Context.get("0","flowA"); + var context1 = Context.get("1","flowB", context0); + var parent = context1.get("$parent"); + should.equal(parent, undefined); + }); + it('should get undefined for $parent of root', function() { + var context0 = Context.get("0","flowA"); + var context1 = Context.get("1","flowB", context0); + var parent = context1.get("$parent.$parent.K"); + should.equal(parent, undefined); + }); + + it('should get value in $parent', function() { + var context0 = Context.get("0","flowA"); + var context1 = Context.get("1","flowB", context0); + context0.set("K", "v"); + var v = context1.get("$parent.K"); + should.equal(v, "v"); + }); + + it('should set value in $parent', function() { + var context0 = Context.get("0","flowA"); + var context1 = Context.get("1","flowB", context0); + context1.set("$parent.K", "v"); + var v = context0.get("K"); + should.equal(v, "v"); + }); + + it('should not contain $parent in keys', function() { + var context0 = Context.get("0","flowA"); + var context1 = Context.get("1","flowB", context0); + var parent = context1.get("$parent"); + context0.set("K0", "v0"); + context1.set("K1", "v1"); + var keys = context1.keys(); + keys.should.have.length(1); + keys[0].should.equal("K1"); + }); + }); });