1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-05-31 01:16:01 +02:00

Merge pull request #52 from finn-no/strategy-api

Strategy api v2
This commit is contained in:
Jari Bakken 2014-11-27 12:49:39 +01:00
commit ccaef73b82
15 changed files with 207 additions and 161 deletions

View File

@ -1,6 +1,6 @@
var eventStore = require('./eventStore'),
eventType = require('./eventType'),
featureDb = require('./featureDb');
var eventStore = require('./eventStore');
var eventType = require('./eventType');
var featureDb = require('./featureDb');
module.exports = function (app) {
@ -44,11 +44,9 @@ module.exports = function (app) {
type: eventType.featureCreated,
createdBy: req.connection.remoteAddress,
data: newFeature
}).then(function () {
res.status(201).end();
}, function () {
res.status(500).end();
});
})
.then(function () { res.status(201).end(); })
.catch(function () { res.status(500).end(); });
};
featureDb.getFeature(newFeature.name)

View File

@ -1,25 +1,7 @@
var eventStore = require('./eventStore');
var eventType = require('./eventType');
var strategyDb = require('./strategyDb');
var strategies = [
{
name: "default",
description: "Default on or off Strategy."
},
{
name: "usersWithEmail",
description: "Active for users defined in the comma-separated emails-parameter.",
parametersTemplate: {
emails: "String"
}
}
];
function byName(name) {
return strategies.filter(function(s) {
return s.name === name;
})[0];
}
module.exports = function (app) {
app.get('/strategies', function (req, res) {
@ -29,16 +11,42 @@ module.exports = function (app) {
});
app.get('/strategies/:name', function (req, res) {
var strategy = byName(req.params.name);
if (strategy) {
res.json(strategy);
} else {
res.json(404, {error: 'Could not find strategy'});
}
strategyDb.getStrategy(req.params.name)
.then(function (strategy) { res.json(strategy); })
.catch(function () { res.json(404, {error: 'Could not find strategy'}); });
});
app.post('/strategies', function (req, res) {
res.json(500, {error: 'Not implemented yet'});
req.checkBody('name', 'Name is required').notEmpty();
req.checkBody('name', 'Name must match format ^[a-zA-Z\\.\\-]+$').matches(/^[a-zA-Z\\.\\-]+$/i);
var errors = req.validationErrors();
if (errors) {
res.status(400).json(errors);
return;
}
var newStrategy = req.body;
var handleStrategyExists = function() {
var errors = [{msg: "A strategy named " + newStrategy.name + " already exists."}];
res.status(403).json(errors);
};
var handleCreateStrategy = function() {
eventStore.create({
type: eventType.strategyCreated,
createdBy: req.connection.remoteAddress,
data: newStrategy
})
.then(function () { res.status(201).end(); })
.catch(function () { res.status(500).end(); });
};
strategyDb.getStrategy(newStrategy.name)
.then(handleStrategyExists)
.catch(handleCreateStrategy);
});
};

View File

@ -71,7 +71,7 @@ var FeatureForm = React.createClass({
},
renderStrategyOptions: function() {
var currentStrategy = this.props.feature ? this.props.feature.strategy : "";
var currentStrategy = this.props.feature ? this.props.feature.strategy : "default";
return this.state.strategyOptions.map(function(name) {
return (

View File

@ -41,12 +41,20 @@ var StrategiesComponent = React.createClass({
},
onSave: function(strategy) {
var strategies = this.state.strategies.concat([strategy]);
this.setState({
createView: false,
strategies: strategies
});
console.log("Saved strategy: ", strategy);
function handleSuccess() {
var strategies = this.state.strategies.concat([strategy]);
this.setState({
createView: false,
strategies: strategies
});
console.log("Saved strategy: ", strategy);
}
strategyStore.createStrategy(strategy)
.then(handleSuccess.bind(this))
.catch(this.onError);
},
render: function() {

View File

@ -1,7 +1,7 @@
var reqwest = require('reqwest');
TYPE = 'json';
CONTENT_TYPE = 'application/json';
var TYPE = 'json';
var CONTENT_TYPE = 'application/json';
var EventStore = {
getEvents: function () {

View File

@ -3,16 +3,16 @@ var reqwest = require('reqwest');
var FeatureStore = function () {
};
FeatureStore.TYPE = 'json';
FeatureStore.CONTENT_TYPE = 'application/json';
var TYPE = 'json';
var CONTENT_TYPE = 'application/json';
FeatureStore.prototype = {
updateFeature: function (feature) {
return reqwest({
url: 'features/' + feature.name,
method: 'put',
type: FeatureStore.TYPE,
contentType: FeatureStore.CONTENT_TYPE,
type: TYPE,
contentType: CONTENT_TYPE,
data: JSON.stringify(feature)
});
},
@ -21,8 +21,8 @@ FeatureStore.prototype = {
return reqwest({
url: 'features',
method: 'post',
type: FeatureStore.TYPE,
contentType: FeatureStore.CONTENT_TYPE,
type: TYPE,
contentType: CONTENT_TYPE,
data: JSON.stringify(feature)
});
},
@ -31,7 +31,7 @@ FeatureStore.prototype = {
return reqwest({
url: 'features',
method: 'get',
type: FeatureStore.TYPE
type: TYPE
});
}
};

View File

@ -1,7 +1,7 @@
var reqwest = require('reqwest');
TYPE = 'json';
CONTENT_TYPE = 'application/json';
var TYPE = 'json';
var CONTENT_TYPE = 'application/json';
var StrategyStore = {
createStrategy: function (strategy) {

View File

@ -1,42 +0,0 @@
var request = require('supertest'),
mockery = require('mockery');
describe('The event api', function () {
var server;
before(function () {
mockery.enable({
warnOnReplace: false,
warnOnUnregistered: false,
useCleanCache: true
});
mockery.registerSubstitute('./eventDb', '../test/eventDbMock');
mockery.registerSubstitute('./featureDb', '../test/featureDbMock');
mockery.registerSubstitute('./strategyDb', '../test/strategyDbMock');
server = require('../server');
request = request('http://localhost:' + server.app.get('port'));
});
after(function () {
mockery.disable();
mockery.deregisterAll();
server.server.close();
});
it('returns events', function (done) {
request
.get('/events')
.expect('Content-Type', /json/)
.expect(200, done);
});
it('returns events given a name', function (done) {
request
.get('/events/myname')
.expect('Content-Type', /json/)
.expect(200, done);
});
});

23
test/eventApiSpec.js Normal file
View File

@ -0,0 +1,23 @@
var specHelper = require('./specHelper');
describe('The event api', function () {
var request;
before(function () { request = specHelper.setupMockServer(); });
after(specHelper.tearDownMockServer);
it('returns events', function (done) {
request
.get('/events')
.expect('Content-Type', /json/)
.expect(200, done);
});
it('returns events given a name', function (done) {
request
.get('/events/myname')
.expect('Content-Type', /json/)
.expect(200, done);
});
});

View File

@ -1,29 +1,10 @@
var request = require('supertest');
var mockery = require('mockery');
var specHelper = require('./specHelper');
describe('The api', function () {
var server;
describe('The features api', function () {
var request;
before(function () {
mockery.enable({
warnOnReplace: false,
warnOnUnregistered: false,
useCleanCache: true
});
mockery.registerSubstitute('./eventDb', '../test/eventDbMock');
mockery.registerSubstitute('./featureDb', '../test/featureDbMock');
mockery.registerSubstitute('./strategyDb', '../test/strategyDbMock');
server = require('../server');
request = request('http://localhost:' + server.app.get('port'));
});
after(function () {
mockery.disable();
mockery.deregisterAll();
server.server.close();
});
before(function () { request = specHelper.setupMockServer(); });
after(specHelper.tearDownMockServer);
it('returns three mocked feature toggles', function (done) {
request
@ -32,6 +13,13 @@ describe('The api', function () {
.expect(200, done);
});
it('gets a feature by name', function (done) {
request
.get('/features/featureX')
.expect('Content-Type', /json/)
.expect(200, done);
});
it('creates new feature toggle', function (done) {
request
.post('/features')

18
test/routerSpec.js Normal file
View File

@ -0,0 +1,18 @@
var specHelper = require('./specHelper');
describe('The routes', function () {
var request;
before(function () { request = specHelper.setupMockServer(); });
after(specHelper.tearDownMockServer);
describe('healthcheck', function () {
it('returns health good', function (done) {
request.get('/health')
.expect('Content-Type', /json/)
.expect(200)
.expect('{"health":"GOOD"}', done);
});
});
});

View File

@ -1,38 +0,0 @@
var request = require('supertest'),
mockery = require('mockery');
describe('The routes', function () {
var server;
before(function () {
mockery.enable({
warnOnReplace: false,
warnOnUnregistered: false,
useCleanCache: true
});
mockery.registerSubstitute('./eventDb', '../test/eventDbMock');
mockery.registerSubstitute('./featureDb', '../test/featureDbMock');
mockery.registerSubstitute('./strategyDb', '../test/strategyDbMock');
server = require('../server');
request = request('http://localhost:' + server.app.get('port'));
});
after(function () {
mockery.disable();
mockery.deregisterAll();
server.server.close();
});
describe('healthcheck', function () {
it('returns health good', function (done) {
request.get('/health')
.expect('Content-Type', /json/)
.expect(200)
.expect('{"health":"GOOD"}', done);
});
});
});

35
test/specHelper.js Normal file
View File

@ -0,0 +1,35 @@
var request = require('supertest');
var mockery = require('mockery');
var server;
function setupMockServer() {
mockery.enable({
warnOnReplace: false,
warnOnUnregistered: false,
useCleanCache: true
});
mockery.registerSubstitute('./eventDb', '../test/eventDbMock');
mockery.registerSubstitute('./featureDb', '../test/featureDbMock');
mockery.registerSubstitute('./strategyDb', '../test/strategyDbMock');
server = require('../server');
return request('http://localhost:' + server.app.get('port'));
}
function tearDownMockServer() {
mockery.disable();
mockery.deregisterAll();
if (server) {
server.server.close();
server = null;
}
}
module.exports = {
setupMockServer: setupMockServer,
tearDownMockServer: tearDownMockServer
};

48
test/strategyApiSpec.js Normal file
View File

@ -0,0 +1,48 @@
var specHelper = require('./specHelper');
describe('The strategy api', function () {
var request;
before(function () { request = specHelper.setupMockServer(); });
after(specHelper.tearDownMockServer);
it('gets all strategies', function (done) {
request
.get('/strategies')
.expect('Content-Type', /json/)
.expect(200, done);
});
it('gets a strategy by name', function (done) {
request
.get('/strategies/default')
.expect('Content-Type', /json/)
.expect(200, done);
});
it('creates a new strategy', function (done) {
request
.post('/strategies')
.send({name: 'myCustomStrategy', description: 'Best strategy ever.'})
.set('Content-Type', 'application/json')
.expect(201, done);
});
it('requires new strategies to have a name', function (done) {
request
.post('/strategies')
.send({name: ''})
.set('Content-Type', 'application/json')
.expect(400, done);
});
it('refuses to create a strategy with an existing name', function (done) {
request
.post('/strategies')
.send({name: 'default'})
.set('Content-Type', 'application/json')
.expect(403, done);
});
});

View File

@ -26,10 +26,10 @@ module.exports = {
resolve(strategies);
});
},
getFeature: function(name) {
var feature = byName(name);
if(feature) {
return Promise.resolve(feature);
getStrategy: function(name) {
var strategy = byName(name);
if(strategy) {
return Promise.resolve(strategy);
} else {
return Promise.reject("strategy not found");
}