mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
feat: UI for view, create and edit context fields (#204)
* feat: UI for view, create and edit context fields * fix: lint
This commit is contained in:
parent
03b4ec9751
commit
d7f9b892a3
@ -74,9 +74,8 @@ export const FormButtons = ({ submitText = 'Create', onCancel }) => (
|
||||
{submitText}
|
||||
</Button>
|
||||
|
||||
<Button type="cancel" ripple raised onClick={onCancel} style={{ float: 'right' }}>
|
||||
<Icon name="cancel" />
|
||||
Cancel
|
||||
<Button type="cancel" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
@ -12,3 +12,11 @@ export const formatFullDateTimeWithLocale = (v, locale, tz) => {
|
||||
}
|
||||
return new Date(v).toLocaleString(locale, dateTimeOptions);
|
||||
};
|
||||
|
||||
export const trim = value => {
|
||||
if (value && value.trim) {
|
||||
return value.trim();
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
25
frontend/src/component/context/create-context-container.js
Normal file
25
frontend/src/component/context/create-context-container.js
Normal file
@ -0,0 +1,25 @@
|
||||
import { connect } from 'react-redux';
|
||||
import ContextComponent from './form-context-component';
|
||||
import { createContextField, validateName } from './../../store/context/actions';
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
let contextField = { name: '', description: '', legalValues: [] };
|
||||
if (props.contextFieldName) {
|
||||
contextField = state.context.toJS().find(n => n.name === props.contextFieldName);
|
||||
}
|
||||
return {
|
||||
contextField,
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
validateName,
|
||||
submit: contextField => createContextField(contextField)(dispatch),
|
||||
});
|
||||
|
||||
const FormAddContainer = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ContextComponent);
|
||||
|
||||
export default FormAddContainer;
|
26
frontend/src/component/context/edit-context-container.js
Normal file
26
frontend/src/component/context/edit-context-container.js
Normal file
@ -0,0 +1,26 @@
|
||||
import { connect } from 'react-redux';
|
||||
import ContextComponent from './form-context-component';
|
||||
import { updateContextField, validateName } from './../../store/context/actions';
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
const contextFieldBase = { name: '', description: '', legalValues: [] };
|
||||
const field = state.context.toJS().find(n => n.name === props.contextFieldName);
|
||||
const contextField = Object.assign(contextFieldBase, field);
|
||||
|
||||
return {
|
||||
contextField,
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
validateName,
|
||||
submit: contextField => updateContextField(contextField)(dispatch),
|
||||
editMode: true,
|
||||
});
|
||||
|
||||
const FormAddContainer = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ContextComponent);
|
||||
|
||||
export default FormAddContainer;
|
171
frontend/src/component/context/form-context-component.jsx
Normal file
171
frontend/src/component/context/form-context-component.jsx
Normal file
@ -0,0 +1,171 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Chip, Textfield, Card, CardTitle, CardText, CardActions } from 'react-mdl';
|
||||
|
||||
import { FormButtons, styles as commonStyles } from '../common';
|
||||
import { trim } from '../common/util';
|
||||
|
||||
class AddContextComponent extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
contextField: props.contextField,
|
||||
errors: {},
|
||||
currentLegalValue: '',
|
||||
dirty: false,
|
||||
};
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
if (!state.contextField.name && props.contextField.name) {
|
||||
return { contextField: props.contextField };
|
||||
}
|
||||
}
|
||||
|
||||
setValue = (field, value) => {
|
||||
const { contextField } = this.state;
|
||||
contextField[field] = value;
|
||||
this.setState({ contextField, dirty: true });
|
||||
};
|
||||
|
||||
validateContextName = async name => {
|
||||
const { errors } = this.state;
|
||||
const { validateName } = this.props;
|
||||
try {
|
||||
await validateName(name);
|
||||
errors.name = undefined;
|
||||
} catch (err) {
|
||||
errors.name = err.message;
|
||||
}
|
||||
|
||||
this.setState({ errors });
|
||||
};
|
||||
|
||||
onCancel = evt => {
|
||||
evt.preventDefault();
|
||||
this.props.history.push('/context');
|
||||
};
|
||||
|
||||
onSubmit = evt => {
|
||||
evt.preventDefault();
|
||||
const { contextField } = this.state;
|
||||
this.props.submit(contextField).then(() => this.props.history.push('/context'));
|
||||
};
|
||||
|
||||
updateCurrentLegalValue = evt => {
|
||||
this.setState({ currentLegalValue: trim(evt.target.value) });
|
||||
};
|
||||
|
||||
addLegalValue = evt => {
|
||||
evt.preventDefault();
|
||||
const { contextField, currentLegalValue, errors } = this.state;
|
||||
|
||||
if (contextField.legalValues.indexOf(currentLegalValue) !== -1) {
|
||||
errors.currentLegalValue = 'Duplicate legal value';
|
||||
this.setState({ errors });
|
||||
return;
|
||||
}
|
||||
|
||||
const legalValues = contextField.legalValues.concat(trim(currentLegalValue));
|
||||
contextField.legalValues = legalValues;
|
||||
this.setState({
|
||||
contextField,
|
||||
currentLegalValue: '',
|
||||
errors: {},
|
||||
});
|
||||
};
|
||||
|
||||
removeLegalValue = index => {
|
||||
const { contextField } = this.state;
|
||||
const legalValues = contextField.legalValues.filter((_, i) => i !== index);
|
||||
contextField.legalValues = legalValues;
|
||||
this.setState({ contextField });
|
||||
};
|
||||
|
||||
renderLegalValue = (value, index) => (
|
||||
<Chip
|
||||
key={`${value}:${index}`}
|
||||
className="mdl-color--blue-grey-100"
|
||||
style={{ marginRight: '4px' }}
|
||||
onClose={() => this.removeLegalValue(index)}
|
||||
>
|
||||
{value}
|
||||
</Chip>
|
||||
);
|
||||
|
||||
render() {
|
||||
const { contextField, errors } = this.state;
|
||||
const { editMode } = this.props;
|
||||
const submitText = editMode ? 'Update' : 'Create';
|
||||
|
||||
return (
|
||||
<Card shadow={0} className={commonStyles.fullwidth} style={{ overflow: 'visible' }}>
|
||||
<CardTitle style={{ paddingTop: '24px', paddingBottom: '0', wordBreak: 'break-all' }}>
|
||||
Create context field
|
||||
</CardTitle>
|
||||
<CardText>
|
||||
Context fields are a basic building block used in Unleash to control roll-out. They can be used
|
||||
together with strategy constraints as part of the activation strategy evaluation.
|
||||
</CardText>
|
||||
<form onSubmit={this.onSubmit}>
|
||||
<section style={{ padding: '16px' }}>
|
||||
<Textfield
|
||||
floatingLabel
|
||||
label="Name"
|
||||
name="name"
|
||||
value={contextField.name}
|
||||
error={errors.name}
|
||||
disabled={editMode}
|
||||
onBlur={v => this.validateContextName(v.target.value)}
|
||||
onChange={v => this.setValue('name', trim(v.target.value))}
|
||||
/>
|
||||
<Textfield
|
||||
floatingLabel
|
||||
style={{ width: '100%' }}
|
||||
rows={1}
|
||||
label="Description"
|
||||
error={errors.description}
|
||||
value={contextField.description}
|
||||
onChange={v => this.setValue('description', v.target.value)}
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
<section style={{ padding: '16px', background: '#fafafa' }}>
|
||||
<h6 style={{ marginTop: '0' }}>Legal values</h6>
|
||||
<p style={{ color: 'rgba(0,0,0,.54)' }}>
|
||||
By defining the legal values the Unleash Admin UI will validate the user input. A
|
||||
concrete example would be that we know all values for our “environment” (local,
|
||||
development, stage, production).
|
||||
</p>
|
||||
<Textfield
|
||||
floatingLabel
|
||||
label="Value"
|
||||
name="value"
|
||||
style={{ width: '130px' }}
|
||||
value={this.state.currentLegalValue}
|
||||
error={errors.currentLegalValue}
|
||||
onChange={this.updateCurrentLegalValue}
|
||||
/>
|
||||
<Button onClick={this.addLegalValue}>Add</Button>
|
||||
<div>{contextField.legalValues.map(this.renderLegalValue)}</div>
|
||||
</section>
|
||||
</section>
|
||||
<CardActions>
|
||||
<FormButtons submitText={submitText} onCancel={this.onCancel} />
|
||||
</CardActions>
|
||||
</form>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AddContextComponent.propTypes = {
|
||||
contextField: PropTypes.object.isRequired,
|
||||
validateName: PropTypes.func.isRequired,
|
||||
fetchContext: PropTypes.func.isRequired,
|
||||
submit: PropTypes.func.isRequired,
|
||||
history: PropTypes.object.isRequired,
|
||||
editMode: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default AddContextComponent;
|
80
frontend/src/component/context/list-component.jsx
Normal file
80
frontend/src/component/context/list-component.jsx
Normal file
@ -0,0 +1,80 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { List, ListItem, ListItemAction, ListItemContent, IconButton, Card } from 'react-mdl';
|
||||
import { HeaderTitle, styles as commonStyles } from '../common';
|
||||
import { CREATE_CONTEXT_FIELD, DELETE_CONTEXT_FIELD } from '../../permissions';
|
||||
|
||||
class ContextFieldListComponent extends Component {
|
||||
static propTypes = {
|
||||
contextFields: PropTypes.array.isRequired,
|
||||
fetchContext: PropTypes.func.isRequired,
|
||||
removeContextField: PropTypes.func.isRequired,
|
||||
history: PropTypes.object.isRequired,
|
||||
hasPermission: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
// this.props.fetchContext();
|
||||
}
|
||||
|
||||
removeContextField = (contextField, evt) => {
|
||||
evt.preventDefault();
|
||||
this.props.removeContextField(contextField);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { contextFields, hasPermission } = this.props;
|
||||
|
||||
return (
|
||||
<Card shadow={0} className={commonStyles.fullwidth} style={{ overflow: 'visible' }}>
|
||||
<HeaderTitle
|
||||
title="Context Fields"
|
||||
actions={
|
||||
hasPermission(CREATE_CONTEXT_FIELD) ? (
|
||||
<IconButton
|
||||
raised
|
||||
colored
|
||||
accent
|
||||
name="add"
|
||||
onClick={() => this.props.history.push('/context/create')}
|
||||
title="Add new context field"
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
/>
|
||||
<List>
|
||||
{contextFields.length > 0 ? (
|
||||
contextFields.map((field, i) => (
|
||||
<ListItem key={i} twoLine>
|
||||
<ListItemContent icon="album" subtitle={field.description}>
|
||||
<Link to={`/context/edit/${field.name}`}>
|
||||
<strong>{field.name}</strong>
|
||||
</Link>
|
||||
</ListItemContent>
|
||||
<ListItemAction>
|
||||
{hasPermission(DELETE_CONTEXT_FIELD) ? (
|
||||
<IconButton
|
||||
name="delete"
|
||||
title="Remove contextField"
|
||||
onClick={this.removeContextField.bind(this, field)}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</ListItemAction>
|
||||
</ListItem>
|
||||
))
|
||||
) : (
|
||||
<ListItem>No context fields defined</ListItem>
|
||||
)}
|
||||
</List>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ContextFieldListComponent;
|
30
frontend/src/component/context/list-container.jsx
Normal file
30
frontend/src/component/context/list-container.jsx
Normal file
@ -0,0 +1,30 @@
|
||||
import { connect } from 'react-redux';
|
||||
import ContextFieldListComponent from './list-component.jsx';
|
||||
import { fetchContext, removeContextField } from './../../store/context/actions';
|
||||
import { hasPermission } from '../../permissions';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const list = state.context.toJS();
|
||||
|
||||
return {
|
||||
contextFields: list,
|
||||
hasPermission: hasPermission.bind(null, state.user.get('profile')),
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
removeContextField: contextField => {
|
||||
// eslint-disable-next-line no-alert
|
||||
if (window.confirm('Are you sure you want to remove this context field?')) {
|
||||
removeContextField(contextField)(dispatch);
|
||||
}
|
||||
},
|
||||
fetchContext: () => fetchContext()(dispatch),
|
||||
});
|
||||
|
||||
const ContextFieldListContainer = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ContextFieldListComponent);
|
||||
|
||||
export default ContextFieldListContainer;
|
@ -145,19 +145,9 @@ exports[`renders correctly with with variants 1`] = `
|
||||
|
||||
<react-mdl-Button
|
||||
onClick={[MockFunction]}
|
||||
raised={true}
|
||||
ripple={true}
|
||||
style={
|
||||
Object {
|
||||
"float": "right",
|
||||
}
|
||||
}
|
||||
type="cancel"
|
||||
>
|
||||
<react-mdl-Icon
|
||||
name="cancel"
|
||||
/>
|
||||
Cancel
|
||||
Cancel
|
||||
</react-mdl-Button>
|
||||
</div>
|
||||
</form>
|
||||
@ -225,19 +215,9 @@ exports[`renders correctly with without variants 1`] = `
|
||||
|
||||
<react-mdl-Button
|
||||
onClick={[MockFunction]}
|
||||
raised={true}
|
||||
ripple={true}
|
||||
style={
|
||||
Object {
|
||||
"float": "right",
|
||||
}
|
||||
}
|
||||
type="cancel"
|
||||
>
|
||||
<react-mdl-Icon
|
||||
name="cancel"
|
||||
/>
|
||||
Cancel
|
||||
Cancel
|
||||
</react-mdl-Button>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -121,6 +121,25 @@ Array [
|
||||
"path": "/applications",
|
||||
"title": "Applications",
|
||||
},
|
||||
Object {
|
||||
"component": [Function],
|
||||
"parent": "/context",
|
||||
"path": "/context/create",
|
||||
"title": "Create",
|
||||
},
|
||||
Object {
|
||||
"component": [Function],
|
||||
"parent": "/context",
|
||||
"path": "/context/edit/:name",
|
||||
"title": ":name",
|
||||
},
|
||||
Object {
|
||||
"component": [Function],
|
||||
"hidden": true,
|
||||
"icon": "apps",
|
||||
"path": "/context",
|
||||
"title": "Context Fields",
|
||||
},
|
||||
Object {
|
||||
"component": [Function],
|
||||
"icon": "exit_to_app",
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { routes, baseRoutes, getRoute } from '../routes';
|
||||
|
||||
test('returns all defined routes', () => {
|
||||
expect(routes.length).toEqual(14);
|
||||
expect(routes.length).toEqual(17);
|
||||
expect(routes).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
@ -11,6 +11,9 @@ import ShowArchive from '../../page/archive/show';
|
||||
import Archive from '../../page/archive';
|
||||
import Applications from '../../page/applications';
|
||||
import ApplicationView from '../../page/applications/view';
|
||||
import ContextFields from '../../page/context';
|
||||
import CreateContextField from '../../page/context/create';
|
||||
import EditContextField from '../../page/context/edit';
|
||||
import LogoutFeatures from '../../page/user/logout';
|
||||
|
||||
export const routes = [
|
||||
@ -42,9 +45,14 @@ export const routes = [
|
||||
{ path: '/applications/:name', title: ':name', parent: '/applications', component: ApplicationView },
|
||||
{ path: '/applications', title: 'Applications', icon: 'apps', component: Applications },
|
||||
|
||||
// Context
|
||||
{ path: '/context/create', parent: '/context', title: 'Create', component: CreateContextField },
|
||||
{ path: '/context/edit/:name', parent: '/context', title: ':name', component: EditContextField },
|
||||
{ path: '/context', title: 'Context Fields', icon: 'apps', component: ContextFields, hidden: true },
|
||||
|
||||
{ path: '/logout', title: 'Sign out', icon: 'exit_to_app', component: LogoutFeatures },
|
||||
];
|
||||
|
||||
export const getRoute = path => routes.find(route => route.path === path);
|
||||
|
||||
export const baseRoutes = routes.filter(route => !route.parent);
|
||||
export const baseRoutes = routes.filter(route => !route.hidden).filter(route => !route.parent);
|
||||
|
@ -1,13 +1,52 @@
|
||||
import { throwIfNotSuccess } from './helper';
|
||||
import { throwIfNotSuccess, headers } from './helper';
|
||||
|
||||
const URI = 'api/admin/context';
|
||||
|
||||
function fetchContext() {
|
||||
function fetchAll() {
|
||||
return fetch(URI, { credentials: 'include' })
|
||||
.then(throwIfNotSuccess)
|
||||
.then(response => response.json());
|
||||
}
|
||||
|
||||
function create(contextField) {
|
||||
return fetch(URI, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify(contextField),
|
||||
credentials: 'include',
|
||||
}).then(throwIfNotSuccess);
|
||||
}
|
||||
|
||||
function update(contextField) {
|
||||
return fetch(`${URI}/${contextField.name}`, {
|
||||
method: 'PUT',
|
||||
headers,
|
||||
body: JSON.stringify(contextField),
|
||||
credentials: 'include',
|
||||
}).then(throwIfNotSuccess);
|
||||
}
|
||||
|
||||
function remove(contextField) {
|
||||
return fetch(`${URI}/${contextField.name}`, {
|
||||
method: 'DELETE',
|
||||
headers,
|
||||
credentials: 'include',
|
||||
}).then(throwIfNotSuccess);
|
||||
}
|
||||
|
||||
function validate(name) {
|
||||
return fetch(`${URI}/validate`, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(name),
|
||||
}).then(throwIfNotSuccess);
|
||||
}
|
||||
|
||||
export default {
|
||||
fetchContext,
|
||||
fetchAll,
|
||||
create,
|
||||
update,
|
||||
remove,
|
||||
validate,
|
||||
};
|
||||
|
11
frontend/src/page/context/create.js
Normal file
11
frontend/src/page/context/create.js
Normal file
@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import CreateContextField from '../../component/context/create-context-container';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const render = ({ history }) => <CreateContextField title="Create context field" history={history} />;
|
||||
|
||||
render.propTypes = {
|
||||
history: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default render;
|
14
frontend/src/page/context/edit.js
Normal file
14
frontend/src/page/context/edit.js
Normal file
@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
import CreateContextField from '../../component/context/edit-context-container';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const render = ({ match: { params }, history }) => (
|
||||
<CreateContextField contextFieldName={params.name} title="Edit context field" history={history} />
|
||||
);
|
||||
|
||||
render.propTypes = {
|
||||
match: PropTypes.object.isRequired,
|
||||
history: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default render;
|
11
frontend/src/page/context/index.js
Normal file
11
frontend/src/page/context/index.js
Normal file
@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import ContextFields from '../../component/context/list-container';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const render = ({ history }) => <ContextFields history={history} />;
|
||||
|
||||
render.propTypes = {
|
||||
history: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default render;
|
@ -6,6 +6,9 @@ export const CREATE_STRATEGY = 'CREATE_STRATEGY';
|
||||
export const UPDATE_STRATEGY = 'UPDATE_STRATEGY';
|
||||
export const DELETE_STRATEGY = 'DELETE_STRATEGY';
|
||||
export const UPDATE_APPLICATION = 'UPDATE_APPLICATION';
|
||||
export const CREATE_CONTEXT_FIELD = 'CREATE_CONTEXT_FIELD';
|
||||
export const UPDATE_CONTEXT_FIELD = 'UPDATE_CONTEXT_FIELD';
|
||||
export const DELETE_CONTEXT_FIELD = 'DELETE_CONTEXT_FIELD';
|
||||
|
||||
export function hasPermission(user, permission) {
|
||||
return (
|
||||
|
@ -3,16 +3,50 @@ import { dispatchAndThrow } from '../util';
|
||||
|
||||
export const RECEIVE_CONTEXT = 'RECEIVE_CONTEXT';
|
||||
export const ERROR_RECEIVE_CONTEXT = 'ERROR_RECEIVE_CONTEXT';
|
||||
export const REMOVE_CONTEXT = 'REMOVE_CONTEXT';
|
||||
export const ERROR_REMOVING_CONTEXT = 'ERROR_REMOVING_CONTEXT';
|
||||
export const ADD_CONTEXT_FIELD = 'ADD_CONTEXT_FIELD';
|
||||
export const ERROR_ADD_CONTEXT_FIELD = 'ERROR_ADD_CONTEXT_FIELD';
|
||||
export const UPDATE_CONTEXT_FIELD = 'UPDATE_CONTEXT_FIELD';
|
||||
export const ERROR_UPDATE_CONTEXT_FIELD = 'ERROR_UPDATE_CONTEXT_FIELD';
|
||||
|
||||
export const receiveContext = json => ({
|
||||
type: RECEIVE_CONTEXT,
|
||||
value: json,
|
||||
});
|
||||
const receiveContext = value => ({ type: RECEIVE_CONTEXT, value });
|
||||
const addContextField = context => ({ type: ADD_CONTEXT_FIELD, context });
|
||||
const upContextField = context => ({ type: UPDATE_CONTEXT_FIELD, context });
|
||||
const createRemoveContext = context => ({ type: REMOVE_CONTEXT, context });
|
||||
|
||||
export function fetchContext() {
|
||||
return dispatch =>
|
||||
api
|
||||
.fetchContext()
|
||||
.fetchAll()
|
||||
.then(json => dispatch(receiveContext(json)))
|
||||
.catch(dispatchAndThrow(dispatch, ERROR_RECEIVE_CONTEXT));
|
||||
}
|
||||
|
||||
export function removeContextField(context) {
|
||||
return dispatch =>
|
||||
api
|
||||
.remove(context)
|
||||
.then(() => dispatch(createRemoveContext(context)))
|
||||
.catch(dispatchAndThrow(dispatch, ERROR_REMOVING_CONTEXT));
|
||||
}
|
||||
|
||||
export function createContextField(context) {
|
||||
return dispatch =>
|
||||
api
|
||||
.create(context)
|
||||
.then(() => dispatch(addContextField(context)))
|
||||
.catch(dispatchAndThrow(dispatch, ERROR_ADD_CONTEXT_FIELD));
|
||||
}
|
||||
|
||||
export function updateContextField(context) {
|
||||
return dispatch =>
|
||||
api
|
||||
.update(context)
|
||||
.then(() => dispatch(upContextField(context)))
|
||||
.catch(dispatchAndThrow(dispatch, ERROR_UPDATE_CONTEXT_FIELD));
|
||||
}
|
||||
|
||||
export function validateName(name) {
|
||||
return api.validate({ name });
|
||||
}
|
||||
|
@ -1,15 +1,24 @@
|
||||
import { RECEIVE_CONTEXT } from './actions';
|
||||
import { List } from 'immutable';
|
||||
import { RECEIVE_CONTEXT, REMOVE_CONTEXT, ADD_CONTEXT_FIELD, UPDATE_CONTEXT_FIELD } from './actions';
|
||||
|
||||
const DEFAULT_CONTEXT_FIELDS = [{ name: 'environment' }, { name: 'userId' }, { name: 'appName' }];
|
||||
|
||||
function getInitState() {
|
||||
return DEFAULT_CONTEXT_FIELDS;
|
||||
return new List(DEFAULT_CONTEXT_FIELDS);
|
||||
}
|
||||
|
||||
const strategies = (state = getInitState(), action) => {
|
||||
switch (action.type) {
|
||||
case RECEIVE_CONTEXT:
|
||||
return action.value;
|
||||
return new List(action.value);
|
||||
case REMOVE_CONTEXT:
|
||||
return state.remove(state.indexOf(action.context));
|
||||
case ADD_CONTEXT_FIELD:
|
||||
return state.push(action.context);
|
||||
case UPDATE_CONTEXT_FIELD: {
|
||||
const index = state.findIndex(item => item.name === action.context.name);
|
||||
return state.set(index, action.context);
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ import {
|
||||
|
||||
import { ERROR_UPDATING_STRATEGY, ERROR_CREATING_STRATEGY, ERROR_RECEIVE_STRATEGIES } from './strategy/actions';
|
||||
|
||||
import { ERROR_ADD_CONTEXT_FIELD, ERROR_UPDATE_CONTEXT_FIELD } from './context/actions';
|
||||
|
||||
import { FORBIDDEN } from './util';
|
||||
|
||||
const debug = require('debug')('unleash:error-store');
|
||||
@ -37,6 +39,8 @@ const strategies = (state = getInitState(), action) => {
|
||||
case ERROR_UPDATING_STRATEGY:
|
||||
case ERROR_CREATING_STRATEGY:
|
||||
case ERROR_RECEIVE_STRATEGIES:
|
||||
case ERROR_ADD_CONTEXT_FIELD:
|
||||
case ERROR_UPDATE_CONTEXT_FIELD:
|
||||
return addErrorIfNotAlreadyInList(state, action.error.message);
|
||||
case FORBIDDEN:
|
||||
return addErrorIfNotAlreadyInList(state, action.error.message || '403 Forbidden');
|
||||
|
@ -20,10 +20,7 @@ const updatedStrategy = strategy => ({ type: UPDATE_STRATEGY, strategy });
|
||||
|
||||
const startRequest = () => ({ type: REQUEST_STRATEGIES });
|
||||
|
||||
const receiveStrategies = json => ({
|
||||
type: RECEIVE_STRATEGIES,
|
||||
value: json.strategies,
|
||||
});
|
||||
const receiveStrategies = json => ({ type: RECEIVE_STRATEGIES, value: json.strategies });
|
||||
|
||||
const startCreate = () => ({ type: START_CREATE_STRATEGY });
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user