diff --git a/lib/eventType.js b/lib/eventType.js index 1dc8897ea6..029516a712 100644 --- a/lib/eventType.js +++ b/lib/eventType.js @@ -1,5 +1,6 @@ module.exports = { featureCreated : 'feature-created', featureUpdated : 'feature-updated', - strategyCreated: 'strategy-created' + strategyCreated: 'strategy-created', + strategyDeleted: 'strategy-deleted' }; \ No newline at end of file diff --git a/lib/strategyApi.js b/lib/strategyApi.js index ec46c0f411..facc307ebf 100644 --- a/lib/strategyApi.js +++ b/lib/strategyApi.js @@ -16,6 +16,18 @@ module.exports = function (app) { .catch(function () { res.json(404, {error: 'Could not find strategy'}); }); }); + app.delete('/strategies/:name', function (req, res) { + eventStore.create({ + type: eventType.strategyDeleted, + createdBy: req.connection.remoteAddress, + data: { + name: req.params.name + } + }) + .then(function () { res.status(200).end(); }) + .catch(function () { res.status(500).end(); }); + }); + app.post('/strategies', function (req, res) { req.checkBody('name', 'Name is required').notEmpty(); req.checkBody('name', 'Name must match format ^[a-zA-Z\\.\\-]+$').matches(/^[a-zA-Z\\.\\-]+$/i); diff --git a/lib/strategyDb.js b/lib/strategyDb.js index b8f9f3bcc1..9c23053f5c 100644 --- a/lib/strategyDb.js +++ b/lib/strategyDb.js @@ -12,6 +12,15 @@ eventStore.on(eventType.strategyCreated, function (event) { }); }); +eventStore.on(eventType.strategyDeleted, function (event) { + knex('strategies') + .where('name', event.data.name) + .del() + .catch(function (err) { + logger.error('Could not delete strategy, error was: ', err); + }); +}); + function getStrategies() { return knex .select(STRATEGY_COLUMNS) diff --git a/public/js/__tests__/Menu-test.js b/public/js/__tests__/components/Menu-test.js similarity index 80% rename from public/js/__tests__/Menu-test.js rename to public/js/__tests__/components/Menu-test.js index deffe6b9c4..59f8024856 100644 --- a/public/js/__tests__/Menu-test.js +++ b/public/js/__tests__/components/Menu-test.js @@ -1,8 +1,8 @@ /** @jsx React.DOM */ -jest.dontMock("../components/Menu"); +jest.dontMock("../../components/Menu"); -var Menu = require("../components/Menu"); +var Menu = require("../../components/Menu"); var React = require("react/addons"); var TestUtils = React.addons.TestUtils; diff --git a/public/js/__tests__/components/feature/FeatureForm-test.js b/public/js/__tests__/components/feature/FeatureForm-test.js new file mode 100644 index 0000000000..d42a0c9639 --- /dev/null +++ b/public/js/__tests__/components/feature/FeatureForm-test.js @@ -0,0 +1,47 @@ +jest.dontMock("../../../components/feature/FeatureForm"); + +var React = require("react/addons"); +var TestUtils = React.addons.TestUtils; +var FeatureForm = require("../../../components/feature/FeatureForm"); +var strategyStore = require("../../../stores/StrategyStore"); + +describe("FeatureForm", function () { + var Component; + beforeEach(function() { + strategyStore.getStrategies.mockImplementation(function() { + return { + then: function (callback) { + return callback({ + strategies: [ + { name: "default"} + ] + }); + } + }; + }); + }); + + afterEach(function() { + React.unmountComponentAtNode(document.body); + }); + + describe("new", function () { + it("should render empty form", function() { + Component = TestUtils .renderIntoDocument(); + var name = Component.getDOMNode().querySelectorAll("input"); + expect(name[0].value).toEqual(undefined); + }); + }); + + describe("edit", function () { + var feature = {name: "Test", strategy: "unknown"}; + + it("should show unknown strategy as deleted", function () { + Component = TestUtils .renderIntoDocument(); + + var strategySelect = Component.getDOMNode().querySelector("select"); + expect(strategySelect.value).toEqual("unknown (deleted)"); + }); + }); + +}); \ No newline at end of file diff --git a/public/js/components/feature/FeatureForm.jsx b/public/js/components/feature/FeatureForm.jsx index 17998506d4..26cb524186 100644 --- a/public/js/components/feature/FeatureForm.jsx +++ b/public/js/components/feature/FeatureForm.jsx @@ -1,5 +1,5 @@ var React = require('react'); -var TextInput = require('../form/TextInput'); +var TextInput = require('../form/TextInput'); var strategyStore = require('../../stores/StrategyStore'); var FeatureForm = React.createClass({ @@ -36,20 +36,30 @@ var FeatureForm = React.createClass({ }, setSelectedStrategy: function(name) { - var selected = this.state.strategyOptions.filter(function(strategy) { + var selectedStrategy = this.state.strategyOptions.filter(function(strategy) { return strategy.name === name; - }); + })[0]; + if(selectedStrategy) { + if(selectedStrategy.parametersTemplate) { + this.setStrategyParams(selectedStrategy); + } + } else { + var updatedStrategyName = name + " (deleted)"; + this.setState({ + currentStrategy: updatedStrategyName, + strategyOptions: this.state.strategyOptions.concat([{name: updatedStrategyName}]) + }); + } + }, + + setStrategyParams: function(strategy) { var requiredParams = []; var key; - - if(selected[0] && selected[0].parametersTemplate) { - for(key in selected[0].parametersTemplate) { - requiredParams.push({name: key, value: this.getParameterValue(key)}); - } + for(key in strategy.parametersTemplate) { + requiredParams.push({name: key, value: this.getParameterValue(key)}); } this.setState({requiredParams: requiredParams}); - }, render: function() { diff --git a/public/js/components/strategy/StrategiesComponent.jsx b/public/js/components/strategy/StrategiesComponent.jsx index 9fc29a97f0..0df00d17de 100644 --- a/public/js/components/strategy/StrategiesComponent.jsx +++ b/public/js/components/strategy/StrategiesComponent.jsx @@ -14,9 +14,16 @@ var StrategiesComponent = React.createClass({ }, componentDidMount: function () { - strategyStore.getStrategies().then(function(res) { - this.setState({strategies: res.strategies}); - }.bind(this), this.initError); + this.fetchStrategies(); + }, + + fetchStrategies: function(res) { + strategyStore.getStrategies() + .then(function(res) { + this.setState({strategies: res.strategies}) + }.bind(this)) + .catch(this.initError); + }, initError: function() { @@ -57,6 +64,12 @@ var StrategiesComponent = React.createClass({ .catch(this.onError); }, + onRemove: function(strategy) { + strategyStore.removeStrategy(strategy) + .then(this.fetchStrategies) + .catch(this.onError); + }, + render: function() { return (
@@ -66,7 +79,7 @@ var StrategiesComponent = React.createClass({
- +
); }, diff --git a/public/js/components/strategy/Strategy.jsx b/public/js/components/strategy/Strategy.jsx index 096a2f099c..b8c383b2e6 100644 --- a/public/js/components/strategy/Strategy.jsx +++ b/public/js/components/strategy/Strategy.jsx @@ -5,12 +5,20 @@ var Strategy = React.createClass({ strategy: React.PropTypes.object.isRequired }, + onRemove: function(event) { + event.preventDefault(); + if (confirm("Are you sure you want to delete strategy '"+this.props.strategy.name+"'?")) { + this.props.onRemove(this.props.strategy); + } + }, + render: function() { return (
- {this.props.strategy.name}
- {this.props.strategy.description} + {this.props.strategy.name} + (remove)
+ {this.props.strategy.description}
); diff --git a/public/js/components/strategy/StrategyList.jsx b/public/js/components/strategy/StrategyList.jsx index 261efbe88e..bf148e26fb 100644 --- a/public/js/components/strategy/StrategyList.jsx +++ b/public/js/components/strategy/StrategyList.jsx @@ -8,8 +8,8 @@ var StrategyList = React.createClass({ render: function() { var strategyNodes = this.props.strategies.map(function(strategy) { - return ; - }); + return ; + }.bind(this)); return (
{strategyNodes}
); diff --git a/public/js/stores/StrategyStore.js b/public/js/stores/StrategyStore.js index 80674d9e38..09388e6e34 100644 --- a/public/js/stores/StrategyStore.js +++ b/public/js/stores/StrategyStore.js @@ -14,6 +14,14 @@ var StrategyStore = { }); }, + removeStrategy: function (strategy) { + return reqwest({ + url: 'strategies/'+strategy.name, + method: 'delete', + type: TYPE + }); + }, + getStrategies: function () { return reqwest({ url: 'strategies', diff --git a/test/strategyApiSpec.js b/test/strategyApiSpec.js index 5da09317ac..78f9cba6aa 100644 --- a/test/strategyApiSpec.js +++ b/test/strategyApiSpec.js @@ -44,5 +44,9 @@ describe('The strategy api', function () { .expect(403, done); }); - + it('deletes a new strategy', function (done) { + request + .delete('/strategies/deletable') + .expect(200, done); + }); }); \ No newline at end of file diff --git a/test/strategyDbMock.js b/test/strategyDbMock.js index ebc1400c64..148e20620e 100644 --- a/test/strategyDbMock.js +++ b/test/strategyDbMock.js @@ -11,6 +11,10 @@ var strategies = [ parametersTemplate: { emails: "String" } + }, + { + name: "deletable", + description: "deletable" } ];