From 77f502f4a707805e47ec1e472563777a8f3e6581 Mon Sep 17 00:00:00 2001 From: Wouter Admiraal Date: Tue, 26 Dec 2023 11:49:15 +0100 Subject: [PATCH] SONAR-21372 Deprecate old issue type project badges --- .../badges/ProjectBadges.tsx | 42 +++++++++++------ .../badges/__tests__/ProjectBadges-test.tsx | 45 ++++++++++++++++++- .../sonar-web/src/main/js/queries/badges.ts | 24 ++++++---- .../sonar/server/badge/ws/MeasureAction.java | 4 ++ .../resources/org/sonar/l10n/core.properties | 1 + 5 files changed, 92 insertions(+), 24 deletions(-) diff --git a/server/sonar-web/src/main/js/apps/projectInformation/badges/ProjectBadges.tsx b/server/sonar-web/src/main/js/apps/projectInformation/badges/ProjectBadges.tsx index 0f735d2a6fd..c31e79cb4d0 100644 --- a/server/sonar-web/src/main/js/apps/projectInformation/badges/ProjectBadges.tsx +++ b/server/sonar-web/src/main/js/apps/projectInformation/badges/ProjectBadges.tsx @@ -33,8 +33,10 @@ import { isEmpty } from 'lodash'; 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, @@ -130,19 +132,31 @@ export default function ProjectBadges(props: ProjectBadgesProps) { {BadgeType.measure === selectedType && ( - - { - if (option) { - setSelectedMetric(option.value); - } - }} - value={metricOptions.find((m) => m.value === selectedMetric)} - /> - + <> + + { + if (option) { + setSelectedMetric(option.value); + } + }} + value={metricOptions.find((m) => m.value === selectedMetric)} + /> + + + {DEPRECATED_METRIC_KEYS.includes(selectedMetric) && ( + + {translateWithParameters( + 'overview.badges.deprecated_badge_x_y', + localizeMetric(selectedMetric), + translate('qualifier', qualifier), + )} + + )} + )} diff --git a/server/sonar-web/src/main/js/apps/projectInformation/badges/__tests__/ProjectBadges-test.tsx b/server/sonar-web/src/main/js/apps/projectInformation/badges/__tests__/ProjectBadges-test.tsx index 51cac5fa82d..8630727c730 100644 --- a/server/sonar-web/src/main/js/apps/projectInformation/badges/__tests__/ProjectBadges-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectInformation/badges/__tests__/ProjectBadges-test.tsx @@ -26,6 +26,7 @@ import { mockBranch } from '../../../../helpers/mocks/branch-like'; 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'; @@ -49,7 +50,7 @@ jest.mock('../../../../api/web-api', () => ({ { 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'] }], }, ], }, @@ -108,13 +109,53 @@ it('should update params', async () => { 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() { diff --git a/server/sonar-web/src/main/js/queries/badges.ts b/server/sonar-web/src/main/js/queries/badges.ts index 0626ba59050..9a448b76153 100644 --- a/server/sonar-web/src/main/js/queries/badges.ts +++ b/server/sonar-web/src/main/js/queries/badges.ts @@ -19,10 +19,9 @@ */ 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'; @@ -38,8 +37,15 @@ export function useRenewBagdeTokenMutation() { }); } +// 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'); @@ -47,11 +53,13 @@ export function useBadgeMetricsQuery() { 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, }; }), }; diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/MeasureAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/MeasureAction.java index 71ce0d3d312..d9262eff3f5 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/MeasureAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/MeasureAction.java @@ -28,6 +28,7 @@ import java.util.Locale; 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; @@ -94,6 +95,8 @@ public class MeasureAction implements ProjectBadgesWsAction { .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 QUALITY_GATE_MESSAGE_BY_STATUS = new EnumMap<>(Map.of( OK, "passed", ERROR, "failed")); @@ -126,6 +129,7 @@ public class MeasureAction implements ProjectBadgesWsAction { .setDescription("Generate badge for project's measure as an SVG.
" + "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) diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 047be49f760..a5204ec5bf3 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -4001,6 +4001,7 @@ overview.badges.quality_gate.description.VW=Displays the current quality gate st 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 -- 2.39.5