Skip to content

Commit 7409cb3

Browse files
committed
Separate library api and runtime components
1 parent e8e8f70 commit 7409cb3

9 files changed

Lines changed: 261 additions & 158 deletions

File tree

red/api/editor/index.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,12 @@ module.exports = {
8686

8787
// Library
8888
var library = require("./library");
89-
library.init(editorApp,runtime);
90-
editorApp.post(new RegExp("/library/flows\/(.*)"),needsPermission("library.write"),library.post,apiUtil.errorHandler);
89+
library.init(editorApp,runtimeAPI);
90+
9191
editorApp.get("/library/flows",needsPermission("library.read"),library.getAll,apiUtil.errorHandler);
92-
editorApp.get(new RegExp("/library/flows\/(.*)"),needsPermission("library.read"),library.get,apiUtil.errorHandler);
92+
editorApp.get(/library\/([^\/]+)(?:$|\/(.*))/,needsPermission("library.read"),library.getEntry);
93+
editorApp.post(/library\/([^\/]+)\/(.*)/,needsPermission("library.write"),library.saveEntry);
94+
9395

9496
// Credentials
9597
var credentials = require("./credentials");

red/api/editor/library.js

Lines changed: 48 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -13,149 +13,71 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
**/
16+
17+
var apiUtils = require("../util");
1618
var fs = require('fs');
1719
var fspath = require('path');
1820
var when = require('when');
1921

20-
var redApp = null;
21-
var storage;
22-
var log;
23-
var redNodes;
24-
var needsPermission = require("../auth").needsPermission;
25-
26-
function createLibrary(type) {
27-
if (redApp) {
28-
redApp.get(new RegExp("/library/"+type+"($|\/(.*))"),needsPermission("library.read"),function(req,res) {
29-
var path = req.params[1]||"";
30-
storage.getLibraryEntry(type,path).then(function(result) {
31-
log.audit({event: "library.get",type:type},req);
32-
if (typeof result === "string") {
33-
res.writeHead(200, {'Content-Type': 'text/plain'});
34-
res.write(result);
35-
res.end();
36-
} else {
37-
res.json(result);
38-
}
39-
}).catch(function(err) {
40-
if (err) {
41-
log.warn(log._("api.library.error-load-entry",{path:path,message:err.toString()}));
42-
if (err.code === 'forbidden') {
43-
log.audit({event: "library.get",type:type,error:"forbidden"},req);
44-
res.status(403).end();
45-
return;
46-
}
47-
}
48-
log.audit({event: "library.get",type:type,error:"not_found"},req);
49-
res.status(404).end();
50-
});
51-
});
52-
53-
redApp.post(new RegExp("/library/"+type+"\/(.*)"),needsPermission("library.write"),function(req,res) {
54-
var path = req.params[0];
55-
var meta = req.body;
56-
var text = meta.text;
57-
delete meta.text;
58-
59-
storage.saveLibraryEntry(type,path,meta,text).then(function() {
60-
log.audit({event: "library.set",type:type},req);
61-
res.status(204).end();
62-
}).catch(function(err) {
63-
log.warn(log._("api.library.error-save-entry",{path:path,message:err.toString()}));
64-
if (err.code === 'forbidden') {
65-
log.audit({event: "library.set",type:type,error:"forbidden"},req);
66-
res.status(403).end();
67-
return;
68-
}
69-
log.audit({event: "library.set",type:type,error:"unexpected_error",message:err.toString()},req);
70-
res.status(500).json({error:"unexpected_error", message:err.toString()});
71-
});
72-
});
73-
}
74-
}
22+
var runtimeAPI;
7523

7624
module.exports = {
77-
init: function(app,runtime) {
78-
redApp = app;
79-
log = runtime.log;
80-
storage = runtime.storage;
81-
redNodes = runtime.nodes;
25+
init: function(app,_runtimeAPI) {
26+
runtimeAPI = _runtimeAPI;
8227
},
83-
register: createLibrary,
8428

8529
getAll: function(req,res) {
86-
storage.getAllFlows().then(function(flows) {
87-
log.audit({event: "library.get.all",type:"flow"},req);
88-
var examples = redNodes.getNodeExampleFlows();
89-
if (examples) {
90-
flows.d = flows.d||{};
91-
flows.d._examples_ = redNodes.getNodeExampleFlows();
92-
}
93-
res.json(flows);
30+
var opts = {
31+
user: req.user,
32+
type: 'flows'
33+
}
34+
runtimeAPI.library.getEntries(opts).then(function(result) {
35+
res.json(result);
36+
}).catch(function(err) {
37+
apiUtils.rejectHandler(req,res,err);
9438
});
9539
},
96-
get: function(req,res) {
97-
if (req.params[0].indexOf("_examples_/") === 0) {
98-
var m = /^_examples_\/(@.*?\/[^\/]+|[^\/]+)\/(.*)$/.exec(req.params[0]);
99-
if (m) {
100-
var module = m[1];
101-
var path = m[2];
102-
var fullPath = redNodes.getNodeExampleFlowPath(module,path);
103-
if (fullPath) {
104-
try {
105-
fs.statSync(fullPath);
106-
log.audit({event: "library.get",type:"flow",path:req.params[0]},req);
107-
return res.sendFile(fullPath,{
108-
headers:{
109-
'Content-Type': 'application/json'
110-
}
111-
})
112-
} catch(err) {
113-
console.log(err);
114-
}
40+
getEntry: function(req,res) {
41+
var opts = {
42+
user: req.user,
43+
type: req.params[0],
44+
path: req.params[1]||""
45+
}
46+
runtimeAPI.library.getEntry(opts).then(function(result) {
47+
if (typeof result === "string") {
48+
if (opts.type === 'flows') {
49+
res.writeHead(200, {'Content-Type': 'application/json'});
50+
} else {
51+
res.writeHead(200, {'Content-Type': 'text/plain'});
11552
}
53+
res.write(result);
54+
res.end();
55+
} else {
56+
res.json(result);
11657
}
117-
// IF we get here, we didn't find the file
118-
log.audit({event: "library.get",type:"flow",path:req.params[0],error:"not_found"},req);
119-
return res.status(404).end();
58+
}).catch(function(err) {
59+
apiUtils.rejectHandler(req,res,err);
60+
});
61+
},
62+
saveEntry: function(req,res) {
63+
var opts = {
64+
user: req.user,
65+
type: req.params[0],
66+
path: req.params[1]||""
67+
}
68+
// TODO: horrible inconsistencies between flows and all other types
69+
if (opts.type === "flows") {
70+
opts.meta = {};
71+
opts.body = JSON.stringify(req.body);
12072
} else {
121-
storage.getFlow(req.params[0]).then(function(data) {
122-
// data is already a JSON string
123-
log.audit({event: "library.get",type:"flow",path:req.params[0]},req);
124-
res.set('Content-Type', 'application/json');
125-
res.send(data);
126-
}).catch(function(err) {
127-
if (err) {
128-
log.warn(log._("api.library.error-load-flow",{path:req.params[0],message:err.toString()}));
129-
if (err.code === 'forbidden') {
130-
log.audit({event: "library.get",type:"flow",path:req.params[0],error:"forbidden"},req);
131-
res.status(403).end();
132-
return;
133-
}
134-
}
135-
log.audit({event: "library.get",type:"flow",path:req.params[0],error:"not_found"},req);
136-
res.status(404).end();
137-
});
73+
opts.meta = req.body;
74+
opts.body = opts.meta.text;
75+
delete opts.meta.text;
13876
}
139-
},
140-
post: function(req,res) {
141-
// if (req.params[0].indexOf("_examples_/") === 0) {
142-
// log.warn(log._("api.library.error-save-flow",{path:req.params[0],message:"forbidden"}));
143-
// log.audit({event: "library.set",type:"flow",path:req.params[0],error:"forbidden"},req);
144-
// return res.status(403).send({error:"unexpected_error", message:"forbidden"});
145-
// }
146-
var flow = JSON.stringify(req.body);
147-
storage.saveFlow(req.params[0],flow).then(function() {
148-
log.audit({event: "library.set",type:"flow",path:req.params[0]},req);
77+
runtimeAPI.library.saveEntry(opts).then(function(result) {
14978
res.status(204).end();
15079
}).catch(function(err) {
151-
log.warn(log._("api.library.error-save-flow",{path:req.params[0],message:err.toString()}));
152-
if (err.code === 'forbidden') {
153-
log.audit({event: "library.set",type:"flow",path:req.params[0],error:"forbidden"},req);
154-
res.status(403).end();
155-
return;
156-
}
157-
log.audit({event: "library.set",type:"flow",path:req.params[0],error:"unexpected_error",message:err.toString()},req);
158-
res.status(500).send({error:"unexpected_error", message:err.toString()});
80+
apiUtils.rejectHandler(req,res,err);
15981
});
16082
}
16183
}

red/runtime-api/flows.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ var api = module.exports = {
223223
*/
224224
getNodeCredentials: function(opts) {
225225
return new Promise(function(resolve,reject) {
226-
log.audit({event: "credentials.get",type:opts.type,id:opts.id});
226+
runtime.log.audit({event: "credentials.get",type:opts.type,id:opts.id});
227227
var credentials = runtime.nodes.getCredentials(opts.id);
228228
if (!credentials) {
229229
return resolve({});

red/runtime-api/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ var api = module.exports = {
3030
api.flows.init(runtime);
3131
api.nodes.init(runtime);
3232
api.settings.init(runtime);
33+
api.library.init(runtime);
3334
},
3435

3536
/**

red/runtime-api/library.js

Lines changed: 94 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,107 @@
1515
**/
1616

1717
/**
18-
* @module red/library
18+
* @namespace RED.library
1919
*/
2020

21-
module.exports = {
21+
var runtime;
22+
23+
var api = module.exports = {
24+
init: function(_runtime) {
25+
runtime = _runtime;
26+
},
2227

2328
/**
24-
* Does something
29+
* Gets an entry from the library.
30+
* @param {Object} opts
31+
* @param {User} opts.user - the user calling the api
32+
* @param {String} opts.type - the type of entry
33+
* @param {String} opts.path - the path of the entry
34+
* @return {Promise<String|Object>} - resolves when complete
35+
* @memberof RED.library
2536
*/
26-
setEnty: function() {},
37+
getEntry: function(opts) {
38+
return new Promise(function(resolve,reject) {
39+
runtime.library.getEntry(opts.type,opts.path).then(function(result) {
40+
runtime.log.audit({event: "library.get",type:opts.type,path:opts.path});
41+
return resolve(result);
42+
}).catch(function(err) {
43+
if (err) {
44+
runtime.log.warn(runtime.log._("api.library.error-load-entry",{path:opts.path,message:err.toString()}));
45+
if (err.code === 'forbidden') {
46+
err.status = 403;
47+
return reject(err);
48+
} else if (err.code === "not_found") {
49+
err.status = 404;
50+
} else {
51+
err.status = 400;
52+
}
53+
runtime.log.audit({event: "library.get",type:opts.type,path:opts.path,error:err.code});
54+
return reject(err);
55+
}
56+
runtime.log.audit({event: "library.get",type:type,error:"not_found"});
57+
var error = new Error();
58+
error.code = "not_found";
59+
error.status = 404;
60+
return reject(error);
61+
});
62+
})
63+
},
64+
2765
/**
28-
* Does something
66+
* Saves an entry to the library
67+
* @param {Object} opts
68+
* @param {User} opts.user - the user calling the api
69+
* @param {String} opts.type - the type of entry
70+
* @param {String} opts.path - the path of the entry
71+
* @param {Object} opts.meta - any meta data associated with the entry
72+
* @param {String} opts.body - the body of the entry
73+
* @return {Promise} - resolves when complete
74+
* @memberof RED.library
2975
*/
30-
getEntry: function() {},
76+
saveEntry: function(opts) {
77+
return new Promise(function(resolve,reject) {
78+
runtime.library.saveEntry(opts.type,opts.path,opts.meta,opts.body).then(function() {
79+
runtime.log.audit({event: "library.set",type:opts.type,path:opts.path});
80+
return resolve();
81+
}).catch(function(err) {
82+
runtime.log.warn(runtime.log._("api.library.error-save-entry",{path:opts.path,message:err.toString()}));
83+
if (err.code === 'forbidden') {
84+
runtime.log.audit({event: "library.set",type:opts.type,path:opts.path,error:"forbidden"});
85+
err.status = 403;
86+
return reject(err);
87+
}
88+
runtime.log.audit({event: "library.set",type:opts.type,path:opts.path,error:"unexpected_error",message:err.toString()});
89+
var error = new Error();
90+
error.code = "not_found";
91+
error.status = 400;
92+
return reject(error);
93+
});
94+
})
95+
},
3196
/**
32-
* Does something
97+
* Returns a complete listing of all entries of a given type in the library.
98+
* @param {Object} opts
99+
* @param {User} opts.user - the user calling the api
100+
* @param {String} opts.type - the type of entry
101+
* @return {Promise<Object>} - the entry listing
102+
* @memberof RED.library
33103
*/
34-
getEntries: function() {}
104+
getEntries: function(opts) {
105+
return new Promise(function(resolve,reject) {
106+
if (opts.type !== 'flows') {
107+
return reject(new Error("API only supports flows"));
108+
109+
}
110+
runtime.storage.getAllFlows().then(function(flows) {
111+
runtime.log.audit({event: "library.get.all",type:"flow"});
112+
var examples = runtime.nodes.getNodeExampleFlows();
113+
if (examples) {
114+
flows.d = flows.d||{};
115+
flows.d._examples_ = examples;
116+
}
117+
return resolve(flows);
118+
});
119+
})
120+
}
35121
}

red/runtime-api/nodes.js

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,27 +14,12 @@
1414
* limitations under the License.
1515
**/
1616
"use strict"
17-
/**
18-
* @namespace RED.nodes
19-
*/
17+
/**
18+
* @namespace RED.nodes
19+
*/
2020

2121
var runtime;
2222

23-
function putNode(node, enabled) {
24-
var promise;
25-
if (!node.err && node.enabled === enabled) {
26-
promise = Promise.resolve(node);
27-
} else {
28-
if (enabled) {
29-
promise = runtime.nodes.enableNode(node.id);
30-
} else {
31-
promise = runtime.nodes.disableNode(node.id);
32-
}
33-
}
34-
return promise;
35-
}
36-
37-
3823
var api = module.exports = {
3924
init: function(_runtime) {
4025
runtime = _runtime;

0 commit comments

Comments
 (0)