]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-21372 Deprecate old issue type project badges
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Tue, 26 Dec 2023 10:49:15 +0000 (11:49 +0100)
committersonartech <sonartech@sonarsource.com>
Thu, 28 Dec 2023 20:03:00 +0000 (20:03 +0000)
server/sonar-web/src/main/js/apps/projectInformation/badges/ProjectBadges.tsx
server/sonar-web/src/main/js/apps/projectInformation/badges/__tests__/ProjectBadges-test.tsx
server/sonar-web/src/main/js/queries/badges.ts
server/sonar-webserver-webapi/src/main/java/org/sonar/server/badge/ws/MeasureAction.java
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 0f735d2a6fd261054d72259d76889ffd4cf142e5..c31e79cb4d02cf84885d1b1e1c42da15def268e1 100644 (file)
@@ -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) {
       </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" />
index 51cac5fa82df6079f823ef69986067e5b15d2e26..8630727c730e881467c5eea7828e196c34c50593 100644 (file)
@@ -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() {
index 0626ba590508e74a2b1859a5ebb76d416cbc1891..9a448b76153aabcb077e898c1da3f2e3f2d9c4dd 100644 (file)
  */
 
 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,
         };
       }),
     };
index 71ce0d3d3129333552dbad4dc86d73802a35a7b1..d9262eff3f5b06095250b9e6f5e8692a32fab42b 100644 (file)
@@ -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<Level, String> 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.<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)
index 047be49f760af1a29ddacf4f93636a09d4954ea4..a5204ec5bf3830ce9ea2b17bb9be59b57f472900 100644 (file)
@@ -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