1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

#108 Add eslint-config-spt and remove jshint (#111)

* #108 Add eslint-config-spt

* #108 Ignore bundle.js file

* #108 Change eslint ignore path to a glob file

* Remove jshint and follow more of eslint rules
This commit is contained in:
Anders Olsen Sandvik 2016-04-24 22:41:37 +02:00 committed by Ivar Conradi Østhus
parent 89c35135ec
commit a96a9f38ce
55 changed files with 340 additions and 351 deletions

24
.eslintrc Normal file
View File

@ -0,0 +1,24 @@
{
"extends": [
"spt",
"spt/env-node",
"spt/env-es6-false",
"spt/env-react"
],
"env": {
"browser": true
},
"rules": {
"quotes": 0,
"strict": 0,
"vars-on-top": 0,
"no-use-before-define": 0,
"dot-notation": 0,
"eqeqeq": 0,
"no-eq-null": 0,
"no-undefined": 0,
"no-else-return": 0,
"no-shadow": 0,
"consistent-this": 0
}
}

View File

@ -1,83 +0,0 @@
{
"globals": {
"jQuery" : false,
"$" : false,
"sinon" : false,
"describe" : false,
"it" : false,
"xit" : false,
"expect" : false,
"before" : false,
"after" : false,
"beforeEach": false,
"afterEach" : false,
"contain" : false
},
"bitwise" : true,
"camelcase" : true,
"curly" : true,
"eqeqeq" : false,
"es3" : false,
"forin" : false,
"freeze" : false,
"immed" : true,
"indent" : 4,
"latedef" : "nofunc",
"newcap" : false,
"noarg" : true,
"noempty" : true,
"nonbsp" : true,
"nonew" : true,
"plusplus" : false,
"quotmark" : false,
"undef" : true,
"unused" : true,
"strict" : false,
"trailing" : true,
"maxparams" : 3,
"maxdepth" : 4,
"maxstatements" : 20,
"maxcomplexity" : 10,
"maxlen" : 120,
"predef": [ "-Promise" ],
"asi" : false,
"boss" : false,
"debug" : false,
"eqnull" : true,
"esnext" : false,
"evil" : false,
"expr" : false,
"funcscope" : false,
"globalstrict" : false,
"iterator" : false,
"lastsemic" : false,
"laxbreak" : false,
"laxcomma" : false,
"loopfunc" : false,
"maxerr" : 100,
"moz" : false,
"multistr" : true,
"notypeof" : false,
"proto" : false,
"scripturl" : false,
"smarttabs" : false,
"shadow" : false,
"sub" : false,
"supernew" : false,
"validthis" : false,
"noyield" : false,
"browser" : true,
"devel" : false,
"node" : true,
"jquery" : false,
"nonstandard" : false,
"prototypejs" : false,
"nomen" : false,
"onevar" : false,
"passfail" : false,
"white" : false
}

6
app.js
View File

@ -10,10 +10,10 @@ var express = require('express'),
strategyApi = require('./lib/strategyApi'),
validator = require('express-validator'),
app = express(),
router = express.Router(),
router = express.Router(), // eslint-disable-line
baseUriPath = process.env.BASE_URI_PATH || '';
if(app.get('env') === 'development') {
if (app.get('env') === 'development') {
app.use(require('errorhandler')());
var webpack = require('webpack'),
@ -41,7 +41,7 @@ app.use(log4js.connectLogger(logger, {
app.set('port', process.env.HTTP_PORT || process.env.PORT || 4242);
app.use(baseUriPath, express.static(__dirname + '/public'));
app.use(bodyParser.json({strict: false}));
app.use(bodyParser.json({ strict: false }));
app.use(cookieParser());

View File

@ -4,4 +4,4 @@ module.exports = {
process: function(src) {
return ReactTools.transform(src);
}
};
};

View File

@ -42,8 +42,8 @@ function createDbPool() {
return knex({
client: 'pg',
connection: isTestEnv() ? getTestDatabaseUrl() : getDatabaseUrl(),
pool: {min: 2, max: 20}
pool: { min: 2, max: 20 }
});
}
module.exports = createDbPool();
module.exports = createDbPool();

View File

@ -10,4 +10,4 @@ function NameExistsError(message) {
util.inherits(NameExistsError, Error);
module.exports = NameExistsError;
module.exports = NameExistsError;

View File

@ -10,4 +10,4 @@ function NotFoundError(message) {
util.inherits(NotFoundError, Error);
module.exports = NotFoundError;
module.exports = NotFoundError;

View File

@ -10,4 +10,4 @@ function ValidationError(message) {
util.inherits(ValidationError, Error);
module.exports = ValidationError;
module.exports = ValidationError;

View File

@ -11,4 +11,4 @@ function validateRequest(req) {
});
}
module.exports = validateRequest;
module.exports = validateRequest;

View File

@ -2,11 +2,10 @@ var eventDb = require('./eventDb');
var eventDiffer = require('./eventDiffer');
module.exports = function (app) {
app.get('/events', function (req, res) {
eventDb.getEvents().then(function (events) {
eventDiffer.addDiffs(events);
res.json({events: events});
res.json({ events: events });
});
});
@ -16,9 +15,8 @@ module.exports = function (app) {
eventDiffer.addDiffs(events);
res.json(events);
} else {
res.status(404).json({error: 'Could not find events'});
res.status(404).json({ error: 'Could not find events' });
}
});
});
};
};

View File

