Browse Source

SONAR-12720 Review tab displays hotspot's comments

tags/8.2.0.32929
Philippe Perrin 4 years ago
parent
commit
2945868268
16 changed files with 541 additions and 108 deletions
  1. 23
    1
      server/sonar-web/src/main/js/api/security-hotspots.ts
  2. 39
    7
      server/sonar-web/src/main/js/apps/securityHotspots/__tests__/utils-test.ts
  3. 4
    4
      server/sonar-web/src/main/js/apps/securityHotspots/components/HotspotViewerRenderer.tsx
  4. 18
    7
      server/sonar-web/src/main/js/apps/securityHotspots/components/HotspotViewerReviewHistoryTab.tsx
  5. 2
    2
      server/sonar-web/src/main/js/apps/securityHotspots/components/HotspotViewerTabs.tsx
  6. 2
    2
      server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/HotspotViewerRenderer-test.tsx
  7. 4
    0
      server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/HotspotViewerReviewHistoryTab-test.tsx
  8. 21
    4
      server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/HotspotSnippetContainer-test.tsx.snap
  9. 21
    4
      server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/HotspotSnippetContainerRenderer-test.tsx.snap
  10. 286
    44
      server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/HotspotViewerRenderer-test.tsx.snap
  11. 52
    6
      server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/HotspotViewerReviewHistoryTab-test.tsx.snap
  12. 21
    15
      server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/HotspotViewerTabs-test.tsx.snap
  13. 19
    6
      server/sonar-web/src/main/js/apps/securityHotspots/utils.ts
  14. 8
    2
      server/sonar-web/src/main/js/helpers/mocks/security-hotspots.ts
  15. 20
    4
      server/sonar-web/src/main/js/types/security-hotspots.ts
  16. 1
    0
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

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

@@ -59,5 +59,27 @@ export function getSecurityHotspots(
}

export function getSecurityHotspotDetails(securityHotspotKey: string): Promise<Hotspot> {
return getJSON('/api/hotspots/show', { hotspot: securityHotspotKey }).catch(throwGlobalError);
return getJSON('/api/hotspots/show', { hotspot: securityHotspotKey })
.then((response: Hotspot & { users: T.UserBase[] }) => {
const { users, ...hotspot } = response;

if (users) {
if (hotspot.assignee) {
hotspot.assigneeUser = users.find(u => u.login === hotspot.assignee) || {
active: true,
login: hotspot.assignee
};
}
hotspot.authorUser = users.find(u => u.login === hotspot.author) || {
active: true,
login: hotspot.author
};
hotspot.comment.forEach(c => {
c.user = users.find(u => u.login === c.login) || { active: true, login: c.login };
});
}

return hotspot;
})
.catch(throwGlobalError);
}

+ 39
- 7
server/sonar-web/src/main/js/apps/securityHotspots/__tests__/utils-test.ts View File

@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { mockHotspot, mockRawHotspot } from '../../../helpers/mocks/security-hotspots';
import { mockUser } from '../../../helpers/testMocks';
import { ReviewHistoryType, RiskExposure } from '../../../types/security-hotspots';
import { getHotspotReviewHistory, groupByCategory, mapRules, sortHotspots } from '../utils';

@@ -158,25 +159,56 @@ describe('getHotspotReviewHistory', () => {
}
]
};
const commentElement = {
key: 'comment-1',
createdAt: '2018-09-10',
htmlText: '<strong>TEST</strong>',
markdown: '*TEST*',
updatable: true,
login: 'dude-1',
user: mockUser({ login: 'dude-1' })
};
const commentElement1 = {
key: 'comment-2',
createdAt: '2018-09-11',
htmlText: '<strong>TEST</strong>',
markdown: '*TEST*',
updatable: true,
login: 'dude-2',
user: mockUser({ login: 'dude-2' })
};
const hotspot = mockHotspot({
creationDate: '2018-09-01',
changelog: [changelogElement]
changelog: [changelogElement],
comment: [commentElement, commentElement1]
});
const history = getHotspotReviewHistory(hotspot);

