Skip to content

Commit 13c8e62

Browse files
author
Jason Bulicek
authored
Merge pull request #355 from ExpediaDotCom/service-graph-updates
Service graph enhancements to include it in usb. Fix the missing root node issue due to circular dependencies. Remove unused trendsRowServiceGraphContainer.jsx.
2 parents 2f1bb28 + 5f1387c commit 13c8e62

15 files changed

Lines changed: 134 additions & 214 deletions

File tree

public/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"app":{"css":"/bundles/style/app.css","js":"/bundles/js/app.js"},"commons":{"js":"/bundles/js/commons.js"},"serviceGraph":{"js":"/bundles/js/serviceGraph.js"},"serviceGraphContainer":{"js":"/bundles/js/serviceGraphContainer.js"},"servicePerformance":{"css":"/bundles/style/servicePerformance.css","js":"/bundles/js/servicePerformance.js"},"vendors~servicePerformance":{"js":"/bundles/js/vendors~servicePerformance.js"},"vendors~vis":{"js":"/bundles/js/vendors~vis.js"}}
1+
{"app":{"css":"/bundles/style/app.css","js":"/bundles/js/app.js"},"commons":{"js":"/bundles/js/commons.js"},"serviceGraph":{"js":"/bundles/js/serviceGraph.js"},"serviceGraphContainer":{"js":"/bundles/js/serviceGraphContainer.js"},"servicePerformance":{"css":"/bundles/style/servicePerformance.css","js":"/bundles/js/servicePerformance.js"},"vendors~serviceGraph~serviceGraphContainer":{"js":"/bundles/js/vendors~serviceGraph~serviceGraphContainer.js"},"vendors~servicePerformance":{"js":"/bundles/js/vendors~servicePerformance.js"},"vendors~vis":{"js":"/bundles/js/vendors~vis.js"}}

server/connectors/serviceGraph/haystack/serviceGraphConnector.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,13 @@ const trendsFetcher = fetcher('serviceGraph');
2424

2525
const connector = {};
2626
const serviceGraphUrl = config.connectors.serviceGraph.serviceGraphUrl;
27-
const WINDOW_SIZE_IN_SECS = config.connectors.serviceGraph.windowSizeInSecs;
2827

2928
function fetchServiceGraph(from, to) {
3029
return trendsFetcher
3130
.fetch(`${serviceGraphUrl}?from=${from}&to=${to}`)
3231
.then(data => extractor.extractGraphs(data));
3332
}
3433

35-
connector.getServiceGraph = () => Q.fcall(() => fetchServiceGraph(Date.now() - (WINDOW_SIZE_IN_SECS * 1000), Date.now()));
3634
connector.getServiceGraphForTimeLine = (from, to) => Q.fcall(() => fetchServiceGraph(from, to));
3735

3836
module.exports = connector;

server/connectors/serviceGraph/stub/serviceGraphConnector.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,6 @@ const serviceGraph = {
158158
]
159159
};
160160

161-
connector.getServiceGraph = () => Q.fcall(() => extractor.extractGraphs(serviceGraph));
162-
163161
/* eslint-disable-next-line no-unused-vars */
164162
connector.getServiceGraphForTimeLine = (from, to) => Q.fcall(() => extractor.extractGraphs(serviceGraph));
165163

server/routes/serviceGraphApi.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,8 @@ const router = express.Router();
2424

2525
router.get('/serviceGraph', (req, res, next) => {
2626
handleResponsePromise(res, next, 'svc_graph_SVC')(
27-
() => {
28-
if (req.query.from && req.query.to) {
29-
return serviceGraphConnector.getServiceGraphForTimeLine(req.query.from, req.query.to);
30-
}
31-
return serviceGraphConnector.getServiceGraph();
32-
}
27+
() => serviceGraphConnector.getServiceGraphForTimeLine(req.query.from, req.query.to)
28+
3329
);
3430
});
3531

src/components/serviceGraph/serviceGraph.jsx

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,14 @@
1616
*/
1717

1818
import React from 'react';
19-
import PropTypes from 'prop-types';
20-
21-
import serviceGraphStore from './stores/serviceGraphStore';
2219
import ServiceGraphContainer from './serviceGraphContainer';
2320
import './serviceGraph.less';
21+
import serviceGraphStore from './stores/serviceGraphStore';
2422

