]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-16376 Update Hotspot page to consume descriptions from the rule api
authorMathieu Suen <mathieu.suen@sonarsource.com>
Tue, 31 May 2022 10:08:43 +0000 (12:08 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 1 Jun 2022 20:03:02 +0000 (20:03 +0000)
server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewer.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewer-test.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotViewer-test.tsx.snap

index 14c648765fd8b99a140eeaffd2e9c3735bc62a03..fc275159d78b29559ed436b73364d3446f68fd93 100644 (file)
@@ -18,6 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import { getRuleDetails } from '../../../api/rules';
 import { getSecurityHotspotDetails } from '../../../api/security-hotspots';
 import { scrollToElement } from '../../../helpers/scrolling';
 import {
@@ -25,7 +26,7 @@ import {
   HotspotStatusFilter,
   HotspotStatusOption
 } from '../../../types/security-hotspots';
-import { Component } from '../../../types/types';
+import { Component, RuleDescriptionSections } from '../../../types/types';
 import { getStatusFilterFromStatusOption } from '../utils';
 import HotspotViewerRenderer from './HotspotViewerRenderer';
 
@@ -74,16 +75,37 @@ export default class HotspotViewer extends React.PureComponent<Props, State> {
     this.mounted = false;
   }
 
-  fetchHotspot = () => {
+  fetchHotspot = async () => {
     this.setState({ loading: true });
-    return getSecurityHotspotDetails(this.props.hotspotKey)
-      .then(hotspot => {
-        if (this.mounted) {
-          this.setState({ hotspot, loading: false });
-        }
-        return hotspot;
-      })
-      .catch(() => this.mounted && this.setState({ loading: false }));
+    try {
+      const hotspot = await getSecurityHotspotDetails(this.props.hotspotKey);
+      const ruleDetails = await getRuleDetails({ key: hotspot.rule.key }).then(r => r.rule);
+
+      if (this.mounted) {
+        hotspot.rule.riskDescription =
+          ruleDetails.descriptionSections?.find(section =>
+            [RuleDescriptionSections.DEFAULT, RuleDescriptionSections.ROOT_CAUSE].includes(
+              section.key
+            )
+          )?.content || hotspot.rule.riskDescription;
+
+        hotspot.rule.fixRecommendations =
+          ruleDetails.descriptionSections?.find(
+            section => RuleDescriptionSections.HOW_TO_FIX === section.key
+          )?.content || hotspot.rule.fixRecommendations;
+
+        hotspot.rule.vulnerabilityDescription =
+          ruleDetails.descriptionSections?.find(
+            section => RuleDescriptionSections.ASSESS_THE_PROBLEM === section.key
+          )?.content || hotspot.rule.vulnerabilityDescription;
+
+        this.setState({ hotspot, loading: false });
+      }
+    } catch (error) {
+      if (this.mounted) {
+        this.setState({ loading: false });
+      }
+    }
   };
 
   handleHotspotUpdate = async (statusUpdate = false, statusOption?: HotspotStatusOption) => {
index 264e81a5812dcf0fa0f54ad9a036b88ad4b03acf..1ed1336d4626fbd27673ebe39e62ba26187b02b8 100644 (file)
 import { shallow } from 'enzyme';
 import { clone } from 'lodash';
 import * as React from 'react';
+import { getRuleDetails } from '../../../../api/rules';
 import { getSecurityHotspotDetails } from '../../../../api/security-hotspots';
 import { mockComponent } from '../../../../helpers/mocks/component';
 import { scrollToElement } from '../../../../helpers/scrolling';
+import { mockRuleDetails } from '../../../../helpers/testMocks';
 import { waitAndUpdate } from '../../../../helpers/testUtils';
 import { HotspotStatusOption } from '../../../../types/security-hotspots';
+import { RuleDescriptionSections } from '../../../../types/types';
 import HotspotViewer from '../HotspotViewer';
 import HotspotViewerRenderer from '../HotspotViewerRenderer';
 
 const hotspotKey = 'hotspot-key';
 
 jest.mock('../../../../api/security-hotspots', () => ({
-  getSecurityHotspotDetails: jest.fn().mockResolvedValue({ id: `I am a detailled hotspot` })
+  getSecurityHotspotDetails: jest
+    .fn()
+    .mockResolvedValue({ id: `I am a detailled hotspot`, rule: {} })
+}));
+
+jest.mock('../../../../api/rules', () => ({
+  getRuleDetails: jest.fn().mockResolvedValue({ rule: { descriptionSections: [] } })
 }));
 
 jest.mock('../../../../helpers/scrolling', () => ({
@@ -54,6 +63,35 @@ it('should render correctly', async () => {
   expect(getSecurityHotspotDetails).toHaveBeenCalledWith(newHotspotKey);
 });
 
+it('should render fetch rule details', async () => {
+  (getRuleDetails as jest.Mock).mockResolvedValueOnce({
+    rule: mockRuleDetails({
+      descriptionSections: [
+        {
+          key: RuleDescriptionSections.ASSESS_THE_PROBLEM,
+          content: 'assess'
+        },
+        {
+          key: RuleDescriptionSections.ROOT_CAUSE,
+          content: 'cause'
+        },
+        {
+          key: RuleDescriptionSections.HOW_TO_FIX,
+          content: 'how'
+        }
+      ]
+    })
+  });
+  const wrapper = shallowRender();
+  await waitAndUpdate(wrapper);
+
+  expect(wrapper.state().hotspot?.rule).toStrictEqual({
+    fixRecommendations: 'how',
+    riskDescription: 'cause',
+    vulnerabilityDescription: 'assess'
+  });
+});
+
 it('should refresh hotspot list on status update', () => {
   const onUpdateHotspot = jest.fn();
   const wrapper = shallowRender({ onUpdateHotspot });
index 0e615b87137556d59e0af1f47e6743000d972640..5b3e0ef3cc423177030bd5b4fbee492e551399cc 100644 (file)
@@ -71,6 +71,11 @@ exports[`should render correctly 2`] = `
   hotspot={
     Object {
       "id": "I am a detailled hotspot",
+      "rule": Object {
+        "fixRecommendations": undefined,
+        "riskDescription": undefined,
+        "vulnerabilityDescription": undefined,
+      },
     }
   }
   loading={false}