]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-12719 Update list when hotspot status is changed
authorJeremy <jeremy.davis@sonarsource.com>
Tue, 17 Dec 2019 17:46:51 +0000 (18:46 +0100)
committerSonarTech <sonartech@sonarsource.com>
Mon, 13 Jan 2020 19:46:29 +0000 (20:46 +0100)
21 files changed:
server/sonar-web/src/main/js/apps/securityHotspots/SecurityHotspotsApp.tsx
server/sonar-web/src/main/js/apps/securityHotspots/SecurityHotspotsAppRenderer.tsx
server/sonar-web/src/main/js/apps/securityHotspots/__tests__/SecurityHotspotsApp-test.tsx
server/sonar-web/src/main/js/apps/securityHotspots/__tests__/SecurityHotspotsAppRenderer-test.tsx
server/sonar-web/src/main/js/apps/securityHotspots/__tests__/__snapshots__/SecurityHotspotsApp-test.tsx.snap
server/sonar-web/src/main/js/apps/securityHotspots/__tests__/__snapshots__/SecurityHotspotsAppRenderer-test.tsx.snap
server/sonar-web/src/main/js/apps/securityHotspots/components/HotspotActions.tsx
server/sonar-web/src/main/js/apps/securityHotspots/components/HotspotActionsForm.tsx
server/sonar-web/src/main/js/apps/securityHotspots/components/HotspotViewer.tsx
server/sonar-web/src/main/js/apps/securityHotspots/components/HotspotViewerRenderer.tsx
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/HotspotActions-test.tsx
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/HotspotActionsForm-test.tsx
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/HotspotViewer-test.tsx
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/HotspotViewerRenderer-test.tsx
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/HotspotCategory-test.tsx.snap
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/HotspotList-test.tsx.snap
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/HotspotListItem-test.tsx.snap
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/HotspotViewer-test.tsx.snap
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/HotspotViewerRenderer-test.tsx.snap
server/sonar-web/src/main/js/helpers/mocks/security-hotspots.ts
server/sonar-web/src/main/js/types/security-hotspots.ts

index e1d9e8a17fd39496dca296700234a77cef11f40b..78d3ddf7d530a0845c1e516c8a47fb5be4f0f324 100644 (file)
@@ -22,7 +22,7 @@ import { addNoFooterPageClass, removeNoFooterPageClass } from 'sonar-ui-common/h
 import { getSecurityHotspots } from '../../api/security-hotspots';
 import { getStandards } from '../../helpers/security-standard';
 import { BranchLike } from '../../types/branch-like';
-import { RawHotspot } from '../../types/security-hotspots';
+import { HotspotUpdate, RawHotspot } from '../../types/security-hotspots';
 import SecurityHotspotsAppRenderer from './SecurityHotspotsAppRenderer';
 import './styles.css';
 import { sortHotspots } from './utils';
@@ -95,6 +95,23 @@ export default class SecurityHotspotsApp extends React.PureComponent<Props, Stat
 
   handleHotspotClick = (key: string) => this.setState({ selectedHotspotKey: key });
 