@ -4,7 +4,7 @@ var EVENT_COLUMNS = ['id', 'type', 'created_by', 'created_at', 'data'];
function storeEvent(event) {
return knex('events').insert({
type: event.type,
created_by: event.createdBy, // jshint ignore:line
created_by: event.createdBy, // eslint-disable-line
data: event.data
});
}
@ -30,8 +30,8 @@ function rowToEvent(row) {
return {
id: row.id,
type: row.type,
createdBy: row.created_by, // jshint ignore:line
createdAt: row.created_at, // jshint ignore:line
createdBy: row.created_by,
createdAt: row.created_at,
data: row.data
};
}
@ -40,4 +40,4 @@ module.exports = {
store: storeEvent,
getEvents: getEvents,
getEventsFilterByName: getEventsFilterByName
};
};

View File

@ -71,4 +71,4 @@ function addDiffs(events) {
module.exports = {
addDiffs: addDiffs
};
};

View File

@ -14,4 +14,4 @@ EventStore.prototype.create = function (event) {
});
};
module.exports = new EventStore();
module.exports = new EventStore();

View File

@ -1,8 +1,8 @@
module.exports = {
featureCreated : 'feature-created',
featureUpdated : 'feature-updated',
featureArchived : 'feature-archived',
featureRevived : 'feature-revived',
featureCreated: 'feature-created',
featureUpdated: 'feature-updated',
featureArchived: 'feature-archived',
featureRevived: 'feature-revived',
strategyCreated: 'strategy-created',
strategyDeleted: 'strategy-deleted'
};
};

View File

@ -1,4 +1,4 @@
function extractUsername(req) {
return req.cookies.username || "unknown";
return req.cookies.username || "unknown";
}
module.exports = extractUsername;

View File

@ -10,18 +10,19 @@ var validateRequest = require('./error/validateRequest');
var extractUser = require('./extractUser');
module.exports = function (app) {
app.get('/features', function (req, res) {
featureDb.getFeatures().then(function (features) {
res.json({features: features});
res.json({ features: features });
});
});
app.get('/features/:featureName', function (req, res) {
featureDb.getFeature(req.params.featureName)
.then(function (feature) { res.json(feature); })
.then(function (feature) {
res.json(feature);
})
.catch(function () {
res.status(404).json({error: 'Could not find feature'});
res.status(404).json({ error: 'Could not find feature' });
});
});
@ -53,7 +54,7 @@ module.exports = function (app) {
logger.error("Could not create feature toggle", err);
res.status(500).end();
});
});
});
app.put('/features/:featureName', function (req, res) {
var featureName = req.params.featureName;

View File

@ -6,10 +6,9 @@ var ValidationError = require('./error/ValidationError');
var validateRequest = require('./error/validateRequest');
module.exports = function (app) {
app.get('/archive/features', function (req, res) {
featureDb.getArchivedFeatures().then(function (archivedFeatures) {
res.json({'features': archivedFeatures});
res.json({ 'features': archivedFeatures });
});
});
@ -34,4 +33,3 @@ module.exports = function (app) {
});
});
};

View File

