mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Add persistable context
and avoid exception when arg is undefined in util/getMessageProperty
This commit is contained in:
		
				
					committed by
					
						 HirokiUchikawa
						HirokiUchikawa
					
				
			
			
				
	
			
			
			
						parent
						
							cd44f13171
						
					
				
				
					commit
					771b598c09
				
			| @@ -14,20 +14,27 @@ | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
|  | ||||
| var clone = require("clone"); | ||||
| var when = require("when"); | ||||
| var util = require("../util"); | ||||
| var util = require("../../util"); | ||||
| var log = require("../../log"); | ||||
| var externalContext = require("./external"); | ||||
|  | ||||
| var contexts = {}; | ||||
| var globalContext = null; | ||||
|  | ||||
| var re = /^(\$.*?)\.(.+)|^(\$.*)/; | ||||
|  | ||||
| function createContext(id,seed) { | ||||
|     var flowId = id; | ||||
|     var data = seed || {}; | ||||
|     var obj = seed || {}; | ||||
|     obj.get = function get(key) { | ||||
|  | ||||
|     function get(key) { | ||||
|         return util.getMessageProperty(data,key); | ||||
|     }; | ||||
|     obj.set = function set(key, value) { | ||||
|     function set(key, value) { | ||||
|         util.setMessageProperty(data,key,value); | ||||
|     } | ||||
|     obj.keys = function() { | ||||
|     }; | ||||
|     function keys() { | ||||
|         var keysData = Object.keys(data); | ||||
|         if (seed == null) { | ||||
|             return keysData; | ||||
| @@ -36,13 +43,32 @@ function createContext(id,seed) { | ||||
|                 return key !== "set" && key !== "get" && key !== "keys"; | ||||
|             }); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     obj.get = function(key) { | ||||
|         if(externalContext.canUse(key)) { | ||||
|             return externalContext.get(key, flowId); | ||||
|         }else{ | ||||
|             return get(key); | ||||
|         } | ||||
|     }; | ||||
|     obj.set = function(key, value) { | ||||
|         if(externalContext.canUse(key)) { | ||||
|             externalContext.set(key, value, flowId); | ||||
|         }else{ | ||||
|             set(key, value); | ||||
|         } | ||||
|     } | ||||
|     obj.keys = function(key) { | ||||
|         if(externalContext.canUse(key)) { | ||||
|             return externalContext.keys(key, flowId); | ||||
|         }else{ | ||||
|             return keys(); | ||||
|         } | ||||
|     } | ||||
|     return obj; | ||||
| } | ||||
|  | ||||
| var contexts = {}; | ||||
| var globalContext = null; | ||||
|  | ||||
| function getContext(localId,flowId) { | ||||
|     var contextId = localId; | ||||
|     if (flowId) { | ||||
| @@ -61,6 +87,7 @@ function getContext(localId,flowId) { | ||||
|     contexts[contextId] = newContext; | ||||
|     return newContext; | ||||
| } | ||||
|  | ||||
| function deleteContext(id,flowId) { | ||||
|     var contextId = id; | ||||
|     if (flowId) { | ||||
| @@ -68,6 +95,7 @@ function deleteContext(id,flowId) { | ||||
|     } | ||||
|     delete contexts[contextId]; | ||||
| } | ||||
|  | ||||
| function clean(flowConfig) { | ||||
|     var activeIds = {}; | ||||
|     var contextId; | ||||
| @@ -81,9 +109,11 @@ function clean(flowConfig) { | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|     init: function(settings) { | ||||
|         globalContext = createContext("global",settings.functionGlobalContext || {}); | ||||
|         externalContext.init(settings); | ||||
|     }, | ||||
|     get: getContext, | ||||
|     delete: deleteContext, | ||||
|   | ||||
| @@ -240,7 +240,7 @@ function getMessageProperty(msg,expr) { | ||||
|     var msgPropParts = normalisePropertyExpression(expr); | ||||
|     var m; | ||||
|     msgPropParts.reduce(function(obj, key) { | ||||
|         result = (typeof obj[key] !== "undefined" ? obj[key] : undefined); | ||||
|         result = ((typeof obj !== "undefined") && (typeof obj[key] !== "undefined") ? obj[key] : undefined); | ||||
|         return result; | ||||
|     }, msg); | ||||
|     return result; | ||||
|   | ||||
| @@ -16,129 +16,223 @@ | ||||
|  | ||||
| var should = require("should"); | ||||
| var sinon = require('sinon'); | ||||
| var Context = require("../../../../red/runtime/nodes/context"); | ||||
| var path = require('path'); | ||||
| var fs = require('fs-extra'); | ||||
| var Context = require("../../../../../red/runtime/nodes/context/index"); | ||||
|  | ||||
| describe('context', function() { | ||||
|     beforeEach(function() { | ||||
|         Context.init({}); | ||||
|     }); | ||||
|     afterEach(function() { | ||||
|         Context.clean({allNodes:{}}); | ||||
|     }); | ||||
|     it('stores local property',function() { | ||||
|         var context1 = Context.get("1","flowA"); | ||||
|         should.not.exist(context1.get("foo")); | ||||
|         context1.set("foo","test"); | ||||
|         context1.get("foo").should.eql("test"); | ||||
|     }); | ||||
|     it('stores local property - creates parent properties',function() { | ||||
|         var context1 = Context.get("1","flowA"); | ||||
|         context1.set("foo.bar","test"); | ||||
|         context1.get("foo").should.eql({bar:"test"}); | ||||
|     }); | ||||
|     it('deletes local property',function() { | ||||
|         var context1 = Context.get("1","flowA"); | ||||
|         context1.set("foo.abc.bar1","test1"); | ||||
|         context1.set("foo.abc.bar2","test2"); | ||||
|         context1.get("foo.abc").should.eql({bar1:"test1",bar2:"test2"}); | ||||
|         context1.set("foo.abc.bar1",undefined); | ||||
|         context1.get("foo.abc").should.eql({bar2:"test2"}); | ||||
|         context1.set("foo.abc",undefined); | ||||
|         should.not.exist(context1.get("foo.abc")); | ||||
|         context1.set("foo",undefined); | ||||
|         should.not.exist(context1.get("foo")); | ||||
|     }); | ||||
|     it('stores flow property',function() { | ||||
|         var context1 = Context.get("1","flowA"); | ||||
|         should.not.exist(context1.flow.get("foo")); | ||||
|         context1.flow.set("foo","test"); | ||||
|         context1.flow.get("foo").should.eql("test"); | ||||
|     }); | ||||
|     it('stores global property',function() { | ||||
|         var context1 = Context.get("1","flowA"); | ||||
|         should.not.exist(context1.global.get("foo")); | ||||
|         context1.global.set("foo","test"); | ||||
|         context1.global.get("foo").should.eql("test"); | ||||
|     describe('localmemory',function() { | ||||
|         beforeEach(function() { | ||||
|             Context.init({}); | ||||
|         }); | ||||
|         afterEach(function() { | ||||
|             Context.clean({allNodes:{}}); | ||||
|         }); | ||||
|         it('stores local property',function() { | ||||
|             var context1 = Context.get("1","flowA"); | ||||
|             should.not.exist(context1.get("foo")); | ||||
|             context1.set("foo","test"); | ||||
|             context1.get("foo").should.eql("test"); | ||||
|         }); | ||||
|         it('stores local property - creates parent properties',function() { | ||||
|             var context1 = Context.get("1","flowA"); | ||||
|             context1.set("foo.bar","test"); | ||||
|             context1.get("foo").should.eql({bar:"test"}); | ||||
|         }); | ||||
|         it('deletes local property',function() { | ||||
|             var context1 = Context.get("1","flowA"); | ||||
|             context1.set("foo.abc.bar1","test1"); | ||||
|             context1.set("foo.abc.bar2","test2"); | ||||
|             context1.get("foo.abc").should.eql({bar1:"test1",bar2:"test2"}); | ||||
|             context1.set("foo.abc.bar1",undefined); | ||||
|             context1.get("foo.abc").should.eql({bar2:"test2"}); | ||||
|             context1.set("foo.abc",undefined); | ||||
|             should.not.exist(context1.get("foo.abc")); | ||||
|             context1.set("foo",undefined); | ||||
|             should.not.exist(context1.get("foo")); | ||||
|         }); | ||||
|         it('stores flow property',function() { | ||||
|             var context1 = Context.get("1","flowA"); | ||||
|             should.not.exist(context1.flow.get("foo")); | ||||
|             context1.flow.set("foo","test"); | ||||
|             context1.flow.get("foo").should.eql("test"); | ||||
|         }); | ||||
|         it('stores global property',function() { | ||||
|             var context1 = Context.get("1","flowA"); | ||||
|             should.not.exist(context1.global.get("foo")); | ||||
|             context1.global.set("foo","test"); | ||||
|             context1.global.get("foo").should.eql("test"); | ||||
|         }); | ||||
|  | ||||
|         it('keeps local context local', function() { | ||||
|             var context1 = Context.get("1","flowA"); | ||||
|             var context2 = Context.get("2","flowA"); | ||||
|  | ||||
|             should.not.exist(context1.get("foo")); | ||||
|             should.not.exist(context2.get("foo")); | ||||
|             context1.set("foo","test"); | ||||
|  | ||||
|             context1.get("foo").should.eql("test"); | ||||
|             should.not.exist(context2.get("foo")); | ||||
|         }); | ||||
|         it('flow context accessible to all flow nodes', function() { | ||||
|             var context1 = Context.get("1","flowA"); | ||||
|             var context2 = Context.get("2","flowA"); | ||||
|  | ||||
|             should.not.exist(context1.flow.get("foo")); | ||||
|             should.not.exist(context2.flow.get("foo")); | ||||
|  | ||||
|             context1.flow.set("foo","test"); | ||||
|             context1.flow.get("foo").should.eql("test"); | ||||
|             context2.flow.get("foo").should.eql("test"); | ||||
|         }); | ||||
|  | ||||
|         it('flow context not shared to nodes on other flows', function() { | ||||
|             var context1 = Context.get("1","flowA"); | ||||
|             var context2 = Context.get("2","flowB"); | ||||
|  | ||||
|             should.not.exist(context1.flow.get("foo")); | ||||
|             should.not.exist(context2.flow.get("foo")); | ||||
|  | ||||
|             context1.flow.set("foo","test"); | ||||
|             context1.flow.get("foo").should.eql("test"); | ||||
|             should.not.exist(context2.flow.get("foo")); | ||||
|         }); | ||||
|  | ||||
|         it('global context shared to all nodes', function() { | ||||
|             var context1 = Context.get("1","flowA"); | ||||
|             var context2 = Context.get("2","flowB"); | ||||
|  | ||||
|             should.not.exist(context1.global.get("foo")); | ||||
|             should.not.exist(context2.global.get("foo")); | ||||
|  | ||||
|             context1.global.set("foo","test"); | ||||
|             context1.global.get("foo").should.eql("test"); | ||||
|             context2.global.get("foo").should.eql("test"); | ||||
|         }); | ||||
|  | ||||
|         it('deletes context',function() { | ||||
|             var context = Context.get("1","flowA"); | ||||
|             should.not.exist(context.get("foo")); | ||||
|             context.set("foo","abc"); | ||||
|             context.get("foo").should.eql("abc"); | ||||
|  | ||||
|             Context.delete("1","flowA"); | ||||
|             context = Context.get("1","flowA"); | ||||
|             should.not.exist(context.get("foo")); | ||||
|         }); | ||||
|  | ||||
|         it('enumerates context keys', function() { | ||||
|             var context = Context.get("1","flowA"); | ||||
|  | ||||
|             var keys = context.keys(); | ||||
|             keys.should.be.an.Array(); | ||||
|             keys.should.be.empty(); | ||||
|  | ||||
|             context.set("foo","bar"); | ||||
|             keys = context.keys(); | ||||
|             keys.should.have.length(1); | ||||
|             keys[0].should.eql("foo"); | ||||
|  | ||||
|             context.set("abc.def","bar"); | ||||
|             keys = context.keys(); | ||||
|             keys.should.have.length(2); | ||||
|             keys[1].should.eql("abc"); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     it('keeps local context local', function() { | ||||
|         var context1 = Context.get("1","flowA"); | ||||
|         var context2 = Context.get("2","flowA"); | ||||
|     describe('external context storage',function() { | ||||
|         var testDir = path.join(__dirname,".testUserHome"); | ||||
|         var context; | ||||
|         var stubGet = sinon.stub(); | ||||
|         var stubSet = sinon.stub(); | ||||
|         var stubKeys = sinon.stub(); | ||||
|         var contextStorage={ | ||||
|                 test:{ | ||||
|                     module: { | ||||
|                         init: function() { | ||||
|                             return true; | ||||
|                         }, | ||||
|                         get: stubGet, | ||||
|                         set: stubSet, | ||||
|                         keys: stubKeys, | ||||
|                     }, | ||||
|                     config:{} | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|         should.not.exist(context1.get("foo")); | ||||
|         should.not.exist(context2.get("foo")); | ||||
|         context1.set("foo","test"); | ||||
|         beforeEach(function() { | ||||
|             Context.init({contextStorage:contextStorage}); | ||||
|             context =  Context.get("1","flow"); | ||||
|         }); | ||||
|         afterEach(function(done) { | ||||
|             stubGet.reset(); | ||||
|             stubSet.reset(); | ||||
|             stubKeys.reset(); | ||||
|             Context.clean({allNodes:{}}); | ||||
|             fs.remove(testDir,done); | ||||
|         }); | ||||
|  | ||||
|         context1.get("foo").should.eql("test"); | ||||
|         should.not.exist(context2.get("foo")); | ||||
|         describe('if external context storage exists',function() { | ||||
|             it('should store local property to external context storage',function() { | ||||
|                 should.not.exist(context.get("$test.foo")); | ||||
|                 context.set("$test.foo","test"); | ||||
|                 context.get("$test.foo"); | ||||
|                 context.keys("$test"); | ||||
|                 stubGet.called.should.be.true(); | ||||
|                 stubSet.called.should.be.true(); | ||||
|                 stubKeys.called.should.be.true(); | ||||
|             }); | ||||
|             it('should store flow property to external context storage',function() { | ||||
|                 should.not.exist(context.flow.get("$test.foo")); | ||||
|                 context.flow.set("$test.foo","test"); | ||||
|                 context.flow.get("$test.foo"); | ||||
|                 context.flow.keys("$test"); | ||||
|                 stubGet.called.should.be.true(); | ||||
|                 stubSet.called.should.be.true(); | ||||
|                 stubKeys.called.should.be.true(); | ||||
|             }); | ||||
|             it('should store global property to external context storage',function() { | ||||
|                 should.not.exist(context.global.get("$test.foo")); | ||||
|                 context.global.set("$test.foo","test"); | ||||
|                 context.global.get("$test.foo"); | ||||
|                 context.global.keys("$test"); | ||||
|                 stubGet.called.should.be.true(); | ||||
|                 stubSet.called.should.be.true(); | ||||
|                 stubKeys.called.should.be.true(); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         describe('if external context storage does not exist',function() { | ||||
|             it('should store local property to local memory',function() { | ||||
|                 should.not.exist(context.flow.get("$nonexist.foo")); | ||||
|                 context.set("$nonexist.foo","test"); | ||||
|                 context.get("$nonexist.foo").should.eql("test"); | ||||
|                 context.keys("$nonexist").should.have.length(1); | ||||
|                 stubGet.notCalled.should.be.true(); | ||||
|                 stubSet.notCalled.should.be.true(); | ||||
|                 stubKeys.notCalled.should.be.true(); | ||||
|             }); | ||||
|  | ||||
|             it('should store flow property to local memory',function() { | ||||
|                 should.not.exist(context.flow.get("$nonexist.foo")); | ||||
|                 context.flow.set("$nonexist.foo","test"); | ||||
|                 context.flow.get("$nonexist.foo").should.eql("test"); | ||||
|                 context.flow.keys("$nonexist").should.have.length(1); | ||||
|                 stubGet.notCalled.should.be.true(); | ||||
|                 stubSet.notCalled.should.be.true(); | ||||
|                 stubKeys.notCalled.should.be.true(); | ||||
|             }); | ||||
|  | ||||
|             it('should store global property to local memory',function() { | ||||
|                 should.not.exist(context.global.get("$nonexist.foo")); | ||||
|                 context.global.set("$nonexist.foo","test"); | ||||
|                 context.global.get("$nonexist.foo").should.eql("test"); | ||||
|                 context.global.keys("$nonexist").should.have.length(1); | ||||
|                 stubGet.notCalled.should.be.true(); | ||||
|                 stubSet.notCalled.should.be.true(); | ||||
|                 stubKeys.notCalled.should.be.true(); | ||||
|             }); | ||||
|         }); | ||||
|     }); | ||||
|     it('flow context accessible to all flow nodes', function() { | ||||
|         var context1 = Context.get("1","flowA"); | ||||
|         var context2 = Context.get("2","flowA"); | ||||
|  | ||||
|         should.not.exist(context1.flow.get("foo")); | ||||
|         should.not.exist(context2.flow.get("foo")); | ||||
|  | ||||
|         context1.flow.set("foo","test"); | ||||
|         context1.flow.get("foo").should.eql("test"); | ||||
|         context2.flow.get("foo").should.eql("test"); | ||||
|     }); | ||||
|  | ||||
|     it('flow context not shared to nodes on other flows', function() { | ||||
|         var context1 = Context.get("1","flowA"); | ||||
|         var context2 = Context.get("2","flowB"); | ||||
|  | ||||
|         should.not.exist(context1.flow.get("foo")); | ||||
|         should.not.exist(context2.flow.get("foo")); | ||||
|  | ||||
|         context1.flow.set("foo","test"); | ||||
|         context1.flow.get("foo").should.eql("test"); | ||||
|         should.not.exist(context2.flow.get("foo")); | ||||
|     }); | ||||
|  | ||||
|     it('global context shared to all nodes', function() { | ||||
|         var context1 = Context.get("1","flowA"); | ||||
|         var context2 = Context.get("2","flowB"); | ||||
|  | ||||
|         should.not.exist(context1.global.get("foo")); | ||||
|         should.not.exist(context2.global.get("foo")); | ||||
|  | ||||
|         context1.global.set("foo","test"); | ||||
|         context1.global.get("foo").should.eql("test"); | ||||
|         context2.global.get("foo").should.eql("test"); | ||||
|     }); | ||||
|  | ||||
|     it('deletes context',function() { | ||||
|         var context = Context.get("1","flowA"); | ||||
|         should.not.exist(context.get("foo")); | ||||
|         context.set("foo","abc"); | ||||
|         context.get("foo").should.eql("abc"); | ||||
|  | ||||
|         Context.delete("1","flowA"); | ||||
|         context = Context.get("1","flowA"); | ||||
|         should.not.exist(context.get("foo")); | ||||
|     }) | ||||
|  | ||||
|     it('enumerates context keys', function() { | ||||
|         var context = Context.get("1","flowA"); | ||||
|  | ||||
|         var keys = context.keys(); | ||||
|         keys.should.be.an.Array(); | ||||
|         keys.should.be.empty(); | ||||
|  | ||||
|         context.set("foo","bar"); | ||||
|         keys = context.keys(); | ||||
|         keys.should.have.length(1); | ||||
|         keys[0].should.eql("foo"); | ||||
|  | ||||
|         context.set("abc.def","bar"); | ||||
|         keys = context.keys(); | ||||
|         keys.should.have.length(2); | ||||
|         keys[1].should.eql("abc"); | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|     }) | ||||
|  | ||||
| }); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user