Merge pull request #3184 from SDA-SE/feat/links-sonarqube-plugin

feat(plugin-sonarqube): add links to each rating
This commit is contained in:
Patrik Oldsberg
2020-11-04 11:25:23 +01:00
committed by GitHub
5 changed files with 86 additions and 45 deletions
+46 -31
View File
@@ -114,22 +114,29 @@ describe('SonarQubeApi', () => {
const client = new SonarQubeApi({ discoveryApi });
const summary = await client.getFindingSummary('our-service');
expect(summary).toEqual({
lastAnalysis: '2020-01-01T00:00:00Z',
metrics: {
alert_status: 'OK',
bugs: '2',
reliability_rating: '3.0',
vulnerabilities: '4',
security_rating: '1.0',
code_smells: '100',
sqale_rating: '2.0',
coverage: '55.5',
duplicated_lines_density: '1.0',
},
projectUrl: 'https://sonarcloud.io/dashboard?id=our-service',
} as FindingSummary);
expect(summary).toEqual(
expect.objectContaining({
lastAnalysis: '2020-01-01T00:00:00Z',
metrics: {
alert_status: 'OK',
bugs: '2',
reliability_rating: '3.0',
vulnerabilities: '4',
security_rating: '1.0',
code_smells: '100',
sqale_rating: '2.0',
coverage: '55.5',
duplicated_lines_density: '1.0',
},
projectUrl: 'https://sonarcloud.io/dashboard?id=our-service',
}),
);
expect(summary?.getIssuesUrl('CODE_SMELL')).toEqual(
'https://sonarcloud.io/project/issues?id=our-service&types=CODE_SMELL&resolved=false',
);
expect(summary?.getComponentMeasuresUrl('COVERAGE')).toEqual(
'https://sonarcloud.io/component_measures?id=our-service&metric=coverage&resolved=false&view=list',
);
});
it('should report finding summary (custom baseUrl)', async () => {
@@ -142,20 +149,28 @@ describe('SonarQubeApi', () => {
const summary = await client.getFindingSummary('our-service');
expect(summary).toEqual({
lastAnalysis: '2020-01-01T00:00:00Z',
metrics: {
alert_status: 'OK',
bugs: '2',
reliability_rating: '3.0',
vulnerabilities: '4',
security_rating: '1.0',
code_smells: '100',
sqale_rating: '2.0',
coverage: '55.5',
duplicated_lines_density: '1.0',
},
projectUrl: 'http://a.instance.local/dashboard?id=our-service',
} as FindingSummary);
expect(summary).toEqual(
expect.objectContaining({
lastAnalysis: '2020-01-01T00:00:00Z',
metrics: {
alert_status: 'OK',
bugs: '2',
reliability_rating: '3.0',
vulnerabilities: '4',
security_rating: '1.0',
code_smells: '100',
sqale_rating: '2.0',
coverage: '55.5',
duplicated_lines_density: '1.0',
},
projectUrl: 'http://a.instance.local/dashboard?id=our-service',
}) as FindingSummary,
);
expect(summary?.getIssuesUrl('CODE_SMELL')).toEqual(
'http://a.instance.local/project/issues?id=our-service&types=CODE_SMELL&resolved=false',
);
expect(summary?.getComponentMeasuresUrl('COVERAGE')).toEqual(
'http://a.instance.local/component_measures?id=our-service&metric=coverage&resolved=false&view=list',
);
});
});
+16 -1
View File
@@ -16,7 +16,12 @@
import { createApiRef, DiscoveryApi } from '@backstage/core';
import fetch from 'cross-fetch';
import { ComponentWrapper, MeasuresWrapper, MetricKey } from './types';
import {
ComponentWrapper,
MeasuresWrapper,
MetricKey,
SonarUrlProcessorFunc,
} from './types';
/**
* Define a type to make sure that all metrics are used
@@ -29,6 +34,8 @@ export interface FindingSummary {
lastAnalysis: string;
metrics: Metrics;
projectUrl: string;
getIssuesUrl: SonarUrlProcessorFunc;
getComponentMeasuresUrl: SonarUrlProcessorFunc;
}
export const sonarQubeApiRef = createApiRef<SonarQubeApi>({
@@ -105,6 +112,14 @@ export class SonarQubeApi {
lastAnalysis: component.component.analysisDate,
metrics,
projectUrl: `${this.baseUrl}dashboard?id=${componentKey}`,
getIssuesUrl: identifier =>
`${
this.baseUrl
}project/issues?id=${componentKey}&types=${identifier.toUpperCase()}&resolved=false`,
getComponentMeasuresUrl: (identifier: string) =>
`${
this.baseUrl
}component_measures?id=${componentKey}&metric=${identifier.toLowerCase()}&resolved=false&view=list`,
};
}
}
+2
View File
@@ -53,3 +53,5 @@ export interface Measure {
value: string;
component: string;
}
export type SonarUrlProcessorFunc = (identifier: string) => string;
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { Grid, Typography } from '@material-ui/core';
import { Grid, Typography, Link } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import React, { ReactNode } from 'react';
@@ -50,29 +50,33 @@ export const RatingCard = ({
rightSlot,
title,
titleIcon,
link,
}: {
leftSlot: ReactNode;
rightSlot: ReactNode;
title: string;
titleIcon?: ReactNode;
link: string;
}) => {
const classes = useStyles();
return (
<Grid item className={classes.root}>
<Grid item className={classes.upper}>
<Grid item className={classes.left}>
{leftSlot}
<Link href={link} color="inherit" underline="none">
<Grid item className={classes.root}>
<Grid item className={classes.upper}>
<Grid item className={classes.left}>
{leftSlot}
</Grid>
<Grid item className={classes.right}>
{rightSlot}
</Grid>
</Grid>
<Grid item className={classes.right}>
{rightSlot}
<Grid item className={classes.cardTitle}>
<Typography variant="body1" className={classes.wrapIcon}>
{titleIcon} {title}
</Typography>
</Grid>
</Grid>
<Grid item className={classes.cardTitle}>
<Typography variant="body1" className={classes.wrapIcon}>
{titleIcon} {title}
</Typography>
</Grid>
</Grid>
</Link>
);
};
@@ -187,29 +187,34 @@ export const SonarQubeCard = ({
<RatingCard
titleIcon={<BugReport />}
title="Bugs"
link={value.getIssuesUrl('BUG')}
leftSlot={<Value value={value.metrics.bugs} />}
rightSlot={<Rating rating={value.metrics.reliability_rating} />}
/>
<RatingCard
titleIcon={<LockOpen />}
title="Vulnerabilities"
link={value.getIssuesUrl('VULNERABILITY')}
leftSlot={<Value value={value.metrics.vulnerabilities} />}
rightSlot={<Rating rating={value.metrics.security_rating} />}
/>
<RatingCard
titleIcon={<SentimentVeryDissatisfied />}
title="Code Smells"
link={value.getIssuesUrl('CODE_SMELL')}
leftSlot={<Value value={value.metrics.code_smells} />}
rightSlot={<Rating rating={value.metrics.sqale_rating} />}
/>
<div style={{ width: '100%' }} />
<RatingCard
link={value.getComponentMeasuresUrl('COVERAGE')}
title="Coverage"
leftSlot={<Percentage value={value.metrics.coverage} />}
rightSlot={<Value value={`${value.metrics.coverage}%`} />}
/>
<RatingCard
title="Duplications"
link={value.getComponentMeasuresUrl('DUPLICATED_LINES_DENSITY')}
leftSlot={<Rating rating={duplicationRating} hideValue />}
rightSlot={
<Value value={`${value.metrics.duplicated_lines_density}%`} />