Skip to content

Commit

Permalink
Merge 2ac47b9 into 11b22b6
Browse files Browse the repository at this point in the history
  • Loading branch information
absrivastava committed Jan 16, 2020
2 parents 11b22b6 + 2ac47b9 commit 152f112
Show file tree
Hide file tree
Showing 5 changed files with 332 additions and 48 deletions.
64 changes: 34 additions & 30 deletions server/connectors/trends/stub/trendsConnector.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,23 @@ const _ = require('lodash');
const connector = {};

function getValue(min, max) {
return _.round((Math.random() * (max - min)) + min, 0);
return _.round(Math.random() * (max - min) + min, 0);
}

function getTimeStamp(addMin) {
const currentTime = ((new Date()).getTime());
return (currentTime - (addMin * 60 * 1000));
const currentTime = new Date().getTime();
return currentTime - addMin * 60 * 1000;
}

function getRandomValues(timeWindow, dataPoints) {
const valuesArr = [];
_.range(dataPoints).forEach(i => valuesArr.push({value: getValue(1000, 10000000), timestamp: getTimeStamp(i * timeWindow)}));
_.range(dataPoints).forEach((i) => valuesArr.push({value: getValue(1000, 10000000), timestamp: getTimeStamp(i * timeWindow)}));
return valuesArr;
}

function getRandomPercentageValues(timeWindow, dataPoints) {
const valuesArr = [];
_.range(dataPoints).forEach(i => valuesArr.push({value: getValue(80, 100), timestamp: getTimeStamp(i * timeWindow)}));
_.range(dataPoints).forEach((i) => valuesArr.push({value: getValue(80, 100), timestamp: getTimeStamp(i * timeWindow)}));
return valuesArr;
}

Expand All @@ -56,38 +56,42 @@ connector.getServiceStats = (serviceName, granularity, from, until) => {
successPercentPoints: getRandomPercentageValues(mins, points),
latestTp99Duration: 14530,
tp99DurationPoints: getRandomValues(mins, points)
}]);
}
]);

return deffered.promise;
};

connector.getServicePerfStats = () => {
const deffered = Q.defer();

deffered.resolve(
[
{serviceName: 'Service 1', successPercent: getValue(90, 100), failureCount: getValue(10, 100000), totalCount: getValue(1000000000, 10000000000)},
{serviceName: 'Service 2', successPercent: getValue(90, 100), failureCount: getValue(10, 100000), totalCount: getValue(1000000, 10000000)},
{serviceName: 'Service 3', successPercent: getValue(95, 100), failureCount: getValue(10, 100000), totalCount: getValue(1000000, 1000000)},
{serviceName: 'Service 4', successPercent: getValue(95, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 100000)},
{serviceName: 'Service 5', successPercent: null, failureCount: getValue(10, 100000), totalCount: getValue(10, 100000)},
{serviceName: 'Service 6', successPercent: getValue(95, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 100000)},
{serviceName: 'Service 7', successPercent: null, failureCount: getValue(10, 100000), totalCount: getValue(10, 100000)},
{serviceName: 'Service 8', successPercent: getValue(95, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 1000)},
{serviceName: 'Service 9', successPercent: getValue(95, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 1000)},
{serviceName: 'Service 10', successPercent: getValue(95, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 1000)},
{serviceName: 'Service 11', successPercent: getValue(95, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 100)},
{serviceName: 'Service 12', successPercent: getValue(95, 100), failureCount: getValue(10, 100000), totalCount: getValue(1, 10)},
{serviceName: 'Service 13', successPercent: getValue(90, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 100)},
{serviceName: 'Service 14', successPercent: getValue(90, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 1000)},
{serviceName: 'Service 15', successPercent: getValue(10, 40), failureCount: getValue(10, 100000), totalCount: getValue(10, 100)},
{serviceName: 'Service 16', successPercent: getValue(90, 100), failureCount: getValue(10, 100000), totalCount: getValue(1000000, 10000000)},
{serviceName: 'Service 17', successPercent: getValue(99, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 100000)},
{serviceName: 'Service 18', successPercent: getValue(99, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 100)},
{serviceName: 'Service 19', successPercent: getValue(10, 40), failureCount: getValue(10, 100000), totalCount: getValue(10, 100)},
{serviceName: 'Service 20', successPercent: getValue(0, 1), failureCount: getValue(10, 100000), totalCount: getValue(10, 100)}
]
);
deffered.resolve([
{
serviceName: 'Service 1',
successPercent: getValue(90, 100),
failureCount: getValue(10, 100000),
totalCount: getValue(1000000000, 10000000000)
},
{serviceName: 'Service 2', successPercent: getValue(90, 100), failureCount: getValue(10, 100000), totalCount: getValue(1000000, 10000000)},
{serviceName: 'Service 3', successPercent: getValue(95, 100), failureCount: getValue(10, 100000), totalCount: getValue(1000000, 1000000)},
{serviceName: 'Service 4', successPercent: getValue(95, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 100000)},
{serviceName: 'Service 5', successPercent: null, failureCount: getValue(10, 100000), totalCount: getValue(10, 100000)},
{serviceName: 'Service 6', successPercent: getValue(95, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 100000)},
{serviceName: 'Service 7', successPercent: null, failureCount: getValue(10, 100000), totalCount: getValue(10, 100000)},
{serviceName: 'Service 8', successPercent: getValue(95, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 1000)},
{serviceName: 'Service 9', successPercent: getValue(95, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 1000)},
{serviceName: 'Service 10', successPercent: getValue(95, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 1000)},
{serviceName: 'Service 11', successPercent: getValue(95, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 100)},
{serviceName: 'Service 12', successPercent: getValue(95, 100), failureCount: getValue(10, 100000), totalCount: getValue(1, 10)},
{serviceName: 'Service 13', successPercent: getValue(90, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 100)},
{serviceName: 'Service 14', successPercent: getValue(90, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 1000)},
{serviceName: 'Service 15', successPercent: getValue(10, 40), failureCount: getValue(10, 100000), totalCount: getValue(10, 100)},
{serviceName: 'Service 16', successPercent: getValue(90, 100), failureCount: getValue(10, 100000), totalCount: getValue(1000000, 10000000)},
{serviceName: 'Service 17', successPercent: getValue(99, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 100000)},
{serviceName: 'Service 18', successPercent: getValue(99, 100), failureCount: getValue(10, 100000), totalCount: getValue(10, 100)},
{serviceName: 'Service 19', successPercent: getValue(10, 40), failureCount: getValue(10, 100000), totalCount: getValue(10, 100)},
{serviceName: 'Service 20', successPercent: getValue(0, 1), failureCount: getValue(10, 100000), totalCount: getValue(10, 100)}
]);

return deffered.promise;
};
Expand Down
69 changes: 54 additions & 15 deletions src/components/trends/operation/operationResults.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import Loading from '../../common/loading';
import OperationResultsTable from './operationResultsTable';
import './operationResults.less';
import Error from '../../common/error';