+  handleHotspotUpdate = ({ key, status, resolution }: HotspotUpdate) => {
+    this.setState(({ hotspots }) => {
+      const index = hotspots.findIndex(h => h.key === key);
+
+      if (index > -1) {
+        const hotspot = {
+          ...hotspots[index],
+          status,
+          resolution
+        };
+
+        return { hotspots: [...hotspots.slice(0, index), hotspot, ...hotspots.slice(index + 1)] };
+      }
+      return null;
+    });
+  };
+
   render() {
     const { branchLike } = this.props;
     const { hotspots, loading, securityCategories, selectedHotspotKey } = this.state;
@@ -105,6 +122,7 @@ export default class SecurityHotspotsApp extends React.PureComponent<Props, Stat
         hotspots={hotspots}
         loading={loading}
         onHotspotClick={this.handleHotspotClick}
+        onUpdateHotspot={this.handleHotspotUpdate}
         securityCategories={securityCategories}
         selectedHotspotKey={selectedHotspotKey}
       />
index 59caa829c270cbeec9009fe518931184e25e34eb..9ba9dc438701dbd95e055b5f7de03403557bf173 100644 (file)
@@ -27,7 +27,7 @@ import A11ySkipTarget from '../../app/components/a11y/A11ySkipTarget';
 import Suggestions from '../../app/components/embed-docs-modal/Suggestions';
 import ScreenPositionHelper from '../../components/common/ScreenPositionHelper';
 import { BranchLike } from '../../types/branch-like';
-import { RawHotspot } from '../../types/security-hotspots';
+import { HotspotUpdate, RawHotspot } from '../../types/security-hotspots';
 import FilterBar from './components/FilterBar';
 import HotspotList from './components/HotspotList';
 import HotspotViewer from './components/HotspotViewer';
@@ -38,6 +38,7 @@ export interface SecurityHotspotsAppRendererProps {
   hotspots: RawHotspot[];
   loading: boolean;
   onHotspotClick: (key: string) => void;
+  onUpdateHotspot: (hotspot: HotspotUpdate) => void;
   selectedHotspotKey?: string;
   securityCategories: T.StandardSecurityCategories;
 }
@@ -91,6 +92,7 @@ export default function SecurityHotspotsAppRenderer(props: SecurityHotspotsAppRe
                       <HotspotViewer
                         branchLike={branchLike}
                         hotspotKey={selectedHotspotKey}
+                        onUpdateHotspot={props.onUpdateHotspot}
                         securityCategories={securityCategories}
                       />
                     )}
index b813bc82417b5e67567fd84414f1fcbeba4bc898..1b43fe4cfcc4ad30a73dd31a39e522f072067d2a 100644 (file)
@@ -26,7 +26,9 @@ import { mockMainBranch } from '../../../helpers/mocks/branch-like';
 import { mockRawHotspot } from '../../../helpers/mocks/security-hotspots';
 import { getStandards } from '../../../helpers/security-standard';
 import { mockComponent } from '../../../helpers/testMocks';
+import { HotspotResolution, HotspotStatus } from '../../../types/security-hotspots';
 import SecurityHotspotsApp from '../SecurityHotspotsApp';
