Browse Source

SONAR-12751 Add Select to filter by status

tags/8.2.0.32929
Jeremy Davis 4 years ago
parent
commit
acdeca733b
18 changed files with 291 additions and 41 deletions
  1. 5
    1
      server/sonar-web/src/main/js/api/security-hotspots.ts
  2. 69
    19
      server/sonar-web/src/main/js/apps/securityHotspots/SecurityHotspotsApp.tsx
  3. 13
    3
      server/sonar-web/src/main/js/apps/securityHotspots/SecurityHotspotsAppRenderer.tsx
  4. 45
    2
      server/sonar-web/src/main/js/apps/securityHotspots/__tests__/SecurityHotspotsApp-test.tsx
  5. 3
    0
      server/sonar-web/src/main/js/apps/securityHotspots/__tests__/SecurityHotspotsAppRenderer-test.tsx
  6. 2
    0
      server/sonar-web/src/main/js/apps/securityHotspots/__tests__/__snapshots__/SecurityHotspotsApp-test.tsx.snap
  7. 6
    1
      server/sonar-web/src/main/js/apps/securityHotspots/__tests__/__snapshots__/SecurityHotspotsAppRenderer-test.tsx.snap
  8. 25
    2
      server/sonar-web/src/main/js/apps/securityHotspots/components/FilterBar.tsx
  9. 4
    3
      server/sonar-web/src/main/js/apps/securityHotspots/components/HotspotList.tsx
  10. 4
    4
      server/sonar-web/src/main/js/apps/securityHotspots/components/HotspotListItem.tsx
  11. 51
    0
      server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/FilterBar-test.tsx
  12. 2
    1
      server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/HotspotList-test.tsx
  13. 1
    1
      server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/HotspotListItem-test.tsx
  14. 41
    0
      server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/FilterBar-test.tsx.snap
  15. 2
    2
      server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/HotspotListItem-test.tsx.snap
  16. 1
    1
      server/sonar-web/src/main/js/apps/securityHotspots/styles.css
  17. 6
    0
      server/sonar-web/src/main/js/types/security-hotspots.ts
  18. 11
    1
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

+ 5
- 1
server/sonar-web/src/main/js/api/security-hotspots.ts View File

@@ -23,8 +23,10 @@ import { BranchParameters } from '../types/branch-like';
import {
DetailedHotspot,
HotspotAssignRequest,
HotspotResolution,
HotspotSearchResponse,
HotspotSetStatusRequest
HotspotSetStatusRequest,
HotspotStatus
} from '../types/security-hotspots';

