mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +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; | ||||
|         // 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