mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Merge branch 'master' into runtime-api
This commit is contained in:
@@ -88,6 +88,7 @@ function getVersion() {
|
||||
}
|
||||
|
||||
function start() {
|
||||
|
||||
return i18n.registerMessageCatalog("runtime",path.resolve(path.join(__dirname,"locales")),"runtime.json")
|
||||
.then(function() { return storage.init(runtime)})
|
||||
.then(function() { return settings.load(storage)})
|
||||
@@ -163,8 +164,10 @@ function start() {
|
||||
if (settings.httpStatic) {
|
||||
log.info(log._("runtime.paths.httpStatic",{path:path.resolve(settings.httpStatic)}));
|
||||
}
|
||||
redNodes.loadFlows().then(redNodes.startFlows).catch(function(err) {});
|
||||
started = true;
|
||||
redNodes.loadContextsPlugin().then(function () {
|
||||
redNodes.loadFlows().then(redNodes.startFlows).catch(function(err) {});
|
||||
started = true;
|
||||
});
|
||||
}).catch(function(err) {
|
||||
console.log(err);
|
||||
});
|
||||
@@ -230,7 +233,9 @@ function stop() {
|
||||
clearTimeout(reinstallTimeout);
|
||||
}
|
||||
started = false;
|
||||
return redNodes.stopFlows();
|
||||
return redNodes.stopFlows().then(function(){
|
||||
return redNodes.closeContextsPlugin();
|
||||
});
|
||||
}
|
||||
|
||||
var runtime = module.exports = {
|
||||
|
@@ -155,5 +155,15 @@
|
||||
"readme": "### About\n\nThis is your project's README.md file. It helps users understand what your\nproject does, how to use it and anything else they may need to know."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"context": {
|
||||
"log-store-init": "Context store : '__name__' [__info__]",
|
||||
"error-loading-module": "Error loading context store '__module__': __message__ ",
|
||||
"error-module-not-defined": "Context store '__storage__' missing 'module' option",
|
||||
"error-invalid-module-name": "Invalid context store name: '__name__'",
|
||||
"error-invalid-default-module": "Default context store unknown: '__storage__'",
|
||||
"unknown-store": "Unknown context store '__name__' specified. Using default store."
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,10 +1,169 @@
|
||||
{
|
||||
"runtime": {
|
||||
"welcome": "Welcome to Node-RED",
|
||||
"version": "__component__ バージョン: __version__",
|
||||
"unsupported_version": "__component__ は未サポートのバージョンです。必要: __requires__ 検知: __version__",
|
||||
"paths": {
|
||||
"settings": "設定ファイル: __path__",
|
||||
"httpStatic": "HTTP Static : __path__"
|
||||
}
|
||||
},
|
||||
|
||||
"server": {
|
||||
"loading": "パレットノードのロード",
|
||||
"palette-editor": {
|
||||
"disabled": "パレットエディタを無効化 : ユーザ設定",
|
||||
"npm-not-found": "バレットエディタを無効化 : npmコマンドが見つかりません"
|
||||
},
|
||||
"errors": "__count__ 個のノードの登録に失敗しました",
|
||||
"errors_plural": "__count__ 個のノードの登録に失敗しました",
|
||||
"errors-help": "詳細は -v を指定して実行してください",
|
||||
"missing-modules": "不足しているノードモジュール:",
|
||||
"node-version-mismatch": "ノードモジュールはこのバージョンではロードできません。必要なバージョン: __version__ ",
|
||||
"type-already-registered": "'__type__' はモジュール __module__ で登録済みです",
|
||||
"removing-modules": "設定からモジュールを削除します",
|
||||
"added-types": "追加したノード:",
|
||||
"removed-types": "削除したノード:",
|
||||
"install": {
|
||||
"invalid": "不正なモジュール名",
|
||||
"installing": "モジュール__name__, バージョン: __version__をインスートールします",
|
||||
"installed": "モジュール __name__ をインストールしました",
|
||||
"install-failed": "インストールに失敗しました",
|
||||
"install-failed-long": "モジュール __name__ のインストールに失敗しました:",
|
||||
"install-failed-not-found": "$t(install-failed-long) モジュールが見つかりません",
|
||||
"upgrading": "モジュール __name__ をバージョン __version__ に更新します",
|
||||
"upgraded": "モジュール __name__ を更新しました。新しいバージョンを使うには、Node-REDを再起動してください。",
|
||||
"upgrade-failed-not-found": "$t(server.install.install-failed-long) バージョンが見つかりません",
|
||||
"install-failed-not-found": "$t(server.install.install-failed-long) モジュールが見つかりません",
|
||||
"uninstalling": "モジュールをアンインストールします: __name__",
|
||||
"uninstall-failed": "アンインストールに失敗しました",
|
||||
"uninstall-failed-long": "モジュール __name__ のアンインストールに失敗しました:",
|
||||
"uninstalled": "モジュール __name__ をアンインストールしました"
|
||||
},
|
||||
"unable-to-listen": "__listenpath__ に対してlistenできません",
|
||||
"port-in-use": "エラー: ポートが使用中です",
|
||||
"uncaught-exception": "未補足の例外:",
|
||||
"admin-ui-disabled": "管理UIを無効化しました",
|
||||
"now-running": "サーバは __listenpath__ で実行中です",
|
||||
"failed-to-start": "サーバの起動に失敗しました:",
|
||||
"headless-mode": "ヘッドレスモードで実行中です",
|
||||
"httpadminauth-deprecated": "httpAdminAuthは非推奨です。代わりに adminAuth を使用してください"
|
||||
},
|
||||
|
||||
"api": {
|
||||
"flows": {
|
||||
"error-save": "フローの保存エラー: __message__",
|
||||
"error-reload": "フローの読み込みエラー: __message__"
|
||||
},
|
||||
"library": {
|
||||
"error-load-entry": "ライブラリエントリ '__path__' の読み込みエラー: __message__",
|
||||
"error-save-entry": "ライブラリエントリ '__path__' の保存エラー: __message__",
|
||||
"error-load-flow": "フロー '__path__' の読み込みエラー: __message__",
|
||||
"error-save-flow": "フロー '__path__' の保存エラー: __message__"
|
||||
},
|
||||
"nodes": {
|
||||
"enabled": "ノードを有効化しました:",
|
||||
"disabled": "ノードを無効化しました:",
|
||||
"error-enable": "ノードの有効化に失敗しました:"
|
||||
}
|
||||
},
|
||||
|
||||
"comms": {
|
||||
"error": "通信チャネルエラー: __message__",
|
||||
"error-server": "サーバエラー: __message__",
|
||||
"error-send": "送信エラー: __message__"
|
||||
},
|
||||
|
||||
"settings": {
|
||||
"user-not-available": "ユーザ設定を保存できません: __message__",
|
||||
"not-available": "設定が利用できません",
|
||||
"property-read-only": "プロパティ '__prop__' は読み出し専用です"
|
||||
},
|
||||
|
||||
"nodes": {
|
||||
"credentials": {
|
||||
"error":"クレデンシャルの読み込みエラー: __message__",
|
||||
"error-saving":"クレデンシャルの保存エラー: __message__",
|
||||
"not-registered": "クレデンシャル '__type__' は登録されていません",
|
||||
"system-key-warning": "\n\n---------------------------------------------------------------------\nフローのクレデンシャルファイルはシステム生成キーで暗号化されています。\n\nシステム生成キーを何らかの理由で失った場合、クレデンシャルファイルを\n復元することはできません。その場合、ファイルを削除してクレデンシャルを\n再入力しなければなりません。\n\n設定ファイル内で 'credentialSecret' オプションを使って独自キーを設定\nします。変更を次にデプロイする際、Node-REDは選択したキーを用いてクレ\nデンシャルを再暗号化します。 \n\n---------------------------------------------------------------------\n"
|
||||
},
|
||||
"flows": {
|
||||
"registered-missing": "欠落しているノードを登録します: __type__",
|
||||
"error": "フローの読み込みエラー: __message__",
|
||||
"starting-modified-nodes": "更新されたノードを開始します",
|
||||
"starting-modified-flows": "更新されたフローを開始します",
|
||||
"starting-flows": "フローを開始します",
|
||||
"started-modified-nodes": "更新されたノードを開始しました",
|
||||
"started-modified-flows": "更新されたフローを開始しました",
|
||||
"started-flows": "フローを開始しました",
|
||||
"stopping-modified-nodes": "更新されたノードを停止します",
|
||||
"stopping-modified-flows": "更新されたフローを停止します",
|
||||
"stopping-flows": "フローを停止します",
|
||||
"stopped-modified-nodes": "更新されたノードを停止しました",
|
||||
"stopped-modified-flows": "更新されたフローを停止しました",
|
||||
"stopped-flows": "フローを停止しました",
|
||||
"stopped": "停止しました",
|
||||
"stopping-error": "ノードの停止に失敗しました: __message__",
|
||||
"added-flow": "フローを追加します: __label__",
|
||||
"updated-flow": "フローを更新しました: __label__",
|
||||
"removed-flow": "フローを削除しました: __label__",
|
||||
"missing-types": "欠落しているノードが登録されるのを待っています:",
|
||||
"missing-type-provided": " - __type__ (npmモジュール __module__ からインストールされました)",
|
||||
"missing-type-install-1": "欠落しているモジュールをインストールするには次のコマンドを実行してください:",
|
||||
"missing-type-install-2": "コマンドの実行は次のディレクトリで行います:"
|
||||
},
|
||||
"flow": {
|
||||
"unknown-type": "不明なノード: __type__",
|
||||
"missing-types": "欠落したノード",
|
||||
"error-loop": "メッセージの例外補足回数が最大値を超えました"
|
||||
},
|
||||
"index": {
|
||||
"unrecognised-id": "不明なID: __id__",
|
||||
"type-in-use": "ノードは使用中です: __msg__",
|
||||
"unrecognised-module": "不明なモジュール: __module__"
|
||||
},
|
||||
"registry": {
|
||||
"localfilesystem": {
|
||||
"module-not-found": "モジュール '__module__' が見つかりません"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"storage": {
|
||||
"index": {
|
||||
"forbidden-flow-name": "不正なフロー名"
|
||||
},
|
||||
"localfilesystem": {
|
||||
"user-dir": "ユーザディレクトリ : __path__",
|
||||
"flows-file": "フローファイル : __path__",
|
||||
"create": "__type__ ファイルを作成します",
|
||||
"empty": "既存の __type__ ファイルが空です",
|
||||
"invalid": "既存の __type__ ファイルはJSON形式ではありません",
|
||||
"restore": " __type__ ファイルをバックアップ __path__ から復元します",
|
||||
"restore-fail": "__type__ ファイルをバックアップから復元するのに失敗しました : __message__",
|
||||
"fsync-fail": "ファイル __path__ のディスクへの書き出しに失敗しました : __message__",
|
||||
"projects": {
|
||||
"changing-project": "プロジェクトを設定します : __project__",
|
||||
"active-project": "選択中のプロジェクト : __project__",
|
||||
"project-not-found": "プロジェクトが見つかりません : __project__",
|
||||
"no-active-project": "プロジェクトが選択されていません : デフォルトのフローファイルを使用します",
|
||||
"disabled": "プロジェクトは無効化されています : editorTheme.projects.enabled=false",
|
||||
"disabledNoFlag": "プロジェクトは無効化されています : 有効にするには editorTheme.projects.enabled=true を設定してください",
|
||||
"git-not-found": "プロジェクトは無効化されています : gitコマンドが見つかりません",
|
||||
"git-version-old": "プロジェクトは無効化されています : git __version__ はサポートされていません。2.xが必要です。",
|
||||
"summary": "Node-REDプロジェクト",
|
||||
"readme": "### 説明\nこれはプロジェクトのREADME.mdファイルです。このファイルには、\nプロジェクトの説明、利用方法、その他の情報を記載します。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"context": {
|
||||
"log-store-init": "コンテクストストア : '__name__' [__info__]",
|
||||
"error-loading-module": "コンテクストストア '__module__' のロードでエラーが発生しました: __message__ ",
|
||||
"error-module-not-defined": "コンテクストストア '__storage__' に 'module' オプションが指定されていません",
|
||||
"error-invalid-module-name": "不正なコンテクストストア名: '__name__'",
|
||||
"error-invalid-default-module": "デフォルトコンテクストストアが不明: '__storage__'",
|
||||
"unknown-store": "不明なコンテクストストア '__name__' が指定されました。デフォルトストアを使用します。"
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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,91 +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)) {
|
||||
var idParts = id.split(":");
|
||||
if (!flowConfig.allNodes.hasOwnProperty(idParts[0])) {
|
||||
delete contexts[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = {
|
||||
init: function(settings) {
|
||||
globalContext = createContext("global",settings.functionGlobalContext || {});
|
||||
},
|
||||
get: getContext,
|
||||
delete: deleteContext,
|
||||
clean:clean
|
||||
};
|
374
red/runtime/nodes/context/index.js
Normal file
374
red/runtime/nodes/context/index.js
Normal file
@@ -0,0 +1,374 @@
|
||||
/**
|
||||
* 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("../../../util").log; // TODO: separate module
|
||||
var memory = require("./memory");
|
||||
|
||||
var settings;
|
||||
|
||||
// A map of scope id to context instance
|
||||
var contexts = {};
|
||||
|
||||
// A map of store name to instance
|
||||
var stores = {};
|
||||
var storeList = [];
|
||||
var defaultStore;
|
||||
|
||||
// Whether there context storage has been configured or left as default
|
||||
var hasConfiguredStore = false;
|
||||
|
||||
// Unknown Stores
|
||||
var unknownStores = {};
|
||||
|
||||
function logUnknownStore(name) {
|
||||
if (name) {
|
||||
var count = unknownStores[name] || 0;
|
||||
if (count == 0) {
|
||||
log.warn(log._("context.unknown-store", {name: name}));
|
||||
count++;
|
||||
unknownStores[name] = count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function init(_settings) {
|
||||
settings = _settings;
|
||||
contexts = {};
|
||||
stores = {};
|
||||
storeList = [];
|
||||
hasConfiguredStore = false;
|
||||
var seed = settings.functionGlobalContext || {};
|
||||
contexts['global'] = createContext("global",seed);
|
||||
// create a default memory store - used by the unit tests that skip the full
|
||||
// `load()` initialisation sequence.
|
||||
// If the user has any stores configured, this will be disgarded
|
||||
stores["_"] = new memory();
|
||||
defaultStore = "memory";
|
||||
}
|
||||
|
||||
function load() {
|
||||
return new Promise(function(resolve,reject) {
|
||||
// load & init plugins in settings.contextStorage
|
||||
var plugins = settings.contextStorage || {};
|
||||
var defaultIsAlias = false;
|
||||
var promises = [];
|
||||
if (plugins && Object.keys(plugins).length > 0) {
|
||||
var hasDefault = plugins.hasOwnProperty('default');
|
||||
var defaultName;
|
||||
for (var pluginName in plugins) {
|
||||
if (plugins.hasOwnProperty(pluginName)) {
|
||||
// "_" is a reserved name - do not allow it to be overridden
|
||||
if (pluginName === "_") {
|
||||
continue;
|
||||
}
|
||||
if (!/^[a-zA-Z0-9_]+$/.test(pluginName)) {
|
||||
return reject(new Error(log._("context.error-invalid-module-name", {name:pluginName})));
|
||||
}
|
||||
|
||||
// Check if this is setting the 'default' context to be a named plugin
|
||||
if (pluginName === "default" && typeof plugins[pluginName] === "string") {
|
||||
// Check the 'default' alias exists before initialising anything
|
||||
if (!plugins.hasOwnProperty(plugins[pluginName])) {
|
||||
return reject(new Error(log._("context.error-invalid-default-module", {storage:plugins["default"]})));
|
||||
}
|
||||
defaultIsAlias = true;
|
||||
continue;
|
||||
}
|
||||
if (!hasDefault && !defaultName) {
|
||||
defaultName = pluginName;
|
||||
}
|
||||
var plugin;
|
||||
if (plugins[pluginName].hasOwnProperty("module")) {
|
||||
// Get the provided config and copy in the 'approved' top-level settings (eg userDir)
|
||||
var config = plugins[pluginName].config || {};
|
||||
copySettings(config, settings);
|
||||
|
||||
if (typeof plugins[pluginName].module === "string") {
|
||||
// This config identifies the module by name - assume it is a built-in one
|
||||
// TODO: check it exists locally, if not, try to require it as-is
|
||||
try {
|
||||
plugin = require("./"+plugins[pluginName].module);
|
||||
} catch(err) {
|
||||
return reject(new Error(log._("context.error-loading-module", {module:plugins[pluginName].module,message:err.toString()})));
|
||||
}
|
||||
} else {
|
||||
// Assume `module` is an already-required module we can use
|
||||
plugin = plugins[pluginName].module;
|
||||
}
|
||||
try {
|
||||
// Create a new instance of the plugin by calling its module function
|
||||
stores[pluginName] = plugin(config);
|
||||
log.info(log._("context.log-store-init", {name:pluginName, info:"module="+plugins[pluginName].module}));
|
||||
} catch(err) {
|
||||
return reject(new Error(log._("context.error-loading-module",{module:pluginName,message:err.toString()})));
|
||||
}
|
||||
} else {
|
||||
// Plugin does not specify a 'module'
|
||||
return reject(new Error(log._("context.error-module-not-defined", {storage:pluginName})));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open all of the configured contexts
|
||||
for (var plugin in stores) {
|
||||
if (stores.hasOwnProperty(plugin)) {
|
||||
promises.push(stores[plugin].open());
|
||||
}
|
||||
}
|
||||
// There is a 'default' listed in the configuration
|
||||
if (hasDefault) {
|
||||
// If 'default' is an alias, point it at the right module - we have already
|
||||
// checked that it exists. If it isn't an alias, then it will
|
||||
// already be set to a configured store
|
||||
if (defaultIsAlias) {
|
||||
stores["_"] = stores[plugins["default"]];
|
||||
defaultStore = plugins["default"];
|
||||
} else {
|
||||
stores["_"] = stores["default"];
|
||||
defaultStore = "default";
|
||||
}
|
||||
} else if (defaultName) {
|
||||
// No 'default' listed, so pick first in list as the default
|
||||
stores["_"] = stores[defaultName];
|
||||
defaultStore = defaultName;
|
||||
defaultIsAlias = true;
|
||||
} else {
|
||||
// else there were no stores list the config object - fall through
|
||||
// to below where we default to a memory store
|
||||
storeList = ["memory"];
|
||||
defaultStore = "memory";
|
||||
}
|
||||
hasConfiguredStore = true;
|
||||
storeList = Object.keys(stores).filter(n=>!(defaultIsAlias && n==="default") && n!== "_");
|
||||
} else {
|
||||
// No configured plugins
|
||||
log.info(log._("context.log-store-init", {name:"default", info:"module=memory"}));
|
||||
promises.push(stores["_"].open())
|
||||
storeList = ["memory"];
|
||||
defaultStore = "memory";
|
||||
}
|
||||
return resolve(Promise.all(promises));
|
||||
});
|
||||
}
|
||||
|
||||
function copySettings(config, settings){
|
||||
var copy = ["userDir"]
|
||||
config.settings = {};
|
||||
copy.forEach(function(setting){
|
||||
config.settings[setting] = clone(settings[setting]);
|
||||
});
|
||||
}
|
||||
|
||||
function getContextStorage(storage) {
|
||||
if (stores.hasOwnProperty(storage)) {
|
||||
// A known context
|
||||
return stores[storage];
|
||||
} else if (stores.hasOwnProperty("_")) {
|
||||
// Not known, but we have a default to fall back to
|
||||
if (storage !== defaultStore) {
|
||||
// It isn't the default store either, so log it
|
||||
logUnknownStore(storage);
|
||||
}
|
||||
return stores["_"];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function createContext(id,seed) {
|
||||
// Seed is only set for global context - sourced from functionGlobalContext
|
||||
var scope = id;
|
||||
var obj = seed || {};
|
||||
var seedKeys;
|
||||
var insertSeedValues;
|
||||
if (seed) {
|
||||
seedKeys = Object.keys(seed);
|
||||
insertSeedValues = function(keys,values) {
|
||||
if (!Array.isArray(keys)) {
|
||||
if (values[0] === undefined) {
|
||||
values[0] = seed[keys];
|
||||
}
|
||||
} else {
|
||||
for (var i=0;i<keys.length;i++) {
|
||||
if (values[i] === undefined) {
|
||||
values[i] = seed[keys[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj.get = function(key, storage, callback) {
|
||||
var context;
|
||||
if (!storage && !callback) {
|
||||
context = stores["_"];
|
||||
} else {
|
||||
if (typeof storage === 'function') {
|
||||
callback = storage;
|
||||
storage = "_";
|
||||
}
|
||||
if (callback && typeof callback !== 'function'){
|
||||
throw new Error("Callback must be a function");
|
||||
}
|
||||
context = getContextStorage(storage);
|
||||
}
|
||||
if (callback) {
|
||||
if (!seed) {
|
||||
context.get(scope,key,callback);
|
||||
} else {
|
||||
context.get(scope,key,function() {
|
||||
if (arguments[0]) {
|
||||
callback(arguments[0]);
|
||||
return;
|
||||
}
|
||||
var results = Array.prototype.slice.call(arguments,[1]);
|
||||
insertSeedValues(key,results);
|
||||
// Put the err arg back
|
||||
results.unshift(undefined);
|
||||
callback.apply(null,results);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// No callback, attempt to do this synchronously
|
||||
var results = context.get(scope,key);
|
||||
if (seed) {
|
||||
if (Array.isArray(key)) {
|
||||
insertSeedValues(key,results);
|
||||
} else if (results === undefined){
|
||||
results = seed[key];
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
};
|
||||
obj.set = function(key, value, storage, callback) {
|
||||
var context;
|
||||
if (!storage && !callback) {
|
||||
context = stores["_"];
|
||||
} else {
|
||||
if (typeof storage === 'function') {
|
||||
callback = storage;
|
||||
storage = "_";
|
||||
}
|
||||
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 = stores["_"];
|
||||
} else {
|
||||
if (typeof storage === 'function') {
|
||||
callback = storage;
|
||||
storage = "_";
|
||||
}
|
||||
if (callback && typeof callback !== 'function') {
|
||||
throw new Error("Callback must be a function");
|
||||
}
|
||||
context = getContextStorage(storage);
|
||||
}
|
||||
if (seed) {
|
||||
if (callback) {
|
||||
context.keys(scope, function(err,keys) {
|
||||
callback(err,Array.from(new Set(seedKeys.concat(keys)).keys()));
|
||||
});
|
||||
} else {
|
||||
var keys = context.keys(scope);
|
||||
return Array.from(new Set(seedKeys.concat(keys)).keys())
|
||||
}
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
newContext.global = contexts['global'];
|
||||
contexts[contextId] = newContext;
|
||||
return newContext;
|
||||
}
|
||||
|
||||
function deleteContext(id,flowId) {
|
||||
if(!hasConfiguredStore){
|
||||
// only delete context if there's no configured storage.
|
||||
var contextId = id;
|
||||
if (flowId) {
|
||||
contextId = id+":"+flowId;
|
||||
}
|
||||
delete contexts[contextId];
|
||||
return stores["_"].delete(contextId);
|
||||
}else{
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
function clean(flowConfig) {
|
||||
var promises = [];
|
||||
for(var plugin in stores){
|
||||
if(stores.hasOwnProperty(plugin)){
|
||||
promises.push(stores[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 stores){
|
||||
if(stores.hasOwnProperty(plugin)){
|
||||
promises.push(stores[plugin].close());
|
||||
}
|
||||
}
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
function listStores() {
|
||||
return {default:defaultStore,stores:storeList};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: init,
|
||||
load: load,
|
||||
listStores: listStores,
|
||||
get: getContext,
|
||||
delete: deleteContext,
|
||||
clean: clean,
|
||||
close: close
|
||||
};
|
291
red/runtime/nodes/context/localfilesystem.js
Normal file
291
red/runtime/nodes/context/localfilesystem.js
Normal file
@@ -0,0 +1,291 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
||||
/**
|
||||
* Local file-system based context storage
|
||||
*
|
||||
* Configuration options:
|
||||
* {
|
||||
* base: "contexts", // the base directory to use
|
||||
* // default: "contexts"
|
||||
* dir: "/path/to/storage", // the directory to create the base directory in
|
||||
* // default: settings.userDir
|
||||
* cache: true // whether to cache contents in memory
|
||||
* // default: true
|
||||
* }
|
||||
*
|
||||
*
|
||||
* $HOME/.node-red/contexts
|
||||
* ├── global
|
||||
* │ └── global_context.json
|
||||
* ├── <id of Flow 1>
|
||||
* │ ├── flow_context.json
|
||||
* │ ├── <id of Node a>.json
|
||||
* │ └── <id of Node b>.json
|
||||
* └── <id of Flow 2>
|
||||
* ├── flow_context.json
|
||||
* ├── <id of Node x>.json
|
||||
* └── <id of Node y>.json
|
||||
*/
|
||||
|
||||
var fs = require('fs-extra');
|
||||
var path = require("path");
|
||||
var util = require("../../util");
|
||||
|
||||
var MemoryStore = require("./memory");
|
||||
|
||||
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);
|
||||
if (config.hasOwnProperty('cache')?config.cache:true) {
|
||||
this.cache = MemoryStore({});
|
||||
}
|
||||
}
|
||||
|
||||
LocalFileSystem.prototype.open = function(){
|
||||
var self = this;
|
||||
if (this.cache) {
|
||||
var scopes = [];
|
||||
var promises = [];
|
||||
var subdirs = [];
|
||||
var subdirPromises = [];
|
||||
return fs.readdir(self.storageBaseDir).then(function(dirs){
|
||||
dirs.forEach(function(fn) {
|
||||
var p = getStoragePath(self.storageBaseDir ,fn)+".json";
|
||||
scopes.push(fn);
|
||||
promises.push(loadFile(p));
|
||||
subdirs.push(path.join(self.storageBaseDir,fn));
|
||||
subdirPromises.push(fs.readdir(path.join(self.storageBaseDir,fn)));
|
||||
})
|
||||
return Promise.all(subdirPromises);
|
||||
}).then(function(dirs) {
|
||||
dirs.forEach(function(files,i) {
|
||||
files.forEach(function(fn) {
|
||||
if (fn !== 'flow.json' && fn !== 'global.json') {
|
||||
scopes.push(fn.substring(0,fn.length-5)+":"+scopes[i]);
|
||||
promises.push(loadFile(path.join(subdirs[i],fn)))
|
||||
}
|
||||
});
|
||||
})
|
||||
return Promise.all(promises);
|
||||
}).then(function(res) {
|
||||
scopes.forEach(function(scope,i) {
|
||||
var data = res[i]?JSON.parse(res[i]):{};
|
||||
Object.keys(data).forEach(function(key) {
|
||||
self.cache.set(scope,key,data[key]);
|
||||
})
|
||||
});
|
||||
}).catch(function(err){
|
||||
if(err.code == 'ENOENT') {
|
||||
return fs.ensureDir(self.storageBaseDir);
|
||||
}else{
|
||||
return Promise.reject(err);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
LocalFileSystem.prototype.close = function(){
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
LocalFileSystem.prototype.get = function(scope, key, callback) {
|
||||
if (this.cache) {
|
||||
return this.cache.get(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){
|
||||
data = JSON.parse(data);
|
||||
if (!Array.isArray(key)) {
|
||||
callback(null, util.getObjectProperty(data,key));
|
||||
} else {
|
||||
var results = [undefined];
|
||||
for (var i=0;i<key.length;i++) {
|
||||
results.push(util.getObjectProperty(data,key[i]))
|
||||
}
|
||||
callback.apply(null,results);
|
||||
}
|
||||
}else{
|
||||
callback(null, undefined);
|
||||
}
|
||||
}).catch(function(err){
|
||||
callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
LocalFileSystem.prototype.set = function(scope, key, value, callback) {
|
||||
var storagePath = getStoragePath(this.storageBaseDir ,scope);
|
||||
if (this.cache) {
|
||||
this.cache.set(scope,key,value,callback);
|
||||
// With cache enabled, no need to re-read the file prior to writing.
|
||||
var newContext = this.cache._export()[scope];
|
||||
fs.outputFile(storagePath + ".json", JSON.stringify(newContext, undefined, 4), "utf8").catch(function(err) {
|
||||
});
|
||||
} else if (callback && typeof callback !== 'function') {
|
||||
throw new Error("Callback must be a function");
|
||||
} else {
|
||||
loadFile(storagePath + ".json").then(function(data){
|
||||
var obj = data ? JSON.parse(data) : {}
|
||||
if (!Array.isArray(key)) {
|
||||
key = [key];
|
||||
value = [value];
|
||||
} else if (!Array.isArray(value)) {
|
||||
// key is an array, but value is not - wrap it as an array
|
||||
value = [value];
|
||||
}
|
||||
for (var i=0;i<key.length;i++) {
|
||||
var v = null;
|
||||
if (i<value.length) {
|
||||
v = value[i];
|
||||
}
|
||||
util.setObjectProperty(obj,key[i],v);
|
||||
}
|
||||
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 (this.cache) {
|
||||
return this.cache.keys(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 cachePromise;
|
||||
if (this.cache) {
|
||||
cachePromise = this.cache.delete(scope);
|
||||
} else {
|
||||
cachePromise = Promise.resolve();
|
||||
}
|
||||
var that = this;
|
||||
return cachePromise.then(function() {
|
||||
var storagePath = getStoragePath(that.storageBaseDir,scope);
|
||||
return fs.remove(storagePath + ".json");
|
||||
});
|
||||
}
|
||||
|
||||
LocalFileSystem.prototype.clean = function(activeNodes){
|
||||
var self = this;
|
||||
var cachePromise;
|
||||
if (this.cache) {
|
||||
cachePromise = this.cache.clean(activeNodes);
|
||||
} else {
|
||||
cachePromise = Promise.resolve();
|
||||
}
|
||||
return cachePromise.then(function() {
|
||||
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);
|
||||
};
|
166
red/runtime/nodes/context/memory.js
Normal file
166
red/runtime/nodes/context/memory.js
Normal file
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* 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._getOne = function(scope, key) {
|
||||
var value;
|
||||
var error;
|
||||
if(this.data[scope]){
|
||||
value = util.getObjectProperty(this.data[scope], key);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
Memory.prototype.get = function(scope, key, callback) {
|
||||
var value;
|
||||
var error;
|
||||
if (!Array.isArray(key)) {
|
||||
try {
|
||||
value = this._getOne(scope,key);
|
||||
} catch(err) {
|
||||
if (!callback) {
|
||||
throw err;
|
||||
}
|
||||
error = err;
|
||||
}
|
||||
if (callback) {
|
||||
callback(error,value);
|
||||
return;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
value = [];
|
||||
for (var i=0; i<key.length; i++) {
|
||||
try {
|
||||
value.push(this._getOne(scope,key[i]));
|
||||
} catch(err) {
|
||||
if (!callback) {
|
||||
throw err;
|
||||
} else {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (callback) {
|
||||
callback.apply(null, [undefined].concat(value));
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
Memory.prototype.set = function(scope, key, value, callback) {
|
||||
if(!this.data[scope]){
|
||||
this.data[scope] = {};
|
||||
}
|
||||
var error;
|
||||
if (!Array.isArray(key)) {
|
||||
key = [key];
|
||||
value = [value];
|
||||
} else if (!Array.isArray(value)) {
|
||||
// key is an array, but value is not - wrap it as an array
|
||||
value = [value];
|
||||
}
|
||||
try {
|
||||
for (var i=0; i<key.length; i++) {
|
||||
var v = null;
|
||||
if (i < value.length) {
|
||||
v = value[i];
|
||||
}
|
||||
util.setObjectProperty(this.data[scope],key[i],v);
|
||||
}
|
||||
} catch(err) {
|
||||
if (callback) {
|
||||
error = err;
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
if(callback){
|
||||
callback(error);
|
||||
}
|
||||
};
|
||||
|
||||
Memory.prototype.keys = function(scope, callback){
|
||||
var values = [];
|
||||
var error;
|
||||
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){
|
||||
error = err;
|
||||
}else{
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
if(callback){
|
||||
if(error){
|
||||
callback(error);
|
||||
} else {
|
||||
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._export = function() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
module.exports = function(config){
|
||||
return new Memory(config);
|
||||
};
|
@@ -163,11 +163,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 {
|
||||
|
@@ -187,6 +187,8 @@ module.exports = {
|
||||
createNode: createNode,
|
||||
getNode: flows.get,
|
||||
eachNode: flows.eachNode,
|
||||
getContext: context.get,
|
||||
|
||||
|
||||
paletteEditorEnabled: registry.paletteEditorEnabled,
|
||||
installModule: installModule,
|
||||
@@ -236,5 +238,10 @@ module.exports = {
|
||||
setCredentialSecret: credentials.setKey,
|
||||
clearCredentials: credentials.clear,
|
||||
exportCredentials: credentials.export,
|
||||
getCredentialKeyType: credentials.getKeyType
|
||||
getCredentialKeyType: credentials.getKeyType,
|
||||
|
||||
// Contexts
|
||||
loadContextsPlugin: context.load,
|
||||
closeContextsPlugin: context.close,
|
||||
listContextStores: context.listStores
|
||||
};
|
||||
|
@@ -48,7 +48,9 @@ function runGitCommand(args,cwd,env) {
|
||||
var err = new Error(stderr);
|
||||
err.stdout = stdout;
|
||||
err.stderr = stderr;
|
||||
if(/Connection refused/i.test(stderr)) {
|
||||
if (/Connection refused/i.test(stderr)) {
|
||||
err.code = "git_connection_failed";
|
||||
} else if (/Connection timed out/i.test(stderr)) {
|
||||
err.code = "git_connection_failed";
|
||||
} else if (/fatal: could not read/i.test(stderr)) {
|
||||
// Username/Password
|
||||
|
@@ -16,6 +16,8 @@
|
||||
|
||||
var clone = require("clone");
|
||||
var jsonata = require("jsonata");
|
||||
var safeJSONStringify = require("json-stringify-safe");
|
||||
var util = require("util");
|
||||
|
||||
function generateId() {
|
||||
return (1+Math.random()*4294967295).toString(16);
|
||||
@@ -233,10 +235,13 @@ function normalisePropertyExpression(str) {
|
||||
}
|
||||
|
||||
function getMessageProperty(msg,expr) {
|
||||
var result = null;
|
||||
if (expr.indexOf('msg.')===0) {
|
||||
expr = expr.substring(4);
|
||||
}
|
||||
return getObjectProperty(msg,expr);
|
||||
}
|
||||
function getObjectProperty(msg,expr) {
|
||||
var result = null;
|
||||
var msgPropParts = normalisePropertyExpression(expr);
|
||||
var m;
|
||||
msgPropParts.reduce(function(obj, key) {
|
||||
@@ -247,12 +252,15 @@ function getMessageProperty(msg,expr) {
|
||||
}
|
||||
|
||||
function setMessageProperty(msg,prop,value,createMissing) {
|
||||
if (typeof createMissing === 'undefined') {
|
||||
createMissing = (typeof value !== 'undefined');
|
||||
}
|
||||
if (prop.indexOf('msg.')===0) {
|
||||
prop = prop.substring(4);
|
||||
}
|
||||
return setObjectProperty(msg,prop,value,createMissing);
|
||||
}
|
||||
function setObjectProperty(msg,prop,value,createMissing) {
|
||||
if (typeof createMissing === 'undefined') {
|
||||
createMissing = (typeof value !== 'undefined');
|
||||
}
|
||||
var msgPropParts = normalisePropertyExpression(prop);
|
||||
var depth = 0;
|
||||
var length = msgPropParts.length;
|
||||
@@ -284,7 +292,7 @@ function setMessageProperty(msg,prop,value,createMissing) {
|
||||
}
|
||||
obj = obj[key];
|
||||
} else {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
obj = obj[key];
|
||||
@@ -303,7 +311,7 @@ function setMessageProperty(msg,prop,value,createMissing) {
|
||||
}
|
||||
}
|
||||
|
||||
function evaluteEnvProperty(value) {
|
||||
function evaluateEnvProperty(value) {
|
||||
if (/^\${[^}]+}$/.test(value)) {
|
||||
// ${ENV_VAR}
|
||||
value = value.substring(2,value.length-1);
|
||||
@@ -320,35 +328,63 @@ function evaluteEnvProperty(value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
function evaluateNodeProperty(value, type, node, msg) {
|
||||
var parseContextStore = function(key) {
|
||||
var parts = {};
|
||||
var m = /^#:\((\S+?)\)::(.*)$/.exec(key);
|
||||
if (m) {
|
||||
parts.store = m[1];
|
||||
parts.key = m[2];
|
||||
} else {
|
||||
parts.key = key;
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
function evaluateNodeProperty(value, type, node, msg, callback) {
|
||||
var result = value;
|
||||
if (type === 'str') {
|
||||
return ""+value;
|
||||
result = ""+value;
|
||||
} else if (type === 'num') {
|
||||
return Number(value);
|
||||
result = Number(value);
|
||||
} else if (type === 'json') {
|
||||
return JSON.parse(value);
|
||||
result = JSON.parse(value);
|
||||
} else if (type === 're') {
|
||||
return new RegExp(value);
|
||||
result = new RegExp(value);
|
||||
} else if (type === 'date') {
|
||||
return Date.now();
|
||||
result = Date.now();
|
||||
} else if (type === 'bin') {
|
||||
var data = JSON.parse(value);
|
||||
return Buffer.from(data);
|
||||
result = Buffer.from(data);
|
||||
} else if (type === 'msg' && msg) {
|
||||
return getMessageProperty(msg,value);
|
||||
} else if (type === 'flow' && node) {
|
||||
return node.context().flow.get(value);
|
||||
} else if (type === 'global' && node) {
|
||||
return node.context().global.get(value);
|
||||
try {
|
||||
result = getMessageProperty(msg,value);
|
||||
} catch(err) {
|
||||
if (callback) {
|
||||
callback(err);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if ((type === 'flow' || type === 'global') && node) {
|
||||
var contextKey = parseContextStore(value);
|
||||
result = node.context()[type].get(contextKey.key,contextKey.store,callback);
|
||||
if (callback) {
|
||||
return;
|
||||
}
|
||||
} else if (type === 'bool') {
|
||||
return /^true$/i.test(value);
|
||||
result = /^true$/i.test(value);
|
||||
} else if (type === 'jsonata') {
|
||||
var expr = prepareJSONataExpression(value,node);
|
||||
return evaluateJSONataExpression(expr,msg);
|
||||
result = evaluateJSONataExpression(expr,msg);
|
||||
} else if (type === 'env') {
|
||||
return evaluteEnvProperty(value);
|
||||
result = evaluateEnvProperty(value);
|
||||
}
|
||||
if (callback) {
|
||||
callback(null,result);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function prepareJSONataExpression(value,node) {
|
||||
@@ -364,15 +400,44 @@ function prepareJSONataExpression(value,node) {
|
||||
})
|
||||
expr.registerFunction('clone', cloneMessage, '<(oa)-:o>');
|
||||
expr._legacyMode = /(^|[^a-zA-Z0-9_'"])msg([^a-zA-Z0-9_'"]|$)/.test(value);
|
||||
expr._node = node;
|
||||
return expr;
|
||||
}
|
||||
|
||||
function evaluateJSONataExpression(expr,msg) {
|
||||
function evaluateJSONataExpression(expr,msg,callback) {
|
||||
var context = msg;
|
||||
if (expr._legacyMode) {
|
||||
context = {msg:msg};
|
||||
}
|
||||
return expr.evaluate(context);
|
||||
var bindings = {};
|
||||
|
||||
if (callback) {
|
||||
// If callback provided, need to override the pre-assigned sync
|
||||
// context functions to be their async variants
|
||||
bindings.flowContext = function(val, store) {
|
||||
return new Promise((resolve,reject) => {
|
||||
expr._node.context().flow.get(val, store, function(err,value) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
bindings.globalContext = function(val, store) {
|
||||
return new Promise((resolve,reject) => {
|
||||
expr._node.context().global.get(val, store, function(err,value) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
return expr.evaluate(context, bindings, callback);
|
||||
}
|
||||
|
||||
|
||||
@@ -389,7 +454,135 @@ function normaliseNodeTypeName(name) {
|
||||
return result;
|
||||
}
|
||||
|
||||
function encodeObject(msg,opts) {
|
||||
var debuglength = 1000;
|
||||
if (opts && opts.hasOwnProperty('maxLength')) {
|
||||
debuglength = opts.maxLength;
|
||||
}
|
||||
var msgType = typeof msg.msg;
|
||||
if (msg.msg instanceof Error) {
|
||||
msg.format = "error";
|
||||
var errorMsg = {};
|
||||
if (msg.msg.name) {
|
||||
errorMsg.name = msg.msg.name;
|
||||
}
|
||||
if (msg.msg.hasOwnProperty('message')) {
|
||||
errorMsg.message = msg.msg.message;
|
||||
} else {
|
||||
errorMsg.message = msg.msg.toString();
|
||||
}
|
||||
msg.msg = JSON.stringify(errorMsg);
|
||||
} else if (msg.msg instanceof Buffer) {
|
||||
msg.format = "buffer["+msg.msg.length+"]";
|
||||
msg.msg = msg.msg.toString('hex');
|
||||
if (msg.msg.length > debuglength) {
|
||||
msg.msg = msg.msg.substring(0,debuglength);
|
||||
}
|
||||
} else if (msg.msg && msgType === 'object') {
|
||||
try {
|
||||
msg.format = msg.msg.constructor.name || "Object";
|
||||
// Handle special case of msg.req/res objects from HTTP In node
|
||||
if (msg.format === "IncomingMessage" || msg.format === "ServerResponse") {
|
||||
msg.format = "Object";
|
||||
}
|
||||
} catch(err) {
|
||||
msg.format = "Object";
|
||||
}
|
||||
if (/error/i.test(msg.format)) {
|
||||
msg.msg = JSON.stringify({
|
||||
name: msg.msg.name,
|
||||
message: msg.msg.message
|
||||
});
|
||||
} else {
|
||||
var isArray = util.isArray(msg.msg);
|
||||
if (isArray) {
|
||||
msg.format = "array["+msg.msg.length+"]";
|
||||
if (msg.msg.length > debuglength) {
|
||||
// msg.msg = msg.msg.slice(0,debuglength);
|
||||
msg.msg = {
|
||||
__enc__: true,
|
||||
type: "array",
|
||||
data: msg.msg.slice(0,debuglength),
|
||||
length: msg.msg.length
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isArray || (msg.format === "Object")) {
|
||||
msg.msg = safeJSONStringify(msg.msg, function(key, value) {
|
||||
if (key === '_req' || key === '_res') {
|
||||
value = {
|
||||
__enc__: true,
|
||||
type: "internal"
|
||||
}
|
||||
} else if (value instanceof Error) {
|
||||
value = value.toString()
|
||||
} else if (util.isArray(value) && value.length > debuglength) {
|
||||
value = {
|
||||
__enc__: true,
|
||||
type: "array",
|
||||
data: value.slice(0,debuglength),
|
||||
length: value.length
|
||||
}
|
||||
} else if (typeof value === 'string') {
|
||||
if (value.length > debuglength) {
|
||||
value = value.substring(0,debuglength)+"...";
|
||||
}
|
||||
} else if (typeof value === 'function') {
|
||||
value = {
|
||||
__enc__: true,
|
||||
type: "function"
|
||||
}
|
||||
} else if (typeof value === 'number') {
|
||||
if (isNaN(value) || value === Infinity || value === -Infinity) {
|
||||
value = {
|
||||
__enc__: true,
|
||||
type: "number",
|
||||
data: value.toString()
|
||||
}
|
||||
}
|
||||
} else if (value && value.constructor) {
|
||||
if (value.type === "Buffer") {
|
||||
value.__enc__ = true;
|
||||
value.length = value.data.length;
|
||||
if (value.length > debuglength) {
|
||||
value.data = value.data.slice(0,debuglength);
|
||||
}
|
||||
} else if (value.constructor.name === "ServerResponse") {
|
||||
value = "[internal]"
|
||||
} else if (value.constructor.name === "Socket") {
|
||||
value = "[internal]"
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}," ");
|
||||
} else {
|
||||
try { msg.msg = msg.msg.toString(); }
|
||||
catch(e) { msg.msg = "[Type not printable]"; }
|
||||
}
|
||||
}
|
||||
} else if (msgType === "function") {
|
||||
msg.format = "function";
|
||||
msg.msg = "[function]"
|
||||
} else if (msgType === "boolean") {
|
||||
msg.format = "boolean";
|
||||
msg.msg = msg.msg.toString();
|
||||
} else if (msgType === "number") {
|
||||
msg.format = "number";
|
||||
msg.msg = msg.msg.toString();
|
||||
} else if (msg.msg === null || msgType === "undefined") {
|
||||
msg.format = (msg.msg === null)?"null":"undefined";
|
||||
msg.msg = "(undefined)";
|
||||
} else {
|
||||
msg.format = "string["+msg.msg.length+"]";
|
||||
if (msg.msg.length > debuglength) {
|
||||
msg.msg = msg.msg.substring(0,debuglength)+"...";
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
encodeObject: encodeObject,
|
||||
ensureString: ensureString,
|
||||
ensureBuffer: ensureBuffer,
|
||||
cloneMessage: cloneMessage,
|
||||
@@ -397,9 +590,12 @@ module.exports = {
|
||||
generateId: generateId,
|
||||
getMessageProperty: getMessageProperty,
|
||||
setMessageProperty: setMessageProperty,
|
||||
getObjectProperty: getObjectProperty,
|
||||
setObjectProperty: setObjectProperty,
|
||||
evaluateNodeProperty: evaluateNodeProperty,
|
||||
normalisePropertyExpression: normalisePropertyExpression,
|
||||
normaliseNodeTypeName: normaliseNodeTypeName,
|
||||
prepareJSONataExpression: prepareJSONataExpression,
|
||||
evaluateJSONataExpression: evaluateJSONataExpression
|
||||
evaluateJSONataExpression: evaluateJSONataExpression,
|
||||
parseContextStore: parseContextStore
|
||||
};
|
||||
|
Reference in New Issue
Block a user