]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-21547 Fix activity/hotspots page sometimes stuck loading
authorDavid Cho-Lerat <david.cho-lerat@sonarsource.com>
Wed, 31 Jan 2024 11:22:28 +0000 (12:22 +0100)
committersonartech <sonartech@sonarsource.com>
Wed, 31 Jan 2024 20:03:37 +0000 (20:03 +0000)
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx
server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsApp.tsx

index 122eacca4226c2d95f6471ac1aaf5a24bfaf21bc..92422ac772673ae4e586b7bd40f4021dd2192e16 100644 (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 });
     }
index ddcfbeb2f7d7c190528e8f4cd441c0b8b5bc2136..2e85343ff49000a3cd53f7613ee37399585cebf9 100644 (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;