From e870c8457c5d06034a479d5b3c63ace10c8a05b9 Mon Sep 17 00:00:00 2001 From: sveisvei Date: Sat, 5 Nov 2016 11:50:34 +0100 Subject: [PATCH] add simple metrics to features --- .../lib/client-metrics/ttl-list.js | 1 - packages/unleash-api/lib/routes/metrics.js | 2 +- .../component/feature/feature-component.jsx | 3 +- .../src/component/feature/feature.scss | 8 +++++ .../src/component/feature/list-component.jsx | 16 ++++++++-- .../src/component/feature/list-container.jsx | 4 +++ .../component/metrics/metrics-component.js | 2 +- .../src/store/feature-metrics-actions.js | 30 +++++++++++++++++++ .../src/store/feature-metrics-api.js | 29 ++++++++++++++++++ .../src/store/feature-metrics-store.js | 17 +++++++++++ .../unleash-frontend-next/src/store/index.js | 2 ++ 11 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 packages/unleash-frontend-next/src/store/feature-metrics-actions.js create mode 100644 packages/unleash-frontend-next/src/store/feature-metrics-api.js create mode 100644 packages/unleash-frontend-next/src/store/feature-metrics-store.js diff --git a/packages/unleash-api/lib/client-metrics/ttl-list.js b/packages/unleash-api/lib/client-metrics/ttl-list.js index 7aa141aa6d..8b046847df 100644 --- a/packages/unleash-api/lib/client-metrics/ttl-list.js +++ b/packages/unleash-api/lib/client-metrics/ttl-list.js @@ -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)) { diff --git a/packages/unleash-api/lib/routes/metrics.js b/packages/unleash-api/lib/routes/metrics.js index acc441813a..35640fa7b9 100644 --- a/packages/unleash-api/lib/routes/metrics.js +++ b/packages/unleash-api/lib/routes/metrics.js @@ -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()); }); diff --git a/packages/unleash-frontend-next/src/component/feature/feature-component.jsx b/packages/unleash-frontend-next/src/component/feature/feature-component.jsx index 6513a922b3..8843d9b059 100644 --- a/packages/unleash-frontend-next/src/component/feature/feature-component.jsx +++ b/packages/unleash-frontend-next/src/component/feature/feature-component.jsx @@ -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 = [ + {metrics.yes} / {metrics.no}, onFeatureClick(feature)} checked={enabled} />, ]; diff --git a/packages/unleash-frontend-next/src/component/feature/feature.scss b/packages/unleash-frontend-next/src/component/feature/feature.scss index 2f99d5281c..636272d55f 100644 --- a/packages/unleash-frontend-next/src/component/feature/feature.scss +++ b/packages/unleash-frontend-next/src/component/feature/feature.scss @@ -6,3 +6,11 @@ color: #aaa !important; cursor: pointer; } + +.yes { + color: green; +} + +.no { + color: red; +} diff --git a/packages/unleash-frontend-next/src/component/feature/list-component.jsx b/packages/unleash-frontend-next/src/component/feature/list-component.jsx index 8ef6b55c66..e725cba7ac 100644 --- a/packages/unleash-frontend-next/src/component/feature/list-component.jsx +++ b/packages/unleash-frontend-next/src/component/feature/list-component.jsx @@ -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 ( {features.map((feature, i) => - + )} ({ features: state.features.toJS(), + featureMetrics: state.featureMetrics.toJS(), }); const mapDispatchToProps = { onFeatureClick: toggleFeature, onFeatureRemove: removeFeatureToggle, fetchFeatureToggles, + fetchFeatureMetrics, }; const FeatureListContainer = connect( diff --git a/packages/unleash-frontend-next/src/component/metrics/metrics-component.js b/packages/unleash-frontend-next/src/component/metrics/metrics-component.js index 4b47bf2b42..fbe3dc49d6 100644 --- a/packages/unleash-frontend-next/src/component/metrics/metrics-component.js +++ b/packages/unleash-frontend-next/src/component/metrics/metrics-component.js @@ -13,7 +13,7 @@ class Metrics extends Component { return ( - Total of {globalCount} toggles checked from {apps.length} apps ({apps.join(', ')})} /> + Total of {globalCount} toggles } /> {clientList.map(({ name, count, ping, appName }, i) => { + 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)); + }; +} diff --git a/packages/unleash-frontend-next/src/store/feature-metrics-api.js b/packages/unleash-frontend-next/src/store/feature-metrics-api.js new file mode 100644 index 0000000000..cc4d28cac1 --- /dev/null +++ b/packages/unleash-frontend-next/src/store/feature-metrics-api.js @@ -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, +}; diff --git a/packages/unleash-frontend-next/src/store/feature-metrics-store.js b/packages/unleash-frontend-next/src/store/feature-metrics-store.js new file mode 100644 index 0000000000..77886b66a7 --- /dev/null +++ b/packages/unleash-frontend-next/src/store/feature-metrics-store.js @@ -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; diff --git a/packages/unleash-frontend-next/src/store/index.js b/packages/unleash-frontend-next/src/store/index.js index c073cdb738..2710c6cc1e 100644 --- a/packages/unleash-frontend-next/src/store/index.js +++ b/packages/unleash-frontend-next/src/store/index.js @@ -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,