diff --git a/packages/unleash-api/lib/routes/feature.js b/packages/unleash-api/lib/routes/feature.js index 2569a00ad2..4706a88718 100644 --- a/packages/unleash-api/lib/routes/feature.js +++ b/packages/unleash-api/lib/routes/feature.js @@ -29,6 +29,26 @@ module.exports = function (app, config) { .catch(() => res.status(404).json({ error: 'Could not find feature' })); }); + app.post('/features-validate', (req, res) => { + req.checkBody('name', 'Name is required').notEmpty(); + req.checkBody('name', 'Name must match format ^[0-9a-zA-Z\\.\\-]+$').matches(/^[0-9a-zA-Z\\.\\-]+$/i); + + validateRequest(req) + .then(validateFormat) + .then(validateUniqueName) + .then(() => res.status(201).end()) + .catch(NameExistsError, () => { + res.status(403) + .json([{ msg: `A feature named '${req.body.name}' already exists.` }]) + .end(); + }) + .catch(ValidationError, () => res.status(400).json(req.validationErrors())) + .catch(err => { + logger.error('Could not create feature toggle', err); + res.status(500).end(); + }); + }); + app.post('/features', (req, res) => { req.checkBody('name', 'Name is required').notEmpty(); req.checkBody('name', 'Name must match format ^[0-9a-zA-Z\\.\\-]+$').matches(/^[0-9a-zA-Z\\.\\-]+$/i); diff --git a/packages/unleash-frontend-next/src/component/feature/form-add-container.jsx b/packages/unleash-frontend-next/src/component/feature/form-add-container.jsx index 9cb6aeef63..8a2ab56105 100644 --- a/packages/unleash-frontend-next/src/component/feature/form-add-container.jsx +++ b/packages/unleash-frontend-next/src/component/feature/form-add-container.jsx @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { hashHistory } from 'react-router'; -import { createFeatureToggles } from '../../store/feature-actions'; +import { createFeatureToggles, validateName } from '../../store/feature-actions'; import { createMapper, createActions } from '../input-helpers'; import FormComponent from './form'; @@ -33,6 +33,13 @@ const prepare = (methods, dispatch) => { methods.removeFromList('strategies', v); }; + methods.validateName = (v) => { + const featureToggleName = v.target.value; + validateName(featureToggleName) + .then(() => methods.setValue('nameError', undefined)) + .catch((err) => methods.setValue('nameError', err.message)); + }; + return methods; }; const actions = createActions({ id: ID, prepare }); diff --git a/packages/unleash-frontend-next/src/component/feature/form-edit-container.jsx b/packages/unleash-frontend-next/src/component/feature/form-edit-container.jsx index 26b262b658..b9264f7b82 100644 --- a/packages/unleash-frontend-next/src/component/feature/form-edit-container.jsx +++ b/packages/unleash-frontend-next/src/component/feature/form-edit-container.jsx @@ -57,6 +57,8 @@ const prepare = (methods, dispatch) => { methods.updateInList('strategies', v, n); }; + methods.validateName = () => {}; + return methods; }; diff --git a/packages/unleash-frontend-next/src/component/feature/form/index.jsx b/packages/unleash-frontend-next/src/component/feature/form/index.jsx index 340a542763..f292085a01 100644 --- a/packages/unleash-frontend-next/src/component/feature/form/index.jsx +++ b/packages/unleash-frontend-next/src/component/feature/form/index.jsx @@ -17,6 +17,7 @@ class AddFeatureToggleComponent extends Component { const { input, setValue, + validateName, addStrategy, removeStrategy, updateStrategy, @@ -27,6 +28,7 @@ class AddFeatureToggleComponent extends Component { const { name, // eslint-disable-line + nameError, description, enabled, } = input; @@ -42,6 +44,8 @@ class AddFeatureToggleComponent extends Component { disabled={editmode} required value={name} + error={nameError} + onBlur={(v) => validateName(v)} onChange={(v) => setValue('name', v)} /> 400 && response.status < 404) { + if (response.status > 399 && response.status < 404) { return new Promise((resolve, reject) => { response.json().then(body => { const errorMsg = body && body.length > 0 ? body[0].msg : defaultErrorMessage; diff --git a/packages/unleash-frontend-next/src/store/feature-actions.js b/packages/unleash-frontend-next/src/store/feature-actions.js index 06db2b5ef1..c21304f8b7 100644 --- a/packages/unleash-frontend-next/src/store/feature-actions.js +++ b/packages/unleash-frontend-next/src/store/feature-actions.js @@ -88,3 +88,6 @@ export function removeFeatureToggle (featureToggleName) { }; } +export function validateName (featureToggleName) { + return api.validate({ name: featureToggleName }); +}