aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx49
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/UpdateConditionsFromOtherModeModal.tsx321
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/utils.ts28
-rw-r--r--server/sonar-web/src/main/js/queries/quality-gates.ts37
-rw-r--r--server/sonar-web/src/main/js/sonar-aligned/types/metrics.ts16
5 files changed, 438 insertions, 13 deletions
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx
index c815e3a8daa..be3d0fb8f82 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Button, Spinner } from '@sonarsource/echoes-react';
import { uniqBy } from 'lodash';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
@@ -31,7 +32,6 @@ import {
LightPrimary,
Link,
Note,
- Spinner,
SubHeading,
} from '~design-system';
import DocHelpTooltip from '~sonar-aligned/components/controls/DocHelpTooltip';
@@ -42,9 +42,16 @@ import { ModalProps } from '../../../components/controls/ModalButton';
import { DocLink } from '../../../helpers/doc-links';
import { useDocUrl } from '../../../helpers/docs';
import { getLocalizedMetricName, translate } from '../../../helpers/l10n';
+import { useStandardExperienceMode } from '../../../queries/settings';
+import { MetricKey } from '../../../sonar-aligned/types/metrics';
import { Feature } from '../../../types/features';
import { CaycStatus, Condition as ConditionType, QualityGate } from '../../../types/types';
-import { groupAndSortByPriorityConditions, isQualityGateOptimized } from '../utils';
+import {
+ groupAndSortByPriorityConditions,
+ isQualityGateOptimized,
+ MQR_CONDITIONS_MAP,
+ STANDARD_CONDITIONS_MAP,
+} from '../utils';
import AddConditionModal from './AddConditionModal';
import AIGeneratedIcon from './AIGeneratedIcon';
import CaycCompliantBanner from './CaycCompliantBanner';
@@ -54,6 +61,7 @@ import CaycFixOptimizeBanner from './CaycFixOptimizeBanner';
import CaycReviewUpdateConditionsModal from './ConditionReviewAndUpdateModal';
import ConditionsTable from './ConditionsTable';
import QGRecommendedIcon from './QGRecommendedIcon';
+import UpdateConditionsFromOtherModeModal from './UpdateConditionsFromOtherModeModal';
interface Props {
isFetching?: boolean;
@@ -66,6 +74,7 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
const [editing, setEditing] = React.useState<boolean>(caycStatus === CaycStatus.NonCompliant);
const metrics = useMetrics();
const { hasFeature } = useAvailableFeatures();
+ const { data: isStandardMode, isLoading } = useStandardExperienceMode();
const canEdit = Boolean(actions?.manageConditions);
const existingConditions = conditions.filter((condition) => metrics[condition.metric]);
@@ -116,10 +125,11 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
[qualityGate, conditions, metrics, isOptimizing, canEdit],
);
+ const conditionsToOtherModeMap = isStandardMode ? MQR_CONDITIONS_MAP : STANDARD_CONDITIONS_MAP;
+
return (
- <div>
+ <Spinner isLoading={isLoading}>
<CaYCConditionsSimplificationGuide qualityGate={qualityGate} />
-
{isBuiltIn && (
<div className="sw-flex sw-items-center">
<QGRecommendedIcon className="sw-mr-1" />
@@ -138,7 +148,6 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
</LightLabel>
</div>
)}
-
{isAICodeAssuranceQualityGate && (
<div className="sw-flex sw-items-center sw-mt-2">
<AIGeneratedIcon className="sw-mr-1" />
@@ -157,7 +166,6 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
</LightLabel>
</div>
)}
-
{isCompliantCustomQualityGate && !isOptimizing && <CaycCompliantBanner />}
{isCompliantCustomQualityGate && isOptimizing && canEdit && (
<CaycFixOptimizeBanner renderCaycModal={renderCaycModal} isOptimizing />
@@ -165,7 +173,26 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
{caycStatus === CaycStatus.NonCompliant && canEdit && (
<CaycFixOptimizeBanner renderCaycModal={renderCaycModal} />
)}
-
+ <UpdateConditionsFromOtherModeModal
+ qualityGateName={qualityGate.name}
+ newCodeConditions={newCodeConditions.filter(
+ (c) => conditionsToOtherModeMap[c.metric as MetricKey] !== undefined,
+ )}
+ overallCodeConditions={overallCodeConditions.filter(
+ (c) => conditionsToOtherModeMap[c.metric as MetricKey] !== undefined,
+ )}
+ >
+ {/* TODO test example */}
+ <Button>Review and update metrics</Button>
+ </UpdateConditionsFromOtherModeModal>
+ <UpdateConditionsFromOtherModeModal
+ qualityGateName={qualityGate.name}
+ isSingleMetric
+ condition={newCodeConditions.find((c) => c.metric.includes('rating'))!}
+ >
+ {/* TODO test example */}
+ <Button>Test single</Button>
+ </UpdateConditionsFromOtherModeModal>
<header className="sw-flex sw-items-center sw-mt-9 sw-mb-4 sw-justify-between">
<div className="sw-flex">
<HeadingDark className="sw-typo-lg-semibold sw-m-0">
@@ -185,7 +212,7 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
<HelperHintIcon />
</DocHelpTooltip>
)}
- <Spinner loading={isFetching} className="sw-ml-4 sw-mt-1" />
+ <Spinner isLoading={isFetching} className="sw-ml-4 sw-mt-1" />
</div>
<div>
{(caycStatus === CaycStatus.NonCompliant || editing) && canEdit && (
@@ -193,7 +220,6 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
)}
</div>
</header>
-
{uniqDuplicates.length > 0 && (
<FlagMessage variant="warning" className="sw-flex sw-mb-4">
<div>
@@ -206,7 +232,6 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
</div>
</FlagMessage>
)}
-
<div className="sw-flex sw-flex-col sw-gap-8">
{caycConditions.length > 0 && (
<div>
@@ -288,7 +313,6 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
</div>
)}
</div>
-
{caycStatus !== CaycStatus.NonCompliant && !editing && canEdit && (
<div className="sw-mt-4 it__qg-unfollow-cayc">
<SubHeading as="p" className="sw-mb-2 sw-typo-default">
@@ -305,12 +329,11 @@ export default function Conditions({ qualityGate, isFetching }: Readonly<Props>)
</ButtonSecondary>
</div>
)}
-
{existingConditions.length === 0 && (
<div className="sw-mt-4 sw-typo-default">
<LightPrimary as="p">{translate('quality_gates.no_conditions')}</LightPrimary>
</div>
)}
- </div>
+ </Spinner>
);
}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/UpdateConditionsFromOtherModeModal.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/UpdateConditionsFromOtherModeModal.tsx
new file mode 100644
index 00000000000..50775c76dda
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/UpdateConditionsFromOtherModeModal.tsx
@@ -0,0 +1,321 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import {
+ Button,
+ ButtonVariety,
+ Heading,
+ IconArrowRight,
+ Modal,
+ ModalSize,
+ Text,
+ TextSize,
+} from '@sonarsource/echoes-react';
+import * as React from 'react';
+import { FormattedMessage, useIntl } from 'react-intl';
+import { useMetrics } from '../../../app/components/metrics/withMetricsContext';
+import DocumentationLink from '../../../components/common/DocumentationLink';
+import { ContentCell, FlagMessageV2, Table, TableRow } from '../../../design-system';
+import { DocLink } from '../../../helpers/doc-links';
+import { translate } from '../../../helpers/l10n';
+import { getOperatorLabel } from '../../../helpers/qualityGates';
+import { useUpdateOrDeleteConditionsMutation } from '../../../queries/quality-gates';
+import { useStandardExperienceMode } from '../../../queries/settings';
+import { MetricKey } from '../../../sonar-aligned/types/metrics';
+import { Condition } from '../../../types/types';
+import {
+ getLocalizedMetricNameNoDiffMetric,
+ MQR_CONDITIONS_MAP,
+ STANDARD_CONDITIONS_MAP,
+} from '../utils';
+import ConditionValue from './ConditionValue';
+
+type Props = React.PropsWithChildren & { qualityGateName: string } & (
+ | {
+ condition?: never;
+ isSingleMetric?: false;
+ newCodeConditions: Condition[];
+ overallCodeConditions: Condition[];
+ }
+ | {
+ condition: Condition;
+ isSingleMetric: true;
+ newCodeConditions?: never;
+ overallCodeConditions?: never;
+ }
+ );
+
+export default function UpdateConditionsFromOtherModeModal({
+ newCodeConditions,
+ overallCodeConditions,
+ qualityGateName,
+ isSingleMetric,
+ condition,
+ children,
+}: Readonly<Props>) {
+ const { data: isStandard } = useStandardExperienceMode();
+ const [isOpen, setOpen] = React.useState(false);
+ const [error, setError] = React.useState(false);
+ const intl = useIntl();
+ const mapper = isStandard ? MQR_CONDITIONS_MAP : STANDARD_CONDITIONS_MAP;
+ const { mutate: updateConditions, isPending } = useUpdateOrDeleteConditionsMutation(
+ qualityGateName,
+ isSingleMetric,
+ );
+
+ const onSubmit = () => {
+ const conditions = isSingleMetric
+ ? [condition]
+ : [...newCodeConditions, ...overallCodeConditions];
+
+ updateConditions(
+ conditions.map((c) => ({ ...c, metric: mapper[c.metric as MetricKey] })),
+ {
+ onSuccess: () => setOpen(false),
+ onError: () => setError(true),
+ },
+ );
+ };
+
+ return (
+ <Modal
+ isOpen={isOpen}
+ onOpenChange={setOpen}
+ title={intl.formatMessage(
+ {
+ id: `quality_gates.update_conditions.header${isSingleMetric ? '.single_metric' : ''}`,
+ },
+ { qualityGate: qualityGateName },
+ )}
+ size={isSingleMetric ? ModalSize.Default : ModalSize.Wide}
+ primaryButton={
+ <Button
+ isDisabled={isPending}
+ isLoading={isPending}
+ onClick={onSubmit}
+ id="update-metrics-button"
+ variety={ButtonVariety.Primary}
+ >
+ {intl.formatMessage({
+ id: isSingleMetric ? 'update_verb' : 'quality_gates.update_conditions.update_metrics',
+ })}
+ </Button>
+ }
+ secondaryButton={<Button onClick={() => setOpen(false)}>{translate('cancel')}</Button>}
+ content={
+ <>
+ {error && (
+ <div>
+ <FlagMessageV2 variant="error">
+ {intl.formatMessage({ id: 'quality_gates.update_conditions.error' })}
+ </FlagMessageV2>
+ </div>
+ )}
+ <Text>
+ <FormattedMessage
+ id="quality_gates.update_conditions.description.line1"
+ values={{
+ b: (chunks) => <Text isHighlighted>{chunks}</Text>,
+ mode: intl.formatMessage({
+ id: `settings.mode.${isStandard ? 'standard' : 'mqr'}.name`,
+ }),
+ }}
+ />
+ </Text>
+
+ {isSingleMetric && <SingleMetric condition={condition} />}
+ <Text>
+ <FormattedMessage
+ id="quality_gates.update_conditions.description.line2"
+ values={{
+ b: (chunks) => <Text isHighlighted>{chunks}</Text>,
+ }}
+ />
+ <br />
+ <br />
+ <FormattedMessage
+ id="quality_gates.update_conditions.description.line3"
+ values={{
+ mode: intl.formatMessage({
+ id: `settings.mode.${isStandard ? 'standard' : 'mqr'}.name`,
+ }),
+ link: (
+ <DocumentationLink to={isStandard ? DocLink.ModeStandard : DocLink.ModeMQR}>
+ {intl.formatMessage({
+ id: 'quality_gates.update_conditions.description.link',
+ })}
+ </DocumentationLink>
+ ),
+ }}
+ />
+ </Text>
+ {!isSingleMetric && (
+ <>
+ {newCodeConditions.length > 0 && (
+ <>
+ <Heading as="h3" className="sw-mt-8">
+ {intl.formatMessage({ id: 'overview.new_code' })}
+ </Heading>
+ <Table
+ columnCount={3}
+ columnWidths={['35%', '35%', 'auto']}
+ className="sw-my-2"
+ header={<Header />}
+ >
+ {newCodeConditions.map((condition) => (
+ <ConditionRow condition={condition} key={condition.id} />
+ ))}
+ </Table>
+ </>
+ )}
+ {overallCodeConditions.length > 0 && (
+ <>
+ <Heading as="h3" className="sw-mt-8">
+ {intl.formatMessage({ id: 'overview.overall_code' })}
+ </Heading>
+ <Table
+ columnCount={3}
+ columnWidths={['35%', '35%', 'auto']}
+ className="sw-my-2"
+ header={<Header />}
+ >
+ {overallCodeConditions.map((condition) => (
+ <ConditionRow condition={condition} key={condition.id} />
+ ))}
+ </Table>
+ </>
+ )}
+ </>
+ )}
+ </>
+ }
+ >
+ {React.cloneElement(children as React.ReactElement, { onClick: () => setOpen(true) })}
+ </Modal>
+ );
+}
+
+function SingleMetric({ condition }: Readonly<{ condition: Condition }>) {
+ const { data: isStandard } = useStandardExperienceMode();
+ const intl = useIntl();
+ const metrics = useMetrics();
+ const metric = metrics[condition.metric];
+ const mapper = isStandard ? MQR_CONDITIONS_MAP : STANDARD_CONDITIONS_MAP;
+ const metricFromOtherMode = mapper[metric.key as MetricKey];
+
+ return (
+ <div className="sw-flex sw-justify-between sw-my-8 sw-items-center sw-w-[80%]">
+ <div>
+ <Text as="div" size={TextSize.Small}>
+ {intl.formatMessage({
+ id: `quality_gates.update_conditions.${isStandard ? 'mqr' : 'standard'}_mode_header`,
+ })}
+ </Text>
+ <Text as="div" size={TextSize.Small} isHighlighted>
+ {getLocalizedMetricNameNoDiffMetric(metrics[condition.metric], metrics)}
+ </Text>
+ </div>
+ <IconArrowRight />
+ <div>
+ {metricFromOtherMode ? (
+ <>
+ <Text as="div" size={TextSize.Small}>
+ {intl.formatMessage({
+ id: `quality_gates.update_conditions.${isStandard ? 'standard' : 'mqr'}_mode_header`,
+ })}
+ </Text>
+ <Text as="div" size={TextSize.Small} isHighlighted>
+ {getLocalizedMetricNameNoDiffMetric(metrics[metricFromOtherMode], metrics)}
+ </Text>
+ </>
+ ) : (
+ <Text size={TextSize.Small}>
+ {intl.formatMessage({ id: 'quality_gates.update_conditions.removed' })}
+ </Text>
+ )}
+ </div>
+ </div>
+ );
+}
+
+function Header() {
+ const intl = useIntl();
+ const { data: isStandard } = useStandardExperienceMode();
+
+ return (
+ <TableRow>
+ <ContentCell className="sw-justify-between sw-pr-4">
+ <Heading as="h4" className="sw-typo-semibold sw-m-0 sw-whitespace-nowrap">
+ {intl.formatMessage({
+ id: `quality_gates.update_conditions.${isStandard ? 'mqr' : 'standard'}_mode_header`,
+ })}
+ </Heading>
+ <IconArrowRight />
+ </ContentCell>
+ <ContentCell>
+ <Heading as="h4" className="sw-typo-semibold sw-m-0 sw-whitespace-nowrap">
+ {intl.formatMessage({
+ id: `quality_gates.update_conditions.${isStandard ? 'standard' : 'mqr'}_mode_header`,
+ })}
+ </Heading>
+ </ContentCell>
+ <ContentCell>
+ <Heading as="h4" className="sw-typo-semibold sw-m-0 sw-whitespace-nowrap">
+ {intl.formatMessage({ id: 'quality_gates.update_conditions.operator_and_value_header' })}
+ </Heading>
+ </ContentCell>
+ <ContentCell />
+ </TableRow>
+ );
+}
+
+function ConditionRow({ condition }: Readonly<{ condition: Condition }>) {
+ const { data: isStandard } = useStandardExperienceMode();
+ const intl = useIntl();
+ const metrics = useMetrics();
+ const { op = 'GT' } = condition;
+ const metric = metrics[condition.metric];
+ const mapper = isStandard ? MQR_CONDITIONS_MAP : STANDARD_CONDITIONS_MAP;
+ const metricFromOtherMode = mapper[metric?.key as MetricKey];
+
+ return (
+ <TableRow>
+ <ContentCell>{getLocalizedMetricNameNoDiffMetric(metric, metrics)}</ContentCell>
+ <ContentCell>
+ {metricFromOtherMode ? (
+ getLocalizedMetricNameNoDiffMetric(metrics[metricFromOtherMode], metrics)
+ ) : (
+ <Text colorOverride="echoes-color-text-danger">
+ {intl.formatMessage({ id: 'quality_gates.update_conditions.removed' })}
+ </Text>
+ )}
+ </ContentCell>
+
+ <ContentCell className="sw-whitespace-nowrap">
+ {metricFromOtherMode && (
+ <Text isSubdued>
+ {getOperatorLabel(op, metric)}&nbsp;
+ <ConditionValue condition={condition} metric={metric} />
+ </Text>
+ )}
+ </ContentCell>
+ </TableRow>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/utils.ts b/server/sonar-web/src/main/js/apps/quality-gates/utils.ts
index 8cfc4f50e87..99e3aad55b3 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/utils.ts
+++ b/server/sonar-web/src/main/js/apps/quality-gates/utils.ts
@@ -20,6 +20,7 @@
import { sortBy } from 'lodash';
import { MetricKey } from '~sonar-aligned/types/metrics';
+import { SOFTWARE_QUALITY_RATING_METRICS_MAP } from '../../helpers/constants';
import { getLocalizedMetricName } from '../../helpers/l10n';
import { isDiffMetric } from '../../helpers/measures';
import { CaycStatus, Condition, Dict, Group, Metric, QualityGate } from '../../types/types';
@@ -148,6 +149,33 @@ const CAYC_CONDITIONS_WITH_FIXED_VALUE: AllCaycMetricKeys[] = [
];
const NON_EDITABLE_CONDITIONS: MetricKey[] = [MetricKey.prioritized_rule_issues];
+export const STANDARD_CONDITIONS_MAP: Partial<Record<MetricKey, MetricKey>> = {
+ [MetricKey.new_blocker_violations]: MetricKey.new_software_quality_blocker_issues,
+ [MetricKey.new_critical_violations]: MetricKey.new_software_quality_high_issues,
+ [MetricKey.new_major_violations]: MetricKey.new_software_quality_medium_issues,
+ [MetricKey.new_minor_violations]: MetricKey.new_software_quality_low_issues,
+ [MetricKey.new_info_violations]: MetricKey.new_software_quality_info_issues,
+ [MetricKey.blocker_violations]: MetricKey.software_quality_blocker_issues,
+ [MetricKey.critical_violations]: MetricKey.software_quality_high_issues,
+ [MetricKey.major_violations]: MetricKey.software_quality_medium_issues,
+ [MetricKey.minor_violations]: MetricKey.software_quality_low_issues,
+ [MetricKey.info_violations]: MetricKey.software_quality_info_issues,
+ [MetricKey.new_vulnerabilities]: MetricKey.new_software_quality_security_issues,
+ [MetricKey.new_bugs]: MetricKey.new_software_quality_reliability_issues,
+ [MetricKey.new_code_smells]: MetricKey.new_software_quality_maintainability_issues,
+ [MetricKey.vulnerabilities]: MetricKey.software_quality_security_issues,
+ [MetricKey.bugs]: MetricKey.software_quality_reliability_issues,
+ [MetricKey.code_smells]: MetricKey.software_quality_maintainability_issues,
+ ...SOFTWARE_QUALITY_RATING_METRICS_MAP,
+};
+
+export const MQR_CONDITIONS_MAP: Partial<Record<MetricKey, MetricKey | null>> = {
+ ...Object.fromEntries(
+ Object.entries(STANDARD_CONDITIONS_MAP).map(([key, value]) => [value, key]),
+ ),
+ [MetricKey.high_impact_accepted_issues]: null,
+};
+
export function isConditionWithFixedValue(condition: Condition) {
return CAYC_CONDITIONS_WITH_FIXED_VALUE.includes(condition.metric as OptimizedCaycMetricKeys);
}
diff --git a/server/sonar-web/src/main/js/queries/quality-gates.ts b/server/sonar-web/src/main/js/queries/quality-gates.ts
index 5b2524e4be2..acc880b1e73 100644
--- a/server/sonar-web/src/main/js/queries/quality-gates.ts
+++ b/server/sonar-web/src/main/js/queries/quality-gates.ts
@@ -19,6 +19,7 @@
*/
import { queryOptions, useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
+import { useIntl } from 'react-intl';
import { addGlobalSuccessMessage } from '~design-system';
import { BranchParameters } from '~sonar-aligned/types/branch-like';
import {
@@ -238,6 +239,42 @@ export function useUpdateConditionMutation(gateName: string) {
});
}
+export function useUpdateOrDeleteConditionsMutation(gateName: string, isSingleMetric?: boolean) {
+ const queryClient = useQueryClient();
+ const intl = useIntl();
+
+ return useMutation({
+ mutationFn: (
+ conditions: (Omit<Condition, 'metric'> & { metric: string | null | undefined })[],
+ ) => {
+ const promiseArr = conditions.map((condition) =>
+ condition.metric
+ ? updateCondition(condition as Condition)
+ : deleteCondition({ id: condition.id }),
+ );
+
+ return Promise.all(promiseArr);
+ },
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: qualityQuery.list() });
+ queryClient.invalidateQueries({ queryKey: qualityQuery.detail(gateName) });
+ addGlobalSuccessMessage(
+ intl.formatMessage(
+ {
+ id: isSingleMetric
+ ? 'quality_gates.condition_updated'
+ : 'quality_gates.conditions_updated_to_the_mode',
+ },
+ { qualityGateName: gateName },
+ ),
+ );
+ },
+ onError: () => {
+ queryClient.invalidateQueries({ queryKey: qualityQuery.detail(gateName) });
+ },
+ });
+}
+
export function useDeleteConditionMutation(gateName: string) {
const queryClient = useQueryClient();
diff --git a/server/sonar-web/src/main/js/sonar-aligned/types/metrics.ts b/server/sonar-web/src/main/js/sonar-aligned/types/metrics.ts
index d711330cfa7..db99c08030b 100644
--- a/server/sonar-web/src/main/js/sonar-aligned/types/metrics.ts
+++ b/server/sonar-web/src/main/js/sonar-aligned/types/metrics.ts
@@ -22,13 +22,16 @@ export enum MetricKey {
accepted_issues = 'accepted_issues',
alert_status = 'alert_status',
blocker_violations = 'blocker_violations',
+ software_quality_blocker_issues = 'software_quality_blocker_issues',
branch_coverage = 'branch_coverage',
bugs = 'bugs',
+ software_quality_reliability_issues = 'software_quality_reliability_issues',
burned_budget = 'burned_budget',
business_value = 'business_value',
class_complexity = 'class_complexity',
classes = 'classes',
code_smells = 'code_smells',
+ software_quality_maintainability_issues = 'software_quality_maintainability_issues',
cognitive_complexity = 'cognitive_complexity',
comment_lines = 'comment_lines',
comment_lines_data = 'comment_lines_data',
@@ -40,6 +43,7 @@ export enum MetricKey {
confirmed_issues = 'confirmed_issues',
coverage = 'coverage',
critical_violations = 'critical_violations',
+ software_quality_high_issues = 'software_quality_high_issues',
development_cost = 'development_cost',
directories = 'directories',
duplicated_blocks = 'duplicated_blocks',
@@ -63,6 +67,7 @@ export enum MetricKey {
generated_ncloc = 'generated_ncloc',
high_impact_accepted_issues = 'high_impact_accepted_issues',
info_violations = 'info_violations',
+ software_quality_info_issues = 'software_quality_info_issues',
issues = 'issues',
last_change_on_maintainability_rating = 'last_change_on_maintainability_rating',
last_change_on_releasability_rating = 'last_change_on_releasability_rating',
@@ -83,23 +88,30 @@ export enum MetricKey {
maintainability_rating_effort = 'maintainability_rating_effort',
software_quality_maintainability_rating_effort = 'software_quality_maintainability_rating_effort',
major_violations = 'major_violations',
+ software_quality_medium_issues = 'software_quality_medium_issues',
minor_violations = 'minor_violations',
+ software_quality_low_issues = 'software_quality_low_issues',
ncloc = 'ncloc',
ncloc_data = 'ncloc_data',
ncloc_language_distribution = 'ncloc_language_distribution',
new_accepted_issues = 'new_accepted_issues',
new_blocker_violations = 'new_blocker_violations',
+ new_software_quality_blocker_issues = 'new_software_quality_blocker_issues',
new_branch_coverage = 'new_branch_coverage',
new_bugs = 'new_bugs',
+ new_software_quality_reliability_issues = 'new_software_quality_reliability_issues',
new_code_smells = 'new_code_smells',
+ new_software_quality_maintainability_issues = 'new_software_quality_maintainability_issues',
new_conditions_to_cover = 'new_conditions_to_cover',
new_coverage = 'new_coverage',
new_critical_violations = 'new_critical_violations',
+ new_software_quality_high_issues = 'new_software_quality_high_issues',
new_development_cost = 'new_development_cost',
new_duplicated_blocks = 'new_duplicated_blocks',
new_duplicated_lines = 'new_duplicated_lines',
new_duplicated_lines_density = 'new_duplicated_lines_density',
new_info_violations = 'new_info_violations',
+ new_software_quality_info_issues = 'new_software_quality_info_issues',
new_issues = 'new_issues',
new_line_coverage = 'new_line_coverage',
new_lines = 'new_lines',
@@ -110,7 +122,9 @@ export enum MetricKey {
new_maintainability_rating_distribution = 'new_maintainability_rating_distribution',
new_software_quality_maintainability_rating_distribution = 'new_software_quality_maintainability_rating_distribution',
new_major_violations = 'new_major_violations',
+ new_software_quality_medium_issues = 'new_software_quality_medium_issues',
new_minor_violations = 'new_minor_violations',
+ new_software_quality_low_issues = 'new_software_quality_low_issues',
new_reliability_issues = 'new_reliability_issues',
new_reliability_rating = 'new_reliability_rating',
new_software_quality_reliability_rating = 'new_software_quality_reliability_rating',
@@ -138,6 +152,7 @@ export enum MetricKey {
new_violations = 'new_violations',
new_violations_rating = 'new_violations_rating',
new_vulnerabilities = 'new_vulnerabilities',
+ new_software_quality_security_issues = 'new_software_quality_security_issues',
open_issues = 'open_issues',
prioritized_rule_issues = 'prioritized_rule_issues',
projects = 'projects',
@@ -195,6 +210,7 @@ export enum MetricKey {
violations = 'violations',
violations_rating = 'violations_rating',
vulnerabilities = 'vulnerabilities',
+ software_quality_security_issues = 'software_quality_security_issues',
wont_fix_issues = 'wont_fix_issues',
}