+import SecurityHotspotsAppRenderer from '../SecurityHotspotsAppRenderer';
 
 jest.mock('sonar-ui-common/helpers/pages', () => ({
   addNoFooterPageClass: jest.fn(),
@@ -72,6 +74,30 @@ it('should load data correctly', async () => {
   expect(wrapper.state());
 });
 
+it('should handle hotspot update', async () => {
+  const key = 'hotspotKey';
+  const hotspots = [mockRawHotspot(), mockRawHotspot({ key })];
+  (getSecurityHotspots as jest.Mock).mockResolvedValue({
+    hotspots
+  });
+
+  const wrapper = shallowRender();
+
+  await waitAndUpdate(wrapper);
+
+  wrapper
+    .find(SecurityHotspotsAppRenderer)
+    .props()
+    .onUpdateHotspot({ key, status: HotspotStatus.REVIEWED, resolution: HotspotResolution.SAFE });
+
+  expect(wrapper.state().hotspots[0]).toEqual(hotspots[0]);
+  expect(wrapper.state().hotspots[1]).toEqual({
+    ...hotspots[1],
+    status: HotspotStatus.REVIEWED,
+    resolution: HotspotResolution.SAFE
+  });
+});
+
 function shallowRender(props: Partial<SecurityHotspotsApp['props']> = {}) {
   return shallow<SecurityHotspotsApp>(
     <SecurityHotspotsApp branchLike={mockMainBranch()} component={mockComponent()} {...props} />
index 8d7db84876b5a21009c27458e447923365ad6fe6..c46274751bf10f7c6d7dd0611248ba9545a82eca 100644 (file)
@@ -54,6 +54,7 @@ function shallowRender(props: Partial<SecurityHotspotsAppRendererProps> = {}) {
       hotspots={[]}
       loading={false}
       onHotspotClick={jest.fn()}
+      onUpdateHotspot={jest.fn()}
       securityCategories={{}}
       {...props}
     />
index 6ad55141e385c155e1c03ecdcca02c2a2855c703..56682f1ba2e02308f1f185cee8276ac0dbe86f5f 100644 (file)
@@ -13,6 +13,7 @@ exports[`should render correctly 1`] = `
   hotspots={Array []}
   loading={true}
   onHotspotClick={[Function]}
+  onUpdateHotspot={[Function]}
   securityCategories={Object {}}
 />
 `;
index f73832adf0837fc2cc61c011967c2a4bfc2e5616..4df9e59c2e91241f8c64562fb6da90ba1189e848 100644 (file)
@@ -118,10 +118,10 @@ exports[`should render correctly with hotspots 1`] = `
                   "line": 81,
                   "message": "'3' is a magic number.",
                   "project": "com.github.kevinsawicki:http-request",
-                  "resolution": "FALSE-POSITIVE",
+                  "resolution": undefined,
                   "rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck",
                   "securityCategory": "command-injection",
-                  "status": "RESOLVED",
+                  "status": "TO_REVIEW",
                   "updateDate": "2013-05-13T17:55:39+0200",
                   "vulnerabilityProbability": "HIGH",
                 },
@@ -133,10 +133,10 @@ exports[`should render correctly with hotspots 1`] = `
                   "line": 81,
                   "message": "'3' is a magic number.",
                   "project": "com.github.kevinsawicki:http-request",
-                  "resolution": "FALSE-POSITIVE",
+                  "resolution": undefined,
                   "rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck",
                   "securityCategory": "command-injection",
-                  "status": "RESOLVED",
+                  "status": "TO_REVIEW",
                   "updateDate": "2013-05-13T17:55:39+0200",
                   "vulnerabilityProbability": "HIGH",
                 },
@@ -198,10 +198,10 @@ exports[`should render correctly with hotspots 2`] = `
                   "line": 81,
                   "message": "'3' is a magic number.",
                   "project": "com.github.kevinsawicki:http-request",
-                  "resolution": "FALSE-POSITIVE",
+                  "resolution": undefined,
                   "rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck",
                   "securityCategory": "command-injection",
-                  "status": "RESOLVED",
+                  "status": "TO_REVIEW",
                   "updateDate": "2013-05-13T17:55:39+0200",
                   "vulnerabilityProbability": "HIGH",
                 },
@@ -213,10 +213,10 @@ exports[`should render correctly with hotspots 2`] = `
                   "line": 81,
                   "message": "'3' is a magic number.",
                   "project": "com.github.kevinsawicki:http-request",
-                  "resolution": "FALSE-POSITIVE",
+                  "resolution": undefined,
                   "rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck",
                   "securityCategory": "command-injection",
-                  "status": "RESOLVED",
+                  "status": "TO_REVIEW",
                   "updateDate": "2013-05-13T17:55:39+0200",
                   "vulnerabilityProbability": "HIGH",
                 },
@@ -232,6 +232,7 @@ exports[`should render correctly with hotspots 2`] = `
         >
           <HotspotViewer
             hotspotKey="h2"
+            onUpdateHotspot={[MockFunction]}
             securityCategories={Object {}}
           />
         </div>
index 9f40f72c4d14330c4de6cf0f3c5a0eadcb0d4e36..2a59969b48f98e8505b64c9e35e15068583d5c4f 100644 (file)
@@ -24,10 +24,12 @@ import OutsideClickHandler from 'sonar-ui-common/components/controls/OutsideClic
 import DropdownIcon from 'sonar-ui-common/components/icons/DropdownIcon';
 import { PopupPlacement } from 'sonar-ui-common/components/ui/popups';
 import { translate } from 'sonar-ui-common/helpers/l10n';
+import { HotspotUpdateFields } from '../../../types/security-hotspots';
 import HotspotActionsForm from './HotspotActionsForm';
 
 export interface HotspotActionsProps {
   hotspotKey: string;
+  onSubmit: (hotspot: HotspotUpdateFields) => void;
 }
 
 const ESCAPE_KEY = 'Escape';
@@ -59,7 +61,13 @@ export default function HotspotActions(props: HotspotActionsProps) {
       {open && (
         <OutsideClickHandler onClickOutside={() => setOpen(false)}>
           <DropdownOverlay placement={PopupPlacement.BottomRight}>
-            <HotspotActionsForm hotspotKey={props.hotspotKey} onSubmit={() => setOpen(false)} />
+            <HotspotActionsForm
+              hotspotKey={props.hotspotKey}
+              onSubmit={data => {
+                setOpen(false);
+                props.onSubmit(data);
+              }}
+            />
           </DropdownOverlay>
         </OutsideClickHandler>
       )}
index e34a2ad1accfd94be0c721f51cf38f7610145442..97713831d4cb1b706730725660bbb1b16ec127be 100644 (file)
@@ -23,13 +23,14 @@ import {
   HotspotResolution,
   HotspotSetStatusRequest,
   HotspotStatus,
-  HotspotStatusOptions
+  HotspotStatusOptions,
+  HotspotUpdateFields
 } from '../../../types/security-hotspots';
 import HotspotActionsFormRenderer from './HotspotActionsFormRenderer';
 
 interface Props {
   hotspotKey: string;
-  onSubmit: () => void;
+  onSubmit: (data: HotspotUpdateFields) => void;
 }
 
 interface State {
@@ -65,9 +66,9 @@ export default class HotspotActionsForm extends React.Component<Props, State> {
     this.setState({ submitting: true });
     return setSecurityHotspotStatus(data)
       .then(() => {
-        this.props.onSubmit();
+        this.props.onSubmit({ status, resolution: data.resolution });
       })
-      .finally(() => {
+      .catch(() => {
         this.setState({ submitting: false });
       });
   };
index 9ca28887e072da4dd5f3ca075123ce2ef79b4b89..b61510d9ad8d729ee70fd2809136888a30200d9e 100644 (file)
 import * as React from 'react';
 import { getSecurityHotspotDetails } from '../../../api/security-hotspots';
 import { BranchLike } from '../../../types/branch-like';
-import { DetailedHotspot } from '../../../types/security-hotspots';
+import {
+  DetailedHotspot,
+  HotspotUpdate,
+  HotspotUpdateFields
+} from '../../../types/security-hotspots';
 import HotspotViewerRenderer from './HotspotViewerRenderer';
 
 interface Props {
   branchLike?: BranchLike;
   hotspotKey: string;
+  onUpdateHotspot: (hotspot: HotspotUpdate) => void;
   securityCategories: T.StandardSecurityCategories;
 }
 
@@ -60,6 +65,10 @@ export default class HotspotViewer extends React.PureComponent<Props, State> {
       .finally(() => this.mounted && this.setState({ loading: false }));
   }
 
+  handleHotspotUpdate = (data: HotspotUpdateFields) => {
+    this.props.onUpdateHotspot({ key: this.props.hotspotKey, ...data });
+  };
+
   render() {
     const { branchLike, securityCategories } = this.props;
     const { hotspot, loading } = this.state;
@@ -69,6 +78,7 @@ export default class HotspotViewer extends React.PureComponent<Props, State> {
         branchLike={branchLike}
         hotspot={hotspot}
         loading={loading}
+        onUpdateHotspot={this.handleHotspotUpdate}
         securityCategories={securityCategories}
       />
     );
index 18cdf29c91b58a86d79d6255598a32cc67bb795a..163693de895e2347d80687495d0421f229195c2c 100644 (file)
@@ -23,7 +23,7 @@ import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n
 import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
 import { isLoggedIn } from '../../../helpers/users';
 import { BranchLike } from '../../../types/branch-like';
-import { DetailedHotspot } from '../../../types/security-hotspots';
+import { DetailedHotspot, HotspotUpdateFields } from '../../../types/security-hotspots';
 import HotspotActions from './HotspotActions';
 import HotspotSnippetContainer from './HotspotSnippetContainer';
 import HotspotViewerTabs from './HotspotViewerTabs';
@@ -33,6 +33,7 @@ export interface HotspotViewerRendererProps {
   currentUser: T.CurrentUser;
   hotspot?: DetailedHotspot;
   loading: boolean;
+  onUpdateHotspot: (hotspot: HotspotUpdateFields) => void;
   securityCategories: T.StandardSecurityCategories;
 }
 
@@ -46,7 +47,9 @@ export function HotspotViewerRenderer(props: HotspotViewerRendererProps) {
           <div className="big-spacer-bottom">
             <div className="display-flex-space-between">
               <h1>{hotspot.message}</h1>
-              {isLoggedIn(currentUser) && <HotspotActions hotspotKey={hotspot.key} />}
+              {isLoggedIn(currentUser) && (
+                <HotspotActions hotspotKey={hotspot.key} onSubmit={props.onUpdateHotspot} />
+              )}
             </div>
             <div className="text-muted">
               <span>{translate('hotspot.category')}</span>
index 7f6f2387fc9fa8416246c886e85abfe494aa842d..aa92a0044880b796ea19c6000f2df220833c40b8 100644 (file)
@@ -66,5 +66,5 @@ it('should register an eventlistener', () => {
 });
 
 function shallowRender(props: Partial<HotspotActionsProps> = {}) {
-  return shallow(<HotspotActions hotspotKey="key" {...props} />);
+  return shallow(<HotspotActions hotspotKey="key" onSubmit={jest.fn()} {...props} />);
 }
index a19f3f27bfd707453745a6bd84815e04aaaa8318..b4add87f98cf1c2755c7e1b13076c90b13319e29 100644 (file)
@@ -55,7 +55,6 @@ it('should handle submit', async () => {
 
   expect(wrapper.state().submitting).toBe(true);
   await promise;
-  expect(wrapper.state().submitting).toBe(false);
   expect(setSecurityHotspotStatus).toBeCalledWith({
     hotspot: 'key',
     status: HotspotStatus.TO_REVIEW
index 380d91bd3cc5824d351c6c34fc73b26acbc5c416..01e8029eee08386132670ca2180cb1848ba0de4e 100644 (file)
@@ -49,6 +49,7 @@ function shallowRender(props?: Partial<HotspotViewer['props']>) {
   return shallow<HotspotViewer>(
     <HotspotViewer
       hotspotKey={hotspotKey}
+      onUpdateHotspot={jest.fn()}
       securityCategories={{ cat1: { title: 'cat1' } }}
       {...props}
     />
index 70eaf778cd825307a9ce98cdac6c39ff17eaa5c5..84025073a894b76ecf0a73ec3896e83c86107e65 100644 (file)
@@ -40,6 +40,7 @@ function shallowRender(props?: Partial<HotspotViewerRendererProps>) {
       currentUser={mockCurrentUser()}
       hotspot={mockDetailledHotspot()}
       loading={false}
+      onUpdateHotspot={jest.fn()}
       securityCategories={{ 'sql-injection': { title: 'SQL injection' } }}
       {...props}
     />
index 3f07388c5adbc3dbd232f174d33b9548d455a2fd..dfde95a28d3f54425a666120af19e8f048addc9a 100644 (file)
@@ -67,10 +67,10 @@ exports[`should handle collapse and expand 2`] = `
             "line": 81,
             "message": "'3' is a magic number.",
             "project": "com.github.kevinsawicki:http-request",
-            "resolution": "FALSE-POSITIVE",
+            "resolution": undefined,
             "rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck",
             "securityCategory": "command-injection",
-            "status": "RESOLVED",
+            "status": "TO_REVIEW",
             "updateDate": "2013-05-13T17:55:39+0200",
             "vulnerabilityProbability": "HIGH",
           }
@@ -124,10 +124,10 @@ exports[`should render correctly with hotspots 1`] = `
             "line": 81,
             "message": "'3' is a magic number.",
             "project": "com.github.kevinsawicki:http-request",
-            "resolution": "FALSE-POSITIVE",
+            "resolution": undefined,
             "rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck",
             "securityCategory": "command-injection",
-            "status": "RESOLVED",
+            "status": "TO_REVIEW",
             "updateDate": "2013-05-13T17:55:39+0200",
             "vulnerabilityProbability": "HIGH",
           }
@@ -149,10 +149,10 @@ exports[`should render correctly with hotspots 1`] = `
             "line": 81,
             "message": "'3' is a magic number.",
             "project": "com.github.kevinsawicki:http-request",
-            "resolution": "FALSE-POSITIVE",
+            "resolution": undefined,
             "rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck",
             "securityCategory": "command-injection",
-            "status": "RESOLVED",
+            "status": "TO_REVIEW",
             "updateDate": "2013-05-13T17:55:39+0200",
             "vulnerabilityProbability": "HIGH",
           }
index 68c4b07fa9bbe8ff6fc1c7e9105d7338ba63c00b..f222cf6e39e328962b5f4786077f8f526e803f8d 100644 (file)
@@ -67,10 +67,10 @@ exports[`should render correctly with hotspots 1`] = `
                   "line": 81,
                   "message": "'3' is a magic number.",
                   "project": "com.github.kevinsawicki:http-request",
-                  "resolution": "FALSE-POSITIVE",
+                  "resolution": undefined,
                   "rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck",
                   "securityCategory": "cat1",
-                  "status": "RESOLVED",
+                  "status": "TO_REVIEW",
                   "updateDate": "2013-05-13T17:55:39+0200",
                   "vulnerabilityProbability": "HIGH",
                 },
@@ -101,10 +101,10 @@ exports[`should render correctly with hotspots 1`] = `
                   "line": 81,
                   "message": "'3' is a magic number.",
                   "project": "com.github.kevinsawicki:http-request",
-                  "resolution": "FALSE-POSITIVE",
+                  "resolution": undefined,
                   "rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck",
                   "securityCategory": "cat2",
-                  "status": "RESOLVED",
+                  "status": "TO_REVIEW",
                   "updateDate": "2013-05-13T17:55:39+0200",
                   "vulnerabilityProbability": "HIGH",
                 },
@@ -154,10 +154,10 @@ exports[`should render correctly with hotspots 1`] = `
                   "line": 81,
                   "message": "'3' is a magic number.",
                   "project": "com.github.kevinsawicki:http-request",
-                  "resolution": "FALSE-POSITIVE",
+                  "resolution": undefined,
                   "rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck",
                   "securityCategory": "cat1",
-                  "status": "RESOLVED",
+                  "status": "TO_REVIEW",
                   "updateDate": "2013-05-13T17:55:39+0200",
                   "vulnerabilityProbability": "MEDIUM",
                 },
@@ -169,10 +169,10 @@ exports[`should render correctly with hotspots 1`] = `
                   "line": 81,
                   "message": "'3' is a magic number.",
                   "project": "com.github.kevinsawicki:http-request",
-                  "resolution": "FALSE-POSITIVE",
+                  "resolution": undefined,
                   "rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck",
                   "securityCategory": "cat1",
-                  "status": "RESOLVED",
+                  "status": "TO_REVIEW",
                   "updateDate": "2013-05-13T17:55:39+0200",
                   "vulnerabilityProbability": "MEDIUM",
                 },
