diff options
author | Mathieu Suen <mathieu.suen@sonarsource.com> | 2024-11-29 11:01:26 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-11-29 20:03:08 +0000 |
commit | d08fbb88944054b2244b1fc509a5efaa7c3d2036 (patch) | |
tree | 06edf249be9a3ed3bbaa3adb2757bd4962e20b22 | |
parent | 640c9331567e0943c453f5d4284e5151b5070159 (diff) | |
download | sonarqube-d08fbb88944054b2244b1fc509a5efaa7c3d2036.tar.gz sonarqube-d08fbb88944054b2244b1fc509a5efaa7c3d2036.zip |
SONAR-23620 Fix modal when disqualifing a Quality Gate for AI assurance
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 |