mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	Add schema validation for strategies
This commit is contained in:
		
							parent
							
								
									ba5810506f
								
							
						
					
					
						commit
						c8a9b39f27
					
				
							
								
								
									
										20
									
								
								lib/routes/strategy-schema.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								lib/routes/strategy-schema.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const joi = require('joi');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const strategySchema = joi.object().keys({
 | 
				
			||||||
 | 
					    name: joi.string()
 | 
				
			||||||
 | 
					        .regex(/^[a-zA-Z0-9\\.\\-]{3,30}$/)
 | 
				
			||||||
 | 
					        .required(),
 | 
				
			||||||
 | 
					    description: joi.string(),
 | 
				
			||||||
 | 
					    parameters: joi.array()
 | 
				
			||||||
 | 
					        .required()
 | 
				
			||||||
 | 
					        .items(joi.object().keys({
 | 
				
			||||||
 | 
					            name: joi.string().required(),
 | 
				
			||||||
 | 
					            type: joi.string().required(),
 | 
				
			||||||
 | 
					            description: joi.string(),
 | 
				
			||||||
 | 
					            required: joi.boolean(),
 | 
				
			||||||
 | 
					        })),
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = strategySchema;
 | 
				
			||||||
@ -1,29 +1,28 @@
 | 
				
			|||||||
'use strict';
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const joi = require('joi');
 | 
				
			||||||
const eventType = require('../event-type');
 | 
					const eventType = require('../event-type');
 | 
				
			||||||
const logger = require('../logger');
 | 
					const logger = require('../logger');
 | 
				
			||||||
const NameExistsError = require('../error/name-exists-error');
 | 
					const NameExistsError = require('../error/name-exists-error');
 | 
				
			||||||
const ValidationError = require('../error/validation-error.js');
 | 
					 | 
				
			||||||
const NotFoundError = require('../error/notfound-error');
 | 
					 | 
				
			||||||
const validateRequest = require('../error/validate-request');
 | 
					 | 
				
			||||||
const extractUser = require('../extract-user');
 | 
					const extractUser = require('../extract-user');
 | 
				
			||||||
 | 
					const strategySchema = require('./strategy-schema');
 | 
				
			||||||
const version = 1;
 | 
					const version = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const handleError = (req, res, error) => {
 | 
					const handleError = (req, res, error) => {
 | 
				
			||||||
    switch (error.constructor) {
 | 
					    switch (error.name) {
 | 
				
			||||||
        case NotFoundError:
 | 
					        case 'NotFoundError':
 | 
				
			||||||
            return res
 | 
					            return res
 | 
				
			||||||
                .status(404)
 | 
					                .status(404)
 | 
				
			||||||
                .end();
 | 
					                .end();
 | 
				
			||||||
        case NameExistsError:
 | 
					        case 'NameExistsError':
 | 
				
			||||||
            return res
 | 
					            return res
 | 
				
			||||||
                .status(403)
 | 
					                .status(403)
 | 
				
			||||||
                .json([{ msg: `A strategy named '${req.body.name}' already exists.` }])
 | 
					                .json([{ msg: `A strategy named '${req.body.name}' already exists.` }])
 | 
				
			||||||
                .end();
 | 
					                .end();
 | 
				
			||||||
        case ValidationError:
 | 
					        case 'ValidationError':
 | 
				
			||||||
            return res
 | 
					            return res
 | 
				
			||||||
                .status(400)
 | 
					                .status(400)
 | 
				
			||||||
                .json(req.validationErrors())
 | 
					                .json(error)
 | 
				
			||||||
                .end();
 | 
					                .end();
 | 
				
			||||||
        default:
 | 
					        default:
 | 
				
			||||||
            logger.error('Could perfom operation', error);
 | 
					            logger.error('Could perfom operation', error);
 | 
				
			||||||
@ -64,14 +63,10 @@ module.exports = function (app, config) {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    app.post('/strategies', (req, res) => {
 | 
					    app.post('/strategies', (req, res) => {
 | 
				
			||||||
        req.checkBody('name', 'Name is required').notEmpty();
 | 
					        const data = req.body;
 | 
				
			||||||
        req.checkBody('name', 'Name must match format ^[0-9a-zA-Z\\.\\-]+$').matches(/^[0-9a-zA-Z\\.\\-]+$/i);
 | 
					        validateInput(data)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        const newStrategy = req.body;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        validateRequest(req)
 | 
					 | 
				
			||||||
            .then(validateStrategyName)
 | 
					            .then(validateStrategyName)
 | 
				
			||||||
            .then(() => eventStore.store({
 | 
					            .then((newStrategy) => eventStore.store({
 | 
				
			||||||
                type: eventType.STRATEGY_CREATED,
 | 
					                type: eventType.STRATEGY_CREATED,
 | 
				
			||||||
                createdBy: extractUser(req),
 | 
					                createdBy: extractUser(req),
 | 
				
			||||||
                data: newStrategy,
 | 
					                data: newStrategy,
 | 
				
			||||||
@ -80,11 +75,22 @@ module.exports = function (app, config) {
 | 
				
			|||||||
            .catch(error => handleError(req, res, error));
 | 
					            .catch(error => handleError(req, res, error));
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function validateStrategyName (req) {
 | 
					    function validateStrategyName (data) {
 | 
				
			||||||
        return new Promise((resolve, reject) => {
 | 
					        return new Promise((resolve, reject) => {
 | 
				
			||||||
            strategyStore.getStrategy(req.body.name)
 | 
					            strategyStore.getStrategy(data.name)
 | 
				
			||||||
                .then(() => reject(new NameExistsError('Feature name already exist')))
 | 
					                .then(() => reject(new NameExistsError('Feature name already exist')))
 | 
				
			||||||
                .catch(() => resolve(req));
 | 
					                .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);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const { getInstance } = require('db-migrate');
 | 
					const { getInstance } = require('db-migrate');
 | 
				
			||||||
const parseDbUrl = require('parse-database-url');
 | 
					const parseDbUrl = require('parse-database-url');
 | 
				
			||||||
 | 
					require('db-migrate-shared').log.silence(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function migrateDb ({ databaseUrl, databaseSchema = 'public' }) {
 | 
					function migrateDb ({ databaseUrl, databaseSchema = 'public' }) {
 | 
				
			||||||
    const custom = parseDbUrl(databaseUrl);
 | 
					    const custom = parseDbUrl(databaseUrl);
 | 
				
			||||||
 | 
				
			|||||||
@ -42,7 +42,7 @@ test.serial('creates a new strategy', async (t) => {
 | 
				
			|||||||
    const { request, destroy } = await setupApp('strategy_api_serial');
 | 
					    const { request, destroy } = await setupApp('strategy_api_serial');
 | 
				
			||||||
    return request
 | 
					    return request
 | 
				
			||||||
        .post('/api/strategies')
 | 
					        .post('/api/strategies')
 | 
				
			||||||
        .send({ name: 'myCustomStrategy', description: 'Best strategy ever.' })
 | 
					        .send({ name: 'myCustomStrategy', description: 'Best strategy ever.', parameters: [] })
 | 
				
			||||||
        .set('Content-Type', 'application/json')
 | 
					        .set('Content-Type', 'application/json')
 | 
				
			||||||
        .expect(201)
 | 
					        .expect(201)
 | 
				
			||||||
        .then(destroy);
 | 
					        .then(destroy);
 | 
				
			||||||
@ -62,7 +62,7 @@ test.serial('refuses to create a strategy with an existing name', async (t) => {
 | 
				
			|||||||
    const { request, destroy } = await setupApp('strategy_api_serial');
 | 
					    const { request, destroy } = await setupApp('strategy_api_serial');
 | 
				
			||||||
    return request
 | 
					    return request
 | 
				
			||||||
        .post('/api/strategies')
 | 
					        .post('/api/strategies')
 | 
				
			||||||
        .send({ name: 'default' })
 | 
					        .send({ name: 'default', parameters: [] })
 | 
				
			||||||
        .set('Content-Type', 'application/json')
 | 
					        .set('Content-Type', 'application/json')
 | 
				
			||||||
        .expect(403)
 | 
					        .expect(403)
 | 
				
			||||||
        .then(destroy);
 | 
					        .then(destroy);
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user