25-
const ServiceGraph = props => (
23+
const ServiceGraph = () => (
2624
<section className="service-graph-panel container">
27-
<ServiceGraphContainer store={serviceGraphStore} history={props.history}/>
25+
<ServiceGraphContainer graphStore={serviceGraphStore} isUniversalSearch={false}/>
2826
</section>
2927
);
3028

31-
ServiceGraph.propTypes = {
32-
history: PropTypes.object.isRequired
33-
};
34-
3529
export default ServiceGraph;

src/components/serviceGraph/serviceGraphContainer.jsx

Lines changed: 79 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,29 @@
1616
*/
1717

1818
import React from 'react';
19-
import { observer } from 'mobx-react';
19+
import {observer} from 'mobx-react';
2020
import PropTypes from 'prop-types';
2121
import _ from 'lodash';
2222
import Loading from '../common/loading';
2323
import ServiceGraphResults from './serviceGraphResults';
2424
import Error from '../common/error';
25+
import Graph from './util/graph';
26+
import timeWindow from '../../utils/timeWindow';
2527

2628
@observer
2729
export default class ServiceGraphContainer extends React.Component {
2830
static propTypes = {
29-
store: PropTypes.object.isRequired,
30-
history: PropTypes.object.isRequired,
31-
isUniversalSearch: PropTypes.bool.isRequired
31+
isUniversalSearch: PropTypes.bool,
32+
search: PropTypes.object,
33+
graphStore: PropTypes.object.isRequired
3234
};
3335

36+
static defaultProps = {
37+
isUniversalSearch: false,
38+
search: {},
39+
serviceName: undefined
40+
}
41+
3442
/**
3543
*
3644
* @param graph
@@ -40,12 +48,17 @@ export default class ServiceGraphContainer extends React.Component {
4048
*/
4149
static findRootNode(graph) {
4250
const dest = _.map(graph, edge => edge.destination.name);
43-
const rootNodes = [];
51+
let rootNodes = [];
4452
_.forEach(graph, (edge) => {
4553
if (!_.includes(dest, edge.source.name)) {
4654
rootNodes.push(edge.source.name);
4755
}
4856
});
57+
// No node found with outgoing connections only. Find the node with the most outgoing traffic as a root node.
58+
if (_.isEmpty(rootNodes)) {
59+
rootNodes = dest;
60+
}
61+
4962
const uniqRoots = _.uniq(rootNodes);
5063
const sortedRoots = _.sortBy(uniqRoots, (node) => {
5164
const outgoingEdges = _.filter(graph, edge => edge.source.name === node);
@@ -61,12 +74,34 @@ export default class ServiceGraphContainer extends React.Component {
6174
tabSelected: 1
6275
};
6376
this.toggleTab = this.toggleTab.bind(this);
64-
this.props.store.fetchServiceGraph();
77+
}
78+
79+
componentDidMount() {
80+
this.fetchServiceGraphFromStore();
6581
}
6682

6783
toggleTab(tabIndex) {
68-
this.props.store.fetchServiceGraph();
6984
this.setState({tabSelected: tabIndex});
85+
this.fetchServiceGraphFromStore();
86+
}
87+
88+
fetchServiceGraphFromStore() {
89+
const time = this.props.search.time;
90+
const isCustomTimeRange = !!(time && time.from && time.to);
91+
let activeWindow;
92+
93+
if (isCustomTimeRange) {
94+
activeWindow = timeWindow.toCustomTimeRange(time.from, time.to);
95+
} else {
96+
activeWindow = (time && time.preset) ? timeWindow.findMatchingPresetByShortName(time.preset) : timeWindow.defaultPreset;
97+
}
98+
99+
const activeWindowTimeRange = timeWindow.toTimeRange(activeWindow.value);
100+
const filterQuery = {
101+
from: activeWindowTimeRange.from,
102+
to: activeWindowTimeRange.until
103+
};
104+
this.props.graphStore.fetchServiceGraph(filterQuery);
70105
}
71106

72107
render() {
@@ -80,31 +115,55 @@ export default class ServiceGraphContainer extends React.Component {
80115
<span className="h6">(will show list of partial graphs if missing data from services)</span>
81116
</div>)
82117
}
118+
{!this.props.search.serviceName &&
83119
<div className="serviceGraph__tabs pull-right">
84120
<ul className="nav nav-tabs">
85121
{
86-
this.props.store.graphs.map(
122+
this.props.graphStore.graphs.map(
87123
(graph, index) => (
88-
<li className={this.state.tabSelected === (index + 1) ? 'active ' : ''}>
89-
<a role="button" className="serviceGraph__tab-link" tabIndex="-1" onClick={() => this.toggleTab(index + 1)} >{ServiceGraphContainer.findRootNode(graph)}</a>
124+
<li className={this.state.tabSelected === (index + 1) ? 'active ' : ''} key={index.toString()}>
125+
<a role="button" className="serviceGraph__tab-link" tabIndex="-1" onClick={() => this.toggleTab(index + 1)}>{ServiceGraphContainer.findRootNode(graph)}</a>
90126
</li>
91127
)
92128
)
93129
}
94130
</ul>
95131
</div>
132+
}
96133
</div>
97134
<div>
98-
{ this.props.store.promiseState && this.props.store.promiseState.case({
99-
pending: () => <Loading className="serviceGraph__loading"/>,
100-
rejected: () => <Error />,
101-
fulfilled: () => ((this.props.store.graphs && this.props.store.graphs.length)
102-
? <ServiceGraphResults serviceGraph={this.props.store.graphs[this.state.tabSelected - 1]} history={this.props.history} />
103-
: <Error />)
104-
})
105-
}
135+
{this.props.graphStore.promiseState && this.props.graphStore.promiseState.case({
136+
pending: () => <Loading className="serviceGraph__loading"/>,
137+
rejected: () => <Error/>,
138+
fulfilled: () => ((this.props.graphStore.graphs && this.props.graphStore.graphs.length)
139+
? <FilteredServiceGraphResults graphs={this.props.graphStore.graphs} serviceName={this.props.search.serviceName} tabSelected={this.state.tabSelected}/>
140+
: <Error/>)
141+
})
142+
}
106143
</div>
107-
</section>
108-
);
144+
</section>);
109145
}
110146
}
147+
148+
function FilteredServiceGraphResults(props) {
149+
if (!props.serviceName) {
150+
return (<ServiceGraphResults serviceGraph={props.graphs[props.tabSelected - 1]}/>);
151+
}
152+
const result = _.find(props.graphs, g => _.includes(Graph.buildGraph(g).allNodes(), props.serviceName));
153+
if (typeof result !== 'undefined') {
154+
const filtered = _.filter(result, edge => edge.source.name === props.serviceName || edge.destination.name === props.serviceName);
155+
return (<ServiceGraphResults serviceGraph={filtered}/>);
156+
}
157+
return (<Error errorMessage="ServiceGraph data not found for the given time frame."/>);
158+
}
159+
160+
FilteredServiceGraphResults.propTypes = {
161+
graphs: PropTypes.object.isRequired,
162+
serviceName: PropTypes.string,
163+
tabSelected: PropTypes.number
164+
};
165+
166+
FilteredServiceGraphResults.defaultProps = {
167+
serviceName: undefined,
168+
tabSelected: 0
169+
};

src/components/serviceGraph/serviceGraphResults.jsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
import React from 'react';
18+
import {PropTypes as MobxPropTypes} from 'mobx-react';
1819
import PropTypes from 'prop-types';
1920
import _ from 'lodash';
2021
import Vizceral from 'vizceral-react';
@@ -27,7 +28,10 @@ import './serviceGraph.less';
2728

2829
export default class ServiceGraphResults extends React.Component {
2930
static propTypes = {
30-
serviceGraph: PropTypes.object.isRequired
31+
serviceGraph: PropTypes.oneOfType([
32+
MobxPropTypes.observableArray.isRequired,
33+
PropTypes.array
34+
]).isRequired
3135
};
3236

3337
static getNodeDisplayDetails(errorRate) {

src/components/serviceGraph/stores/serviceGraphStore.js

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,16 @@ import {ErrorHandlingStore} from '../../../stores/errorHandlingStore';
2121

2222
export class ServiceGraphStore extends ErrorHandlingStore {
2323
@observable graphs = [];
24-
@observable promiseState = null ;
24+
@observable promiseState = null;
25+
@observable filterQuery = null;
2526

26-
@observable filteredGraphs = [];
27-
@observable filterQuery = {};
28-
29-
@action fetchServiceGraph() {
27+
@action fetchServiceGraph(filterQuery) {
3028
this.promiseState = fromPromise(
31-
axios
32-
.get('/api/serviceGraph')
33-
.then((result) => {
34-
this.graphs = result.data;
35-
})
36-
.catch((result) => {
37-
ServiceGraphStore.handleError(result);
38-
})
39-
);
40-
}
41-
42-
@action fetchServiceGraphForTimeline(filterQuery) {
43-
this.filteredGraphPromiseState = fromPromise(
4429
axios
4530
.get(`/api/serviceGraph?from=${filterQuery.from}&to=${filterQuery.to}`)
4631
.then((result) => {
4732
this.filterQuery = filterQuery;
48-
this.filteredGraphs = result.data;
33+
this.graphs = result.data;
4934
})
5035
.catch((result) => {
5136
ServiceGraphStore.handleError(result);

src/components/serviceGraph/trendsRowServiceGraphContainer.jsx

Lines changed: 0 additions & 85 deletions
This file was deleted.

src/components/trends/details/graphs/graphContainer.jsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,10 @@ import {observer} from 'mobx-react';
2020
import CountGraph from './countGraph';
2121
import DurationGraph from './durationGraph';
2222
import SuccessGraph from './successGraph';
23-
import TrendsRowServiceGraphContainer from '../../../serviceGraph/trendsRowServiceGraphContainer';
2423
@observer
2524
export default class GraphContainer extends React.Component {
2625
static propTypes = {
27-
trendsStore: PropTypes.object.isRequired,
28-
serviceName: PropTypes.string.isRequired
26+
trendsStore: PropTypes.object.isRequired
2927
};
3028
render() {
3129
const {
@@ -47,7 +45,6 @@ export default class GraphContainer extends React.Component {
4745
<CountGraph countPoints={count} successPoints={successCount} failurePoints={failureCount} from={from} until={until}/>
4846
<DurationGraph meanPoints={meanDuration} tp95Points={tp95Duration} tp99Points={tp99Duration} from={from} until={until} />
4947
<SuccessGraph successCount={successCount} failureCount={failureCount} from={from} until={until} />
50-
<TrendsRowServiceGraphContainer serviceName={this.props.serviceName} from={from} to={until} />
5148
</div>
5249
);
5350
}

0 commit comments

Comments
 (0)