import OperationResultsHeatmap from './operationResultsHeatmap';

@observer
export default class operationResults extends React.Component {
Expand All @@ -32,24 +32,63 @@ export default class operationResults extends React.Component {
interval: PropTypes.oneOfType([PropTypes.oneOf([null]), PropTypes.string]).isRequired
};

constructor(props) {
super(props);
this.state = {};
this.toggleView = this.toggleView.bind(this);
}

toggleView() {
this.setState({showHeatmap: !this.state.showHeatmap});
}

render() {
const showHeatmap = this.state.showHeatmap;
return (
<section className="operation-results">
{ this.props.operationStore.statsPromiseState && this.props.operationStore.statsPromiseState.case({
empty: () => <Loading />,
pending: () => <Loading />,
rejected: () => <Error />,
fulfilled: () => ((this.props.operationStore.statsPromiseState && this.props.operationStore.statsResults.length)
? <OperationResultsTable
operationStore={this.props.operationStore}
serviceName={this.props.serviceName}
interval={this.props.interval}
/>
: <Error errorMessage="There was a problem displaying operation results. Please try again later." />)
})
}
{this.props.operationStore.statsPromiseState &&
this.props.operationStore.statsPromiseState.case({
empty: () => <Loading />,
pending: () => <Loading />,
rejected: () => <Error />,
fulfilled: () =>
this.props.operationStore.statsPromiseState && this.props.operationStore.statsResults.length ? (
<section>
<div className="operation-results-view-selector text-center">
<div className="btn-group btn-group-sm">
<button
className={showHeatmap ? 'btn btn-sm btn-default' : 'btn btn-sm btn-primary'}
onClick={() => this.toggleView()}
>
Graph View
</button>
<button
className={!showHeatmap ? 'btn btn-sm btn-default' : 'btn btn-sm btn-primary'}
onClick={() => this.toggleView()}
>
Availability
</button>
</div>
</div>
{showHeatmap ? (
<OperationResultsHeatmap
operationStore={this.props.operationStore}
serviceName={this.props.serviceName}
interval={this.props.interval}
/>
) : (
<OperationResultsTable
operationStore={this.props.operationStore}
serviceName={this.props.serviceName}
interval={this.props.interval}
/>
)}
</section>
) : (
<Error errorMessage="There was a problem displaying operation results. Please try again later." />
)
})}
</section>
);
}
}

