mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-22 19:07:54 +01:00
add simple metrics to features
This commit is contained in:
parent
26220fde09
commit
17ec5da59e
@ -29,7 +29,6 @@ module.exports = class TTLList extends EventEmitter {
|
||||
let done = false;
|
||||
// TODO: might use internal linkedlist
|
||||
this.cache.forEachReverse((entry, index) => {
|
||||
console.log(now.format(), entry.ttl.format());
|
||||
if (done) {
|
||||
return;
|
||||
} else if (now.isBefore(entry.ttl)) {
|
||||
|
@ -23,7 +23,7 @@ module.exports = function (app, config) {
|
||||
res.json(metrics.getMetricsOverview());
|
||||
});
|
||||
|
||||
app.get('/toggle-metrics', (req, res) => {
|
||||
app.get('/metrics/features', (req, res) => {
|
||||
res.json(metrics.getTogglesMetrics());
|
||||
});
|
||||
|
||||
|
@ -8,7 +8,7 @@ import Chip from 'react-toolbox/lib/chip';
|
||||
|
||||
import style from './feature.scss';
|
||||
|
||||
const Feature = ({ feature, onFeatureClick, onFeatureRemove }) => {
|
||||
const Feature = ({ feature, onFeatureClick, onFeatureRemove, metrics = { yes: 0, no: 0, hasData: false } }) => {
|
||||
const { name, description, enabled, strategies } = feature;
|
||||
|
||||
const actions = [
|
||||
@ -20,6 +20,7 @@ const Feature = ({ feature, onFeatureClick, onFeatureRemove }) => {
|
||||
];
|
||||
|
||||
const leftActions = [
|
||||
<Chip><span className={style.yes}>{metrics.yes}</span> / <span className={style.no}>{metrics.no}</span></Chip>,
|
||||
<Switch key="left-actions" onChange={() => onFeatureClick(feature)} checked={enabled} />,
|
||||
];
|
||||
|
||||
|
@ -6,3 +6,11 @@
|
||||
color: #aaa !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.yes {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.no {
|
||||
color: red;
|
||||
}
|
||||
|
@ -9,7 +9,9 @@ export default class FeatureListComponent extends React.Component {
|
||||
onFeatureClick: PropTypes.func.isRequired,
|
||||
onFeatureRemove: PropTypes.func.isRequired,
|
||||
features: PropTypes.array.isRequired,
|
||||
fetchFeatureToggles: PropTypes.array.isRequired,
|
||||
featureMetrics: PropTypes.object.isRequired,
|
||||
fetchFeatureToggles: PropTypes.func.isRequired,
|
||||
fetchFeatureMetrics: PropTypes.func.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
@ -19,16 +21,24 @@ export default class FeatureListComponent extends React.Component {
|
||||
|
||||
componentDidMount () {
|
||||
this.props.fetchFeatureToggles();
|
||||
this.props.fetchFeatureMetrics();
|
||||
this.timer = setInterval(() => {
|
||||
this.props.fetchFeatureMetrics();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { features, onFeatureClick, onFeatureRemove } = this.props;
|
||||
const { features, onFeatureClick, onFeatureRemove, featureMetrics } = this.props;
|
||||
|
||||
return (
|
||||
<List>
|
||||
<ListSubHeader caption="Feature toggles" />
|
||||
{features.map((feature, i) =>
|
||||
<Feature key={i} feature={feature} onFeatureClick={onFeatureClick} onFeatureRemove={onFeatureRemove}/>
|
||||
<Feature key={i} metrics={featureMetrics[feature.name]} feature={feature} onFeatureClick={onFeatureClick} onFeatureRemove={onFeatureRemove}/>
|
||||
)}
|
||||
<ListDivider />
|
||||
<ListItem
|
||||
|
@ -1,15 +1,19 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { toggleFeature, fetchFeatureToggles, removeFeatureToggle } from '../../store/feature-actions';
|
||||
import { fetchFeatureMetrics } from '../../store/feature-metrics-actions';
|
||||
|
||||
import FeatureListComponent from './list-component';
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
features: state.features.toJS(),
|
||||
featureMetrics: state.featureMetrics.toJS(),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
onFeatureClick: toggleFeature,
|
||||
onFeatureRemove: removeFeatureToggle,
|
||||
fetchFeatureToggles,
|
||||
fetchFeatureMetrics,
|
||||
};
|
||||
|
||||
const FeatureListContainer = connect(
|
||||
|
@ -13,7 +13,7 @@ class Metrics extends Component {
|
||||
|
||||
return (
|
||||
<List>
|
||||
<ListSubHeader caption={<span>Total of {globalCount} toggles checked from {apps.length} apps ({apps.join(', ')})</span>} />
|
||||
<ListSubHeader caption={<span>Total of {globalCount} toggles </span>} />
|
||||
<ListDivider />
|
||||
{clientList.map(({ name, count, ping, appName }, i) =>
|
||||
<ListItem
|
||||
|
@ -0,0 +1,30 @@
|
||||
import api from './feature-metrics-api';
|
||||
|
||||
export const START_FETCH_FEATURE_METRICS = 'START_FETCH_FEATURE_METRICS';
|
||||
export const RECEIVE_FEATURE_METRICS = 'RECEIVE_FEATURE_METRICS';
|
||||
export const ERROR_FETCH_FEATURE_TOGGLES = 'ERROR_FETCH_FEATURE_TOGGLES';
|
||||
|
||||
function receiveFeatureMetrics (json) {
|
||||
return {
|
||||
type: RECEIVE_FEATURE_METRICS,
|
||||
metrics: json,
|
||||
receivedAt: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
function dispatchAndThrow (dispatch, type) {
|
||||
return (error) => {
|
||||
dispatch({ type, error, receivedAt: Date.now() });
|
||||
throw error;
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchFeatureMetrics () {
|
||||
return dispatch => {
|
||||
dispatch({ type: START_FETCH_FEATURE_METRICS });
|
||||
|
||||
return api.fetchFeatureMetrics()
|
||||
.then(json => dispatch(receiveFeatureMetrics(json)))
|
||||
.catch(dispatchAndThrow(dispatch, ERROR_FETCH_FEATURE_TOGGLES));
|
||||
};
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
const defaultErrorMessage = 'Unexptected exception when talking to unleash-api';
|
||||
|
||||
function throwIfNotSuccess (response) {
|
||||
if (!response.ok) {
|
||||
if (response.status > 400 && response.status < 404) {
|
||||
return new Promise((resolve, reject) => {
|
||||
response.json().then(body => {
|
||||
const errorMsg = body && body.length > 0 ? body[0].msg : defaultErrorMessage;
|
||||
let error = new Error(errorMsg);
|
||||
error.statusCode = response.status;
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return Promise.reject(new Error(defaultErrorMessage));
|
||||
}
|
||||
}
|
||||
return Promise.resolve(response);
|
||||
}
|
||||
|
||||
function fetchFeatureMetrics () {
|
||||
return fetch('/metrics/features')
|
||||
.then(throwIfNotSuccess)
|
||||
.then(response => response.json());
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
fetchFeatureMetrics,
|
||||
};
|
@ -0,0 +1,17 @@
|
||||
import { Map as $Map } from 'immutable';
|
||||
|
||||
import {
|
||||
RECEIVE_FEATURE_METRICS,
|
||||
} from './feature-metrics-actions';
|
||||
|
||||
|
||||
const metrics = (state = new $Map(), action) => {
|
||||
switch (action.type) {
|
||||
case RECEIVE_FEATURE_METRICS:
|
||||
return new $Map(action.metrics);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default metrics;
|
@ -1,5 +1,6 @@
|
||||
import { combineReducers } from 'redux';
|
||||
import features from './feature-store';
|
||||
import featureMetrics from './feature-metrics-store';
|
||||
import strategies from './strategy-store';
|
||||
import input from './input-store';
|
||||
import history from './history-store'; // eslint-disable-line
|
||||
@ -11,6 +12,7 @@ import clientInstances from './client-instance-store';
|
||||
|
||||
const unleashStore = combineReducers({
|
||||
features,
|
||||
featureMetrics,
|
||||
strategies,
|
||||
input,
|
||||
history,
|
||||
|
Loading…
Reference in New Issue
Block a user