From 827faba4386026acaa37c36f1883380db77bf10d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivar=20Conradi=20=C3=98sthus?= Date: Tue, 3 Mar 2015 19:27:17 +0100 Subject: [PATCH] Renamed FeatureToggleStore and simplified action names. --- .../feature/ArchiveFeatureComponent-test.js | 48 +++---- .../feature/ArchiveFeatureComponent.jsx | 109 ++++++++-------- .../feature/FeatureTogglesComponent.jsx | 79 ++++-------- public/js/stores/FeatureActions.js | 4 - public/js/stores/FeatureStore.js | 62 --------- public/js/stores/FeatureStore2.js | 69 ---------- public/js/stores/FeatureToggleActions.js | 7 + public/js/stores/FeatureToggleServerFacade.js | 98 ++++++++++++++ public/js/stores/FeatureToggleStore.js | 122 ++++++++++++++++++ 9 files changed, 330 insertions(+), 268 deletions(-) delete mode 100644 public/js/stores/FeatureActions.js delete mode 100644 public/js/stores/FeatureStore.js delete mode 100644 public/js/stores/FeatureStore2.js create mode 100644 public/js/stores/FeatureToggleActions.js create mode 100644 public/js/stores/FeatureToggleServerFacade.js create mode 100644 public/js/stores/FeatureToggleStore.js diff --git a/public/js/__tests__/components/feature/ArchiveFeatureComponent-test.js b/public/js/__tests__/components/feature/ArchiveFeatureComponent-test.js index 9391e19fae..a461da3c43 100644 --- a/public/js/__tests__/components/feature/ArchiveFeatureComponent-test.js +++ b/public/js/__tests__/components/feature/ArchiveFeatureComponent-test.js @@ -1,32 +1,28 @@ jest.dontMock("../../../components/feature/ArchiveFeatureComponent"); +jest.mock("../../../stores/FeatureToggleServerFacade"); +jest.autoMockOff(); var React = require("react/addons"); var TestUtils = React.addons.TestUtils; -var FeatureArchive = require("../../../components/feature/ArchiveFeatureComponent"); -var FeatureStore = require("../../../stores/FeatureStore"); +var FeatureArchive = require("../../../components/feature/ArchiveFeatureComponent"); +var Server = require("../../../stores/FeatureToggleServerFacade"); +var FeatureToggleStore = require("../../../stores/FeatureToggleStore"); describe("FeatureForm", function () { var Component; beforeEach(function() { - FeatureStore.getArchivedFeatures.mockImplementation(function() { - return { - then: function (callback) { - return callback({ - features: [ - { name: "featureX" }, - { name: "featureY" } - ] - }); - } - }; - }); - FeatureStore.reviveFeature.mockImplementation(function() { - return { - then: function (callback) {return callback();} - }; + var archivedToggles = [ + { name: "featureX" }, + { name: "featureY" } + ]; + + Server.getArchivedFeatures.mockImplementation(function(cb) { + cb(archivedToggles); }); - Component = TestUtils .renderIntoDocument(); + FeatureToggleStore.initStore([], archivedToggles); + + Component = TestUtils.renderIntoDocument(); }); afterEach(function() { @@ -35,17 +31,15 @@ describe("FeatureForm", function () { it("should render two archived features", function() { var rows = Component.getDOMNode().querySelectorAll("tbody tr"); + expect(rows.length).toEqual(2); }); it("should revive archived feature toggle", function() { - var button = Component.getDOMNode().querySelector("tbody button"); - TestUtils.Simulate.click(button); - var rows = Component.getDOMNode().querySelectorAll("tbody tr"); + var button = Component.getDOMNode().querySelector("tbody button"); + TestUtils.Simulate.click(button); - expect(rows.length).toEqual(1); - expect(FeatureStore.reviveFeature).toBeCalledWith({ - name: "featureX" - }); + jest.runAllTimers(); + expect(Server.reviveFeature).toBeCalled(); }); -}); \ No newline at end of file +}); diff --git a/public/js/components/feature/ArchiveFeatureComponent.jsx b/public/js/components/feature/ArchiveFeatureComponent.jsx index 7bd403eb75..0443a930ea 100644 --- a/public/js/components/feature/ArchiveFeatureComponent.jsx +++ b/public/js/components/feature/ArchiveFeatureComponent.jsx @@ -1,64 +1,65 @@ -var React = require("react"); -var FeatureStore = require('../../stores/FeatureStore'); +var React = require("react"); +var FeatureActions = require('../../stores/FeatureToggleActions'); +var FeatureToggleStore = require('../../stores/FeatureToggleStore'); var ArchiveFeatureComponent = React.createClass({ - getInitialState: function() { - return { - archivedFeatures: [] - }; - }, + getInitialState: function() { + return { + archivedFeatures: FeatureToggleStore.getArchivedToggles() + }; + }, - removeToggleFromState: function(item) { - var updatedArchive = this.state.archivedFeatures.filter(function(f) { - return f.name !== item.name; - }); - this.setState({archivedFeatures: updatedArchive}); - }, + onStoreChange: function() { + this.setState({ + archivedFeatures: FeatureToggleStore.getArchivedToggles() + }); + }, - onRevive: function( item) { - FeatureStore.reviveFeature(item).then(this.removeToggleFromState.bind(this, item)); - }, + componentDidMount: function() { + this.unsubscribe = FeatureToggleStore.listen(this.onStoreChange); + }, - componentDidMount: function () { - FeatureStore.getArchivedFeatures().then(function(data) { - this.setState({archivedFeatures: data.features}); - }.bind(this)) - }, + componentWillUnmount: function() { + this.unsubscribe(); + }, - render: function () { - return ( -
-

Archived feature toggles

- - - - - - - - - {this.state.archivedFeatures.map(this.renderArchivedItem)} - -
Name
-
- ); - }, + onRevive: function(item) { + FeatureActions.revive(item); + }, - renderArchivedItem: function(f) { - return ( - - - {f.name}
- {f.description} + render: function () { + return ( +
+

Archived feature toggles

+ + + + + + + + + {this.state.archivedFeatures.map(this.renderArchivedItem)} + +
Name
+
+ ); + }, - - - - - ); - } + renderArchivedItem: function(f) { + return ( + + + {f.name}
+ {f.description} + + + + + ); + } }); -module.exports = ArchiveFeatureComponent; \ No newline at end of file +module.exports = ArchiveFeatureComponent; diff --git a/public/js/components/feature/FeatureTogglesComponent.jsx b/public/js/components/feature/FeatureTogglesComponent.jsx index 815718c066..c8d2853646 100644 --- a/public/js/components/feature/FeatureTogglesComponent.jsx +++ b/public/js/components/feature/FeatureTogglesComponent.jsx @@ -1,25 +1,33 @@ -var React = require('react'); -var Timer = require('../../utils/Timer'); -var ErrorMessages = require('../ErrorMessages'); -var FeatureList = require('./FeatureList'); -var FeatureForm = require('./FeatureForm'); -var FeatureStore = require('../../stores/FeatureStore'); -var FeatureStore2 = require('../../stores/FeatureStore2'); -var FeatureActions = require('../../stores/FeatureActions'); -var Reflux = require('reflux'); +var React = require('react'); +var ErrorMessages = require('../ErrorMessages'); +var FeatureList = require('./FeatureList'); +var FeatureForm = require('./FeatureForm'); +var FeatureActions = require('../../stores/FeatureToggleActions'); +var FeatureToggleStore = require('../../stores/FeatureToggleStore'); var FeatureTogglesComponent = React.createClass({ getInitialState: function() { return { + features: FeatureToggleStore.getFeatureToggles(), errors: [], - createView: false, - featurePoller: new Timer(this.loadFeaturesFromServer, this.props.pollInterval) + createView: false }; }, - mixins: [Reflux.connect(FeatureStore2,"features")], + onFeatureToggleChange: function() { + this.setState({ + features: FeatureToggleStore.getFeatureToggles() + }); + }, + componentDidMount: function() { + this.unsubscribe = FeatureToggleStore.listen(this.onFeatureToggleChange); + }, + componentWillUnmount: function() { + this.unsubscribe(); + }, handleError: function (error) { + console.log(error); if (this.isClientError(error)) { var errors = JSON.parse(error.responseText) errors.forEach(function(e) { this.addError(e.msg); }.bind(this)) @@ -28,57 +36,22 @@ var FeatureTogglesComponent = React.createClass({ } else { this.addError(error); } - - this.forceUpdate(); }, updateFeature: function (feature) { - this.stopFeaturePoller(); - - FeatureStore - .updateFeature(feature) - .then(this.startFeaturePoller) + FeatureActions.update.triggerPromise(feature) .catch(this.handleError); }, archiveFeature: function (feature) { - var updatedFeatures = this.state.features.filter(function(item) { - return item.name !== feature.name; - }); - - FeatureStore - .archiveFeature(feature) - .then(function() { - this.setState({features: updatedFeatures}) - }.bind(this)) + FeatureActions.archive.triggerPromise(feature) .catch(this.handleError); }, - startFeaturePoller: function () { - this.state.featurePoller.start(); - }, - - stopFeaturePoller: function () { - if (this.state.featurePoller != null) { - this.state.featurePoller.stop(); - } - }, - createFeature: function (feature) { - //this.stopFeaturePoller(); - - FeatureActions.addToggle.triggerPromise(feature) + FeatureActions.create.triggerPromise(feature) .then(this.cancelNewFeature) .catch(this.handleError); - -/* - - FeatureStore - .createFeature(feature) - .then(this.cancelNewFeature) - .then(this.startFeaturePoller) - .catch(this.handleError); -*/ }, newFeature: function() { @@ -94,8 +67,10 @@ var FeatureTogglesComponent = React.createClass({ }, addError: function(msg) { - if (this.state.errors[this.state.errors.length - 1] !== msg) { - this.state.errors.push(msg); + var errors = this.state.errors; + if (errors[errors.length - 1] !== msg) { + errors.push(msg); + this.setState(errors); } }, diff --git a/public/js/stores/FeatureActions.js b/public/js/stores/FeatureActions.js deleted file mode 100644 index 8c610f4348..0000000000 --- a/public/js/stores/FeatureActions.js +++ /dev/null @@ -1,4 +0,0 @@ -var Reflux = require("reflux"); -module.exports = { - addToggle: Reflux.createAction({ asyncResult: true }) -}; diff --git a/public/js/stores/FeatureStore.js b/public/js/stores/FeatureStore.js deleted file mode 100644 index 333ab6bdb6..0000000000 --- a/public/js/stores/FeatureStore.js +++ /dev/null @@ -1,62 +0,0 @@ -var reqwest = require('reqwest'); - -var TYPE = 'json'; -var CONTENT_TYPE = 'application/json'; - -FeatureStore = { - updateFeature: function (feature) { - return reqwest({ - url: 'features/' + feature.name, - method: 'put', - type: TYPE, - contentType: CONTENT_TYPE, - data: JSON.stringify(feature) - }); - }, - - createFeature: function (feature) { - return reqwest({ - url: 'features', - method: 'post', - type: TYPE, - contentType: CONTENT_TYPE, - data: JSON.stringify(feature) - }); - }, - - archiveFeature: function (feature) { - return reqwest({ - url: 'features/' + feature.name, - method: 'delete', - type: TYPE - }); - }, - - getFeatures: function () { - return reqwest({ - url: 'features', - method: 'get', - type: TYPE - }); - }, - - getArchivedFeatures: function () { - return reqwest({ - url: 'archive/features', - method: 'get', - type: TYPE - }); - }, - - reviveFeature: function (feature) { - return reqwest({ - url: 'archive/revive', - method: 'post', - type: TYPE, - contentType: CONTENT_TYPE, - data: JSON.stringify(feature) - }); - } -}; - -module.exports = FeatureStore; diff --git a/public/js/stores/FeatureStore2.js b/public/js/stores/FeatureStore2.js deleted file mode 100644 index 6f4bb23ac3..0000000000 --- a/public/js/stores/FeatureStore2.js +++ /dev/null @@ -1,69 +0,0 @@ -var reqwest = require('reqwest'); -var Reflux = require('reflux'); -var FeatureActions = require('./FeatureActions'); - -var TYPE = 'json'; -var CONTENT_TYPE = 'application/json'; - -function getFeatures() { - return reqwest({ - url: 'features', - method: 'get', - type: CONTENT_TYPE - }); -} - - -var _toggles = []; - -// Creates a DataStore -var FeatureStore = Reflux.createStore({ - // Initial setup - init: function() { - this.listenTo(FeatureActions.addToggle, this.onAddToggle); - - getFeatures() - .then(function(data) { - this.setToggles(JSON.parse(data.response).features); - }.bind(this)) - .catch(this.handleError); - }, - - onAddToggle: function(feature) { - reqwest({ - url: 'features', - method: 'post', - type: TYPE, - contentType: CONTENT_TYPE, - data: JSON.stringify(feature), - error: function (error) { - FeatureActions.addToggle.failed(error); - }, - success: function () { - this.setToggles(_toggles.concat([feature])); - FeatureActions.addToggle.completed(); - }.bind(this) - }); - }, - - setToggles: function(toggles) { - _toggles = toggles; - this.trigger(_toggles); - }, - - handleError: function(er) { - console.log("error: "+ er); - }, - - //getter for notes - getToggles: function() { - return _toggles; - }, - - getInitialState: function() { - return _toggles; - } - -}); - -module.exports = FeatureStore; diff --git a/public/js/stores/FeatureToggleActions.js b/public/js/stores/FeatureToggleActions.js new file mode 100644 index 0000000000..a7e8b282c0 --- /dev/null +++ b/public/js/stores/FeatureToggleActions.js @@ -0,0 +1,7 @@ +var Reflux = require("reflux"); +module.exports = { + create: Reflux.createAction({ asyncResult: true }), + update: Reflux.createAction({ asyncResult: true }), + archive: Reflux.createAction({ asyncResult: true }), + revive: Reflux.createAction({ asyncResult: true }) +}; diff --git a/public/js/stores/FeatureToggleServerFacade.js b/public/js/stores/FeatureToggleServerFacade.js new file mode 100644 index 0000000000..20b2452b20 --- /dev/null +++ b/public/js/stores/FeatureToggleServerFacade.js @@ -0,0 +1,98 @@ +var reqwest = require('reqwest'); + +var TYPE = 'json'; +var CONTENT_TYPE = 'application/json'; + +var FeatureToggleServerFacade = { + updateFeature: function (feature, cb) { + reqwest({ + url: 'features/' + feature.name, + method: 'put', + type: TYPE, + contentType: CONTENT_TYPE, + data: JSON.stringify(feature), + error: function(error) { + cb(error); + }, + success: function() { + cb(); + } + }); + }, + + createFeature: function (feature, cb) { + reqwest({ + url: 'features', + method: 'post', + type: TYPE, + contentType: CONTENT_TYPE, + data: JSON.stringify(feature), + error: function(error) { + cb(error); + }, + success: function() { + cb(); + } + }); + }, + + archiveFeature: function(feature, cb) { + reqwest({ + url: 'features/' + feature.name, + method: 'delete', + type: TYPE, + error: function(error) { + cb(error); + }, + success: function() { + cb(); + } + }); + }, + + getFeatures: function(cb) { + reqwest({ + url: 'features', + method: 'get', + type: TYPE, + error: function(error) { + cb(error); + }, + success: function(data) { + cb(null, data.features); + } + }); + }, + + getArchivedFeatures: function(cb) { + reqwest({ + url: 'archive/features', + method: 'get', + type: TYPE, + error: function(error) { + cb(error); + }, + success: function(data) { + cb(null, data.features); + } + }); + }, + + reviveFeature: function (feature, cb) { + reqwest({ + url: 'archive/revive', + method: 'post', + type: TYPE, + contentType: CONTENT_TYPE, + data: JSON.stringify(feature), + error: function(error) { + cb(error); + }, + success: function() { + cb(); + } + }); + } +}; + +module.exports = FeatureToggleServerFacade; diff --git a/public/js/stores/FeatureToggleStore.js b/public/js/stores/FeatureToggleStore.js new file mode 100644 index 0000000000..370f62f985 --- /dev/null +++ b/public/js/stores/FeatureToggleStore.js @@ -0,0 +1,122 @@ +var Reflux = require('reflux'); +var FeatureActions = require('./FeatureToggleActions'); +var Server = require('./FeatureToggleServerFacade'); + +var _featureToggles = []; +var _archivedToggles = []; + +// Creates a DataStore +var FeatureStore = Reflux.createStore({ + // Initial setup + init: function() { + this.listenTo(FeatureActions.create, this.onCreate); + this.listenTo(FeatureActions.update, this.onUpdate); + this.listenTo(FeatureActions.archive, this.onArchive); + this.listenTo(FeatureActions.revive, this.onRevive); + + Server.getFeatures(function(err, featureToggles) { + this.setToggles(featureToggles); + }.bind(this)); + + Server.getArchivedFeatures(function(err, archivedToggles) { + _archivedToggles = archivedToggles; + this.trigger(); + }.bind(this)); + }, + + onCreate: function(feature) { + Server.createFeature(feature, function(error) { + if(error) { + FeatureActions.create.failed(error); + } else { + this.setToggles([feature].concat(_featureToggles)); + FeatureActions.create.completed(); + } + }.bind(this)); + }, + + onArchive: function(feature) { + Server.archiveFeature(feature, function(error) { + if(error) { + FeatureActions.archive.failed(error); + } else { + this.archiveToggle(feature); + FeatureActions.archive.completed(); + } + }.bind(this)); + }, + + onUpdate: function(feature) { + Server.updateFeature(feature, function(error) { + if(error) { + FeatureActions.update.failed(error); + } else { + this.updateToggle(feature); + FeatureActions.update.completed(); + } + }.bind(this)); + }, + + onRevive: function(feature) { + Server.reviveFeature(feature, function(error) { + if(error) { + FeatureActions.revive.failed(error); + } else { + this.revive(feature); + FeatureActions.revive.completed(); + } + }.bind(this)); + }, + + setToggles: function(toggles) { + _featureToggles = toggles; + this.trigger(); + }, + + updateToggle: function(feature) { + var idx; + _featureToggles.forEach(function(item, i) { + if(item.name === feature.name) { + idx = i; + } + }); + _featureToggles[idx] = feature; + this.trigger(); + }, + + archiveToggle: function(feature) { + var idx; + _featureToggles.forEach(function(item, i) { + if(item.name === feature.name) { + idx = i; + } + }); + _featureToggles.splice(idx, 1); + _archivedToggles.unshift(feature); + this.trigger(); + }, + + revive: function(item) { + var newStore = _archivedToggles.filter(function(f) { + return f.name !== item.name; + }); + _archivedToggles = newStore; + _featureToggles.push(item); + this.trigger(); + }, + + getFeatureToggles: function() { + return _featureToggles; + }, + + getArchivedToggles: function() { + return _archivedToggles; + }, + + initStore: function(toggles, archivedToggles) { + _featureToggles = toggles; + _archivedToggles = archivedToggles; + } +}); + +module.exports = FeatureStore;