diff --git a/red/storage/index.js b/red/storage/index.js index 28311c2ec..9d5dcfa8e 100644 --- a/red/storage/index.js +++ b/red/storage/index.js @@ -92,21 +92,7 @@ var storageModuleInterface = { }, /* Library Functions */ - getAllFlows: function() { - return storageModule.getAllFlows(); - }, - getFlow: function(fn) { - if (is_malicious(fn)) { - return when.reject(new Error('forbidden flow name')); - } - return storageModule.getFlow(fn); - }, - saveFlow: function(fn, data) { - if (is_malicious(fn)) { - return when.reject(new Error('forbidden flow name')); - } - return storageModule.saveFlow(fn, data); - }, + getLibraryEntry: function(type, path) { if (is_malicious(path)) { return when.reject(new Error('forbidden flow name')); @@ -118,7 +104,78 @@ var storageModuleInterface = { return when.reject(new Error('forbidden flow name')); } return storageModule.saveLibraryEntry(type, path, meta, body); + }, + +/* Deprecated functions */ + getAllFlows: function() { + if (storageModule.hasOwnProperty("getAllFlows")) { + return storageModule.getAllFlows(); + } else { + return listFlows("/"); + } + }, + getFlow: function(fn) { + if (is_malicious(fn)) { + return when.reject(new Error('forbidden flow name')); + } + if (storageModule.hasOwnProperty("getFlow")) { + return storageModule.getFlow(fn); + } else { + return storageModule.getLibraryEntry("flows",fn); + } + + }, + saveFlow: function(fn, data) { + if (is_malicious(fn)) { + return when.reject(new Error('forbidden flow name')); + } + if (storageModule.hasOwnProperty("saveFlow")) { + return storageModule.saveFlow(fn, data); + } else { + return storageModule.saveLibraryEntry("flows",fn,{},data); + } } +/* End deprecated functions */ + } + +function listFlows(path) { + return storageModule.getLibraryEntry("flows",path).then(function(res) { + return when.promise(function(resolve) { + var promises = []; + res.forEach(function(r) { + if (typeof r === "string") { + promises.push(listFlows(path+r)); + } else { + promises.push(when.resolve(r)); + } + }); + var i=0; + when.settle(promises).then(function(res2) { + var result = {}; + res2.forEach(function(r) { + // TODO: name||fn + if (r.value.fn) { + var name = r.value.name; + if (!name) { + name = r.value.fn.split(".")[0]; + } + result.f = result.f || []; + result.f.push(name); + } else { + result.d = result.d || {}; + result.d[res[i]] = r.value; + //console.log(">",r.value); + } + i++; + }); + resolve(result); + }); + }); + }); +} + + + module.exports = storageModuleInterface; diff --git a/red/storage/localfilesystem.js b/red/storage/localfilesystem.js index f61f325c4..c7cd29bb9 100644 --- a/red/storage/localfilesystem.js +++ b/red/storage/localfilesystem.js @@ -37,27 +37,6 @@ var libDir; var libFlowsDir; var globalSettingsFile; -function listFiles(dir) { - var dirs = {}; - var files = []; - var dirCount = 0; - return nodeFn.call(fs.readdir, dir).then(function (contents) { - contents.sort().forEach(function(fn) { - var stats = fs.lstatSync(dir+"/"+fn); - if (stats.isDirectory()) { - dirCount += 1; - dirs[fn] = listFiles(dir+"/"+fn) - } else { - files.push(fn.split(".")[0]); - } - }) - var result = {}; - if (dirCount > 0) { result.d = keys.all(dirs); } - if (files.length > 0) { result.f = when.resolve(files); } - return keys.all(result); - }) -} - function getFileMeta(root,path) { var fn = fspath.join(root,path); var fd = fs.openSync(fn,"r"); @@ -312,30 +291,6 @@ var localfilesystem = { return writeFile(sessionsFile,JSON.stringify(sessions)); }, - getAllFlows: function() { - return listFiles(libFlowsDir); - }, - - getFlow: function(fn) { - var defer = when.defer(); - var file = fspath.join(libFlowsDir,fn+".json"); - fs.exists(file, function(exists) { - if (exists) { - defer.resolve(nodeFn.call(fs.readFile,file,'utf8')); - } else { - defer.reject(); - } - }); - return defer.promise; - }, - - saveFlow: function(fn,data) { - var file = fspath.join(libFlowsDir,fn+".json"); - return promiseDir(fspath.dirname(file)).then(function () { - return writeFile(file,data); - }); - }, - getLibraryEntry: function(type,path) { var root = fspath.join(libDir,type); var rootPath = fspath.join(libDir,type,path); @@ -366,6 +321,15 @@ var localfilesystem = { }); return dirs.concat(files); }); + }).otherwise(function(err) { + if (type === "flows" && !/\.json$/.test(path)) { + return localfilesystem.getLibraryEntry(type,path+".json") + .otherwise(function(e) { + throw err; + }); + } else { + throw err; + } }); }); }, diff --git a/test/red/storage/index_spec.js b/test/red/storage/index_spec.js index 374a8c4fb..2f05fd518 100644 --- a/test/red/storage/index_spec.js +++ b/test/red/storage/index_spec.js @@ -1,5 +1,5 @@ /** - * Copyright 2014 IBM Corp. + * Copyright 2014, 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ +var when = require("when"); var should = require("should"); var storage = require("../../../red/storage/index"); @@ -139,6 +140,81 @@ describe("red/storage/index", function() { calledFlagGetAllFlows.should.be.true; }); + describe('respects deprecated flow library functions', function() { + + var savePath; + var saveContent; + var saveMeta; + var saveType; + + var interfaceCheckerModule = { + init : function (settings) { + settings.should.be.an.Object; + }, + getLibraryEntry : function(type, path) { + if (type === "flows") { + if (path == "/") { + return when.resolve(["a",{fn:"test.json"}]); + } else if (path == "/a") { + return when.resolve([{fn:"test2.json"}]); + } else if (path == "/a/test2.json") { + return when.resolve("test content"); + } + } + }, + saveLibraryEntry : function(type, path, meta, body) { + saveType = type; + savePath = path; + saveContent = body; + saveMeta = meta; + return when.resolve(); + } + }; + + var moduleToLoad = { + storageModule : interfaceCheckerModule + }; + before(function() { + storage.init(moduleToLoad); + }); + it('getAllFlows',function(done) { + storage.getAllFlows().then(function (res) { + try { + res.should.eql({ d: { a: { f: ['test2'] } }, f: [ 'test' ] }); + done(); + } catch(err) { + done(err); + } + }); + }); + + it('getFlow',function(done) { + storage.getFlow("/a/test2.json").then(function(res) { + try { + res.should.eql("test content"); + done(); + } catch(err) { + done(err); + } + }); + }); + + it ('saveFlow', function (done) { + storage.saveFlow("/a/test2.json","new content").then(function(res) { + try { + savePath.should.eql("/a/test2.json"); + saveContent.should.eql("new content"); + saveMeta.should.eql({}); + saveType.should.eql("flows"); + done(); + } catch(err) { + done(err); + } + }); + + }); + }); + describe('handles missing settings/sessions interface', function() { before(function() { var interfaceCheckerModule = { diff --git a/test/red/storage/localfilesystem_spec.js b/test/red/storage/localfilesystem_spec.js index dfcac981f..d0cb1a272 100644 --- a/test/red/storage/localfilesystem_spec.js +++ b/test/red/storage/localfilesystem_spec.js @@ -437,72 +437,6 @@ describe('LocalFileSystem', function() { }); - - - it('should return an empty list of library flows',function(done) { - localfilesystem.init({userDir:userDir}).then(function() { - localfilesystem.getAllFlows().then(function(flows) { - flows.should.eql({}); - done(); - }).otherwise(function(err) { - done(err); - }); - }).otherwise(function(err) { - done(err); - }); - }); - - it('should return a valid list of library flows',function(done) { - localfilesystem.init({userDir:userDir}).then(function() { - var flowLib = path.join(userDir,"lib","flows"); - fs.closeSync(fs.openSync(path.join(flowLib,"A.json"),"w")); - fs.closeSync(fs.openSync(path.join(flowLib,"B.json"),"w")); - fs.mkdirSync(path.join(flowLib,"C")); - fs.closeSync(fs.openSync(path.join(flowLib,"C","D.json"),"w")); - var testFlowsList = {"d":{"C":{"f":["D"]}},"f":["A","B"]}; - - localfilesystem.getAllFlows().then(function(flows) { - flows.should.eql(testFlowsList); - done(); - }).otherwise(function(err) { - done(err); - }); - }).otherwise(function(err) { - done(err); - }); - }); - - it('should fail a non-existent flow', function(done) { - localfilesystem.init({userDir:userDir}).then(function() { - localfilesystem.getFlow("a/b/c.json").then(function(flow) { - should.fail(flow,"No flow","Flow found"); - }).otherwise(function(err) { - // err should be null, so this will pass - done(err); - }); - }).otherwise(function(err) { - done(err); - }); - }); - - it('should return a flow',function(done) { - localfilesystem.init({userDir:userDir}).then(function() { - var testflowString = JSON.stringify(testFlow); - localfilesystem.saveFlow("a/b/c/d.json",testflowString).then(function() { - localfilesystem.getFlow("a/b/c/d.json").then(function(flow) { - flow.should.eql(testflowString); - done(); - }).otherwise(function(err) { - done(err); - }); - }).otherwise(function(err) { - done(err); - }); - }).otherwise(function(err) { - done(err); - }); - }); - it('should return an empty list of library objects',function(done) { localfilesystem.init({userDir:userDir}).then(function() { localfilesystem.getLibraryEntry('object','').then(function(flows) { @@ -529,14 +463,19 @@ describe('LocalFileSystem', function() { }); }); - function createObjectLibrary() { - var objLib = path.join(userDir,"lib","object"); - fs.mkdirSync(objLib); + function createObjectLibrary(type) { + type = type ||"object"; + var objLib = path.join(userDir,"lib",type); + try { + fs.mkdirSync(objLib); + } catch(err) { + } fs.mkdirSync(path.join(objLib,"A")); fs.mkdirSync(path.join(objLib,"B")); fs.mkdirSync(path.join(objLib,"B","C")); fs.writeFileSync(path.join(objLib,"file1.js"),"// abc: def\n// not a metaline \n\n Hi",'utf8'); fs.writeFileSync(path.join(objLib,"B","file2.js"),"// ghi: jkl\n// not a metaline \n\n Hi",'utf8'); + fs.writeFileSync(path.join(objLib,"B","flow.json"),"Hi",'utf8'); } it('should return a directory listing of library objects',function(done) { @@ -546,7 +485,7 @@ describe('LocalFileSystem', function() { localfilesystem.getLibraryEntry('object','').then(function(flows) { flows.should.eql([ 'A', 'B', { abc: 'def', fn: 'file1.js' } ]); localfilesystem.getLibraryEntry('object','B').then(function(flows) { - flows.should.eql([ 'C', { ghi: 'jkl', fn: 'file2.js' } ]); + flows.should.eql([ 'C', { ghi: 'jkl', fn: 'file2.js' }, { fn: 'flow.json' } ]); localfilesystem.getLibraryEntry('object','B/C').then(function(flows) { flows.should.eql([]); done(); @@ -564,6 +503,19 @@ describe('LocalFileSystem', function() { }); }); + it('should load a flow library object with .json unspecified', function(done) { + localfilesystem.init({userDir:userDir}).then(function() { + createObjectLibrary("flows"); + localfilesystem.getLibraryEntry('flows','B/flow').then(function(flows) { + flows.should.eql("Hi"); + done(); + }).otherwise(function(err) { + done(err); + }); + }); + + }); + it('should return a library object',function(done) { localfilesystem.init({userDir:userDir}).then(function() { createObjectLibrary(); @@ -582,7 +534,7 @@ describe('LocalFileSystem', function() { localfilesystem.init({userDir:userDir}).then(function() { createObjectLibrary(); localfilesystem.getLibraryEntry('object','B').then(function(flows) { - flows.should.eql([ 'C', { ghi: 'jkl', fn: 'file2.js' } ]); + flows.should.eql([ 'C', { ghi: 'jkl', fn: 'file2.js' }, {fn:'flow.json'} ]); localfilesystem.saveLibraryEntry('object','B/D/file3.js',{mno:'pqr'},"// another non meta line\n\n Hi There").then(function() { localfilesystem.getLibraryEntry('object','B/D').then(function(flows) { flows.should.eql([ { mno: 'pqr', fn: 'file3.js' } ]);