]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19754 Lock application hotspots when reindexing
authorDavid Cho-Lerat <david.cho-lerat@sonarsource.com>
Tue, 11 Jul 2023 11:58:44 +0000 (13:58 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 19 Jul 2023 20:03:05 +0000 (20:03 +0000)
server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsApp.tsx
server/sonar-web/src/main/js/components/hoc/__tests__/withIndexationGuard-test.tsx
server/sonar-web/src/main/js/components/hoc/withIndexationGuard.tsx

index 795d7fc2ed757fcb61a9266e4bd968955c0ec5fa..b9c9a1947cdbbb4ab4f3fd374e53ca66d63b23b5 100644 (file)
@@ -24,6 +24,7 @@ import { getMeasures } from '../../api/measures';
 import { getSecurityHotspotList, getSecurityHotspots } from '../../api/security-hotspots';
 import withComponentContext from '../../app/components/componentContext/withComponentContext';
 import withCurrentUserContext from '../../app/components/current-user/withCurrentUserContext';
+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';
@@ -32,6 +33,7 @@ import { KeyboardKeys } from '../../helpers/keycodes';
 import { getStandards } from '../../helpers/security-standard';
 import { withBranchLikes } from '../../queries/branch';
 import { BranchLike } from '../../types/branch-like';
+import { ComponentQualifier } from '../../types/component';
 import { MetricKey } from '../../types/metrics';
 import { SecurityStandard, Standards } from '../../types/security';
 import {
@@ -108,7 +110,9 @@ export class SecurityHotspotsApp extends React.PureComponent<Props, State> {
 
   componentDidMount() {
     this.mounted = true;
+
     this.fetchInitialData();
+
     this.registerKeyboardEvents();
   }
 
@@ -472,12 +476,12 @@ export class SecurityHotspotsApp extends React.PureComponent<Props, State> {
     this.props.router.push({
       pathname: this.props.location.pathname,
       query: {
+        assignedToMe: undefined,
         file: undefined,
         fileUuid: undefined,
         hotspots: [],
-        sinceLeakPeriod: undefined,
-        assignedToMe: undefined,
         id: this.props.component.key,
+        sinceLeakPeriod: undefined,
       },
     });
   };
@@ -551,6 +555,7 @@ export class SecurityHotspotsApp extends React.PureComponent<Props, State> {
 
   render() {
     const { branchLike, component } = this.props;
+
     const {
       filterByCategory,
       filterByCWE,
@@ -572,10 +577,10 @@ export class SecurityHotspotsApp extends React.PureComponent<Props, State> {
       <SecurityHotspotsAppRenderer
         branchLike={branchLike}
         component={component}
-        filters={filters}
         filterByCategory={filterByCategory}
         filterByCWE={filterByCWE}
         filterByFile={filterByFile}
+        filters={filters}
         hotspots={hotspots}
         hotspotsReviewedMeasure={hotspotsReviewedMeasure}
         hotspotsTotal={hotspotsTotal}
@@ -586,12 +591,12 @@ export class SecurityHotspotsApp extends React.PureComponent<Props, State> {
         loadingMeasure={loadingMeasure}
         loadingMore={loadingMore}
         onChangeFilters={this.handleChangeFilters}
-        onShowAllHotspots={this.handleShowAllHotspots}
         onHotspotClick={this.handleHotspotClick}
         onLoadMore={this.handleLoadMore}
+        onLocationClick={this.handleLocationClick}
+        onShowAllHotspots={this.handleShowAllHotspots}
         onSwitchStatusFilter={this.handleChangeStatusFilter}
         onUpdateHotspot={this.handleHotspotUpdate}
-        onLocationClick={this.handleLocationClick}
         securityCategories={standards[SecurityStandard.SONARSOURCE]}
         selectedHotspot={selectedHotspot}
         selectedHotspotLocation={selectedHotspotLocationIndex}
@@ -602,5 +607,15 @@ export class SecurityHotspotsApp extends React.PureComponent<Props, State> {
 }
 
 export default withRouter(
-  withComponentContext(withCurrentUserContext(withBranchLikes(SecurityHotspotsApp)))
+  withComponentContext(
+    withCurrentUserContext(
+      withBranchLikes(
+        withIndexationGuard({
+          Component: SecurityHotspotsApp,
+          showIndexationMessage: ({ component }) =>
+            !!(component.qualifier === ComponentQualifier.Application && component.needIssueSync),
+        })
+      )
+    )
+  )
 );
index 39cab4e402e4b918ddace7d84a49184f2e3b6b0c..13683555887c11a7e2c58310313babd75a25900a 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { mount } from 'enzyme';
+
+import { render, screen } from '@testing-library/react';
 import * as React from 'react';
 import { IndexationContext } from '../../../app/components/indexation/IndexationContext';
-import { IndexationContextInterface } from '../../../types/indexation';
 import withIndexationGuard from '../withIndexationGuard';
 
-it('should not render children because indexation is in progress', () => {
-  const wrapper = mountRender();
-  expect(wrapper.find(TestComponent).exists()).toBe(false);
-});
+describe('withIndexationGuard', () => {
+  it('should render indexation message when showIndexationMessage returns true', () => {
+    renderComponentWithIndexationGuard(() => true);
+    expect(screen.getByText(/indexation\.page_unavailable\.description/)).toBeInTheDocument();
+  });
 
-it('should not render children because indexation has failures', () => {
-  const wrapper = mountRender({
-    status: { isCompleted: true, percentCompleted: 100, hasFailures: true },
+  it('should render children when showIndexationMessage returns false', () => {
+    renderComponentWithIndexationGuard(() => false);
+    expect(screen.getByText('TestComponent')).toBeInTheDocument();
   });
-  expect(wrapper.find(TestComponent).exists()).toBe(false);
 });
 
-it('should render children because indexation is completed without failures', () => {
-  const wrapper = mountRender({
-    status: { isCompleted: true, percentCompleted: 100, hasFailures: false },
+function renderComponentWithIndexationGuard(showIndexationMessage: () => boolean) {
+  const TestComponentWithGuard = withIndexationGuard({
+    Component: TestComponent,
+    showIndexationMessage,
   });
-  expect(wrapper.find(TestComponent).exists()).toBe(true);
-});
 
-function mountRender(context?: Partial<IndexationContextInterface>) {
-  return mount(
+  return render(
     <IndexationContext.Provider
       value={{
         status: { isCompleted: false, percentCompleted: 23, hasFailures: false },
-        ...context,
       }}
     >
       <TestComponentWithGuard />
@@ -55,10 +52,6 @@ function mountRender(context?: Partial<IndexationContextInterface>) {
   );
 }
 
-class TestComponent extends React.PureComponent {
-  render() {
-    return <h1>TestComponent</h1>;
-  }
+function TestComponent() {
+  return <h1>TestComponent</h1>;
 }
-
-const TestComponentWithGuard = withIndexationGuard(TestComponent);
index b70ee49246c5edc09469fdc183446dce200016de..f3b882132d4ea2145325675e563d05c0d2d37c88 100644 (file)
  */
 
 import * as React from 'react';
-import { IndexationContext } from '../../app/components/indexation/IndexationContext';
 import PageUnavailableDueToIndexation from '../../app/components/indexation/PageUnavailableDueToIndexation';
 
-export default function withIndexationGuard<P>(WrappedComponent: React.ComponentType<P>) {
+export default function withIndexationGuard<P>({
+  Component,
+  showIndexationMessage,
+}: {
+  Component: React.ComponentType<P>;
+  showIndexationMessage: (props: P) => boolean;
+}) {
   return function WithIndexationGuard(props: React.PropsWithChildren<P>) {
-    return (
-      <IndexationContext.Consumer>
-        {(context) =>
-          context?.status.isCompleted && !context?.status.hasFailures ? (
-            <WrappedComponent {...props} />
-          ) : (
-            <PageUnavailableDueToIndexation />
-          )
-        }
-      </IndexationContext.Consumer>
+    return showIndexationMessage(props) ? (
+      <PageUnavailableDueToIndexation />
+    ) : (
+      <Component {...props} />
     );
   };
 }