@@ -203,10 +203,10 @@ exports[`should render correctly with hotspots 1`] = `
                   "line": 81,
                   "message": "'3' is a magic number.",
                   "project": "com.github.kevinsawicki:http-request",
-                  "resolution": "FALSE-POSITIVE",
+                  "resolution": undefined,
                   "rule": "checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck",
                   "securityCategory": "cat2",
-                  "status": "RESOLVED",
+                  "status": "TO_REVIEW",
                   "updateDate": "2013-05-13T17:55:39+0200",
                   "vulnerabilityProbability": "MEDIUM",
                 },
index 457deefac039ca91a628015a1de9af371e1d0003..7df6dbe4ac588d7ea6d6189cff6a94868b83c42a 100644 (file)
@@ -14,7 +14,7 @@ exports[`should render correctly 1`] = `
   <div
     className="badge spacer-top"
   >
-    issue.status.RESOLVED
+    issue.status.TO_REVIEW
   </div>
 </a>
 `;
@@ -33,7 +33,7 @@ exports[`should render correctly 2`] = `
   <div
     className="badge spacer-top"
   >
-    issue.status.RESOLVED
+    issue.status.TO_REVIEW
   </div>
 </a>
 `;
index 39de7c9e177aff8bd7cbdbf54a5edcb62e5c3a24..c9bd0adb0ab9506755dd744ff124915eecba2fae 100644 (file)
@@ -3,6 +3,7 @@
 exports[`should render correctly 1`] = `
 <Connect(withCurrentUser(HotspotViewerRenderer))
   loading={true}
