aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMathieu Suen <mathieu.suen@sonarsource.com>2024-11-29 11:01:26 +0100
committersonartech <sonartech@sonarsource.com>2024-11-29 20:03:08 +0000
commitd08fbb88944054b2244b1fc509a5efaa7c3d2036 (patch)
tree06edf249be9a3ed3bbaa3adb2757bd4962e20b22
parent640c9331567e0943c453f5d4284e5151b5070159 (diff)
downloadsonarqube-d08fbb88944054b2244b1fc509a5efaa7c3d2036.tar.gz
sonarqube-d08fbb88944054b2244b1fc509a5efaa7c3d2036.zip
SONAR-23620 Fix modal when disqualifing a Quality Gate for AI assurance
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/Projects.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGate-it.tsx22
-rw-r--r--server/sonar-web/src/main/js/queries/quality-gates.ts24
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties2
6 files changed, 28 insertions, 36 deletions
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.tsx
index a02ed0244ae..bc8138e3144 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.tsx
@@ -23,6 +23,7 @@ import * as React from 'react';
import { FlagMessage } from '~design-system';
import DocHelpTooltip from '~sonar-aligned/components/controls/DocHelpTooltip';
import { translate } from '../../../helpers/l10n';
+import { useInvalidateQualityGateQuery } from '../../../queries/quality-gates';
import { QualityGate } from '../../../types/types';
import Conditions from './Conditions';
import Projects from './Projects';
@@ -36,6 +37,7 @@ export interface DetailsContentProps {
export function DetailsContent(props: DetailsContentProps) {
const { qualityGate, isFetching } = props;
const actions = qualityGate.actions ?? {};
+ const invalidateQueryCache = useInvalidateQualityGateQuery();
return (
<div>
@@ -64,6 +66,7 @@ export function DetailsContent(props: DetailsContentProps) {
) : (
<Projects
canEdit={actions.associateProjects}
+ onUpdate={invalidateQueryCache}
// pass unique key to re-mount the component when the quality gate changes
key={qualityGate.name}
qualityGate={qualityGate}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.tsx
index 09df183df4d..466907065cc 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.tsx
@@ -61,9 +61,7 @@ export default function DetailsHeader({ qualityGate }: Readonly<Props>) {
actions.setAsDefault,
])['true'];
const { mutateAsync: setQualityGateAsDefault } = useSetQualityGateAsDefaultMutation();
- const { mutateAsync: setAiSupportedQualityGate } = useSetAiSupportedQualityGateMutation(
- qualityGate.name,
- );
+ const { mutateAsync: setAiSupportedQualityGate } = useSetAiSupportedQualityGateMutation();
const { data: qualityGateProjectsHavingAiCode = [], isLoading: isCountLoading } =
useGetAllQualityGateProjectsQuery(
{ gateName: qualityGate.name, selected: 'selected' },
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/Projects.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/Projects.tsx
index 4578b2a11d9..5ad41bbc748 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/Projects.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/Projects.tsx
@@ -36,6 +36,7 @@ import { QualityGate } from '../../../types/types';
interface Props {
canEdit?: boolean;
+ onUpdate: () => void;
qualityGate: QualityGate;
}
@@ -117,6 +118,7 @@ export default class Projects extends React.PureComponent<Props, State> {
needToReload: true,
selectedProjects: [...prevState.selectedProjects, key],
}));
+ this.props.onUpdate();
}
});
@@ -129,6 +131,7 @@ export default class Projects extends React.PureComponent<Props, State> {
needToReload: true,
selectedProjects: without(prevState.selectedProjects, key),
}));
+ this.props.onUpdate();
}
});
@@ -143,7 +146,8 @@ export default class Projects extends React.PureComponent<Props, State> {
{project.name}
<br />
<Note>{project.key}</Note>
- {project.aiCodeAssurance === AiCodeAssuranceStatus.CONTAINS_AI_CODE && (
+ {(project.aiCodeAssurance === AiCodeAssuranceStatus.CONTAINS_AI_CODE ||
+ project.aiCodeAssurance === AiCodeAssuranceStatus.AI_CODE_ASSURED) && (
<p>
<Note>{translate('quality_gates.projects.ai_assured_message')}</Note>
</p>
@@ -166,9 +170,6 @@ export default class Projects extends React.PureComponent<Props, State> {
return (
<SelectList
elements={this.state.projects.map((project) => project.key)}
- disabledElements={this.state.projects
- .filter((project) => project.aiCodeAssurance === AiCodeAssuranceStatus.CONTAINS_AI_CODE)
- .map((project) => project.key)}
elementsTotalCount={this.state.projectsTotalCount}
labelAll={translate('quality_gates.projects.all')}
labelSelected={translate('quality_gates.projects.with')}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGate-it.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGate-it.tsx
index cf7f770416d..abbd122690c 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGate-it.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGate-it.tsx
@@ -808,12 +808,11 @@ describe('The Project section', () => {
// eslint-disable-next-line jest-dom/prefer-in-document
expect(screen.getAllByRole('checkbox')).toHaveLength(3);
- // projects with disabled as true are not selectable
- // last checked project in mock service is disabled
+ // Show ai code assurance project
const disabledCheckedProjects = screen.getByRole('checkbox', {
name: 'test5 test5 quality_gates.projects.ai_assured_message',
});
- expect(disabledCheckedProjects).toBeDisabled();
+ expect(disabledCheckedProjects).toBeInTheDocument();
// change tabs to show deselected projects
await user.click(screen.getByRole('radio', { name: 'quality_gates.projects.without' }));
@@ -825,26 +824,9 @@ describe('The Project section', () => {
await user.click(reloadButton);
expect(screen.getAllByRole('checkbox')).toHaveLength(3);
- // projects with disabled as true are not selectable
- // last unchecked project in mock service is disabled
- const disabledUncheckedProjects = screen.getByRole('checkbox', {
- name: 'test6 test6 quality_gates.projects.ai_assured_message',
- });
- expect(disabledUncheckedProjects).toBeDisabled();
-
// change tabs to show all projects
await user.click(screen.getByRole('radio', { name: 'quality_gates.projects.all' }));
expect(screen.getAllByRole('checkbox')).toHaveLength(7);
-
- const disabledCheckedProjectsAll = screen.getByRole('checkbox', {
- name: 'test5 test5 quality_gates.projects.ai_assured_message',
- });
- expect(disabledCheckedProjectsAll).toBeDisabled();
-
- const disabledUncheckedProjectsAll = screen.getByRole('checkbox', {
- name: 'test6 test6 quality_gates.projects.ai_assured_message',
- });
- expect(disabledUncheckedProjectsAll).toBeDisabled();
});
it('should handle the search of projects', async () => {
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 448755f2872..2066c6f7f6a 100644
--- a/server/sonar-web/src/main/js/queries/quality-gates.ts
+++ b/server/sonar-web/src/main/js/queries/quality-gates.ts
@@ -50,13 +50,13 @@ const QUERY_STALE_TIME = 5 * 60 * 1000;
const qualityQuery = {
all: () => ['quality-gate'] as const,
- list: () => [qualityQuery.all(), 'list'] as const,
- details: () => [qualityQuery.all(), 'details'] as const,
+ list: () => [...qualityQuery.all(), 'list'] as const,
+ details: () => [...qualityQuery.all(), 'details'] as const,
detail: (name?: string) => [...qualityQuery.details(), name ?? ''] as const,
- projectsAssoc: () => [qualityQuery.all(), 'project-assoc'] as const,
+ projectsAssoc: () => [...qualityQuery.all(), 'project-assoc'] as const,
projectAssoc: (project: string) => [...qualityQuery.projectsAssoc(), project] as const,
allProjectsSearch: (qualityGate: string) =>
- [qualityQuery.all(), 'all-project-search', qualityGate] as const,
+ [...qualityQuery.all(), 'all-project-search', qualityGate] as const,
};
// This is internal to "enable" query when searching from the project page
@@ -181,7 +181,7 @@ export function useDeleteQualityGateMutation(name: string) {
});
}
-export function useSetAiSupportedQualityGateMutation(name: string) {
+export function useSetAiSupportedQualityGateMutation() {
const queryClient = useQueryClient();
return useMutation({
@@ -195,9 +195,7 @@ export function useSetAiSupportedQualityGateMutation(name: string) {
return setQualityGateAiQualified(name, isQualityGateAiSupported);
},
onSuccess: () => {
- queryClient.invalidateQueries({ queryKey: qualityQuery.list() });
- queryClient.invalidateQueries({ queryKey: qualityQuery.projectsAssoc() });
- queryClient.invalidateQueries({ queryKey: qualityQuery.detail(name) });
+ queryClient.invalidateQueries({ queryKey: qualityQuery.all() });
},
});
}
@@ -383,3 +381,13 @@ export const useApplicationQualityGateStatus = createQueryHook(
});
},
);
+
+/**
+ * @deprecated This is only for component that has not been migrated to react-query
+ */
+export function useInvalidateQualityGateQuery() {
+ const queryClient = useQueryClient();
+ return () => {
+ queryClient.invalidateQueries({ queryKey: qualityQuery.all() });
+ };
+}
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 554a8d40e74..abb2e71ca56 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -2492,7 +2492,7 @@ quality_gates.projects.noResults=No Projects
quality_gates.projects.select_hint=Click to associate this project with the quality gate
quality_gates.projects.deselect_hint=Click to remove association between this project and the quality gate
quality_gates.projects.cannot_associate_projects_no_conditions=You must configure at least 1 condition before you can assign projects to this quality gate.
-quality_gates.projects.ai_assured_message=This project contains AI-generated code. It must use Sonar way.
+quality_gates.projects.ai_assured_message=This project contains AI-generated code.
quality_gates.operator.LT=is less than
quality_gates.operator.inverted.LT=greater than or equal
quality_gates.operator.GT=is greater than