1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-01 00:08:27 +01:00

Merge pull request #170 from Unleash/add_strategies_check

Added strategies validation when creating/updating toggles
This commit is contained in:
Ivar Conradi Østhus 2016-11-15 21:39:57 +01:00 committed by GitHub
commit c6b66c80c1
6 changed files with 59 additions and 10 deletions

View File

@ -65,27 +65,31 @@ module.exports = function (app, config) {
app.post('/features', (req, res) => { app.post('/features', (req, res) => {
req.checkBody('name', 'Name is required').notEmpty(); req.checkBody('name', 'Name is required').notEmpty();
req.checkBody('name', 'Name must match format ^[0-9a-zA-Z\\.\\-]+$').matches(/^[0-9a-zA-Z\\.\\-]+$/i); req.checkBody('name', 'Name must match format ^[0-9a-zA-Z\\.\\-]+$').matches(/^[0-9a-zA-Z\\.\\-]+$/i);
const userName = extractUser(req);
validateRequest(req) validateRequest(req)
.then(validateFormat) .then(validateFormat)
.then(validateUniqueName) .then(validateUniqueName)
.then(() => eventStore.store({ .then((req) => legacyFeatureMapper.toNewFormat(req.body))
.then(validateStrategy)
.then((featureToggle) => eventStore.store({
type: eventType.featureCreated, type: eventType.featureCreated,
createdBy: extractUser(req), createdBy: userName,
data: legacyFeatureMapper.toNewFormat(req.body), data: featureToggle,
})) }))
.then(() => res.status(201).end()) .then(() => res.status(201).end())
.catch(error => handleErrors(req, res, error)); .catch(error => handleErrors(req, res, error));
}); });
app.put('/features/:featureName', (req, res) => { app.put('/features/:featureName', (req, res) => {
const featureName = req.params.featureName; const featureName = req.params.featureName;
const userName = extractUser(req); const userName = extractUser(req);
const updatedFeature = legacyFeatureMapper.toNewFormat(req.body); const updatedFeature = legacyFeatureMapper.toNewFormat(req.body);
updatedFeature.name = featureName; updatedFeature.name = featureName;
featureToggleStore.getFeature(featureName) featureToggleStore.getFeature(featureName)
.then(() => validateStrategy(updatedFeature))
.then(() => eventStore.store({ .then(() => eventStore.store({
type: eventType.featureUpdated, type: eventType.featureUpdated,
createdBy: userName, createdBy: userName,
@ -96,8 +100,8 @@ module.exports = function (app, config) {
}); });
app.delete('/features/:featureName', (req, res) => { app.delete('/features/:featureName', (req, res) => {
const featureName = req.params.featureName; const featureName = req.params.featureName;
const userName = extractUser(req); const userName = extractUser(req);
featureToggleStore.getFeature(featureName) featureToggleStore.getFeature(featureName)
.then(() => eventStore.store({ .then(() => eventStore.store({
@ -126,4 +130,12 @@ module.exports = function (app, config) {
return Promise.resolve(req); return Promise.resolve(req);
} }
function validateStrategy (featureToggle) {
if (!featureToggle.strategies || featureToggle.strategies.length === 0) {
return Promise.reject(new ValidationError('You must define at least one strategy'));
}
return Promise.resolve(featureToggle);
}
}; };

View File

@ -43,7 +43,7 @@ test.serial('creates new feature toggle', async t => {
const { request, destroy } = await setupApp('feature_api_serial'); const { request, destroy } = await setupApp('feature_api_serial');
return request return request
.post('/features') .post('/features')
.send({ name: 'com.test.feature', enabled: false }) .send({ name: 'com.test.feature', enabled: false, strategies: [{name: 'default'}] })
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.expect(201) .expect(201)
.then(destroy); .then(destroy);
@ -54,7 +54,7 @@ test.serial('creates new feature toggle with createdBy', async t => {
logger.setLevel('FATAL'); logger.setLevel('FATAL');
request request
.post('/features') .post('/features')
.send({ name: 'com.test.Username', enabled: false }) .send({ name: 'com.test.Username', enabled: false, strategies: [{name: 'default'}] })
.set('Cookie', ['username=ivaosthu']) .set('Cookie', ['username=ivaosthu'])
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.end(() => { .end(() => {
@ -93,7 +93,7 @@ test.serial('can change status of feature toggle that does exist', async t => {
logger.setLevel('FATAL'); logger.setLevel('FATAL');
return request return request
.put('/features/featureY') .put('/features/featureY')
.send({ name: 'featureY', enabled: true }) .send({ name: 'featureY', enabled: true, strategies: [{name: 'default'}] })
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.expect(200).then(destroy); .expect(200).then(destroy);
}); });

View File

@ -62,3 +62,23 @@ test('should add version numbers for /features', t => {
}); });
}); });
test('should require at least one strategy when creating a feature toggle', t => {
const { request, base } = getSetup();
return request
.post(`${base}/features`)
.send({ name: 'sample.missing.strategy' })
.set('Content-Type', 'application/json')
.expect(400)
});
test('should require at least one strategy when updating a feature toggle', t => {
const { request, featureToggleStore, base } = getSetup();
featureToggleStore.addFeature({ name: 'ts', strategies: [{ name: 'default' }] });
return request
.put(`${base}/features/ts`)
.send({ name: 'ts' })
.set('Content-Type', 'application/json')
.expect(400)
});

View File

@ -0,0 +1,7 @@
'use strict';
module.exports = () => {
return {
store: () => Promise.resolve(),
};
};

View File

@ -4,6 +4,14 @@
module.exports = () => { module.exports = () => {
const _features = []; const _features = [];
return { return {
getFeature: (name) => {
const toggle = _features.find(f => f.name === name);
if (toggle) {
return Promise.resolve(toggle);
} else {
return Promise.reject();
}
},
getFeatures: () => Promise.resolve(_features), getFeatures: () => Promise.resolve(_features),
addFeature: (feature) => _features.push(feature), addFeature: (feature) => _features.push(feature),
}; };

View File

@ -4,6 +4,7 @@ const clientMetricsStore = require('./fake-metrics-store');
const clientStrategyStore = require('./fake-client-strategy-store'); const clientStrategyStore = require('./fake-client-strategy-store');
const clientInstanceStore = require('./fake-client-instance-store'); const clientInstanceStore = require('./fake-client-instance-store');
const featureToggleStore = require('./fake-feature-toggle-store'); const featureToggleStore = require('./fake-feature-toggle-store');
const eventStore = require('./fake-event-store');
const strategyStore = require('./fake-strategies-store'); const strategyStore = require('./fake-strategies-store');
@ -22,6 +23,7 @@ module.exports = {
clientStrategyStore: clientStrategyStore(), clientStrategyStore: clientStrategyStore(),
clientInstanceStore: clientInstanceStore(), clientInstanceStore: clientInstanceStore(),
featureToggleStore: featureToggleStore(), featureToggleStore: featureToggleStore(),
eventStore: eventStore(),
strategyStore: strategyStore(), strategyStore: strategyStore(),
}; };
}, },