1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-06-18 01:18:23 +02:00

feature: Add support for permission system in unleash frontend

refactored PermissionComponent to pure component
fixed feature-list-item-component
This commit is contained in:
Benjamin Ludewig 2019-01-02 10:21:06 +01:00
parent 1eb8fc0464
commit aad612d3d6
6 changed files with 113 additions and 94 deletions

View File

@ -1,47 +1,21 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { ADMIN } from '../../permissions'; import { ADMIN } from '../../permissions';
class PermissionComponent extends Component { const PermissionComponent = ({ user, permission, component, otherwise }) => {
static propTypes = { if (
user &&
(!user.permissions || user.permissions.indexOf(ADMIN) !== -1 || user.permissions.indexOf(permission) !== -1)
) {
return component;
}
return otherwise || '';
};
PermissionComponent.propTypes = {
user: PropTypes.object, user: PropTypes.object,
component: PropTypes.oneOfType([PropTypes.node, PropTypes.func]), component: PropTypes.node,
others: PropTypes.object,
denied: PropTypes.object,
granted: PropTypes.object,
otherwise: PropTypes.node, otherwise: PropTypes.node,
permission: PropTypes.string, permission: PropTypes.string,
children: PropTypes.node, };
};
render() {
const { user, otherwise, component: Component, permission, granted, denied, children, ...others } = this.props;
let grantedComponent = Component;
let deniedCompoinent = otherwise || '';
if (granted || denied) {
grantedComponent = (
<Component {...others} {...granted || {}}>
{children}
</Component>
);
deniedCompoinent = (
<Component {...others} {...denied || {}}>
{children}
</Component>
);
}
if (!user) return deniedCompoinent;
if (
!user.permissions ||
user.permissions.indexOf(ADMIN) !== -1 ||
user.permissions.indexOf(permission) !== -1
) {
return grantedComponent;
}
return deniedCompoinent;
}
}
export default PermissionComponent; export default PermissionComponent;

View File

