mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-01 00:08:27 +01:00
add simple metrics to features
This commit is contained in:
parent
a0276a61d2
commit
e870c8457c
@ -29,7 +29,6 @@ module.exports = class TTLList extends EventEmitter {
|
|||||||
let done = false;
|
let done = false;
|
||||||
// TODO: might use internal linkedlist
|
// TODO: might use internal linkedlist
|
||||||
this.cache.forEachReverse((entry, index) => {
|
this.cache.forEachReverse((entry, index) => {
|
||||||
console.log(now.format(), entry.ttl.format());
|
|
||||||
if (done) {
|
if (done) {
|
||||||
return;
|
return;
|
||||||
} else if (now.isBefore(entry.ttl)) {
|
} else if (now.isBefore(entry.ttl)) {
|
||||||
|
@ -23,7 +23,7 @@ module.exports = function (app, config) {
|
|||||||
res.json(metrics.getMetricsOverview());
|
res.json(metrics.getMetricsOverview());
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/toggle-metrics', (req, res) => {
|
app.get('/metrics/features', (req, res) => {
|
||||||
res.json(metrics.getTogglesMetrics());
|
res.json(metrics.getTogglesMetrics());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import Chip from 'react-toolbox/lib/chip';
|
|||||||
|
|
||||||
import style from './feature.scss';
|
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 { name, description, enabled, strategies } = feature;
|
||||||
|
|
||||||
const actions = [
|
const actions = [
|
||||||
@ -20,6 +20,7 @@ const Feature = ({ feature, onFeatureClick, onFeatureRemove }) => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const leftActions = [
|
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} />,
|
<Switch key="left-actions" onChange={() => onFeatureClick(feature)} checked={enabled} />,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -6,3 +6,11 @@
|
|||||||
color: #aaa !important;
|
color: #aaa !important;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.yes {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
@ -9,7 +9,9 @@ export default class FeatureListComponent extends React.Component {
|
|||||||
onFeatureClick: PropTypes.func.isRequired,
|
onFeatureClick: PropTypes.func.isRequired,
|
||||||
onFeatureRemove: PropTypes.func.isRequired,
|
onFeatureRemove: PropTypes.func.isRequired,
|
||||||
features: PropTypes.array.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 () {
|
componentDidMount () {
|
||||||
this.props.fetchFeatureToggles();
|
this.props.fetchFeatureToggles();
|
||||||
|
this.props.fetchFeatureMetrics();
|
||||||
|
this.timer = setInterval(() => {
|
||||||
|
this.props.fetchFeatureMetrics();
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount () {
|
||||||
|
clearInterval(this.timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { features, onFeatureClick, onFeatureRemove } = this.props;
|
const { features, onFeatureClick, onFeatureRemove, featureMetrics } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List>
|
<List>
|
||||||
<ListSubHeader caption="Feature toggles" />
|
<ListSubHeader caption="Feature toggles" />
|
||||||
{features.map((feature, i) =>
|
{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 />
|
<ListDivider />
|
||||||
<ListItem
|
<ListItem
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { toggleFeature, fetchFeatureToggles, removeFeatureToggle } from '../../store/feature-actions';
|
import { toggleFeature, fetchFeatureToggles, removeFeatureToggle } from '../../store/feature-actions';
|
||||||
|
import { fetchFeatureMetrics } from '../../store/feature-metrics-actions';
|
||||||
|
|
||||||
import FeatureListComponent from './list-component';
|
import FeatureListComponent from './list-component';
|
||||||
|
|
||||||
const mapStateToProps = (state) => ({
|
const mapStateToProps = (state) => ({
|
||||||
features: state.features.toJS(),
|
features: state.features.toJS(),
|
||||||
|
featureMetrics: state.featureMetrics.toJS(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
onFeatureClick: toggleFeature,
|
onFeatureClick: toggleFeature,
|
||||||
onFeatureRemove: removeFeatureToggle,
|
onFeatureRemove: removeFeatureToggle,
|
||||||
fetchFeatureToggles,
|
fetchFeatureToggles,
|
||||||
|
fetchFeatureMetrics,
|
||||||
};
|
};
|
||||||
|
|
||||||
const FeatureListContainer = connect(
|
const FeatureListContainer = connect(
|
||||||
|
@ -13,7 +13,7 @@ class Metrics extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<List>
|
<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 />
|
<ListDivider />
|
||||||
{clientList.map(({ name, count, ping, appName }, i) =>
|
{clientList.map(({ name, count, ping, appName }, i) =>
|
||||||
<ListItem
|
<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 { combineReducers } from 'redux';
|
||||||
import features from './feature-store';
|
import features from './feature-store';
|
||||||
|
import featureMetrics from './feature-metrics-store';
|
||||||
import strategies from './strategy-store';
|
import strategies from './strategy-store';
|
||||||
import input from './input-store';
|
import input from './input-store';
|
||||||
import history from './history-store'; // eslint-disable-line
|
import history from './history-store'; // eslint-disable-line
|
||||||
@ -11,6 +12,7 @@ import clientInstances from './client-instance-store';
|
|||||||
|
|
||||||
const unleashStore = combineReducers({
|
const unleashStore = combineReducers({
|
||||||
features,
|
features,
|
||||||
|
featureMetrics,
|
||||||
strategies,
|
strategies,
|
||||||
input,
|
input,
|
||||||
history,
|
history,
|
||||||
|
Loading…
Reference in New Issue
Block a user