9 changes: 6 additions & 3 deletions src/components/trends/operation/operationResults.less
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
@import (reference) '../../../app';

.operation-results .react-bs-table-container {
margin-top: 0;
margin-top: 0;
}
.results-table-heading {
font-size: xx-large;
}
font-size: xx-large;
}
.operation-results-view-selector {
margin-bottom: @spacing-m;
}
158 changes: 158 additions & 0 deletions src/components/trends/operation/operationResultsHeatmap.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* Copyright 2019 Expedia Group
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';

import linkBuilder from '../../../utils/linkBuilder';
import './operationResultsHeatmap.less';

const percentColors = [
{pct: 0.0, color: {r: 140, g: 40, b: 39}},
{pct: 0.5, color: {r: 179, g: 77, b: 75}},
{pct: 0.75, color: {r: 236, g: 139, b: 131}},
{pct: 0.9, color: {r: 246, g: 198, b: 181}},
{pct: 0.95, color: {r: 245, g: 255, b: 211}},
{pct: 0.98, color: {r: 209, g: 230, b: 204}},
{pct: 0.99, color: {r: 165, g: 206, b: 185}},
{pct: 0.999, color: {r: 118, g: 182, b: 163}},
{pct: 1.0, color: {r: 75, g: 157, b: 147}}
];

export default class OperationResultsHeatmap extends React.Component {
static propTypes = {
operationStore: PropTypes.object.isRequired,
serviceName: PropTypes.string.isRequired
};

static handleCellClick(serviceName, operation, from, to) {
const tracesLink = linkBuilder.withAbsoluteUrl(
linkBuilder.universalSearchTracesLink({
query_1: {
serviceName,
operationName: operation.operationName
},
time: {
from,
to
}
})
);

const win = window.open(tracesLink, '_blank');
win.focus();
}

static getColorForPercentage(pct) {
let i;
for (i = 1; i < percentColors.length - 1; i++) {
if (pct < percentColors[i].pct) {
break;
}
}
const lower = percentColors[i - 1];
const upper = percentColors[i];
const range = upper.pct - lower.pct;
const rangePct = (pct - lower.pct) / range;
const pctLower = 1 - rangePct;
const pctUpper = rangePct;
const color = {
r: Math.floor(lower.color.r * pctLower + upper.color.r * pctUpper),
g: Math.floor(lower.color.g * pctLower + upper.color.g * pctUpper),
b: Math.floor(lower.color.b * pctLower + upper.color.b * pctUpper)
};
return `rgb(${[color.r, color.g, color.b].join(',')})`;
}

static getColumnHeaders(from, until, granularity) {
const headers = [];
let i = 0;
const fromRounded = Math.ceil(moment(from) / granularity) * granularity;
do {
const timestamp = fromRounded + granularity * i;
headers.push(timestamp);
i += 1;
} while (headers[headers.length - 1] < until);
return headers;
}

static coloumnHeaderFormatter(timestamp) {
return moment(timestamp).format('lll');
}

render() {
const data = this.props.operationStore.statsResults;
const serviceName = this.props.serviceName;
const {from, until, granularity} = this.props.operationStore.statsQuery;
const columnHeaders = OperationResultsHeatmap.getColumnHeaders(from, until, granularity);
return (
<section>
<div className="heatmap-container">
<table className="heatmap">
<thead>
<tr>
<th />
{columnHeaders.map((header) => (
<th>
<div>
<span>{OperationResultsHeatmap.coloumnHeaderFormatter(header)}</span>
</div>
</th>
))}
</tr>
</thead>
<tbody>
{data.map((op) => (
<tr>
<th>{op.operationName}</th>
{columnHeaders.map((headerTimestamp, index) => {
const availableDatapoint = op.successPercentPoints.find(
(sucessPercentDatapoint) =>
sucessPercentDatapoint.timestamp >= headerTimestamp &&
sucessPercentDatapoint.timestamp < columnHeaders[index + 1]
);
return availableDatapoint ? (
<td style={{background: OperationResultsHeatmap.getColorForPercentage(availableDatapoint.value / 100)}}>
<div
role="link"
tabIndex="-1"
onClick={() =>
OperationResultsHeatmap.handleCellClick(
serviceName,
op,
headerTimestamp,
columnHeaders[index + 1]
)
}
>
{' '}
{availableDatapoint.value} %
</div>
</td>
) : (
<td style={{background: '#f9edf3'}} />
);
})}
</tr>
))}
</tbody>
</table>
</div>
</section>
);
}
}

0 comments on commit 152f112

Please sign in to comment.