@ -22,7 +22,7 @@ exports[`renders correctly with one feature 1`] = `
> >
<react-mdl-Switch <react-mdl-Switch
checked={false} checked={false}
disabled={true} disabled={false}
onChange={[Function]} onChange={[Function]}
title="Toggle Another" title="Toggle Another"
/> />

View File

@ -1,8 +1,12 @@
import React from 'react'; import React from 'react';
import { MemoryRouter } from 'react-router-dom'; import { MemoryRouter } from 'react-router-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import { Map as $Map } from 'immutable';
import Feature from './../feature-list-item-component'; import Feature from './../feature-list-item-component';
import renderer from 'react-test-renderer'; import renderer from 'react-test-renderer';
import { UPDATE_FEATURE } from '../../../permissions';
jest.mock('react-mdl'); jest.mock('react-mdl');
@ -21,9 +25,11 @@ test('renders correctly with one feature', () => {
], ],
createdAt: '2018-02-04T20:27:52.127Z', createdAt: '2018-02-04T20:27:52.127Z',
}; };
const store = { user: new $Map({ profile: { permissions: [UPDATE_FEATURE] } }) };
const featureMetrics = { lastHour: {}, lastMinute: {}, seenApps: {} }; const featureMetrics = { lastHour: {}, lastMinute: {}, seenApps: {} };
const settings = { sort: 'name' }; const settings = { sort: 'name' };
const tree = renderer.create( const tree = renderer.create(
<Provider store={createStore(state => state, store)}>
<MemoryRouter> <MemoryRouter>
<Feature <Feature
key={0} key={0}
@ -34,6 +40,7 @@ test('renders correctly with one feature', () => {
toggleFeature={jest.fn()} toggleFeature={jest.fn()}
/> />
</MemoryRouter> </MemoryRouter>
</Provider>
); );
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();

View File

@ -3,6 +3,8 @@ import PropTypes from 'prop-types';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Switch, Chip, ListItem, ListItemAction, Icon } from 'react-mdl'; import { Switch, Chip, ListItem, ListItemAction, Icon } from 'react-mdl';
import Progress from './progress'; import Progress from './progress';
import PermissionComponent from '../common/permission-container';
import { UPDATE_FEATURE } from '../../permissions';
import { calc, styles as commonStyles } from '../common'; import { calc, styles as commonStyles } from '../common';
import styles from './feature.scss'; import styles from './feature.scss';
@ -14,7 +16,6 @@ const Feature = ({
metricsLastHour = { yes: 0, no: 0, isFallback: true }, metricsLastHour = { yes: 0, no: 0, isFallback: true },
metricsLastMinute = { yes: 0, no: 0, isFallback: true }, metricsLastMinute = { yes: 0, no: 0, isFallback: true },
revive, revive,
updateable,
}) => { }) => {
const { name, description, enabled, strategies } = feature; const { name, description, enabled, strategies } = feature;
const { showLastHour = false } = settings; const { showLastHour = false } = settings;
@ -42,13 +43,27 @@ const Feature = ({
<Progress strokeWidth={15} percentage={percent} isFallback={isStale} /> <Progress strokeWidth={15} percentage={percent} isFallback={isStale} />
</span> </span>
<span className={styles.listItemToggle}> <span className={styles.listItemToggle}>
<PermissionComponent
permission={UPDATE_FEATURE}
component={
<Switch <Switch
disabled={!updateable || toggleFeature === undefined} disabled={toggleFeature === undefined}
title={`Toggle ${name}`} title={`Toggle ${name}`}
key="left-actions" key="left-actions"
onChange={() => toggleFeature(name)} onChange={() => toggleFeature(name)}
checked={enabled} checked={enabled}
/> />
}
otherwise={
<Switch
disabled
title={`Toggle ${name}`}
key="left-actions"
onChange={() => toggleFeature(name)}
checked={enabled}
/>
}
/>
</span> </span>
<span className={['mdl-list__item-primary-content', styles.listItemLink].join(' ')}> <span className={['mdl-list__item-primary-content', styles.listItemLink].join(' ')}>
<Link to={featureUrl} className={[commonStyles.listLink, commonStyles.truncate].join(' ')}> <Link to={featureUrl} className={[commonStyles.listLink, commonStyles.truncate].join(' ')}>
@ -60,10 +75,16 @@ const Feature = ({
{strategyChips} {strategyChips}
{summaryChip} {summaryChip}
</span> </span>
{updateable && revive ? ( {revive ? (
<PermissionComponent
permission={UPDATE_FEATURE}
component={
<ListItemAction onClick={() => revive(feature.name)}> <ListItemAction onClick={() => revive(feature.name)}>
<Icon name="undo" /> <Icon name="undo" />
</ListItemAction> </ListItemAction>
}
otherwise={<span />}
/>
) : ( ) : (
<span /> <span />
)} )}

View File

@ -8,10 +8,10 @@ import { HeaderTitle } from '../../common';
class StrategiesSectionComponent extends React.Component { class StrategiesSectionComponent extends React.Component {
static propTypes = { static propTypes = {
strategies: PropTypes.array.isRequired, strategies: PropTypes.array.isRequired,
addStrategy: PropTypes.func.isRequired, addStrategy: PropTypes.func,
removeStrategy: PropTypes.func.isRequired, removeStrategy: PropTypes.func,
updateStrategy: PropTypes.func.isRequired, updateStrategy: PropTypes.func,
fetchStrategies: PropTypes.func.isRequired, fetchStrategies: PropTypes.func,
}; };
componentWillMount() { componentWillMount() {

View File

@ -155,20 +155,29 @@ export default class ViewFeatureToggleComponent extends React.Component {
{this.isFeatureView ? ( {this.isFeatureView ? (
<PermissionComponent <PermissionComponent
permission={UPDATE_FEATURE} permission={UPDATE_FEATURE}
component={Textfield} component={
granted={{ <Textfield
onChange: v => setValue('description', v),
onBlur: updateFeatureToggle,
}}
denied={{
disabled: true,
}}
floatingLabel floatingLabel
style={{ width: '100%' }} style={{ width: '100%' }}
rows={1} rows={1}
label="Description" label="Description"
required required
value={featureToggle.description} value={featureToggle.description}
onChange={v => setValue('description', v)}
onBlur={updateFeatureToggle}
/>
}
otherwise={
<Textfield
disabled
floatingLabel
style={{ width: '100%' }}
rows={1}
label="Description"
required
value={featureToggle.description}
/>
}
/> />
) : ( ) : (
<Textfield <Textfield
@ -194,19 +203,27 @@ export default class ViewFeatureToggleComponent extends React.Component {
<span style={{ paddingRight: '24px' }}> <span style={{ paddingRight: '24px' }}>
<PermissionComponent <PermissionComponent
permission={UPDATE_FEATURE} permission={UPDATE_FEATURE}
component={Switch} component={
granted={{ <Switch
disabled: !this.isFeatureView, disabled={!this.isFeatureView}
}}
denied={{
disabled: true,
}}
ripple ripple
checked={featureToggle.enabled} checked={featureToggle.enabled}
onChange={() => toggleFeature(featureToggle.name)} onChange={() => toggleFeature(featureToggle.name)}
> >
{featureToggle.enabled ? 'Enabled' : 'Disabled'} {featureToggle.enabled ? 'Enabled' : 'Disabled'}
</PermissionComponent> </Switch>
}
otherwise={
<Switch
disabled
ripple
checked={featureToggle.enabled}
onChange={() => toggleFeature(featureToggle.name)}
>
{featureToggle.enabled ? 'Enabled' : 'Disabled'}
</Switch>
}
/>
</span> </span>
{this.isFeatureView ? ( {this.isFeatureView ? (