Browse Source

SONAR-21547 Fix activity/hotspots page sometimes stuck loading

tags/10.4.0.87286
David Cho-Lerat 3 months ago
parent
commit
dc10327ac1

+ 29
- 4
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx View File

@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import * as React from 'react';
import { useSearchParams } from 'react-router-dom';
import { getApplicationLeak } from '../../../api/application';
@@ -38,7 +39,7 @@ import {
isCustomGraph,
} from '../../../components/activity-graph/utils';
import { Location, Router, withRouter } from '../../../components/hoc/withRouter';
import { getBranchLikeQuery, isSameBranchLike } from '../../../helpers/branch-like';
import { getBranchLikeQuery } from '../../../helpers/branch-like';
import { HIDDEN_METRICS } from '../../../helpers/constants';
import { parseDate } from '../../../helpers/dates';
import { serializeStringArray } from '../../../helpers/query';
@@ -94,6 +95,7 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {

constructor(props: Props) {
super(props);

this.state = {
analyses: [],
analysesLoading: false,
@@ -117,15 +119,15 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {

const hasQueryChanged = prevProps.location.query !== unparsedQuery;

const hasBranchChanged = !isSameBranchLike(prevProps.branchLike, this.props.branchLike);
const wasBranchJustFetched = !!prevProps.isFetchingBranch && !this.props.isFetchingBranch;

if (this.isBranchReady() && (hasBranchChanged || hasQueryChanged)) {
if (this.isBranchReady() && (hasQueryChanged || wasBranchJustFetched)) {
const query = parseQuery(unparsedQuery);

if (
query.graph !== this.state.query.graph ||
customMetricsChanged(this.state.query, query) ||
hasBranchChanged
wasBranchJustFetched
) {
if (this.state.initialized) {
this.updateGraphData(query.graph || DEFAULT_GRAPH, query.customMetrics);
@@ -133,6 +135,7 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {
this.firstLoadData(query, this.props.component);
}
}

this.setState({ query });
}
}
@@ -172,6 +175,7 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {
this.state.query.graph || DEFAULT_GRAPH,
this.state.query.customMetrics,
);

this.setState(actions.deleteAnalysis(analysis));
}
});
@@ -199,11 +203,13 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {
ps,
...getBranchLikeQuery(this.props.branchLike),
};

return getProjectActivity({ ...additional, ...parameters }).then(({ analyses, paging }) => ({
analyses: analyses.map((analysis) => ({
...analysis,
date: parseDate(analysis.date),
})) as ParsedAnalysis[],

paging,
}));
};
@@ -212,6 +218,7 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {
if (metrics.length <= 0) {
return Promise.resolve([]);
}

return getAllTimeMachineData({
component: this.props.component.key,
metrics: metrics.join(),
@@ -219,6 +226,7 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {
}).then(({ measures }) =>
measures.map((measure) => ({
metric: measure.metric,

history: measure.history.map((analysis) => ({
date: parseDate(analysis.date),
value: analysis.value,
@@ -229,6 +237,7 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {

fetchAllActivities = (topLevelComponent: string) => {
this.setState({ analysesLoading: true });

this.loadAllActivities(topLevelComponent).then(
({ analyses }) => {
if (this.mounted) {
@@ -256,7 +265,9 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {
) {
return Promise.resolve(prevResult);
}

const nextPage = prevResult ? prevResult.paging.pageIndex + 1 : 1;

return this.fetchActivity(
project,
[
@@ -269,6 +280,7 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {
if (!prevResult) {
return this.loadAllActivities(project, result);
}

return this.loadAllActivities(project, {
analyses: prevResult.analyses.concat(result.analyses),
paging: result.paging,
@@ -278,6 +290,7 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {

getTopLevelComponent = (component: Component) => {
let current = component.breadcrumbs.length - 1;

while (
current > 0 &&
!(
@@ -290,6 +303,7 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {
) {
current--;
}

return component.breadcrumbs[current].key;
};

@@ -314,6 +328,7 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {
async firstLoadData(query: Query, component: Component) {
const graphMetrics = getHistoryMetrics(query.graph || DEFAULT_GRAPH, query.customMetrics);
const topLevelComponent = this.getTopLevelComponent(component);

try {
const [{ analyses }, measuresHistory, leaks] = await Promise.all([
this.fetchActivity(
@@ -326,7 +341,9 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {
ACTIVITY_PAGE_SIZE_FIRST_BATCH,
serializeQuery(query),
),

this.fetchMeasuresHistory(graphMetrics),

component.qualifier === ComponentQualifier.Application
? // eslint-disable-next-line local-rules/no-api-imports
getApplicationLeak(component.key)
@@ -335,6 +352,7 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {

if (this.mounted) {
let leakPeriodDate;

if (isApplication(component.qualifier) && leaks?.length) {
[leakPeriodDate] = leaks
.map((leak) => parseDate(leak.date))
@@ -363,6 +381,7 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {
updateGraphData = (graph: GraphType, customMetrics: string[]) => {
const graphMetrics = getHistoryMetrics(graph, customMetrics);
this.setState({ graphLoading: true });

this.fetchMeasuresHistory(graphMetrics).then(
(measuresHistory) => {
if (this.mounted) {
@@ -382,6 +401,7 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {
...this.state.query,
...newQuery,
});

this.props.router.push({
pathname: this.props.location.pathname,
query: {
@@ -394,6 +414,7 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {

render() {
const metrics = this.filterMetrics();

return (
<ProjectActivityAppRenderer
onAddCustomEvent={this.handleAddCustomEvent}
@@ -418,11 +439,13 @@ class ProjectActivityApp extends React.PureComponent<Props, State> {

const isFiltered = (searchParams: URLSearchParams) => {
let filtered = false;

searchParams.forEach((value, key) => {
if (key !== 'id' && value !== '') {
filtered = true;
}
});

return filtered;
};

@@ -442,9 +465,11 @@ function RedirectWrapper(props: Props) {
if (shouldRedirect) {
const query = parseQuery(searchParams);
const newQuery = { ...query, graph };

if (isCustomGraph(newQuery.graph)) {
searchParams.set('custom_metrics', customGraphs.join(','));
}

searchParams.set('graph', graph);
setSearchParams(searchParams, { replace: true });
}

+ 9
- 3
server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsApp.tsx View File

@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { flatMap, range } from 'lodash';
import * as React from 'react';
import { getMeasures } from '../../api/measures';
@@ -26,7 +27,7 @@ import withCurrentUserContext from '../../app/components/current-user/withCurren
import withIndexationGuard from '../../components/hoc/withIndexationGuard';
import { Location, Router, withRouter } from '../../components/hoc/withRouter';
import { getLeakValue } from '../../components/measure/utils';
import { getBranchLikeQuery, isPullRequest, isSameBranchLike } from '../../helpers/branch-like';
import { getBranchLikeQuery, isPullRequest } from '../../helpers/branch-like';
import { isInput } from '../../helpers/keyboardEventHelpers';
import { KeyboardKeys } from '../../helpers/keycodes';
import { getStandards } from '../../helpers/security-standard';
@@ -89,6 +90,7 @@ export class SecurityHotspotsApp extends React.PureComponent<Props, State> {
...this.constructFiltersFromProps(props),
status: HotspotStatusFilter.TO_REVIEW,
},

hotspots: [],
hotspotsPageIndex: 1,
hotspotsTotal: 0,
@@ -96,6 +98,7 @@ export class SecurityHotspotsApp extends React.PureComponent<Props, State> {
loadingMeasure: false,
loadingMore: false,
selectedHotspot: undefined,

standards: {
[SecurityStandard.CWE]: {},
[SecurityStandard.OWASP_ASVS_4_0]: {},
@@ -119,8 +122,10 @@ export class SecurityHotspotsApp extends React.PureComponent<Props, State> {
}

componentDidUpdate(previous: Props) {
const wasBranchJustFetched = !!previous.isFetchingBranch && !this.props.isFetchingBranch;

if (
!isSameBranchLike(this.props.branchLike, previous.branchLike) ||
wasBranchJustFetched ||
this.props.component.key !== previous.component.key ||
this.props.location.query.hotspots !== previous.location.query.hotspots ||
SECURITY_STANDARDS.some((s) => this.props.location.query[s] !== previous.location.query[s]) ||
@@ -130,7 +135,7 @@ export class SecurityHotspotsApp extends React.PureComponent<Props, State> {
}

if (
!isSameBranchLike(this.props.branchLike, previous.branchLike) ||
wasBranchJustFetched ||
isLoggedIn(this.props.currentUser) !== isLoggedIn(previous.currentUser) ||
this.props.location.query.assignedToMe !== previous.location.query.assignedToMe ||
this.props.location.query.inNewCodePeriod !== previous.location.query.inNewCodePeriod
@@ -337,6 +342,7 @@ export class SecurityHotspotsApp extends React.PureComponent<Props, State> {
category: string;
}
| undefined;

filterByCWE: string | undefined;
filterByFile: string | undefined;
page: number;

Loading…
Cancel
Save