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
-
-
-
- Name |
- |
-
-
-
- {this.state.archivedFeatures.map(this.renderArchivedItem)}
-
-
-
- );
- },
+ onRevive: function(item) {
+ FeatureActions.revive(item);
+ },
- renderArchivedItem: function(f) {
- return (
-
-
- {f.name}
- {f.description}
+ render: function () {
+ return (
+
+ Archived feature toggles
+
+
+
+ Name |
+ |
+
+
+
+ {this.state.archivedFeatures.map(this.renderArchivedItem)}
+
+
+
+ );
+ },
- |
-
-
- |
-
);
- }
+ 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;