mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-28 00:06:53 +01:00
Merge branch 'master' into add-sdk-version
This commit is contained in:
commit
98b0ce39a4
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
- Add sdkVersion in client registration
|
- Add sdkVersion in client registration
|
||||||
|
- disable edit of built-in strategies
|
||||||
|
|
||||||
## 3.0.0-alpha.1
|
## 3.0.0-alpha.1
|
||||||
- upgrade unleash-frontend to 3.0.0-alpha.1
|
- upgrade unleash-frontend to 3.0.0-alpha.1
|
||||||
|
@ -7,7 +7,7 @@ const {
|
|||||||
} = require('../event-type');
|
} = require('../event-type');
|
||||||
const logger = require('../logger');
|
const logger = require('../logger');
|
||||||
const NotFoundError = require('../error/notfound-error');
|
const NotFoundError = require('../error/notfound-error');
|
||||||
const STRATEGY_COLUMNS = ['name', 'description', 'parameters'];
|
const STRATEGY_COLUMNS = ['name', 'description', 'parameters', 'built_in'];
|
||||||
const TABLE = 'strategies';
|
const TABLE = 'strategies';
|
||||||
|
|
||||||
class StrategyStore {
|
class StrategyStore {
|
||||||
@ -46,9 +46,9 @@ class StrategyStore {
|
|||||||
if (!row) {
|
if (!row) {
|
||||||
throw new NotFoundError('No strategy found');
|
throw new NotFoundError('No strategy found');
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: row.name,
|
name: row.name,
|
||||||
|
editable: row.built_in !== 1,
|
||||||
description: row.description,
|
description: row.description,
|
||||||
parameters: row.parameters,
|
parameters: row.parameters,
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,7 @@ const joi = require('joi');
|
|||||||
|
|
||||||
const strategySchema = joi.object().keys({
|
const strategySchema = joi.object().keys({
|
||||||
name: joi.string().regex(/^[a-zA-Z0-9\\.\\-]{3,100}$/).required(),
|
name: joi.string().regex(/^[a-zA-Z0-9\\.\\-]{3,100}$/).required(),
|
||||||
|
editable: joi.boolean().default(true),
|
||||||
description: joi.string(),
|
description: joi.string(),
|
||||||
parameters: joi.array().required().items(
|
parameters: joi.array().required().items(
|
||||||
joi.object().keys({
|
joi.object().keys({
|
||||||
|
@ -33,6 +33,28 @@ const handleError = (req, res, error) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function validateEditable(strategyName) {
|
||||||
|
return strategy => {
|
||||||
|
if (strategy.editable === false) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot edit strategy ${strategyName}, editable is false`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return strategy;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateInput(data) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
joi.validate(data, strategySchema, (err, cleaned) => {
|
||||||
|
if (err) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
return resolve(cleaned);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
exports.router = function(config) {
|
exports.router = function(config) {
|
||||||
const { strategyStore, eventStore } = config.stores;
|
const { strategyStore, eventStore } = config.stores;
|
||||||
const router = Router();
|
const router = Router();
|
||||||
@ -57,6 +79,7 @@ exports.router = function(config) {
|
|||||||
|
|
||||||
strategyStore
|
strategyStore
|
||||||
.getStrategy(strategyName)
|
.getStrategy(strategyName)
|
||||||
|
.then(validateEditable(strategyName))
|
||||||
.then(() =>
|
.then(() =>
|
||||||
eventStore.store({
|
eventStore.store({
|
||||||
type: eventType.STRATEGY_DELETED,
|
type: eventType.STRATEGY_DELETED,
|
||||||
@ -70,6 +93,17 @@ exports.router = function(config) {
|
|||||||
.catch(error => handleError(req, res, error));
|
.catch(error => handleError(req, res, error));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function validateStrategyName(data) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
strategyStore
|
||||||
|
.getStrategy(data.name)
|
||||||
|
.then(() =>
|
||||||
|
reject(new NameExistsError('Feature name already exist'))
|
||||||
|
)
|
||||||
|
.catch(() => resolve(data));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
router.post('/', (req, res) => {
|
router.post('/', (req, res) => {
|
||||||
const data = req.body;
|
const data = req.body;
|
||||||
validateInput(data)
|
validateInput(data)
|
||||||
@ -93,6 +127,7 @@ exports.router = function(config) {
|
|||||||
|
|
||||||
strategyStore
|
strategyStore
|
||||||
.getStrategy(strategyName)
|
.getStrategy(strategyName)
|
||||||
|
.then(validateEditable(strategyName))
|
||||||
.then(() => validateInput(updatedStrategy))
|
.then(() => validateInput(updatedStrategy))
|
||||||
.then(() =>
|
.then(() =>
|
||||||
eventStore.store({
|
eventStore.store({
|
||||||
@ -105,27 +140,5 @@ exports.router = function(config) {
|
|||||||
.catch(error => handleError(req, res, error));
|
.catch(error => handleError(req, res, error));
|
||||||
});
|
});
|
||||||
|
|
||||||
function validateStrategyName(data) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
strategyStore
|
|
||||||
.getStrategy(data.name)
|
|
||||||
.then(() =>
|
|
||||||
reject(new NameExistsError('Feature name already exist'))
|
|
||||||
)
|
|
||||||
.catch(() => resolve(data));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateInput(data) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
joi.validate(data, strategySchema, (err, cleaned) => {
|
|
||||||
if (err) {
|
|
||||||
return reject(err);
|
|
||||||
}
|
|
||||||
return resolve(cleaned);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
};
|
};
|
||||||
|
@ -29,7 +29,7 @@ test.beforeEach(() => {
|
|||||||
logger.setLevel('FATAL');
|
logger.setLevel('FATAL');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should add version numbers for /stategies', t => {
|
test('add version numbers for /stategies', t => {
|
||||||
t.plan(1);
|
t.plan(1);
|
||||||
const { request, base } = getSetup();
|
const { request, base } = getSetup();
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ test('should add version numbers for /stategies', t => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should require a name when creating a new stratey', t => {
|
test('require a name when creating a new stratey', t => {
|
||||||
t.plan(1);
|
t.plan(1);
|
||||||
const { request, base } = getSetup();
|
const { request, base } = getSetup();
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ test('should require a name when creating a new stratey', t => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should require parameters array when creating a new stratey', t => {
|
test('require parameters array when creating a new stratey', t => {
|
||||||
t.plan(1);
|
t.plan(1);
|
||||||
const { request, base } = getSetup();
|
const { request, base } = getSetup();
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ test('should require parameters array when creating a new stratey', t => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should create a new stratey with empty parameters', t => {
|
test('create a new stratey with empty parameters', t => {
|
||||||
t.plan(0);
|
t.plan(0);
|
||||||
const { request, base } = getSetup();
|
const { request, base } = getSetup();
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ test('should create a new stratey with empty parameters', t => {
|
|||||||
.expect(201);
|
.expect(201);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not be possible to override name', t => {
|
test('not be possible to override name', t => {
|
||||||
t.plan(0);
|
t.plan(0);
|
||||||
const { request, base, strategyStore } = getSetup();
|
const { request, base, strategyStore } = getSetup();
|
||||||
strategyStore.addStrategy({ name: 'Testing', parameters: [] });
|
strategyStore.addStrategy({ name: 'Testing', parameters: [] });
|
||||||
@ -89,7 +89,7 @@ test('should not be possible to override name', t => {
|
|||||||
.expect(403);
|
.expect(403);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should update strategy', t => {
|
test('update strategy', t => {
|
||||||
t.plan(0);
|
t.plan(0);
|
||||||
const name = 'AnotherStrat';
|
const name = 'AnotherStrat';
|
||||||
const { request, base, strategyStore } = getSetup();
|
const { request, base, strategyStore } = getSetup();
|
||||||
@ -101,7 +101,7 @@ test('should update strategy', t => {
|
|||||||
.expect(200);
|
.expect(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not update uknown strategy', t => {
|
test('not update uknown strategy', t => {
|
||||||
t.plan(0);
|
t.plan(0);
|
||||||
const name = 'UnknownStrat';
|
const name = 'UnknownStrat';
|
||||||
const { request, base } = getSetup();
|
const { request, base } = getSetup();
|
||||||
@ -112,7 +112,7 @@ test('should not update uknown strategy', t => {
|
|||||||
.expect(404);
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should validate format when updating strategy', t => {
|
test('validate format when updating strategy', t => {
|
||||||
t.plan(0);
|
t.plan(0);
|
||||||
const name = 'AnotherStrat';
|
const name = 'AnotherStrat';
|
||||||
const { request, base, strategyStore } = getSetup();
|
const { request, base, strategyStore } = getSetup();
|
||||||
@ -123,3 +123,46 @@ test('should validate format when updating strategy', t => {
|
|||||||
.send({})
|
.send({})
|
||||||
.expect(400);
|
.expect(400);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('editable=false will stop delete request', t => {
|
||||||
|
t.plan(0);
|
||||||
|
const name = 'default';
|
||||||
|
const { request, base } = getSetup();
|
||||||
|
|
||||||
|
return request.delete(`${base}/api/admin/strategies/${name}`).expect(500);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('editable=false will stop edit request', t => {
|
||||||
|
t.plan(0);
|
||||||
|
const name = 'default';
|
||||||
|
const { request, base } = getSetup();
|
||||||
|
|
||||||
|
return request
|
||||||
|
.put(`${base}/api/admin/strategies/${name}`)
|
||||||
|
.send({ name, parameters: [] })
|
||||||
|
.expect(500);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('editable=true will allow delete request', t => {
|
||||||
|
t.plan(0);
|
||||||
|
const name = 'deleteStrat';
|
||||||
|
const { request, base, strategyStore } = getSetup();
|
||||||
|
strategyStore.addStrategy({ name, parameters: [] });
|
||||||
|
|
||||||
|
return request
|
||||||
|
.delete(`${base}/api/admin/strategies/${name}`)
|
||||||
|
.send({})
|
||||||
|
.expect(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('editable=true will allow edit request', t => {
|
||||||
|
t.plan(0);
|
||||||
|
const name = 'editStrat';
|
||||||
|
const { request, base, strategyStore } = getSetup();
|
||||||
|
strategyStore.addStrategy({ name, parameters: [] });
|
||||||
|
|
||||||
|
return request
|
||||||
|
.put(`${base}/api/admin/strategies/${name}`)
|
||||||
|
.send({ name, parameters: [] })
|
||||||
|
.expect(200);
|
||||||
|
});
|
||||||
|
2
test/fixtures/fake-strategies-store.js
vendored
2
test/fixtures/fake-strategies-store.js
vendored
@ -3,7 +3,7 @@
|
|||||||
const NotFoundError = require('../../lib/error/notfound-error');
|
const NotFoundError = require('../../lib/error/notfound-error');
|
||||||
|
|
||||||
module.exports = () => {
|
module.exports = () => {
|
||||||
const _strategies = [{ name: 'default', parameters: {} }];
|
const _strategies = [{ name: 'default', editable: false, parameters: {} }];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getStrategies: () => Promise.resolve(_strategies),
|
getStrategies: () => Promise.resolve(_strategies),
|
||||||
|
Loading…
Reference in New Issue
Block a user