mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Merge branch '0.19' into context-tab
This commit is contained in:
@@ -105,12 +105,12 @@ Node.prototype.close = function(removed) {
|
||||
if (promises.length > 0) {
|
||||
return when.settle(promises).then(function() {
|
||||
if (this._context) {
|
||||
context.delete(this._alias||this.id,this.z);
|
||||
return context.delete(this._alias||this.id,this.z);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (this._context) {
|
||||
context.delete(this._alias||this.id,this.z);
|
||||
return context.delete(this._alias||this.id,this.z);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@@ -1,92 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var clone = require("clone");
|
||||
var when = require("when");
|
||||
var util = require("../util");
|
||||
|
||||
function createContext(id,seed) {
|
||||
var data = seed || {};
|
||||
var obj = seed || {};
|
||||
obj.get = function get(key) {
|
||||
return util.getMessageProperty(data,key);
|
||||
};
|
||||
obj.set = function set(key, value) {
|
||||
util.setMessageProperty(data,key,value);
|
||||
}
|
||||
obj.keys = function() {
|
||||
var keysData = Object.keys(data);
|
||||
if (seed == null) {
|
||||
return keysData;
|
||||
} else {
|
||||
return keysData.filter(function (key) {
|
||||
return key !== "set" && key !== "get" && key !== "keys";
|
||||
});
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
var contexts = {};
|
||||
var globalContext = null;
|
||||
|
||||
function getContext(localId,flowId) {
|
||||
var contextId = localId;
|
||||
if (flowId) {
|
||||
contextId = localId+":"+flowId;
|
||||
}
|
||||
if (contexts.hasOwnProperty(contextId)) {
|
||||
return contexts[contextId];
|
||||
}
|
||||
var newContext = createContext(contextId);
|
||||
if (flowId) {
|
||||
newContext.flow = getContext(flowId);
|
||||
}
|
||||
if (globalContext) {
|
||||
newContext.global = globalContext;
|
||||
}
|
||||
contexts[contextId] = newContext;
|
||||
return newContext;
|
||||
}
|
||||
function deleteContext(id,flowId) {
|
||||
var contextId = id;
|
||||
if (flowId) {
|
||||
contextId = id+":"+flowId;
|
||||
}
|
||||
delete contexts[contextId];
|
||||
}
|
||||
function clean(flowConfig) {
|
||||
var activeIds = {};
|
||||
var contextId;
|
||||
var node;
|
||||
for (var id in contexts) {
|
||||
if (contexts.hasOwnProperty(id) && id !== "global") {
|
||||
var idParts = id.split(":");
|
||||
if (!flowConfig.allNodes.hasOwnProperty(idParts[0])) {
|
||||
delete contexts[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = {
|
||||
init: function(settings) {
|
||||
globalContext = createContext("global",settings.functionGlobalContext || {});
|
||||
contexts['global'] = globalContext;
|
||||
},
|
||||
get: getContext,
|
||||
delete: deleteContext,
|
||||
clean:clean
|
||||
};
|
233
red/runtime/nodes/context/index.js
Normal file
233
red/runtime/nodes/context/index.js
Normal file
@@ -0,0 +1,233 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var clone = require("clone");
|
||||
var log = require("../../log");
|
||||
var memory = require("./memory");
|
||||
|
||||
var settings;
|
||||
var contexts = {};
|
||||
var globalContext = null;
|
||||
var externalContexts = {};
|
||||
var noContextStorage = true;
|
||||
|
||||
function init(_settings) {
|
||||
settings = _settings;
|
||||
externalContexts = {};
|
||||
|
||||
// init memory plugin
|
||||
var seed = settings.functionGlobalContext || {};
|
||||
externalContexts["_"] = memory();
|
||||
externalContexts["_"].setGlobalContext(seed);
|
||||
globalContext = createContext("global",seed);
|
||||
contexts['global'] = globalContext;
|
||||
}
|
||||
|
||||
function load() {
|
||||
// load & init plugins in settings.contextStorage
|
||||
var plugins = settings.contextStorage;
|
||||
var isAlias = false;
|
||||
if (plugins) {
|
||||
var promises = [];
|
||||
noContextStorage = false;
|
||||
for(var pluginName in plugins){
|
||||
if(pluginName === "_"){
|
||||
continue;
|
||||
}
|
||||
if(pluginName === "default" && typeof plugins[pluginName] === "string"){
|
||||
isAlias = true;
|
||||
continue;
|
||||
}
|
||||
var plugin;
|
||||
if(plugins[pluginName].hasOwnProperty("module")){
|
||||
var config = plugins[pluginName].config || {};
|
||||
copySettings(config, settings);
|
||||
if(typeof plugins[pluginName].module === "string") {
|
||||
try{
|
||||
plugin = require("./"+plugins[pluginName].module);
|
||||
}catch(err){
|
||||
return Promise.reject(new Error(log._("context.error-module-not-loaded", {module:plugins[pluginName].module})));
|
||||
}
|
||||
} else {
|
||||
plugin = plugins[pluginName].module;
|
||||
}
|
||||
externalContexts[pluginName] = plugin(config);
|
||||
}else{
|
||||
return Promise.reject(new Error(log._("context.error-module-not-defined", {storage:pluginName})));
|
||||
}
|
||||
}
|
||||
for(var plugin in externalContexts){
|
||||
if(externalContexts.hasOwnProperty(plugin)){
|
||||
promises.push(externalContexts[plugin].open());
|
||||
}
|
||||
}
|
||||
if(isAlias){
|
||||
if(externalContexts.hasOwnProperty(plugins["default"])){
|
||||
externalContexts["default"] = externalContexts[plugins["default"]];
|
||||
}else{
|
||||
return Promise.reject(new Error(log._("context.error-invalid-default-module", {storage:plugins["default"]})));
|
||||
}
|
||||
}
|
||||
return Promise.all(promises);
|
||||
} else {
|
||||
noContextStorage = true;
|
||||
return externalContexts["_"].open();
|
||||
}
|
||||
}
|
||||
|
||||
function copySettings(config, settings){
|
||||
var copy = ["userDir"]
|
||||
config.settings = {};
|
||||
copy.forEach(function(setting){
|
||||
config.settings[setting] = clone(settings[setting]);
|
||||
});
|
||||
}
|
||||
|
||||
function getContextStorage(storage) {
|
||||
if (externalContexts.hasOwnProperty(storage)) {
|
||||
return externalContexts[storage];
|
||||
} else if (externalContexts.hasOwnProperty("default")) {
|
||||
return externalContexts["default"];
|
||||
} else {
|
||||
var contextError = new Error(log._("context.error-use-undefined-storage", {storage:storage}));
|
||||
contextError.name = "ContextError";
|
||||
throw contextError;
|
||||
}
|
||||
}
|
||||
|
||||
function createContext(id,seed) {
|
||||
var scope = id;
|
||||
var obj = seed || {};
|
||||
|
||||
obj.get = function(key, storage, callback) {
|
||||
var context;
|
||||
if (!storage && !callback) {
|
||||
context = externalContexts["_"];
|
||||
} else {
|
||||
if (typeof storage === 'function') {
|
||||
callback = storage;
|
||||
storage = "default";
|
||||
}
|
||||
if (typeof callback !== 'function'){
|
||||
throw new Error("Callback must be a function");
|
||||
}
|
||||
context = getContextStorage(storage);
|
||||
}
|
||||
return context.get(scope, key, callback);
|
||||
};
|
||||
obj.set = function(key, value, storage, callback) {
|
||||
var context;
|
||||
if (!storage && !callback) {
|
||||
context = externalContexts["_"];
|
||||
} else {
|
||||
if (typeof storage === 'function') {
|
||||
callback = storage;
|
||||
storage = "default";
|
||||
}
|
||||
if (callback && typeof callback !== 'function') {
|
||||
throw new Error("Callback must be a function");
|
||||
}
|
||||
context = getContextStorage(storage);
|
||||
}
|
||||
context.set(scope, key, value, callback);
|
||||
};
|
||||
obj.keys = function(storage, callback) {
|
||||
var context;
|
||||
if (!storage && !callback) {
|
||||
context = externalContexts["_"];
|
||||
} else {
|
||||
if (typeof storage === 'function') {
|
||||
callback = storage;
|
||||
storage = "default";
|
||||
}
|
||||
if (typeof callback !== 'function') {
|
||||
throw new Error("Callback must be a function");
|
||||
}
|
||||
context = getContextStorage(storage);
|
||||
}
|
||||
return context.keys(scope, callback);
|
||||
};
|
||||
return obj;
|
||||
}
|
||||
|
||||
function getContext(localId,flowId) {
|
||||
var contextId = localId;
|
||||
if (flowId) {
|
||||
contextId = localId+":"+flowId;
|
||||
}
|
||||
if (contexts.hasOwnProperty(contextId)) {
|
||||
return contexts[contextId];
|
||||
}
|
||||
var newContext = createContext(contextId);
|
||||
if (flowId) {
|
||||
newContext.flow = getContext(flowId);
|
||||
}
|
||||
if (globalContext) {
|
||||
newContext.global = globalContext;
|
||||
}
|
||||
contexts[contextId] = newContext;
|
||||
return newContext;
|
||||
}
|
||||
|
||||
function deleteContext(id,flowId) {
|
||||
if(noContextStorage){
|
||||
var contextId = id;
|
||||
if (flowId) {
|
||||
contextId = id+":"+flowId;
|
||||
}
|
||||
delete contexts[contextId];
|
||||
return externalContexts["_"].delete(contextId);
|
||||
}else{
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
function clean(flowConfig) {
|
||||
var promises = [];
|
||||
for(var plugin in externalContexts){
|
||||
if(externalContexts.hasOwnProperty(plugin)){
|
||||
promises.push(externalContexts[plugin].clean(Object.keys(flowConfig.allNodes)));
|
||||
}
|
||||
}
|
||||
for (var id in contexts) {
|
||||
if (contexts.hasOwnProperty(id) && id !== "global") {
|
||||
var idParts = id.split(":");
|
||||
if (!flowConfig.allNodes.hasOwnProperty(idParts[0])) {
|
||||
delete contexts[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
function close() {
|
||||
var promises = [];
|
||||
for(var plugin in externalContexts){
|
||||
if(externalContexts.hasOwnProperty(plugin)){
|
||||
promises.push(externalContexts[plugin].close());
|
||||
}
|
||||
}
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: init,
|
||||
load: load,
|
||||
get: getContext,
|
||||
delete: deleteContext,
|
||||
clean: clean,
|
||||
close: close
|
||||
};
|
164
red/runtime/nodes/context/localfilesystem.js
Normal file
164
red/runtime/nodes/context/localfilesystem.js
Normal file
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var fs = require('fs-extra');
|
||||
var path = require("path");
|
||||
var util = require("../../util");
|
||||
|
||||
function getStoragePath(storageBaseDir, scope) {
|
||||
if(scope.indexOf(":") === -1){
|
||||
if(scope === "global"){
|
||||
return path.join(storageBaseDir,"global",scope);
|
||||
}else{ // scope:flow
|
||||
return path.join(storageBaseDir,scope,"flow");
|
||||
}
|
||||
}else{ // scope:local
|
||||
var ids = scope.split(":")
|
||||
return path.join(storageBaseDir,ids[1],ids[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function getBasePath(config) {
|
||||
var base = config.base || "contexts";
|
||||
var storageBaseDir;
|
||||
if (!config.dir) {
|
||||
if(config.settings && config.settings.userDir){
|
||||
storageBaseDir = path.join(config.settings.userDir, base);
|
||||
}else{
|
||||
try {
|
||||
fs.statSync(path.join(process.env.NODE_RED_HOME,".config.json"));
|
||||
storageBaseDir = path.join(process.env.NODE_RED_HOME, base);
|
||||
} catch(err) {
|
||||
try {
|
||||
// Consider compatibility for older versions
|
||||
if (process.env.HOMEPATH) {
|
||||
fs.statSync(path.join(process.env.HOMEPATH,".node-red",".config.json"));
|
||||
storageBaseDir = path.join(process.env.HOMEPATH, ".node-red", base);
|
||||
}
|
||||
} catch(err) {
|
||||
}
|
||||
if (!storageBaseDir) {
|
||||
storageBaseDir = path.join(process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH || process.env.NODE_RED_HOME,".node-red", base);
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
storageBaseDir = path.join(config.dir, base);
|
||||
}
|
||||
return storageBaseDir;
|
||||
}
|
||||
|
||||
function loadFile(storagePath){
|
||||
return fs.pathExists(storagePath).then(function(exists){
|
||||
if(exists === true){
|
||||
return fs.readFile(storagePath, "utf8");
|
||||
}else{
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}).catch(function(err){
|
||||
throw Promise.reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
function LocalFileSystem(config){
|
||||
this.config = config;
|
||||
this.storageBaseDir = getBasePath(this.config);
|
||||
}
|
||||
|
||||
LocalFileSystem.prototype.open = function(){
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
LocalFileSystem.prototype.close = function(){
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
LocalFileSystem.prototype.get = function(scope, key, callback) {
|
||||
if(typeof callback !== "function"){
|
||||
throw new Error("Callback must be a function");
|
||||
}
|
||||
var storagePath = getStoragePath(this.storageBaseDir ,scope);
|
||||
loadFile(storagePath + ".json").then(function(data){
|
||||
if(data){
|
||||
callback(null, util.getMessageProperty(JSON.parse(data),key));
|
||||
}else{
|
||||
callback(null, undefined);
|
||||
}
|
||||
}).catch(function(err){
|
||||
callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
LocalFileSystem.prototype.set =function(scope, key, value, callback) {
|
||||
var storagePath = getStoragePath(this.storageBaseDir ,scope);
|
||||
loadFile(storagePath + ".json").then(function(data){
|
||||
var obj = data ? JSON.parse(data) : {}
|
||||
util.setMessageProperty(obj,key,value);
|
||||
return fs.outputFile(storagePath + ".json", JSON.stringify(obj, undefined, 4), "utf8");
|
||||
}).then(function(){
|
||||
if(typeof callback === "function"){
|
||||
callback(null);
|
||||
}
|
||||
}).catch(function(err){
|
||||
if(typeof callback === "function"){
|
||||
callback(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
LocalFileSystem.prototype.keys = function(scope, callback){
|
||||
if(typeof callback !== "function"){
|
||||
throw new Error("Callback must be a function");
|
||||
}
|
||||
var storagePath = getStoragePath(this.storageBaseDir ,scope);
|
||||
loadFile(storagePath + ".json").then(function(data){
|
||||
if(data){
|
||||
callback(null, Object.keys(JSON.parse(data)));
|
||||
}else{
|
||||
callback(null, []);
|
||||
}
|
||||
}).catch(function(err){
|
||||
callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
LocalFileSystem.prototype.delete = function(scope){
|
||||
var storagePath = getStoragePath(this.storageBaseDir ,scope);
|
||||
return fs.remove(storagePath + ".json");
|
||||
}
|
||||
|
||||
LocalFileSystem.prototype.clean = function(activeNodes){
|
||||
var self = this;
|
||||
return fs.readdir(self.storageBaseDir).then(function(dirs){
|
||||
return Promise.all(dirs.reduce(function(result, item){
|
||||
if(item !== "global" && activeNodes.indexOf(item) === -1){
|
||||
result.push(fs.remove(path.join(self.storageBaseDir,item)));
|
||||
}
|
||||
return result;
|
||||
},[]));
|
||||
}).catch(function(err){
|
||||
if(err.code == 'ENOENT') {
|
||||
return Promise.resolve();
|
||||
}else{
|
||||
return Promise.reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = function(config){
|
||||
return new LocalFileSystem(config);
|
||||
};
|
||||
|
118
red/runtime/nodes/context/memory.js
Normal file
118
red/runtime/nodes/context/memory.js
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var util = require("../../util");
|
||||
|
||||
function Memory(config){
|
||||
this.data = {};
|
||||
}
|
||||
|
||||
Memory.prototype.open = function(){
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
Memory.prototype.close = function(){
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
Memory.prototype.get = function(scope, key, callback) {
|
||||
var value;
|
||||
try{
|
||||
if(this.data[scope]){
|
||||
value = util.getMessageProperty(this.data[scope], key);
|
||||
}
|
||||
}catch(err){
|
||||
if(callback){
|
||||
callback(err);
|
||||
}else{
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
if(callback){
|
||||
callback(null, value);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
Memory.prototype.set =function(scope, key, value, callback) {
|
||||
if(!this.data[scope]){
|
||||
this.data[scope] = {};
|
||||
}
|
||||
try{
|
||||
util.setMessageProperty(this.data[scope],key,value);
|
||||
}catch(err){
|
||||
if(callback){
|
||||
callback(err);
|
||||
}else{
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
if(callback){
|
||||
callback(null);
|
||||
}
|
||||
};
|
||||
|
||||
Memory.prototype.keys = function(scope, callback){
|
||||
var values = [];
|
||||
try{
|
||||
if(this.data[scope]){
|
||||
if (scope !== "global") {
|
||||
values = Object.keys(this.data[scope]);
|
||||
} else {
|
||||
values = Object.keys(this.data[scope]).filter(function (key) {
|
||||
return key !== "set" && key !== "get" && key !== "keys";
|
||||
});
|
||||
}
|
||||
}
|
||||
}catch(err){
|
||||
if(callback){
|
||||
callback(err);
|
||||
}else{
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
if(callback){
|
||||
callback(null, values);
|
||||
} else {
|
||||
return values;
|
||||
}
|
||||
};
|
||||
|
||||
Memory.prototype.delete = function(scope){
|
||||
delete this.data[scope];
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
Memory.prototype.clean = function(activeNodes){
|
||||
for(var id in this.data){
|
||||
if(this.data.hasOwnProperty(id) && id !== "global"){
|
||||
var idParts = id.split(":");
|
||||
if(activeNodes.indexOf(idParts[0]) === -1){
|
||||
delete this.data[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
Memory.prototype.setGlobalContext= function(seed){
|
||||
this.data["global"] = seed;
|
||||
};
|
||||
|
||||
module.exports = function(config){
|
||||
return new Memory(config);
|
||||
};
|
@@ -160,11 +160,12 @@ function setFlows(_config,type,muteLog,forceStart) {
|
||||
activeFlowConfig = newFlowConfig;
|
||||
if (forceStart || started) {
|
||||
return stop(type,diff,muteLog).then(function() {
|
||||
context.clean(activeFlowConfig);
|
||||
start(type,diff,muteLog).then(function() {
|
||||
events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});
|
||||
return context.clean(activeFlowConfig).then(function() {
|
||||
start(type,diff,muteLog).then(function() {
|
||||
events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});
|
||||
});
|
||||
return flowRevision;
|
||||
});
|
||||
return flowRevision;
|
||||
}).catch(function(err) {
|
||||
})
|
||||
} else {
|
||||
|
@@ -218,5 +218,9 @@ module.exports = {
|
||||
setCredentialSecret: credentials.setKey,
|
||||
clearCredentials: credentials.clear,
|
||||
exportCredentials: credentials.export,
|
||||
getCredentialKeyType: credentials.getKeyType
|
||||
getCredentialKeyType: credentials.getKeyType,
|
||||
|
||||
// Contexts
|
||||
loadContextsPlugin: context.load,
|
||||
closeContextsPlugin: context.close
|
||||
};
|
||||
|
Reference in New Issue
Block a user