export function assignSecurityHotspot(
@@ -48,6 +50,8 @@ export function getSecurityHotspots(
projectKey: string;
p: number;
ps: number;
status?: HotspotStatus;
resolution?: HotspotResolution;
} & BranchParameters
): Promise<HotspotSearchResponse> {
return getJSON('/api/hotspots/search', data).catch(throwGlobalError);

+ 69
- 19
server/sonar-web/src/main/js/apps/securityHotspots/SecurityHotspotsApp.tsx View File

@@ -23,7 +23,13 @@ import { getSecurityHotspots } from '../../api/security-hotspots';
import { getBranchLikeQuery } from '../../helpers/branch-like';
import { getStandards } from '../../helpers/security-standard';
import { BranchLike } from '../../types/branch-like';
import { HotspotUpdate, RawHotspot } from '../../types/security-hotspots';
import {
HotspotResolution,
HotspotStatus,
HotspotStatusFilters,
HotspotUpdate,
RawHotspot
} from '../../types/security-hotspots';
import SecurityHotspotsAppRenderer from './SecurityHotspotsAppRenderer';
import './styles.css';
import { sortHotspots } from './utils';
@@ -40,6 +46,7 @@ interface State {
loading: boolean;
securityCategories: T.StandardSecurityCategories;
selectedHotspotKey: string | undefined;
statusFilter: HotspotStatusFilters;
}

export default class SecurityHotspotsApp extends React.PureComponent<Props, State> {
@@ -48,7 +55,8 @@ export default class SecurityHotspotsApp extends React.PureComponent<Props, Stat
loading: true,
hotspots: [],
securityCategories: {},
selectedHotspotKey: undefined
selectedHotspotKey: undefined,
statusFilter: HotspotStatusFilters.TO_REVIEW
};

componentDidMount() {
@@ -68,18 +76,14 @@ export default class SecurityHotspotsApp extends React.PureComponent<Props, Stat
this.mounted = false;
}

fetchInitialData() {
const { branchLike, component } = this.props;
handleCallFailure = () => {
if (this.mounted) {
this.setState({ loading: false });
}
};

return Promise.all([
getStandards(),
getSecurityHotspots({
projectKey: component.key,
p: 1,
ps: PAGE_SIZE,
...getBranchLikeQuery(branchLike)
})
])
fetchInitialData() {
return Promise.all([getStandards(), this.fetchSecurityHotspots()])
.then(([{ sonarsourceSecurity }, response]) => {
if (!this.mounted) {
return;
@@ -94,13 +98,57 @@ export default class SecurityHotspotsApp extends React.PureComponent<Props, Stat
selectedHotspotKey: hotspots.length > 0 ? hotspots[0].key : undefined
});
})
.catch(() => {
if (this.mounted) {
this.setState({ loading: false });
}
});
.catch(this.handleCallFailure);
}

fetchSecurityHotspots() {
const { branchLike, component } = this.props;
const { statusFilter } = this.state;

const status =
statusFilter === HotspotStatusFilters.TO_REVIEW
? HotspotStatus.TO_REVIEW
: HotspotStatus.REVIEWED;

const resolution =
statusFilter === HotspotStatusFilters.TO_REVIEW ? undefined : HotspotResolution[statusFilter];

return getSecurityHotspots({
projectKey: component.key,
p: 1,
ps: PAGE_SIZE,
status,
resolution,
...getBranchLikeQuery(branchLike)
});
}

reloadSecurityHotspotList = () => {
const { securityCategories } = this.state;

this.setState({ loading: true });

return this.fetchSecurityHotspots()
.then(response => {
if (!this.mounted) {
return;
}

const hotspots = sortHotspots(response.hotspots, securityCategories);

this.setState({
hotspots,
loading: false,
selectedHotspotKey: hotspots.length > 0 ? hotspots[0].key : undefined
});
})
.catch(this.handleCallFailure);
};

handleChangeStatusFilter = (statusFilter: HotspotStatusFilters) => {
this.setState({ statusFilter }, this.reloadSecurityHotspotList);
};

handleHotspotClick = (key: string) => this.setState({ selectedHotspotKey: key });

handleHotspotUpdate = ({ key, status, resolution }: HotspotUpdate) => {
@@ -122,17 +170,19 @@ export default class SecurityHotspotsApp extends React.PureComponent<Props, Stat

render() {
const { branchLike } = this.props;
const { hotspots, loading, securityCategories, selectedHotspotKey } = this.state;
const { hotspots, loading, securityCategories, selectedHotspotKey, statusFilter } = this.state;

return (
<SecurityHotspotsAppRenderer
branchLike={branchLike}
hotspots={hotspots}
loading={loading}
onChangeStatusFilter={this.handleChangeStatusFilter}
onHotspotClick={this.handleHotspotClick}
onUpdateHotspot={this.handleHotspotUpdate}
securityCategories={securityCategories}
selectedHotspotKey={selectedHotspotKey}
statusFilter={statusFilter}
/>
);
}

+ 13
- 3
server/sonar-web/src/main/js/apps/securityHotspots/SecurityHotspotsAppRenderer.tsx View 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 { HotspotUpdate, RawHotspot } from '../../types/security-hotspots';
import { HotspotStatusFilters, HotspotUpdate, RawHotspot } from '../../types/security-hotspots';
import FilterBar from './components/FilterBar';
import HotspotList from './components/HotspotList';
import HotspotViewer from './components/HotspotViewer';
@@ -37,18 +37,27 @@ export interface SecurityHotspotsAppRendererProps {
branchLike?: BranchLike;
hotspots: RawHotspot[];
loading: boolean;
onChangeStatusFilter: (status: HotspotStatusFilters) => void;
onHotspotClick: (key: string) => void;
onUpdateHotspot: (hotspot: HotspotUpdate) => void;
selectedHotspotKey?: string;
securityCategories: T.StandardSecurityCategories;
statusFilter: HotspotStatusFilters;
}

export default function SecurityHotspotsAppRenderer(props: SecurityHotspotsAppRendererProps) {
const { branchLike, hotspots, loading, securityCategories, selectedHotspotKey } = props;
const {
branchLike,
hotspots,
loading,
securityCategories,
selectedHotspotKey,
statusFilter
} = props;

return (
<div id="security_hotspots">
<FilterBar />
<FilterBar onChangeStatus={props.onChangeStatusFilter} statusFilter={statusFilter} />
<ScreenPositionHelper>
{({ top }) => (
<div className="wrapper" style={{ top }}>
@@ -85,6 +94,7 @@ export default function SecurityHotspotsAppRenderer(props: SecurityHotspotsAppRe
onHotspotClick={props.onHotspotClick}
securityCategories={securityCategories}
selectedHotspotKey={selectedHotspotKey}
statusFilter={statusFilter}
/>
</div>
<div className="main">

+ 45
- 2
server/sonar-web/src/main/js/apps/securityHotspots/__tests__/SecurityHotspotsApp-test.tsx View File

@@ -26,7 +26,11 @@ import { mockBranch } 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 {
HotspotResolution,
HotspotStatus,
HotspotStatusFilters
} from '../../../types/security-hotspots';
import SecurityHotspotsApp from '../SecurityHotspotsApp';
import SecurityHotspotsAppRenderer from '../SecurityHotspotsAppRenderer';

@@ -54,7 +58,7 @@ it('should load data correctly', async () => {
(getStandards as jest.Mock).mockResolvedValue({ sonarsourceSecurity });

const hotspots = [mockRawHotspot()];
(getSecurityHotspots as jest.Mock).mockResolvedValue({
(getSecurityHotspots as jest.Mock).mockResolvedValueOnce({
hotspots
});

@@ -104,6 +108,45 @@ it('should handle hotspot update', async () => {
});
});

it('should handle status filter change', async () => {
const hotspots = [mockRawHotspot({ key: 'key1' })];
const hotspots2 = [mockRawHotspot({ key: 'key2' })];
(getSecurityHotspots as jest.Mock)
.mockResolvedValueOnce({ hotspots })
.mockResolvedValueOnce({ hotspots: hotspots2 })
.mockResolvedValueOnce({ hotspots: [] });

const wrapper = shallowRender();

expect(getSecurityHotspots).toBeCalledWith(
expect.objectContaining({ status: HotspotStatus.TO_REVIEW, resolution: undefined })
);

await waitAndUpdate(wrapper);

// Set filter to SAFE:
wrapper.instance().handleChangeStatusFilter(HotspotStatusFilters.SAFE);

expect(getSecurityHotspots).toBeCalledWith(
expect.objectContaining({ status: HotspotStatus.REVIEWED, resolution: HotspotResolution.SAFE })
);

await waitAndUpdate(wrapper);

expect(wrapper.state().hotspots[0]).toBe(hotspots2[0]);

// Set filter to FIXED
wrapper.instance().handleChangeStatusFilter(HotspotStatusFilters.FIXED);

expect(getSecurityHotspots).toBeCalledWith(
expect.objectContaining({ status: HotspotStatus.REVIEWED, resolution: HotspotResolution.FIXED })
);

await waitAndUpdate(wrapper);

expect(wrapper.state().hotspots).toHaveLength(0);
});

function shallowRender(props: Partial<SecurityHotspotsApp['props']> = {}) {
return shallow<SecurityHotspotsApp>(
<SecurityHotspotsApp branchLike={branch} component={mockComponent()} {...props} />

+ 3
- 0
server/sonar-web/src/main/js/apps/securityHotspots/__tests__/SecurityHotspotsAppRenderer-test.tsx View File

@@ -21,6 +21,7 @@ import { shallow } from 'enzyme';
import * as React from 'react';
import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
import { mockRawHotspot } from '../../../helpers/mocks/security-hotspots';
import { HotspotStatusFilters } from '../../../types/security-hotspots';
import SecurityHotspotsAppRenderer, {
SecurityHotspotsAppRendererProps
} from '../SecurityHotspotsAppRenderer';
@@ -53,9 +54,11 @@ function shallowRender(props: Partial<SecurityHotspotsAppRendererProps> = {}) {
<SecurityHotspotsAppRenderer
hotspots={[]}
loading={false}
onChangeStatusFilter={jest.fn()}
onHotspotClick={jest.fn()}
onUpdateHotspot={jest.fn()}
securityCategories={{}}
statusFilter={HotspotStatusFilters.TO_REVIEW}
{...props}
/>
);

+ 2
- 0
server/sonar-web/src/main/js/apps/securityHotspots/__tests__/__snapshots__/SecurityHotspotsApp-test.tsx.snap View File

@@ -12,8 +12,10 @@ exports[`should render correctly 1`] = `
}
hotspots={Array []}
loading={true}
onChangeStatusFilter={[Function]}
onHotspotClick={[Function]}
onUpdateHotspot={[Function]}
securityCategories={Object {}}
statusFilter="TO_REVIEW"
/>
`;

+ 6
- 1
server/sonar-web/src/main/js/apps/securityHotspots/__tests__/__snapshots__/SecurityHotspotsAppRenderer-test.tsx.snap View File

@@ -4,7 +4,10 @@ exports[`should render correctly 1`] = `
<div
id="security_hotspots"
>
<FilterBar />
<FilterBar
onChangeStatus={[MockFunction]}
statusFilter="TO_REVIEW"
/>
<ScreenPositionHelper>
<Component />
</ScreenPositionHelper>
@@ -144,6 +147,7 @@ exports[`should render correctly with hotspots 1`] = `
}
onHotspotClick={[MockFunction]}
securityCategories={Object {}}
statusFilter="TO_REVIEW"
/>
</div>
<div
@@ -225,6 +229,7 @@ exports[`should render correctly with hotspots 2`] = `
onHotspotClick={[MockFunction]}
securityCategories={Object {}}
selectedHotspotKey="h2"
statusFilter="TO_REVIEW"
/>
</div>
<div

+ 25
- 2
server/sonar-web/src/main/js/apps/securityHotspots/components/FilterBar.tsx View File

@@ -18,13 +18,36 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import Select from 'sonar-ui-common/components/controls/Select';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { HotspotStatusFilters } from '../../../types/security-hotspots';

export interface FilterBarProps {}
export interface FilterBarProps {
onChangeStatus: (status: HotspotStatusFilters) => void;
statusFilter: HotspotStatusFilters;
}

const statusOptions: Array<{ label: string; value: string }> = [
{ label: translate('hotspot.filters.status.to_review'), value: HotspotStatusFilters.TO_REVIEW },
{ label: translate('hotspot.filters.status.fixed'), value: HotspotStatusFilters.FIXED },
{ label: translate('hotspot.filters.status.safe'), value: HotspotStatusFilters.SAFE }
];

export default function FilterBar(props: FilterBarProps) {
const { statusFilter } = props;
return (
<div className="filter-bar display-flex-center">
<h3 {...props}>Filter</h3>
<h3 className="big-spacer-right">{translate('hotspot.filters.title')}</h3>

<span className="spacer-right">{translate('status')}</span>
<Select
className="input-medium big-spacer-right"
clearable={false}
onChange={(option: { value: HotspotStatusFilters }) => props.onChangeStatus(option.value)}
options={statusOptions}
searchable={false}
value={statusFilter}
/>
</div>
);
}

+ 4
- 3
server/sonar-web/src/main/js/apps/securityHotspots/components/HotspotList.tsx View File

@@ -22,7 +22,7 @@ import { groupBy } from 'lodash';
import * as React from 'react';
import SecurityHotspotIcon from 'sonar-ui-common/components/icons/SecurityHotspotIcon';
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
import { RawHotspot, RiskExposure } from '../../../types/security-hotspots';
import { HotspotStatusFilters, RawHotspot, RiskExposure } from '../../../types/security-hotspots';
import { groupByCategory, RISK_EXPOSURE_LEVELS } from '../utils';
import HotspotCategory from './HotspotCategory';
import './HotspotList.css';
@@ -32,10 +32,11 @@ export interface HotspotListProps {
onHotspotClick: (key: string) => void;
securityCategories: T.StandardSecurityCategories;
selectedHotspotKey: string | undefined;
statusFilter: HotspotStatusFilters;
}

export default function HotspotList(props: HotspotListProps) {
const { hotspots, securityCategories, selectedHotspotKey } = props;
const { hotspots, securityCategories, selectedHotspotKey, statusFilter } = props;

const groupedHotspots: Array<{
risk: RiskExposure;
@@ -53,7 +54,7 @@ export default function HotspotList(props: HotspotListProps) {
<>
<h1 className="hotspot-list-header bordered-bottom">
<SecurityHotspotIcon className="spacer-right" />
{translateWithParameters(`hotspots.list_title.TO_REVIEW`, hotspots.length)}
{translateWithParameters(`hotspots.list_title.${statusFilter}`, hotspots.length)}
</h1>
<ul className="huge-spacer-bottom">
{groupedHotspots.map(riskGroup => (

+ 4
- 4
server/sonar-web/src/main/js/apps/securityHotspots/components/HotspotListItem.tsx View File

@@ -28,7 +28,7 @@ export interface HotspotListItemProps {
selected: boolean;
}

export function HotspotListItem(props: HotspotListItemProps) {
export default function HotspotListItem(props: HotspotListItemProps) {
const { hotspot, selected } = props;
return (
<a
@@ -36,9 +36,9 @@ export function HotspotListItem(props: HotspotListItemProps) {
href="#"
onClick={() => !selected && props.onClick(hotspot.key)}>
<div className="little-spacer-left">{hotspot.message}</div>
<div className="badge spacer-top">{translate('issue.status', hotspot.status)}</div>
<div className="badge spacer-top">
{translate('hotspot.status', hotspot.resolution || hotspot.status)}
</div>
</a>
);
}

export default React.memo(HotspotListItem);

+ 51
- 0
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/FilterBar-test.tsx View File

@@ -0,0 +1,51 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { shallow } from 'enzyme';
import * as React from 'react';
import Select from 'sonar-ui-common/components/controls/Select';
import { HotspotStatusFilters } from '../../../../types/security-hotspots';
import FilterBar, { FilterBarProps } from '../FilterBar';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});

it('should trigger onChange', () => {
const onChangeStatus = jest.fn();
const wrapper = shallowRender({ onChangeStatus });

const { onChange } = wrapper.find(Select).props();

if (!onChange) {
return fail("Select's onChange should be defined");
}
onChange({ value: HotspotStatusFilters.SAFE });
expect(onChangeStatus).toBeCalledWith(HotspotStatusFilters.SAFE);
});

function shallowRender(props: Partial<FilterBarProps> = {}) {
return shallow(
<FilterBar
onChangeStatus={jest.fn()}
statusFilter={HotspotStatusFilters.TO_REVIEW}
{...props}
/>
);
}

+ 2
- 1
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/HotspotList-test.tsx View File

@@ -20,7 +20,7 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockRawHotspot } from '../../../../helpers/mocks/security-hotspots';
import { RiskExposure } from '../../../../types/security-hotspots';
import { HotspotStatusFilters, RiskExposure } from '../../../../types/security-hotspots';
import HotspotList, { HotspotListProps } from '../HotspotList';

it('should render correctly', () => {
@@ -57,6 +57,7 @@ function shallowRender(props: Partial<HotspotListProps> = {}) {
onHotspotClick={jest.fn()}
securityCategories={{}}
selectedHotspotKey="h2"
statusFilter={HotspotStatusFilters.TO_REVIEW}
{...props}
/>
);

+ 1
- 1
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/HotspotListItem-test.tsx View File

@@ -20,7 +20,7 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockRawHotspot } from '../../../../helpers/mocks/security-hotspots';
import { HotspotListItem, HotspotListItemProps } from '../HotspotListItem';
import HotspotListItem, { HotspotListItemProps } from '../HotspotListItem';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();

+ 41
- 0
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/FilterBar-test.tsx.snap View File

@@ -0,0 +1,41 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<div
className="filter-bar display-flex-center"
>
<h3
className="big-spacer-right"
>
hotspot.filters.title
</h3>
<span
className="spacer-right"
>
status
</span>
<Select
className="input-medium big-spacer-right"
clearable={false}
onChange={[Function]}
options={
Array [
Object {
"label": "hotspot.filters.status.to_review",
"value": "TO_REVIEW",
},
Object {
"label": "hotspot.filters.status.fixed",
"value": "FIXED",
},
Object {
"label": "hotspot.filters.status.safe",
"value": "SAFE",
},
]
}
searchable={false}
value="TO_REVIEW"
/>
</div>
`;

+ 2
- 2
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/HotspotListItem-test.tsx.snap View File

@@ -14,7 +14,7 @@ exports[`should render correctly 1`] = `
<div
className="badge spacer-top"
>
issue.status.TO_REVIEW
hotspot.status.TO_REVIEW
</div>
</a>
`;
@@ -33,7 +33,7 @@ exports[`should render correctly 2`] = `
<div
className="badge spacer-top"
>
issue.status.TO_REVIEW
hotspot.status.TO_REVIEW
</div>
</a>
`;

+ 1
- 1
server/sonar-web/src/main/js/apps/securityHotspots/styles.css View File

@@ -34,7 +34,7 @@
#security_hotspots .filter-bar {
max-width: 1280px;
margin: 0 auto;
padding: var(--gridSize) 20px;
padding: calc(2 * var(--gridSize)) 20px;
border-bottom: 1px solid var(--barBorderColor);
}


+ 6
- 0
server/sonar-web/src/main/js/types/security-hotspots.ts View File

@@ -33,6 +33,12 @@ export enum HotspotResolution {
SAFE = 'SAFE'
}

export enum HotspotStatusFilters {
FIXED = 'FIXED',
SAFE = 'SAFE',
TO_REVIEW = 'TO_REVIEW'
}

export enum HotspotStatusOptions {
FIXED = 'FIXED',
SAFE = 'SAFE',

+ 11
- 1
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

@@ -649,7 +649,8 @@ hotspots.no_hotspots.title=There are no Security Hotspots to review
hotspots.no_hotspots.description=Next time you analyse a piece of code that contains a potential security risk, it will show up here.
hotspots.learn_more=Learn more about Security Hotspots
hotspots.list_title.TO_REVIEW={0} Security Hotspots to review
hotspots.list_title.REVIEWED={0} reviewed Security Hotspots
hotspots.list_title.FIXED={0} Security Hotspots reviewed as fixed
hotspots.list_title.SAFE={0} Security Hotspots reviewed as safe
hotspots.risk_exposure=Review priority:

hotspot.category=Category:
@@ -660,6 +661,15 @@ hotspot.tabs.vulnerability_description=Are you vulnerable?
hotspot.tabs.fix_recommendations=How can you fix it?
hotspots.review_hotspot=Review Hotspot

hotspot.status.TO_REVIEW=To review
hotspot.status.FIXED=Fixed
hotspot.status.SAFE=Safe

hotspot.filters.title=Filters
hotspot.filters.status.to_review=To review
hotspot.filters.status.fixed=Reviewed as fixed
hotspot.filters.status.safe=Reviewed as safe

hotspots.form.title=Mark Security Hotspot as:

hotspots.form.assign_to=Assign to:

Loading…
Cancel
Save