From a169ec13524560376718d5c43c5b9cace311c941 Mon Sep 17 00:00:00 2001 From: Jari Bakken Date: Tue, 25 Nov 2014 15:28:08 +0100 Subject: [PATCH] Add ability to create custom stratgies. Closes #11. --- lib/featureApi.js | 14 ++--- lib/strategyApi.js | 62 +++++++++++-------- .../strategy/StrategiesComponent.jsx | 20 ++++-- public/js/stores/EventStore.js | 4 +- public/js/stores/FeatureStore.js | 14 ++--- public/js/stores/StrategyStore.js | 4 +- test/featureApiSpec.js | 7 +++ test/strategyApiSpec.js | 40 ++++++++++++ test/strategyDbMock.js | 8 +-- 9 files changed, 117 insertions(+), 56 deletions(-) create mode 100644 test/strategyApiSpec.js diff --git a/lib/featureApi.js b/lib/featureApi.js index 3f511d423d..a9a7a10313 100644 --- a/lib/featureApi.js +++ b/lib/featureApi.js @@ -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) diff --git a/lib/strategyApi.js b/lib/strategyApi.js index 19413f1236..ec46c0f411 100644 --- a/lib/strategyApi.js +++ b/lib/strategyApi.js @@ -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); }); }; diff --git a/public/js/components/strategy/StrategiesComponent.jsx b/public/js/components/strategy/StrategiesComponent.jsx index 092f5bdd5e..9fc29a97f0 100644 --- a/public/js/components/strategy/StrategiesComponent.jsx +++ b/public/js/components/strategy/StrategiesComponent.jsx @@ -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() { diff --git a/public/js/stores/EventStore.js b/public/js/stores/EventStore.js index a26ffe135f..696fb66c22 100644 --- a/public/js/stores/EventStore.js +++ b/public/js/stores/EventStore.js @@ -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 () { diff --git a/public/js/stores/FeatureStore.js b/public/js/stores/FeatureStore.js index 7dc52c5d5a..b70dbd2932 100644 --- a/public/js/stores/FeatureStore.js +++ b/public/js/stores/FeatureStore.js @@ -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 }); } }; diff --git a/public/js/stores/StrategyStore.js b/public/js/stores/StrategyStore.js index e28febca7c..80674d9e38 100644 --- a/public/js/stores/StrategyStore.js +++ b/public/js/stores/StrategyStore.js @@ -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) { diff --git a/test/featureApiSpec.js b/test/featureApiSpec.js index 60c210b12d..50a6cd2895 100644 --- a/test/featureApiSpec.js +++ b/test/featureApiSpec.js @@ -13,6 +13,13 @@ describe('The features 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') diff --git a/test/strategyApiSpec.js b/test/strategyApiSpec.js new file mode 100644 index 0000000000..d11c1e0200 --- /dev/null +++ b/test/strategyApiSpec.js @@ -0,0 +1,40 @@ +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); + }); + + +}); \ No newline at end of file diff --git a/test/strategyDbMock.js b/test/strategyDbMock.js index e518d51c08..ebc1400c64 100644 --- a/test/strategyDbMock.js +++ b/test/strategyDbMock.js @@ -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"); }