Add v2 /flows api and deploy-overwrite protection

This commit is contained in:
Nick O'Leary
2016-10-09 22:02:24 +01:00
parent c60e0d389c
commit b4be1184fd
17 changed files with 876 additions and 81 deletions

View File

@@ -34,12 +34,12 @@ describe("flows api", function() {
app.post("/flows",flows.post);
});
it('returns flow', function(done) {
it('returns flow - v1', function(done) {
flows.init({
settings: {},
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
getFlows: function() { return [1,2,3]; }
getFlows: function() { return {rev:"123",flows:[1,2,3]}; }
}
});
request(app)
@@ -50,13 +50,60 @@ describe("flows api", function() {
if (err) {
return done(err);
}
res.body.should.be.an.Array;
res.body.should.have.lengthOf(3);
done();
try {
res.body.should.have.lengthOf(3);
done();
} catch(e) {
return done(e);
}
});
});
it('sets flows - default', function(done) {
it('returns flow - v2', function(done) {
flows.init({
settings: {},
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
getFlows: function() { return {rev:"123",flows:[1,2,3]}; }
}
});
request(app)
.get('/flows')
.set('Accept', 'application/json')
.set('Node-RED-API-Version','v2')
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
try {
res.body.should.have.a.property('rev','123');
res.body.should.have.a.property('flows');
res.body.flows.should.have.lengthOf(3);
done();
} catch(e) {
return done(e);
}
});
});
it('returns flow - bad version', function(done) {
request(app)
.get('/flows')
.set('Accept', 'application/json')
.set('Node-RED-API-Version','xxx')
.expect(400)
.end(function(err,res) {
if (err) {
return done(err);
}
try {
res.body.should.have.a.property('error','bad_api_version');
done();
} catch(e) {
return done(e);
}
});
});
it('sets flows - default - v1', function(done) {
var setFlows = sinon.spy(function() { return when.resolve();});
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
@@ -77,7 +124,7 @@ describe("flows api", function() {
done();
});
});
it('sets flows - non-default', function(done) {
it('sets flows - non-default - v1', function(done) {
var setFlows = sinon.spy(function() { return when.resolve();});
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
@@ -100,6 +147,96 @@ describe("flows api", function() {
});
});
it('set flows - rejects mismatched revision - v2', function(done) {
var setFlows = sinon.spy(function() { return when.resolve();});
var getFlows = sinon.spy(function() { return {rev:123,flows:[1,2,3]}});
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
setFlows: setFlows,
getFlows: getFlows
}
});
request(app)
.post('/flows')
.set('Accept', 'application/json')
.set('Node-RED-API-Version','v2')
.send({rev:456,flows:[4,5,6]})
.expect(409)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property("error","version_mismatch");
done();
});
});
it('set flows - rev provided - v2', function(done) {
var setFlows = sinon.spy(function() { return when.resolve(456);});
var getFlows = sinon.spy(function() { return {rev:123,flows:[1,2,3]}});
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
setFlows: setFlows,
getFlows: getFlows
}
});
request(app)
.post('/flows')
.set('Accept', 'application/json')
.set('Node-RED-API-Version','v2')
.send({rev:123,flows:[4,5,6]})
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property("rev",456);
done();
});
});
it('set flows - no rev provided - v2', function(done) {
var setFlows = sinon.spy(function() { return when.resolve(456);});
var getFlows = sinon.spy(function() { return {rev:123,flows:[1,2,3]}});
flows.init({
log:{warn:function(){},_:function(){},audit:function(){}},
nodes:{
setFlows: setFlows,
getFlows: getFlows
}
});
request(app)
.post('/flows')
.set('Accept', 'application/json')
.set('Node-RED-API-Version','v2')
.send({flows:[4,5,6]})
.expect(200)
.end(function(err,res) {
if (err) {
return done(err);
}
res.body.should.have.property("rev",456);
done();
});
});
it('sets flow - bad version', function(done) {
request(app)
.post('/flows')
.set('Accept', 'application/json')
.set('Node-RED-API-Version','xxx')
.expect(400)
.end(function(err,res) {
if (err) {
return done(err);
}
try {
res.body.should.have.a.property('error','bad_api_version');
done();
} catch(e) {
return done(e);
}
});
});
it('reloads flows', function(done) {
var loadFlows = sinon.spy(function() { return when.resolve(); });
flows.init({

View File

@@ -116,22 +116,31 @@ describe('flows/index', function() {
flows.setFlows(originalConfig).then(function() {
credentialsClean.called.should.be.true;
storage.hasOwnProperty('conf').should.be.true;
flows.getFlows().should.eql(originalConfig);
flows.getFlows().flows.should.eql(originalConfig);
done();
});
});
it('sets the full flow for type load', function(done) {
it('loads the full flow for type load', function(done) {
var originalConfig = [
{id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]},
{id:"t1",type:"tab"}
];
flows.init({settings:{},storage:storage});
var loadStorage = {
saveFlows: function(conf) {
loadStorage.conf = conf;
return when.resolve(456);
},
getFlows: function() {
return when.resolve({flows:originalConfig,rev:123})
}
}
flows.init({settings:{},storage:loadStorage});
flows.setFlows(originalConfig,"load").then(function() {
credentialsClean.called.should.be.false;
// 'load' type does not trigger a save
storage.hasOwnProperty('conf').should.be.false;
flows.getFlows().should.eql(originalConfig);
loadStorage.hasOwnProperty('conf').should.be.false;
flows.getFlows().flows.should.eql(originalConfig);
done();
});
@@ -147,10 +156,10 @@ describe('flows/index', function() {
credentialsClean.called.should.be.true;
storage.hasOwnProperty('conf').should.be.true;
var cleanedFlows = flows.getFlows();
storage.conf.flows.should.eql(cleanedFlows);
cleanedFlows.should.not.eql(originalConfig);
cleanedFlows[0].credentials = {"a":1};
cleanedFlows.should.eql(originalConfig);
storage.conf.flows.should.eql(cleanedFlows.flows);
cleanedFlows.flows.should.not.eql(originalConfig);
cleanedFlows.flows[0].credentials = {"a":1};
cleanedFlows.flows.should.eql(originalConfig);
done();
});
});
@@ -170,7 +179,7 @@ describe('flows/index', function() {
events.once('nodes-started',function() {
flows.setFlows(newConfig,"nodes").then(function() {
flows.getFlows().should.eql(newConfig);
flows.getFlows().flows.should.eql(newConfig);
flowCreate.flows['t1'].update.called.should.be.true;
flowCreate.flows['t2'].start.called.should.be.true;
flowCreate.flows['_GLOBAL_'].update.called.should.be.true;
@@ -199,7 +208,7 @@ describe('flows/index', function() {
events.once('nodes-started',function() {
flows.setFlows(newConfig,"nodes").then(function() {
flows.getFlows().should.eql(newConfig);
flows.getFlows().flows.should.eql(newConfig);
flowCreate.flows['t1'].update.called.should.be.true;
flowCreate.flows['t2'].start.called.should.be.true;
flowCreate.flows['_GLOBAL_'].update.called.should.be.true;
@@ -229,7 +238,7 @@ describe('flows/index', function() {
credentialsLoad.called.should.be.true;
// 'load' type does not trigger a save
storage.hasOwnProperty('conf').should.be.false;
flows.getFlows().should.eql(originalConfig);
flows.getFlows().flows.should.eql(originalConfig);
done();
});
});
@@ -535,7 +544,7 @@ describe('flows/index', function() {
{id:"t2-3",z:"t1",type:"test"}
]
}).then(function(id) {
flows.getFlows().should.have.lengthOf(6);
flows.getFlows().flows.should.have.lengthOf(6);
var createdFlows = Object.keys(flowCreate.flows);
createdFlows.should.have.lengthOf(3);
createdFlows[2].should.eql(id);

View File

@@ -39,13 +39,14 @@ describe("red/nodes/index", function() {
});
var testFlows = [{"type":"test","id":"tab1","label":"Sheet 1"}];
var testCredentials = {"tab1":{"b":1,"c":2}};
var storage = {
getFlows: function() {
return when({flows:testFlows,credentials:{"tab1":{"b":1,"c":2}}});
return when({red:123,flows:testFlows,credentials:testCredentials});
},
saveFlows: function(conf) {
should.deepEqual(testFlows, conf.flows);
return when();
return when.resolve(123);
}
};
@@ -84,7 +85,9 @@ describe("red/nodes/index", function() {
it('flows should be initialised',function(done) {
index.init(runtime);
index.loadFlows().then(function() {
should.deepEqual(testFlows, index.getFlows());
console.log(testFlows);
console.log(index.getFlows());
should.deepEqual(testFlows, index.getFlows().flows);
done();
}).otherwise(function(err) {
done(err);
@@ -173,8 +176,8 @@ describe("red/nodes/index", function() {
index.registerType('test', TestNode);
index.loadFlows().then(function() {
var info = index.disableNode("5678");
registry.disableNode.calledOnce.should.be.true;
registry.disableNode.calledWith("5678").should.be.true;
registry.disableNode.calledOnce.should.be.true();
registry.disableNode.calledWith("5678").should.be.true();
info.should.eql(randomNodeInfo);
done();
}).otherwise(function(err) {

View File

@@ -79,6 +79,7 @@ describe("red/storage/index", function() {
},
saveFlows : function (flows) {
flows.should.be.true;
return when.resolve("");
},
getCredentials : function() {
calledFlagGetCredentials = true;