@ -25,7 +25,7 @@ function getFeatures() {
return knex
.select(FEATURE_COLUMNS)
.from('features')
.where({archived: 0})
.where({ archived: 0 })
.orderBy('name', 'asc')
.map(rowToFeature);
}
@ -34,7 +34,7 @@ function getFeature(name) {
return knex
.first(FEATURE_COLUMNS)
.from('features')
.where({name: name})
.where({ name: name })
.then(rowToFeature);
}
@ -42,7 +42,7 @@ function getArchivedFeatures() {
return knex
.select(FEATURE_COLUMNS)
.from('features')
.where({archived: 1})
.where({ archived: 1 })
.orderBy('name', 'asc')
.map(rowToFeature);
}
@ -57,7 +57,7 @@ function rowToFeature(row) {
name: row.name,
description: row.description,
enabled: row.enabled > 0,
strategy: row.strategy_name, // jshint ignore: line
strategy: row.strategy_name, // eslint-disable-line
parameters: row.parameters
};
}
@ -68,7 +68,7 @@ function eventDataToRow(data) {
description: data.description,
enabled: data.enabled ? 1 : 0,
archived: data.archived ? 1 :0,
strategy_name: data.strategy, // jshint ignore: line
strategy_name: data.strategy, // eslint-disable-line
parameters: data.parameters
};
}
@ -83,7 +83,7 @@ function createFeature(data) {
function updateFeature(data) {
return knex('features')
.where({name: data.name})
.where({ name: data.name })
.update(eventDataToRow(data))
.catch(function (err) {
logger.error('Could not update feature, error was: ', err);
@ -92,8 +92,8 @@ function updateFeature(data) {
function archiveFeature(data) {
return knex('features')
.where({name: data.name})
.update({archived: 1})
.where({ name: data.name })
.update({ archived: 1 })
.catch(function (err) {
logger.error('Could not archive feature, error was: ', err);
});
@ -101,8 +101,8 @@ function archiveFeature(data) {
function reviveFeature(data) {
return knex('features')
.where({name: data.name})
.update({archived: 0, enabled: 0})
.where({ name: data.name })
.update({ archived: 0, enabled: 0 })
.catch(function (err) {
logger.error('Could not archive feature, error was: ', err);
});

View File

@ -3,7 +3,7 @@ var util = require('util');
var path = require('path');
var runMigration = function(path, db, callback) {
db.runSql(fs.readFileSync(path, {encoding: 'utf8'}), callback);
db.runSql(fs.readFileSync(path, { encoding: 'utf8' }), callback);
};
module.exports = {

View File

@ -2,17 +2,15 @@ var knex = require('./dbPool');
var logger = require('./logger');
module.exports = function (app) {
app.get('/health', function (req, res) {
knex.select(1)
.from('features')
.then(function() {
res.json({health: 'GOOD'});
res.json({ health: 'GOOD' });
})
.catch(function(err) {
logger.error('Could not select from features, error was: ', err);
res.status(500).json({health: 'BAD'});
res.status(500).json({ health: 'BAD' });
});
});
};

View File

@ -10,17 +10,20 @@ var validateRequest = require('./error/validateRequest');
var extractUser = require('./extractUser');
module.exports = function (app) {
app.get('/strategies', function (req, res) {
strategyDb.getStrategies().then(function (strategies) {
res.json({strategies: strategies});
res.json({ strategies: strategies });
});
});
app.get('/strategies/:name', function (req, res) {
strategyDb.getStrategy(req.params.name)
.then(function (strategy) { res.json(strategy); })
.catch(function () { res.status(404).json({error: 'Could not find strategy'}); });
.then(function (strategy) {
res.json(strategy);
})
.catch(function () {
res.status(404).json({ error: 'Could not find strategy' });
});
});
app.delete('/strategies/:name', function (req, res) {
@ -67,7 +70,7 @@ module.exports = function (app) {
res.status(201).end();
})
.catch(NameExistsError, function() {
res.status(403).json([{msg: "A strategy named '" + req.body.name + "' already exists."}]).end();
res.status(403).json([{ msg: "A strategy named '" + req.body.name + "' already exists." }]).end();
})
.catch(ValidationError, function() {
res.status(400).json(req.validationErrors());
@ -88,5 +91,4 @@ module.exports = function (app) {
});
});
}
};

View File

@ -30,7 +30,7 @@ function getStrategy(name) {
return knex
.first(STRATEGY_COLUMNS)
.from('strategies')
.where({name: name})
.where({ name: name })
.then(rowToStrategy);
}
@ -42,7 +42,7 @@ function rowToStrategy(row) {
return {
name: row.name,
description: row.description,
parametersTemplate: row.parameters_template // jshint ignore: line
parametersTemplate: row.parameters_template
};
}
@ -50,7 +50,7 @@ function eventDataToRow(data) {
return {
name: data.name,
description: data.description,
parameters_template: data.parametersTemplate // jshint ignore: line
parameters_template: data.parametersTemplate // eslint-disable-line
};
}

View File

@ -1 +1 @@
module.exports = require('../lib/migrationRunner').create('001-initial-schema');
module.exports = require('../lib/migrationRunner').create('001-initial-schema');

View File

@ -1,2 +1 @@
module.exports = require('../lib/migrationRunner').create('002-add-description-to-features');

View File

@ -1 +1,2 @@
module.exports = require('../lib/migrationRunner').create('003-add-parameters-template-to-strategies');

View File

@ -1 +1 @@
module.exports = require('../lib/migrationRunner').create('004-insert-default-strategy');
module.exports = require('../lib/migrationRunner').create('004-insert-default-strategy');

View File

@ -1 +1 @@
module.exports = require('../lib/migrationRunner').create('004-insert-default-strategy-event');
module.exports = require('../lib/migrationRunner').create('004-insert-default-strategy-event');

View File

@ -1 +1 @@
module.exports = require('../lib/migrationRunner').create('006-rename-eventtype');
module.exports = require('../lib/migrationRunner').create('006-rename-eventtype');

View File

@ -35,7 +35,7 @@
"coverage-report": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage",
"postinstall": "npm run build",
"jest": "jest",
"lint": "jshint server.js lib test && jsxhint public/js/**/*.jsx"
"lint": "eslint . --ignore-path .gitignore"
},
"dependencies": {
"bluebird": "2.9.14",
@ -65,9 +65,11 @@
"devDependencies": {
"chai": "2.1.2",
"coveralls": "^2.11.2",
"eslint": "^2.7.0",
"eslint-config-spt": "^2.0.0",
"eslint-plugin-react": "^4.3.0",
"istanbul": "^0.3.5",
"jest-cli": "0.5.4",
"jshint": "^2.6.0",
"mocha": "^2.1.0",
"mocha-lcov-reporter": "0.0.2",
"nsp": "^1.0.0",

View File

@ -0,0 +1,25 @@
{
"extends": [
"spt",
"spt/env-node",
"spt/env-es6-false"
],
"env": {
"browser": true
},
"globals": {
"it": false,
"jest": false,
"beforeEach": false,
"expect": false,
"describe": false,
"afterEach": false
},
"rules": {
"quotes": 0,
"strict": 0,
"vars-on-top": 0,
"no-undefined": 0,
"no-shadow": 0
}
}

View File

@ -11,4 +11,4 @@ describe('Menu test', function () {
var Compononent = TestUtils .renderIntoDocument(<Menu />);
expect(Compononent.getDOMNode().textContent).toMatch('unleash');
});
});
});

View File

@ -6,7 +6,6 @@ var React = require("react/addons");
var TestUtils = React.addons.TestUtils;
var FeatureArchive = require("../../../components/feature/ArchiveFeatureComponent");
var FeatureActions = require("../../../stores/FeatureToggleActions");
var FeatureToggleStore = require("../../../stores/ArchivedToggleStore");
describe("FeatureForm", function () {
var Component;
@ -31,10 +30,10 @@ describe("FeatureForm", function () {
});
it("should revive archived feature toggle", function() {
var button = Component.getDOMNode().querySelector("tbody button");
TestUtils.Simulate.click(button);
var button = Component.getDOMNode().querySelector("tbody button");
TestUtils.Simulate.click(button);
jest.runAllTimers();
expect(FeatureActions.revive.triggerPromise).toBeCalled();
jest.runAllTimers();
expect(FeatureActions.revive.triggerPromise).toBeCalled();
});
});

View File

@ -7,7 +7,7 @@ var FeatureForm = require("../../../components/feature/FeatureForm");
describe("FeatureForm", function () {
var Component;
var strategies = [
{ name: "default"}
{ name: "default" }
];
afterEach(function() {
React.unmountComponentAtNode(document.body);
@ -22,7 +22,7 @@ describe("FeatureForm", function () {
});
describe("edit", function () {
var feature = {name: "Test", strategy: "unknown"};
var feature = { name: "Test", strategy: "unknown" };
it("should show unknown strategy as default", function () {
Component = TestUtils .renderIntoDocument(<FeatureForm feature={feature} strategies={strategies} />);
@ -31,5 +31,4 @@ describe("FeatureForm", function () {
expect(strategySelect.value).toEqual("default");
});
});
});

View File

@ -14,7 +14,7 @@ describe("FeatureList", function () {
{ name: "group.featureY", strategy: "default" }
];
var strategies=[
{ name: "default"}
{ name: "default" }
];
Component = TestUtils .renderIntoDocument(<FeatureList features={features} strategies={strategies} />);
});
@ -30,7 +30,7 @@ describe("FeatureList", function () {
it("should filter list of features", function() {
var filterNode = Component.refs.filter.getDOMNode();
TestUtils.Simulate.change(filterNode, {target: {value: "group"}});
TestUtils.Simulate.change(filterNode, { target: { value: "group" } });
var features = Component.getDOMNode().querySelectorAll(".feature");
expect(features.length).toEqual(1);
@ -38,7 +38,7 @@ describe("FeatureList", function () {
it("should filter list of features ignoring case", function() {
var filterNode = Component.refs.filter.getDOMNode();
TestUtils.Simulate.change(filterNode, {target: {value: "GROUP"}});
TestUtils.Simulate.change(filterNode, { target: { value: "GROUP" } });
var features = Component.getDOMNode().querySelectorAll(".feature");
expect(features.length).toEqual(1);
@ -48,11 +48,10 @@ describe("FeatureList", function () {
it("should filter list of features by strategy name", function() {
var searchString = "other";
var filterNode = Component.refs.filter.getDOMNode();
TestUtils.Simulate.change(filterNode, {target: {value: searchString}});
TestUtils.Simulate.change(filterNode, { target: { value: searchString } });
var features = Component.getDOMNode().querySelectorAll(".feature");
expect(features.length).toEqual(1);
expect(features[0].textContent).toMatch(searchString);
});
});

View File

@ -1,16 +1,15 @@
jest.autoMockOff()
jest.autoMockOff();
jest.dontMock('../../stores/FeatureToggleActions');
jest.dontMock('../../stores/FeatureToggleStore');
describe('FeatureToggleStore', function() {
var Actions, Store, toggles;
beforeEach(function() {
Actions = require('../../stores/FeatureToggleActions');
Store = require('../../stores/FeatureToggleStore');
toggles = [
{name: "app.feature", enabled: true, strategy: "default"}
{ name: "app.feature", enabled: true, strategy: "default" }
];
});
@ -29,7 +28,7 @@ describe('FeatureToggleStore', function() {
it('should add a another toggle', function() {
Actions.init.completed(toggles);
var newToggle = {name: "app.featureB", enabled: true, strategy: "default"};
var newToggle = { name: "app.featureB", enabled: true, strategy: "default" };
Actions.create.completed(newToggle);
@ -47,15 +46,14 @@ describe('FeatureToggleStore', function() {
expect(Store.getFeatureToggles().length).toBe(0);
});
it('should keep toggles in sorted order', function() {
Actions.init.completed([
{name: "A"},
{name: "B"},
{name: "C"}
{ name: "A" },
{ name: "B" },
{ name: "C" }
]);
Actions.create.completed({name: "AA"});
Actions.create.completed({ name: "AA" });
jest.runAllTimers();
expect(Store.getFeatureToggles()[0].name).toEqual("A");
@ -74,8 +72,4 @@ describe('FeatureToggleStore', function() {
jest.runAllTimers();
expect(Store.getFeatureToggles()[0].enabled).toEqual(false);
});
});

View File

@ -13,7 +13,6 @@ var FeatureStore = Reflux.createStore({
this.listenTo(FeatureActions.initArchive.completed, this.onInit);
this.listenTo(FeatureActions.archive.completed, this.onArchive);
this.listenTo(FeatureActions.revive.completed, this.onRevive);
},
onInit: function(toggles) {

View File

@ -16,10 +16,12 @@ var FeatureStore = Reflux.createStore({
this.errors = [];
},
onError: function (error) {
onError: function (error) {
if (this.isClientError(error)) {
var errors = JSON.parse(error.responseText);
errors.forEach(function(e) { this.addError(e.msg); }.bind(this));
errors.forEach(function(e) {
this.addError(e.msg);
}.bind(this));
} else if (error.status === 0) {
this.addError("server unreachable");
} else {

View File

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

View File

@ -2,71 +2,71 @@ var Reflux = require("reflux");
var Server = require('./FeatureToggleServerFacade');
var FeatureToggleActions = Reflux.createActions({
'init': { asyncResult: true },
'initArchive':{ asyncResult: true },
'create': { asyncResult: true },
'update': { asyncResult: true },
'archive': { asyncResult: true },
'revive': { asyncResult: true }
'init': { asyncResult: true },
'initArchive': { asyncResult: true },
'create': { asyncResult: true },
'update': { asyncResult: true },
'archive': { asyncResult: true },
'revive': { asyncResult: true }
});
FeatureToggleActions.init.listen(function(){
FeatureToggleActions.init.listen(function() {
Server.getFeatures(function(error, features) {
if(error) {
this.failed(error);
} else {
this.completed(features);
}
if (error) {
this.failed(error);
} else {
this.completed(features);
}
}.bind(this));
});
FeatureToggleActions.initArchive.listen(function(){
FeatureToggleActions.initArchive.listen(function() {
Server.getArchivedFeatures(function(error, archivedToggles) {
if(error) {
this.failed(error);
} else {
this.completed(archivedToggles);
}
if (error) {
this.failed(error);
} else {
this.completed(archivedToggles);
}
}.bind(this));
});
FeatureToggleActions.create.listen(function(feature){
FeatureToggleActions.create.listen(function(feature) {
Server.createFeature(feature, function(error) {
if(error) {
this.failed(error);
} else {
this.completed(feature);
}
if (error) {
this.failed(error);
} else {
this.completed(feature);
}
}.bind(this));
});
FeatureToggleActions.update.listen(function(feature){
FeatureToggleActions.update.listen(function(feature) {
Server.updateFeature(feature, function(error) {
if(error) {
this.failed(error);
} else {
this.completed(feature);
}
if (error) {
this.failed(error);
} else {
this.completed(feature);
}
}.bind(this));
});
FeatureToggleActions.archive.listen(function(feature){
FeatureToggleActions.archive.listen(function(feature) {
Server.archiveFeature(feature, function(error) {
if(error) {
this.failed(error);
} else {
this.completed(feature);
}
if (error) {
this.failed(error);
} else {
this.completed(feature);
}
}.bind(this));
});
FeatureToggleActions.revive.listen(function(feature){
FeatureToggleActions.revive.listen(function(feature) {
Server.reviveFeature(feature, function(error) {
if(error) {
this.failed(error);
} else {
this.completed(feature);
}
if (error) {
this.failed(error);
} else {
this.completed(feature);
}
}.bind(this));
});

View File

@ -12,10 +12,10 @@ var FeatureToggleServerFacade = {
contentType: CONTENT_TYPE,
data: JSON.stringify(feature),
error: function(error) {
cb(error);
cb(error);
},
success: function() {
cb();
cb();
}
});
},
@ -28,10 +28,10 @@ var FeatureToggleServerFacade = {
contentType: CONTENT_TYPE,
data: JSON.stringify(feature),
error: function(error) {
cb(error);
cb(error);
},
success: function() {
cb();
cb();
}
});
},
@ -42,10 +42,10 @@ var FeatureToggleServerFacade = {
method: 'delete',
type: TYPE,
error: function(error) {
cb(error);
cb(error);
},
success: function() {
cb();
cb();
}
});
},
@ -56,10 +56,10 @@ var FeatureToggleServerFacade = {
method: 'get',
type: TYPE,
error: function(error) {
cb(error);
cb(error);
},
success: function(data) {
cb(null, data.features);
cb(null, data.features);
}
});
},
@ -70,10 +70,10 @@ var FeatureToggleServerFacade = {
method: 'get',
type: TYPE,
error: function(error) {
cb(error);
cb(error);
},
success: function(data) {
cb(null, data.features);
cb(null, data.features);
}
});
},
@ -86,10 +86,10 @@ var FeatureToggleServerFacade = {
contentType: CONTENT_TYPE,
data: JSON.stringify(feature),
error: function(error) {
cb(error);
cb(error);
},
success: function() {
cb();
cb();
}
});
}

View File

@ -9,49 +9,49 @@ var _featureToggles = [];
var FeatureStore = Reflux.createStore({
// Initial setup
init: function() {
this.listenTo(FeatureActions.init.completed, this.setToggles);
this.listenTo(FeatureActions.create.completed, this.onCreate);
this.listenTo(FeatureActions.update.completed, this.onUpdate);
this.listenTo(FeatureActions.archive.completed, this.onArchive);
this.listenTo(FeatureActions.revive.completed, this.onRevive);
},
init: function() {
this.listenTo(FeatureActions.init.completed, this.setToggles);
this.listenTo(FeatureActions.create.completed, this.onCreate);
this.listenTo(FeatureActions.update.completed, this.onUpdate);
this.listenTo(FeatureActions.archive.completed, this.onArchive);
this.listenTo(FeatureActions.revive.completed, this.onRevive);
},
onCreate: function(feature) {
this.setToggles([feature].concat(_featureToggles));
},
onCreate: function(feature) {
this.setToggles([feature].concat(_featureToggles));
},
setToggles: function(toggles) {
_featureToggles = sortBy(toggles, 'name');
this.trigger();
},
setToggles: function(toggles) {
_featureToggles = sortBy(toggles, 'name');
this.trigger();
},
onUpdate: function(feature) {
var idx = findIndex(_featureToggles, 'name', feature.name);
_featureToggles[idx] = feature;
this.trigger();
},
onUpdate: function(feature) {
var idx = findIndex(_featureToggles, 'name', feature.name);
_featureToggles[idx] = feature;
this.trigger();
},
onArchive: function(feature) {
var featureToggles = filter(_featureToggles, function(item) {
return item.name !== feature.name;
});
this.setToggles(featureToggles);
this.trigger();
},
onArchive: function(feature) {
var featureToggles = filter(_featureToggles, function(item) {
return item.name !== feature.name;
});
this.setToggles(featureToggles);
this.trigger();
},
onRevive: function(item) {
this.setToggles(_featureToggles.concat([item]));
this.trigger();
},
onRevive: function(item) {
this.setToggles(_featureToggles.concat([item]));
this.trigger();
},
getFeatureToggles: function() {
return _featureToggles;
},
getFeatureToggles: function() {
return _featureToggles;
},
initStore: function(toggles) {
_featureToggles = toggles;
}
initStore: function(toggles) {
_featureToggles = toggles;
}
});
module.exports = FeatureStore;

View File

@ -12,10 +12,10 @@ var StrategyAPI = {
contentType: CONTENT_TYPE,
data: JSON.stringify(strategy),
error: function(error) {
cb(error);
cb(error);
},
success: function() {
cb(null, strategy);
cb(null, strategy);
}
});
},
@ -26,10 +26,10 @@ var StrategyAPI = {
method: 'delete',
type: TYPE,
error: function(error) {
cb(error);
cb(error);
},
success: function() {
cb(null, strategy);
cb(null, strategy);
}
});
},
@ -40,10 +40,10 @@ var StrategyAPI = {
method: 'get',
type: TYPE,
error: function(error) {
cb(error);
cb(error);
},
success: function(data) {
cb(null, data.strategies);
cb(null, data.strategies);
}
});
}

View File

@ -2,14 +2,14 @@ var Reflux = require("reflux");
var StrategyAPI = require('./StrategyAPI');
var StrategyActions = Reflux.createActions({
'init': { asyncResult: true },
'create': { asyncResult: true },
'remove': { asyncResult: true },
'init': { asyncResult: true },
'create': { asyncResult: true },
'remove': { asyncResult: true },
});
StrategyActions.init.listen(function(){
StrategyActions.init.listen(function() {
StrategyAPI.getStrategies(function(err, strategies) {
if(err) {
if (err) {
this.failed(err);
} else {
this.completed(strategies);
@ -17,9 +17,9 @@ StrategyActions.init.listen(function(){
}.bind(this));
});
StrategyActions.create.listen(function(feature){
StrategyActions.create.listen(function(feature) {
StrategyAPI.createStrategy(feature, function(err) {
if(err) {
if (err) {
this.failed(err);
} else {
this.completed(feature);
@ -27,9 +27,9 @@ StrategyActions.create.listen(function(feature){
}.bind(this));
});
StrategyActions.remove.listen(function(feature){
StrategyActions.remove.listen(function(feature) {
StrategyAPI.removeStrategy(feature, function(err) {
if(err) {
if (err) {
this.failed(err);
} else {
this.completed(feature);

View File

@ -1,16 +1,16 @@
var _username;
//Ref: http://stackoverflow.com/questions/10730362/get-cookie-by-name
// Ref: http://stackoverflow.com/questions/10730362/get-cookie-by-name
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
for (var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') {
c = c.substring(1,c.length);
c = c.substring(1, c.length);
}
if (c.indexOf(nameEQ) === 0) {
return c.substring(nameEQ.length,c.length);
return c.substring(nameEQ.length, c.length);
}
}
return null;
@ -18,16 +18,16 @@ function readCookie(name) {
var UserStore = {
init: function init() {
_username = readCookie("username");
_username = readCookie("username");
},
set: function set(username) {
_username=username;
document.cookie="username="+_username+"; expires=Thu, 18 Dec 2099 12:00:00 UTC";
_username=username;
document.cookie="username="+_username+"; expires=Thu, 18 Dec 2099 12:00:00 UTC";
},
get: function get() {
return _username;
return _username;
}
};

View File

@ -24,4 +24,4 @@ Timer.prototype.stop = function() {
}
};
module.exports = Timer;
module.exports = Timer;

View File

@ -20,7 +20,9 @@ changeLog.att('xsi:schemaLocation',
'http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd');
fs.readdir(sqlRoot, function (err, files) {
if (err) { throw err; }
if (err) {
throw err;
}
var changes = {};
@ -37,23 +39,23 @@ fs.readdir(sqlRoot, function (err, files) {
var direction = match[2];
changes[name] = changes[name] || {};
changes[name][direction] = fs.readFileSync(path.resolve(sqlRoot, sqlFile), {encoding: encoding});
changes[name][direction] = fs.readFileSync(path.resolve(sqlRoot, sqlFile), { encoding: encoding });
});
Object.keys(changes).forEach(function (name) {
var change = changes[name];
var el = changeLog.ele('changeSet', {id: name, author: 'unleash'});
var el = changeLog.ele('changeSet', { id: name, author: 'unleash' });
el.ele('sql', {}, change.up);
el.ele('rollback', {}, change.down);
});
util.puts(changeLog.end({pretty: true}));
util.puts(changeLog.end({ pretty: true }));
});
function initialInit(changes) {
changes["init-prepare"] = {};
changes["init-prepare"]["up"] = fs.readFileSync(path.resolve(__dirname, './init.up.sql'), {encoding: encoding});
changes["init-prepare"]["down"] = fs.readFileSync(path.resolve(__dirname, './init.down.sql'), {encoding: encoding});
changes["init-prepare"]["up"] = fs.readFileSync(path.resolve(__dirname, './init.up.sql'), { encoding: encoding });
changes["init-prepare"]["down"] = fs.readFileSync(path.resolve(__dirname, './init.down.sql'), { encoding: encoding });
}

20
test/.eslintrc Normal file
View File

@ -0,0 +1,20 @@
{
"extends": [
"spt",
"spt/env-node",
"spt/env-es6-false"
],
"env": {
"browser": true
},
"globals": {
"it": false,
"beforeEach": false,
"describe": false
},
"rules": {
"quotes": 0,
"vars-on-top": 0,
"max-nested-callbacks": 0
}
}

View File

@ -1,3 +1,4 @@
'use strict';
var request = require('./specHelper').request;
describe('The event api', function () {
@ -14,5 +15,4 @@ describe('The event api', function () {
.expect('Content-Type', /json/)
.expect(200, done);
});
});
});

View File

@ -1,3 +1,4 @@
'use strict';
var eventDiffer = require('../lib/eventDiffer');
var eventType = require('../lib/eventType');
var assert = require('assert');
@ -5,8 +6,8 @@ var assert = require('assert');
describe('eventDiffer', function () {
it('fails if events include an unknown event type', function () {
var events = [
{type: eventType.featureCreated, data: {}},
{type: 'unknown-type', data: {}}
{ type: eventType.featureCreated, data: {} },
{ type: 'unknown-type', data: {} }
];
assert.throws(function () {
@ -21,19 +22,19 @@ describe('eventDiffer', function () {
var events = [
{
type: eventType.featureUpdated,
data: {name: name, description: desc, strategy: 'default', enabled: true, parameters: {value: 2 }}
data: { name: name, description: desc, strategy: 'default', enabled: true, parameters: { value: 2 } }
},
{
type: eventType.featureCreated,
data: {name: name, description: desc, strategy: 'default', enabled: false, parameters: {value: 1}}
data: { name: name, description: desc, strategy: 'default', enabled: false, parameters: { value: 1 } }
}
];
eventDiffer.addDiffs(events);
assert.deepEqual(events[0].diffs, [
{kind: 'E', path: ["enabled"], lhs: false, rhs: true},
{kind: 'E', path: ["parameters", "value"], lhs: 1, rhs: 2}
{ kind: 'E', path: ["enabled"], lhs: false, rhs: true },
{ kind: 'E', path: ["parameters", "value"], lhs: 1, rhs: 2 }
]);
assert.strictEqual(events[1].diffs, null);
@ -43,19 +44,19 @@ describe('eventDiffer', function () {
var events = [
{
type: eventType.featureUpdated,
data: {name: 'bar', description: 'desc', strategy: 'default', enabled: true, parameters: {}}
data: { name: 'bar', description: 'desc', strategy: 'default', enabled: true, parameters: {} }
},
{
type: eventType.featureUpdated,
data: {name: 'foo', description: 'desc', strategy: 'default', enabled: false, parameters: {}}
data: { name: 'foo', description: 'desc', strategy: 'default', enabled: false, parameters: {} }
},
{
type: eventType.featureCreated,
data: {name: 'bar', description: 'desc', strategy: 'default', enabled: false, parameters: {}}
data: { name: 'bar', description: 'desc', strategy: 'default', enabled: false, parameters: {} }
},
{
type: eventType.featureCreated,
data: {name: 'foo', description: 'desc', strategy: 'default', enabled: true, parameters: {}}
data: { name: 'foo', description: 'desc', strategy: 'default', enabled: true, parameters: {} }
}
];
@ -71,11 +72,11 @@ describe('eventDiffer', function () {
var events = [
{
type: eventType.featureUpdated,
data: {name: 'foo', description: 'desc', strategy: 'default', enabled: true, parameters: {}}
data: { name: 'foo', description: 'desc', strategy: 'default', enabled: true, parameters: {} }
},
{
type: eventType.featureCreated,
data: {name: 'foo', description: 'desc', strategy: 'default', enabled: true, parameters: {}}
data: { name: 'foo', description: 'desc', strategy: 'default', enabled: true, parameters: {} }
}
];
@ -87,11 +88,11 @@ describe('eventDiffer', function () {
var events = [
{
type: eventType.featureUpdated,
data: {name: 'foo', description: 'desc', strategy: 'default', enabled: true, parameters: {}}
data: { name: 'foo', description: 'desc', strategy: 'default', enabled: true, parameters: {} }
}
];
eventDiffer.addDiffs(events);
assert.strictEqual(events[0].diffs, null);
});
});
});

View File

@ -1,7 +1,10 @@
'use strict';
var assert = require('assert');
var specHelper = require('./specHelper');
var request = specHelper.request;
var stringify = function (o) { return JSON.stringify(o, null, ' '); };
var stringify = function (o) {
return JSON.stringify(o, null, ' ');
};
describe('The features api', function () {
beforeEach(function (done) {
@ -38,7 +41,7 @@ describe('The features api', function () {
it('creates new feature toggle', function (done) {
request
.post('/features')
.send({name: 'com.test.feature', enabled: false})
.send({ name: 'com.test.feature', enabled: false })
.set('Content-Type', 'application/json')
.expect(201, done);
});
@ -46,23 +49,23 @@ describe('The features api', function () {
it('creates new feature toggle with createdBy', function (done) {
request
.post('/features')
.send({name: 'com.test.Username', enabled: false})
.send({ name: 'com.test.Username', enabled: false })
.set('Cookie', ['username=ivaosthu'])
.set('Content-Type', 'application/json')
.end(function(){
request
.get('/events')
.end(function (err, res) {
assert.equal(res.body.events[0].createdBy, 'ivaosthu');
done();
});
.end(function() {
request
.get('/events')
.end(function (err, res) {
assert.equal(res.body.events[0].createdBy, 'ivaosthu');
done();
});
});
});
it('require new feature toggle to have a name', function (done) {
request
.post('/features')
.send({name: ''})
.send({ name: '' })
.set('Content-Type', 'application/json')
.expect(400, done);
});
@ -70,7 +73,7 @@ describe('The features api', function () {
it('can not change status of feature toggle that does not exist', function (done) {
request
.put('/features/should-not-exist')
.send({name: 'should-not-exist', enabled: false})
.send({ name: 'should-not-exist', enabled: false })
.set('Content-Type', 'application/json')
.expect(404, done);
});
@ -78,7 +81,7 @@ describe('The features api', function () {
it('can change status of feature toggle that does exist', function (done) {
request
.put('/features/featureY')
.send({name: 'featureY', enabled: true})
.send({ name: 'featureY', enabled: true })
.set('Content-Type', 'application/json')
.expect(200, done);
});
@ -98,9 +101,8 @@ describe('The features api', function () {
it('refuses to create a feature with an existing name', function (done) {
request
.post('/features')
.send({name: 'featureX'})
.send({ name: 'featureX' })
.set('Content-Type', 'application/json')
.expect(403, done);
});
});

View File

@ -1,7 +1,10 @@
'use strict';
var assert = require('assert');
var specHelper = require('./specHelper');
var request = specHelper.request;
var stringify = function (o) { return JSON.stringify(o, null, ' '); };
var stringify = function (o) {
return JSON.stringify(o, null, ' ');
};
describe('The archive features api', function () {
beforeEach(function (done) {
@ -24,7 +27,7 @@ describe('The archive features api', function () {
it('revives a feature by name', function (done) {
request
.post('/archive/revive')
.send({name: 'featureArchivedX'})
.send({ name: 'featureArchivedX' })
.set('Content-Type', 'application/json')
.expect(200, done);
});
@ -34,5 +37,4 @@ describe('The archive features api', function () {
.post('/archive/revive')
.expect(400, done);
});
});
});

View File

@ -1,8 +1,8 @@
'use strict';
var specHelper = require('./specHelper');
var request = specHelper.request;
describe('The routes', function () {
describe('healthcheck', function () {
it('returns health good', function (done) {
request.get('/health')
@ -11,5 +11,4 @@ describe('The routes', function () {
.expect('{"health":"GOOD"}', done);
});
});
});
});

View File

@ -1,3 +1,4 @@
'use strict';
process.env.NODE_ENV = 'test';
var Promise = require('bluebird');
@ -23,7 +24,9 @@ function createStrategies() {
emails: "String"
}
}
], function (strategy) { return strategyDb._createStrategy(strategy); });
], function (strategy) {
return strategyDb._createStrategy(strategy);
});
}
function createFeatures() {
@ -79,7 +82,9 @@ function createFeatures() {
"foo": "rab"
}
}
], function (feature) { return featureDb._createFeature(feature); });
], function (feature) {
return featureDb._createFeature(feature);
});
}
function destroyStrategies() {
@ -103,6 +108,8 @@ module.exports = {
db: {
reset: resetDatabase,
setup: setupDatabase,
resetAndSetup: function () { return resetDatabase().then(setupDatabase); }
resetAndSetup: function () {
return resetDatabase().then(setupDatabase);
}
}
};
};

View File

@ -1,3 +1,4 @@
'use strict';
var specHelper = require('./specHelper');
var request = specHelper.request;
@ -32,7 +33,7 @@ describe('The strategy api', function () {
it('creates a new strategy', function (done) {
request
.post('/strategies')
.send({name: 'myCustomStrategy', description: 'Best strategy ever.'})
.send({ name: 'myCustomStrategy', description: 'Best strategy ever.' })
.set('Content-Type', 'application/json')
.expect(201, done);
});
@ -40,7 +41,7 @@ describe('The strategy api', function () {
it('requires new strategies to have a name', function (done) {
request
.post('/strategies')
.send({name: ''})
.send({ name: '' })
.set('Content-Type', 'application/json')
.expect(400, done);
});
@ -48,7 +49,7 @@ describe('The strategy api', function () {
it('refuses to create a strategy with an existing name', function (done) {
request
.post('/strategies')
.send({name: 'default'})
.send({ name: 'default' })
.set('Content-Type', 'application/json')
.expect(403, done);
});
@ -64,5 +65,4 @@ describe('The strategy api', function () {
.delete('/strategies/unknown')
.expect(404, done);
});
});
});

View File

@ -2,21 +2,21 @@
var path = require('path');
var root = path.normalize(path.join(__dirname, '.'));
var js_root = path.join(path.join(root, 'public'), 'js');
var jsroot = path.join(path.join(root, 'public'), 'js');
module.exports = {
context: js_root,
context: jsroot,
entry: 'app',
output: {
path: js_root,
path: jsroot,
filename: 'bundle.js',
publicPath: '/js/'
},
resolve: {
root: [js_root],
root: [jsroot],
extensions: ['', '.js', '.jsx'],
modulesDirectories: ["web_modules", "node_modules"]
},