+  onUpdateHotspot={[Function]}
   securityCategories={
     Object {
       "cat1": Object {
@@ -21,6 +22,7 @@ exports[`should render correctly 2`] = `
     }
   }
   loading={false}
+  onUpdateHotspot={[Function]}
   securityCategories={
     Object {
       "cat1": Object {
index 315b57ab62795c70e87b63ea8d067f04c3f55c18..eda423b8398ed876dc299dc8efc5a387f4741e2e 100644 (file)
@@ -689,6 +689,7 @@ exports[`should render correctly: user logged in 1`] = `
         </h1>
         <HotspotActions
           hotspotKey="01fc972e-2a3c-433e-bcae-0bd7f88f5123"
+          onSubmit={[MockFunction]}
         />
       </div>
       <div
index 007a7c29df272bae8b6902eaa74a9718d5b15e38..58ebfcbe3658371cfac8020292faa7444c1f0288 100644 (file)
@@ -21,6 +21,7 @@ import { ComponentQualifier } from '../../types/component';
 import {
   DetailedHotspot,
   DetailedHotspotRule,
+  HotspotStatus,
   RawHotspot,
   RiskExposure
 } from '../../types/security-hotspots';
@@ -32,8 +33,8 @@ export function mockRawHotspot(overrides: Partial<RawHotspot> = {}): RawHotspot
     component: 'com.github.kevinsawicki:http-request:com.github.kevinsawicki.http.HttpRequest',
     project: 'com.github.kevinsawicki:http-request',
     rule: 'checkstyle:com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck',
-    status: 'RESOLVED',
-    resolution: 'FALSE-POSITIVE',
+    status: HotspotStatus.TO_REVIEW,
+    resolution: undefined,
     securityCategory: 'command-injection',
     vulnerabilityProbability: RiskExposure.HIGH,
     message: "'3' is a magic number.",
index cabf1267559f842e0fcfcd8b05911274d8653238..13b8a12bb01d9bf6d4d7f60235ca51de3da00ab0 100644 (file)
@@ -48,7 +48,7 @@ export interface RawHotspot {
   line?: number;
   message: string;
   project: string;
-  resolution: string;
+  resolution?: string;
   rule: string;
   securityCategory: string;
   status: string;
@@ -66,13 +66,22 @@ export interface DetailedHotspot {
   line?: number;
   message: string;
   project: T.Component;
-  resolution: string;
+  resolution?: string;
   rule: DetailedHotspotRule;
   status: string;
   textRange: T.TextRange;
   updateDate: string;
 }
 
+export interface HotspotUpdateFields {
+  status: HotspotStatus;
+  resolution?: HotspotResolution;
+}
+
+export interface HotspotUpdate extends HotspotUpdateFields {
+  key: string;
+}
+
 export interface DetailedHotspotRule {
   fixRecommendations?: string;
   key: string;