1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-04-29 01:15:48 +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 { ADMIN } from '../../permissions';
class PermissionComponent extends Component {
static propTypes = {
user: PropTypes.object,
component: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
others: PropTypes.object,
denied: PropTypes.object,
granted: PropTypes.object,
otherwise: PropTypes.node,
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;
const PermissionComponent = ({ user, permission, component, otherwise }) => {
if (
user &&
(!user.permissions || user.permissions.indexOf(ADMIN) !== -1 || user.permissions.indexOf(permission) !== -1)
) {
return component;
}
}
return otherwise || '';
};
PermissionComponent.propTypes = {
user: PropTypes.object,
component: PropTypes.node,
otherwise: PropTypes.node,
permission: PropTypes.string,
};
export default PermissionComponent;

View File

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

View File

@ -1,8 +1,12 @@
import React from 'react';
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 renderer from 'react-test-renderer';
import { UPDATE_FEATURE } from '../../../permissions';
jest.mock('react-mdl');
@ -21,19 +25,22 @@ test('renders correctly with one feature', () => {
],
createdAt: '2018-02-04T20:27:52.127Z',
};
const store = { user: new $Map({ profile: { permissions: [UPDATE_FEATURE] } }) };
const featureMetrics = { lastHour: {}, lastMinute: {}, seenApps: {} };
const settings = { sort: 'name' };
const tree = renderer.create(
<MemoryRouter>
<Feature
key={0}
settings={settings}
metricsLastHour={featureMetrics.lastHour[feature.name]}
metricsLastMinute={featureMetrics.lastMinute[feature.name]}
feature={feature}
toggleFeature={jest.fn()}
/>
</MemoryRouter>
<Provider store={createStore(state => state, store)}>
<MemoryRouter>
<Feature
key={0}
settings={settings}
metricsLastHour={featureMetrics.lastHour[feature.name]}
metricsLastMinute={featureMetrics.lastMinute[feature.name]}
feature={feature}
toggleFeature={jest.fn()}
/>
</MemoryRouter>
</Provider>
);
expect(tree).toMatchSnapshot();

View File

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

View File

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

View File

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