expect(history.length).toBe(2);
expect(history.length).toBe(4);
expect(history[0]).toEqual(
expect.objectContaining({
type: ReviewHistoryType.Creation,
date: hotspot.creationDate,
user: {
avatar: hotspot.author.avatar,
name: hotspot.author.name,
active: hotspot.author.active
}
user: hotspot.authorUser
})
);
expect(history[1]).toEqual(
expect.objectContaining({
type: ReviewHistoryType.Comment,
date: commentElement.createdAt,
user: commentElement.user,
html: commentElement.htmlText
})
);
expect(history[2]).toEqual(
expect.objectContaining({
type: ReviewHistoryType.Comment,
date: commentElement1.createdAt,
user: commentElement1.user,
html: commentElement1.htmlText
})
);
expect(history[3]).toEqual(
expect.objectContaining({
type: ReviewHistoryType.Diff,
date: changelogElement.creationDate,

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

@@ -63,13 +63,13 @@ export function HotspotViewerRenderer(props: HotspotViewerRendererProps) {
<span className="badge little-spacer-left">
{translate('hotspot.status', hotspot.resolution || hotspot.status)}
</span>
{hotspot.assignee && hotspot.assignee.name && (
{hotspot.assigneeUser && hotspot.assigneeUser.name && (
<>
<span className="huge-spacer-left">{translate('assigned_to')}:</span>
<strong className="little-spacer-left">
{hotspot.assignee.active
? hotspot.assignee.name
: translateWithParameters('user.x_deleted', hotspot.assignee.name)}
{hotspot.assigneeUser.active
? hotspot.assigneeUser.name
: translateWithParameters('user.x_deleted', hotspot.assigneeUser.name)}
</strong>
</>
)}

+ 18
- 7
server/sonar-web/src/main/js/apps/securityHotspots/components/HotspotViewerReviewHistoryTab.tsx View File

@@ -17,6 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { sanitize } from 'dompurify';
import * as React from 'react';
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
@@ -34,9 +36,9 @@ export default function HotspotViewerReviewHistoryTab(props: HotspotViewerReview
return (
<>
{history.map((elt, i) => (
<React.Fragment key={`${elt.user.name}-${elt.date}`}>
<React.Fragment key={i}>
{i > 0 && <hr />}
<div>
<div className="padded">
<div className="display-flex-center">
{elt.user.name && (
<>
@@ -56,6 +58,11 @@ export default function HotspotViewerReviewHistoryTab(props: HotspotViewerReview
{translate('hotspots.tabs.review_history.created')}
</span>
)}
{elt.type === ReviewHistoryType.Comment && (
<span className="little-spacer-left">
{translate('hotspots.tabs.review_history.comment')}
</span>
)}
<span className="little-spacer-left little-spacer-right">-</span>
</>
)}
@@ -64,14 +71,18 @@ export default function HotspotViewerReviewHistoryTab(props: HotspotViewerReview

{elt.type === ReviewHistoryType.Diff && elt.diffs && (
<div className="spacer-top">
{elt.diffs.map(diff => (
<IssueChangelogDiff
diff={diff}
key={`${diff.key}-${diff.oldValue}-${diff.newValue}`}
/>
{elt.diffs.map((diff, i) => (
<IssueChangelogDiff diff={diff} key={i} />
))}
</div>
)}

{elt.type === ReviewHistoryType.Comment && elt.html && (
<div
className="spacer-top markdown"
dangerouslySetInnerHTML={{ __html: sanitize(elt.html) }}
/>
)}
</div>
</React.Fragment>
))}

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

@@ -84,10 +84,10 @@ export default function HotspotViewerTabs(props: HotspotViewerTabsProps) {
selected={currentTabKey}
tabs={tabs}
/>
<div className="boxed-group big-padded">
<div className="bordered">
{typeof currentTab.content === 'string' ? (
<div
className="markdown"
className="markdown big-padded"
dangerouslySetInnerHTML={{ __html: sanitize(currentTab.content) }}
/>
) : (

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

@@ -31,12 +31,12 @@ it('should render correctly', () => {
'unassigned'
);
expect(
shallowRender({ hotspot: mockHotspot({ assignee: mockUser({ active: false }) }) })
shallowRender({ hotspot: mockHotspot({ assigneeUser: mockUser({ active: false }) }) })
).toMatchSnapshot('deleted assignee');
expect(
shallowRender({
hotspot: mockHotspot({
assignee: mockUser({ name: undefined, login: 'assignee_login' })
assigneeUser: mockUser({ name: undefined, login: 'assignee_login' })
})
})
).toMatchSnapshot('assignee without name');

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

@@ -44,6 +44,10 @@ function shallowRender(props?: Partial<HotspotViewerReviewHistoryTabProps>) {
{ key: 'test', oldValue: 'old', newValue: 'new' },
{ key: 'test-1', oldValue: 'old-1', newValue: 'new-1' }
]
}),
mockHotspotReviewHistoryElement({
type: ReviewHistoryType.Comment,
html: '<strong>bold text</strong>'
})
]}
{...props}

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

@@ -13,19 +13,22 @@ exports[`should render correctly 1`] = `
highlightedSymbols={Array []}
hotspot={
Object {
"assignee": Object {
"assignee": "assignee",
"assigneeUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "assignee",
"name": "John Doe",
},
"author": Object {
"author": "author",
"authorUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "author",
"name": "John Doe",
},
"changelog": Array [],
"comment": Array [],
"component": Object {
"breadcrumbs": Array [],
"key": "my-project",
@@ -90,6 +93,20 @@ exports[`should render correctly 1`] = `
"startOffset": 26,
},
"updateDate": "2013-05-13T17:55:42+0200",
"users": Array [
Object {
"active": true,
"local": true,
"login": "assignee",
"name": "John Doe",
},
Object {
"active": true,
"local": true,
"login": "author",
"name": "John Doe",
},
],
}
}
loading={true}

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

@@ -137,19 +137,22 @@ exports[`should render correctly: with sourcelines 1`] = `
index={0}
issue={
Object {
"assignee": Object {
"assignee": "assignee",
"assigneeUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "assignee",
"name": "John Doe",
},
"author": Object {
"author": "author",
"authorUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "author",
"name": "John Doe",
},
"changelog": Array [],
"comment": Array [],
"component": Object {
"breadcrumbs": Array [],
"key": "my-project",
@@ -214,6 +217,20 @@ exports[`should render correctly: with sourcelines 1`] = `
"startOffset": 26,
},
"updateDate": "2013-05-13T17:55:42+0200",
"users": Array [
Object {
"active": true,
"local": true,
"login": "assignee",
"name": "John Doe",
},
Object {
"active": true,
"local": true,
"login": "author",
"name": "John Doe",
},
],
}
}
issuesByLine={Object {}}

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

@@ -59,19 +59,22 @@ exports[`should render correctly 1`] = `
<HotspotSnippetContainer
hotspot={
Object {
"assignee": Object {
"assignee": "assignee",
"assigneeUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "assignee",
"name": "John Doe",
},
"author": Object {
"author": "author",
"authorUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "author",
"name": "John Doe",
},
"changelog": Array [],
"comment": Array [],
"component": Object {
"breadcrumbs": Array [],
"key": "my-project",
@@ -136,25 +139,42 @@ exports[`should render correctly 1`] = `
"startOffset": 26,
},
"updateDate": "2013-05-13T17:55:42+0200",
"users": Array [
Object {
"active": true,
"local": true,
"login": "assignee",
"name": "John Doe",
},
Object {
"active": true,
"local": true,
"login": "author",
"name": "John Doe",
},
],
}
}
/>
<HotspotViewerTabs
hotspot={
Object {
"assignee": Object {
"assignee": "assignee",
"assigneeUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "assignee",
"name": "John Doe",
},
"author": Object {
"author": "author",
"authorUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "author",
"name": "John Doe",
},
"changelog": Array [],
"comment": Array [],
"component": Object {
"breadcrumbs": Array [],
"key": "my-project",
@@ -219,6 +239,20 @@ exports[`should render correctly 1`] = `
"startOffset": 26,
},
"updateDate": "2013-05-13T17:55:42+0200",
"users": Array [
Object {
"active": true,
"local": true,
"login": "assignee",
"name": "John Doe",
},
Object {
"active": true,
"local": true,
"login": "author",
"name": "John Doe",
},
],
}
}
/>
@@ -285,19 +319,22 @@ exports[`should render correctly: anonymous user 1`] = `
<HotspotSnippetContainer
hotspot={
Object {
"assignee": Object {
"assignee": "assignee",
"assigneeUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "assignee",
"name": "John Doe",
},
"author": Object {
"author": "author",
"authorUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "author",
"name": "John Doe",
},
"changelog": Array [],
"comment": Array [],
"component": Object {
"breadcrumbs": Array [],
"key": "my-project",
@@ -362,25 +399,42 @@ exports[`should render correctly: anonymous user 1`] = `
"startOffset": 26,
},
"updateDate": "2013-05-13T17:55:42+0200",
"users": Array [
Object {
"active": true,
"local": true,
"login": "assignee",
"name": "John Doe",
},
Object {
"active": true,
"local": true,
"login": "author",
"name": "John Doe",
},
],
}
}
/>
<HotspotViewerTabs
hotspot={
Object {
"assignee": Object {
"assignee": "assignee",
"assigneeUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "assignee",
"name": "John Doe",
},
"author": Object {
"author": "author",
"authorUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "author",
"name": "John Doe",
},
"changelog": Array [],
"comment": Array [],
"component": Object {
"breadcrumbs": Array [],
"key": "my-project",
@@ -445,6 +499,20 @@ exports[`should render correctly: anonymous user 1`] = `
"startOffset": 26,
},
"updateDate": "2013-05-13T17:55:42+0200",
"users": Array [
Object {
"active": true,
"local": true,
"login": "assignee",
"name": "John Doe",
},
Object {
"active": true,
"local": true,
"login": "author",
"name": "John Doe",
},
],
}
}
/>
@@ -500,19 +568,22 @@ exports[`should render correctly: assignee without name 1`] = `
<HotspotSnippetContainer
hotspot={
Object {
"assignee": Object {
"assignee": "assignee",
"assigneeUser": Object {
"active": true,
"local": true,
"login": "assignee_login",
"name": undefined,
},
"author": Object {
"author": "author",
"authorUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "author",
"name": "John Doe",
},
"changelog": Array [],
"comment": Array [],
"component": Object {
"breadcrumbs": Array [],
"key": "my-project",
@@ -577,25 +648,42 @@ exports[`should render correctly: assignee without name 1`] = `
"startOffset": 26,
},
"updateDate": "2013-05-13T17:55:42+0200",
"users": Array [
Object {
"active": true,
"local": true,
"login": "assignee",
"name": "John Doe",
},
Object {
"active": true,
"local": true,
"login": "author",
"name": "John Doe",
},
],
}
}
/>
<HotspotViewerTabs
hotspot={
Object {
"assignee": Object {
"assignee": "assignee",
"assigneeUser": Object {
"active": true,
"local": true,
"login": "assignee_login",
"name": undefined,
},
"author": Object {
"author": "author",
"authorUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "author",
"name": "John Doe",
},
"changelog": Array [],
"comment": Array [],
"component": Object {
"breadcrumbs": Array [],
"key": "my-project",
@@ -660,6 +748,20 @@ exports[`should render correctly: assignee without name 1`] = `
"startOffset": 26,
},
"updateDate": "2013-05-13T17:55:42+0200",
"users": Array [
Object {
"active": true,
"local": true,
"login": "assignee",
"name": "John Doe",
},
Object {
"active": true,
"local": true,
"login": "author",
"name": "John Doe",
},
],
}
}
/>
@@ -726,19 +828,22 @@ exports[`should render correctly: deleted assignee 1`] = `
<HotspotSnippetContainer
hotspot={
Object {
"assignee": Object {
"assignee": "assignee",
"assigneeUser": Object {
"active": false,
"local": true,
"login": "john.doe",
"name": "John Doe",
},
"author": Object {
"author": "author",
"authorUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "author",
"name": "John Doe",
},
"changelog": Array [],
"comment": Array [],
"component": Object {
"breadcrumbs": Array [],
"key": "my-project",
@@ -803,25 +908,42 @@ exports[`should render correctly: deleted assignee 1`] = `
"startOffset": 26,
},
"updateDate": "2013-05-13T17:55:42+0200",
"users": Array [
Object {
"active": true,
"local": true,
"login": "assignee",
"name": "John Doe",
},
Object {
"active": true,
"local": true,
"login": "author",
"name": "John Doe",
},
],
}
}
/>
<HotspotViewerTabs
hotspot={
Object {
"assignee": Object {
"assignee": "assignee",
"assigneeUser": Object {
"active": false,
"local": true,
"login": "john.doe",
"name": "John Doe",
},
"author": Object {
"author": "author",
"authorUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "author",
"name": "John Doe",
},
"changelog": Array [],
"comment": Array [],
"component": Object {
"breadcrumbs": Array [],
"key": "my-project",
@@ -886,6 +1008,20 @@ exports[`should render correctly: deleted assignee 1`] = `
"startOffset": 26,
},
"updateDate": "2013-05-13T17:55:42+0200",
"users": Array [
Object {
"active": true,
"local": true,
"login": "assignee",
"name": "John Doe",
},
Object {
"active": true,
"local": true,
"login": "author",
"name": "John Doe",
},
],
}
}
/>
@@ -944,18 +1080,37 @@ exports[`should render correctly: unassigned 1`] = `
>
hotspot.status.FIXED
</span>
<span
className="huge-spacer-left"
>
assigned_to
:
</span>
<strong
className="little-spacer-left"
>
John Doe
</strong>
</div>
<HotspotSnippetContainer
hotspot={
Object {
"assignee": undefined,
"author": Object {
"assigneeUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "assignee",
"name": "John Doe",
},
"author": "author",
"authorUser": Object {
"active": true,
"local": true,
"login": "author",
"name": "John Doe",
},
"changelog": Array [],
"comment": Array [],
"component": Object {
"breadcrumbs": Array [],
"key": "my-project",
@@ -1020,6 +1175,20 @@ exports[`should render correctly: unassigned 1`] = `
"startOffset": 26,
},
"updateDate": "2013-05-13T17:55:42+0200",
"users": Array [
Object {
"active": true,
"local": true,
"login": "assignee",
"name": "John Doe",
},
Object {
"active": true,
"local": true,
"login": "author",
"name": "John Doe",
},
],
}
}
/>
@@ -1027,13 +1196,21 @@ exports[`should render correctly: unassigned 1`] = `
hotspot={
Object {
"assignee": undefined,
"author": Object {
"assigneeUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "assignee",
"name": "John Doe",
},
"author": "author",
"authorUser": Object {
"active": true,
"local": true,
"login": "author",
"name": "John Doe",
},
"changelog": Array [],
"comment": Array [],
"component": Object {
"breadcrumbs": Array [],
"key": "my-project",
@@ -1098,6 +1275,20 @@ exports[`should render correctly: unassigned 1`] = `
"startOffset": 26,
},
"updateDate": "2013-05-13T17:55:42+0200",
"users": Array [
Object {
"active": true,
"local": true,
"login": "assignee",
"name": "John Doe",
},
Object {
"active": true,
"local": true,
"login": "author",
"name": "John Doe",
},
],
}
}
/>
@@ -1125,19 +1316,22 @@ exports[`should render correctly: user logged in 1`] = `
<HotspotActions
hotspot={
Object {
"assignee": Object {
"assignee": "assignee",
"assigneeUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "assignee",
"name": "John Doe",
},
"author": Object {
"author": "author",
"authorUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "author",
"name": "John Doe",
},
"changelog": Array [],
"comment": Array [],
"component": Object {
"breadcrumbs": Array [],
"key": "my-project",
@@ -1202,6 +1396,20 @@ exports[`should render correctly: user logged in 1`] = `
"startOffset": 26,
},
"updateDate": "2013-05-13T17:55:42+0200",
"users": Array [
Object {
"active": true,
"local": true,
"login": "assignee",
"name": "John Doe",
},
Object {
"active": true,
"local": true,
"login": "author",
"name": "John Doe",
},
],
}
}
onSubmit={[MockFunction]}
@@ -1248,19 +1456,22 @@ exports[`should render correctly: user logged in 1`] = `
<HotspotSnippetContainer
hotspot={
Object {
"assignee": Object {
"assignee": "assignee",
"assigneeUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "assignee",
"name": "John Doe",
},
"author": Object {
"author": "author",
"authorUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "author",
"name": "John Doe",
},
"changelog": Array [],
"comment": Array [],
"component": Object {
"breadcrumbs": Array [],
"key": "my-project",
@@ -1325,25 +1536,42 @@ exports[`should render correctly: user logged in 1`] = `
"startOffset": 26,
},
"updateDate": "2013-05-13T17:55:42+0200",
"users": Array [
Object {
"active": true,
"local": true,
"login": "assignee",
"name": "John Doe",
},
Object {
"active": true,
"local": true,
"login": "author",
"name": "John Doe",
},
],
}
}
/>
<HotspotViewerTabs
hotspot={
Object {
"assignee": Object {
"assignee": "assignee",
"assigneeUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "assignee",
"name": "John Doe",
},
"author": Object {
"author": "author",
"authorUser": Object {
"active": true,
"local": true,
"login": "john.doe",
"login": "author",
"name": "John Doe",
},
"changelog": Array [],
"comment": Array [],
"component": Object {
"breadcrumbs": Array [],
"key": "my-project",
@@ -1408,6 +1636,20 @@ exports[`should render correctly: user logged in 1`] = `
"startOffset": 26,
},
"updateDate": "2013-05-13T17:55:42+0200",
"users": Array [
Object {
"active": true,
"local": true,
"login": "assignee",
"name": "John Doe",
},
Object {
"active": true,
"local": true,
"login": "author",
"name": "John Doe",
},
],
}
}
/>

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

@@ -2,7 +2,9 @@

exports[`should render correctly 1`] = `
<Fragment>
<div>
<div
className="padded"
>
<div
className="display-flex-center"
>
@@ -31,7 +33,9 @@ exports[`should render correctly 1`] = `
</div>
</div>
<hr />
<div>
<div
className="padded"
>
<div
className="display-flex-center"
>
@@ -59,7 +63,9 @@ exports[`should render correctly 1`] = `
</div>
</div>
<hr />
<div>
<div
className="padded"
>
<div
className="display-flex-center"
>
@@ -69,7 +75,9 @@ exports[`should render correctly 1`] = `
</div>
</div>
<hr />
<div>
<div
className="padded"
>
<div
className="display-flex-center"
>
@@ -101,7 +109,7 @@ exports[`should render correctly 1`] = `
"oldValue": "old",
}
}
key="test-old-new"
key="0"
/>
<IssueChangelogDiff
diff={
@@ -111,9 +119,47 @@ exports[`should render correctly 1`] = `
"oldValue": "old-1",
}
}
key="test-1-old-1-new-1"
key="1"
/>
</div>
</div>
<hr />
<div
className="padded"
>
<div
className="display-flex-center"
>
<Connect(Avatar)
className="little-spacer-right"
name="John Doe"
size={20}
/>
<strong>
John Doe
</strong>
<span
className="little-spacer-left"
>
hotspots.tabs.review_history.comment
</span>
<span
className="little-spacer-left little-spacer-right"
>
-
</span>
<DateTimeFormatter
date="2019-09-13T17:55:42+0200"
/>
</div>
<div
className="spacer-top markdown"
dangerouslySetInnerHTML={
Object {
"__html": "<strong>bold text</strong>",
}
}
/>
</div>
</Fragment>
`;

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

@@ -26,7 +26,8 @@ exports[`should render correctly: empty tab 1`] = `
"type": 0,
"user": Object {
"active": true,
"avatar": undefined,
"local": true,
"login": "author",
"name": "John Doe",
},
},
@@ -49,10 +50,10 @@ exports[`should render correctly: empty tab 1`] = `
}
/>
<div
className="boxed-group big-padded"
className="bordered"
>
<div
className="markdown"
className="markdown big-padded"
dangerouslySetInnerHTML={
Object {
"__html": "<p>This a <strong>strong</strong> message about vulnerability !</p>",
@@ -94,7 +95,8 @@ exports[`should render correctly: fix 1`] = `
"type": 0,
"user": Object {
"active": true,
"avatar": undefined,
"local": true,
"login": "author",
"name": "John Doe",
},
},
@@ -117,10 +119,10 @@ exports[`should render correctly: fix 1`] = `
}
/>
<div
className="boxed-group big-padded"
className="bordered"
>
<div
className="markdown"
className="markdown big-padded"
dangerouslySetInnerHTML={
Object {
"__html": "<p>This a <strong>strong</strong> message about fixing !</p>",
@@ -164,7 +166,8 @@ exports[`should render correctly: review 1`] = `
"type": 0,
"user": Object {
"active": true,
"avatar": undefined,
"local": true,
"login": "author",
"name": "John Doe",
},
},
@@ -187,7 +190,7 @@ exports[`should render correctly: review 1`] = `
}
/>
<div
className="boxed-group big-padded"
className="bordered"
>
<HotspotViewerReviewHistoryTab
history={
@@ -197,7 +200,8 @@ exports[`should render correctly: review 1`] = `
"type": 0,
"user": Object {
"active": true,
"avatar": undefined,
"local": true,
"login": "author",
"name": "John Doe",
},
},
@@ -239,7 +243,8 @@ exports[`should render correctly: risk 1`] = `
"type": 0,
"user": Object {
"active": true,
"avatar": undefined,
"local": true,
"login": "author",
"name": "John Doe",
},
},
@@ -262,10 +267,10 @@ exports[`should render correctly: risk 1`] = `
}
/>
<div
className="boxed-group big-padded"
className="bordered"
>
<div
className="markdown"
className="markdown big-padded"
dangerouslySetInnerHTML={
Object {
"__html": "<p>This a <strong>strong</strong> message about risk !</p>",
@@ -307,7 +312,8 @@ exports[`should render correctly: vulnerability 1`] = `
"type": 0,
"user": Object {
"active": true,
"avatar": undefined,
"local": true,
"login": "author",
"name": "John Doe",
},
},
@@ -330,10 +336,10 @@ exports[`should render correctly: vulnerability 1`] = `
}
/>
<div
className="boxed-group big-padded"
className="bordered"
>
<div
className="markdown"
className="markdown big-padded"
dangerouslySetInnerHTML={
Object {
"__html": "<p>This a <strong>strong</strong> message about vulnerability !</p>",

+ 19
- 6
server/sonar-web/src/main/js/apps/securityHotspots/utils.ts View File

@@ -89,27 +89,40 @@ export function getHotspotReviewHistory(hotspot: Hotspot): ReviewHistoryElement[
type: ReviewHistoryType.Creation,
date: hotspot.creationDate,
user: {
avatar: hotspot.author.avatar,
name: hotspot.author.name || hotspot.author.login,
active: hotspot.author.active
...hotspot.authorUser,
name: hotspot.authorUser.name || hotspot.authorUser.login
}
});
}

if (hotspot.changelog) {
if (hotspot.changelog && hotspot.changelog.length > 0) {
history.push(
...hotspot.changelog.map(log => ({
type: ReviewHistoryType.Diff,
date: log.creationDate,
user: {
active: log.isUserActive,
avatar: log.avatar,
name: log.userName || log.user,
active: log.isUserActive
name: log.userName || log.user
},
diffs: log.diffs
}))
);
}

if (hotspot.comment && hotspot.comment.length > 0) {
history.push(
...hotspot.comment.map(comment => ({
type: ReviewHistoryType.Comment,
date: comment.createdAt,
user: {
...comment.user,
name: comment.user.name || comment.user.login
},
html: comment.htmlText
}))
);
}

return sortBy(history, elt => elt.date);
}

+ 8
- 2
server/sonar-web/src/main/js/helpers/mocks/security-hotspots.ts View File

@@ -50,10 +50,15 @@ export function mockRawHotspot(overrides: Partial<RawHotspot> = {}): RawHotspot
}

export function mockHotspot(overrides?: Partial<Hotspot>): Hotspot {
const assigneeUser = mockUser({ login: 'assignee' });
const authorUser = mockUser({ login: 'author' });
return {
assignee: mockUser(),
author: mockUser(),
assignee: 'assignee',
assigneeUser,
author: 'author',
authorUser,
changelog: [],
comment: [],
component: mockComponent({ qualifier: ComponentQualifier.File }),
creationDate: '2013-05-13T17:55:41+0200',
key: '01fc972e-2a3c-433e-bcae-0bd7f88f5123',
@@ -70,6 +75,7 @@ export function mockHotspot(overrides?: Partial<Hotspot>): Hotspot {
endOffset: 83
},
updateDate: '2013-05-13T17:55:42+0200',
users: [assigneeUser, authorUser],
...overrides
};
}

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

@@ -69,9 +69,12 @@ export interface RawHotspot {
}

export interface Hotspot {
assignee?: Pick<T.UserBase, 'active' | 'login' | 'name'>;
author: Pick<T.UserBase, 'active' | 'avatar' | 'login' | 'name'>;
changelog?: T.IssueChangelog[];
assignee?: string;
assigneeUser?: T.UserBase;
author: string;
authorUser: T.UserBase;
changelog: T.IssueChangelog[];
comment: HotspotComment[];
component: T.Component;
creationDate: string;
key: string;
@@ -83,6 +86,7 @@ export interface Hotspot {
status: string;
textRange: T.TextRange;
updateDate: string;
users: T.UserBase[];
}

export interface HotspotUpdateFields {
@@ -104,16 +108,28 @@ export interface HotspotRule {
vulnerabilityProbability: RiskExposure;
}

export interface HotspotComment {
key: string;
htmlText: string;
markdown: string;
updatable: boolean;
createdAt: string;
login: string;
user: T.UserBase;
}

export interface ReviewHistoryElement {
type: ReviewHistoryType;
date: string;
user: Pick<T.UserBase, 'active' | 'avatar' | 'name'>;
diffs?: T.IssueChangelogDiff[];
html?: string;
}

export enum ReviewHistoryType {
Creation,
Diff
Diff,
Comment
}

export interface HotspotSearchResponse {

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

@@ -661,6 +661,7 @@ hotspots.tabs.vulnerability_description=Are you vulnerable?
hotspots.tabs.fix_recommendations=How can you fix it?
hotspots.tabs.review_history=Review history
hotspots.tabs.review_history.created=created Security Hotspot
hotspots.tabs.review_history.comment=added a comment

hotspot.change_status.REVIEWED=Change status
hotspot.change_status.TO_REVIEW=Review Hotspot

Loading…
Cancel
Save