1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

feat: Add stale marking of feature toggles

This commit is contained in:
Ivar Conradi Østhus 2020-08-07 09:36:32 +02:00
parent e7e0b78697
commit f5ed3eaa1f
11 changed files with 176 additions and 9 deletions

View File

@ -140,8 +140,8 @@ IconLink.propTypes = {
icon: PropTypes.string,
};
export const DropdownButton = ({ label, id }) => (
<Button id={id} className={styles.dropdownButton}>
export const DropdownButton = ({ label, id, className }) => (
<Button id={id} className={className || styles.dropdownButton}>
{label}
<Icon name="arrow_drop_down" className="mdl-color-text--grey-600" />
</Button>

View File

@ -47,7 +47,7 @@ exports[`renders correctly with one feature 1`] = `
className="listItemStrategies hideLt920"
>
<react-mdl-Chip
className="mdl-color--blue-grey-100"
className="typeChip"
/>
</span>
<span />
@ -100,7 +100,7 @@ exports[`renders correctly with one feature without permission 1`] = `
className="listItemStrategies hideLt920"
>
<react-mdl-Chip
className="mdl-color--blue-grey-100"
className="typeChip"
/>
</span>
<span />

View File

@ -10,6 +10,19 @@ exports[`renders correctly with one feature 1`] = `
}
}
>
<react-mdl-Chip
className="mdl-color--light-green-500 mdl-color-text--white mdl-shadow--2dp"
style={
Object {
"position": "absolute",
"right": "4px",
"top": "4px",
}
}
title="Feature toggle is active."
>
Active
</react-mdl-Chip>
<react-mdl-CardTitle
style={
Object {
@ -73,6 +86,39 @@ exports[`renders correctly with one feature 1`] = `
</react-mdl-Switch>
</span>
<div>
<span>
<react-mdl-Button
className="mdl-button"
id="update_status"
>
Status
<react-mdl-Icon
className="mdl-color-text--grey-600"
name="arrow_drop_down"
/>
</react-mdl-Button>
<react-mdl-Menu
onClick={[Function]}
style={
Object {
"width": "168px",
}
}
target="update_status"
>
<react-mdl-MenuItem
data-target="active"
disabled={true}
>
Set toggle Active
</react-mdl-MenuItem>
<react-mdl-MenuItem
data-target="stale"
>
Mark toggle as Stale
</react-mdl-MenuItem>
</react-mdl-Menu>
</span>
<a
href="/features/copy/Another"
onClick={[Function]}

View File

@ -5,6 +5,7 @@ import { Switch, Chip, ListItem, ListItemAction, Icon } from 'react-mdl';
import Progress from './progress';
import { UPDATE_FEATURE } from '../../permissions';
import { calc, styles as commonStyles } from '../common';
import StatusComponent from './status-component';
import styles from './feature.scss';
@ -17,7 +18,7 @@ const Feature = ({
revive,
hasPermission,
}) => {
const { name, description, enabled, type } = feature;
const { name, description, enabled, type, stale } = feature;
const { showLastHour = false } = settings;
const isStale = showLastHour ? metricsLastHour.isFallback : metricsLastMinute.isFallback;
const percent =
@ -25,7 +26,6 @@ const Feature = ({
(showLastHour
? calc(metricsLastHour.yes, metricsLastHour.yes + metricsLastHour.no, 0)
: calc(metricsLastMinute.yes, metricsLastMinute.yes + metricsLastMinute.no, 0));
const typeChip = <Chip className="mdl-color--blue-grey-100">{type}</Chip>;
const featureUrl = toggleFeature === undefined ? `/archive/strategies/${name}` : `/features/strategies/${name}`;
return (
<ListItem twoLine>
@ -51,7 +51,10 @@ const Feature = ({
<span className={['mdl-list__item-sub-title', commonStyles.truncate].join(' ')}>{description}</span>
</Link>
</span>
<span className={[styles.listItemStrategies, commonStyles.hideLt920].join(' ')}>{typeChip}</span>
<span className={[styles.listItemStrategies, commonStyles.hideLt920].join(' ')}>
<StatusComponent stale={stale} showActive={false} />
<Chip className={styles.typeChip}>{type}</Chip>
</span>
{revive && hasPermission(UPDATE_FEATURE) ? (
<ListItemAction onClick={() => revive(feature.name)}>
<Icon name="undo" />

View File

@ -35,6 +35,7 @@
}
.typeChip {
margin-left: 8px !important;
background: #d3c1ff;
margin: 0 8px !important;
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12);
background-color: #cfd8dc !important;
}

View File

@ -0,0 +1,28 @@
import React from 'react';
import { Chip } from 'react-mdl';
import PropTypes from 'prop-types';
export default function StatusComponent({ stale, style, showActive = true }) {
if (!stale && !showActive) {
return null;
}
const className = stale
? 'mdl-color--red mdl-color-text--white mdl-shadow--2dp'
: 'mdl-color--light-green-500 mdl-color-text--white mdl-shadow--2dp';
const title = stale ? 'Feature toggle is deprecated.' : 'Feature toggle is active.';
const value = stale ? 'Stale' : 'Active';
return (
<Chip style={style} title={title} className={className}>
{value}
</Chip>
);
}
StatusComponent.propTypes = {
stale: PropTypes.bool.isRequired,
style: PropTypes.object,
showActive: PropTypes.bool,
};

View File

@ -0,0 +1,37 @@
import React from 'react';
import { Menu, MenuItem } from 'react-mdl';
import { DropdownButton } from '../common';
import PropTypes from 'prop-types';
export default function StatusUpdateComponent({ stale, updateStale }) {
function setStatus(field) {
if (field === 'active') {
updateStale(false);
} else {
updateStale(true);
}
}
return (
<span>
<DropdownButton className="mdl-button" id="update_status" label="Status" />
<Menu
target="update_status"
onClick={e => setStatus(e.target.getAttribute('data-target'))}
style={{ width: '168px' }}
>
<MenuItem disabled={!stale} data-target="active">
Set toggle Active
</MenuItem>
<MenuItem disabled={stale} data-target="stale">
Mark toggle as Stale
</MenuItem>
</Menu>
</span>
);
}
StatusUpdateComponent.propTypes = {
stale: PropTypes.bool.isRequired,
updateStale: PropTypes.func.isRequired,
};

View File

@ -12,6 +12,8 @@ import FeatureTypeSelect from './form/feature-type-select-container';
import UpdateDescriptionComponent from './form/update-description-component';
import { styles as commonStyles } from '../common';
import { CREATE_FEATURE, DELETE_FEATURE, UPDATE_FEATURE } from '../../permissions';
import StatusComponent from './status-component';
import StatusUpdateComponent from './status-update-component';
const TABS = {
strategies: 0,
@ -32,6 +34,7 @@ export default class ViewFeatureToggleComponent extends React.Component {
featureToggleName: PropTypes.string.isRequired,
features: PropTypes.array.isRequired,
toggleFeature: PropTypes.func,
setStale: PropTypes.func,
removeFeatureToggle: PropTypes.func,
revive: PropTypes.func,
fetchArchive: PropTypes.func,
@ -159,8 +162,20 @@ export default class ViewFeatureToggleComponent extends React.Component {
this.props.editFeatureToggle(feature);
};
const updateStale = stale => {
this.props.setStale(stale, featureToggleName);
};
return (
<Card shadow={0} className={commonStyles.fullwidth} style={{ overflow: 'visible' }}>
<StatusComponent
stale={featureToggle.stale}
style={{
position: 'absolute',
right: '4px',
top: '4px',
}}
/>
<CardTitle style={{ wordBreak: 'break-all', paddingBottom: 0 }}>{featureToggle.name} </CardTitle>
<CardText>
<UpdateDescriptionComponent
@ -201,6 +216,7 @@ export default class ViewFeatureToggleComponent extends React.Component {
{this.isFeatureView ? (
<div>
<StatusUpdateComponent stale={featureToggle.stale} updateStale={updateStale} />
<Link
to={`/features/copy/${featureToggle.name}`}
title="Create new feature toggle by cloning configuration"

View File

@ -3,6 +3,7 @@ import { connect } from 'react-redux';
import {
fetchFeatureToggles,
toggleFeature,
setStale,
removeFeatureToggle,
editFeatureToggle,
} from './../../store/feature-actions';
@ -20,6 +21,7 @@ export default connect(
{
fetchFeatureToggles,
toggleFeature,
setStale,
removeFeatureToggle,
editFeatureToggle,
}

View File

@ -62,6 +62,17 @@ function toggle(enable, name) {
}).then(throwIfNotSuccess);
}
function setStale(isStale, name) {
const action = isStale ? 'on' : 'off';
return fetch(`${URI}/${name}/stale/${action}`, {
method: 'POST',
headers,
credentials: 'include',
})
.then(throwIfNotSuccess)
.then(response => response.json());
}
function remove(featureToggleName) {
return fetch(`${URI}/${featureToggleName}`, {
method: 'DELETE',
@ -75,5 +86,6 @@ export default {
validate,
update,
toggle,
setStale,
remove,
};

View File

@ -26,6 +26,13 @@ export function toggleFeature(enable, name) {
};
}
export function setStale(stale, name) {
debug('Set stale property on feature toggle ', name);
return dispatch => {
dispatch(requestSetStaleFeatureToggle(stale, name));
};
}
export function editFeatureToggle(featureToggle) {
debug('Update feature toggle ', featureToggle);
return dispatch => {
@ -76,6 +83,21 @@ export function requestToggleFeatureToggle(enable, name) {
};
}
export function requestSetStaleFeatureToggle(stale, name) {
return dispatch => {
dispatch({ type: START_UPDATE_FEATURE_TOGGLE });
return api
.setStale(stale, name)
.then(featureToggle => {
const info = `${name} marked as ${stale ? 'Stale' : 'Active'}.`;
setTimeout(() => dispatch({ type: MUTE_ERROR, error: info }), 1000);
dispatch({ type: UPDATE_FEATURE_TOGGLE, featureToggle, info });
})
.catch(dispatchAndThrow(dispatch, ERROR_UPDATE_FEATURE_TOGGLE));
};
}
export function requestUpdateFeatureToggle(featureToggle) {
return dispatch => {
dispatch({ type: START_UPDATE_FEATURE_TOGGLE });