ui/infra-compare/InfraCompareTableView.jsx (226 lines of code) (raw):
import React from 'react';
import PropTypes from 'prop-types';
import { Col, Row, Container, Alert } from 'reactstrap';
import ErrorMessages from '../shared/ErrorMessages';
import { genericErrorMessage, errorMessageClass } from '../helpers/constants';
import ErrorBoundary from '../shared/ErrorBoundary';
import { getData } from '../helpers/http';
import { createApiUrl } from '../helpers/url';
import LoadingSpinner from '../shared/LoadingSpinner';
import TruncatedText from '../shared/TruncatedText';
import RevisionInformation from '../shared/RevisionInformation';
import ComparePageTitle from '../shared/ComparePageTitle';
import InfraCompareTableControls from './InfraCompareTableControls';
import { compareDefaultTimeRange, endpoints, phTimeRanges } from './constants';
export default class InfraCompareTableView extends React.Component {
constructor(props) {
super(props);
this.state = {
compareResults: new Map(),
failureMessages: [],
loading: false,
timeRange: this.setTimeRange(),
tabTitle: null,
};
}
componentDidMount() {
const { compareData, location } = this.props;
if (
compareData &&
compareData.size > 0 &&
location.pathname === '/infracompare'
) {
this.setState({ compareResults: compareData });
} else {
this.getInfraData();
}
}
componentDidUpdate(prevProps) {
if (this.props.location.search !== prevProps.location.search) {
this.getInfraData();
}
}
setTimeRange = () => {
const { selectedTimeRange, originalRevision } = this.props.validated;
if (originalRevision) {
return null;
}
let timeRange;
if (selectedTimeRange) {
timeRange = phTimeRanges.find(
(timeRange) => timeRange.value === parseInt(selectedTimeRange, 10),
);
}
return timeRange || compareDefaultTimeRange;
};
getInfraData = async () => {
const { getQueryParams, getDisplayResults } = this.props;
const {
originalProject,
originalRevision,
newProject,
newRevision,
} = this.props.validated;
const { timeRange, failureMessages } = this.state;
this.setState({ loading: true });
const [originalParams, newParams] = getQueryParams(timeRange);
const [originalResults, newResults] = await Promise.all([
getData(createApiUrl(endpoints.infra_compare, originalParams)),
getData(createApiUrl(endpoints.infra_compare, newParams)),
]);
if (originalResults.failureStatus) {
return this.setState({
failureMessages: [originalResults.data, ...failureMessages],
loading: false,
});
}
if (newResults.failureStatus) {
return this.setState({
failureMessages: [newResults.data, ...failureMessages],
loading: false,
});
}
const data = [...originalResults.data, ...newResults.data];
let title;
if (!data.length) {
return this.setState({ loading: false });
}
const tableNames = [
...new Set(data.map((item) => item.job_type__name.replace(/-\d+$/, ''))),
].sort();
const text = originalRevision
? `${originalRevision} (${originalProject})`
: originalProject;
this.setState({
tabTitle:
title ||
`Comparison between ${text} and ${newRevision} (${newProject})`,
});
const updates = getDisplayResults(
originalResults.data,
newResults.data,
tableNames,
);
updates.title = title;
return this.setState(updates);
};
updateTimeRange = (selection) => {
const { updateParams } = this.props.validated;
const timeRange = phTimeRanges.find((item) => item.text === selection);
updateParams({ selectedTimeRange: timeRange.value });
this.setState({ timeRange }, () => this.getInfraData());
};
render() {
const {
originalProject,
newProject,
originalRevision,
newRevision,
originalResultSet,
newResultSet,
pageTitle,
} = this.props.validated;
const { jobsNotDisplayed } = this.props;
const {
compareResults,
loading,
failureMessages,
timeRange,
tabTitle,
} = this.state;
const compareDropdowns = [];
const params = {
originalProject,
newProject,
newRevision,
};
if (originalRevision) {
params.originalRevision = originalRevision;
} else if (timeRange) {
params.selectedTimeRange = timeRange.value;
}
if (!originalRevision) {
compareDropdowns.push({
options: phTimeRanges.map((option) => option.text),
selectedItem: timeRange.text,
updateData: (timeRange) => this.updateTimeRange(timeRange),
});
}
return (
<Container fluid className="max-width-default">
{loading && !failureMessages.length && <LoadingSpinner />}
<ErrorBoundary
errorClasses={errorMessageClass}
message={genericErrorMessage}
>
<div className="mx-auto">
<Row className="justify-content-center">
<Col sm="8" className="text-center">
{failureMessages.length !== 0 && (
<ErrorMessages errorMessages={failureMessages} />
)}
</Col>
</Row>
{newRevision && newProject && (originalRevision || timeRange) && (
<Row>
<Col sm="12" className="text-center pb-1">
<h1>
<ComparePageTitle
title="Infra Compare Revisions"
pageTitleQueryParam={pageTitle}
defaultPageTitle={tabTitle}
/>
</h1>
<RevisionInformation
originalProject={originalProject}
originalRevision={originalRevision}
originalResultSet={originalResultSet}
newProject={newProject}
newRevision={newRevision}
newResultSet={newResultSet}
selectedTimeRange={timeRange}
/>
</Col>
</Row>
)}
{jobsNotDisplayed && jobsNotDisplayed.length > 0 && (
<Row className="pt-5 justify-content-center">
<Col small="12" className="px-0 max-width-default">
<Alert color="warning">
<TruncatedText
title="Tests without results: "
maxLength={174}
text={jobsNotDisplayed.join(', ')}
/>
</Alert>
</Col>
</Row>
)}
<InfraCompareTableControls
{...this.props}
updateState={(state) => this.setState(state)}
compareResults={compareResults}
/>
</div>
</ErrorBoundary>
</Container>
);
}
}
InfraCompareTableView.propTypes = {
validated: PropTypes.shape({
originalResultSet: PropTypes.shape({}),
newResultSet: PropTypes.shape({}),
newRevision: PropTypes.string,
originalProject: PropTypes.string,
newProject: PropTypes.string,
originalRevision: PropTypes.string,
selectedTimeRange: PropTypes.string,
updateParams: PropTypes.func.isRequired,
}),
getDisplayResults: PropTypes.func.isRequired,
getQueryParams: PropTypes.func.isRequired,
};
InfraCompareTableView.defaultProps = {
validated: PropTypes.shape({}),
};