import * as React from 'react';
import { useState } from 'react';
import { getBranchLikeQuery } from '../../../helpers/branch-like';
-import { translate } from '../../../helpers/l10n';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { localizeMetric } from '../../../helpers/measures';
import {
+ DEPRECATED_METRIC_KEYS,
useBadgeMetricsQuery,
useBadgeTokenQuery,
useRenewBagdeTokenMutation,
</Spinner>
{BadgeType.measure === selectedType && (
- <FormField htmlFor="badge-param-customize" label={translate('overview.badges.metric')}>
- <InputSelect
- className="sw-w-abs-300"
- inputId="badge-param-customize"
- options={metricOptions}
- onChange={(option) => {
- if (option) {
- setSelectedMetric(option.value);
- }
- }}
- value={metricOptions.find((m) => m.value === selectedMetric)}
- />
- </FormField>
+ <>
+ <FormField htmlFor="badge-param-customize" label={translate('overview.badges.metric')}>
+ <InputSelect
+ className="sw-w-abs-300"
+ inputId="badge-param-customize"
+ options={metricOptions}
+ onChange={(option) => {
+ if (option) {
+ setSelectedMetric(option.value);
+ }
+ }}
+ value={metricOptions.find((m) => m.value === selectedMetric)}
+ />
+ </FormField>
+
+ {DEPRECATED_METRIC_KEYS.includes(selectedMetric) && (
+ <FlagMessage className="sw-mb-4" variant="warning">
+ {translateWithParameters(
+ 'overview.badges.deprecated_badge_x_y',
+ localizeMetric(selectedMetric),
+ translate('qualifier', qualifier),
+ )}
+ </FlagMessage>
+ )}
+ </>
)}
<BasicSeparator className="sw-mb-4" />
import { mockComponent } from '../../../../helpers/mocks/component';
import { renderComponent } from '../../../../helpers/testReactTestingUtils';
import { Location } from '../../../../helpers/urls';
+import { ComponentQualifier } from '../../../../types/component';
import { MetricKey } from '../../../../types/metrics';
import ProjectBadges, { ProjectBadgesProps } from '../ProjectBadges';
import { BadgeType } from '../utils';
{
key: 'measure',
// eslint-disable-next-line local-rules/use-metrickey-enum
- params: [{ key: 'metric', possibleValues: ['alert_status', 'coverage'] }],
+ params: [{ key: 'metric', possibleValues: ['alert_status', 'coverage', 'bugs'] }],
},
],
},
await act(async () => {
await selectEvent.openMenu(screen.getByLabelText('overview.badges.metric'));
});
- fireEvent.click(screen.getByText(MetricKey.coverage));
+ fireEvent.click(screen.getByText(`metric.${MetricKey.coverage}.name`));
expect(
screen.getByText(
`host/api/project_badges/measure?branch=branch-6.7&project=my-project&metric=${MetricKey.coverage}&token=foo`,
),
).toBeInTheDocument();
+
+ fireEvent.click(
+ screen.getByRole('button', {
+ name: `overview.badges.${BadgeType.qualityGate}.alt overview.badges.${BadgeType.qualityGate}.description.${ComponentQualifier.Project}`,
+ }),
+ );
+
+ expect(
+ screen.getByText(
+ `host/api/project_badges/quality_gate?branch=branch-6.7&project=my-project&token=foo`,
+ ),
+ ).toBeInTheDocument();
+
+ fireEvent.click(
+ screen.getByRole('button', {
+ name: `overview.badges.${BadgeType.measure}.alt overview.badges.${BadgeType.measure}.description.${ComponentQualifier.Project}`,
+ }),
+ );
+
+ expect(
+ screen.getByText(
+ `host/api/project_badges/measure?branch=branch-6.7&project=my-project&metric=${MetricKey.coverage}&token=foo`,
+ ),
+ ).toBeInTheDocument();
+});
+
+it('should warn about deprecated metrics', async () => {
+ renderProjectBadges();
+ await appLoaded();
+
+ await act(async () => {
+ await selectEvent.openMenu(screen.getByLabelText('overview.badges.metric'));
+ });
+ fireEvent.click(screen.getByText(`metric.${MetricKey.bugs}.name (deprecated)`));
+
+ expect(
+ screen.getByText(
+ `overview.badges.deprecated_badge_x_y.metric.${MetricKey.bugs}.name.qualifier.${ComponentQualifier.Project}`,
+ ),
+ ).toBeInTheDocument();
});
async function appLoaded() {
*/
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
-import { useContext } from 'react';
import { getProjectBadgesToken, renewProjectBadgesToken } from '../api/project-badges';
-import { MetricsContext } from '../app/components/metrics/MetricsContext';
-import { getLocalizedMetricName } from '../helpers/l10n';
+import { translate } from '../helpers/l10n';
+import { localizeMetric } from '../helpers/measures';
import { MetricKey } from '../types/metrics';
import { useWebApiQuery } from './web-api';
});
}
+// The same list of deprecated metric keys is maintained on the backend at org.sonar.server.badge.ws.MeasureAction.
+export const DEPRECATED_METRIC_KEYS = [
+ MetricKey.bugs,
+ MetricKey.code_smells,
+ MetricKey.security_hotspots,
+ MetricKey.vulnerabilities,
+];
+
export function useBadgeMetricsQuery() {
- const metrics = useContext(MetricsContext);
const { data: webservices = [], ...rest } = useWebApiQuery();
const domain = webservices.find((d) => d.path === 'api/project_badges');
const ws = domain?.actions.find((w) => w.key === 'measure');
if (param?.possibleValues) {
return {
...rest,
- data: param.possibleValues.map((key) => {
- const metric = metrics[key];
+ data: param.possibleValues.map((key: MetricKey) => {
+ const label = localizeMetric(key);
return {
- value: key as MetricKey,
- label: metric ? getLocalizedMetricName(metric) : key,
+ value: key,
+ label: DEPRECATED_METRIC_KEYS.includes(key)
+ ? `${label} (${translate('deprecated')})`
+ : label,
};
}),
};
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
+import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
.put(VULNERABILITIES_KEY, "vulnerabilities")
.build();
+ private static final String[] DEPRECATED_METRIC_KEYS = {BUGS_KEY, CODE_SMELLS_KEY, SECURITY_HOTSPOTS_KEY, VULNERABILITIES_KEY};
+
private static final Map<Level, String> QUALITY_GATE_MESSAGE_BY_STATUS = new EnumMap<>(Map.of(
OK, "passed",
ERROR, "failed"));
.setDescription("Generate badge for project's measure as an SVG.<br/>" +
"Requires 'Browse' permission on the specified project.")
.setSince("7.1")
+ .setChangelog(new Change("10.4", String.format("The following metric keys are now deprecated: %s", String.join(", ", DEPRECATED_METRIC_KEYS))))
.setResponseExample(Resources.getResource(getClass(), "measure-example.svg"));
support.addProjectAndBranchParams(action);
action.createParam(PARAM_METRIC)
overview.badges.leak_warning=Project badges can expose your security rating and other measures. Only use project badges in trusted environments.
overview.badges.renew=Renew Token
overview.badges.renew.description=If your project badge security token has leaked to an unsafe environment, you can renew it:
+overview.badges.deprecated_badge_x_y=Badges displaying {0} are deprecated and will be removed in a future version. Please choose another badge for your {1}.
overview.quality_profiles_update_after_sq_upgrade.message=Upgrade to SonarQube {sqVersion} has updated your Quality Profiles. Issues on your project may have been affected. {link}
overview.quality_profiles_update_after_sq_upgrade.link=See more details