mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Update runtime apis to support multiple libraries
This commit is contained in:
parent
5e43a02cd3
commit
b581e33611
@ -93,9 +93,8 @@ module.exports = {
|
|||||||
// Library
|
// Library
|
||||||
var library = require("./library");
|
var library = require("./library");
|
||||||
library.init(runtimeAPI);
|
library.init(runtimeAPI);
|
||||||
editorApp.get("/library/flows",needsPermission("library.read"),library.getAll,apiUtil.errorHandler);
|
editorApp.get(/library\/([^\/]+)\/([^\/]+)(?:$|\/(.*))/,needsPermission("library.read"),library.getEntry);
|
||||||
editorApp.get(/library\/([^\/]+)(?:$|\/(.*))/,needsPermission("library.read"),library.getEntry);
|
editorApp.post(/library\/([^\/]+)\/([^\/]+)\/(.*)/,needsPermission("library.write"),library.saveEntry);
|
||||||
editorApp.post(/library\/([^\/]+)\/(.*)/,needsPermission("library.write"),library.saveEntry);
|
|
||||||
|
|
||||||
|
|
||||||
// Credentials
|
// Credentials
|
||||||
|
@ -25,23 +25,12 @@ module.exports = {
|
|||||||
init: function(_runtimeAPI) {
|
init: function(_runtimeAPI) {
|
||||||
runtimeAPI = _runtimeAPI;
|
runtimeAPI = _runtimeAPI;
|
||||||
},
|
},
|
||||||
|
|
||||||
getAll: function(req,res) {
|
|
||||||
var opts = {
|
|
||||||
user: req.user,
|
|
||||||
type: 'flows'
|
|
||||||
}
|
|
||||||
runtimeAPI.library.getEntries(opts).then(function(result) {
|
|
||||||
res.json(result);
|
|
||||||
}).catch(function(err) {
|
|
||||||
apiUtils.rejectHandler(req,res,err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getEntry: function(req,res) {
|
getEntry: function(req,res) {
|
||||||
var opts = {
|
var opts = {
|
||||||
user: req.user,
|
user: req.user,
|
||||||
type: req.params[0],
|
library: req.params[0],
|
||||||
path: req.params[1]||""
|
type: req.params[1],
|
||||||
|
path: req.params[2]||""
|
||||||
}
|
}
|
||||||
runtimeAPI.library.getEntry(opts).then(function(result) {
|
runtimeAPI.library.getEntry(opts).then(function(result) {
|
||||||
if (typeof result === "string") {
|
if (typeof result === "string") {
|
||||||
@ -62,8 +51,9 @@ module.exports = {
|
|||||||
saveEntry: function(req,res) {
|
saveEntry: function(req,res) {
|
||||||
var opts = {
|
var opts = {
|
||||||
user: req.user,
|
user: req.user,
|
||||||
type: req.params[0],
|
library: req.params[0],
|
||||||
path: req.params[1]||""
|
type: req.params[1],
|
||||||
|
path: req.params[2]||""
|
||||||
}
|
}
|
||||||
// TODO: horrible inconsistencies between flows and all other types
|
// TODO: horrible inconsistencies between flows and all other types
|
||||||
if (opts.type === "flows") {
|
if (opts.type === "flows") {
|
||||||
|
@ -78,7 +78,7 @@ RED.clipboard = (function() {
|
|||||||
var filename = $("#clipboard-dialog-tab-library-name").val().trim();
|
var filename = $("#clipboard-dialog-tab-library-name").val().trim();
|
||||||
var saveFlow = function() {
|
var saveFlow = function() {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url:'library/flows/'+selectedPath.path + filename,
|
url:'library/'+selectedPath.library+'/'+selectedPath.type+'/'+selectedPath.path + filename,
|
||||||
type: "POST",
|
type: "POST",
|
||||||
data: flowToExport,
|
data: flowToExport,
|
||||||
contentType: "application/json; charset=utf-8"
|
contentType: "application/json; charset=utf-8"
|
||||||
@ -139,7 +139,7 @@ RED.clipboard = (function() {
|
|||||||
} else {
|
} else {
|
||||||
var selectedPath = libraryBrowser.getSelected();
|
var selectedPath = libraryBrowser.getSelected();
|
||||||
if (selectedPath.path) {
|
if (selectedPath.path) {
|
||||||
$.get('library/flows/'+selectedPath.path, function(data) {
|
$.get('library/'+selectedPath.library+'/'+selectedPath.type+'/'+selectedPath.path, function(data) {
|
||||||
RED.view.importNodes(data,addNewFlow);
|
RED.view.importNodes(data,addNewFlow);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -609,59 +609,39 @@ RED.clipboard = (function() {
|
|||||||
$("#clipboard-dialog-download").show();
|
$("#clipboard-dialog-download").show();
|
||||||
|
|
||||||
}
|
}
|
||||||
function transformFlowList(list,label,root,includeExamples) {
|
|
||||||
var result = {
|
|
||||||
icon: root===""?"fa fa-archive":'fa fa-folder',
|
|
||||||
label: label,
|
|
||||||
path: root
|
|
||||||
};
|
|
||||||
result.children = [];
|
|
||||||
|
|
||||||
if (list.f) {
|
|
||||||
list.f.forEach(function(f) {
|
|
||||||
result.children.push({
|
|
||||||
icon: 'fa fa-file-o',
|
|
||||||
label: f,
|
|
||||||
path: root+f
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (list.d) {
|
|
||||||
for (var l in list.d) {
|
|
||||||
if (list.d.hasOwnProperty(l)) {
|
|
||||||
if (root+l !== "_examples_") {
|
|
||||||
result.children.push(transformFlowList(list.d[l], l,root+l+"/",includeExamples))
|
|
||||||
} else if (includeExamples) {
|
|
||||||
result._examples = transformFlowList(list.d[l], l,root+l+"/",includeExamples)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.children.sort(function(A,B){
|
|
||||||
if (A.children && !B.children) {
|
|
||||||
return -1;
|
|
||||||
} else if (!A.children && B.children) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return A.label.localeCompare(B.label);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadFlowLibrary(browser,includeExamples) {
|
function loadFlowLibrary(browser,includeExamples) {
|
||||||
$.getJSON("library/flows", function(data) {
|
var listing = [];
|
||||||
var listing = [transformFlowList(data,RED._("library.types.local"),"",includeExamples)];
|
if (includeExamples) {
|
||||||
listing[0].expanded = true;
|
listing.push({
|
||||||
if (includeExamples && listing[0]._examples) {
|
library: "_examples_",
|
||||||
var examples = listing[0]._examples;
|
type: "flows",
|
||||||
delete listing[0]._examples;
|
icon: 'fa fa-hdd-o',
|
||||||
examples.label = RED._("library.types.examples");
|
label: RED._("library.types.examples"),
|
||||||
examples.icon = "fa fa-archive";
|
path: "",
|
||||||
listing.unshift(examples)
|
children: function(item,done) {
|
||||||
|
RED.library.loadLibraryFolder("_examples_","flows","",function(children) {
|
||||||
|
item.children = children;
|
||||||
|
done(children);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
listing.push({
|
||||||
|
library: "local",
|
||||||
|
type: "flows",
|
||||||
|
icon: 'fa fa-hdd-o',
|
||||||
|
label: RED._("library.types.local"),
|
||||||
|
path: "",
|
||||||
|
expanded: true,
|
||||||
|
children: function(item,done) {
|
||||||
|
RED.library.loadLibraryFolder("local","flows","",function(children) {
|
||||||
|
item.children = children;
|
||||||
|
done(children);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
browser.data(listing);
|
})
|
||||||
});
|
browser.data(listing);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideDropTarget() {
|
function hideDropTarget() {
|
||||||
|
@ -58,7 +58,6 @@ RED.library = (function() {
|
|||||||
}
|
}
|
||||||
var filename = $("#node-dialog-library-save-filename").val().trim()
|
var filename = $("#node-dialog-library-save-filename").val().trim()
|
||||||
var selectedPath = saveLibraryBrowser.getSelected();
|
var selectedPath = saveLibraryBrowser.getSelected();
|
||||||
console.log(selectedPath);
|
|
||||||
if (!selectedPath.children) {
|
if (!selectedPath.children) {
|
||||||
selectedPath = selectedPath.parent;
|
selectedPath = selectedPath.parent;
|
||||||
}
|
}
|
||||||
@ -75,7 +74,7 @@ RED.library = (function() {
|
|||||||
data.text = activeLibrary.editor.getValue();
|
data.text = activeLibrary.editor.getValue();
|
||||||
var saveFlow = function() {
|
var saveFlow = function() {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url:"library/"+activeLibrary.url+'/'+selectedPath.path + filename,
|
url:"library/"+selectedPath.library+'/'+selectedPath.type+'/'+selectedPath.path + filename,
|
||||||
type: "POST",
|
type: "POST",
|
||||||
data: JSON.stringify(data),
|
data: JSON.stringify(data),
|
||||||
contentType: "application/json; charset=utf-8"
|
contentType: "application/json; charset=utf-8"
|
||||||
@ -89,8 +88,6 @@ RED.library = (function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
console.log(filename);
|
|
||||||
console.log(selectedPath);
|
|
||||||
if (selectedPath.children) {
|
if (selectedPath.children) {
|
||||||
var exists = false;
|
var exists = false;
|
||||||
selectedPath.children.forEach(function(f) {
|
selectedPath.children.forEach(function(f) {
|
||||||
@ -125,16 +122,18 @@ RED.library = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadLibraryFolder(url,root,done) {
|
function loadLibraryFolder(library,type,root,done) {
|
||||||
$.getJSON("library/"+url+"/"+root,function(data) {
|
$.getJSON("library/"+library+"/"+type+"/"+root,function(data) {
|
||||||
var items = data.map(function(d) {
|
var items = data.map(function(d) {
|
||||||
if (typeof d === "string") {
|
if (typeof d === "string") {
|
||||||
return {
|
return {
|
||||||
|
library: library,
|
||||||
|
type: type,
|
||||||
icon: 'fa fa-folder',
|
icon: 'fa fa-folder',
|
||||||
label: d,
|
label: d,
|
||||||
path: root+d+"/",
|
path: root+d+"/",
|
||||||
children: function(item,done) {
|
children: function(item,done) {
|
||||||
loadLibraryFolder(url,root+d+"/", function(children) {
|
loadLibraryFolder(library,type,root+d+"/", function(children) {
|
||||||
item.children = children; // TODO: should this be done by treeList for us
|
item.children = children; // TODO: should this be done by treeList for us
|
||||||
done(children);
|
done(children);
|
||||||
})
|
})
|
||||||
@ -142,6 +141,8 @@ RED.library = (function() {
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
|
library: library,
|
||||||
|
type: type,
|
||||||
icon: 'fa fa-file-o',
|
icon: 'fa fa-file-o',
|
||||||
label: d.fn,
|
label: d.fn,
|
||||||
path: root+d.fn,
|
path: root+d.fn,
|
||||||
@ -209,9 +210,11 @@ RED.library = (function() {
|
|||||||
|
|
||||||
$('#node-input-'+options.type+'-menu-open-library').click(function(e) {
|
$('#node-input-'+options.type+'-menu-open-library').click(function(e) {
|
||||||
activeLibrary = options;
|
activeLibrary = options;
|
||||||
loadLibraryFolder(options.url, "", function(items) {
|
loadLibraryFolder("local",options.url, "", function(items) {
|
||||||
var listing = [{
|
var listing = [{
|
||||||
icon: 'fa fa-archive',
|
library: "local",
|
||||||
|
type: options.url,
|
||||||
|
icon: 'fa fa-hdd-o',
|
||||||
label: RED._("library.types.local"),
|
label: RED._("library.types.local"),
|
||||||
path: "",
|
path: "",
|
||||||
expanded: true,
|
expanded: true,
|
||||||
@ -255,7 +258,7 @@ RED.library = (function() {
|
|||||||
}
|
}
|
||||||
$("#node-dialog-library-save-filename").attr("value",filename+".js");
|
$("#node-dialog-library-save-filename").attr("value",filename+".js");
|
||||||
|
|
||||||
loadLibraryFolder(options.url, "", function(items) {
|
loadLibraryFolder("local",options.url, "", function(items) {
|
||||||
var listing = [{
|
var listing = [{
|
||||||
icon: 'fa fa-archive',
|
icon: 'fa fa-archive',
|
||||||
label: RED._("library.types.local"),
|
label: RED._("library.types.local"),
|
||||||
@ -483,7 +486,7 @@ RED.library = (function() {
|
|||||||
var table = $("#node-dialog-library-load-preview-details-table").empty();
|
var table = $("#node-dialog-library-load-preview-details-table").empty();
|
||||||
selectedLibraryItem = file.props;
|
selectedLibraryItem = file.props;
|
||||||
if (file && file.label && !file.children) {
|
if (file && file.label && !file.children) {
|
||||||
$.get("library/"+activeLibrary.url+"/"+file.path, function(data) {
|
$.get("library/"+file.library+"/"+file.type+"/"+file.path, function(data) {
|
||||||
//TODO: nls + sanitize
|
//TODO: nls + sanitize
|
||||||
var propRow = $('<tr class="node-info-node-row"><td>Type</td><td></td></tr>').appendTo(table);
|
var propRow = $('<tr class="node-info-node-row"><td>Type</td><td></td></tr>').appendTo(table);
|
||||||
$(propRow.children()[1]).text(activeLibrary.type);
|
$(propRow.children()[1]).text(activeLibrary.type);
|
||||||
@ -516,6 +519,7 @@ RED.library = (function() {
|
|||||||
},
|
},
|
||||||
create: createUI,
|
create: createUI,
|
||||||
createBrowser:createBrowser,
|
createBrowser:createBrowser,
|
||||||
export: exportFlow
|
export: exportFlow,
|
||||||
|
loadLibraryFolder: loadLibraryFolder
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var fspath = require('path');
|
var fspath = require('path');
|
||||||
var when = require('when');
|
|
||||||
|
|
||||||
var runtime;
|
var runtime;
|
||||||
|
|
||||||
@ -24,7 +23,7 @@ var exampleRoots = {};
|
|||||||
var exampleFlows = null;
|
var exampleFlows = null;
|
||||||
|
|
||||||
function getFlowsFromPath(path) {
|
function getFlowsFromPath(path) {
|
||||||
return when.promise(function(resolve,reject) {
|
return new Promise(function(resolve,reject) {
|
||||||
var result = {};
|
var result = {};
|
||||||
fs.readdir(path,function(err,files) {
|
fs.readdir(path,function(err,files) {
|
||||||
var promises = [];
|
var promises = [];
|
||||||
@ -37,11 +36,11 @@ function getFlowsFromPath(path) {
|
|||||||
promises.push(getFlowsFromPath(fullPath));
|
promises.push(getFlowsFromPath(fullPath));
|
||||||
} else if (/\.json$/.test(file)){
|
} else if (/\.json$/.test(file)){
|
||||||
validFiles.push(file);
|
validFiles.push(file);
|
||||||
promises.push(when.resolve(file.split(".")[0]))
|
promises.push(Promise.resolve(file.split(".")[0]))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
var i=0;
|
var i=0;
|
||||||
when.all(promises).then(function(results) {
|
Promise.all(promises).then(function(results) {
|
||||||
results.forEach(function(r) {
|
results.forEach(function(r) {
|
||||||
if (typeof r === 'string') {
|
if (typeof r === 'string') {
|
||||||
result.f = result.f||[];
|
result.f = result.f||[];
|
||||||
@ -62,21 +61,20 @@ function getFlowsFromPath(path) {
|
|||||||
function addNodeExamplesDir(module,path) {
|
function addNodeExamplesDir(module,path) {
|
||||||
exampleRoots[module] = path;
|
exampleRoots[module] = path;
|
||||||
return getFlowsFromPath(path).then(function(result) {
|
return getFlowsFromPath(path).then(function(result) {
|
||||||
exampleFlows = exampleFlows||{d:{}};
|
exampleFlows = exampleFlows||{};
|
||||||
exampleFlows.d[module] = result;
|
exampleFlows[module] = result;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function removeNodeExamplesDir(module) {
|
function removeNodeExamplesDir(module) {
|
||||||
delete exampleRoots[module];
|
delete exampleRoots[module];
|
||||||
if (exampleFlows && exampleFlows.d) {
|
if (exampleFlows) {
|
||||||
delete exampleFlows.d[module];
|
delete exampleFlows[module];
|
||||||
}
|
}
|
||||||
if (exampleFlows && Object.keys(exampleFlows.d).length === 0) {
|
if (exampleFlows && Object.keys(exampleFlows).length === 0) {
|
||||||
exampleFlows = null;
|
exampleFlows = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
exampleRoots = {};
|
exampleRoots = {};
|
||||||
exampleFlows = null;
|
exampleFlows = null;
|
||||||
|
@ -29,6 +29,7 @@ var api = module.exports = {
|
|||||||
* Gets an entry from the library.
|
* Gets an entry from the library.
|
||||||
* @param {Object} opts
|
* @param {Object} opts
|
||||||
* @param {User} opts.user - the user calling the api
|
* @param {User} opts.user - the user calling the api
|
||||||
|
* @param {String} opts.library - the library
|
||||||
* @param {String} opts.type - the type of entry
|
* @param {String} opts.type - the type of entry
|
||||||
* @param {String} opts.path - the path of the entry
|
* @param {String} opts.path - the path of the entry
|
||||||
* @return {Promise<String|Object>} - resolves when complete
|
* @return {Promise<String|Object>} - resolves when complete
|
||||||
@ -36,12 +37,12 @@ var api = module.exports = {
|
|||||||
*/
|
*/
|
||||||
getEntry: function(opts) {
|
getEntry: function(opts) {
|
||||||
return new Promise(function(resolve,reject) {
|
return new Promise(function(resolve,reject) {
|
||||||
runtime.library.getEntry(opts.type,opts.path).then(function(result) {
|
runtime.library.getEntry(opts.library,opts.type,opts.path).then(function(result) {
|
||||||
runtime.log.audit({event: "library.get",type:opts.type,path:opts.path});
|
runtime.log.audit({event: "library.get",library:opts.library,type:opts.type,path:opts.path});
|
||||||
return resolve(result);
|
return resolve(result);
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
runtime.log.warn(runtime.log._("api.library.error-load-entry",{path:opts.path,message:err.toString()}));
|
runtime.log.warn(runtime.log._("api.library.error-load-entry",{library:opts.library,type:opts.type,path:opts.path,message:err.toString()}));
|
||||||
if (err.code === 'forbidden') {
|
if (err.code === 'forbidden') {
|
||||||
err.status = 403;
|
err.status = 403;
|
||||||
return reject(err);
|
return reject(err);
|
||||||
@ -50,10 +51,10 @@ var api = module.exports = {
|
|||||||
} else {
|
} else {
|
||||||
err.status = 400;
|
err.status = 400;
|
||||||
}
|
}
|
||||||
runtime.log.audit({event: "library.get",type:opts.type,path:opts.path,error:err.code});
|
runtime.log.audit({event: "library.get",library:opts.library,type:opts.type,path:opts.path,error:err.code});
|
||||||
return reject(err);
|
return reject(err);
|
||||||
}
|
}
|
||||||
runtime.log.audit({event: "library.get",type:opts.type,error:"not_found"});
|
runtime.log.audit({event: "library.get",library:opts.library,type:opts.type,error:"not_found"});
|
||||||
var error = new Error();
|
var error = new Error();
|
||||||
error.code = "not_found";
|
error.code = "not_found";
|
||||||
error.status = 404;
|
error.status = 404;
|
||||||
@ -66,6 +67,7 @@ var api = module.exports = {
|
|||||||
* Saves an entry to the library
|
* Saves an entry to the library
|
||||||
* @param {Object} opts
|
* @param {Object} opts
|
||||||
* @param {User} opts.user - the user calling the api
|
* @param {User} opts.user - the user calling the api
|
||||||
|
* @param {String} opts.library - the library
|
||||||
* @param {String} opts.type - the type of entry
|
* @param {String} opts.type - the type of entry
|
||||||
* @param {String} opts.path - the path of the entry
|
* @param {String} opts.path - the path of the entry
|
||||||
* @param {Object} opts.meta - any meta data associated with the entry
|
* @param {Object} opts.meta - any meta data associated with the entry
|
||||||
@ -75,7 +77,7 @@ var api = module.exports = {
|
|||||||
*/
|
*/
|
||||||
saveEntry: function(opts) {
|
saveEntry: function(opts) {
|
||||||
return new Promise(function(resolve,reject) {
|
return new Promise(function(resolve,reject) {
|
||||||
runtime.library.saveEntry(opts.type,opts.path,opts.meta,opts.body).then(function() {
|
runtime.library.saveEntry(opts.library,opts.type,opts.path,opts.meta,opts.body).then(function() {
|
||||||
runtime.log.audit({event: "library.set",type:opts.type,path:opts.path});
|
runtime.log.audit({event: "library.set",type:opts.type,path:opts.path});
|
||||||
return resolve();
|
return resolve();
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
@ -91,30 +93,5 @@ var api = module.exports = {
|
|||||||
return reject(error);
|
return reject(error);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Returns a complete listing of all entries of a given type in the library.
|
|
||||||
* @param {Object} opts
|
|
||||||
* @param {User} opts.user - the user calling the api
|
|
||||||
* @param {String} opts.type - the type of entry
|
|
||||||
* @return {Promise<Object>} - the entry listing
|
|
||||||
* @memberof @node-red/runtime_library
|
|
||||||
*/
|
|
||||||
getEntries: function(opts) {
|
|
||||||
return new Promise(function(resolve,reject) {
|
|
||||||
if (opts.type !== 'flows') {
|
|
||||||
return reject(new Error("API only supports flows"));
|
|
||||||
|
|
||||||
}
|
|
||||||
runtime.storage.getAllFlows().then(function(flows) {
|
|
||||||
runtime.log.audit({event: "library.get.all",type:"flow"});
|
|
||||||
var examples = runtime.nodes.getNodeExampleFlows();
|
|
||||||
if (examples) {
|
|
||||||
flows.d = flows.d||{};
|
|
||||||
flows.d._examples_ = examples;
|
|
||||||
}
|
|
||||||
return resolve(flows);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
101
packages/node_modules/@node-red/runtime/lib/library/examples.js
vendored
Normal file
101
packages/node_modules/@node-red/runtime/lib/library/examples.js
vendored
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* 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');
|
||||||
|
|
||||||
|
var runtime;
|
||||||
|
|
||||||
|
function init(_runtime) {
|
||||||
|
runtime = _runtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEntry(type,path) {
|
||||||
|
var examples = runtime.nodes.getNodeExampleFlows();
|
||||||
|
var result = [];
|
||||||
|
if (path === "") {
|
||||||
|
return Promise.resolve(Object.keys(examples));
|
||||||
|
} else {
|
||||||
|
path = path.replace(/\/$/,"");
|
||||||
|
var parts = path.split("/");
|
||||||
|
var module = parts.shift();
|
||||||
|
if (module[0] === "@") {
|
||||||
|
module = module+"/"+parts.shift();
|
||||||
|
}
|
||||||
|
if (examples.hasOwnProperty(module)) {
|
||||||
|
examples = examples[module];
|
||||||
|
examples = parts.reduce(function(ex,k) {
|
||||||
|
if (ex) {
|
||||||
|
if (ex.d && ex.d[k]) {
|
||||||
|
return ex.d[k]
|
||||||
|
}
|
||||||
|
if (ex.f && ex.f.indexOf(k) > -1) {
|
||||||
|
return runtime.nodes.getNodeExampleFlowPath(module,parts.join("/"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},examples);
|
||||||
|
|
||||||
|
if (!examples) {
|
||||||
|
return new Promise(function (resolve,reject) {
|
||||||
|
var error = new Error("not_found");
|
||||||
|
error.code = "not_found";
|
||||||
|
return reject(error);
|
||||||
|
});
|
||||||
|
} else if (typeof examples === 'string') {
|
||||||
|
return new Promise(function(resolve,reject) {
|
||||||
|
try {
|
||||||
|
fs.readFile(examples,'utf8',function(err, data) {
|
||||||
|
runtime.log.audit({event: "library.get",library:"_examples",type:"flow",path:path});
|
||||||
|
if (err) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
return resolve(data);
|
||||||
|
})
|
||||||
|
} catch(err) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (examples.d) {
|
||||||
|
for (var d in examples.d) {
|
||||||
|
if (examples.d.hasOwnProperty(d)) {
|
||||||
|
result.push(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (examples.f) {
|
||||||
|
examples.f.forEach(function(f) {
|
||||||
|
result.push({fn:f})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return Promise.resolve(result);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return new Promise(function (resolve,reject) {
|
||||||
|
var error = new Error("not_found");
|
||||||
|
error.code = "not_found";
|
||||||
|
return reject(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: '_examples_',
|
||||||
|
init: init,
|
||||||
|
getEntry: getEntry
|
||||||
|
}
|
@ -14,18 +14,22 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
var fs = require('fs');
|
|
||||||
var fspath = require('path');
|
|
||||||
|
|
||||||
var runtime;
|
|
||||||
var knownTypes = {};
|
var knownTypes = {};
|
||||||
|
|
||||||
var storage;
|
var libraries = {};
|
||||||
|
|
||||||
|
|
||||||
|
function init(runtime) {
|
||||||
|
knownTypes = {
|
||||||
|
'flows': 'node-red'
|
||||||
|
};
|
||||||
|
|
||||||
|
libraries["_examples_"] = require("./examples");
|
||||||
|
libraries["_examples_"].init(runtime);
|
||||||
|
libraries["local"] = require("./local");
|
||||||
|
libraries["local"].init(runtime);
|
||||||
|
|
||||||
function init(_runtime) {
|
|
||||||
runtime = _runtime;
|
|
||||||
storage = runtime.storage;
|
|
||||||
knownTypes = {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerType(id,type) {
|
function registerType(id,type) {
|
||||||
@ -37,66 +41,34 @@ function registerType(id,type) {
|
|||||||
knownTypes[type] = id;
|
knownTypes[type] = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// function getAllEntries(type) {
|
function getEntry(library,type,path) {
|
||||||
// if (!knownTypes.hasOwnProperty(type)) {
|
if (!knownTypes.hasOwnProperty(type)) {
|
||||||
// throw new Error(`Unknown library type '${type}'`);
|
throw new Error(`Unknown library type '${type}'`);
|
||||||
// }
|
}
|
||||||
// }
|
if (libraries.hasOwnProperty(library)) {
|
||||||
|
return libraries[library].getEntry(type,path);
|
||||||
function getEntry(type,path) {
|
|
||||||
if (type !== 'flows') {
|
|
||||||
if (!knownTypes.hasOwnProperty(type)) {
|
|
||||||
throw new Error(`Unknown library type '${type}'`);
|
|
||||||
}
|
|
||||||
return storage.getLibraryEntry(type,path);
|
|
||||||
} else {
|
} else {
|
||||||
return new Promise(function(resolve,reject) {
|
throw new Error(`Unknown library '${library}'`);
|
||||||
if (path.indexOf("_examples_/") === 0) {
|
|
||||||
var m = /^_examples_\/(@.*?\/[^\/]+|[^\/]+)\/(.*)$/.exec(path);
|
|
||||||
if (m) {
|
|
||||||
var module = m[1];
|
|
||||||
var entryPath = m[2];
|
|
||||||
var fullPath = runtime.nodes.getNodeExampleFlowPath(module,entryPath);
|
|
||||||
if (fullPath) {
|
|
||||||
try {
|
|
||||||
fs.readFile(fullPath,'utf8',function(err, data) {
|
|
||||||
runtime.log.audit({event: "library.get",type:"flow",path:path});
|
|
||||||
if (err) {
|
|
||||||
return reject(err);
|
|
||||||
}
|
|
||||||
return resolve(data);
|
|
||||||
})
|
|
||||||
} catch(err) {
|
|
||||||
return reject(err);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// IF we get here, we didn't find the file
|
|
||||||
var error = new Error("not_found");
|
|
||||||
error.code = "not_found";
|
|
||||||
return reject(error);
|
|
||||||
} else {
|
|
||||||
resolve(storage.getFlow(path));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function saveEntry(type,path,meta,body) {
|
function saveEntry(library,type,path,meta,body) {
|
||||||
if (type !== 'flows') {
|
if (!knownTypes.hasOwnProperty(type)) {
|
||||||
if (!knownTypes.hasOwnProperty(type)) {
|
throw new Error(`Unknown library type '${type}'`);
|
||||||
throw new Error(`Unknown library type '${type}'`);
|
}
|
||||||
|
if (libraries.hasOwnProperty(library)) {
|
||||||
|
if (libraries[library].hasOwnProperty("saveEntry")) {
|
||||||
|
return libraries[library].saveEntry(type,path,meta,body);
|
||||||
|
} else {
|
||||||
|
throw new Error(`Library '${library}' is read-only`);
|
||||||
}
|
}
|
||||||
return storage.saveLibraryEntry(type,path,meta,body);
|
|
||||||
} else {
|
} else {
|
||||||
return storage.saveFlow(path,body);
|
throw new Error(`Unknown library '${library}'`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
init: init,
|
init: init,
|
||||||
register: registerType,
|
register: registerType,
|
||||||
// getAllEntries: getAllEntries,
|
|
||||||
getEntry: getEntry,
|
getEntry: getEntry,
|
||||||
saveEntry: saveEntry
|
saveEntry: saveEntry
|
||||||
|
|
||||||
|
37
packages/node_modules/@node-red/runtime/lib/library/local.js
vendored
Normal file
37
packages/node_modules/@node-red/runtime/lib/library/local.js
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* 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 runtime;
|
||||||
|
var storage;
|
||||||
|
|
||||||
|
function init(_runtime) {
|
||||||
|
runtime = _runtime;
|
||||||
|
storage = runtime.storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEntry(type,path) {
|
||||||
|
return storage.getLibraryEntry(type,path);
|
||||||
|
}
|
||||||
|
function saveEntry(type,path,meta,body) {
|
||||||
|
return storage.saveLibraryEntry(type,path,meta,body);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: 'local',
|
||||||
|
init: init,
|
||||||
|
getEntry: getEntry,
|
||||||
|
saveEntry: saveEntry
|
||||||
|
}
|
@ -31,60 +31,11 @@ describe("api/editor/library", function() {
|
|||||||
before(function() {
|
before(function() {
|
||||||
app = express();
|
app = express();
|
||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
|
app.get(/library\/([^\/]+)\/([^\/]+)(?:$|\/(.*))/,library.getEntry);
|
||||||
app.get("/library/flows",library.getAll);
|
app.post(/library\/([^\/]+)\/([^\/]+)\/(.*)/,library.saveEntry);
|
||||||
app.post(/library\/([^\/]+)\/(.*)/,library.saveEntry);
|
|
||||||
app.get(/library\/([^\/]+)(?:$|\/(.*))/,library.getEntry);
|
|
||||||
});
|
});
|
||||||
after(function() {
|
after(function() {
|
||||||
});
|
});
|
||||||
it('returns all flows', function(done) {
|
|
||||||
library.init({
|
|
||||||
library: {
|
|
||||||
getEntries: function(opts) {
|
|
||||||
return Promise.resolve({a:1,b:2});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
request(app)
|
|
||||||
.get('/library/flows')
|
|
||||||
.expect(200)
|
|
||||||
.end(function(err,res) {
|
|
||||||
if (err) {
|
|
||||||
return done(err);
|
|
||||||
}
|
|
||||||
res.body.should.have.property('a',1);
|
|
||||||
res.body.should.have.property('b',2);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
it('returns an error on all flows', function(done) {
|
|
||||||
library.init({
|
|
||||||
library: {
|
|
||||||
getEntries: function(opts) {
|
|
||||||
var err = new Error("message");
|
|
||||||
err.code = "random_error";
|
|
||||||
err.status = 400;
|
|
||||||
var p = Promise.reject(err);
|
|
||||||
p.catch(()=>{});
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
request(app)
|
|
||||||
.get('/library/flows')
|
|
||||||
.expect(400)
|
|
||||||
.end(function(err,res) {
|
|
||||||
if (err) {
|
|
||||||
return done(err);
|
|
||||||
}
|
|
||||||
res.body.should.have.property('code');
|
|
||||||
res.body.code.should.be.equal("random_error");
|
|
||||||
res.body.should.have.property('message');
|
|
||||||
res.body.message.should.be.equal("message");
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns an individual entry - flow type', function(done) {
|
it('returns an individual entry - flow type', function(done) {
|
||||||
var opts;
|
var opts;
|
||||||
@ -97,7 +48,7 @@ describe("api/editor/library", function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
request(app)
|
request(app)
|
||||||
.get('/library/flows/abc')
|
.get('/library/local/flows/abc')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err,res) {
|
.end(function(err,res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@ -105,6 +56,7 @@ describe("api/editor/library", function() {
|
|||||||
}
|
}
|
||||||
res.body.should.have.property('a',1);
|
res.body.should.have.property('a',1);
|
||||||
res.body.should.have.property('b',2);
|
res.body.should.have.property('b',2);
|
||||||
|
opts.should.have.property('library','local');
|
||||||
opts.should.have.property('type','flows');
|
opts.should.have.property('type','flows');
|
||||||
opts.should.have.property('path','abc');
|
opts.should.have.property('path','abc');
|
||||||
done();
|
done();
|
||||||
@ -121,7 +73,7 @@ describe("api/editor/library", function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
request(app)
|
request(app)
|
||||||
.get('/library/flows/abc/def')
|
.get('/library/local/flows/abc/def')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err,res) {
|
.end(function(err,res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@ -129,6 +81,7 @@ describe("api/editor/library", function() {
|
|||||||
}
|
}
|
||||||
res.body.should.have.property('a',1);
|
res.body.should.have.property('a',1);
|
||||||
res.body.should.have.property('b',2);
|
res.body.should.have.property('b',2);
|
||||||
|
opts.should.have.property('library','local');
|
||||||
opts.should.have.property('type','flows');
|
opts.should.have.property('type','flows');
|
||||||
opts.should.have.property('path','abc/def');
|
opts.should.have.property('path','abc/def');
|
||||||
done();
|
done();
|
||||||
@ -145,12 +98,13 @@ describe("api/editor/library", function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
request(app)
|
request(app)
|
||||||
.get('/library/non-flow/abc')
|
.get('/library/local/non-flow/abc')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err,res) {
|
.end(function(err,res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
|
opts.should.have.property('library','local');
|
||||||
opts.should.have.property('type','non-flow');
|
opts.should.have.property('type','non-flow');
|
||||||
opts.should.have.property('path','abc');
|
opts.should.have.property('path','abc');
|
||||||
res.text.should.eql('{"a":1,"b":2}');
|
res.text.should.eql('{"a":1,"b":2}');
|
||||||
@ -168,7 +122,7 @@ describe("api/editor/library", function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
request(app)
|
request(app)
|
||||||
.get('/library/non-flow/abc/def')
|
.get('/library/local/non-flow/abc/def')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err,res) {
|
.end(function(err,res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@ -176,6 +130,7 @@ describe("api/editor/library", function() {
|
|||||||
}
|
}
|
||||||
res.body.should.have.property('a',1);
|
res.body.should.have.property('a',1);
|
||||||
res.body.should.have.property('b',2);
|
res.body.should.have.property('b',2);
|
||||||
|
opts.should.have.property('library','local');
|
||||||
opts.should.have.property('type','non-flow');
|
opts.should.have.property('type','non-flow');
|
||||||
opts.should.have.property('path','abc/def');
|
opts.should.have.property('path','abc/def');
|
||||||
done();
|
done();
|
||||||
@ -198,12 +153,13 @@ describe("api/editor/library", function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
request(app)
|
request(app)
|
||||||
.get('/library/flows/123')
|
.get('/library/local/flows/123')
|
||||||
.expect(400)
|
.expect(400)
|
||||||
.end(function(err,res) {
|
.end(function(err,res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
|
opts.should.have.property('library','local');
|
||||||
opts.should.have.property('type','flows');
|
opts.should.have.property('type','flows');
|
||||||
opts.should.have.property('path','123');
|
opts.should.have.property('path','123');
|
||||||
|
|
||||||
@ -227,13 +183,14 @@ describe("api/editor/library", function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
request(app)
|
request(app)
|
||||||
.post('/library/flows/abc/def')
|
.post('/library/local/flows/abc/def')
|
||||||
.expect(204)
|
.expect(204)
|
||||||
.send({a:1,b:2,c:3})
|
.send({a:1,b:2,c:3})
|
||||||
.end(function(err,res) {
|
.end(function(err,res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
|
opts.should.have.property('library','local');
|
||||||
opts.should.have.property('type','flows');
|
opts.should.have.property('type','flows');
|
||||||
opts.should.have.property('path','abc/def');
|
opts.should.have.property('path','abc/def');
|
||||||
opts.should.have.property('meta',{});
|
opts.should.have.property('meta',{});
|
||||||
@ -253,13 +210,14 @@ describe("api/editor/library", function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
request(app)
|
request(app)
|
||||||
.post('/library/non-flow/abc/def')
|
.post('/library/local/non-flow/abc/def')
|
||||||
.expect(204)
|
.expect(204)
|
||||||
.send({a:1,b:2,text:"123"})
|
.send({a:1,b:2,text:"123"})
|
||||||
.end(function(err,res) {
|
.end(function(err,res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
|
opts.should.have.property('library','local');
|
||||||
opts.should.have.property('type','non-flow');
|
opts.should.have.property('type','non-flow');
|
||||||
opts.should.have.property('path','abc/def');
|
opts.should.have.property('path','abc/def');
|
||||||
opts.should.have.property('meta',{a:1,b:2});
|
opts.should.have.property('meta',{a:1,b:2});
|
||||||
@ -284,7 +242,7 @@ describe("api/editor/library", function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
request(app)
|
request(app)
|
||||||
.post('/library/non-flow/abc/def')
|
.post('/library/local/non-flow/abc/def')
|
||||||
.send({a:1,b:2,text:"123"})
|
.send({a:1,b:2,text:"123"})
|
||||||
.expect(400)
|
.expect(400)
|
||||||
.end(function(err,res) {
|
.end(function(err,res) {
|
||||||
@ -292,6 +250,7 @@ describe("api/editor/library", function() {
|
|||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
opts.should.have.property('type','non-flow');
|
opts.should.have.property('type','non-flow');
|
||||||
|
opts.should.have.property('library','local');
|
||||||
opts.should.have.property('path','abc/def');
|
opts.should.have.property('path','abc/def');
|
||||||
|
|
||||||
res.body.should.have.property('code');
|
res.body.should.have.property('code');
|
||||||
|
@ -38,7 +38,7 @@ describe("library api", function() {
|
|||||||
library.addExamplesDir("test-module",path.resolve(__dirname+'/resources/examples')).then(function() {
|
library.addExamplesDir("test-module",path.resolve(__dirname+'/resources/examples')).then(function() {
|
||||||
try {
|
try {
|
||||||
var flows = library.getExampleFlows();
|
var flows = library.getExampleFlows();
|
||||||
flows.should.deepEqual({"d":{"test-module":{"f":["one"]}}});
|
flows.should.deepEqual({"test-module":{"f":["one"]}});
|
||||||
|
|
||||||
var examplePath = library.getExampleFlowPath('test-module','one');
|
var examplePath = library.getExampleFlowPath('test-module','one');
|
||||||
examplePath.should.eql(path.resolve(__dirname+'/resources/examples/one.json'))
|
examplePath.should.eql(path.resolve(__dirname+'/resources/examples/one.json'))
|
||||||
|
@ -38,7 +38,7 @@ describe("runtime-api/library", function() {
|
|||||||
library.init({
|
library.init({
|
||||||
log: mockLog,
|
log: mockLog,
|
||||||
library: {
|
library: {
|
||||||
getEntry: function(type,path) {
|
getEntry: function(library, type,path) {
|
||||||
if (type === "known") {
|
if (type === "known") {
|
||||||
return Promise.resolve("known");
|
return Promise.resolve("known");
|
||||||
} else if (type === "forbidden") {
|
} else if (type === "forbidden") {
|
||||||
@ -67,13 +67,13 @@ describe("runtime-api/library", function() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
it("returns a known entry", function(done) {
|
it("returns a known entry", function(done) {
|
||||||
library.getEntry({type: "known", path: "/abc"}).then(function(result) {
|
library.getEntry({library: "local",type: "known", path: "/abc"}).then(function(result) {
|
||||||
result.should.eql("known")
|
result.should.eql("known")
|
||||||
done();
|
done();
|
||||||
}).catch(done)
|
}).catch(done)
|
||||||
})
|
})
|
||||||
it("rejects a forbidden entry", function(done) {
|
it("rejects a forbidden entry", function(done) {
|
||||||
library.getEntry({type: "forbidden", path: "/abc"}).then(function(result) {
|
library.getEntry({library: "local",type: "forbidden", path: "/abc"}).then(function(result) {
|
||||||
done(new Error("did not reject"));
|
done(new Error("did not reject"));
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
err.should.have.property("code","forbidden");
|
err.should.have.property("code","forbidden");
|
||||||
@ -82,7 +82,7 @@ describe("runtime-api/library", function() {
|
|||||||
}).catch(done)
|
}).catch(done)
|
||||||
})
|
})
|
||||||
it("rejects an unknown entry", function(done) {
|
it("rejects an unknown entry", function(done) {
|
||||||
library.getEntry({type: "not_found", path: "/abc"}).then(function(result) {
|
library.getEntry({library: "local",type: "not_found", path: "/abc"}).then(function(result) {
|
||||||
done(new Error("did not reject"));
|
done(new Error("did not reject"));
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
err.should.have.property("code","not_found");
|
err.should.have.property("code","not_found");
|
||||||
@ -91,7 +91,7 @@ describe("runtime-api/library", function() {
|
|||||||
}).catch(done)
|
}).catch(done)
|
||||||
})
|
})
|
||||||
it("rejects a blank (unknown) entry", function(done) {
|
it("rejects a blank (unknown) entry", function(done) {
|
||||||
library.getEntry({type: "blank", path: "/abc"}).then(function(result) {
|
library.getEntry({library: "local",type: "blank", path: "/abc"}).then(function(result) {
|
||||||
done(new Error("did not reject"));
|
done(new Error("did not reject"));
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
err.should.have.property("code","not_found");
|
err.should.have.property("code","not_found");
|
||||||
@ -100,7 +100,7 @@ describe("runtime-api/library", function() {
|
|||||||
}).catch(done)
|
}).catch(done)
|
||||||
})
|
})
|
||||||
it("rejects unexpected error", function(done) {
|
it("rejects unexpected error", function(done) {
|
||||||
library.getEntry({type: "error", path: "/abc"}).then(function(result) {
|
library.getEntry({library: "local",type: "error", path: "/abc"}).then(function(result) {
|
||||||
done(new Error("did not reject"));
|
done(new Error("did not reject"));
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
err.should.have.property("status",400);
|
err.should.have.property("status",400);
|
||||||
@ -114,7 +114,7 @@ describe("runtime-api/library", function() {
|
|||||||
library.init({
|
library.init({
|
||||||
log: mockLog,
|
log: mockLog,
|
||||||
library: {
|
library: {
|
||||||
saveEntry: function(type,path,meta,body) {
|
saveEntry: function(library,type,path,meta,body) {
|
||||||
opts = {type,path,meta,body};
|
opts = {type,path,meta,body};
|
||||||
if (type === "known") {
|
if (type === "known") {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
@ -137,7 +137,7 @@ describe("runtime-api/library", function() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("saves an entry", function(done) {
|
it("saves an entry", function(done) {
|
||||||
library.saveEntry({type: "known", path: "/abc", meta: {a:1}, body:"123"}).then(function() {
|
library.saveEntry({library: "local",type: "known", path: "/abc", meta: {a:1}, body:"123"}).then(function() {
|
||||||
opts.should.have.property("type","known");
|
opts.should.have.property("type","known");
|
||||||
opts.should.have.property("path","/abc");
|
opts.should.have.property("path","/abc");
|
||||||
opts.should.have.property("meta",{a:1});
|
opts.should.have.property("meta",{a:1});
|
||||||
@ -146,7 +146,7 @@ describe("runtime-api/library", function() {
|
|||||||
}).catch(done)
|
}).catch(done)
|
||||||
})
|
})
|
||||||
it("rejects a forbidden entry", function(done) {
|
it("rejects a forbidden entry", function(done) {
|
||||||
library.saveEntry({type: "forbidden", path: "/abc", meta: {a:1}, body:"123"}).then(function() {
|
library.saveEntry({library: "local",type: "forbidden", path: "/abc", meta: {a:1}, body:"123"}).then(function() {
|
||||||
done(new Error("did not reject"));
|
done(new Error("did not reject"));
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
err.should.have.property("code","forbidden");
|
err.should.have.property("code","forbidden");
|
||||||
@ -155,7 +155,7 @@ describe("runtime-api/library", function() {
|
|||||||
}).catch(done)
|
}).catch(done)
|
||||||
})
|
})
|
||||||
it("rejects an unknown entry", function(done) {
|
it("rejects an unknown entry", function(done) {
|
||||||
library.saveEntry({type: "not_found", path: "/abc", meta: {a:1}, body:"123"}).then(function() {
|
library.saveEntry({library: "local",type: "not_found", path: "/abc", meta: {a:1}, body:"123"}).then(function() {
|
||||||
done(new Error("did not reject"));
|
done(new Error("did not reject"));
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
err.should.have.property("status",400);
|
err.should.have.property("status",400);
|
||||||
@ -163,377 +163,5 @@ describe("runtime-api/library", function() {
|
|||||||
}).catch(done)
|
}).catch(done)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe("getEntries", function() {
|
|
||||||
var opts;
|
|
||||||
before(function() {
|
|
||||||
library.init({
|
|
||||||
log: mockLog,
|
|
||||||
storage: {
|
|
||||||
getAllFlows: function() {
|
|
||||||
return Promise.resolve({a:1});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
nodes: {
|
|
||||||
getNodeExampleFlows: function() {
|
|
||||||
return {b:2};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it("returns all flows", function(done) {
|
|
||||||
library.getEntries({type:"flows"}).then(function(result) {
|
|
||||||
result.should.eql({a:1,d:{_examples_:{b:2}}});
|
|
||||||
done();
|
|
||||||
}).catch(done)
|
|
||||||
});
|
|
||||||
it("fails for non-flows (currently)", function(done) {
|
|
||||||
library.getEntries({type:"functions"}).then(function(result) {
|
|
||||||
done(new Error("did not reject"));
|
|
||||||
}).catch(function(err) {
|
|
||||||
done();
|
|
||||||
}).catch(done)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
var should = require("should");
|
|
||||||
var sinon = require("sinon");
|
|
||||||
var fs = require("fs");
|
|
||||||
var fspath = require('path');
|
|
||||||
var request = require('supertest');
|
|
||||||
var express = require('express');
|
|
||||||
var bodyParser = require('body-parser');
|
|
||||||
|
|
||||||
var when = require('when');
|
|
||||||
|
|
||||||
var app;
|
|
||||||
var library = require("../../../../red/api/editor/library");
|
|
||||||
var auth = require("../../../../red/api/auth");
|
|
||||||
|
|
||||||
describe("api/editor/library", function() {
|
|
||||||
|
|
||||||
function initLibrary(_flows,_libraryEntries,_examples,_exampleFlowPathFunction) {
|
|
||||||
var flows = _flows;
|
|
||||||
var libraryEntries = _libraryEntries;
|
|
||||||
library.init(app,{
|
|
||||||
log:{audit:function(){},_:function(){},warn:function(){}},
|
|
||||||
storage: {
|
|
||||||
init: function() {
|
|
||||||
return when.resolve();
|
|
||||||
},
|
|
||||||
getAllFlows: function() {
|
|
||||||
return when.resolve(flows);
|
|
||||||
},
|
|
||||||
getFlow: function(fn) {
|
|
||||||
if (flows[fn]) {
|
|
||||||
return when.resolve(flows[fn]);
|
|
||||||
} else if (fn.indexOf("..")!==-1) {
|
|
||||||
var err = new Error();
|
|
||||||
err.code = 'forbidden';
|
|
||||||
return when.reject(err);
|
|
||||||
} else {
|
|
||||||
return when.reject();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
saveFlow: function(fn,data) {
|
|
||||||
if (fn.indexOf("..")!==-1) {
|
|
||||||
var err = new Error();
|
|
||||||
err.code = 'forbidden';
|
|
||||||
return when.reject(err);
|
|
||||||
}
|
|
||||||
flows[fn] = data;
|
|
||||||
return when.resolve();
|
|
||||||
},
|
|
||||||
getLibraryEntry: function(type,path) {
|
|
||||||
if (path.indexOf("..")!==-1) {
|
|
||||||
var err = new Error();
|
|
||||||
err.code = 'forbidden';
|
|
||||||
return when.reject(err);
|
|
||||||
}
|
|
||||||
if (libraryEntries[type] && libraryEntries[type][path]) {
|
|
||||||
return when.resolve(libraryEntries[type][path]);
|
|
||||||
} else {
|
|
||||||
return when.reject();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
saveLibraryEntry: function(type,path,meta,body) {
|
|
||||||
if (path.indexOf("..")!==-1) {
|
|
||||||
var err = new Error();
|
|
||||||
err.code = 'forbidden';
|
|
||||||
return when.reject(err);
|
|
||||||
}
|
|
||||||
libraryEntries[type][path] = body;
|
|
||||||
return when.resolve();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
events: {
|
|
||||||
on: function(){},
|
|
||||||
removeListener: function(){}
|
|
||||||
},
|
|
||||||
nodes: {
|
|
||||||
getNodeExampleFlows: function() {
|
|
||||||
return _examples;
|
|
||||||
},
|
|
||||||
getNodeExampleFlowPath: _exampleFlowPathFunction
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("flows", function() {
|
|
||||||
before(function() {
|
|
||||||
app = express();
|
|
||||||
app.use(bodyParser.json());
|
|
||||||
app.get("/library/flows",library.getAll);
|
|
||||||
app.post(new RegExp("/library/flows\/(.*)"),library.post);
|
|
||||||
app.get(new RegExp("/library/flows\/(.*)"),library.get);
|
|
||||||
app.response.sendFile = function (path) {
|
|
||||||
app.response.json.call(this, {sendFile: path});
|
|
||||||
};
|
|
||||||
sinon.stub(fs,"statSync",function() { return true; });
|
|
||||||
});
|
|
||||||
after(function() {
|
|
||||||
fs.statSync.restore();
|
|
||||||
});
|
|
||||||
it('returns empty result', function(done) {
|
|
||||||
initLibrary({},{flows:{}});
|
|
||||||
request(app)
|
|
||||||
.get('/library/flows')
|
|
||||||
.expect(200)
|
|
||||||
.end(function(err,res) {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
res.body.should.not.have.property('f');
|
|
||||||
res.body.should.not.have.property('d');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns 404 for non-existent entry', function(done) {
|
|
||||||
initLibrary({},{flows:{}});
|
|
||||||
request(app)
|
|
||||||
.get('/library/flows/foo')
|
|
||||||
.expect(404)
|
|
||||||
.end(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('can store and retrieve item', function(done) {
|
|
||||||
initLibrary({},{flows:{}});
|
|
||||||
var flow = '[]';
|
|
||||||
request(app)
|
|
||||||
.post('/library/flows/foo')
|
|
||||||
.set('Content-Type', 'application/json')
|
|
||||||
.send(flow)
|
|
||||||
.expect(204).end(function (err, res) {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
request(app)
|
|
||||||
.get('/library/flows/foo')
|
|
||||||
.expect(200)
|
|
||||||
.end(function(err,res) {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
res.text.should.equal(flow);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('lists a stored item', function(done) {
|
|
||||||
initLibrary({f:["bar"]});
|
|
||||||
request(app)
|
|
||||||
.get('/library/flows')
|
|
||||||
.expect(200)
|
|
||||||
.end(function(err,res) {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
res.body.should.have.property('f');
|
|
||||||
should.deepEqual(res.body.f,['bar']);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns 403 for malicious get attempt', function(done) {
|
|
||||||
initLibrary({});
|
|
||||||
// without the userDir override the malicious url would be
|
|
||||||
// http://127.0.0.1:1880/library/flows/../../package to
|
|
||||||
// obtain package.json from the node-red root.
|
|
||||||
request(app)
|
|
||||||
.get('/library/flows/../../../../../package')
|
|
||||||
.expect(403)
|
|
||||||
.end(done);
|
|
||||||
});
|
|
||||||
it('returns 403 for malicious post attempt', function(done) {
|
|
||||||
initLibrary({});
|
|
||||||
// without the userDir override the malicious url would be
|
|
||||||
// http://127.0.0.1:1880/library/flows/../../package to
|
|
||||||
// obtain package.json from the node-red root.
|
|
||||||
request(app)
|
|
||||||
.post('/library/flows/../../../../../package')
|
|
||||||
.expect(403)
|
|
||||||
.end(done);
|
|
||||||
});
|
|
||||||
it('includes examples flows if set', function(done) {
|
|
||||||
var examples = {"d":{"node-module":{"f":["example-one"]}}};
|
|
||||||
initLibrary({},{},examples);
|
|
||||||
request(app)
|
|
||||||
.get('/library/flows')
|
|
||||||
.expect(200)
|
|
||||||
.end(function(err,res) {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
res.body.should.have.property('d');
|
|
||||||
res.body.d.should.have.property('_examples_');
|
|
||||||
should.deepEqual(res.body.d._examples_,examples);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can retrieve an example flow', function(done) {
|
|
||||||
var examples = {"d":{"node-module":{"f":["example-one"]}}};
|
|
||||||
initLibrary({},{},examples,function(module,path) {
|
|
||||||
return module + ':' + path
|
|
||||||
});
|
|
||||||
request(app)
|
|
||||||
.get('/library/flows/_examples_/node-module/example-one')
|
|
||||||
.expect(200)
|
|
||||||
.end(function(err,res) {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
res.body.should.have.property('sendFile',
|
|
||||||
fspath.resolve('node-module') + ':example-one');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can retrieve an example flow in an org scoped package', function(done) {
|
|
||||||
var examples = {"d":{"@org_scope/node_package":{"f":["example-one"]}}};
|
|
||||||
initLibrary({},{},examples,function(module,path) {
|
|
||||||
return module + ':' + path
|
|
||||||
});
|
|
||||||
request(app)
|
|
||||||
.get('/library/flows/_examples_/@org_scope/node_package/example-one')
|
|
||||||
.expect(200)
|
|
||||||
.end(function(err,res) {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
res.body.should.have.property('sendFile',
|
|
||||||
fspath.resolve('@org_scope/node_package') +
|
|
||||||
':example-one');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("type", function() {
|
|
||||||
before(function() {
|
|
||||||
|
|
||||||
app = express();
|
|
||||||
app.use(bodyParser.json());
|
|
||||||
initLibrary({},{});
|
|
||||||
auth.init({settings:{}});
|
|
||||||
library.register("test");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns empty result', function(done) {
|
|
||||||
initLibrary({},{'test':{"":[]}});
|
|
||||||
request(app)
|
|
||||||
.get('/library/test')
|
|
||||||
.expect(200)
|
|
||||||
.end(function(err,res) {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
res.body.should.not.have.property('f');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns 404 for non-existent entry', function(done) {
|
|
||||||
initLibrary({},{});
|
|
||||||
request(app)
|
|
||||||
.get('/library/test/foo')
|
|
||||||
.expect(404)
|
|
||||||
.end(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can store and retrieve item', function(done) {
|
|
||||||
initLibrary({},{'test':{}});
|
|
||||||
var flow = {text:"test content"};
|
|
||||||
request(app)
|
|
||||||
.post('/library/test/foo')
|
|
||||||
.set('Content-Type', 'application/json')
|
|
||||||
.send(flow)
|
|
||||||
.expect(204).end(function (err, res) {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
request(app)
|
|
||||||
.get('/library/test/foo')
|
|
||||||
.expect(200)
|
|
||||||
.end(function(err,res) {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
res.text.should.equal(flow.text);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('lists a stored item', function(done) {
|
|
||||||
initLibrary({},{'test':{'a':['abc','def']}});
|
|
||||||
request(app)
|
|
||||||
.get('/library/test/a')
|
|
||||||
.expect(200)
|
|
||||||
.end(function(err,res) {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
// This response isn't strictly accurate - but it
|
|
||||||
// verifies the api returns what storage gave it
|
|
||||||
should.deepEqual(res.body,['abc','def']);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('returns 403 for malicious access attempt', function(done) {
|
|
||||||
request(app)
|
|
||||||
.get('/library/test/../../../../../../../../../../etc/passwd')
|
|
||||||
.expect(403)
|
|
||||||
.end(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns 403 for malicious access attempt', function(done) {
|
|
||||||
request(app)
|
|
||||||
.get('/library/test/..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\etc\\passwd')
|
|
||||||
.expect(403)
|
|
||||||
.end(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns 403 for malicious access attempt', function(done) {
|
|
||||||
request(app)
|
|
||||||
.post('/library/test/../../../../../../../../../../etc/passwd')
|
|
||||||
.set('Content-Type', 'text/plain')
|
|
||||||
.send('root:x:0:0:root:/root:/usr/bin/tclsh')
|
|
||||||
.expect(403)
|
|
||||||
.end(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
138
test/unit/@node-red/runtime/lib/library/examples_spec.js
Normal file
138
test/unit/@node-red/runtime/lib/library/examples_spec.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
/**
|
||||||
|
* 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 should = require("should");
|
||||||
|
var sinon = require("sinon");
|
||||||
|
var fs = require("fs");
|
||||||
|
|
||||||
|
var NR_TEST_UTILS = require("nr-test-utils");
|
||||||
|
var examplesLibrary = NR_TEST_UTILS.require("@node-red/runtime/lib/library/examples")
|
||||||
|
|
||||||
|
var mockLog = {
|
||||||
|
log: sinon.stub(),
|
||||||
|
debug: sinon.stub(),
|
||||||
|
trace: sinon.stub(),
|
||||||
|
warn: sinon.stub(),
|
||||||
|
info: sinon.stub(),
|
||||||
|
metric: sinon.stub(),
|
||||||
|
audit: sinon.stub(),
|
||||||
|
_: function() { return "abc"}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("runtime/library/examples", function() {
|
||||||
|
describe("getEntry", function() {
|
||||||
|
before(function() {
|
||||||
|
examplesLibrary.init({
|
||||||
|
log: mockLog,
|
||||||
|
storage: {
|
||||||
|
getLibraryEntry: function(type,path) {
|
||||||
|
return Promise.resolve({type,path});
|
||||||
|
},
|
||||||
|
getFlow: function(path) {
|
||||||
|
return Promise.resolve({path});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nodes: {
|
||||||
|
getNodeExampleFlows: function() {
|
||||||
|
return {
|
||||||
|
"test-module": {
|
||||||
|
f: ["abc"]
|
||||||
|
},
|
||||||
|
"@scope/test-module": {
|
||||||
|
f: ["abc","throw"]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getNodeExampleFlowPath: function(module,entryPath) {
|
||||||
|
if (module === "unknown") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return "/tmp/"+module+"/"+entryPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
sinon.stub(fs,"readFile", function(path,opts,callback) {
|
||||||
|
if (path === "/tmp/test-module/abc") {
|
||||||
|
callback(null,"Example flow result");
|
||||||
|
} else if (path === "/tmp/@scope/test-module/abc") {
|
||||||
|
callback(null,"Example scope flow result");
|
||||||
|
} else if (path === "/tmp/test-module/throw") {
|
||||||
|
throw new Error("Instant error")
|
||||||
|
} else {
|
||||||
|
callback(new Error("Unexpected path:"+path))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
after(function() {
|
||||||
|
fs.readFile.restore();
|
||||||
|
})
|
||||||
|
|
||||||
|
it ('returns a flow example entry', function(done) {
|
||||||
|
examplesLibrary.getEntry("flows","test-module/abc").then(function(result) {
|
||||||
|
result.should.eql("Example flow result");
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it ('returns a flow example listing - top level', function(done) {
|
||||||
|
examplesLibrary.getEntry("flows","").then(function(result) {
|
||||||
|
result.should.eql([ 'test-module', '@scope/test-module' ])
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
});
|
||||||
|
it ('returns a flow example listing - in module', function(done) {
|
||||||
|
examplesLibrary.getEntry("flows","test-module").then(function(result) {
|
||||||
|
result.should.eql([{ fn: 'abc' }])
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
});
|
||||||
|
it ('returns a flow example listing - in scoped module', function(done) {
|
||||||
|
examplesLibrary.getEntry("flows","@scope/test-module").then(function(result) {
|
||||||
|
result.should.eql([{ fn: 'abc' }, {fn: 'throw'}])
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
});
|
||||||
|
it ('returns a flow example entry from scoped module', function(done) {
|
||||||
|
examplesLibrary.getEntry("flows","@scope/test-module/abc").then(function(result) {
|
||||||
|
result.should.eql("Example scope flow result");
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
});
|
||||||
|
it ('returns an error for unknown flow example entry', function(done) {
|
||||||
|
examplesLibrary.getEntry("flows","unknown/abc").then(function(result) {
|
||||||
|
done(new Error("No error thrown"))
|
||||||
|
}).catch(function(err) {
|
||||||
|
err.should.have.property("code","not_found");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it ('returns an error for file load error - async', function(done) {
|
||||||
|
examplesLibrary.getEntry("flows","test-module/unknown").then(function(result) {
|
||||||
|
done(new Error("No error thrown"))
|
||||||
|
}).catch(function(err) {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it ('returns an error for file load error - sync', function(done) {
|
||||||
|
examplesLibrary.getEntry("flows","test-module/throw").then(function(result) {
|
||||||
|
done(new Error("No error thrown"))
|
||||||
|
}).catch(function(err) {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -16,10 +16,11 @@
|
|||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var sinon = require("sinon");
|
var sinon = require("sinon");
|
||||||
var fs = require("fs");
|
|
||||||
|
|
||||||
var NR_TEST_UTILS = require("nr-test-utils");
|
var NR_TEST_UTILS = require("nr-test-utils");
|
||||||
var library = NR_TEST_UTILS.require("@node-red/runtime/lib/library/index")
|
var library = NR_TEST_UTILS.require("@node-red/runtime/lib/library/index")
|
||||||
|
var localLibrary = NR_TEST_UTILS.require("@node-red/runtime/lib/library/local")
|
||||||
|
var examplesLibrary = NR_TEST_UTILS.require("@node-red/runtime/lib/library/examples")
|
||||||
|
|
||||||
var mockLog = {
|
var mockLog = {
|
||||||
log: sinon.stub(),
|
log: sinon.stub(),
|
||||||
@ -34,6 +35,36 @@ var mockLog = {
|
|||||||
|
|
||||||
|
|
||||||
describe("runtime/library", function() {
|
describe("runtime/library", function() {
|
||||||
|
before(function() {
|
||||||
|
sinon.stub(localLibrary,"getEntry",function(type,path) {
|
||||||
|
return Promise.resolve({
|
||||||
|
library: "local",
|
||||||
|
type:type,
|
||||||
|
path:path
|
||||||
|
})
|
||||||
|
});
|
||||||
|
sinon.stub(localLibrary,"saveEntry",function(type, path, meta, body) {
|
||||||
|
return Promise.resolve({
|
||||||
|
library: "local",
|
||||||
|
type:type,
|
||||||
|
path:path,
|
||||||
|
meta:meta,
|
||||||
|
body:body
|
||||||
|
})
|
||||||
|
});
|
||||||
|
sinon.stub(examplesLibrary,"getEntry",function(type,path) {
|
||||||
|
return Promise.resolve({
|
||||||
|
library: "_examples_",
|
||||||
|
type:type,
|
||||||
|
path:path
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
after(function() {
|
||||||
|
localLibrary.getEntry.restore();
|
||||||
|
localLibrary.saveEntry.restore();
|
||||||
|
examplesLibrary.getEntry.restore();
|
||||||
|
})
|
||||||
describe("register", function() {
|
describe("register", function() {
|
||||||
// it("throws error for duplicate type", function() {
|
// it("throws error for duplicate type", function() {
|
||||||
// library.init({});
|
// library.init({});
|
||||||
@ -43,47 +74,19 @@ describe("runtime/library", function() {
|
|||||||
})
|
})
|
||||||
describe("getEntry", function() {
|
describe("getEntry", function() {
|
||||||
before(function() {
|
before(function() {
|
||||||
library.init({
|
library.init({});
|
||||||
log: mockLog,
|
|
||||||
storage: {
|
|
||||||
getLibraryEntry: function(type,path) {
|
|
||||||
return Promise.resolve({type,path});
|
|
||||||
},
|
|
||||||
getFlow: function(path) {
|
|
||||||
return Promise.resolve({path});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
nodes: {
|
|
||||||
getNodeExampleFlowPath: function(module,entryPath) {
|
|
||||||
if (module === "unknown") {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return "/tmp/"+module+"/"+entryPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
sinon.stub(fs,"readFile", function(path,opts,callback) {
|
|
||||||
if (path === "/tmp/test-module/abc") {
|
|
||||||
callback(null,"Example flow result");
|
|
||||||
} else if (path === "/tmp/@scope/test-module/abc") {
|
|
||||||
callback(null,"Example scope flow result");
|
|
||||||
} else if (path === "/tmp/test-module/throw") {
|
|
||||||
throw new Error("Instant error")
|
|
||||||
} else {
|
|
||||||
callback(new Error("Unexpected path:"+path))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
after(function() {
|
|
||||||
fs.readFile.restore();
|
|
||||||
})
|
|
||||||
it('throws error for unregistered type', function() {
|
it('throws error for unregistered type', function() {
|
||||||
should(()=>{library.getEntry("unknown","/abc")} ).throw();
|
should(()=>{library.getEntry("local","unknown","/abc")} ).throw();
|
||||||
|
});
|
||||||
|
it('throws error for unknown library', function() {
|
||||||
|
should(()=>{library.getEntry("unknown","unknown","/abc")} ).throw();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns a registered non-flow entry', function(done) {
|
it('returns a registered non-flow entry', function(done) {
|
||||||
library.register("test-module","test-type");
|
library.register("test-module","test-type");
|
||||||
library.getEntry("test-type","/abc").then(function(result) {
|
library.getEntry("local","test-type","/abc").then(function(result) {
|
||||||
|
result.should.have.property("library","local")
|
||||||
result.should.have.property("type","test-type")
|
result.should.have.property("type","test-type")
|
||||||
result.should.have.property("path","/abc")
|
result.should.have.property("path","/abc")
|
||||||
done();
|
done();
|
||||||
@ -91,76 +94,37 @@ describe("runtime/library", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it ('returns a flow entry', function(done) {
|
it ('returns a flow entry', function(done) {
|
||||||
library.getEntry("flows","/abc").then(function(result) {
|
library.getEntry("local","flows","/abc").then(function(result) {
|
||||||
|
result.should.have.property("library","local")
|
||||||
result.should.have.property("path","/abc")
|
result.should.have.property("path","/abc")
|
||||||
done();
|
done();
|
||||||
}).catch(done);
|
}).catch(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it ('returns a flow example entry', function(done) {
|
it ('returns a flow example entry', function(done) {
|
||||||
library.getEntry("flows","_examples_/test-module/abc").then(function(result) {
|
library.getEntry("_examples_","flows","/test-module/abc").then(function(result) {
|
||||||
result.should.eql("Example flow result");
|
result.should.have.property("library","_examples_")
|
||||||
|
result.should.have.property("path","/test-module/abc")
|
||||||
done();
|
done();
|
||||||
}).catch(done);
|
}).catch(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it ('returns a flow example entry from scoped module', function(done) {
|
|
||||||
library.getEntry("flows","_examples_/@scope/test-module/abc").then(function(result) {
|
|
||||||
result.should.eql("Example scope flow result");
|
|
||||||
done();
|
|
||||||
}).catch(done);
|
|
||||||
});
|
|
||||||
it ('returns an error for unknown flow example entry', function(done) {
|
|
||||||
library.getEntry("flows","_examples_/unknown/abc").then(function(result) {
|
|
||||||
done(new Error("No error thrown"))
|
|
||||||
}).catch(function(err) {
|
|
||||||
err.should.have.property("code","not_found");
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it ('returns an error for file load error - async', function(done) {
|
|
||||||
library.getEntry("flows","_examples_/test-module/unknown").then(function(result) {
|
|
||||||
done(new Error("No error thrown"))
|
|
||||||
}).catch(function(err) {
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it ('returns an error for file load error - sync', function(done) {
|
|
||||||
library.getEntry("flows","_examples_/test-module/throw").then(function(result) {
|
|
||||||
done(new Error("No error thrown"))
|
|
||||||
}).catch(function(err) {
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("saveEntry", function() {
|
describe("saveEntry", function() {
|
||||||
before(function() {
|
before(function() {
|
||||||
library.init({
|
library.init({});
|
||||||
log: mockLog,
|
});
|
||||||
storage: {
|
it('throws error for unknown library', function() {
|
||||||
saveLibraryEntry: function(type, path, meta, body) {
|
should(()=>{library.saveEntry("unknown","unknown","/abc",{id:"meta"},{id:"body"})} ).throw();
|
||||||
return Promise.resolve({type,path,meta,body})
|
|
||||||
},
|
|
||||||
saveFlow: function(path,body) {
|
|
||||||
return Promise.resolve({path,body});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
nodes: {
|
|
||||||
getNodeExampleFlowPath: function(module,entryPath) {
|
|
||||||
if (module === "unknown") {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return "/tmp/"+module+"/"+entryPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
it('throws error for unregistered type', function() {
|
it('throws error for unregistered type', function() {
|
||||||
should(()=>{library.saveEntry("unknown","/abc",{id:"meta"},{id:"body"})} ).throw();
|
should(()=>{library.saveEntry("local","unknown","/abc",{id:"meta"},{id:"body"})} ).throw();
|
||||||
|
});
|
||||||
|
it('throws error for save to readonly library', function() {
|
||||||
|
should(()=>{library.saveEntry("_examples_","unknown","/abc",{id:"meta"},{id:"body"})} ).throw();
|
||||||
});
|
});
|
||||||
it('saves a flow entry', function(done) {
|
it('saves a flow entry', function(done) {
|
||||||
library.saveEntry('flows','/abc',{id:"meta"},{id:"body"}).then(function(result) {
|
library.saveEntry('local','flows','/abc',{id:"meta"},{id:"body"}).then(function(result) {
|
||||||
result.should.have.property("path","/abc");
|
result.should.have.property("path","/abc");
|
||||||
result.should.have.property("body",{id:"body"});
|
result.should.have.property("body",{id:"body"});
|
||||||
done();
|
done();
|
||||||
@ -168,7 +132,7 @@ describe("runtime/library", function() {
|
|||||||
})
|
})
|
||||||
it('saves a non-flow entry', function(done) {
|
it('saves a non-flow entry', function(done) {
|
||||||
library.register("test-module","test-type");
|
library.register("test-module","test-type");
|
||||||
library.saveEntry('test-type','/abc',{id:"meta"},{id:"body"}).then(function(result) {
|
library.saveEntry('local','test-type','/abc',{id:"meta"},{id:"body"}).then(function(result) {
|
||||||
result.should.have.property("type","test-type");
|
result.should.have.property("type","test-type");
|
||||||
result.should.have.property("path","/abc");
|
result.should.have.property("path","/abc");
|
||||||
result.should.have.property("meta",{id:"meta"});
|
result.should.have.property("meta",{id:"meta"});
|
||||||
|
93
test/unit/@node-red/runtime/lib/library/local_spec.js
Normal file
93
test/unit/@node-red/runtime/lib/library/local_spec.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/**
|
||||||
|
* 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 should = require("should");
|
||||||
|
var sinon = require("sinon");
|
||||||
|
|
||||||
|
var NR_TEST_UTILS = require("nr-test-utils");
|
||||||
|
var localLibrary = NR_TEST_UTILS.require("@node-red/runtime/lib/library/local")
|
||||||
|
|
||||||
|
var mockLog = {
|
||||||
|
log: sinon.stub(),
|
||||||
|
debug: sinon.stub(),
|
||||||
|
trace: sinon.stub(),
|
||||||
|
warn: sinon.stub(),
|
||||||
|
info: sinon.stub(),
|
||||||
|
metric: sinon.stub(),
|
||||||
|
audit: sinon.stub(),
|
||||||
|
_: function() { return "abc"}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("runtime/library/local", function() {
|
||||||
|
|
||||||
|
describe("getEntry", function() {
|
||||||
|
before(function() {
|
||||||
|
localLibrary.init({
|
||||||
|
log: mockLog,
|
||||||
|
storage: {
|
||||||
|
getLibraryEntry: function(type,path) {
|
||||||
|
return Promise.resolve({type,path});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns a registered non-flow entry', function(done) {
|
||||||
|
localLibrary.getEntry("test-type","/abc").then(function(result) {
|
||||||
|
result.should.have.property("type","test-type")
|
||||||
|
result.should.have.property("path","/abc")
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it ('returns a flow entry', function(done) {
|
||||||
|
localLibrary.getEntry("flows","/abc").then(function(result) {
|
||||||
|
result.should.have.property("path","/abc")
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("saveEntry", function() {
|
||||||
|
before(function() {
|
||||||
|
localLibrary.init({
|
||||||
|
log: mockLog,
|
||||||
|
storage: {
|
||||||
|
saveLibraryEntry: function(type, path, meta, body) {
|
||||||
|
return Promise.resolve({type,path,meta,body})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('saves a flow entry', function(done) {
|
||||||
|
localLibrary.saveEntry('flows','/abc',{id:"meta"},{id:"body"}).then(function(result) {
|
||||||
|
result.should.have.property("path","/abc");
|
||||||
|
result.should.have.property("body",{id:"body"});
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
})
|
||||||
|
it('saves a non-flow entry', function(done) {
|
||||||
|
localLibrary.saveEntry('test-type','/abc',{id:"meta"},{id:"body"}).then(function(result) {
|
||||||
|
result.should.have.property("type","test-type");
|
||||||
|
result.should.have.property("path","/abc");
|
||||||
|
result.should.have.property("meta",{id:"meta"});
|
||||||
|
result.should.have.property("body",{id:"body"});
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
})
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user