mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Validate hook names when they are added
This commit is contained in:
parent
460e1f5563
commit
605177dcf0
@ -1,5 +1,17 @@
|
|||||||
const Log = require("@node-red/util").log;
|
const Log = require("@node-red/util").log;
|
||||||
|
|
||||||
|
const VALID_HOOKS = [
|
||||||
|
// Message Routing Path
|
||||||
|
"onSend",
|
||||||
|
"preRoute",
|
||||||
|
"preDeliver",
|
||||||
|
"postDeliver",
|
||||||
|
"onReceive",
|
||||||
|
"postReceive",
|
||||||
|
"onComplete"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
// Flags for what hooks have handlers registered
|
// Flags for what hooks have handlers registered
|
||||||
let states = { }
|
let states = { }
|
||||||
|
|
||||||
@ -34,6 +46,9 @@ let labelledHooks = { }
|
|||||||
*/
|
*/
|
||||||
function add(hookId, callback) {
|
function add(hookId, callback) {
|
||||||
let [id, label] = hookId.split(".");
|
let [id, label] = hookId.split(".");
|
||||||
|
if (VALID_HOOKS.indexOf(id) === -1) {
|
||||||
|
throw new Error(`Invalid hook '${id}'`);
|
||||||
|
}
|
||||||
if (label) {
|
if (label) {
|
||||||
if (labelledHooks[label] && labelledHooks[label][id]) {
|
if (labelledHooks[label] && labelledHooks[label][id]) {
|
||||||
throw new Error("Hook "+hookId+" already registered")
|
throw new Error("Hook "+hookId+" already registered")
|
||||||
@ -149,8 +164,17 @@ function clear() {
|
|||||||
labelledHooks = {}
|
labelledHooks = {}
|
||||||
states = {}
|
states = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function has(hookId) {
|
||||||
|
let [id, label] = hookId.split(".");
|
||||||
|
if (label) {
|
||||||
|
return !!(labelledHooks[label] && labelledHooks[label][id])
|
||||||
|
}
|
||||||
|
return !!states[id]
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
get states() { return states },
|
has,
|
||||||
clear,
|
clear,
|
||||||
add,
|
add,
|
||||||
remove,
|
remove,
|
||||||
|
@ -9,47 +9,78 @@ describe("runtime/hooks", function() {
|
|||||||
})
|
})
|
||||||
it("allows a hook to be registered", function(done) {
|
it("allows a hook to be registered", function(done) {
|
||||||
let calledWith = null;
|
let calledWith = null;
|
||||||
should.not.exist(hooks.states.foo);
|
hooks.has("onSend").should.be.false();
|
||||||
hooks.add("foo", function(payload) { calledWith = payload } )
|
hooks.add("onSend", function(payload) { calledWith = payload } )
|
||||||
hooks.states.foo.should.be.true();
|
hooks.has("onSend").should.be.true();
|
||||||
let data = { a: 1 };
|
let data = { a: 1 };
|
||||||
hooks.trigger("foo",data,err => {
|
hooks.trigger("onSend",data,err => {
|
||||||
calledWith.should.equal(data);
|
calledWith.should.equal(data);
|
||||||
done(err);
|
done(err);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
it("rejects invalid hook id", function(done) {
|
||||||
|
try {
|
||||||
|
hooks.add("foo", function(payload) {})
|
||||||
|
done(new Error("Invalid hook accepted"))
|
||||||
|
} catch(err) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
})
|
||||||
it("calls hooks in the order they were registered", function(done) {
|
it("calls hooks in the order they were registered", function(done) {
|
||||||
hooks.add("foo", function(payload) { payload.order.push("A") } )
|
hooks.add("onSend", function(payload) { payload.order.push("A") } )
|
||||||
hooks.add("foo", function(payload) { payload.order.push("B") } )
|
hooks.add("onSend", function(payload) { payload.order.push("B") } )
|
||||||
let data = { order:[] };
|
let data = { order:[] };
|
||||||
hooks.trigger("foo",data,err => {
|
hooks.trigger("onSend",data,err => {
|
||||||
data.order.should.eql(["A","B"])
|
data.order.should.eql(["A","B"])
|
||||||
done(err);
|
done(err);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("does not allow multiple hooks with same id.label", function() {
|
it("does not allow multiple hooks with same id.label", function() {
|
||||||
hooks.add("foo.one", function(payload) { payload.order.push("A") } );
|
hooks.has("onSend.one").should.be.false();
|
||||||
|
hooks.has("onSend").should.be.false();
|
||||||
|
hooks.add("onSend.one", function(payload) { payload.order.push("A") } );
|
||||||
|
hooks.has("onSend.one").should.be.true();
|
||||||
|
hooks.has("onSend").should.be.true();
|
||||||
(function() {
|
(function() {
|
||||||
hooks.add("foo.one", function(payload) { payload.order.push("B") } )
|
hooks.add("onSend.one", function(payload) { payload.order.push("B") } )
|
||||||
}).should.throw();
|
}).should.throw();
|
||||||
})
|
})
|
||||||
|
|
||||||
it("removes labelled hook", function(done) {
|
it("removes labelled hook", function(done) {
|
||||||
hooks.add("foo.A", function(payload) { payload.order.push("A") } )
|
hooks.has("onSend.A").should.be.false();
|
||||||
hooks.add("foo.B", function(payload) { payload.order.push("B") } )
|
hooks.has("onSend.B").should.be.false();
|
||||||
|
hooks.has("onSend").should.be.false();
|
||||||
|
|
||||||
|
hooks.add("onSend.A", function(payload) { payload.order.push("A") } )
|
||||||
|
|
||||||
|
hooks.has("onSend.A").should.be.true();
|
||||||
|
hooks.has("onSend.B").should.be.false();
|
||||||
|
hooks.has("onSend").should.be.true();
|
||||||
|
|
||||||
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
||||||
|
|
||||||
|
hooks.has("onSend.A").should.be.true();
|
||||||
|
hooks.has("onSend.B").should.be.true();
|
||||||
|
hooks.has("onSend").should.be.true();
|
||||||
|
|
||||||
|
hooks.remove("onSend.A");
|
||||||
|
|
||||||
|
hooks.has("onSend.A").should.be.false();
|
||||||
|
hooks.has("onSend.B").should.be.true();
|
||||||
|
hooks.has("onSend").should.be.true();
|
||||||
|
|
||||||
hooks.remove("foo.A");
|
|
||||||
hooks.states.foo.should.be.true();
|
|
||||||
|
|
||||||
let data = { order:[] };
|
let data = { order:[] };
|
||||||
hooks.trigger("foo",data,err => {
|
hooks.trigger("onSend",data,err => {
|
||||||
try {
|
try {
|
||||||
data.order.should.eql(["B"])
|
data.order.should.eql(["B"])
|
||||||
|
|
||||||
hooks.remove("foo.B");
|
hooks.remove("onSend.B");
|
||||||
should.not.exist(hooks.states.foo);
|
|
||||||
|
hooks.has("onSend.A").should.be.false();
|
||||||
|
hooks.has("onSend.B").should.be.false();
|
||||||
|
hooks.has("onSend").should.be.false();
|
||||||
|
|
||||||
done(err);
|
done(err);
|
||||||
} catch(err2) {
|
} catch(err2) {
|
||||||
@ -59,30 +90,30 @@ describe("runtime/hooks", function() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("cannot remove unlabelled hook", function() {
|
it("cannot remove unlabelled hook", function() {
|
||||||
hooks.add("foo", function(payload) { payload.order.push("A") } );
|
hooks.add("onSend", function(payload) { payload.order.push("A") } );
|
||||||
(function() {
|
(function() {
|
||||||
hooks.remove("foo")
|
hooks.remove("onSend")
|
||||||
}).should.throw();
|
}).should.throw();
|
||||||
})
|
})
|
||||||
it("removes all hooks with same label", function(done) {
|
it("removes all hooks with same label", function(done) {
|
||||||
hooks.add("foo.A", function(payload) { payload.order.push("A") } )
|
hooks.add("onSend.A", function(payload) { payload.order.push("A") } )
|
||||||
hooks.add("foo.B", function(payload) { payload.order.push("B") } )
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
||||||
hooks.add("bar.A", function(payload) { payload.order.push("C") } )
|
hooks.add("preRoute.A", function(payload) { payload.order.push("C") } )
|
||||||
hooks.add("bar.B", function(payload) { payload.order.push("D") } )
|
hooks.add("preRoute.B", function(payload) { payload.order.push("D") } )
|
||||||
|
|
||||||
let data = { order:[] };
|
let data = { order:[] };
|
||||||
hooks.trigger("foo",data,err => {
|
hooks.trigger("onSend",data,err => {
|
||||||
data.order.should.eql(["A","B"])
|
data.order.should.eql(["A","B"])
|
||||||
hooks.trigger("bar", data, err => {
|
hooks.trigger("preRoute", data, err => {
|
||||||
data.order.should.eql(["A","B","C","D"])
|
data.order.should.eql(["A","B","C","D"])
|
||||||
|
|
||||||
data.order = [];
|
data.order = [];
|
||||||
|
|
||||||
hooks.remove("*.A");
|
hooks.remove("*.A");
|
||||||
|
|
||||||
hooks.trigger("foo",data,err => {
|
hooks.trigger("onSend",data,err => {
|
||||||
data.order.should.eql(["B"])
|
data.order.should.eql(["B"])
|
||||||
hooks.trigger("bar", data, err => {
|
hooks.trigger("preRoute", data, err => {
|
||||||
data.order.should.eql(["B","D"])
|
data.order.should.eql(["B","D"])
|
||||||
})
|
})
|
||||||
done(err);
|
done(err);
|
||||||
@ -93,22 +124,22 @@ describe("runtime/hooks", function() {
|
|||||||
|
|
||||||
|
|
||||||
it("halts execution on return false", function(done) {
|
it("halts execution on return false", function(done) {
|
||||||
hooks.add("foo.A", function(payload) { payload.order.push("A"); return false } )
|
hooks.add("onSend.A", function(payload) { payload.order.push("A"); return false } )
|
||||||
hooks.add("foo.B", function(payload) { payload.order.push("B") } )
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
||||||
|
|
||||||
let data = { order:[] };
|
let data = { order:[] };
|
||||||
hooks.trigger("foo",data,err => {
|
hooks.trigger("onSend",data,err => {
|
||||||
data.order.should.eql(["A"])
|
data.order.should.eql(["A"])
|
||||||
err.should.be.false();
|
err.should.be.false();
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it("halts execution on thrown error", function(done) {
|
it("halts execution on thrown error", function(done) {
|
||||||
hooks.add("foo.A", function(payload) { payload.order.push("A"); throw new Error("error") } )
|
hooks.add("onSend.A", function(payload) { payload.order.push("A"); throw new Error("error") } )
|
||||||
hooks.add("foo.B", function(payload) { payload.order.push("B") } )
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
||||||
|
|
||||||
let data = { order:[] };
|
let data = { order:[] };
|
||||||
hooks.trigger("foo",data,err => {
|
hooks.trigger("onSend",data,err => {
|
||||||
data.order.should.eql(["A"])
|
data.order.should.eql(["A"])
|
||||||
should.exist(err);
|
should.exist(err);
|
||||||
err.should.not.be.false()
|
err.should.not.be.false()
|
||||||
@ -117,47 +148,47 @@ describe("runtime/hooks", function() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("handler can use callback function", function(done) {
|
it("handler can use callback function", function(done) {
|
||||||
hooks.add("foo.A", function(payload, done) {
|
hooks.add("onSend.A", function(payload, done) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
payload.order.push("A")
|
payload.order.push("A")
|
||||||
done()
|
done()
|
||||||
},30)
|
},30)
|
||||||
})
|
})
|
||||||
hooks.add("foo.B", function(payload) { payload.order.push("B") } )
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
||||||
|
|
||||||
let data = { order:[] };
|
let data = { order:[] };
|
||||||
hooks.trigger("foo",data,err => {
|
hooks.trigger("onSend",data,err => {
|
||||||
data.order.should.eql(["A","B"])
|
data.order.should.eql(["A","B"])
|
||||||
done(err);
|
done(err);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("handler can use callback function - halt execution", function(done) {
|
it("handler can use callback function - halt execution", function(done) {
|
||||||
hooks.add("foo.A", function(payload, done) {
|
hooks.add("onSend.A", function(payload, done) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
payload.order.push("A")
|
payload.order.push("A")
|
||||||
done(false)
|
done(false)
|
||||||
},30)
|
},30)
|
||||||
})
|
})
|
||||||
hooks.add("foo.B", function(payload) { payload.order.push("B") } )
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
||||||
|
|
||||||
let data = { order:[] };
|
let data = { order:[] };
|
||||||
hooks.trigger("foo",data,err => {
|
hooks.trigger("onSend",data,err => {
|
||||||
data.order.should.eql(["A"])
|
data.order.should.eql(["A"])
|
||||||
err.should.be.false()
|
err.should.be.false()
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it("handler can use callback function - halt on error", function(done) {
|
it("handler can use callback function - halt on error", function(done) {
|
||||||
hooks.add("foo.A", function(payload, done) {
|
hooks.add("onSend.A", function(payload, done) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
done(new Error("test error"))
|
done(new Error("test error"))
|
||||||
},30)
|
},30)
|
||||||
})
|
})
|
||||||
hooks.add("foo.B", function(payload) { payload.order.push("B") } )
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
||||||
|
|
||||||
let data = { order:[] };
|
let data = { order:[] };
|
||||||
hooks.trigger("foo",data,err => {
|
hooks.trigger("onSend",data,err => {
|
||||||
data.order.should.eql([])
|
data.order.should.eql([])
|
||||||
should.exist(err);
|
should.exist(err);
|
||||||
err.should.not.be.false()
|
err.should.not.be.false()
|
||||||
@ -166,7 +197,7 @@ describe("runtime/hooks", function() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("handler be an async function", function(done) {
|
it("handler be an async function", function(done) {
|
||||||
hooks.add("foo.A", async function(payload) {
|
hooks.add("onSend.A", async function(payload) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
payload.order.push("A")
|
payload.order.push("A")
|
||||||
@ -174,17 +205,17 @@ describe("runtime/hooks", function() {
|
|||||||
},30)
|
},30)
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
hooks.add("foo.B", function(payload) { payload.order.push("B") } )
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
||||||
|
|
||||||
let data = { order:[] };
|
let data = { order:[] };
|
||||||
hooks.trigger("foo",data,err => {
|
hooks.trigger("onSend",data,err => {
|
||||||
data.order.should.eql(["A","B"])
|
data.order.should.eql(["A","B"])
|
||||||
done(err);
|
done(err);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("handler be an async function - halt execution", function(done) {
|
it("handler be an async function - halt execution", function(done) {
|
||||||
hooks.add("foo.A", async function(payload) {
|
hooks.add("onSend.A", async function(payload) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
payload.order.push("A")
|
payload.order.push("A")
|
||||||
@ -192,26 +223,26 @@ describe("runtime/hooks", function() {
|
|||||||
},30)
|
},30)
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
hooks.add("foo.B", function(payload) { payload.order.push("B") } )
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
||||||
|
|
||||||
let data = { order:[] };
|
let data = { order:[] };
|
||||||
hooks.trigger("foo",data,err => {
|
hooks.trigger("onSend",data,err => {
|
||||||
data.order.should.eql(["A"])
|
data.order.should.eql(["A"])
|
||||||
done(err);
|
done(err);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it("handler be an async function - halt on error", function(done) {
|
it("handler be an async function - halt on error", function(done) {
|
||||||
hooks.add("foo.A", async function(payload) {
|
hooks.add("onSend.A", async function(payload) {
|
||||||
return new Promise((resolve,reject) => {
|
return new Promise((resolve,reject) => {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
reject(new Error("test error"))
|
reject(new Error("test error"))
|
||||||
},30)
|
},30)
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
hooks.add("foo.B", function(payload) { payload.order.push("B") } )
|
hooks.add("onSend.B", function(payload) { payload.order.push("B") } )
|
||||||
|
|
||||||
let data = { order:[] };
|
let data = { order:[] };
|
||||||
hooks.trigger("foo",data,err => {
|
hooks.trigger("onSend",data,err => {
|
||||||
data.order.should.eql([])
|
data.order.should.eql([])
|
||||||
should.exist(err);
|
should.exist(err);
|
||||||
err.should.not.be.false()
|
err.should.not.be.false()
|
||||||
|
Loading…
Reference in New Issue
Block a user